MRPT  1.9.9
CFFMPEG_InputStream.cpp
Go to the documentation of this file.
1 /* +------------------------------------------------------------------------+
2  | Mobile Robot Programming Toolkit (MRPT) |
3  | http://www.mrpt.org/ |
4  | |
5  | Copyright (c) 2005-2018, Individual contributors, see AUTHORS file |
6  | See: http://www.mrpt.org/Authors - All rights reserved. |
7  | Released under BSD License. See details in http://www.mrpt.org/License |
8  +------------------------------------------------------------------------+ */
9 
10 #if defined(__GNUC__) // Needed for ffmpeg headers. Only allowed here when not
11 // using precomp. headers
12 #define __STDC_CONSTANT_MACROS // Needed for having "UINT64_C" and so
13 #endif
14 
15 #include "hwdrivers-precomp.h" // Precompiled headers
16 
17 #include <mrpt/config.h>
18 
19 #if MRPT_HAS_FFMPEG
20 extern "C" {
21 #define _MSC_STDINT_H_ // We already have pstdint.h in MRPT
22 #include <libavformat/avformat.h>
23 #include <libavcodec/avcodec.h>
24 #include <libswscale/swscale.h>
25 #include <libavutil/imgutils.h>
26 }
27 #endif
28 
30 
31 using namespace mrpt;
32 using namespace mrpt::hwdrivers;
33 
34 // JLBC: This file takes portions of code from the example
35 // "avcodec_sample.0.4.9.cpp"
36 #if MRPT_HAS_FFMPEG
37 namespace mrpt::hwdrivers
38 {
39 
40 // All context for ffmpeg:
41 struct TFFMPEGContext
42 {
43  AVFormatContext* pFormatCtx;
44  int videoStream;
45  AVCodecContext* pCodecCtx;
46  AVCodec* pCodec;
47  AVFrame* pFrame;
48  AVFrame* pFrameRGB;
49  SwsContext* img_convert_ctx;
50  std::vector<uint8_t> buffer;
51 };
52 }
53 #endif
54 
56 {
57 #if MRPT_HAS_FFMPEG
58  TFFMPEGContext m_state;
59 #endif
60 };
61 
62 /* --------------------------------------------------------
63  Ctor
64  -------------------------------------------------------- */
66 #if MRPT_HAS_FFMPEG
67 : m_impl(mrpt::make_impl<CFFMPEG_InputStream::Impl>())
68 {
69  TFFMPEGContext* ctx = &m_impl->m_state;
70 
71  ctx->pFormatCtx = nullptr;
72  ctx->pCodecCtx = nullptr;
73  ctx->pCodec = nullptr;
74  ctx->videoStream = 0;
75  ctx->pFrame = nullptr;
76  ctx->pFrameRGB = nullptr;
77  ctx->img_convert_ctx = nullptr;
78 
79  // Register all formats and codecs
80  av_register_all();
81 }
82 #else
83 {
84  THROW_EXCEPTION("MRPT has been compiled without FFMPEG libraries.");
85 }
86 #endif
87 
88 /* --------------------------------------------------------
89  Dtor
90  -------------------------------------------------------- */
92 {
93 #if MRPT_HAS_FFMPEG
94  // Close everything:
95  this->close();
96 
97 #endif
98 }
99 
100 /* --------------------------------------------------------
101  isOpen
102  -------------------------------------------------------- */
104 {
105 #if MRPT_HAS_FFMPEG
106  const TFFMPEGContext* ctx = &m_impl->m_state;
107  return ctx->pFormatCtx != nullptr;
108 #else
109  return false;
110 #endif
111 }
112 
113 /* --------------------------------------------------------
114  openURL
115  -------------------------------------------------------- */
117  const std::string& url, bool grab_as_grayscale, bool verbose)
118 {
119 #if MRPT_HAS_FFMPEG
120  this->close(); // Close first
121 
122  TFFMPEGContext* ctx = &m_impl->m_state;
123 
124  this->m_url = url;
125  this->m_grab_as_grayscale = grab_as_grayscale;
126 
127 // Open video file
128 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53, 2, 0)
129  if (avformat_open_input(&ctx->pFormatCtx, url.c_str(), nullptr, nullptr) !=
130  0)
131 #else
132  if (av_open_input_file(
133  &ctx->pFormatCtx, url.c_str(), nullptr, 0, nullptr) != 0)
134 #endif
135 
136  {
137  ctx->pFormatCtx = nullptr;
138  std::cerr << "[CFFMPEG_InputStream::openURL] Cannot open video: " << url
139  << std::endl;
140  return false;
141  }
142 
143  // Retrieve stream information
144  if (
145 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53, 35, 0)
146  avformat_find_stream_info(ctx->pFormatCtx, nullptr) < 0
147 #else
148  av_find_stream_info(ctx->pFormatCtx) < 0
149 #endif
150  )
151  {
152  std::cerr << "[CFFMPEG_InputStream::openURL] Couldn't find stream "
153  "information: "
154  << url << std::endl;
155  return false;
156  }
157 
158  // Dump information about file onto standard error
159  if (verbose)
160  {
161 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53, 2, 0)
162  av_dump_format(ctx->pFormatCtx, 0, url.c_str(), false);
163 #else
164  dump_format(ctx->pFormatCtx, 0, url.c_str(), false);
165 #endif
166  }
167 
168  // Find the first video stream
169  ctx->videoStream = -1;
170  for (unsigned int i = 0; i < ctx->pFormatCtx->nb_streams; i++)
171  {
172  if (ctx->pFormatCtx->streams[i]->codec->codec_type ==
173 #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(53, 0, 0)
174  CODEC_TYPE_VIDEO
175 #else
176  AVMEDIA_TYPE_VIDEO
177 #endif
178  )
179  {
180  ctx->videoStream = (int)i;
181  break;
182  }
183  }
184  if (ctx->videoStream == -1)
185  {
186  std::cerr
187  << "[CFFMPEG_InputStream::openURL] Didn't find a video stream: "
188  << url << std::endl;
189  return false;
190  }
191 
192  // Get a pointer to the codec context for the video stream
193  ctx->pCodecCtx = ctx->pFormatCtx->streams[ctx->videoStream]->codec;
194 
195  // Find the decoder for the video stream
196  ctx->pCodec = avcodec_find_decoder(ctx->pCodecCtx->codec_id);
197  if (ctx->pCodec == nullptr)
198  {
199  std::cerr << "[CFFMPEG_InputStream::openURL] Codec not found: " << url
200  << std::endl;
201  return false;
202  }
203 
204 // Open codec
205 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53, 6, 0)
206  if (avcodec_open2(ctx->pCodecCtx, ctx->pCodec, nullptr) < 0)
207 #else
208  if (avcodec_open(ctx->pCodecCtx, ctx->pCodec) < 0)
209 #endif
210  {
211  std::cerr << "[CFFMPEG_InputStream::openURL] Could not open codec: "
212  << url << std::endl;
213  return false;
214  }
215 
216 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55, 46, 0)
217  // Allocate video frame
218  ctx->pFrame = av_frame_alloc();
219  // Allocate an AVFrame structure
220  ctx->pFrameRGB = av_frame_alloc();
221 #else
222  // Allocate video frame
223  ctx->pFrame = avcodec_alloc_frame();
224  // Allocate an AVFrame structure
225  ctx->pFrameRGB = avcodec_alloc_frame();
226 #endif
227 
228  if (ctx->pFrameRGB == nullptr || ctx->pFrame == nullptr)
229  {
230  std::cerr << "[CFFMPEG_InputStream::openURL] Could not alloc memory "
231  "for frame buffers: "
232  << url << std::endl;
233  return false;
234  }
235 
236 // Determine required buffer size and allocate buffer
237 #if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(54, 6, 0)
238  size_t numBytes = avpicture_get_size(
239 #else
240  size_t numBytes = av_image_get_buffer_size(
241 #endif
242  m_grab_as_grayscale ? // BGR vs. RGB for OpenCV
243 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55, 00, 0)
244  AV_PIX_FMT_GRAY8
245  : AV_PIX_FMT_BGR24,
246 #else
247  PIX_FMT_GRAY8
248  : PIX_FMT_BGR24,
249 #endif
250  ctx->pCodecCtx->width, ctx->pCodecCtx->height
251 #if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(54, 6, 0)
252  ,
253  1
254 #endif
255  );
256 
257  ctx->buffer.resize(numBytes);
258 
259  // Assign appropriate parts of buffer to image planes in pFrameRGB
260  avpicture_fill(
261  (AVPicture*)ctx->pFrameRGB, &ctx->buffer[0],
262  m_grab_as_grayscale ? // BGR vs. RGB for OpenCV
263 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55, 00, 0)
264  AV_PIX_FMT_GRAY8
265  : AV_PIX_FMT_BGR24,
266 #else
267  PIX_FMT_GRAY8
268  : PIX_FMT_BGR24,
269 #endif
270  ctx->pCodecCtx->width, ctx->pCodecCtx->height);
271 
272  return true; // OK.
273 #else
274  return false;
275 #endif
276 }
277 
278 /* --------------------------------------------------------
279  close
280  -------------------------------------------------------- */
282 {
283 #if MRPT_HAS_FFMPEG
284  if (!this->isOpen()) return;
285 
286  TFFMPEGContext* ctx = &m_impl->m_state;
287 
288  // Close the codec
289  if (ctx->pCodecCtx)
290  {
291  avcodec_close(ctx->pCodecCtx);
292  ctx->pCodecCtx = nullptr;
293  }
294 
295  // Close the video file
296  if (ctx->pFormatCtx)
297  {
298 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53, 35, 0)
299  avformat_close_input(&ctx->pFormatCtx);
300 #else
301  av_close_input_file(ctx->pFormatCtx);
302 #endif
303  ctx->pFormatCtx = nullptr;
304  }
305 
306  // Free frames memory:
307  ctx->buffer.clear();
308 
309  if (ctx->pFrameRGB)
310  {
311 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55, 46, 0)
312  av_frame_free(&ctx->pFrameRGB);
313 #else
314  av_free(ctx->pFrameRGB);
315 #endif
316  ctx->pFrameRGB = nullptr;
317  }
318  if (ctx->pFrame)
319  {
320 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55, 46, 0)
321  av_frame_free(&ctx->pFrame);
322 #else
323  av_free(ctx->pFrame);
324 #endif
325  ctx->pFrame = nullptr;
326  }
327 
328  if (ctx->img_convert_ctx)
329  {
330  sws_freeContext(ctx->img_convert_ctx);
331  ctx->img_convert_ctx = nullptr;
332  }
333 
334 #endif
335 }
336 
337 /* --------------------------------------------------------
338  retrieveFrame
339  -------------------------------------------------------- */
341 {
342 #if MRPT_HAS_FFMPEG
343  if (!this->isOpen()) return false;
344 
345  TFFMPEGContext* ctx = &m_impl->m_state;
346 
347  AVPacket packet;
348  int frameFinished;
349 
350  while (av_read_frame(ctx->pFormatCtx, &packet) >= 0)
351  {
352  // Is this a packet from the video stream?
353  if (packet.stream_index == ctx->videoStream)
354  {
355 // Decode video frame
356 #if LIBAVCODEC_VERSION_MAJOR > 52 || \
357  (LIBAVCODEC_VERSION_MAJOR == 52 && LIBAVCODEC_VERSION_MINOR >= 72)
358  avcodec_decode_video2(
359  ctx->pCodecCtx, ctx->pFrame, &frameFinished, &packet);
360 #else
361  avcodec_decode_video(
362  ctx->pCodecCtx, ctx->pFrame, &frameFinished, packet.data,
363  packet.size);
364 #endif
365  // Did we get a video frame?
366  if (frameFinished)
367  {
368  // Convert the image from its native format to RGB:
369  ctx->img_convert_ctx = sws_getCachedContext(
370  ctx->img_convert_ctx, ctx->pCodecCtx->width,
371  ctx->pCodecCtx->height, ctx->pCodecCtx->pix_fmt,
372  ctx->pCodecCtx->width, ctx->pCodecCtx->height,
373  m_grab_as_grayscale ? // BGR vs. RGB for OpenCV
374 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55, 00, 0)
375  AV_PIX_FMT_GRAY8
376  : AV_PIX_FMT_BGR24,
377 #else
378  PIX_FMT_GRAY8
379  : PIX_FMT_BGR24,
380 #endif
381  SWS_BICUBIC, nullptr, nullptr, nullptr);
382 
383  sws_scale(
384  ctx->img_convert_ctx, ctx->pFrame->data,
385  ctx->pFrame->linesize, 0, ctx->pCodecCtx->height,
386  ctx->pFrameRGB->data, ctx->pFrameRGB->linesize);
387 
388  // std::cout << "[retrieveFrame] Generating image: " <<
389  // ctx->pCodecCtx->width << "x" << ctx->pCodecCtx->height <<
390  // std::endl;
391  // std::cout << " linsize: " << ctx->pFrameRGB->linesize[0] <<
392  // std::endl;
393 
394  if (ctx->pFrameRGB->linesize[0] !=
395  ((m_grab_as_grayscale ? 1 : 3) * ctx->pCodecCtx->width))
397  "FIXME: linesize!=width case not handled yet.")
398 
399  out_img.loadFromMemoryBuffer(
400  ctx->pCodecCtx->width, ctx->pCodecCtx->height,
401  !m_grab_as_grayscale, // Color
402  ctx->pFrameRGB->data[0]);
403 
404 // Free the packet that was allocated by av_read_frame
405 #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55, 16, 0)
406  av_free_packet(&packet);
407 #else
408  av_packet_unref(&packet);
409 #endif
410  return true;
411  }
412  }
413 
414 // Free the packet that was allocated by av_read_frame
415 #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55, 16, 0)
416  av_free_packet(&packet);
417 #else
418  av_packet_unref(&packet);
419 #endif
420  }
421 
422  return false; // Error reading/ EOF
423 #else
424  return false;
425 #endif
426 }
427 
428 /* --------------------------------------------------------
429  getVideoFPS
430  -------------------------------------------------------- */
432 {
433 #if MRPT_HAS_FFMPEG
434  if (!this->isOpen()) return -1;
435 
436  const TFFMPEGContext* ctx = &m_impl->m_state;
437  if (!ctx) return -1;
438  if (!ctx->pCodecCtx) return -1;
439 
440  return static_cast<double>(ctx->pCodecCtx->time_base.den) /
441  ctx->pCodecCtx->time_base.num;
442 #else
443  return false;
444 #endif
445 }
double getVideoFPS() const
Get the frame-per-second (FPS) of the video source, or "-1" if the video is not open.
CFFMPEG_InputStream()
Default constructor, does not open any video source at startup.
bool retrieveFrame(mrpt::img::CImage &out_img)
Get the next frame from the video stream.
bool openURL(const std::string &url, bool grab_as_grayscale=false, bool verbose=false)
Open a video file or a video stream (rtsp://) This can be used to open local video files (eg.
bool isOpen() const
Return whether the video source was open correctly.
void close()
Close the video stream (this is called automatically at destruction).
A class for storing images as grayscale or RGB bitmaps.
Definition: img/CImage.h:131
void loadFromMemoryBuffer(unsigned int width, unsigned int height, bool color, unsigned char *rawpixels, bool swapRedBlue=false)
Reads the image from raw pixels buffer in memory.
Definition: CImage.cpp:385
#define THROW_EXCEPTION(msg)
Definition: exceptions.h:41
GLuint buffer
Definition: glext.h:3917
GLsizei const GLchar ** string
Definition: glext.h:4101
Contains classes for various device interfaces.
This is the global namespace for all Mobile Robot Programming Toolkit (MRPT) libraries.



Page generated by Doxygen 1.9.1 for MRPT 1.9.9 Git: 814d80880 Fri Aug 24 01:51:28 2018 +0200 at mar 26 may 2026 12:30:59 CEST