# include "FrameReader.hpp"
# include <assert.h>
# include <unistd.h>
static int ffmpeg_lockmgr_cb ( void * * arg , enum AVLockOp op ) {
pthread_mutex_t * mutex = ( pthread_mutex_t * ) * arg ;
int err ;
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 ;
case AV_LOCK_OBTAIN :
if ( ( err = pthread_mutex_lock ( mutex ) ) )
return AVERROR ( err ) ;
return 0 ;
case AV_LOCK_RELEASE :
if ( ( err = pthread_mutex_unlock ( mutex ) ) )
return AVERROR ( err ) ;
return 0 ;
case AV_LOCK_DESTROY :
if ( mutex )
pthread_mutex_destroy ( mutex ) ;
free ( mutex ) ;
* arg = NULL ;
return 0 ;
}
return 1 ;
}
FrameReader : : FrameReader ( const char * fn ) {
int ret ;
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 ;
if ( avformat_open_input ( & pFormatCtx , url , NULL , NULL ) ! = 0 ) {
fprintf ( stderr , " error loading %s \n " , url ) ;
valid = false ;
return ;
}
av_dump_format ( pFormatCtx , 0 , url , 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 ) ;
assert ( ret = = 0 ) ;
ret = avcodec_open2 ( pCodecCtx , pCodec , NULL ) ;
assert ( ret > = 0 ) ;
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 ) ;
}
free ( pkt ) ;
printf ( " framereader download done \n " ) ;
joined = true ;
// cache
while ( 1 ) {
GOPCache ( to_cache . get ( ) ) ;
}
}
void FrameReader : : GOPCache ( int idx ) {
AVFrame * pFrame ;
int gop = idx - idx % 15 ;
mcache . lock ( ) ;
bool has_gop = cache . find ( gop ) ! = cache . end ( ) ;
mcache . unlock ( ) ;
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 * 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 ) ) ;
avpicture_fill ( ( AVPicture * ) pFrameRGB , buffer , AV_PIX_FMT_BGR24 , pFrame - > width , pFrame - > height ) ;
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 ) ;
}
}
}
return dat ;
}