ffmpeg分析系列

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;

}


具體請參看
ffmpeg分析系列之三(輸入輸出格式)

格式上下文結構:

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);
}


本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/454947.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/454947.shtml
英文地址,請注明出處:http://en.pswp.cn/news/454947.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Linux kernel的中斷子系統之(二):IRQ Domain介紹

返回目錄&#xff1a;《ARM-Linux中斷系統》。 總結&#xff1a;一、二概述了軟硬件不同角度的IRQ Number和HW Interrupt ID&#xff0c;這就需要他們之間架個橋梁。 三介紹了架設這種橋梁的幾種方式&#xff1a;Linear、Radix Tree和no map。 四介紹了兩種基礎數據結構描述中斷…

mysql返回yyyy mm dd_怎么把取出mysql數據庫中的yyyy-MM-dd日期轉成yyyy年MM月dd日格式...

您好&#xff0c;通過兩個個步驟可以完成轉換&#xff1a;第一步&#xff1a;日期處理可以在模板數據集中通過sql語句轉換&#xff0c;轉換方式方式如下&#xff1a;SELECT DATE_FORMAT(NOW(),%Y) YEAR輸出結果&#xff1a;2018SELECT DATE_F…

關于JS的時間控制

關于JS的時間控制實現動態效果及實例操作 <script>BOM //Bowers Object Model 瀏覽器對象模型setTimeout() // 延遲執行一次setInterval() // 間隔執行var a 300;window.setTimeout(abc(a),3000); // 自定義函數賦值function abc(i){alert(i);}//setInterv…

感動一生的幾句話

為什么80%的碼農都做不了架構師&#xff1f;>>> 很多東西就掌握在我們手中&#xff1a; 比如快樂&#xff0c;你不快樂&#xff0c;誰會同情你的悲傷&#xff1b; 比如堅強&#xff0c;你不堅強&#xff0c;誰會憐憫你的懦弱&#xff1b; 比如努力&#xff0c;你不…

mysql5.6 memcached_MySQL 5.6 安裝配置InnoDB memcached Plugin

準備工作, 安裝libmemached包&#xff0c;提供一些memcat/cp/dump命令&#xff0c;方便測試。# yum install libmemcached.x86_64 -y1. Setup required tables.mysql> source MYSQL_HOME/share/innodb_memcached_config.sqlQuery OK, 1 row affected (0.00 sec)Database cha…

Java 監聽器,國際化

1. 監聽器 1.1 概述 監聽器&#xff1a; 主要是用來監聽特定對象的創建或銷毀、屬性的變化的&#xff01; 是一個實現特定接口的普通java類&#xff01; 對象&#xff1a; 自己創建自己用 (不用監聽) 別人創建自己用 &#xff08;需要監聽&#xff09; Servlet中哪些對象需要監…

patator mysql 字典_利用patator進行子域名爆破

前言:原來朋友寫的一個子域名爆破工具挺好用,這前幾天API接口關了.痛苦萬分.自己也寫了一個類似的但是不咋穩定.特地google找了下 找到一款patator.效果和速度還是不錯的。knock的速度真心受不了啊patator是由Python寫的 不用安裝下載即可.下載地址&#xff1a;http://code.goo…

div 超出高度滾動條,超出寬度點點點

超出高度滾動條style"width:230px; height: 180px; overflow: auto;"超出寬度點點點style"width: 220px; overflow: hidden; white-space:nowrap; text-overflow:ellipsis;"轉載于:https://www.cnblogs.com/thinkingthigh/p/7603703.html

mp4(H264容器)的詳細文件格式分析

十六進制碼流分析&#xff1a; ftyp Box 00 00 00 1C: size ,28,表示此BOX有28個字節&#xff0c;表示長度的四個字節也計算在內。以下同 66 74 79 70: type,表示BOX TYPE,此處為ftyp 6D 70 34 32: 可能是兼容的格式信息&#xff0c;/mp42 00 00 00…

hdu 5925 搜索

題意&#xff1a;一個圖&#xff0c;n個障礙&#xff0c;求聯通塊 思路&#xff1a; 圖很大&#xff0c;障礙物很少。把聯通的障礙物塊摳出來&#xff0c;然后暴力。 代碼&#xff1a; #include<bits/stdc.h> using namespace std; #define MEM(a,b) memset(a,b,sizeof(a…

分析數據庫CitusDB:提供彈性計算能力

本文講的是分析數據庫CitusDB&#xff1a;提供彈性計算能力,企業數據庫市場很龐大&#xff0c;在這個領域既有Oracle這樣行家&#xff0c;也有IBM(DB2)和微軟(SQL Server)這樣的跨界巨頭。它們都與中小企業常用到的開源數據庫MySQL一樣&#xff0c;都屬于傳統關系型數據庫。似乎…

mysql不能創建innodb類型表_MYSQL have_innodb DISABLED無法創建innodb類型的表

今天在一臺MYSQL服務器上發現&#xff0c;明明用了engineinnodb創建的表&#xff0c;結果創建出來卻成了myisam的表。再看show variables like %innodb%;have_innodb 成了DISABLED。經過一番試驗&#xff0c;發現是我關閉數據庫后&#xff0c;直接刪除ibdata1文件造成的。刪除該…

[bzoj1059]矩陣游戲

雖然是一道水難題&#xff0c;但是我這種蒟蒻還是要講一講的。 Description 小Q是一個非常聰明的孩子&#xff0c;除了國際象棋&#xff0c;他還很喜歡玩一個電腦益智游戲——矩陣游戲。矩陣游戲在一個N*N黑白方陣進行&#xff08;如同國際象棋一般&#xff0c;只是顏色是隨意的…

H264 RTP頭分析

h264 RTP頭解析流程 結合NALDecoder.c分析 協議分析 &#xff1a;每一個RTP數據報都由頭部&#xff08;Header&#xff09;和負載&#xff08;Payload&#xff09;兩個部分組成&#xff0c;其中頭部前 12 個字節的含義是固定的&#xff0c;而負載則可以是音頻或者視頻數據。 一…

golang mysql 插入_Mysql學習(一)添加一個新的用戶并用golang操作Mysql

Mysql添加一個新的用戶并賦予權限添加一個自己的用戶到mysql首先我們需要先用root用戶登錄mysql&#xff0c;但是剛安裝完沒有密碼&#xff0c;我們先跳過密碼ailumiyanaailumiyana:~/Git_Project/Go_Test$ sudo mysqld_safe --skip-grant-tables2019-01-07T01:35:51.559420Z m…

云計算構建基石之Hyper-V:虛擬機管理

本文講的是云計算構建基石之Hyper-V:虛擬機管理,作為云計算的重要基石&#xff0c;虛擬化技術的好壞起著關鍵作用。Hyper-V作為微軟重要的虛擬化解決技術&#xff0c;在微軟云計算構建解決方案中&#xff0c;更是關鍵至關鍵&#xff0c;基礎之基礎。在本系列文章中&#xff0c;…

Delphi語言最好的JSON代碼庫 mORMot學習筆記1

mORMot沒有控件安裝&#xff0c;直接添加到lib路徑,工程中直接添加syncommons&#xff0c;syndb等到uses里 --------------------------------------------------------- 在進行網絡編程中需要JSON對象的構建與解析&#xff0c;這個Delphi XE自帶&#xff1a;{$IF CompilerVers…

3GP文件格式分析

1. 概述現在很多智能手機都支持多媒體功能&#xff0c;特別是音頻和視頻播放功能&#xff0c;而3GP文件格式是手機端普遍支持的視頻文件格式。目前很多手機都支持h263視頻編碼格式的視頻文件播放&#xff0c;還有些手機支持h264。音頻文件格式普遍支持amr&#xff0c;有些手…

mysql group concat_MySQL 的 GROUP_CONCAT 函數詳解

GROUP_CONCAT(expr) 函數會從 expr 中連接所有非 NULL 的字符串。如果沒有非 NULL 的字符串&#xff0c;那么它就會返回 NULL。語法如下&#xff1a;GROUP_CONCAT 語法規則它在遞歸查詢中用的比較多&#xff0c;但要使用好它并不容易。所以讓我們一起來看看吧&#xff1a;假設有…

ORACLE數據庫 常用命令和Sql常用語句

ORACLE 賬號相關 如何獲取表及權限 1.COPY表空間backup scottexp登錄管理員賬號system2.創建用戶 create user han identified(認證) by mima default tablespace users&#xff08;默認的表空間&#xff09; quota&#xff08;配額&#xff09;10M on users;創建賬號分配權限g…