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



Page generated by Doxygen 1.8.14 for MRPT 1.9.9 Git: ae4571287 Thu Nov 23 00:06:53 2017 +0100 at dom oct 27 23:51:55 CET 2019