contrib/brl/bbas/vidl2/vidl2_ffmpeg_istream_v2.txx

Go to the documentation of this file.
00001 // This is brl/bbas/vidl2/vidl2_ffmpeg_istream_v2.txx
00002 #ifndef vidl2_ffmpeg_istream_v2_txx_
00003 #define vidl2_ffmpeg_istream_v2_txx_
00004 #include "vidl2_ffmpeg_istream.h"
00005 //:
00006 // \file
00007 // \author Matt Leota
00008 // \author Amitha Perera
00009 // \date   26 Dec 2007
00010 //
00011 // Update implementation based on ffmpeg svn -r11322.
00012 
00013 //-----------------------------------------------------------------------------
00014 
00015 #include "vidl2_ffmpeg_init.h"
00016 #include "vidl2_frame.h"
00017 #include "vidl2_ffmpeg_convert.h"
00018 
00019 #include <vcl_string.h>
00020 #include <vcl_iostream.h>
00021 
00022 extern "C" {
00023 #if FFMPEG_IN_SEVERAL_DIRECTORIES
00024 #include <libavcodec/avcodec.h>
00025 #include <libavformat/avformat.h>
00026 #include <libswscale/swscale.h>
00027 #else
00028 #include <ffmpeg/avcodec.h>
00029 #include <ffmpeg/avformat.h>
00030 #include <ffmpeg/swscale.h>
00031 #endif 
00032 }
00033 
00034 //--------------------------------------------------------------------------------
00035 
00036 struct vidl2_ffmpeg_istream::pimpl
00037 {
00038   pimpl()
00039   : fmt_cxt_( NULL ),
00040     vid_index_( -1 ),
00041     vid_str_( NULL ),
00042     last_dts( 0 ),
00043     frame_( NULL ),
00044     num_frames_( 0 ),
00045     sws_context_( NULL ),
00046     cur_frame_( NULL ),
00047     deinterlace_( false ),
00048     frame_number_offset_( 0 )
00049   {
00050   }
00051 
00052   AVFormatContext* fmt_cxt_;
00053   int vid_index_;
00054   AVStream* vid_str_;
00055 
00056   //: Decode time of last frame.
00057   int64_t last_dts;
00058 
00059   //: Start time of the stream, to offset the dts when computing the frame number.
00060   int64_t start_time;
00061 
00062   //: The last successfully read frame.
00063   //
00064   // If frame_->data[0] is not NULL, then the frame corresponds to
00065   // the codec state, so that codec.width and so on apply to the
00066   // frame data.
00067   AVFrame* frame_;
00068 
00069   //: number of counted frames
00070   int num_frames_;
00071 
00072   //: A software scaling context
00073   //
00074   // This is the context used for the software scaling and colour
00075   // conversion routines. Since the conversion is likely to be the
00076   // same for each frame, we save the context to avoid re-creating it
00077   // every time.
00078   SwsContext* sws_context_;
00079 
00080   //: A contiguous memory buffer to store the current image data
00081   vil_memory_chunk_sptr contig_memory_;
00082 
00083   //: The last successfully decoded frame.
00084   mutable vidl2_frame_sptr cur_frame_;
00085 
00086   //: Apply deinterlacing on the frames?
00087   bool deinterlace_;
00088 
00089   //: Some codec/file format combinations need a frame number offset.
00090   // These codecs have a delay between reading packets and generating frames.
00091   unsigned frame_number_offset_;
00092 };
00093 
00094 
00095 //--------------------------------------------------------------------------------
00096 
00097 //: Constructor
00098 vidl2_ffmpeg_istream::
00099 vidl2_ffmpeg_istream()
00100   : is_( new vidl2_ffmpeg_istream::pimpl )
00101 {
00102   vidl2_ffmpeg_init();
00103 }
00104 
00105 
00106 //: Constructor - from a filename
00107 vidl2_ffmpeg_istream::
00108 vidl2_ffmpeg_istream(const vcl_string& filename)
00109   : is_( new vidl2_ffmpeg_istream::pimpl )
00110 {
00111   vidl2_ffmpeg_init();
00112   open(filename);
00113 }
00114 
00115 
00116 //: Destructor
00117 vidl2_ffmpeg_istream::
00118 ~vidl2_ffmpeg_istream()
00119 {
00120   close();
00121   delete is_;
00122 }
00123 
00124 //: Open a new stream using a filename
00125 bool
00126 vidl2_ffmpeg_istream::
00127 open(const vcl_string& filename)
00128 {
00129   // Close any currently opened file
00130   close();
00131 
00132   // Open the file
00133   int err;
00134   if ( ( err = av_open_input_file( &is_->fmt_cxt_, filename.c_str(), NULL, 0, NULL ) ) != 0 ) {
00135     return false;
00136   }
00137 
00138   // Get the stream information by reading a bit of the file
00139   if ( av_find_stream_info( is_->fmt_cxt_ ) < 0 ) {
00140     return false;
00141   }
00142 
00143   // Find a video stream. Use the first one we find.
00144   is_->vid_index_ = -1;
00145   for ( unsigned i = 0; i < is_->fmt_cxt_->nb_streams; ++i ) {
00146     AVCodecContext *enc = is_->fmt_cxt_->streams[i]->codec;
00147     if ( enc->codec_type == CODEC_TYPE_VIDEO ) {
00148       is_->vid_index_ = i;
00149       break;
00150     }
00151   }
00152   if ( is_->vid_index_ == -1 ) {
00153     return false;
00154   }
00155 
00156   dump_format( is_->fmt_cxt_, 0, filename.c_str(), 0 );
00157   AVCodecContext *enc = is_->fmt_cxt_->streams[is_->vid_index_]->codec;
00158 
00159   // Open the stream
00160   AVCodec* codec = avcodec_find_decoder(enc->codec_id);
00161   if ( !codec || avcodec_open( enc, codec ) < 0 ) {
00162     return false;
00163   }
00164 
00165   is_->vid_str_ = is_->fmt_cxt_->streams[ is_->vid_index_ ];
00166   is_->frame_ = avcodec_alloc_frame();
00167 
00168   if ( is_->vid_str_->start_time == int64_t(1)<<63 ) {
00169     is_->start_time = 0;
00170   } else {
00171     is_->start_time = is_->vid_str_->start_time;
00172   }
00173 
00174 
00175   // The MPEG 2 codec has a latency of 1 frame when encoded in an AVI
00176   // stream, so the dts of the last packet (stored in last_dts) is
00177   // actually the next frame's dts.
00178   if ( is_->vid_str_->codec->codec_id == CODEC_ID_MPEG2VIDEO &&
00179        vcl_string("avi") == is_->fmt_cxt_->iformat->name ) {
00180     is_->frame_number_offset_ = 1;
00181   }
00182 
00183   is_->num_frames_ = 0;
00184   while(advance())
00185     ++is_->num_frames_;
00186 
00187   av_seek_frame( is_->fmt_cxt_, is_->vid_index_, 0, AVSEEK_FLAG_BACKWARD );
00188 
00189   return true;
00190 }
00191 
00192 
00193 //: Close the stream
00194 void
00195 vidl2_ffmpeg_istream::
00196 close()
00197 {
00198   if ( is_->frame_ ) {
00199     av_freep( &is_->frame_ );
00200   }
00201 
00202   is_->num_frames_ = 0;
00203   is_->contig_memory_ = 0;
00204   is_->vid_index_ = -1;
00205   if ( is_->vid_str_ ) {
00206     avcodec_close( is_->vid_str_->codec );
00207     is_->vid_str_ = 0;
00208   }
00209   if ( is_->fmt_cxt_ ) {
00210     av_close_input_file( is_->fmt_cxt_ );
00211     is_->fmt_cxt_ = 0;
00212   }
00213 }
00214 
00215 
00216 //: Return true if the stream is open for reading
00217 bool
00218 vidl2_ffmpeg_istream::
00219 is_open() const
00220 {
00221   return is_->frame_;
00222 }
00223 
00224 
00225 //: Return true if the stream is in a valid state
00226 bool
00227 vidl2_ffmpeg_istream::
00228 is_valid() const
00229 {
00230   return is_open() && is_->frame_->data[0] != 0;
00231 }
00232 
00233 
00234 //: Return true if the stream support seeking
00235 bool
00236 vidl2_ffmpeg_istream::
00237 is_seekable() const
00238 {
00239   return true;
00240 }
00241 
00242 
00243 //: Return the number of frames if known
00244 //  returns -1 for non-seekable streams
00245 int
00246 vidl2_ffmpeg_istream::num_frames() const
00247 {
00248   return is_->num_frames_;
00249 }
00250 
00251 
00252 //: Return the current frame number
00253 unsigned int
00254 vidl2_ffmpeg_istream::
00255 frame_number() const
00256 {
00257   // Quick return if the stream isn't open.
00258   if ( !is_valid() ) {
00259     return static_cast<unsigned int>(-1);
00260   }
00261 
00262   return ((is_->last_dts - is_->start_time)
00263       * is_->vid_str_->r_frame_rate.num / is_->vid_str_->r_frame_rate.den
00264       * is_->vid_str_->time_base.num + is_->vid_str_->time_base.den/2)
00265           / is_->vid_str_->time_base.den
00266       - is_->frame_number_offset_;
00267 }
00268 
00269 
00270 //: Return the width of each frame
00271 unsigned int
00272 vidl2_ffmpeg_istream
00273 ::width() const
00274 {
00275   // Quick return if the stream isn't open.
00276   if ( !is_open() ) {
00277     return 0;
00278   }
00279 
00280   return is_->fmt_cxt_->streams[is_->vid_index_]->codec->width;
00281 }
00282 
00283 
00284 //: Return the height of each frame
00285 unsigned int 
00286 vidl2_ffmpeg_istream
00287 ::height() const
00288 {
00289   // Quick return if the stream isn't open.
00290   if ( !is_open() ) {
00291     return 0;
00292   }
00293 
00294   return is_->fmt_cxt_->streams[is_->vid_index_]->codec->height;
00295 }
00296 
00297 
00298 //: Return the pixel format
00299 vidl2_pixel_format 
00300 vidl2_ffmpeg_istream
00301 ::format() const
00302 {
00303   // Quick return if the stream isn't open.
00304   if ( !is_open() ) {
00305     return VIDL2_PIXEL_FORMAT_UNKNOWN;
00306   }
00307 
00308   AVCodecContext* enc = is_->fmt_cxt_->streams[is_->vid_index_]->codec;
00309   vidl2_pixel_format fmt = vidl2_pixel_format_from_ffmpeg(enc->pix_fmt);
00310   if (fmt == VIDL2_PIXEL_FORMAT_UNKNOWN)
00311     return VIDL2_PIXEL_FORMAT_RGB_24;
00312   return fmt;
00313 }
00314 
00315 
00316 //: Return the frame rate (0.0 if unspecified)
00317 double 
00318 vidl2_ffmpeg_istream
00319 ::frame_rate() const
00320 {
00321   // Quick return if the stream isn't open.
00322   if ( !is_open() ) {
00323     return 0.0;
00324   }
00325 
00326   return static_cast<double>(is_->vid_str_->time_base.num)/is_->vid_str_->time_base.den
00327          * static_cast<double>(is_->vid_str_->r_frame_rate.num) / is_->vid_str_->r_frame_rate.den;
00328 }
00329 
00330 
00331 //: Advance to the next frame (but don't acquire an image)
00332 bool
00333 vidl2_ffmpeg_istream::
00334 advance()
00335 {
00336   // Quick return if the file isn't open.
00337   if ( !is_open() ) {
00338     return false;
00339   }
00340 
00341   AVCodecContext* codec = is_->fmt_cxt_->streams[is_->vid_index_]->codec;
00342 
00343   AVPacket pkt;
00344   int got_picture = 0;
00345 
00346   while ( got_picture == 0 ) {
00347     if ( av_read_frame( is_->fmt_cxt_, &pkt ) < 0 ) {
00348       break;
00349     }
00350     is_->last_dts = pkt.dts;
00351 
00352     // Make sure that the packet is from the actual video stream.
00353     if (pkt.stream_index==is_->vid_index_)
00354     {
00355       if ( avcodec_decode_video( codec,
00356                                  is_->frame_, &got_picture,
00357                                  pkt.data, pkt.size ) < 0 ) {
00358         vcl_cerr << "vidl2_ffmpeg_istream: Error decoding packet!\n";
00359         return false;
00360       }
00361     }
00362     av_free_packet( &pkt );
00363   }
00364 
00365   // From ffmpeg apiexample.c: some codecs, such as MPEG, transmit the
00366   // I and P frame with a latency of one frame. You must do the
00367   // following to have a chance to get the last frame of the video.
00368   if ( !got_picture ) {
00369     if ( avcodec_decode_video( codec,
00370                                is_->frame_, &got_picture,
00371                                NULL, 0 ) >= 0 ) {
00372       is_->last_dts += int64_t(is_->vid_str_->time_base.den) * is_->vid_str_->r_frame_rate.den
00373         / is_->vid_str_->time_base.num / is_->vid_str_->r_frame_rate.num;
00374     }
00375   }
00376 
00377   // The cached frame is out of date, whether we managed to get a new
00378   // frame or not.
00379   if (is_->cur_frame_)
00380     is_->cur_frame_->invalidate();
00381   is_->cur_frame_ = 0;
00382 
00383   if ( ! got_picture ) {
00384     is_->frame_->data[0] = NULL;
00385   }
00386 
00387   return got_picture != 0;
00388 }
00389 
00390 
00391 //: Read the next frame from the stream
00392 vidl2_frame_sptr
00393 vidl2_ffmpeg_istream::read_frame()
00394 {
00395   if (advance())
00396     return current_frame();
00397   return NULL;
00398 }
00399 
00400 
00401 //: Return the current frame in the stream
00402 vidl2_frame_sptr
00403 vidl2_ffmpeg_istream::current_frame()
00404 {
00405   // Quick return if the stream isn't valid
00406   if ( !is_valid() ) {
00407     return NULL;
00408   }
00409   AVCodecContext* enc = is_->fmt_cxt_->streams[is_->vid_index_]->codec;
00410   // If we have not already converted this frame, try to convert it
00411   if ( !is_->cur_frame_ && is_->frame_->data[0] != 0 )
00412   {
00413     int width = enc->width;
00414     int height = enc->height;
00415 
00416     // Deinterlace if requested
00417     if ( is_->deinterlace_ ) {
00418       avpicture_deinterlace( (AVPicture*)is_->frame_, (AVPicture*)is_->frame_,
00419                              enc->pix_fmt, width, height );
00420     }
00421 
00422     // If the pixel format is not recognized by vidl2 then convert the data into RGB_24
00423     vidl2_pixel_format fmt = vidl2_pixel_format_from_ffmpeg(enc->pix_fmt);
00424     if (fmt == VIDL2_PIXEL_FORMAT_UNKNOWN)
00425     {
00426       int size = width*height*3;
00427       if (!is_->contig_memory_)
00428         is_->contig_memory_ = new vil_memory_chunk(size, VIL_PIXEL_FORMAT_BYTE);
00429       else
00430         is_->contig_memory_->set_size(size, VIL_PIXEL_FORMAT_BYTE);
00431 
00432       // Reuse the previous context if we can.
00433       is_->sws_context_ = sws_getCachedContext(
00434         is_->sws_context_,
00435         width, height, enc->pix_fmt,
00436         width, height, PIX_FMT_RGB24,
00437         SWS_BILINEAR,
00438         NULL, NULL, NULL );
00439 
00440       if ( is_->sws_context_ == NULL ) {
00441         vcl_cerr << "vidl2_ffmpeg_istream: couldn't create conversion context\n";
00442         return vidl2_frame_sptr();
00443       }
00444 
00445       AVPicture rgb_frame;
00446       avpicture_fill(&rgb_frame, (uint8_t*)is_->contig_memory_->data(), PIX_FMT_RGB24, width, height);
00447 
00448       sws_scale( is_->sws_context_,
00449                  is_->frame_->data, is_->frame_->linesize,
00450                  0, height,
00451                  rgb_frame.data, rgb_frame.linesize );
00452 
00453       is_->cur_frame_ = new vidl2_shared_frame(is_->contig_memory_->data(),width,height,
00454                                                VIDL2_PIXEL_FORMAT_RGB_24);
00455     }
00456     else
00457     {
00458       // Test for contiguous memory.  Sometimes FFMPEG uses scanline buffers larger
00459       // than the image width.  The extra memory is used in optimized decoding routines.
00460       // This leads to a segmented image buffer, not supported by vidl2.
00461       AVPicture test_frame;
00462       avpicture_fill(&test_frame, is_->frame_->data[0], enc->pix_fmt, width, height);
00463       if (test_frame.data[1] == is_->frame_->data[1] &&
00464          test_frame.data[2] == is_->frame_->data[2] &&
00465          test_frame.linesize[0] == is_->frame_->linesize[0] &&
00466          test_frame.linesize[1] == is_->frame_->linesize[1] &&
00467          test_frame.linesize[2] == is_->frame_->linesize[2] )
00468       {
00469         is_->cur_frame_ = new vidl2_shared_frame(is_->frame_->data[0], width, height, fmt);
00470       }
00471       // Copy the image into contiguous memory.
00472       else
00473       {
00474         if (!is_->contig_memory_){
00475           int size = avpicture_get_size( enc->pix_fmt, width, height );
00476           is_->contig_memory_ = new vil_memory_chunk(size, VIL_PIXEL_FORMAT_BYTE);
00477         }
00478         avpicture_fill(&test_frame, (uint8_t*)is_->contig_memory_->data(), enc->pix_fmt, width, height);
00479         av_picture_copy(&test_frame, (AVPicture*)is_->frame_, enc->pix_fmt, width, height);
00480         // use a shared frame because the vil_memory_chunk is reused for each frame
00481         is_->cur_frame_ = new vidl2_shared_frame(is_->contig_memory_->data(),width,height,fmt);
00482       }
00483     }
00484   }
00485 
00486   return is_->cur_frame_;
00487 }
00488 
00489 
00490 //: Seek to the given frame number
00491 // \returns true if successful
00492 bool
00493 vidl2_ffmpeg_istream::
00494 seek_frame(unsigned int frame)
00495 {
00496   // Quick return if the stream isn't open.
00497   if ( !is_open() ) {
00498     return false;
00499   }
00500 
00501   // We rely on the initial cast to make sure all the operations happen in int64.
00502   int64_t req_timestamp =
00503     int64_t(frame + is_->frame_number_offset_)
00504     * is_->vid_str_->time_base.den
00505     * is_->vid_str_->r_frame_rate.den
00506     / is_->vid_str_->time_base.num
00507     / is_->vid_str_->r_frame_rate.num
00508     + is_->start_time;
00509 
00510   // Seek to a keyframe before the timestamp that we want.
00511   int seek = av_seek_frame( is_->fmt_cxt_, is_->vid_index_, req_timestamp, AVSEEK_FLAG_BACKWARD );
00512 
00513   if ( seek < 0 )
00514     return false;
00515 
00516   avcodec_flush_buffers( is_->vid_str_->codec );
00517 
00518   // We got to a key frame. Forward until we get to the frame we want.
00519   while ( true )
00520   {
00521     if ( ! advance() ) {
00522       return false;
00523     }
00524     if ( is_->last_dts >= req_timestamp ) {
00525       if ( is_->last_dts > req_timestamp ) {
00526         vcl_cerr << "Warning: seek went into the future!\n";
00527         return false;
00528       }
00529       return true;
00530     }
00531   }
00532 }
00533 
00534 #endif // vidl2_ffmpeg_istream_v2_txx_

Generated on Wed Dec 3 05:21:03 2008 for contrib/brl/bbas/vidl2 by  doxygen 1.5.1