hello,各位好,本人是一名嵌入式軟件工程師,目前正使用ffmpeg開發一款嵌入式多媒體播放器,《ffmpeg分析》系列博文是本人在閱讀ffmpeg源代碼時所做的筆記,希望對各位有點幫助。分析過程結合下面的例程:
????http://dranger.com/ffmpeg/tutorial05.c
?
一. 調用av_register_all函數注冊所有的格式和編碼解碼器.
1.1 先調用avcodec_register_all函數注冊所有的編碼解碼器.
1. 下面列出與H264相關的注冊:
?? ?// 注冊硬件加速器 ????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. 分析一下注冊函數, 以avcodec_register函數為例:
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函數把輸入的AVCodec連成一個鏈表, 其它注冊函數與之類似, 就不多言了.
4. 上面調用了avcodec_init函數:
void avcodec_init(void) { ????static int initialized= 0; ?????if (initialized!= 0) ????????return; ????initialized = 1; ????dsputil_static_init (); } |
?? ?這個函數只會真正執行一次.
5. 上面調用了dsputil_static_init函數:
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; } |
?? ?
?? ?可以看到, 它初始化了一些靜態數據.
1.2 注冊所有的格式和外部庫及協議.
1. 下面列出與H264相關的注冊:
?? ?// 注冊分離器和混合器 ????REGISTER_MUXDEMUX (H264, h264); ????// 注冊文件協議 ????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);} |
?? ?這些注冊函數與avcodec_register函數類似, 就不多言了.
URL協議結構:
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協議:
staticint 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函數注冊了file協議:
#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); ????/* 省略部分代碼 */ } |
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
探測數據結構:
/** 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的探測函數:
staticint 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; } |
視頻讀首部函數:
staticint 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; } |
原始地讀實際的包函數:
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; } |
原始地寫包函數:
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函數注冊了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); ????/* 省略部分代碼 */ } |
把注冊格式函數也貼出來吧:
/** 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
調用av_open_input_file(&pFormatCtx, is->filename,NULL, 0,NULL)函數打開輸入的文件.
1. 分析一下函數原型:
int av_open_input_file(AVFormatContext**ic_ptr,// 輸出參數: 格式上下文
???????????????????????const char *filename,// 文件名
???????????????????????AVInputFormat *fmt,// 輸入的格式, 為NULL, 即未知
???????????????????????int buf_size,// 緩沖的大小, 為0
???????????????????????AVFormatParameters *ap);// 格式的參數, 為NULL
2. 初始化探測數據:
????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);
????}
????進入av_probe_input_format函數:
AVInputFormat *av_probe_input_format(AVProbeData*pd, int is_opened) {
????int score=0;
????return av_probe_input_format2(pd, is_opened,&score);
}
????進入av_probe_input_format2函數:
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 沒有設置 AVFMT_NOFILE 標志時成立
????????????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; // 字節IO上下文 ????????if ((err=url_fopen(&pb, filename, URL_RDONLY))< 0) { // 只讀方式打開輸入的文件 ????????????goto fail; ????????} ????????if (buf_size> 0) { // 因 buf_size == 0, 不成立 ????????????url_setbufsize(pb, buf_size); ?? ? ? ?} |
進入url_fopen函數:
int url_fopen(ByteIOContext**s,// 輸出參數: 字節IO上下文 ??????????????const char *filename,// 文件名 ??????????????int flags)// 標志 { ????URLContext *h;// URL(統一資源定位)上下文 ????int err; ????err = url_open(&h, filename, flags);// 打開URL ????if (err< 0) ????????return err; ????err = url_fdopen(s, h);// 用URL上下文打開字節IO上下文 ????if (err< 0) { ????????url_close(h); ????????return err; ????} ????return 0; } |
進入url_open函數:
int url_open(URLContext**puc,? //?輸出參數:?URL上下文 constchar *filename,? // 文件名 int flags)?// 標志 { ????URLProtocol *up; ????const char*p; ????char proto_str[128],*q; ????// 提取協議 ????p = filename; ????q = proto_str; ????while (*p!= '\0' &&*p !=':') { // 未結束, 并未遇到分隔符':' ????????if (!isalpha(*p))// 如果不是英文字母 ????????????goto file_proto; ????????if ((q- proto_str)< sizeof(proto_str)- 1) ????????????*q++= *p;// 記錄協議字符串 ????????p++; ????} ????if (*p== '\0' || is_dos_path(filename)){ // 如果上面是因為結束而跳出, 或且 文件名是DOS路徑 ????file_proto: ????????strcpy(proto_str,"file");// 文件協議 ????} else { ????????*q ='\0'; // 追加結束符 ????} ????up = first_protocol; ????while (up!= NULL) { ????????if (!strcmp(proto_str, up->name))// 協議匹配 ????????????return url_open_protocol (puc, up, filename, flags);// 用這個協議打開URL ????????up = up->next; ????} ????*puc = NULL; ????return AVERROR(ENOENT); } |
進入
url_open_protocol函數:
int url_open_protocol(URLContext **puc,? //?輸出參數:?URL上下文 struct URLProtocol*up,? // URL協議 constchar *filename,? // 文件名 int flags)?// 標志 { ????URLContext *uc; ????int err; ????// 網絡初始化 #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協議 ????uc->flags= flags;?// 標志 ????uc->is_streamed= 0;?// 默認不是流, 可以在up->url_open函數里修改 ????uc->max_packet_size= 0; //? 包最大多大,?默認為0,可以在up->url_open函數里修改 ????// 打開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"))?//?或且是文件協議 ????????//?如果不是流并且不可以url_seek ????????if(!uc->is_streamed&& url_seek(uc, 0,SEEK_SET) < 0) ????????????uc->is_streamed= 1;//?強制為流 ????// 輸出 參數:?URL上下文 ????*puc= uc; ????return 0; ?fail: ????*puc = NULL; #if CONFIG_NETWORK ????ff_network_close(); #endif ????return err; } |
先來看看url_get_max_packet_size函數
int?url_get_max_packet_size(URLContext?*h) { ????return?h->max_packet_size;?// 包最大多大, 被上面初始化為0 } |
進入url_fdopen函數:
int url_fdopen( ByteIOContext**s,// 輸出參數: 字節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));// 分配字節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;// 讀暫停函數 ????????(*s)->read_seek= (int64_t(*)(void*, int, int64_t,int))h->prot->url_read_seek;// 讀seek函數 ????} ????return 0; } |
進入init_put_byte函數:
int init_put_byte(ByteIOContext*s, // 字節IO上下文
??????????????????unsigned char *buffer,// 緩沖
??????????????????int buffer_size,// 緩沖的大小
??????????????????int write_flag,// 寫標志
??????????????????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))// 調整文件指針
{
????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 時才執行 ff_probe_input_buffer 函數,?因為 fmt 就等于NULL, 成立.
ff_probe_input_buffer函數的原型:
int ff_probe_input_buffer(ByteIOContext**pb,// 字節IO上下文, 執行url_fopen得到的 ?? ? ? ? ? ? ? ? ? ? ? ? ?AVInputFormat **fmt,// 輸出參數: 輸入的格式 ?? ? ? ? ? ? ? ? ? ? ? ? ?const char *filename,// 文件名 ?? ? ? ? ? ? ? ? ? ? ? ? ?void *logctx, // NULL ?? ? ? ? ? ? ? ? ? ? ? ? ?unsigned int offset, // 0 ?? ? ? ? ? ? ? ? ? ? ? ? ?unsigned int max_probe_size)? ?// 0 |
關鍵的代碼片斷:
?? ? ? ?/* 讀待探測的數據 */ ????????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函數, 有兩處比較關鍵:
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函數, 有一處比較關鍵:
staticvoid fill_buffer(ByteIOContext*s) { ??????? /* 省略部分代碼 */ ?? ? ? ?/* 讀包 */ ??????? if(s->read_packet) ?? ? ? ? ??len = s->read_packet(s->opaque, dst, len); ??????? /* 省略部分代碼 */ } |
好了, 到第二次探測輸入格式的地方了:
*fmt= av_probe_input_format2(&pd, 1,&score); |
進入av_probe_input_format2函數:
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設置AVFMT_NOFILE標志才時成立?*/ /* 由于 h264_demuxer.flags == AVFMT_GENERIC_INDEX, 所以上面不成立, 繼續執行?*/ ????????score = 0; ????????if (fmt1->read_probe){ ????????????score = fmt1->read_probe(pd);?/*?調用h264_demuxer.h264_probe */ ????????} elseif (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; ????????}elseif (score == *score_max) ????????????fmt = NULL; ????} ????return fmt; } |
av_match_ext函數:
int av_match_ext(constchar *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,?// 輸出參數: 格式上下文 ByteIOContext *pb,? // 字節IO上下文 constchar *filename,?// 文件名 AVInputFormat *fmt,? // 輸入的格式 AVFormatParameters*ap)?// 格式參數, 調用時為NULL { ????int err; ????AVFormatContext *ic; ????AVFormatParameters default_ap; ?? ? // 使用缺省的格式參數 ????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;?// 字節IO上下文 ????ic->duration= AV_NOPTS_VALUE; ????ic->start_time= AV_NOPTS_VALUE; ????av_strlcpy(ic->filename, filename,sizeof(ic->filename));?// 文件名 ????/* 分配私有數據 */ ????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; ????} ?? ?// 獲得數據偏移 ????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; ?? ? // 輸出參數:?格式上下文 ????*ic_ptr= ic; ????return 0; } |
具體請參看
格式上下文結構:
typedef?struct?AVFormatContext?{ ????const?AVClass?*av_class;?/**< Set by avformat_alloc_context. */ ?? ?//?省略部分內容 } |
AV類結構:
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;
|
進入avformat_alloc_context函數, 分配格式上下文:
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; } |
staticconst AVClass av_format_context_class = { "AVFormatContext", format_to_name, options, LIBAVUTIL_VERSION_INT}; |
進入avformat_get_context_defaults函數, 格式獲得缺省上下文:
staticvoid 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函數就不分析了.?
下面繼續分析:
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, };
|
會調用video_read_header函數:
staticint 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};?//?設置時基 ????} ????av_set_pts_info(st, 64, 1, 1200000);?//?設置PTS(顯示時間截)信息 ????return 0; } |
進入av_new_stream函數:
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;?// 當前的解碼時間截 ????st->first_dts= AV_NOPTS_VALUE;?// 起始的解碼時間截 ????st->probe_packets= MAX_PROBE_PACKETS;?//?探測的最大包數 ????/* default pts setting is MPEG-like */ ????av_set_pts_info(st, 33, 1, 90000);?//?設置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;?//?記錄流, 同時流數加一 ????return st; } |
分配編碼解碼器上下文:
?
staticconst 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);
}