您的位置:首页 > 运维架构

ffmpeg源码分析--3.avformat_alloc_context与avformat_open_input

2016-09-05 17:07 1641 查看
一. avformat_alloc_context
1.1 在./libavformat/options.c中

AVFormatContext *avformat_alloc_context(void)

{

    AVFormatContext *ic;

    ic = av_malloc(sizeof(AVFormatContext));
  //为AVFormatContex分配内存

    avformat_get_context_defaults(ic);
        //下面就是一些初始化的操作

    ic->internal = av_mallocz(sizeof(*ic->internal));

    if (!ic->internal) {

        avformat_free_context(ic);

        return NULL;

    }

    ic->internal->offset = AV_NOPTS_VALUE;

    ic->internal->raw_packet_buffer_remaining_size = RAW_PACKET_BUFFER_SIZE;

    return ic;

}

为AVFormatContext分配内存并初始化
二. avformat_open_init
2.1 avformat_open_init总体分析
在libavformat/utils.c中
avformat_open_input(&ic, is->filename, is->iformat, &format_opts);
{
     1. avformat_alloc_context                          ::分配一个AVFormatContext结构体
     2.init_input(s, filename, &tmp);
      --> av_probe_input_format2                     ::没有打开媒体文件之前的probe
      -->s->io_open                                           ::打开媒体文件
        -->io_open_default
        -->ffio_open_whitelist
          -->ffurl_alloc
              -->url_find_protocol   (file protocol)
              -->url_alloc_for_protocol
          -->ffurl_connect ::uc->prot->url_open
               -->file_open
               -->avpriv_open   ::即调用系统的open
      -->av_probe_input_buffer2          
                  ::打开媒体文件之后的probe
          --> avio_read   ::s->read_packet
              --> ffurl_read
              --> file_read ::即调用系统的read
          -->av_probe_input_format2  
            --> av_probe_input_format3
               --> matroska_probe
               --> mov_probe
    3.s->iformat->read_header(s)                     ::读取媒体文件中的信息并分析
         --> mov_read_header                            
}
2.2代码分析 
2.2.1 avformat_open_input
            --> init_input

static int init_input(AVFormatContext *s, const char *filename, AVDictionary **options)

{

    int ret; 

    AVProbeData pd = { filename, NULL, 0 }; 

    int score = AVPROBE_SCORE_RETRY;

    if (s->pb) {
                   //如果用户指定了必须使用哪个iformat则进入下面的流程

        ...  //这儿略过        

        return 0;

    } 

    if ((s->iformat && s->iformat->flags & AVFMT_NOFILE) ||

        (!s->iformat && (s->iformat = av_probe_input_format2(&pd, 0, &score))))

        return score;

    if ((ret = s->io_open(s, &s->pb, filename, AVIO_FLAG_READ | s->avio_flags, options)) < 0) 

        return ret; 

    if (s->iformat)

        return 0;

    return av_probe_input_buffer2(s->pb, &s->iformat, filename, s, 0, s->format_probesize);

}

说明:
a.av_probe_input_format2 时score=25,probe出来的probe_score=0,
probe_scoreiformat=NULL

b. av_probe_input_buffer2中也调用了av_probe_input_format2只不过中间的参数is_opened是1
说明文件己被打开
2.2.2 
avformat_open_input
--> init_input
  
  --> av_probe_input_buffer2

int av_probe_input_buffer2(AVIOContext *pb, AVInputFormat **fmt,

                          const char *filename, void *logctx,

                          unsigned int offset, unsigned int max_probe_size)

{

    AVProbeData pd = { filename ? filename : "" };

    uint8_t *buf = NULL;

    int ret = 0, probe_size, buf_offset = 0;

    int score = 0;

    int ret2;

    if (!max_probe_size)

        max_probe_size = PROBE_BUF_MAX;

    else if (max_probe_size < PROBE_BUF_MIN) {

        av_log(logctx, AV_LOG_ERROR,

               "Specified probe size value %u cannot be < %u\n", max_probe_size, PROBE_BUF_MIN);

        return AVERROR(EINVAL);

    }

    if (offset >= max_probe_size)

        return AVERROR(EINVAL);

    if (pb->av_class) {

        uint8_t *mime_type_opt = NULL;

        char *semi;

        av_opt_get(pb, "mime_type", AV_OPT_SEARCH_CHILDREN, &mime_type_opt);

        pd.mime_type = (const char *)mime_type_opt;

        semi = pd.mime_type ? strchr(pd.mime_type, ';') : NULL;

        if (semi) {

            *semi = '\0';

        }

    }

    for (probe_size = PROBE_BUF_MIN; probe_size <= max_probe_size && !*fmt;

         probe_size = FFMIN(probe_size << 1,

                            FFMAX(max_probe_size, probe_size + 1))) {

        score = probe_size < max_probe_size ? AVPROBE_SCORE_RETRY : 0;

        //分配内存为读取媒体文件的内容作准备

        av_reallocp(&buf, probe_size + AVPROBE_PADDING_SIZE);

        //读取媒体文件的内容作,就是一个调用read的过程

        avio_read(pb, buf + buf_offset, probe_size - buf_offset);

        buf_offset += ret;

        if (buf_offset < offset)

            continue;

        pd.buf_size = buf_offset - offset;

        pd.buf = &buf[offset];

        memset(pd.buf + pd.buf_size, 0, AVPROBE_PADDING_SIZE);

        /* Guess file format. */

        *fmt = av_probe_input_format2(&pd, 1, &score);
   //对媒体文件的内容进行probe看是属于哪个类型

        if (*fmt) {

            /* This can only be true in the
last iteration. */

            if (score <= AVPROBE_SCORE_RETRY) {

                av_log(logctx, AV_LOG_WARNING,

                       "Format %s detected only with low score of %d, "

                       "misdetection possible!\n", (*fmt)->name, score);

            } else

                av_log(logctx, AV_LOG_DEBUG,

                       "Format %s probed with size=%d and score=%d\n",

                       (*fmt)->name, probe_size, score);

        }

    }

    if (!*fmt)

        ret = AVERROR_INVALIDDATA;

fail:

    /* Rewind. Reuse probe buffer to avoid
seeking. */

    ret2 = ffio_rewind_with_probe_data(pb, &buf, buf_offset);

    if (ret >= 0)

        ret = ret2;

    av_freep(&pd.mime_type);

    return ret < 0 ? ret : score;

}

2.2.3 
avformat_open_input
--> init_input
  
  --> av_probe_input_buffer2
        -->av_probe_input_format3

AVInputFormat *av_probe_input_format3(AVProbeData *pd, int is_opened,

                                      int *score_ret)

{

    AVProbeData lpd = *pd;

    AVInputFormat *fmt1 = NULL, *fmt;

    int score, score_max = 0;

    const static uint8_t zerobuffer[AVPROBE_PADDING_SIZE];

    enum nodat {

        NO_ID3,

        ID3_ALMOST_GREATER_PROBE,

        ID3_GREATER_PROBE,

        ID3_GREATER_MAX_PROBE,

    } nodat = NO_ID3;

    if (!lpd.buf)

        lpd.buf = (unsigned char *) zerobuffer;

    if (lpd.buf_size > 10 && ff_id3v2_match(lpd.buf, ID3v2_DEFAULT_MAGIC)) {

        int id3len = ff_id3v2_tag_len(lpd.buf);

        if (lpd.buf_size > id3len + 16) {

            if (lpd.buf_size < 2LL*id3len + 16)

                nodat = ID3_ALMOST_GREATER_PROBE;

            lpd.buf += id3len;

            lpd.buf_size -= id3len;

        } else if (id3len >= PROBE_BUF_MAX) {

            nodat = ID3_GREATER_MAX_PROBE;

        } else

            nodat = ID3_GREATER_PROBE;

    }

    fmt = NULL;

    while ((fmt1 = av_iformat_next(fmt1))) {

        //当is_opened=0时,只对alsa fbdev rtp rtsp这样flag=AVFMT_NOFILE的AVInputFormat调用probe

        //当is_opened=1时,只对ac3 dts h263 h264 这样flag不含AVFMT_NOFILE的调用probe

        if (!is_opened == !(fmt1->flags & AVFMT_NOFILE) && strcmp(fmt1->name, "image2"))

            continue;

        score = 0;

        if (fmt1->read_probe) {

            score = fmt1->read_probe(&lpd);
        //lpd中的buf是媒体文件中的头2048个字节

            if (score)

                av_log(NULL, AV_LOG_TRACE, "Probing
%s score:%d size:%d\n", fmt1->name, score, lpd.buf_size);

            if (fmt1->extensions && av_match_ext(lpd.filename, fmt1->extensions)) {

                switch (nodat) {

                case NO_ID3:

                    score = FFMAX(score, 1);

                    break;

                case ID3_GREATER_PROBE:

                case ID3_ALMOST_GREATER_PROBE:

                    score = FFMAX(score, AVPROBE_SCORE_EXTENSION / 2 - 1);

                    break;

                case ID3_GREATER_MAX_PROBE:

                    score = FFMAX(score, AVPROBE_SCORE_EXTENSION);

                    break;

                }

            }

        } else if (fmt1->extensions) {

            if (av_match_ext(lpd.filename, fmt1->extensions))

                score = AVPROBE_SCORE_EXTENSION;

        }

        if (av_match_name(lpd.mime_type, fmt1->mime_type))

            score = FFMAX(score, AVPROBE_SCORE_MIME);

        if (score > score_max) {
   //循环结束之后就是要取最大的score所代表的fmt

            score_max = score;

            fmt = fmt1;

        } else if (score == score_max)

            fmt = NULL;

    }

    if (nodat == ID3_GREATER_PROBE)

        score_max = FFMIN(AVPROBE_SCORE_EXTENSION / 2 - 1, score_max);

    *score_ret = score_max;             //将probe中产生的最大的score与fmt返回

    return fmt;

}

s->iformat->name=mov,mp4,m4a,3gp,3g2,mj2
s->iformat->name=matroska,webm

三. 媒体文件的读写
3.1 媒体文件的打开过程
avformat_open_input
init_input
    --> s->io_open     
       -->io_open_default                   ::libavformat/options.c
            --> ffio_open_whitelist         ::libavformat/aviobuf.c
                 --> ffurl_open_whitelist   ::libavformat/avio.c
                    -->ffurl_connect           ::libavformat/avio.c
                      --> file_open             ::libavformat/file.c 
AVFormatContext s;
s->io_open(s, &s->pb, filename, AVIO_FLAG_READ | s->avio_flags, options)
3.2代码分析
3.2.1 avio层

int ffio_open_whitelist(AVIOContext **s, const char *filename, int flags,

                         const AVIOInterruptCB *int_cb, AVDictionary **options,

                         const char *whitelist

                        )

{

    ffurl_open_whitelist(&h, filename, flags, int_cb, options, whitelist);

    --> furl_alloc(puc, filename, flags, int_cb);

        --> p = url_find_protocol(filename);  //根据文件名判断protocol类型,并返回protocol的指针             

        --> url_alloc_for_protocol(puc, p, filename, flags, int_cb);  //分配内存并初始化p

    --> ffurl_connect(*puc, options);

    ffio_fdopen(s, h);

    return 0;

}

3.2.2 avio层

static struct URLProtocol *url_find_protocol(const char *filename)

{

    URLProtocol *up = NULL;

    char proto_str[128], proto_nested[128], *ptr;

    size_t proto_len = strspn(filename, URL_SCHEME_CHARS);

    //根据文件名来判断protocol类型,若文件名中没有:则为file类型的protocol

    if (filename[proto_len] != ':' &&

        (strncmp(filename, "subfile,", 8) || !strchr(filename + proto_len + 1, ':')) ||

        is_dos_path(filename))

        strcpy(proto_str, "file");

    else

        av_strlcpy(proto_str, filename,

                   FFMIN(proto_len + 1, sizeof(proto_str)));

    if ((ptr = strchr(proto_str, ',')))

        *ptr = '\0';

    av_strlcpy(proto_nested, proto_str, sizeof(proto_nested));

    if ((ptr = strchr(proto_nested, '+')))

        *ptr = '\0';

    while (up = ffurl_protocol_next(up)) {
     //查找到file类型的URLProtocol并返回

        if (!strcmp(proto_str, up->name))

            break;

        if (up->flags & URL_PROTOCOL_FLAG_NESTED_SCHEME &&

            !strcmp(proto_nested, up->name))

            break;

    }

    return up;

}

注:ffmpeg所支持的所有的protocol都在libavformat/Makefile中以CONFIG_XXX_PROTOCOL标志
3.2.3 avio层

int ffurl_connect(URLContext *uc, AVDictionary **options)

{

    //uc->prot->url_open是真正的打开文件的函数

    uc->prot->url_open2 ? uc->prot->url_open2(uc,
uc->filename, uc->flags, options) :

        uc->prot->url_open(uc, uc->filename, uc->flags); 
      

    return 0;

}

3.2.4 这就到了protocol层
libavformat/file.c

static int file_open(URLContext *h, const char *filename, int flags)

{

    FileContext *c = h->priv_data;

    int fd;  

    

    fd = avpriv_open(filename, access, 0666);

       --> 这个就是open函数的最后一层封装    

    c->fd = fd;

    return 0;

}

3.3 媒体文件的读过程

3.3.1 读写函数的初始化过程

ffio_open_whitelist                           ::libavformat/aviobuf.c

ffio_fdopen 初始代读写函数                ::libavformat/aviobuf.c        

avio_alloc_context                            ::libavformat/aviobuf.c

ffio_init_context                                ::libavformat/aviobuf.c           

ffio_init_context                                ::libavformat/aviobuf.c

   s->write_packet    = write_packet;  :: ffurl_write  终于找到了

    s->read_packet     = read_packet;   :: ffurl_read

3.3.2 读写函数的调用过程

av_probe_input_buffer2        ::libavformat/format.c

avio_read                              ::libavformat/aviobuf.c   ::ffmpeg提供的read接口

    s->read_packet
    ffurl_read                                ::libavformat/avio.c

    retry_transfer_wrapper           ::libavformat/avio.c
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: