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 }
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: 7d5e6d718 Fri Aug 24 01:51:28 2018 +0200 at lun nov 2 08:35:50 CET 2020