@ -11,35 +11,42 @@ int readFunction(void *opaque, uint8_t *buf, int buf_size) {
return iss . gcount ( ) ? iss . gcount ( ) : AVERROR_EOF ;
}
enum AVPixelFormat get_hw_format ( AVCodecContext * ctx , const enum AVPixelFormat * pix_fmts ) {
enum AVPixelFormat * hw_pix_fmt = reinterpret_cast < enum AVPixelFormat * > ( ctx - > opaque ) ;
for ( const enum AVPixelFormat * p = pix_fmts ; * p ! = - 1 ; p + + ) {
if ( * p = = * hw_pix_fmt ) return * p ;
}
printf ( " Please run replay with the --no-cuda flag! \n " ) ;
assert ( 0 ) ;
return AV_PIX_FMT_NONE ;
}
} // namespace
FrameReader : : FrameReader ( bool local_cache , int chunk_size , int retries ) : FileReader ( local_cache , chunk_size , retries ) {
pFormatCtx_ = avformat_alloc_context ( ) ;
av_frame_ = av_frame_alloc ( ) ;
rgb_frame_ = av_frame_alloc ( ) ;
yuv_frame_ = av_frame_alloc ( ) ;
input_ctx = avformat_alloc_context ( ) ;
sws_frame . reset ( av_frame_alloc ( ) ) ;
}
FrameReader : : ~ FrameReader ( ) {
for ( auto & f : frames_ ) {
av_free_ packet ( & f . pkt ) ;
av_packet_unref ( & f . pkt ) ;
}
if ( pCodecCtx_ ) avcodec_free_context ( & pCodecCtx_ ) ;
if ( pFormatCtx_ ) avformat_close_input ( & pFormatCtx_ ) ;
if ( av_frame_ ) av_frame_free ( & av_frame_ ) ;
if ( rgb_frame_ ) av_frame_free ( & rgb_frame_ ) ;
if ( yuv_frame_ ) av_frame_free ( & yuv_frame_ ) ;
if ( decoder_ctx ) avcodec_free_context ( & decoder_ctx ) ;
if ( input_ctx ) avformat_close_input ( & input_ctx ) ;
if ( hw_device_ctx ) av_buffer_unref ( & hw_device_ctx ) ;
if ( rgb_sws_ctx_ ) sws_freeContext ( rgb_sws_ctx_ ) ;
if ( yuv_sws_ctx_ ) sws_freeContext ( yuv_sws_ctx_ ) ;
if ( avio_ctx_ ) {
av_freep ( & avio_ctx_ - > buffer ) ;
av_freep ( & avio_ctx_ ) ;
avio_context _free ( & avio_ctx_ ) ;
}
}
bool FrameReader : : load ( const std : : string & url , std : : atomic < bool > * abort ) {
bool FrameReader : : load ( const std : : string & url , bool no_cuda , std : : atomic < bool > * abort ) {
std : : string content = read ( url , abort ) ;
if ( content . empty ( ) ) return false ;
@ -47,48 +54,56 @@ bool FrameReader::load(const std::string &url, std::atomic<bool> *abort) {
const int avio_ctx_buffer_size = 64 * 1024 ;
unsigned char * avio_ctx_buffer = ( unsigned char * ) av_malloc ( avio_ctx_buffer_size ) ;
avio_ctx_ = avio_alloc_context ( avio_ctx_buffer , avio_ctx_buffer_size , 0 , & iss , readFunction , nullptr , nullptr ) ;
pFormatCtx_ - > pb = avio_ctx_ ;
input_ctx - > pb = avio_ctx_ ;
pFormatCtx_ - > probesize = 10 * 1024 * 1024 ; // 10MB
int ret = avformat_open_input ( & pFormatCtx_ , url . c_str ( ) , NULL , NULL ) ;
input_ctx - > probesize = 10 * 1024 * 1024 ; // 10MB
int ret = avformat_open_input ( & input_ctx , url . c_str ( ) , NULL , NULL ) ;
if ( ret ! = 0 ) {
char err_str [ 1024 ] = { 0 } ;
av_strerror ( ret , err_str , std : : size ( err_str ) ) ;
printf ( " Error loading video - %s - %s \n " , err_str , url . c_str ( ) ) ;
return false ;
}
avformat_find_stream_info ( pFormatCtx_ , NULL ) ;
// av_dump_format(pFormatCtx_, 0, url.c_str(), 0);
AVStream * video = pFormatCtx_ - > streams [ 0 ] ;
auto pCodec = avcodec_find_decoder ( video - > codec - > codec_id ) ;
if ( ! pCodec ) return false ;
ret = avformat_find_stream_info ( input_ctx , nullptr ) ;
if ( ret < 0 ) {
printf ( " cannot find a video stream in the input file \n " ) ;
return false ;
}
AVStream * video = input_ctx - > streams [ 0 ] ;
AVCodec * decoder = avcodec_find_decoder ( video - > codec - > codec_id ) ;
if ( ! decoder ) return false ;
pCodecCtx_ = avcodec_alloc_context3 ( pCodec ) ;
ret = avcodec_parameters_to_context ( pCodecCtx_ , video - > codecpar ) ;
decoder_ctx = avcodec_alloc_context3 ( decoder ) ;
ret = avcodec_parameters_to_context ( decoder_ctx , video - > codecpar ) ;
if ( ret ! = 0 ) return false ;
// pCodecCtx_->thread_count = 0;
// pCodecCtx_->thread_type = FF_THREAD_FRAME;
ret = avcodec_open2 ( pCodecCtx_ , pCodec , NULL ) ;
if ( ret < 0 ) return false ;
width = ( decoder_ctx - > width + 3 ) & ~ 3 ;
height = decoder_ctx - > height ;
width = ( pCodecCtx_ - > width + 3 ) & ~ 3 ;
height = pCodecCtx_ - > height ;
rgb_sws_ctx_ = sws_getContext ( pCodecCtx_ - > width , pCodecCtx_ - > height , AV_PIX_FMT_YUV420P ,
width , height , AV_PIX_FMT_BGR24 ,
SWS_FAST_BILINEAR , NULL , NULL , NULL ) ;
if ( ! rgb_sws_ctx_ ) return false ;
if ( ! no_cuda ) {
if ( ! initHardwareDecoder ( AV_HWDEVICE_TYPE_CUDA ) ) {
printf ( " No CUDA capable device was found. fallback to CPU decoding. \n " ) ;
}
}
yuv_sws_ctx_ = sws_getContext ( pCodecCtx_ - > width , pCodecCtx_ - > height , AV_PIX_FMT_YUV420P ,
width , height , AV_PIX_FMT_YUV420P ,
SWS_FAST_BILINEAR , NULL , NULL , NULL ) ;
rgb_sws_ctx_ = sws_getContext ( decoder_ctx - > width , decoder_ctx - > height , sws_src_format ,
width , height , AV_PIX_FMT_BGR24 ,
SWS_BILINEAR , NULL , NULL , NULL ) ;
if ( ! rgb_sws_ctx_ ) return false ;
yuv_sws_ctx_ = sws_getContext ( decoder_ctx - > width , decoder_ctx - > height , sws_src_format ,
width , height , AV_PIX_FMT_YUV420P ,
SWS_BILINEAR , NULL , NULL , NULL ) ;
if ( ! yuv_sws_ctx_ ) return false ;
ret = avcodec_open2 ( decoder_ctx , decoder , NULL ) ;
if ( ret < 0 ) return false ;
frames_ . reserve ( 60 * 20 ) ; // 20fps, one minute
while ( ! ( abort & & * abort ) ) {
Frame & frame = frames_ . emplace_back ( ) ;
ret = av_read_frame ( pFormatCtx_ , & frame . pkt ) ;
ret = av_read_frame ( input_ctx , & frame . pkt ) ;
if ( ret < 0 ) {
frames_ . pop_back ( ) ;
valid_ = ( ret = = AVERROR_EOF ) ;
@ -100,6 +115,43 @@ bool FrameReader::load(const std::string &url, std::atomic<bool> *abort) {
return valid_ ;
}
bool FrameReader : : initHardwareDecoder ( AVHWDeviceType hw_device_type ) {
for ( int i = 0 ; ; i + + ) {
const AVCodecHWConfig * config = avcodec_get_hw_config ( decoder_ctx - > codec , i ) ;
if ( ! config ) {
printf ( " decoder %s does not support hw device type %s. \n " ,
decoder_ctx - > codec - > name , av_hwdevice_get_type_name ( hw_device_type ) ) ;
return false ;
}
if ( config - > methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX & & config - > device_type = = hw_device_type ) {
hw_pix_fmt = config - > pix_fmt ;
break ;
}
}
int ret = av_hwdevice_ctx_create ( & hw_device_ctx , hw_device_type , nullptr , nullptr , 0 ) ;
if ( ret < 0 ) {
printf ( " Failed to create specified HW device %d. \n " , ret ) ;
return false ;
}
// get sws source format
AVHWFramesConstraints * hw_frames_const = av_hwdevice_get_hwframe_constraints ( hw_device_ctx , nullptr ) ;
assert ( hw_frames_const ! = 0 ) ;
for ( AVPixelFormat * p = hw_frames_const - > valid_sw_formats ; * p ! = AV_PIX_FMT_NONE ; p + + ) {
if ( sws_isSupportedInput ( * p ) ) {
sws_src_format = * p ;
break ;
}
}
av_hwframe_constraints_free ( & hw_frames_const ) ;
decoder_ctx - > hw_device_ctx = av_buffer_ref ( hw_device_ctx ) ;
decoder_ctx - > opaque = & hw_pix_fmt ;
decoder_ctx - > get_format = get_hw_format ;
return true ;
}
bool FrameReader : : get ( int idx , uint8_t * rgb , uint8_t * yuv ) {
assert ( rgb | | yuv ) ;
if ( ! valid_ | | idx < 0 | | idx > = frames_ . size ( ) ) {
@ -125,35 +177,51 @@ bool FrameReader::decode(int idx, uint8_t *rgb, uint8_t *yuv) {
for ( int i = from_idx ; i < = idx ; + + i ) {
Frame & frame = frames_ [ i ] ;
if ( ( ! frame . decoded | | i = = idx ) & & ! frame . failed ) {
frame . decoded = decodeFrame ( & frame . pkt ) ;
AVFrame * f = decodeFrame ( & frame . pkt ) ;
frame . decoded = f ! = nullptr ;
frame . failed = ! frame . decoded ;
if ( frame . decoded & & i = = idx ) {
return copyBuffers ( av_ frame_ , rgb , yuv ) ;
return copyBuffers ( f , rgb , yuv ) ;
}
}
}
return false ;
}
bool FrameReader : : decodeFrame ( AVPacket * pkt ) {
int ret = avcodec_send_packet ( pCodecCtx_ , pkt ) ;
AVFrame * FrameReader : : decodeFrame ( AVPacket * pkt ) {
int ret = avcodec_send_packet ( decoder_ctx , pkt ) ;
if ( ret < 0 ) {
printf ( " Error sending a packet for decoding \n " ) ;
return false ;
return nullptr ;
}
av_frame_ . reset ( av_frame_alloc ( ) ) ;
ret = avcodec_receive_frame ( decoder_ctx , av_frame_ . get ( ) ) ;
if ( ret ! = 0 ) {
return nullptr ;
}
if ( av_frame_ - > format = = hw_pix_fmt ) {
hw_frame . reset ( av_frame_alloc ( ) ) ;
if ( ( ret = av_hwframe_transfer_data ( hw_frame . get ( ) , av_frame_ . get ( ) , 0 ) ) < 0 ) {
printf ( " error transferring the data from GPU to CPU \n " ) ;
return nullptr ;
}
return hw_frame . get ( ) ;
} else {
return av_frame_ . get ( ) ;
}
ret = avcodec_receive_frame ( pCodecCtx_ , av_frame_ ) ;
return ret = = 0 ;
}
bool FrameReader : : copyBuffers ( AVFrame * f , uint8_t * rgb , uint8_t * yuv ) {
// images is going to be written to output buffers, no alignment (align = 1)
if ( yuv ) {
av_image_fill_arrays ( yuv_frame_ - > data , yuv_frame_ - > linesize , yuv , AV_PIX_FMT_YUV420P , width , height , 1 ) ;
int ret = sws_scale ( yuv_sws_ctx_ , ( const uint8_t * * ) f - > data , f - > linesize , 0 , f - > height , yuv_frame_ - > data , yuv_frame_ - > linesize ) ;
av_image_fill_arrays ( sws_frame - > data , sws_frame - > linesize , yuv , AV_PIX_FMT_YUV420P , width , height , 1 ) ;
int ret = sws_scale ( yuv_sws_ctx_ , ( const uint8_t * * ) f - > data , f - > linesize , 0 , f - > height , sws_frame - > data , sws_frame - > linesize ) ;
if ( ret < 0 ) return false ;
}
av_image_fill_arrays ( rgb_frame_ - > data , rgb_frame_ - > linesize , rgb , AV_PIX_FMT_BGR24 , width , height , 1 ) ;
int ret = sws_scale ( rgb_sws_ctx_ , ( const uint8_t * * ) f - > data , f - > linesize , 0 , f - > height , rgb_frame_ - > data , rgb_frame_ - > linesize ) ;
av_image_fill_arrays ( sws_frame - > data , sws_frame - > linesize , rgb , AV_PIX_FMT_BGR24 , width , height , 1 ) ;
int ret = sws_scale ( rgb_sws_ctx_ , ( const uint8_t * * ) f - > data , f - > linesize , 0 , f - > height , sws_frame - > data , sws_frame - > linesize ) ;
return ret > = 0 ;
}