@ -3,8 +3,7 @@
# include <unistd.h>
# include <cassert>
# include <QDebug>
# include "selfdrive/common/timing.h"
static int ffmpeg_lockmgr_cb ( void * * arg , enum AVLockOp op ) {
std : : mutex * mutex = ( std : : mutex * ) * arg ;
@ -36,18 +35,13 @@ public:
~ AVInitializer ( ) { avformat_network_deinit ( ) ; }
} ;
static AVInitializer av_initializer ;
FrameReader : : FrameReader ( const std : : string & url , QObject * parent ) : url_ ( url ) , QObject ( parent ) {
process_thread_ = QThread : : create ( & FrameReader : : process , this ) ;
connect ( process_thread_ , & QThread : : finished , process_thread_ , & QThread : : deleteLater ) ;
process_thread_ - > start ( ) ;
FrameReader : : FrameReader ( const std : : string & url , int timeout_sec ) : url_ ( url ) , timeout_ ( timeout_sec ) {
static AVInitializer av_initializer ;
}
FrameReader : : ~ FrameReader ( ) {
// wait until thread is finished.
exit_ = true ;
process_thread_ - > wait ( ) ;
cv_decode_ . notify_all ( ) ;
cv_frame_ . notify_all ( ) ;
if ( decode_thread_ . joinable ( ) ) {
@ -65,25 +59,35 @@ FrameReader::~FrameReader() {
delete [ ] buffer_pool . front ( ) ;
buffer_pool . pop ( ) ;
}
av_frame_free ( & frmRgb_ ) ;
avcodec_close ( pCodecCtx_ ) ;
avcodec_free_context ( & pCodecCtx_ ) ;
avformat_close_input ( & pFormatCtx_ ) ;
sws_freeContext ( sws_ctx_ ) ;
}
void FrameReader : : process ( ) {
if ( processFrames ( ) ) {
decode_thread_ = std : : thread ( & FrameReader : : decodeThread , this ) ;
if ( frmRgb_ ) {
av_frame_free ( & frmRgb_ ) ;
}
if ( pCodecCtx_ ) {
avcodec_close ( pCodecCtx_ ) ;
avcodec_free_context ( & pCodecCtx_ ) ;
}
if ( ! exit _) {
emit finished ( ) ;
if ( pFormatCtx_ ) {
avformat_close_input ( & pFormatCtx_ ) ;
}
if ( sws_ctx_ ) {
sws_freeContext ( sws_ctx_ ) ;
}
}
int FrameReader : : check_interrupt ( void * p ) {
FrameReader * fr = static_cast < FrameReader * > ( p ) ;
return fr - > exit_ | | ( fr - > timeout_ > 0 & & millis_since_boot ( ) > fr - > timeout_ms_ ) ;
}
bool FrameReader : : processFrames ( ) {
bool FrameReader : : process ( ) {
pFormatCtx_ = avformat_alloc_context ( ) ;
pFormatCtx_ - > interrupt_callback . callback = & FrameReader : : check_interrupt ;
pFormatCtx_ - > interrupt_callback . opaque = ( void * ) this ;
if ( timeout_ > 0 ) {
timeout_ms_ = millis_since_boot ( ) + timeout_ * 1000 ;
}
if ( avformat_open_input ( & pFormatCtx_ , url_ . c_str ( ) , NULL , NULL ) ! = 0 ) {
qDebug ( ) < < " error loading " < < url_ . c_str ( ) ;
printf ( " error loading %s \n " , url_ . c_str ( ) ) ;
return false ;
}
avformat_find_stream_info ( pFormatCtx_ , NULL ) ;
@ -91,14 +95,14 @@ bool FrameReader::processFrames() {
auto pCodecCtxOrig = pFormatCtx_ - > streams [ 0 ] - > codec ;
auto pCodec = avcodec_find_decoder ( pCodecCtxOrig - > codec_id ) ;
assert ( pCodec ) ;
if ( ! pCodec ) return false ;
pCodecCtx_ = avcodec_alloc_context3 ( pCodec ) ;
int ret = avcodec_copy_context ( pCodecCtx_ , pCodecCtxOrig ) ;
assert ( ret = = 0 ) ;
if ( ret ! = 0 ) return false ;
ret = avcodec_open2 ( pCodecCtx_ , pCodec , NULL ) ;
assert ( ret > = 0 ) ;
if ( ret < 0 ) return false ;
width = pCodecCtxOrig - > width ;
height = pCodecCtxOrig - > height ;
@ -106,21 +110,25 @@ bool FrameReader::processFrames() {
sws_ctx_ = sws_getContext ( width , height , AV_PIX_FMT_YUV420P ,
width , height , AV_PIX_FMT_BGR24 ,
SWS_BILINEAR , NULL , NULL , NULL ) ;
assert ( sws_ctx_ ) ;
if ( ! sws_ctx_ ) return false ;
frmRgb_ = av_frame_alloc ( ) ;
assert ( frmRgb_ ) ;
if ( ! frmRgb_ ) return false ;
frames_ . reserve ( 60 * 20 ) ; // 20fps, one minute
do {
Frame & frame = frames_ . emplace_back ( ) ;
if ( av_read_frame ( pFormatCtx_ , & frame . pkt ) < 0 ) {
int err = av_read_frame ( pFormatCtx_ , & frame . pkt ) ;
if ( err < 0 ) {
frames_ . pop_back ( ) ;
valid_ = ( err = = AVERROR_EOF ) ;
break ;
}
} while ( ! exit_ ) ;
valid_ = ! exit_ ;
if ( valid_ ) {
decode_thread_ = std : : thread ( & FrameReader : : decodeThread , this ) ;
}
return valid_ ;
}
@ -128,22 +136,18 @@ uint8_t *FrameReader::get(int idx) {
if ( ! valid_ | | idx < 0 | | idx > = frames_ . size ( ) ) {
return nullptr ;
}
{
std : : unique_lock lk ( mutex_ ) ;
decode_idx_ = idx ;
cv_decode_ . notify_one ( ) ;
cv_frame_ . wait ( lk , [ = ] { return exit_ | | frames_ [ idx ] . data | | frames_ [ idx ] . failed ; } ) ;
}
std : : unique_lock lk ( mutex_ ) ;
decode_idx_ = idx ;
cv_decode_ . notify_one ( ) ;
cv_frame_ . wait ( lk , [ = ] { return exit_ | | frames_ [ idx ] . data | | frames_ [ idx ] . failed ; } ) ;
return frames_ [ idx ] . data ;
}
void FrameReader : : decodeThread ( ) {
int idx = 0 ;
while ( ! exit_ ) {
const int from = std : : max ( idx , 0 ) ;
const int to = std : : min ( from + 20 , ( int ) frames_ . size ( ) ) ;
const int from = std : : max ( idx - 15 , 0 ) ;
const int to = std : : min ( idx + 20 , ( int ) frames_ . size ( ) ) ;
for ( int i = 0 ; i < frames_ . size ( ) & & ! exit_ ; + + i ) {
Frame & frame = frames_ [ i ] ;
if ( i > = from & & i < to ) {