SLIP  1.4
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
AvReader.hpp
Go to the documentation of this file.
1 
10 #ifndef AVREADER_HPP_
11 #define AVREADER_HPP_
12 
13 #include <iostream>
14 #include <sstream>
15 
16 extern "C" {
17 #include <libswscale/swscale.h>
18 #include <libavformat/avformat.h>
19 #include <libavcodec/avcodec.h>
20 #include <libavutil/pixfmt.h>
21 }
22 
23 #include "ContainerReader.hpp"
24 #include "error.hpp"
25 
26 namespace slip {
27 
48 template<class VideoContainer, typename T, std::size_t Nb_components, std::size_t Nb_block>
49 class AvReader: public ContainerReader<VideoContainer,T,Nb_block>{
50 public:
51  typedef VideoContainer container_type;
52  typedef T value_type;
54 
59 
64  :base(),pFormatCtx(NULL),pCodecCtx(NULL),pCodec(NULL),pFrame(NULL),pFrameScaled(NULL),numBytes(0),buffer(NULL),packet(),
65  next_frame_loaded(0),videoStream(-1),colorspace(PIX_FMT_GRAY8),remained_frames(0),read_frames(0),nbframes_is_approximated(0),
66  block_ind(1),block_size(0),last_block_size(0),last_it(0),finished(0),initialized(0)
67  {}
68 
74  AvReader(std::string data_filename)
75  :base(data_filename),pFormatCtx(NULL),pCodecCtx(NULL),pCodec(NULL),pFrame(NULL),pFrameScaled(NULL),
76  numBytes(0),buffer(NULL),packet(),next_frame_loaded(0),videoStream(-1), colorspace(PIX_FMT_GRAY8),remained_frames(0),read_frames(0),
77  nbframes_is_approximated(0),block_ind(1),block_size(0),last_block_size(0),last_it(0),finished(0),initialized(0)
78  { initialize();}
79 
84  virtual ~AvReader(){
85  if(initialized){
86  release();
87  }
88  }
95  void initialize();
96 
101  void release();
102 
112  int read(VideoContainer & in);
113 
114 
119 
124  AVCodec * get_codec() const{
125  if(pCodec != NULL)
126  return pCodec;
127  else
128  return NULL;
129  }
130 
135  CodecID get_codec_id() const{
136  if(pCodec != NULL)
137  return pCodec->id;
138  else
139  return CODEC_ID_NONE;
140  }
141 
145  int get_read_frames() const {
146  return read_frames;
147  }
148 
152  int get_nb_frames() const {
153  return (read_frames + remained_frames);
154  }
155 
160  int get_bit_rate() const{
161  if(pCodecCtx != NULL)
162  return pCodecCtx->bit_rate;
163  else
164  return 0;
165  }
166 
171  std::size_t get_frame_width() const{
172  if(pCodecCtx != NULL)
173  return static_cast<std::size_t>(pCodecCtx->width);
174  else
175  return 0;
176  }
177 
182  std::size_t get_frame_height() const{
183  if(pCodecCtx != NULL)
184  return static_cast<std::size_t>(pCodecCtx->height);
185  else
186  return 0;
187  }
188 
193  AVRational get_time_base() const{
194  if(pCodecCtx != NULL)
195  return pCodecCtx->time_base;
196  else
197  return (AVRational){0,1};
198  }
199 
204  int64_t get_duration() const{
205  if(pFormatCtx != NULL)
206  return pFormatCtx->duration;
207  else
208  return 0;
209  }
210 
213 private:
214  AVFormatContext *pFormatCtx;
215  AVCodecContext *pCodecCtx;
216  AVCodec *pCodec;
217  AVFrame *pFrame;
218  AVFrame *pFrameScaled;
220  int numBytes;
221  uint8_t *buffer;
222  AVPacket packet;
223  bool next_frame_loaded;
225  int videoStream;
226  PixelFormat colorspace;
227 
229  std::size_t remained_frames;
230  std::size_t read_frames;
231  bool nbframes_is_approximated;
233  int block_ind;
235  int block_size;
237  int last_block_size;
239  bool last_it;
241  bool finished;
243  bool initialized;
244 };
245 
246 }
249 namespace slip {
250 
251 template<class VideoContainer, typename T, std::size_t Nb_components, std::size_t Nb_block>
252 inline
254  if (!initialized){
255  if (Nb_components != 1 && Nb_components != 3){
256  std::ostringstream err;
257  err << FILE_READ_ERROR << this->data_filename_ << " | Nb_components should be 1 or 3.";
258  slip::slip_exception exc(std::string("slip"), std::string("AvReader::initialize()"), err.str());
259  throw (exc);
260  }
261  if (Nb_components == 3)
262  colorspace = PIX_FMT_RGB24;
263 
264  // Register all formats and codecs
265  av_register_all();
266 
267 
268  // Open video file
269  if(avformat_open_input(&pFormatCtx, this->data_filename_.c_str(), NULL, NULL)!=0) {
270  std::ostringstream err;
271  err << FILE_OPEN_ERROR << this->data_filename_;
272  slip::slip_exception exc(std::string("slip"), std::string("AvReader::initialize()"), err.str());
273  throw (exc);
274  }
275 
276  // Retrieve stream information
277  if(avformat_find_stream_info(pFormatCtx,NULL) < 0){
278  std::ostringstream err;
279  err << FILE_READ_ERROR << this->data_filename_ << " | Couldn't find stream information.";
280  slip::slip_exception exc(std::string("slip"), std::string("AvReader::initialize()"), err.str());
281  throw (exc);
282  }
283 
284  // Dump information about file onto standard error
285  av_dump_format(pFormatCtx, 0, this->data_filename_.c_str(), false);
286 
287  // Find the first video stream
288  for(unsigned int i=0; i<pFormatCtx->nb_streams; i++)
289  if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO)
290  {
291  videoStream=i;
292  break;
293  }
294  if(videoStream==-1){
295  std::ostringstream err;
296  err << FILE_READ_ERROR << this->data_filename_ << " | Didn't find a video stream.";
297  slip::slip_exception exc(std::string("slip"), std::string("AvReader::initialize()"), err.str());
298  throw (exc);
299  }
300 
301  // Get a pointer to the codec context for the video stream
302  pCodecCtx=pFormatCtx->streams[videoStream]->codec;
303 
304  // Find the decoder for the video stream
305  pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
306  if(pCodec==NULL){
307  std::ostringstream err;
308  err << FILE_READ_ERROR << this->data_filename_ << " | Codec not found.";
309  slip::slip_exception exc(std::string("slip"), std::string("AvReader::initialize()"), err.str());
310  throw (exc);
311  }
312 
313 
314  // Open codec
315  if(avcodec_open2(pCodecCtx, pCodec, NULL)<0){
316  std::ostringstream err;
317  err << FILE_READ_ERROR << this->data_filename_ << " | Could not open codec.";
318  slip::slip_exception exc(std::string("slip"), std::string("AvReader::initialize()"), err.str());
319  throw (exc);
320  }
321 
322  // Hack to correct wrong frame rates that seem to be generated by some
323  // codecs
324  // if(pCodecCtx->frame_rate>1000 && pCodecCtx->frame_rate_base==1)
325  // pCodecCtx->frame_rate_base=1000;
326 
327  // Allocate video frame
328  pFrame=avcodec_alloc_frame();
329 
330  // Allocate an AVFrame structure
331  pFrameScaled=avcodec_alloc_frame();
332  if(pFrameScaled==NULL){
333  std::ostringstream err;
334  err << FILE_READ_ERROR << this->data_filename_ << " | Could not allocate AVFrame structure.";
335  slip::slip_exception exc(std::string("slip"), std::string("AvReader::initialize()"), err.str());
336  throw (exc);
337  }
338 
339  // Determine required buffer size and allocate buffer
340 
341  numBytes=avpicture_get_size(colorspace, pCodecCtx->width,
342  pCodecCtx->height);
343  buffer=new uint8_t[numBytes];
344  // Assign appropriate parts of buffer to image planes in pFrameScaled
345  avpicture_fill((AVPicture *)pFrameScaled, buffer, colorspace,
346  pCodecCtx->width, pCodecCtx->height);
347 
348  remained_frames = pFormatCtx->streams[videoStream]->nb_frames;
349  if (remained_frames == 0){
350  nbframes_is_approximated = true;
351  remained_frames = pFormatCtx->streams[videoStream]->codec_info_nb_frames;
352  //std::cout << "Nb Frames estimated : " << remaining_frames << "\n";
353  }
354 
355  block_size = remained_frames / Nb_block;
356  long int one_frame_size = pCodecCtx->width * pCodecCtx->height * Nb_components;
357  double container_size = static_cast<double>(block_size) * static_cast<double>(one_frame_size);
358  double limit_1_go = 1073741824.0;
359  if ( container_size > limit_1_go)
360  {
361  std::ostringstream err;
362  err << FILE_READ_ERROR << this->data_filename_ << " | Containers size ";
363  err << container_size << " is greater than 1Go = " << limit_1_go;
364  slip::slip_exception exc(std::string("slip"), std::string("AvReader::initialize()"), err.str());
365  throw (exc);
366  }
367 
368  last_block_size = remained_frames % Nb_block;
369  last_block_size = (last_block_size == 0 ? block_size : block_size+last_block_size);
370  last_it = (Nb_block == 1);
371  finished = (last_it && last_block_size == 0);
372  initialized = true;
373  }
374 
375 }
376 
377 template<class VideoContainer, typename T, std::size_t Nb_components, std::size_t Nb_block>
378 inline
380  if(!initialized){
381  std::ostringstream err;
382  err << FILE_READ_ERROR << this->data_filename_ << " | The reader needs to be initialized before reading.";
383  slip::slip_exception exc(std::string("slip"), std::string("AvReader::read()"), err.str());
384  throw (exc);
385  }
386  if (finished)
387  return 0;
388 
389  if (last_it)
390  in.resize(last_block_size,pCodecCtx->height,pCodecCtx->width,T());
391  else{
392  in.resize(block_size,pCodecCtx->height,pCodecCtx->width,T());
393  }
394 
395  if((!nbframes_is_approximated) && (remained_frames < in.slices())){
396  std::ostringstream err;
397  err << FILE_READ_ERROR << this->data_filename_ << " | Bad image dimensions.";
398  slip::slip_exception exc(std::string("slip"), std::string("JpegReader::read()"), err.str());
399  throw (exc);
400  }
401 
402  // Read frames and save first five frames to disk
403  std::size_t filled_frames=0;
404  int frameFinished = 0;
405  std::size_t height = static_cast<std::size_t>(pCodecCtx->height);
406  std::size_t width = static_cast<std::size_t>(pCodecCtx->width);
407  std::size_t row_stride = static_cast<std::size_t>(pCodecCtx->width * Nb_components);
408  std::size_t in_slices = in.slices();
409  if (!next_frame_loaded)
410  next_frame_loaded = (av_read_frame(pFormatCtx, &packet)>=0);
411  while(next_frame_loaded && (filled_frames < in_slices))
412  {
413  // Is this a packet from the video stream?
414  if(packet.stream_index==videoStream)
415  {
416  // Decode video frame
417  if(avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished,&packet) < 0){
418  std::ostringstream err;
419  err << FILE_READ_ERROR << this->data_filename_ << " | The frame can not be decompressed.";
420  slip::slip_exception exc(std::string("slip"), std::string("AvReader::read()"), err.str());
421  throw (exc);
422  }
423  // Did we get a video frame?
424  if(frameFinished)
425  {
426  static struct SwsContext *img_convert_ctx = NULL;
427  img_convert_ctx = sws_getCachedContext(img_convert_ctx,width, height,pCodecCtx->pix_fmt,
428  width, height, colorspace, SWS_FAST_BILINEAR, NULL, NULL, NULL);
429  std::size_t h = sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize,0, height,
430  pFrameScaled->data, pFrameScaled->linesize);
431  if (h > height){
432  std::ostringstream err;
433  err << FILE_READ_ERROR << this->data_filename_ << " | The frame size is too large.";
434  slip::slip_exception exc(std::string("slip"), std::string("AvReader::read()"), err.str());
435  throw (exc);
436  }
437  // Save the frame to the input container
438  for(std::size_t y=0; y<height; y++){
439  T * ptr_i = reinterpret_cast<T *>(in[filled_frames][y]);
440  std::copy(pFrameScaled->data[0]+y*pFrameScaled->linesize[0],
441  pFrameScaled->data[0]+y*pFrameScaled->linesize[0] + row_stride,ptr_i);
442  }
443  filled_frames++;
444  }
445  }
446  // Free the packet that was allocated by av_read_frame
447  av_free_packet(&packet);
448  //Read the next frame
449  next_frame_loaded = (av_read_frame(pFormatCtx, &packet)>=0);
450  }
451  read_frames += filled_frames;
452  remained_frames -= filled_frames;
453  block_ind++;
454  if (last_it){
455  finished = true;
456  if (nbframes_is_approximated){
457  if (in.slices() > (filled_frames + 1)){
458  std::size_t rs = in.slices() - filled_frames - 1;
459  std::cerr << "slip::AvReader::read | Warning the last container has not been fully filled, the "
460  << rs << " last frames remain unchanged." << std::endl;
461  }else if(next_frame_loaded){
462  std::cerr << "slip::AvReader::read | Warning some end frames of the video could have not been read." << std::endl;
463  }
464  }
465  }
466  else
467  last_it = (block_ind == Nb_block);
468  return (!finished);
469 
470 }
471 
472 template<class VideoContainer, typename T, std::size_t Nb_components, std::size_t Nb_block>
473 inline
475  if(initialized){
476 
477  // Free the RGB image
478  delete [] buffer;
479  av_free(pFrameScaled);
480 
481  // Free the YUV frame
482  av_free(pFrame);
483 
484  // Close the codec
485  avcodec_close(pCodecCtx);
486 
487  // Close the video file
488  avformat_close_input(&pFormatCtx);
489 
490  pFormatCtx = NULL;
491  pCodecCtx = NULL;
492  pCodec = NULL;
493  pFrame = NULL;
494  pFrameScaled = NULL;
495  numBytes = 0;
496  buffer = NULL;
497  next_frame_loaded = false;
498  videoStream = -1;
499  colorspace = PIX_FMT_GRAY8;
500  remained_frames = 0;
501  read_frames = 0;
502  nbframes_is_approximated = false;
503  block_ind = 1;
504  block_size = 0;
505  last_block_size = 0;
506  last_it = 0;
507  finished = false;
508  initialized = false;
509  }
510 }
511 
512 }
513 #endif /* AVREADER_HPP_ */
AVCodec * get_codec() const
get the codec pointer
Definition: AvReader.hpp:124
int64_t get_duration() const
get the duration of the video stream
Definition: AvReader.hpp:204
void initialize()
initialized the reading process.
Definition: AvReader.hpp:253
AvReader(std::string data_filename)
Definition: AvReader.hpp:74
Contains the container reader base class.
void release()
release the reading process.
Definition: AvReader.hpp:474
standard exception extension name have been changed to eliminate conflicts with QT ...
Definition: error.hpp:106
ContainerReader< VideoContainer, T, Nb_block > base
Definition: AvReader.hpp:53
std::size_t get_frame_width() const
get the width of a decoded frame picture
Definition: AvReader.hpp:171
int get_nb_frames() const
get the total number of frames
Definition: AvReader.hpp:152
int get_bit_rate() const
get the the average bit rate of the codec
Definition: AvReader.hpp:160
const std::string FILE_OPEN_ERROR
Definition: error.hpp:82
ContainerReader is the base class of the readers classes. All readers are working the same way: ...
std::size_t get_frame_height() const
get the height of a decoded frame picture
Definition: AvReader.hpp:182
void copy(_II first, _II last, _OI output_first)
Copy algorithm optimized for slip iterators.
Definition: copy_ext.hpp:177
AvReader, inherited from ContainerReader, is a reader for videos.
Definition: AvReader.hpp:49
virtual ~AvReader()
Definition: AvReader.hpp:84
AVRational get_time_base() const
get the time base of the video stream (1/framerate)
Definition: AvReader.hpp:193
int get_read_frames() const
get the number of read frames
Definition: AvReader.hpp:145
Provides SLIP error messages.
CodecID get_codec_id() const
get the codec id (see avcodec.h : enum AVCodecID)
Definition: AvReader.hpp:135
VideoContainer container_type
Definition: AvReader.hpp:51
int read(VideoContainer &in)
virtual read function. If Nb_block is more than one, it reads only one container, and returns 0...
Definition: AvReader.hpp:379
const std::string FILE_READ_ERROR
Definition: error.hpp:90