ffmpeg的avformat_alloc_context()分析过程(九)

流程图

ffmpeg的avformat_alloc_context()分析过程(九)_第1张图片

代码解析

主要框架

avformat_alloc_context(void)//option.c
{
    avformat_get_context_defaults(ic)//option.c
       s->av_class = &av_format_context_class;//静态类,函数相关指针
                        .option         = avformat_options//option.c
                                   avformat_options//options_table.h, 设置probsize等
       s->io_open  = io_open_default//option.c,初始化函数指针io_open
/*-------------为后面的avformat_open_input做准备-------*/
                     ffio_open_whitelist//aviobuf.c这部分还不不会调用,只是方便理解,先把他的流程写出来
                         ffurl_alloc//avio.c
                         ffurl_connect//avio.c
/*--------------------------------------------------*/
       s->io_close = io_close_default//option.c

       av_opt_set_defaults(s)//option.c
                     av_opt_next//opt.c
                         class->option

}

avformat_alloc_context这个函数主要实现了分配一个AVFormatContext。他可用于由框架分配的在上下文和所有内容 ,即分配解复用器上下文

/**
 * Allocate an AVFormatContext.
 * avformat_free_context() can be used to free the context and everything
 * allocated by the  framework within it.
 */
AVFormatContext *avformat_alloc_context(void)
{
    AVFormatContext *ic;
    ic = av_malloc(sizeof(AVFormatContext));
    if (!ic) return ic;
    /* 设置 ic 中各成员的默认值 */
    avformat_get_context_defaults(ic);
    
    /* internal: 
     * An opaque field for libavformat internal usage.
     * Must not be accessed in any way by callers. */
    ic->internal = av_mallocz(sizeof(*ic->internal));
    if (!ic->internal) {
        avformat_free_context(ic);
        return NULL;
    }
    /* offset: 
     * 偏移重新映射时间戳为非负值
     * 以时间戳为单位表示.
     * @see AVStream.mux_ts_offset */
    ic->internal->offset = AV_NOPTS_VALUE;
    /* raw_packet_buffer_remaining_size:
     * raw_packet_buffer 缓存中剩余可用的字节数
     * 初值为 2500000 */
    ic->internal->raw_packet_buffer_remaining_size = RAW_PACKET_BUFFER_SIZE;
    /* shortest_end:
     * 最短的流结束的时间戳 */
    ic->internal->shortest_end = AV_NOPTS_VALUE;

    return ic;
}

av_malloc(sizeof(AVFormatContext))这个是内存分配,这个是没什么可以解释的

typedef struct AVFormatContext {
    AVIOContext *pb;//输入数据的缓存
    unsigned int nb_streams;//视音频流的个数
    AVStream **streams;//视频流指针
    char filename[1024];//文件名
    int64_t start_time;播放开始时间
    int64_t bit_rate;
    AVDictionary *metadata;//元数据
    protocol_blacklist;协议黑名单
    protocol_whitelist;协议白名单

AVFormatContext是一个贯穿始终的数据结构,很多函数都要用到它作为参数。它是FFMPEG解封装(flv,mp4,rmvb,avi)功能的结构体。avformat_alloc_context首先为 AVFormatContext 结构体分配动态内存,然后调用 avformat_get_context_defaults 函数获取该 AVFormatContext 的默认值。

static void avformat_get_context_defaults(AVFormatContext *s)
{
    memset(s, 0, sizeof(AVFormatContext));

    s->av_class = &av_format_context_class;

    s->io_open  = io_open_default;
    s->io_close = io_close_default;

    av_opt_set_defaults(s);
}

首先,s->av_class 指向一个全局静态结构体变量 av_format_context_class,给结构体变量的定义如下.

static const AVClass av_format_context_class = {
    /**
     * 类的名字,通常它与 AVClass 关联的上下文结构体类型的名字相同。
     * 这里类名为"AVFormatContext",说明该 AVClass 关联的上下文结构体为
     * AVFormatContext.
     */
    .class_name     = "AVFormatContext",
    /** 
     * 这里这个函数返回的是 AVFormatContext 中 iformat->name 
     * 或者 oformat->name 字符串.
     */
    .item_name      = format_to_name,
    /**
     * avformat_options 是一个 AVOption 结构体类的数组,
     * 该数组中的元素为 AVFormatContext 中的一个个字段,
     * 以后可以通过 av_opt_* 系列函数对 AVFormatContext
     * 中的字段进行操作。
     */
    .option         = avformat_options,
    .version        = LIBAVUTIL_VERSION_INT,
    /**
     * 返回下一个启用 AVOption 的子项
     */
    .child_next     = format_child_next,
    .child_class_next = format_child_class_next,
    .category       = AV_CLASS_CATEGORY_MUXER,
    .get_category   = get_category,
};

初始化函数指针io_open和io_close

static int io_open_default(AVFormatContext *s, AVIOContext **pb,
                           const char *url, int flags, AVDictionary **options)
{
    int loglevel;

    if (!strcmp(url, s->filename) ||
        s->iformat && !strcmp(s->iformat->name, "image2") ||
        s->oformat && !strcmp(s->oformat->name, "image2")
    ) {
        loglevel = AV_LOG_DEBUG;
    } else
        loglevel = AV_LOG_INFO;

    av_log(s, loglevel, "Opening \'%s\' for %s\n", url, flags & AVIO_FLAG_WRITE ? "writing" : "reading");

#if FF_API_OLD_OPEN_CALLBACKS
FF_DISABLE_DEPRECATION_WARNINGS
    if (s->open_cb)
        return s->open_cb(s, pb, url, flags, &s->interrupt_callback, options);
FF_ENABLE_DEPRECATION_WARNINGS
#endif

    return ffio_open_whitelist(pb, url, flags, &s->interrupt_callback, options, s->protocol_whitelist, s->protocol_blacklist);
}

使用ffio_open_whitelist()函数打开URL

ffio_open_whitelist() 声明:

所属库:libavformat(lavf

头文件:libavformat/avio_internal.h  如其名,是IO内部使用的函数,不是public api

声明:

int ffio_open_whitelist(AVIOContext **s, const char *url, int flags,
                         const AVIOInterruptCB *int_cb, AVDictionary **options,
                         const char *whitelist, const char *blacklist);

 文件:libavformat/aviobuf.c, 源码如下所示:

int ffio_open_whitelist(AVIOContext **s, const char *filename, int flags,
                         const AVIOInterruptCB *int_cb, AVDictionary **options,
                         const char *whitelist, const char *blacklist
                        )
{
    URLContext *h;
    int err;

    err = ffurl_open_whitelist(&h, filename, flags, int_cb, options, whitelist, blacklist, NULL);
    if (err < 0)
        return err;
    err = ffio_fdopen(s, h);
    if (err < 0) {
        ffurl_close(h);
        return err;
    }
    return 0;
}

ffurl_open_whitelist() 声明:

所属库:libavformat(lavf

头文件:libavformat/url.h

功能说明:根据URL来创建一个URLContext对象,用来访问URL所定位的资源,并打开之。对于该函数的入参以及返回值说明如下源码:

/**
 * Create an URLContext for accessing to the resource indicated by
 * url, and open it.
 * 
 * puc指向成功打开后的URLContext对象
 * @param puc pointer to the location where, in case of success, the
 * function puts the pointer to the created URLContext
 *
 * flags参数控制如何打开url指定的资源
 * @param flags flags which control how the resource indicated by url
 * is to be opened
 *
 * int_cb 中断函数,供URLContext使用,可空
 * @param int_cb interrupt callback to use for the URLContext, may be
 * NULL
 *
 * options参数,传入协议私有的选项。生效的选项,对应条目将被销毁,未生效的选项在本函数执
 * 行之后仍保留在该参数中。该参数可空。
 * @param options  A dictionary filled with protocol-private options. On return
 * this parameter will be destroyed and replaced with a dict containing options
 * that were not found. May be NULL.
 *
 * parent参数代表创建成功的URLContext的父URLContext对象,父URLContext对象的通用选项
 * 也应用到创建的URLContext对象中。
 * @param parent An enclosing URLContext, whose generic options should
 *               be applied to this URLContext as well.
 * 成功返回0或正数,失败返回负数,并且是由AVERROR宏返回的错误码
 * @return >= 0 in case of success, a negative value corresponding to an
 * AVERROR code in case of failure
 */
int ffurl_open_whitelist(URLContext **puc, const char *filename, int flags,
               const AVIOInterruptCB *int_cb, AVDictionary **options,
               const char *whitelist, const char* blacklist,
               URLContext *parent);

ffio_open_whitelist函数 

int ffurl_open_whitelist(URLContext **puc, const char *filename, int flags,
                         const AVIOInterruptCB *int_cb, AVDictionary **options,
                         const char *whitelist, const char* blacklist,
                         URLContext *parent)
{
    AVDictionary *tmp_opts = NULL;
    AVDictionaryEntry *e;
    int ret = ffurl_alloc(puc, filename, flags, int_cb);  // 创建URLContext
    if (ret < 0)
        return ret;
    if (parent)
        av_opt_copy(*puc, parent); // 拷贝父URLContext的选项->子URLContext
 
    // 设置用户传入的选项到URLContext
    if (options &&
        (ret = av_opt_set_dict(*puc, options)) < 0) 
        goto fail;
 
    // 上一步中的options含有非URLContext类的直接选项信息
    // 则设置用户传入的选项到URLContext.priv_data数据中
    if (options && (*puc)->prot->priv_data_class && 
        (ret = av_opt_set_dict((*puc)->priv_data, options)) < 0)
        goto fail;
    
    // 传入的options为空,options指向内部临时变量,后续操作黑白名单需要用上选项
    // 此处操作就是为了不论是否用户传入了options,还是传入NULL,后续操作能统一使用一个变量。 
    if (!options)
        options = &tmp_opts;
 
    // 对白名单进行断言
    av_assert0(!whitelist ||
               !(e=av_dict_get(*options, "protocol_whitelist", NULL, 0)) ||
               !strcmp(whitelist, e->value));
    av_assert0(!blacklist ||
               !(e=av_dict_get(*options, "protocol_blacklist", NULL, 0)) ||
               !strcmp(blacklist, e->value));
 
    // 将白名单放到options中
    if ((ret = av_dict_set(options, "protocol_whitelist", whitelist, 0)) < 0)
        goto fail;
 
    // 将黑名单放到options中
    if ((ret = av_dict_set(options, "protocol_blacklist", blacklist, 0)) < 0)
        goto fail;
 
    // 将options的黑白名单应用到URLContext
    if ((ret = av_opt_set_dict(*puc, options)) < 0)
        goto fail;
 
    ret = ffurl_connect(*puc, options);
 
    if (!ret)
        return 0;
fail:
    ffurl_close(*puc);
    *puc = NULL;
    return ret;
}

https://blog.csdn.net/ice_ly000/article/details/90339330文章介绍了io_open_default()相关的,介绍得挺好

av_opt_set_defaults根据前端设置的option进行初始化,比如前端设置的probsize等参数

#define OFFSET(x) offsetof(AVFormatContext,x)
/**
 * should be NAN but it does not work as it is 
 * not a constant in glibc as required by ANSI/ISO C
 */
#define DEFAULT 0 
// these names are too long to be readable
#define E AV_OPT_FLAG_ENCODING_PARAM
#define D AV_OPT_FLAG_DECODING_PARAM

static const AVOption avformat_options[] = {
/**
 * AVOption:
 * name: 该选项的名称
 * help: 该选项对应的一些简短的描述
 * offset: 对于命名常量,该值为0;否则为该选项在 AVFormatContext 中的偏移值
 * type: 该选项的类型,为 bool,或者常量 const
 * default_val:该字段是一个联合体类型,存放的是该选项的默认值
 * min: 该选项的最小值
 * max:该选项的最大值
 * flags:标志该选项的用途,如encoding
 * unit: 该选项所属的逻辑单元。非常量选项和相应的命名常量选项共享
 *    同一个单元。该字段可能为 NULL。
 */

{"avioflags", NULL, OFFSET(avio_flags), AV_OPT_TYPE_FLAGS, 
    {.i64 = DEFAULT }, INT_MIN, INT_MAX, D|E, "avioflags"},
{"direct", "reduce buffering", 0, AV_OPT_TYPE_CONST, 
    {.i64 = AVIO_FLAG_DIRECT }, INT_MIN, INT_MAX, D|E, "avioflags"},
{"probesize", "set probing size", OFFSET(probesize), 
    AV_OPT_TYPE_INT64, {.i64 = 5000000 }, 32, INT64_MAX, D},
{"formatprobesize", "number of bytes to probe file format", 
    OFFSET(format_probesize), AV_OPT_TYPE_INT, {.i64 = PROBE_BUF_MAX}, 0, INT_MAX-1, D},
{"packetsize", "set packet size", OFFSET(packet_size), AV_OPT_TYPE_INT, 
    {.i64 = DEFAULT }, 0, INT_MAX, E},
{"fflags", NULL, OFFSET(flags), AV_OPT_TYPE_FLAGS, 
    {.i64 = AVFMT_FLAG_FLUSH_PACKETS | AVFMT_FLAG_AUTO_BSF }, 

总结:

avformat_alloc_context()主要工作:

1)创建AVFormatContext,他可用于由框架分配的在上下文和所有内容 ,即分配解复用器上下文

2)初始化io_open等函数指针,为init_input做准备

3)并初始化option相关参数给ic

你可能感兴趣的:(ijkplayer,流媒体,ffmpeg,播放器)