TuttleOFX  1
LibAVVideoReader.cpp
Go to the documentation of this file.
00001 #include "LibAVVideoReader.hpp"
00002 
00003 #include <tuttle/plugin/global.hpp>
00004 
00005 #include <boost/cstdint.hpp>
00006 #include <boost/numeric/conversion/cast.hpp>
00007 #include <boost/filesystem.hpp>
00008 
00009 #include <limits>
00010 
00011 namespace tuttle {
00012 namespace plugin {
00013 namespace av {
00014 
00015 namespace fs = boost::filesystem;
00016 
00017 LibAVVideoReader::LibAVVideoReader()
00018         : _avFormatOptions( NULL )
00019         , _stream ( NULL )
00020         , _format( NULL )
00021         , _avFrame( NULL )
00022         , _videoCodec( NULL )
00023         , _sws_context( NULL )
00024         , _fpsNum( 0 )
00025         , _fpsDen( 0 )
00026         , _currVideoIdx( -1 )
00027         , _nbFrames( 0 )
00028         , _width( 0 )
00029         , _height( 0 )
00030         , _aspect( 1.0 )
00031         , _offsetTime( true )
00032         , _lastSearchPos( -1 )
00033         , _lastDecodedPos( -1 )
00034         , _lastDecodedFrame( -1 )
00035         , _isOpen( false )
00036 {
00037 //      for( int i = 0; i < AVMEDIA_TYPE_NB; ++i )
00038 //      {
00039 //              _avctxOptions[i] = avcodec_alloc_context3( avcodec_find_encoder( i ) );
00040 //      }
00041         _avFormatOptions = avformat_alloc_context();
00042         _avFrame         = avcodec_alloc_frame();
00043 
00044 }
00045 
00046 LibAVVideoReader::~LibAVVideoReader()
00047 {
00048         close();
00049 
00050         av_free( _avFrame );
00051 //      for( int i = 0; i < AVMEDIA_TYPE_NB; ++i )
00052 //      {
00053 //              av_free( _avctxOptions[i] );
00054 //      }
00055         av_free( _avFormatOptions );
00056 
00057 }
00058 
00059 bool LibAVVideoReader::open( const std::string& filename )
00060 {
00061         if( isOpen() )
00062         {
00063                 close();
00064         }
00065 
00066         _isOpen = 0;
00067 
00068 #if LIBAVCODEC_VERSION_MAJOR <= 52
00069         int error = av_open_input_file( &_avFormatOptions, filename.c_str(), _format, 0, 0 );
00070 #else
00071         int error = avformat_open_input( &_avFormatOptions, filename.c_str(), NULL, NULL );
00072 #endif
00073         if( error < 0 )
00074         {
00075                 std::cerr << "avReader: " << libavError_toString( error ) << std::endl;
00076                 _isOpen = 0;
00077                 return false;
00078         }
00079         // FIXME_GC: needs to know if it's streamable.
00080         error = avformat_find_stream_info( _avFormatOptions, NULL );
00081         if( error < 0 )
00082         {
00083                 std::cerr << "avReader: " << libavError_toString( error ) << std::endl;
00084                 return false;
00085         }
00086 
00087         if( !setupStreamInfo() )
00088         {
00089                 std::cerr << "avReader: Unable to find codec." << std::endl;
00090                 return false;
00091         }
00092 
00093         _stream = getVideoStream();
00094         
00095         AVCodecContext* codecContext = _stream->codec;
00096         
00097         if( getVideoStream()->sample_aspect_ratio.num )
00098         {
00099                 _aspect = av_q2d( getVideoStream()->sample_aspect_ratio );
00100         }
00101         else if( codecContext->sample_aspect_ratio.num )
00102         {
00103                 _aspect = av_q2d( codecContext->sample_aspect_ratio );
00104         }
00105         _bitRate = codecContext->bit_rate;
00106 
00107         _data.resize( width() * height() * 3 );
00108 
00109         // hack so seeking works from our intended position.
00110         if( !strcmp( codecContext->codec->name, "mjpeg" ) || !strcmp( codecContext->codec->name, "dvvideo" ) )
00111         {
00112                 std::cerr << "avReader: [WARNING] codec is using a specific offsetTime" << codecContext->codec->name << std::endl;
00113                 _offsetTime = false;
00114         }
00115 
00116         /*
00117             std::cout << "MetaData::CREATOR: " << _context->author << std::endl;
00118             std::cout << "MetaData::COPYRIGHT: " << _context->copyright << std::endl;
00119             std::cout << "MetaData::COMMENT: " << _context->comment << std::endl;
00120             std::cout << "MetaData::PROJECT: " << _context->album << std::endl;
00121             std::cout << "MetaData::FILE_CREATION_TIME: " << double(_context->timestamp ) << std::endl;
00122             std::cout << "libav/num_streams: " << _context->nb_streams << std::endl;
00123             std::cout << "MetaData::FRAME_RATE: " << fps( ) << std::endl;
00124             std::cout << "libav/codec/codecName: " << codecContext->codec->name << std::endl;
00125 
00126             meta.setData(MetaData::CREATOR, context_->author);
00127             meta.setData(MetaData::COPYRIGHT, context_->copyright);
00128             meta.setData(MetaData::COMMENT, context_->comment);
00129             meta.setData(MetaData::PROJECT, context_->album);
00130             meta.setData(MetaData::FILE_CREATION_TIME, double(context_->timestamp));
00131             meta.setData("libav/num_streams", context_->nb_streams);
00132             meta.setData(MetaData::FRAME_RATE, fps());
00133             meta.setData("libav/codec/codecName", codecContext->codec->name);
00134          */
00135 
00136         _isOpen = 1;
00137         return true;
00138 }
00139 
00140 void LibAVVideoReader::close()
00141 {
00142         _isOpen = false;
00143         closeVideoCodec();
00144         if( _avFormatOptions )
00145         {
00146 #if LIBAVCODEC_VERSION_MAJOR <= 52
00147                 av_close_input_file( _avFormatOptions );
00148 #else
00149                 avformat_close_input( &_avFormatOptions );
00150 #endif
00151                 _avFormatOptions = NULL;
00152         }
00153 }
00154 
00155 bool LibAVVideoReader::read( const int frame )
00156 {
00157         const int frameNumber = frame % _nbFrames;
00158 
00159         if( frameNumber != frame )
00160         {
00161                 std::cerr << "Read outside the video range (time:" << frame << ", video size:" << _nbFrames << std::endl;
00162         }
00163         if( _lastDecodedFrame + 1 != frameNumber )
00164         {
00165                 seek( 0 );
00166                 seek( frameNumber );
00167         }
00168 
00169         av_init_packet( &_pkt );
00170 
00171         bool hasPicture = false;
00172         int error       = 0;
00173         //      int i = 0;
00174         while( error >= 0 && !hasPicture )
00175         {
00176                 error = av_read_frame( _avFormatOptions, &_pkt );
00177                 // on error or end of file
00178                 if( error < 0 && error != AVERROR_EOF )
00179                 {
00180                         return false;
00181                 }
00182 
00183                 if( _videoIdx.size() && _currVideoIdx != -1 && _pkt.stream_index == _videoIdx[_currVideoIdx] )
00184                 {
00185                         hasPicture = decodeImage( frameNumber );
00186                 }
00187 
00188                 av_free_packet( &_pkt );
00189         }
00190 
00191         _lastDecodedFrame = frameNumber;
00192 
00193         return true;
00194 }
00195 
00196 bool LibAVVideoReader::setupStreamInfo()
00197 {
00198         _currVideoIdx = -1;
00199         for( std::size_t i = 0; i < _avFormatOptions->nb_streams; ++i )
00200         {
00201                 AVCodecContext* codecContext = _avFormatOptions->streams[i]->codec;
00202                 if( codecContext->codec_id == AV_CODEC_ID_NONE )
00203                 {
00204                         std::cerr << "avReader: Can't find decoder codec_id: CODEC_ID_NONE codecType:" << codecType_toString( codecContext->codec_type ) << std::endl;
00205                         continue;
00206                 }
00207                 if( avcodec_find_decoder( codecContext->codec_id ) == NULL )
00208                 {
00209                         std::cerr << "avReader: Can't find decoder codec_id: " << codecContext->codec_id << " codecType:" << codecType_toString( codecContext->codec_type ) << std::endl;
00210                         continue;
00211                 }
00212 
00213                 switch( codecContext->codec_type )
00214                 {
00215                         case AVMEDIA_TYPE_VIDEO:
00216                         {
00217                                 _videoIdx.push_back( i );
00218                                 if( _currVideoIdx < 0 )
00219                                 {
00220                                         _currVideoIdx = 0;
00221                                 }
00222                                 _width  = codecContext->width;
00223                                 _height = codecContext->height;
00224                                 break;
00225                         }
00226                         // ignore all audio streams
00227                         case AVMEDIA_TYPE_AUDIO:
00228                         case AVMEDIA_TYPE_UNKNOWN:
00229                         case AVMEDIA_TYPE_DATA:
00230                         case AVMEDIA_TYPE_SUBTITLE:
00231                         case AVMEDIA_TYPE_ATTACHMENT:
00232                         default:
00233                                 break;
00234                 }
00235         }
00236 
00237         if( !hasVideo() )
00238         {
00239                 return false;
00240         }
00241 
00242         AVStream* stream = getVideoStream();
00243         if( !stream )
00244         {
00245                 return false;
00246         }
00247         if( stream->codec->coded_frame && stream->codec->coded_frame->interlaced_frame )
00248         {
00249                 if( stream->codec->coded_frame->top_field_first )
00250                 {
00251                         _interlacment = eInterlacmentUpper;
00252                 }
00253                 else
00254                 {
00255                         _interlacment = eInterlacmentLower;
00256                 }
00257         }
00258         else
00259         {
00260                 _interlacment = eInterlacmentNone;
00261         }
00262 
00263         if( stream->avg_frame_rate.num != 0 && stream->avg_frame_rate.den != 0 )
00264         {
00265                 TUTTLE_TLOG( TUTTLE_INFO, "fps " << stream->avg_frame_rate.num << " / " << stream->avg_frame_rate.den );
00266                 _fpsNum = stream->avg_frame_rate.num;
00267                 _fpsDen = stream->avg_frame_rate.den;
00268         }
00269 
00270         openVideoCodec();
00271 
00272         // Set the duration
00273         if( _avFormatOptions->duration > 0 )
00274         {
00275                 _nbFrames = boost::numeric_cast<boost::uint64_t>( ( fps() * (double) _avFormatOptions->duration / (double) AV_TIME_BASE ) );
00276         }
00277         else
00278         {
00279                 _nbFrames = 1 << 29;
00280         }
00281 
00282         // try to calculate the number of frames
00283         if( !_nbFrames )
00284         {
00285                 seek( 0 );
00286                 av_init_packet( &_pkt );
00287                 av_read_frame( _avFormatOptions, &_pkt );
00288                 boost::uint64_t firstPts = _pkt.pts;
00289                 boost::uint64_t maxPts   = firstPts;
00290                 seek( 1 << 29 );
00291                 av_init_packet( &_pkt );
00292                 while( stream && av_read_frame( _avFormatOptions, &_pkt ) >= 0 )
00293                 {
00294                         boost::uint64_t currPts = boost::numeric_cast<boost::uint64_t>( av_q2d( getVideoStream()->time_base ) * ( _pkt.pts - firstPts ) * fps() );
00295                         if( currPts > maxPts )
00296                                 maxPts = currPts;
00297                 }
00298 
00299                 _nbFrames = maxPts;
00300         }
00301 
00302         // nb_frames will be set if that information is present in the container,
00303         // otherwise it will be zero.
00304         if ( stream->nb_frames )
00305         {
00306                 if ( (int64_t)_nbFrames != stream->nb_frames )
00307                 {
00308                         std::cerr << "Warning: calculated number of frames ("   <<
00309                         _nbFrames << ") does not match container information (" <<
00310                         stream->nb_frames << ")" << std::endl;
00311                 }
00312         }
00313 
00314         return true;
00315 }
00316 
00317 void LibAVVideoReader::openVideoCodec()
00318 {
00319         AVStream* stream = getVideoStream();
00320 
00321         if( stream )
00322         {
00323                 AVCodecContext* codecContext = stream->codec;
00324                 _videoCodec = avcodec_find_decoder( codecContext->codec_id );
00325                 if( _videoCodec == NULL || avcodec_open2( codecContext, _videoCodec, NULL ) < 0 )
00326                 {
00327                         _currVideoIdx = -1;
00328                 }
00329         }
00330         else
00331         {
00332                 _currVideoIdx = -1;
00333         }
00334 }
00335 
00336 void LibAVVideoReader::closeVideoCodec()
00337 {
00338         AVStream* stream = getVideoStream();
00339 
00340         if( stream && stream->codec )
00341                 avcodec_close( stream->codec );
00342 }
00343 
00344 boost::int64_t LibAVVideoReader::getTimeStamp( int pos ) const
00345 {
00346         boost::int64_t timestamp = boost::numeric_cast<boost::int64_t>( ( (double) pos / fps() ) * AV_TIME_BASE );
00347 
00348         if( (int)_avFormatOptions->start_time != AV_NOPTS_VALUE )
00349                 timestamp += _avFormatOptions->start_time;
00350         return timestamp;
00351 }
00352 
00353 bool LibAVVideoReader::seek( const std::size_t pos )
00354 {
00355         boost::int64_t offset = getTimeStamp( pos );
00356 
00357         if( _offsetTime )
00358         {
00359                 offset -= AV_TIME_BASE;
00360                 if( offset < _avFormatOptions->start_time )
00361                         offset = 0;
00362         }
00363 
00364         AVStream* stream = getVideoStream();
00365         if( !stream )
00366                 return false;
00367 
00368         avcodec_flush_buffers( stream->codec );
00369         if( av_seek_frame( _avFormatOptions, -1, offset, AVSEEK_FLAG_BACKWARD ) < 0 )
00370         {
00371                 return false;
00372         }
00373 
00374         return true;
00375 }
00376 
00377 bool LibAVVideoReader::decodeImage( const int frame )
00378 {
00379         // search for our picture.
00380         double pts = 0;
00381 
00382         if( _pkt.dts != (int64_t)AV_NOPTS_VALUE )
00383         {
00384                 AVStream* stream = getVideoStream();
00385                 if( stream )
00386                 {
00387                         pts = av_q2d( stream->time_base ) * _pkt.dts;
00388                 }
00389         }
00390 
00391         int curPos = int(pts * fps() + 0.5f);
00392         if( curPos == _lastSearchPos )
00393                 curPos = _lastSearchPos + 1;
00394         _lastSearchPos = curPos;
00395 
00396         if( _avFormatOptions->start_time != (int64_t)AV_NOPTS_VALUE )
00397                 curPos -= int(_avFormatOptions->start_time * fps() / AV_TIME_BASE);
00398 
00399         int hasPicture   = 0;
00400         int curSearch    = 0;
00401         AVStream* stream = getVideoStream();
00402         if( !stream )
00403                 return false;
00404 
00405         AVCodecContext* codecContext = stream->codec;
00406         if( curPos >= frame )
00407         {
00408                 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT( 52, 21, 0 )
00409                 avcodec_decode_video2( codecContext, _avFrame, &hasPicture, &_pkt );
00410                 #else
00411                 avcodec_decode_video( codecContext, _avFrame, &hasPicture, _pkt.data, _pkt.size );
00412                 #endif
00413         }
00414         else if( _offsetTime )
00415         {
00416                 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT( 52, 21, 0 )
00417                 avcodec_decode_video2( codecContext, _avFrame, &curSearch, &_pkt );
00418                 #else
00419                 avcodec_decode_video( codecContext, _avFrame, &curSearch, _pkt.data, _pkt.size );
00420                 #endif
00421         }
00422 
00423         if( !hasPicture )
00424         {
00425                 return false;
00426         }
00427 
00428         _lastDecodedPos = _lastSearchPos;
00429 
00430         AVPicture output;
00431         avpicture_fill( &output, &_data[0], PIX_FMT_RGB24, _width, _height );
00432 
00433         // patched to use swscale instead of img_convert:
00434         PixelFormat in_pixelFormat  = codecContext->pix_fmt; // pixel format source
00435         PixelFormat out_pixelFormat = PIX_FMT_RGB24; // pixel format dest
00436 
00437         _sws_context = sws_getCachedContext( _sws_context, width(), height(), in_pixelFormat, width(), height(), out_pixelFormat, SWS_BICUBIC, NULL, NULL, NULL );
00438 
00439         if( !_sws_context )
00440         {
00441                 std::cerr << "avReader: libav-conversion failed (" << in_pixelFormat << "->" << out_pixelFormat << ")" << std::endl;
00442                 return false;
00443         }
00444         int error = sws_scale( _sws_context, _avFrame->data, _avFrame->linesize, 0, height(), output.data, output.linesize );
00445         if( error < 0 )
00446         {
00447                 std::cerr << "avReader: libav-conversion failed (" << in_pixelFormat << "->" << out_pixelFormat << ")" << std::endl;
00448                 return false;
00449         }
00450 
00451         // std::cout << "decodeImage " << frame << " OK" << std::endl;
00452         return true;
00453 }
00454 
00455 }
00456 }
00457 }