分享

ffmpeg分析系列

 SamBookshelf 2013-07-10
hello,各位好,本人是一名嵌入式软件工程师,目前正使用ffmpeg开发一款嵌入式多媒体播放器,《ffmpeg分析》系列博文是本人在阅读ffmpeg源代码时所做的笔记,希望对各位有点帮助。分析过程结合下面的例程:
 
一. 调用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./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./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);
}


    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约