@ -4,125 +4,114 @@
# include <unistd.h>
static int ffmpeg_lockmgr_cb ( void * * arg , enum AVLockOp op ) {
pthread_mutex_t * mutex = ( pthread_mutex_t * ) * arg ;
int err ;
std : : mutex * mutex = ( std : : mutex * ) * arg ;
switch ( op ) {
case AV_LOCK_CREATE :
mutex = ( pthread_mutex_t * ) malloc ( sizeof ( * mutex ) ) ;
if ( ! mutex )
return AVERROR ( ENOMEM ) ;
if ( ( err = pthread_mutex_init ( mutex , NULL ) ) ) {
free ( mutex ) ;
return AVERROR ( err ) ;
}
* arg = mutex ;
return 0 ;
mutex = new std : : mutex ( ) ;
break ;
case AV_LOCK_OBTAIN :
if ( ( err = pthread_mutex_lock ( mutex ) ) )
return AVERROR ( err ) ;
return 0 ;
mutex - > lock ( ) ;
break ;
case AV_LOCK_RELEASE :
if ( ( err = pthread_mutex_unlock ( mutex ) ) )
return AVERROR ( err ) ;
return 0 ;
mutex - > unlock ( ) ;
case AV_LOCK_DESTROY :
if ( mutex )
pthread_mutex_destroy ( mutex ) ;
free ( mutex ) ;
* arg = NULL ;
return 0 ;
delete mutex ;
break ;
}
return 1 ;
return 0 ;
}
FrameReader : : FrameReader ( const char * fn ) {
int ret ;
ret = av_lockmgr_register ( ffmpeg_lockmgr_cb ) ;
FrameReader : : FrameReader ( const std : : string & fn ) : url ( fn ) {
int ret = av_lockmgr_register ( ffmpeg_lockmgr_cb ) ;
assert ( ret > = 0 ) ;
avformat_network_init ( ) ;
av_register_all ( ) ;
snprintf ( url , sizeof ( url ) - 1 , " %s " , fn ) ;
t = new std : : thread ( [ & ] ( ) { this - > loaderThread ( ) ; } ) ;
}
void FrameReader : : loaderThread ( ) {
int ret ;
FrameReader : : ~ FrameReader ( ) {
exit_ = true ;
thread . join ( ) ;
for ( auto & f : frames ) {
delete f - > pkt ;
if ( f - > picture ) {
av_frame_free ( & f - > picture ) ;
}
delete f ;
}
avcodec_free_context ( & pCodecCtx ) ;
avformat_free_context ( pFormatCtx ) ;
sws_freeContext ( sws_ctx ) ;
avformat_network_deinit ( ) ;
}
if ( avformat_open_input ( & pFormatCtx , url , NULL , NULL ) ! = 0 ) {
fprintf ( stderr , " error loading %s \n " , url ) ;
void FrameReader : : process ( ) {
if ( avformat_open_input ( & pFormatCtx , url . c_str ( ) , NULL , NULL ) ! = 0 ) {
fprintf ( stderr , " error loading %s \n " , url . c_str ( ) ) ;
valid = false ;
return ;
}
av_dump_format ( pFormatCtx , 0 , url , 0 ) ;
avformat_find_stream_info ( pFormatCtx , NULL ) ;
av_dump_format ( pFormatCtx , 0 , url . c_str ( ) , 0 ) ;
auto pCodecCtxOrig = pFormatCtx - > streams [ 0 ] - > codec ;
auto pCodec = avcodec_find_decoder ( pCodecCtxOrig - > codec_id ) ;
assert ( pCodec ! = NULL ) ;
pCodecCtx = avcodec_alloc_context3 ( pCodec ) ;
ret = avcodec_copy_context ( pCodecCtx , pCodecCtxOrig ) ;
int ret = avcodec_copy_context ( pCodecCtx , pCodecCtxOrig ) ;
assert ( ret = = 0 ) ;
ret = avcodec_open2 ( pCodecCtx , pCodec , NULL ) ;
assert ( ret > = 0 ) ;
width = pCodecCtxOrig - > width ;
height = pCodecCtxOrig - > height ;
sws_ctx = sws_getContext ( width , height , AV_PIX_FMT_YUV420P ,
width , height , AV_PIX_FMT_BGR24 ,
SWS_BILINEAR , NULL , NULL , NULL ) ;
assert ( sws_ctx ! = NULL ) ;
AVPacket * pkt = ( AVPacket * ) malloc ( sizeof ( AVPacket ) ) ;
assert ( pkt ! = NULL ) ;
bool first = true ;
while ( av_read_frame ( pFormatCtx , pkt ) > = 0 ) {
//printf("%d pkt %d %d\n", pkts.size(), pkt->size, pkt->pos);
if ( first ) {
AVFrame * pFrame = av_frame_alloc ( ) ;
int frameFinished ;
avcodec_decode_video2 ( pCodecCtx , pFrame , & frameFinished , pkt ) ;
first = false ;
}
pkts . push_back ( pkt ) ;
pkt = ( AVPacket * ) malloc ( sizeof ( AVPacket ) ) ;
assert ( pkt ! = NULL ) ;
do {
AVPacket * pkt = new AVPacket ;
if ( av_read_frame ( pFormatCtx , pkt ) < 0 ) {
delete pkt ;
break ;
}
free ( pkt ) ;
Frame * frame = new Frame { . pkt = pkt } ;
frames . push_back ( frame ) ;
} while ( true ) ;
printf ( " framereader download done \n " ) ;
joined = true ;
// cache
while ( 1 ) {
GOPCache ( to_cache . get ( ) ) ;
}
thread = std : : thread ( & FrameReader : : decodeThread , this ) ;
}
void FrameReader : : decodeThread ( ) {
while ( ! exit_ ) {
int gop = 0 ;
{
std : : unique_lock lk ( mutex ) ;
cv_decode . wait ( lk , [ = ] { return exit_ | | decode_idx ! = - 1 ; } ) ;
if ( exit_ ) break ;
void FrameReader : : GOPCache ( int idx ) {
AVFrame * pFrame ;
int gop = idx - idx % 15 ;
gop = std : : max ( decode_idx - decode_idx % 15 , 0 ) ;
decode_idx = - 1 ;
}
mcache . lock ( ) ;
bool has_gop = cache . find ( gop ) ! = cache . end ( ) ;
mcache . unlock ( ) ;
for ( int i = gop ; i < std : : min ( gop + 15 , ( int ) frames . size ( ) ) ; + + i ) {
if ( frames [ i ] - > picture ! = nullptr ) continue ;
if ( ! has_gop ) {
//printf("caching %d\n", gop);
for ( int i = gop ; i < gop + 15 ; i + + ) {
if ( i > = pkts . size ( ) ) break ;
//printf("decode %d\n", i);
int frameFinished ;
pFrame = av_frame_alloc ( ) ;
avcodec_decode_video2 ( pCodecCtx , pFrame , & frameFinished , pkts [ i ] ) ;
uint8_t * dat = toRGB ( pFrame ) - > data [ 0 ] ;
mcache . lock ( ) ;
cache . insert ( std : : make_pair ( i , dat ) ) ;
mcache . unlock ( ) ;
AVFrame * pFrame = av_frame_alloc ( ) ;
avcodec_decode_video2 ( pCodecCtx , pFrame , & frameFinished , frames [ i ] - > pkt ) ;
AVFrame * picture = toRGB ( pFrame ) ;
av_frame_free ( & pFrame ) ;
std : : unique_lock lk ( mutex ) ;
frames [ i ] - > picture = picture ;
cv_frame . notify_all ( ) ;
}
}
}
@ -130,48 +119,23 @@ void FrameReader::GOPCache(int idx) {
AVFrame * FrameReader : : toRGB ( AVFrame * pFrame ) {
AVFrame * pFrameRGB = av_frame_alloc ( ) ;
int numBytes = avpicture_get_size ( AV_PIX_FMT_BGR24 , pFrame - > width , pFrame - > height ) ;
uint8_t * buffer = ( uint8_t * ) av_malloc ( numBytes * sizeof ( uint8_t ) ) ;
uint8_t * buffer = ( uint8_t * ) av_malloc ( numBytes * sizeof ( uint8_t ) ) ;
avpicture_fill ( ( AVPicture * ) pFrameRGB , buffer , AV_PIX_FMT_BGR24 , pFrame - > width , pFrame - > height ) ;
sws_scale ( sws_ctx , ( uint8_t const * const * ) pFrame - > data ,
sws_scale ( sws_ctx , ( uint8_t const * const * ) pFrame - > data ,
pFrame - > linesize , 0 , pFrame - > height ,
pFrameRGB - > data , pFrameRGB - > linesize ) ;
return pFrameRGB ;
}
uint8_t * FrameReader : : get ( int idx ) {
if ( ! valid ) return NULL ;
waitForReady ( ) ;
// TODO: one line?
uint8_t * dat = NULL ;
// lookahead
to_cache . put ( idx ) ;
to_cache . put ( idx + 15 ) ;
mcache . lock ( ) ;
auto it = cache . find ( idx ) ;
if ( it ! = cache . end ( ) ) {
dat = it - > second ;
}
mcache . unlock ( ) ;
if ( dat = = NULL ) {
to_cache . put_front ( idx ) ;
// lookahead
while ( dat = = NULL ) {
// wait for frame
usleep ( 50 * 1000 ) ;
// check for frame
mcache . lock ( ) ;
auto it = cache . find ( idx ) ;
if ( it ! = cache . end ( ) ) dat = it - > second ;
mcache . unlock ( ) ;
if ( dat = = NULL ) {
printf ( " . " ) ;
fflush ( stdout ) ;
if ( ! valid | | idx < 0 | | idx > = frames . size ( ) ) return nullptr ;
std : : unique_lock lk ( mutex ) ;
decode_idx = idx ;
cv_decode . notify_one ( ) ;
Frame * frame = frames [ idx ] ;
if ( ! frame - > picture ) {
cv_frame . wait ( lk , [ = ] { return exit_ | | frame - > picture ! = nullptr ; } ) ;
}
}
}
return dat ;
return frame - > picture ? frame - > picture - > data [ 0 ] : nullptr ;
}