hello,各位好,本人是一名嵌入式軟件工程師,目前正使用ffmpeg開發(fā)一款嵌入式多媒 體播放器,《ffmpeg分析》系列博文是本人在閱讀ffmpeg源代碼時所做的筆記,希望對各位 有點幫助。 分析過程結(jié)合下面的例程:
????http://dranger.com/ffmpeg/tutorial05.c
?
一. 調(diào)用av_register_all函數(shù)注冊所有的格式和編碼解碼器.
1.1 先調(diào)用avcodec_register_all函數(shù)注冊所有的編碼解碼器.
1. 下面列出與H264相關(guān)的注冊:
?? ?// 注冊硬件加速器 ????REGISTER_HWACCEL ( H264_DXVA2, h264_dxva2) ; ????REGISTER_HWACCEL ( H264_VAAPI, h264_vaapi) ; ????// 注冊解碼器 ????REGISTER_DECODER ( H264, h264) ; ????REGISTER_DECODER ( H264_VDPAU, h264_vdpau) ; ????// 注冊編碼器 ????REGISTER_ENCODER ( LIBX264, libx264) ; ????// 注冊分析器 ????REGISTER_PARSER ( H264, h264) ; ????// 注冊位流分離器 ????REGISTER_BSF ( H264_MP4TOANNEXB, h264_mp4toannexb) ;
2. 下面列出注冊宏:
# define REGISTER_HWACCEL( X, x) { \ ??????????extern AVHWAccel x# # _hwaccel; \ ??????????if ( CONFIG_# # X# # _HWACCEL) av_register_hwaccel( & x# # _hwaccel) ; } # define REGISTER_ENCODER( X, x) { \ ??????????extern AVCodec x# # _encoder; \ ??????????if ( CONFIG_# # X# # _ENCODER) avcodec_register( & x# # _encoder) ; } # define REGISTER_DECODER( X, x) { \ ??????????extern AVCodec x# # _decoder; \ ??????????if ( CONFIG_# # X# # _DECODER) avcodec_register( & x# # _decoder) ; } # define REGISTER_ENCDEC( X, x) REGISTER_ENCODER( X, x) ; REGISTER_DECODER( X, x) # define REGISTER_PARSER( X, x) { \ ??????????extern AVCodecParser x# # _parser; \ ??????????if ( CONFIG_# # X# # _PARSER) av_register_codec_parser( & x# # _parser) ; } # define REGISTER_BSF( X, x) { \ ??????????extern AVBitStreamFilter x# # _bsf; \ ??????????if ( CONFIG_# # X# # _BSF) av_register_bitstream_filter( & x# # _bsf) ; }
3. 分析一下注冊函數(shù), 以avcodec_register函數(shù)為例:
void avcodec_register( AVCodec* codec) { ????AVCodec * * p; ????avcodec_init( ) ; ????p = & first_avcodec; ????while ( * p! = NULL ) p = & ( * p) - > next; ????* p = codec; ????codec- > next= NULL ; }
?? ?可以看到avcodec_register函數(shù)把輸入的AVCodec連成一個鏈表, 其它注冊函數(shù)與之類似, 就不多言了.
4. 上面調(diào)用了avcodec_init函數(shù):
void avcodec_init( void ) { ????static int initialized= 0; ?????if ( initialized! = 0) ????????return ; ????initialized = 1; ????dsputil_static_init ( ) ; }
?? ?這個函數(shù)只會真正執(zhí)行一次. 5. 上面調(diào)用了dsputil_static_init函數(shù):
av_coldvoid dsputil_static_init( void ) { ????int i; ?????for ( i= 0; i< 256; i+ + ) ff_cropTbl[ i + MAX_NEG_CROP] = i; ????for ( i= 0; i< MAX_NEG_CROP; i+ + ) { ????????ff_cropTbl[ i] = 0; ????????ff_cropTbl[ i + MAX_NEG_CROP + 256] = 255; ????} ?????for ( i= 0; i< 512; i+ + ) { ????????ff_squareTbl[ i] = ( i - 256) * ( i - 256) ; ????} ?????for ( i= 0; i< 64; i+ + ) inv_zigzag_direct16[ ff_zigzag_direct[ i] ] = i+ 1; }
?? ?
?? ?可以看到, 它初始化了一些靜態(tài)數(shù)據(jù).
1.2 注冊所有的格式和外部庫及協(xié)議.
1. 下面列出與H264相關(guān)的注冊:
?? ?// 注冊分離器和混合器 ????REGISTER_MUXDEMUX ( H264, h264) ; ????// 注冊文件協(xié)議 ????REGISTER_PROTOCOL ( FILE , file ) ;
2. 下面列出注冊宏:
# define REGISTER_MUXER( X, x) { \ ????extern AVOutputFormat x# # _muxer; \ ????if ( CONFIG_# # X# # _MUXER) av_register_output_format( & x# # _muxer) ; } # define REGISTER_DEMUXER( X, x) { \ ????extern AVInputFormat x# # _demuxer; \ ????if ( CONFIG_# # X# # _DEMUXER) av_register_input_format( & x# # _demuxer) ; } # define REGISTER_MUXDEMUX( X, x) REGISTER_MUXER( X, x) ; REGISTER_DEMUXER( X, x) # define REGISTER_PROTOCOL( X, x) { \ ????extern URLProtocol x# # _protocol; \ ????if ( CONFIG_# # X# # _PROTOCOL) av_register_protocol( & x# # _protocol) ; }
?? ?這些注冊函數(shù)與avcodec_register函數(shù)類似, 就不多言了.
URL協(xié)議結(jié)構(gòu):
typedef ?struct ?URLProtocol?{ ????const ?char ?* name; ????int ?( * url_open) ( URLContext?* h, ?const ?char ?* url, ?int ?flags) ; ????int ?( * url_read) ( URLContext?* h, ?unsigned ?char ?* buf, ?int ?size) ; ????int ?( * url_write) ( URLContext?* h, ?unsigned ?char ?* buf, ?int ?size) ; ????int64_t ?( * url_seek) ( URLContext?* h, ?int64_t ?pos, ?int ?whence) ; ????int ?( * url_close) ( URLContext?* h) ; ????struct ?URLProtocol?* next; ????int ?( * url_read_pause) ( URLContext?* h, ?int ?pause) ; ????int64_t ?( * url_read_seek) ( URLContext?* h, ?int ?stream_index, ?????????????????????????????int64_t ?timestamp, ?int ?flags) ; ????int ?( * url_get_file_handle) ( URLContext?* h) ; } ?URLProtocol;
libavformat/file.c文件的file協(xié)議:
static int file_open( URLContext* h, const char * filename, int flags) { ????int access; ????int fd; ????av_strstart( filename, "file:" , & filename) ; ????if ( flags& URL_RDWR) { ????????access = O_CREAT | O_TRUNC | O_RDWR; ????} else if ( flags & URL_WRONLY) { ????????access = O_CREAT | O_TRUNC | O_WRONLY; ????} else { ????????access = O_RDONLY; ????} # ifdef O_BINARY ????access | = O_BINARY; # endif ????fd = open ( filename, access, 0666) ; ????if ( fd = = - 1) ????????return AVERROR( errno ) ; ????h- > priv_data= ( void * ) ( intptr_t ) fd; ????return 0; } static int file_read( URLContext* h, unsigned char * buf, int size) { ????int fd = ( intptr_t ) h- > priv_data; ????return read ( fd, buf, size) ; } static int file_write( URLContext* h, unsigned char * buf, int size) { ????int fd = ( intptr_t ) h- > priv_data; ????return write ( fd, buf, size) ; } /* XXX: use llseek */ static int64_t file_seek( URLContext* h, int64_t pos, int whence) { ????int fd = ( intptr_t ) h- > priv_data; ????if ( whence= = AVSEEK_SIZE) { ????????struct stat st; ????????int ret = fstat( fd, & st) ; ????????return ret < 0 ? AVERROR( errno ) : st. st_size; ????} ????return lseek( fd, pos, whence) ; } static int file_close( URLContext* h) { ????int fd = ( intptr_t ) h- > priv_data; ????return close ( fd) ; } static int file_get_handle( URLContext* h) { ????return ( intptr_t ) h- > priv_data; } URLProtocol file_protocol = { ????"file" , ????file_open, ????file_read, ????file_write, ????file_seek, ????file_close, ????. url_get_file_handle = file_get_handle, } ;
libavformat/allformats.c文件的
av_register_all函數(shù) 注冊了file協(xié)議:
# define REGISTER_PROTOCOL( X, x) { \ ????extern URLProtocol x# # _protocol; \ ????if ( CONFIG_# # X# # _PROTOCOL) av_register_protocol( & x# # _protocol) ; }
void av_register_all( void ) { ????/* 省略部分代碼 */ ????/* protocols */ ????REGISTER_PROTOCOL ( FILE , file ) ; ????/* 省略部分代碼 */ }
把注冊協(xié)議函數(shù)也貼出來吧:
URLProtocol* first_protocol = NULL ;
int av_register_protocol( URLProtocol* protocol) { ????URLProtocol * * p; ????p = & first_protocol; ????while ( * p! = NULL ) p = & ( * p) - > next; ????* p = protocol; ????protocol- > next= NULL ; ????return 0;
}
http://blogold.chinaunix.net/u3/104564/showart_2369209.html
探測數(shù)據(jù)結(jié)構(gòu):
/** This structure contains the data a format has to probe a file. */ typedef ?struct ?AVProbeData?{ ????const ?char ?* filename; ????unsigned ?char ?* buf; ?/**< Buffer must have AVPROBE_PADDING_SIZE of extra allocated bytes filled with zero. */ ????int ?buf_size; ?/**< Size of buf except extra allocated bytes */ } ?AVProbeData;
h264的探測函數(shù): static int h264_probe( AVProbeData* p) { ????uint32_t code= - 1; ????int sps= 0, pps= 0, idr= 0, res= 0, sli= 0; ????int i; ????for ( i= 0; i< p- > buf_size; i+ + ) { ????????code = ( code< < 8) + p- > buf[ i] ; ????????if ( ( code& 0xffffff00) = = 0x100) { ????????????int ref_idc= ( code> > 5) & 3; ????????????int type = code & 0x1F; ????????????static const int8_t ref_zero[ 32] = { ????????????????2, 0, 0, 0, 0, - 1, 1, - 1, ???????????????- 1, 1, 1, 1, 1, - 1, 2, 2, ????????????????2, 2, 2, 0, 2, 2, 2, 2, ????????????????2, 2, 2, 2, 2, 2, 2, 2 ????????????} ; ????????????if ( code& 0x80) //forbidden bit ????????????????return 0; ????????????if ( ref_zero[ type] = = 1 & & ref_idc) ????????????????return 0; ????????????if ( ref_zero[ type] = = - 1& & ! ref_idc) ????????????????return 0; ????????????if ( ref_zero[ type] = = 2) ????????????????res+ + ; ????????????switch ( type) { ????????????case 1: sli+ + ; break ; ????????????case 5: idr+ + ; break ; ????????????case 7: ????????????????if ( p- > buf[ i+ 2] & 0x0F) ????????????????????return 0; ????????????????sps+ + ; ????????????????break ; ????????????case 8: pps+ + ; break ; ????????????} ????????} ????} ????if ( sps & & pps & & ( idr| | sli> 3) & & res< ( sps+ pps+ idr) ) ????????return AVPROBE_SCORE_MAX/ 2+ 1; // +1 for .mpg ????return 0; }
視頻讀首部函數(shù): static int video_read_header( AVFormatContext* s, ?????????????????????????????AVFormatParameters * ap) { ????AVStream * st; ????st = av_new_stream( s, 0) ; ????if ( ! st) ????????return AVERROR( ENOMEM) ; ????st- > codec- > codec_type= AVMEDIA_TYPE_VIDEO; ????st- > codec- > codec_id= s- > iformat- > value; ????st- > need_parsing= AVSTREAM_PARSE_FULL; ????/* for MJPEG, specify frame rate */ ????/* for MPEG-4 specify it, too (most MPEG-4 streams do not have the fixed_vop_rate set ...)*/ ????if ( ap- > time_base . num) { ????????st- > codec- > time_base = ap- > time_base ; ????} else if ( st- > codec- > codec_id= = CODEC_ID_MJPEG| | ????????????????st- > codec- > codec_id= = CODEC_ID_MPEG4| | ????????????????st- > codec- > codec_id= = CODEC_ID_DIRAC| | ????????????????st- > codec- > codec_id= = CODEC_ID_DNXHD| | ????????????????st- > codec- > codec_id= = CODEC_ID_H264) { ????????st- > codec- > time_base = ( AVRational) { 1, 25} ; ????} ????av_set_pts_info( st, 64, 1, 1200000) ; ????return 0; }
原始地讀實際的包函數(shù): int ff_raw_read_partial_packet( AVFormatContext* s, AVPacket* pkt) { ????int ret, size; ????size = RAW_PACKET_SIZE; ????if ( av_new_packet( pkt, size) < 0) ????????return AVERROR( ENOMEM) ; ????pkt- > pos= url_ftell( s- > pb) ; ????pkt- > stream_index= 0; ????ret = get_partial_buffer( s- > pb, pkt- > data, size) ; ????if ( ret< 0) { ????????av_free_packet( pkt) ; ????????return ret; ????} ????pkt- > size= ret; ????return ret; }
原始地寫包函數(shù):
static ?int ?raw_write_packet( struct ?AVFormatContext?* s, ?AVPacket?* pkt) { ????put_buffer( s- > pb, ?pkt- > data, ?pkt- > size) ; ????put_flush_packet( s- > pb) ; ????return ?0; }
h264混合器: AVOutputFormat h264_muxer?= ?{ ????"h264" , ????NULL_IF_CONFIG_SMALL( "raw H.264 video format" ) , ????NULL , ????"h264" , ????0, ????CODEC_ID_NONE, ????CODEC_ID_H264, ????NULL , ????raw_write_packet, ????. flags= ?AVFMT_NOTIMESTAMPS, } ;
h264分離器: AVInputFormat h264_demuxer= { ????"h264" , ????NULL_IF_CONFIG_SMALL( "raw H.264 video format" ) , ????0, ????h264_probe, ????video_read_header, ????ff_raw_read_partial_packet, ????. flags= AVFMT_GENERIC_INDEX, ????. extensions = "h26l,h264,264" , //FIXME remove after writing mpeg4_probe ????. value = CODEC_ID_H264, }
libavformat/allformats.c文件的av_register_all函數(shù) 注冊了h264分離器和混合器: # define REGISTER_MUXER( X, x) { \ ????extern AVOutputFormat x# # _muxer; \ ????if ( CONFIG_# # X# # _MUXER) av_register_output_format( & x# # _muxer) ; } # define REGISTER_DEMUXER( X, x) { \ ????extern AVInputFormat x# # _demuxer; \ ????if ( CONFIG_# # X# # _DEMUXER) av_register_input_format( & x# # _demuxer) ; } # define REGISTER_MUXDEMUX( X, x) REGISTER_MUXER( X, x) ; REGISTER_DEMUXER( X, x)
void av_register_all( void ) { ????/* 省略部分代碼 */ ????/* protocols */ ????REGISTER_MUXDEMUX ( H264, h264) ; ????/* 省略部分代碼 */ }
把注冊格式函數(shù)也貼出來吧: /** head of registered input format linked list */ AVInputFormat * first_iformat = NULL ; /** head of registered output format linked list */ AVOutputFormat * first_oformat = NULL ;
void av_register_input_format( AVInputFormat* format) { ????AVInputFormat * * p; ????p = & first_iformat; ????while ( * p! = NULL ) p = & ( * p) - > next; ????* p = format; ????format- > next= NULL ; } void av_register_output_format( AVOutputFormat* format) { ????AVOutputFormat * * p; ????p = & first_oformat; ????while ( * p! = NULL ) p = & ( * p) - > next; ????* p = format; ????format- > next= NULL ; }
http://blogold.chinaunix.net/u3/104564/showart_2369231.html
調(diào)用av_open_input_file( & pFormatCtx, is- > filename, NULL , 0, NULL ) 函數(shù)打開輸入的文件. 1. 分析一下函數(shù)原型: int av_open_input_file( AVFormatContext* * ic_ptr, // 輸出參數(shù): 格式上下文 ???????????????????????const char * filename, // 文件名 ???????????????????????AVInputFormat * fmt, // 輸入的格式, 為NULL, 即未知 ???????????????????????int buf_size, // 緩沖的大小, 為0 ???????????????????????AVFormatParameters * ap) ; // 格式的參數(shù), 為NULL 2. 初始化探測數(shù)據(jù): ????AVProbeData probe_data, * pd = & probe_data; ????pd- > filename= "" ; ????if ( filename) ????????pd- > filename= filename; ????pd- > buf= NULL ; ????pd- > buf_size= 0; 3. 探測輸入的格式: ????if ( ! fmt) { // fmt == NULL, 成立 ????????fmt = av_probe_input_format( pd, 0) ; ????} ????進(jìn)入av_probe_input_format函數(shù): AVInputFormat * av_probe_input_format( AVProbeData* pd, int is_opened) { ????int score= 0; ????return av_probe_input_format2( pd, is_opened, & score) ; } ????進(jìn)入av_probe_input_format2函數(shù): AVInputFormat * av_probe_input_format2( AVProbeData* pd, int is_opened, int * score_max) { ????AVInputFormat * fmt1, * fmt; ????int score; ????fmt = NULL ; ????for ( fmt1= first_iformat; fmt1! = NULL ; fmt1 = fmt1- > next) { ????????if ( ! is_opened= = ! ( fmt1- > flags& AVFMT_NOFILE) ) // is_opened == 0, fmt1->flags 沒有設(shè)置 AVFMT_NOFILE 標(biāo)志時成立 ????????????continue ; ????/* 省略部分代碼 */ } ????見libavformat/ raw. c文件: AVInputFormat h264_demuxer = { ????"h264" , ????NULL_IF_CONFIG_SMALL( "raw H.264 video format" ) , ????0, ????h264_probe, ????video_read_header, ????ff_raw_read_partial_packet, ????. flags= AVFMT_GENERIC_INDEX, ????. extensions = "h26l,h264,264" , //FIXME remove after writing mpeg4_probe ????. value = CODEC_ID_H264, } ; ????由于 h264_demuxer. flags = = AVFMT_GENERIC_INDEX, 所以上面成立, continue , 返回的 AVInputFormat 指針為NULL, 探測不成功 .
1. 打開文件:
if ?( ! fmt?| | ?! ( fmt- > flags?& ?AVFMT_NOFILE) ) ?{ ?
因 fmt == NULL, 上面成立, 再看下面的代碼:
?? ? ? ?ByteIOContext* pb = NULL ; // 字節(jié)IO上下文 ????????if ( ( err= url_fopen( & pb, filename, URL_RDONLY) ) < 0) { // 只讀方式打開輸入的文件 ????????????goto fail; ????????} ????????if ( buf_size> 0) { // 因 buf_size == 0, 不成立 ????????????url_setbufsize( pb, buf_size) ;
?? ? ? ?}
進(jìn)入url_fopen函數(shù):
int url_fopen( ByteIOContext* * s, // 輸出參數(shù): 字節(jié)IO上下文 ??????????????const char * filename, // 文件名 ??????????????int flags) // 標(biāo)志 { ????URLContext * h; // URL(統(tǒng)一資源定位)上下文 ????int err; ????err = url_open( & h, filename, flags) ; // 打開URL ????if ( err< 0) ????????return err; ????err = url_fdopen( s, h) ; // 用URL上下文打開字節(jié)IO上下文 ????if ( err< 0) { ????????url_close( h) ; ????????return err; ????} ????return 0; }
進(jìn)入url_open函數(shù):
int url_open( URLContext* * puc, ?//? 輸出參數(shù):? URL上下文
const char * filename, ?// 文件名
int flags) ?// 標(biāo)志
{ ????URLProtocol * up; ????const char * p; ????char proto_str[ 128] , * q; ????// 提取協(xié)議 ????p = filename; ????q = proto_str; ????while ( * p! = '\0' & & * p ! = ':' ) { // 未結(jié)束, 并未遇到分隔符':' ????????if ( ! isalpha ( * p) ) // 如果不是英文字母 ????????????goto file_proto; ????????if ( ( q- proto_str) < sizeof ( proto_str) - 1) ????????????* q+ + = * p; // 記錄協(xié)議字符串 ????????p+ + ; ????} ????if ( * p= = '\0' | | is_dos_path( filename) ) { // 如果上面是因為結(jié)束而跳出, 或且 文件名 是DOS路徑
????file_proto: ????????strcpy ( proto_str, "file" ) ; // 文件協(xié)議 ????} else { ????????* q = '\0' ; // 追加結(jié)束符 ????} ????up = first_protocol; ????while ( up! = NULL ) { ????????if ( ! strcmp ( proto_str, up- > name) ) // 協(xié)議匹配 ????????????return url_open_protocol ( puc, up, filename, flags) ; // 用這個協(xié)議打開URL ????????up = up- > next; ????} ????* puc = NULL ; ????return AVERROR( ENOENT) ; }
進(jìn)入
url_open_protocol 函數(shù):
int url_open_protocol( URLContext * * puc, ?//? 輸出參數(shù):? URL上下文
struct URLProtocol* up, ? // URL協(xié)議
const char * filename, ?// 文件名
int flags)? // 標(biāo)志 { ????URLContext * uc; ????int err; ????// 網(wǎng)絡(luò)初始化 # if CONFIG_NETWORK ????if ( ! ff_network_init( ) ) ????????return AVERROR( EIO) ; # endif ????// 分配URL上下文并加上 文件名的存儲空間 ????uc = av_mallocz( sizeof ( URLContext) + strlen ( filename) + 1) ; ????if ( ! uc) { ????????err = AVERROR( ENOMEM) ; ????????goto fail; ????} ????// 初始化URL上下文 # if LIBAVFORMAT_VERSION_MAJOR> = 53 ????uc- > av_class= & urlcontext_class; # endif ????// 記錄文件名 ????uc- > filename= ( char * ) & uc[ 1] ; ????strcpy ( uc- > filename, filename) ; ? ????uc- > prot= up;? // URL協(xié)議
????uc- > flags= flags;? // 標(biāo)志 ????uc- > is_streamed= 0;? // 默認(rèn)不是流, 可以在up->url_open函數(shù)里修改 ????uc- > max_packet_size= 0; //? 包最大多大,? 默認(rèn)為0,可以在up->url_open函數(shù)里修改 ????// 打開URL ????err = up- > url_open( uc, filename, flags) ; ????if ( err< 0) { ????????av_free( uc) ; ????????goto fail; ????}
?
????if ( ( flags & ( URL_WRONLY | URL_RDWR) )?//?如果以可寫方式打開
???????| | ! strcmp ( up- > name, "file" ) )?//?或且是文件協(xié)議
????????//?如果不是流并且不可以url_seek
????????if ( ! uc- > is_streamed& & url_seek( uc, 0, SEEK_SET ) < 0) ????????????uc- > is_streamed= 1;//?強制為流 ????// 輸出 參數(shù):? URL上下文
????* puc= uc; ????return 0; ?fail: ????* puc = NULL ; # if CONFIG_NETWORK ????ff_network_close( ) ; # endif ????return err; }
先來看看url_get_max_packet_size函數(shù)
int ?url_get_max_packet_size( URLContext?* h) { ????return ?h- > max_packet_size; ?// 包最大多大, 被上面初始化為0 }
進(jìn)入url_fdopen函數(shù):
int url_fdopen(
ByteIOContext* * s, // 輸出參數(shù): 字節(jié)IO上下文
URLContext* h) // URL上下文 { ????uint8_t * buffer; ????int buffer_size, max_packet_size; ????max_packet_size = url_get_max_packet_size( h) ; ????if ( max_packet_size) { ????????buffer_size = max_packet_size; ????} else { ????????buffer_size = IO_BUFFER_SIZE; // 緩沖大小為IO_BUFFER_SIZE ????} ????buffer = av_malloc( buffer_size) ; // 分配緩沖 ????if ( ! buffer) ????????return AVERROR( ENOMEM) ; ????* s = av_mallocz( sizeof ( ByteIOContext) ) ; // 分配字節(jié)IO上下文
????if ( ! * s) { ????????av_free( buffer) ; ????????return AVERROR( ENOMEM) ; ????} ????if ( init_put_byte( * s, buffer, buffer_size, ??????????????????????( h- > flags& URL_WRONLY | | h- > flags& URL_RDWR) , h, ??????????????????????url_read, url_write, url_seek) < 0) { ????????av_free( buffer) ; ????????av_freep( s) ; ????????return AVERROR( EIO) ; ????} ????( * s) - > is_streamed= h- > is_streamed;? // 是否為流 ????( * s) - > max_packet_size= max_packet_size;? //? 包最大多大 ????if ( h- > prot) { ????????( * s) - > read_pause= ( int ( * ) ( void * , int ) ) h- > prot- > url_read_pause; // 讀暫停函數(shù) ????????( * s) - > read_seek= ( int64_t ( * ) ( void * , int , int64_t , int ) ) h- > prot- > url_read_seek; // 讀seek函數(shù) ????} ????return 0; }
進(jìn)入init_put_byte函數(shù):
int init_put_byte( ByteIOContext* s, // 字節(jié)IO上下文 ??????????????????unsigned char * buffer, // 緩沖 ??????????????????int buffer_size, // 緩沖的大小 ??????????????????int write_flag, // 寫標(biāo)志 ??????????????????void * opaque, // URL上下文 ??????????????????int ( * read_packet) ( void * opaque, uint8_t * buf, int buf_size) , // 讀包 ??????????????????int ( * write_packet) ( void * opaque, uint8_t * buf, int buf_size) , // 寫包 ??????????????????int64_t ( * seek) ( void * opaque, int64_t offset, int whence) ) // 調(diào)整文件指針 { ????s- > buffer= buffer; ????s- > buffer_size= buffer_size; ????s- > buf_ptr= buffer; ????s- > opaque= opaque; ????url_resetbuf( s, write_flag? URL_WRONLY : URL_RDONLY) ; ????s- > write_packet= write_packet; ????s- > read_packet= read_packet; ????s- > seek= seek; ????s- > pos = 0; ????s- > must_flush= 0; ????s- > eof_reached= 0; ????s- > error = 0; ????s- > is_streamed= 0; ????s- > max_packet_size= 0; ????s- > update_checksum= NULL ; ????if ( ! read_packet& & ! write_flag) { ????????s- > pos= buffer_size; ????????s- > buf_end= s- > buffer+ buffer_size; ????} ????s- > read_pause= NULL ; ????s- > read_seek= NULL ; ????return 0; }
void * logctx= ap& & ap- > prealloced_context? * ic_ptr : NULL ; // 因為 ap == NULL, 所以 logctx 也 == NULL. ????if ( ! fmt& & ( err = ff_probe_input_buffer( & pb, & fmt, filename, logctx, 0, ????????logctx ? ( * ic_ptr) - > probesize: 0) ) < 0) { ????????goto fail; ????} ????// fmt == NULL 時才執(zhí)行 ff_probe_input_buffer 函數(shù),?因為 fmt 就等于NULL, 成立.
ff_probe_input_buffer函數(shù)的原型: int ff_probe_input_buffer( ByteIOContext* * pb, // 字節(jié)IO上下文, 執(zhí)行url_fopen得到的 ?? ? ? ? ? ? ? ? ? ? ? ? ?AVInputFormat * * fmt, // 輸出參數(shù): 輸入的格式 ?? ? ? ? ? ? ? ? ? ? ? ? ?const char * filename, // 文件名 ?? ? ? ? ? ? ? ? ? ? ? ? ?void * logctx, // NULL ?? ? ? ? ? ? ? ? ? ? ? ? ?unsigned int offset, // 0 ?? ? ? ? ? ? ? ? ? ? ? ? ?unsigned int max_probe_size)? ?// 0
關(guān)鍵的代碼片斷:
?? ? ? ?/* 讀待探測的數(shù)據(jù) */ ????????buf = av_realloc( buf, probe_size+ AVPROBE_PADDING_SIZE) ; ????????if ( ( ret= get_buffer( * pb, buf+ buf_offset, probe_size- buf_offset) ) < 0) { ????????????/* fail if error was not end of file, otherwise, lower score */ ????????????if ( ret! = AVERROR_EOF) { ????????????????av_free( buf) ; ????????????????return ret; ????????????} ????????????score = 0; ????????????ret = 0; /* error was end of file, nothing read */ ????????} ????????pd. buf_size + = ret; ????????pd. buf = & buf[ offset] ; ????????memset ( pd. buf+ pd. buf_size, 0, AVPROBE_PADDING_SIZE) ; ????????/* 猜測文件格式 */ ????????* fmt = av_probe_input_format2( & pd, 1, & score) ;
get_buffer函數(shù), 有兩處比較關(guān)鍵:
int ?get_buffer ( ByteIOContext* s, unsigned char * buf, int size) ; {
??????? /* 省略部分代碼 */ ?? ? ? ?/* 讀包 */ ????????if ( s- > read_packet) ?? ? ? ? ? len = s- > read_packet( s- > opaque, buf, size) ;
??????? /* 省略部分代碼 */
?? ? ? ?/* 填充緩沖 */
??????? fill_buffer(s);
??????? /* 省略部分代碼 */
}
fill_buffer函數(shù) , 有一處比較關(guān)鍵:
static void fill_buffer( ByteIOContext* s) { ??????? /* 省略部分代碼 */ ?? ? ? ?/* 讀包 */ ??????? if ( s- > read_packet) ?? ? ? ? ??len = s- > read_packet( s- > opaque, dst, len) ;
??????? /* 省略部分代碼 */ }
好了, 到第二次探測輸入格式的地方了:
* fmt= av_probe_input_format2( & pd, 1, & score) ;
進(jìn)入av_probe_input_format2函數(shù):
AVInputFormat* av_probe_input_format2( AVProbeData* pd, int is_opened, int * score_max) { ????AVInputFormat * fmt1, * fmt; ????int score; ????fmt = NULL ; ????for ( fmt1= first_iformat; fmt1! = NULL ; fmt1 = fmt1- > next) { ????????if ( ! is_opened= = ! ( fmt1- > flags& AVFMT_NOFILE) ) ????????????continue ;
/* 這次 is_opened == 1, fmt1->flags設(shè)置AVFMT_NOFILE標(biāo)志才時成立?*/
/* 由于 h264_demuxer.flags == AVFMT_GENERIC_INDEX, 所以上面不成立, 繼續(xù)執(zhí)行?*/ ????????score = 0; ????????if ( fmt1- > read_probe) { ????????????score = fmt1- > read_probe( pd) ;? /*?調(diào)用h264_demuxer.h264_probe */ ????????} else if ( fmt1- > extensions) { ????????????if ( av_match_ext( pd- > filename, fmt1- > extensions) ) {? /*?文件名和格式擴展名的匹配 */
/*?h264_demuxer.extensions = "h26l,h264,264" */ ????????????????score = 50; ????????????} ????????} ????????if ( score> * score_max) { ????????????* score_max = score; ????????????fmt = fmt1; ????????} else if ( score = = * score_max) ????????????fmt = NULL ; ????} ????return fmt; }
av_match_ext函數(shù): int av_match_ext( const char * filename, const char * extensions) { ????const char * ext, * p; ????char ext1[ 32] , * q; ????if ( ! filename) ????????return 0; ????ext = strrchr( filename, '.' ) ; ????if ( ext) { ????????ext+ + ; ????????p = extensions; ????????for ( ; ; ) { ????????????q = ext1; ????????????while ( * p ! = '\0' & & * p ! = ',' & & q- ext1< sizeof ( ext1) - 1) ????????????????* q+ + = * p+ + ; ????????????* q = '\0' ; ????????????if ( ! strcasecmp( ext1, ext) ) ????????????????return 1; ????????????if ( * p= = '\0' ) ????????????????break ; ????????????p+ + ; ????????} ????} ????return 0; }
總算探測到輸入格式了.
err = av_open_input_stream( ic_ptr, pb, filename, fmt, ap) ;
int av_open_input_stream(
AVFormatContext* * ic_ptr,? // 輸出參數(shù): 格式上下文 ByteIOContext * pb, ?// 字節(jié)IO上下文
const char * filename,? // 文件名 AVInputFormat * fmt, ?// 輸入的格式
AVFormatParameters* ap)? // 格式參數(shù), 調(diào)用時為NULL { ????int err; ????AVFormatContext * ic; ????AVFormatParameters default_ap;
?? ? // 使用缺省的格式參數(shù) ????if ( ! ap) { ????????ap= & default_ap; ????????memset ( ap, 0, sizeof ( default_ap) ) ; ????} ????if ( ! ap- > prealloced_context) ????????ic = avformat_alloc_context( ) ;? // 分配格式上下文 ????else ????????ic = * ic_ptr; ????if ( ! ic) { ????????err = AVERROR( ENOMEM) ; ????????goto fail; ????}
?? ?// 初始化格式上下文 ????ic- > iformat= fmt;? // 格式 ????ic- > pb = pb;? // 字節(jié)IO上下文 ????ic- > duration= AV_NOPTS_VALUE; ????ic- > start_time= AV_NOPTS_VALUE; ????av_strlcpy( ic- > filename, filename, sizeof ( ic- > filename) ) ;? // 文件名 ????/* 分配私有數(shù)據(jù) */ ????if ( fmt- > priv_data_size> 0) { ????????ic- > priv_data= av_mallocz( fmt- > priv_data_size) ; ????????if ( ! ic- > priv_data) { ????????????err = AVERROR( ENOMEM) ; ????????????goto fail; ????????} ????} else { ????????ic- > priv_data= NULL ; ????}
?? ?// 讀首部 ????if ( ic- > iformat- > read_header) { ????????err = ic- > iformat- > read_header( ic, ap) ; ????????if ( err< 0) ????????????goto fail; ????}
?? ?// 獲得數(shù)據(jù)偏移 ????if ( pb & & ! ic- > data_offset) ????????ic- > data_offset= url_ftell( ic- > pb) ; # if LIBAVFORMAT_VERSION_MAJOR< 53 ????ff_metadata_demux_compat( ic) ; # endif
?? ?// 原始的包緩沖剩余的大小 ????ic- > raw_packet_buffer_remaining_size= RAW_PACKET_BUFFER_SIZE;
?? ? // 輸出參數(shù):? 格式上下文
????* ic_ptr= ic; ????return 0;
}
具體請參看
格式上下文結(jié)構(gòu):
typedef ?struct ?AVFormatContext?{ ????const ?AVClass?* av_class; ?/**< Set by avformat_alloc_context. */
?? ?//?省略部分內(nèi)容 }
AV類結(jié)構(gòu): typedef ?struct ?{ ????/** ?????* The name of the class; usually it is the same name as the ?????* context structure type to which the AVClass is associated. ?????*/ ????const ?char * ?class_name; ????/** ?????* A pointer to a function which returns the name of a context ?????* instance ctx associated with the class. ?????*/ ????const ?char * ?( * item_name) ( void * ?ctx) ; ????/** ?????* a pointer to the first option specified in the class if any or NULL ?????* ?????* @see av_set_default_options() ?????*/ ????const ?struct ?AVOption?* option; ????/** ?????* LIBAVUTIL_VERSION with which this structure was created. ?????* This is used to allow fields to be added without requiring major ?????* version bumps everywhere. ?????*/ ????int ?version; } ?AVClass;
進(jìn)入avformat_alloc_context函數(shù), 分配格式上下文: AVFormatContext* avformat_alloc_context( void ) { ????AVFormatContext * ic; ????ic = av_malloc( sizeof ( AVFormatContext) ) ; ????if ( ! ic) return ic; ????avformat_get_context_defaults( ic) ; ????ic- > av_class= & av_format_context_class; ????return ic; }
static const AVClass av_format_context_class = { "AVFormatContext" , format_to_name, options, LIBAVUTIL_VERSION_INT} ;
進(jìn)入avformat_get_context_defaults函數(shù), 格式獲得缺省上下文: static void avformat_get_context_defaults( AVFormatContext* s) { ????memset ( s, 0, sizeof ( AVFormatContext) ) ; ????s- > av_class= & av_format_context_class; ????av_opt_set_defaults( s) ; }
av_opt_set_defaults函數(shù)就不分析了.?
下面繼續(xù)分析:
err?= ?ic- > iformat- > read_header( ic, ?ap)
以輸入格式為 libavformat/raw.c下的
h264_demuxer 為例:
AVInputFormat h264_demuxer?= ?{ ????"h264" , ????NULL_IF_CONFIG_SMALL( "raw H.264 video format" ) , ????0, ????h264_probe, ????video_read_header, ????ff_raw_read_partial_packet, ????. flags= ?AVFMT_GENERIC_INDEX, ????. extensions?= ?"h26l,h264,264" , ?//FIXME remove after writing mpeg4_probe ????. value?= ?CODEC_ID_H264, } ;
會調(diào)用 video_read_header函數(shù) :
static int video_read_header( AVFormatContext* s, ?????????????????????????????AVFormatParameters * ap) { ????AVStream * st; ????st = av_new_stream( s, 0) ;? // 格式上下文增加一個流 ????if ( ! st) ????????return AVERROR( ENOMEM) ;
?? ? // 初始化流 ????st- > codec- > codec_type= AVMEDIA_TYPE_VIDEO;? //?編碼編碼 器類型 ????st- > codec- > codec_id= s- > iformat- > value;? // 為 CODEC_ID_H264 ????st- > need_parsing= AVSTREAM_PARSE_FULL;? //?需要全分析 ????/* for MJPEG, specify frame rate */ ????/* for MPEG-4 specify it, too (most MPEG-4 streams do not have the fixed_vop_rate set ...)*/ ????if ( ap- > time_base . num) {
????????st- > codec- > time_base = ap- > time_base ; ????} else if ( st- > codec- > codec_id= = CODEC_ID_MJPEG| | ????????????????st- > codec- > codec_id= = CODEC_ID_MPEG4| | ????????????????st- > codec- > codec_id= = CODEC_ID_DIRAC| | ????????????????st- > codec- > codec_id= = CODEC_ID_DNXHD| | ????????????????st- > codec- > codec_id= = CODEC_ID_H264) { ????????st- > codec- > time_base = ( AVRational) { 1, 25} ;? //?設(shè)置時基 ????} ????av_set_pts_info( st, 64, 1, 1200000) ;? //?設(shè)置PTS(顯示時間截)信息 ????return 0; }
進(jìn)入
av_new_stream函數(shù):
AVStream* av_new_stream( AVFormatContext* s, int id) { ????AVStream * st; ????int i;
?? ?// 格式上下文不能太多流 ????if ( s- > nb_streams> = MAX_STREAMS) ????????return NULL ;
?? ?// 分配一個流 ????st = av_mallocz( sizeof ( AVStream) ) ; ????if ( ! st) ????????return NULL ;
?? ?// 分配解碼器上下文 ????st- > codec= avcodec_alloc_context( ) ; ????if ( s- > iformat) { ????????/* no default bitrate if decoding */ ????????st- > codec- > bit_rate= 0; ????} ????st- > index= s- > nb_streams;? // 流索引 ????st- > id = id;? // ID, 為0 ????st- > start_time= AV_NOPTS_VALUE;? // 開始時間 ????st- > duration= AV_NOPTS_VALUE; ????????/* we set the current DTS to 0 so that formats without any timestamps ???????????but durations get some timestamps, formats with some unknown ???????????timestamps have their first few packets buffered and the ???????????timestamps corrected before they are returned to the user */ ????st- > cur_dts= 0;? // 當(dāng)前的解碼時間截 ????st- > first_dts= AV_NOPTS_VALUE;? // 起始的 解碼時間截 ????st- > probe_packets= MAX_PROBE_PACKETS;? //?探測的最大包數(shù) ????/* default pts setting is MPEG-like */
????av_set_pts_info( st, 33, 1, 90000) ;? //?設(shè)置PTS顯示時間截信息 ????st- > last_IP_pts= AV_NOPTS_VALUE;? ????for ( i= 0; i< MAX_REORDER_DELAY+ 1; i+ + ) ????????st- > pts_buffer[ i] = AV_NOPTS_VALUE; ????st- > reference_dts= AV_NOPTS_VALUE; ????st- > sample_aspect_ratio= ( AVRational) { 0, 1} ; ????s- > streams[ s- > nb_streams+ + ] = st;? //?記錄流, 同時流數(shù)加一 ????return st; }
分配編碼解碼器上下文:
?
static const AVClass av_codec_context_class = { "AVCodecContext" , context_to_name, options, LIBAVUTIL_VERSION_INT} ;
void avcodec_get_context_defaults2( AVCodecContext* s, enum AVMediaType codec_type) { ????int flags= 0; ????memset ( s, 0, sizeof ( AVCodecContext) ) ; ????s- > av_class= & av_codec_context_class; ????s- > codec_type= codec_type; ????if ( codec_type= = AVMEDIA_TYPE_AUDIO) ????????flags= AV_OPT_FLAG_AUDIO_PARAM; ????else if ( codec_type= = AVMEDIA_TYPE_VIDEO) ????????flags= AV_OPT_FLAG_VIDEO_PARAM; ????else if ( codec_type= = AVMEDIA_TYPE_SUBTITLE) ????????flags= AV_OPT_FLAG_SUBTITLE_PARAM; ????av_opt_set_defaults2( s, flags, flags) ; ????s- > time_base = ( AVRational) { 0, 1} ; ????s- > get_buffer= avcodec_default_get_buffer; ????s- > release_buffer= avcodec_default_release_buffer; ????s- > get_format= avcodec_default_get_format; ????s- > execute= avcodec_default_execute; ????s- > execute2= avcodec_default_execute2; ????s- > sample_aspect_ratio= ( AVRational) { 0, 1} ; ????s- > pix_fmt= PIX_FMT_NONE; ????s- > sample_fmt= SAMPLE_FMT_NONE; ????s- > palctrl= NULL ; ????s- > reget_buffer= avcodec_default_reget_buffer; ????s- > reordered_opaque= AV_NOPTS_VALUE; } AVCodecContext * avcodec_alloc_context2( enum AVMediaType codec_type) { ????AVCodecContext * avctx= av_malloc( sizeof ( AVCodecContext) ) ; ????if ( avctx= = NULL ) return NULL ; ????avcodec_get_context_defaults2( avctx, codec_type) ; ????return avctx; } void avcodec_get_context_defaults( AVCodecContext* s) { ????avcodec_get_context_defaults2( s, AVMEDIA_TYPE_UNKNOWN) ; } AVCodecContext * avcodec_alloc_context( void ) { ????return avcodec_alloc_context2( AVMEDIA_TYPE_UNKNOWN) ; }
總結(jié)
以上是生活随笔 為你收集整理的ffmpeg分析系列 的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔 推薦給好友。