TuttleOFX
1
|
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 }