Main MRPT website > C++ reference for MRPT 1.5.7
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 using precomp. headers
11  #define __STDC_CONSTANT_MACROS // Needed for having "UINT64_C" and so
12 #endif
13 
14 #include "hwdrivers-precomp.h" // Precompiled headers
15 
16 #include <mrpt/config.h>
17 #include <mrpt/utils/utils_defs.h>
18 
19 #if MRPT_HAS_FFMPEG
20  extern "C"
21  {
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  }
27 #endif
28 
29 
31 
32 
33 
34 using namespace mrpt;
35 using namespace mrpt::hwdrivers;
36 
37 // JLBC: This file takes portions of code from the example "avcodec_sample.0.4.9.cpp"
38 #if MRPT_HAS_FFMPEG
39 namespace mrpt
40 {
41  namespace hwdrivers
42  {
43  // All context for ffmpeg:
44  struct TFFMPEGContext
45  {
46  AVFormatContext *pFormatCtx;
47  int videoStream;
48  AVCodecContext *pCodecCtx;
49  AVCodec *pCodec;
50  AVFrame *pFrame;
51  AVFrame *pFrameRGB;
52  SwsContext *img_convert_ctx;
53  std::vector<uint8_t> buffer;
54  };
55  }
56 }
57 #endif
58 
59 #define MY_FFMPEG_STATE const_cast<TFFMPEGContext*>(static_cast<const TFFMPEGContext*>(m_state.get()))
60 
61 
62 
63 /* --------------------------------------------------------
64  Ctor
65  -------------------------------------------------------- */
67 {
68 #if MRPT_HAS_FFMPEG
69  m_state.set( new TFFMPEGContext[1] );
70  TFFMPEGContext *ctx = MY_FFMPEG_STATE;
71 
72  ctx->pFormatCtx = NULL;
73  ctx->pCodecCtx = NULL;
74  ctx->pCodec = NULL;
75  ctx->videoStream = 0;
76  ctx->pFrame = NULL;
77  ctx->pFrameRGB = NULL;
78  ctx->img_convert_ctx = NULL;
79 
80  // Register all formats and codecs
81  av_register_all();
82 #else
83  THROW_EXCEPTION("MRPT has been compiled without FFMPEG libraries.")
84 #endif
85 }
86 
87 /* --------------------------------------------------------
88  Dtor
89  -------------------------------------------------------- */
91 {
92 #if MRPT_HAS_FFMPEG
93  // Close everything:
94  this->close();
95 
96  // Free context struct. memory
97  delete[] MY_FFMPEG_STATE;
98  m_state.set(NULL);
99 #endif
100 }
101 
102 /* --------------------------------------------------------
103  isOpen
104  -------------------------------------------------------- */
106 {
107 #if MRPT_HAS_FFMPEG
108  TFFMPEGContext *ctx = MY_FFMPEG_STATE;
109  return ctx->pFormatCtx != NULL;
110 #else
111  return false;
112 #endif
113 }
114 
115 /* --------------------------------------------------------
116  openURL
117  -------------------------------------------------------- */
118 bool CFFMPEG_InputStream::openURL( 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(), NULL, NULL)!=0)
131 #else
132  if(av_open_input_file( &ctx->pFormatCtx, url.c_str(), NULL, 0, NULL)!=0)
133 #endif
134 
135  {
136  ctx->pFormatCtx = NULL;
137  std::cerr << "[CFFMPEG_InputStream::openURL] Cannot open video: " << url << std::endl;
138  return false;
139  }
140 
141  // Retrieve stream information
142  if (
143 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53,35,0)
144  avformat_find_stream_info(ctx->pFormatCtx, NULL)<0
145 #else
146  av_find_stream_info(ctx->pFormatCtx)<0
147 #endif
148  )
149  {
150  std::cerr << "[CFFMPEG_InputStream::openURL] Couldn't find stream information: " << url << std::endl;
151  return false;
152  }
153 
154  // Dump information about file onto standard error
155  if (verbose)
156  {
157 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53,2,0)
158  av_dump_format(ctx->pFormatCtx, 0, url.c_str(), false);
159 #else
160  dump_format(ctx->pFormatCtx, 0, url.c_str(), false);
161 #endif
162  }
163 
164  // Find the first video stream
165  ctx->videoStream=-1;
166  for(unsigned int i=0; i<ctx->pFormatCtx->nb_streams; i++)
167  {
168  if(ctx->pFormatCtx->streams[i]->codec->codec_type==
169 #if LIBAVCODEC_VERSION_INT<AV_VERSION_INT(53,0,0)
170  CODEC_TYPE_VIDEO
171 #else
172  AVMEDIA_TYPE_VIDEO
173 #endif
174  )
175  {
176  ctx->videoStream=(int)i;
177  break;
178  }
179  }
180  if(ctx->videoStream==-1)
181  {
182  std::cerr << "[CFFMPEG_InputStream::openURL] Didn't find a video stream: " << url << std::endl;
183  return false;
184  }
185 
186  // Get a pointer to the codec context for the video stream
187  ctx->pCodecCtx= ctx->pFormatCtx->streams[ctx->videoStream]->codec;
188 
189  // Find the decoder for the video stream
190  ctx->pCodec=avcodec_find_decoder(ctx->pCodecCtx->codec_id);
191  if(ctx->pCodec==NULL)
192  {
193  std::cerr << "[CFFMPEG_InputStream::openURL] Codec not found: " << url << std::endl;
194  return false;
195  }
196 
197  // Open codec
198 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53,6,0)
199  if(avcodec_open2(ctx->pCodecCtx, ctx->pCodec,NULL)<0)
200 #else
201  if(avcodec_open(ctx->pCodecCtx, ctx->pCodec)<0)
202 #endif
203  {
204  std::cerr << "[CFFMPEG_InputStream::openURL] Could not open codec: " << url << std::endl;
205  return false;
206  }
207 
208 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55,46,0)
209  // Allocate video frame
210  ctx->pFrame=av_frame_alloc();
211  // Allocate an AVFrame structure
212  ctx->pFrameRGB=av_frame_alloc();
213 #else
214  // Allocate video frame
215  ctx->pFrame=avcodec_alloc_frame();
216  // Allocate an AVFrame structure
217  ctx->pFrameRGB=avcodec_alloc_frame();
218 #endif
219 
220 
221  if(ctx->pFrameRGB==NULL || ctx->pFrame==NULL)
222  {
223  std::cerr << "[CFFMPEG_InputStream::openURL] Could not alloc memory for frame buffers: " << url << std::endl;
224  return false;
225  }
226 
227  // Determine required buffer size and allocate buffer
228  size_t numBytes=avpicture_get_size(
229  m_grab_as_grayscale ? // BGR vs. RGB for OpenCV
230 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55,00,0)
231  AV_PIX_FMT_GRAY8 : AV_PIX_FMT_BGR24,
232 #else
233  PIX_FMT_GRAY8 : PIX_FMT_BGR24,
234 #endif
235  ctx->pCodecCtx->width,
236  ctx->pCodecCtx->height);
237 
238  ctx->buffer.resize(numBytes);
239 
240  // Assign appropriate parts of buffer to image planes in pFrameRGB
241  avpicture_fill(
242  (AVPicture *)ctx->pFrameRGB,
243  &ctx->buffer[0],
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 : AV_PIX_FMT_BGR24,
247 #else
248  PIX_FMT_GRAY8 : PIX_FMT_BGR24,
249 #endif
250  ctx->pCodecCtx->width,
251  ctx->pCodecCtx->height);
252 
253 
254  return true; // OK.
255 #else
256  return false;
257 #endif
258 }
259 
260 /* --------------------------------------------------------
261  close
262  -------------------------------------------------------- */
264 {
265 #if MRPT_HAS_FFMPEG
266  if (!this->isOpen()) return;
267 
268  TFFMPEGContext *ctx = MY_FFMPEG_STATE;
269 
270  // Close the codec
271  if (ctx->pCodecCtx)
272  {
273  avcodec_close(ctx->pCodecCtx);
274  ctx->pCodecCtx=NULL;
275  }
276 
277  // Close the video file
278  if (ctx->pFormatCtx)
279  {
280 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53,35,0)
281  avformat_close_input(&ctx->pFormatCtx);
282 #else
283  av_close_input_file(ctx->pFormatCtx);
284 #endif
285  ctx->pFormatCtx = NULL;
286  }
287 
288  // Free frames memory:
289  ctx->buffer.clear();
290 
291  if (ctx->pFrameRGB)
292  {
293 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55,46,0)
294  av_frame_free(&ctx->pFrameRGB);
295 #else
296  av_free(ctx->pFrameRGB);
297 #endif
298  ctx->pFrameRGB=NULL;
299  }
300  if (ctx->pFrame)
301  {
302 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55,46,0)
303  av_frame_free(&ctx->pFrame);
304 #else
305  av_free(ctx->pFrame);
306 #endif
307  ctx->pFrame = NULL;
308  }
309 
310  if (ctx->img_convert_ctx)
311  {
312  sws_freeContext( ctx->img_convert_ctx );
313  ctx->img_convert_ctx = NULL;
314  }
315 
316 #endif
317 }
318 
319 /* --------------------------------------------------------
320  retrieveFrame
321  -------------------------------------------------------- */
323 {
324 #if MRPT_HAS_FFMPEG
325  if (!this->isOpen()) return false;
326 
327  TFFMPEGContext *ctx = MY_FFMPEG_STATE;
328 
329  AVPacket packet;
330  int frameFinished;
331 
332  while(av_read_frame(ctx->pFormatCtx, &packet)>=0)
333  {
334  // Is this a packet from the video stream?
335  if(packet.stream_index==ctx->videoStream)
336  {
337  // Decode video frame
338 #if LIBAVCODEC_VERSION_MAJOR>52 || (LIBAVCODEC_VERSION_MAJOR==52 && LIBAVCODEC_VERSION_MINOR>=72)
339  avcodec_decode_video2(
340  ctx->pCodecCtx,
341  ctx->pFrame,
342  &frameFinished,
343  &packet);
344 #else
345  avcodec_decode_video(
346  ctx->pCodecCtx,
347  ctx->pFrame,
348  &frameFinished,
349  packet.data,
350  packet.size);
351 #endif
352  // Did we get a video frame?
353  if(frameFinished)
354  {
355  // Convert the image from its native format to RGB:
356  ctx->img_convert_ctx = sws_getCachedContext(
357  ctx->img_convert_ctx,
358  ctx->pCodecCtx->width,
359  ctx->pCodecCtx->height,
360  ctx->pCodecCtx->pix_fmt,
361  ctx->pCodecCtx->width,
362  ctx->pCodecCtx->height,
363  m_grab_as_grayscale ? // BGR vs. RGB for OpenCV
364 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55,00,0)
365  AV_PIX_FMT_GRAY8 : AV_PIX_FMT_BGR24,
366 #else
367  PIX_FMT_GRAY8 : PIX_FMT_BGR24,
368 #endif
369  SWS_BICUBIC,
370  NULL, NULL, NULL);
371 
372  sws_scale(
373  ctx->img_convert_ctx,
374  ctx->pFrame->data,
375  ctx->pFrame->linesize,0,
376  ctx->pCodecCtx->height,
377  ctx->pFrameRGB->data,
378  ctx->pFrameRGB->linesize);
379 
380  //std::cout << "[retrieveFrame] Generating image: " << ctx->pCodecCtx->width << "x" << ctx->pCodecCtx->height << std::endl;
381  //std::cout << " linsize: " << ctx->pFrameRGB->linesize[0] << std::endl;
382 
383  if( ctx->pFrameRGB->linesize[0]!= ((m_grab_as_grayscale ? 1:3)*ctx->pCodecCtx->width) )
384  THROW_EXCEPTION("FIXME: linesize!=width case not handled yet.")
385 
386  out_img.loadFromMemoryBuffer(
387  ctx->pCodecCtx->width,
388  ctx->pCodecCtx->height,
389  !m_grab_as_grayscale, // Color
390  ctx->pFrameRGB->data[0]
391  );
392 
393  // Free the packet that was allocated by av_read_frame
394  av_free_packet(&packet);
395  return true;
396  }
397  }
398 
399  // Free the packet that was allocated by av_read_frame
400  av_free_packet(&packet);
401  }
402 
403  return false; // Error reading/ EOF
404 #else
405  return false;
406 #endif
407 }
408 
409 /* --------------------------------------------------------
410  getVideoFPS
411  -------------------------------------------------------- */
413 {
414 #if MRPT_HAS_FFMPEG
415  if (!this->isOpen()) return -1;
416 
417  TFFMPEGContext *ctx = MY_FFMPEG_STATE;
418  if (!ctx) return -1;
419  if (!ctx->pCodecCtx) return -1;
420 
421  return static_cast<double>(ctx->pCodecCtx->time_base.den) / ctx->pCodecCtx->time_base.num;
422 #else
423  return false;
424 #endif
425 }
A class for storing images as grayscale or RGB bitmaps.
Definition: CImage.h:101
GLuint buffer
Definition: glext.h:3775
#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:387
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:3919
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.5.7 Git: 5902e14cc Wed Apr 24 15:04:01 2019 +0200 at lun oct 28 01:39:17 CET 2019