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



Page generated by Doxygen 1.8.14 for MRPT 1.9.9 Git: ad3a9d8ae Tue May 1 23:10:22 2018 -0700 at lun oct 28 00:14:14 CET 2019