• ffmpeg源码阅读之avformat_open_input


    头文件

    /**
     * Open an input stream and read the header. The codecs are not opened.
     * The stream must be closed with avformat_close_input().
     *
     * @param ps Pointer to user-supplied AVFormatContext (allocated by avformat_alloc_context).
     *           May be a pointer to NULL, in which case an AVFormatContext is allocated by this
     *           function and written into ps.
     *           Note that a user-supplied AVFormatContext will be freed on failure.
     * @param url URL of the stream to open.
     * @param fmt If non-NULL, this parameter forces a specific input format.
     *            Otherwise the format is autodetected.
     * @param options  A dictionary filled with AVFormatContext and demuxer-private options.
     *                 On return this parameter will be destroyed and replaced with a dict containing
     *                 options that were not found. May be NULL.
     *
     * @return 0 on success, a negative AVERROR on failure.
     *
     * @note If you want to use custom IO, preallocate the format context and set its pb field.
     */
    int avformat_open_input(AVFormatContext **ps, const char *url,const AVInputFormat *fmt, AVDictionary **options);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    翻译的意思
    打开输入流并读取标头。编解码器未打开。必须使用avformat_close_input()关闭流。参数ps 指向用户提供的AVFormatContext(由avformat_alloc_context分配)的指针。 可能是指向NULL的指针,在这种情况下,AVFormatContext由该函数分配写入ps。请注意,用户提供的AVFormatContext将在失败时释放。要打开的流的url url。fmt如果非NULL,则此参数强制使用特定的输入格式。否则将自动检测格式。options用AVFormatContext和demuxer私有选项填充的字典。返回时,此参数将被销毁,并替换为包含未找到的选项的dict。可以为空。成功时返回0,失败时返回负AVERROR。注意:如果要使用自定义IO,请预先分配格式上下文并设置其pb字段

    头文件解读

    1. 使用avformat_open_input函数之后,必须要和***avformat_close_input().***一起使用
    2. 参数ps,分两种情况:
      1. 参数为空,由此函数进行分配和初始化
      2. 参数不为空,即我们自定在外面定义了这个结构体,如果分配失败的话,会由此函数进行释放资源
    3. 参数url: 数据流的地址
    4. 输入的格式:一般默认为NULL,让程序自动检测数据的封装格式
    5. 键值对: 提供配置属性的作用(暂定默认为null,使用默认的键值对)

    源码

    int avformat_open_input(AVFormatContext **ps, const char *filename,
                            const AVInputFormat *fmt, AVDictionary **options)
    {
        AVFormatContext *s = *ps;
        FFFormatContext *si;
        AVDictionary *tmp = NULL;
        ID3v2ExtraMeta *id3v2_extra_meta = NULL;
        int ret = 0;
    	// ps为空的时候,会分配空间
        if (!s && !(s = avformat_alloc_context()))
            return AVERROR(ENOMEM);
        si = ffformatcontext(s);
        if (!s->av_class) {
            av_log(NULL, AV_LOG_ERROR, "Input context has not been properly allocated by avformat_alloc_context() and is not NULL either\n");
            return AVERROR(EINVAL);
        }
        if (fmt)
            s->iformat = fmt;
    
        if (options)
            av_dict_copy(&tmp, *options, 0);
    
        if (s->pb) // must be before any goto fail
            s->flags |= AVFMT_FLAG_CUSTOM_IO;
    
        if ((ret = av_opt_set_dict(s, &tmp)) < 0)
            goto fail;
    
        if (!(s->url = av_strdup(filename ? filename : ""))) {
            ret = AVERROR(ENOMEM);
            goto fail;
        }
    
        if ((ret = init_input(s, filename, &tmp)) < 0)
            goto fail;
        s->probe_score = ret;
    
        if (!s->protocol_whitelist && s->pb && s->pb->protocol_whitelist) {
            s->protocol_whitelist = av_strdup(s->pb->protocol_whitelist);
            if (!s->protocol_whitelist) {
                ret = AVERROR(ENOMEM);
                goto fail;
            }
        }
    
        if (!s->protocol_blacklist && s->pb && s->pb->protocol_blacklist) {
            s->protocol_blacklist = av_strdup(s->pb->protocol_blacklist);
            if (!s->protocol_blacklist) {
                ret = AVERROR(ENOMEM);
                goto fail;
            }
        }
    
        if (s->format_whitelist && av_match_list(s->iformat->name, s->format_whitelist, ',') <= 0) {
            av_log(s, AV_LOG_ERROR, "Format not on whitelist \'%s\'\n", s->format_whitelist);
            ret = AVERROR(EINVAL);
            goto fail;
        }
    
        avio_skip(s->pb, s->skip_initial_bytes);
    
        /* Check filename in case an image number is expected. */
        if (s->iformat->flags & AVFMT_NEEDNUMBER) {
            if (!av_filename_number_test(filename)) {
                ret = AVERROR(EINVAL);
                goto fail;
            }
        }
    
        s->duration = s->start_time = AV_NOPTS_VALUE;
    
        /* Allocate private data. */
        if (s->iformat->priv_data_size > 0) {
            if (!(s->priv_data = av_mallocz(s->iformat->priv_data_size))) {
                ret = AVERROR(ENOMEM);
                goto fail;
            }
            if (s->iformat->priv_class) {
                *(const AVClass **) s->priv_data = s->iformat->priv_class;
                av_opt_set_defaults(s->priv_data);
                if ((ret = av_opt_set_dict(s->priv_data, &tmp)) < 0)
                    goto fail;
            }
        }
    
        /* e.g. AVFMT_NOFILE formats will not have an AVIOContext */
        if (s->pb)
            ff_id3v2_read_dict(s->pb, &si->id3v2_meta, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta);
    
        if (s->iformat->read_header)
            if ((ret = s->iformat->read_header(s)) < 0) {
                if (s->iformat->flags_internal & FF_FMT_INIT_CLEANUP)
                    goto close;
                goto fail;
            }
    
        if (!s->metadata) {
            s->metadata    = si->id3v2_meta;
            si->id3v2_meta = NULL;
        } else if (si->id3v2_meta) {
            av_log(s, AV_LOG_WARNING, "Discarding ID3 tags because more suitable tags were found.\n");
            av_dict_free(&si->id3v2_meta);
        }
    
        if (id3v2_extra_meta) {
            if (!strcmp(s->iformat->name, "mp3") || !strcmp(s->iformat->name, "aac") ||
                !strcmp(s->iformat->name, "tta") || !strcmp(s->iformat->name, "wav")) {
                if ((ret = ff_id3v2_parse_apic(s, id3v2_extra_meta)) < 0)
                    goto close;
                if ((ret = ff_id3v2_parse_chapters(s, id3v2_extra_meta)) < 0)
                    goto close;
                if ((ret = ff_id3v2_parse_priv(s, id3v2_extra_meta)) < 0)
                    goto close;
            } else
                av_log(s, AV_LOG_DEBUG, "demuxer does not support additional id3 data, skipping\n");
            ff_id3v2_free_extra_meta(&id3v2_extra_meta);
        }
    
        if ((ret = avformat_queue_attached_pictures(s)) < 0)
            goto close;
    
        if (s->pb && !si->data_offset)
            si->data_offset = avio_tell(s->pb);
    
        si->raw_packet_buffer_size = 0;
    
        update_stream_avctx(s);
    
        if (options) {
            av_dict_free(options);
            *options = tmp;
        }
        *ps = s;
        return 0;
    
    close:
        if (s->iformat->read_close)
            s->iformat->read_close(s);
    fail:
        ff_id3v2_free_extra_meta(&id3v2_extra_meta);
        av_dict_free(&tmp);
        if (s->pb && !(s->flags & AVFMT_FLAG_CUSTOM_IO))
            avio_closep(&s->pb);
        avformat_free_context(s);
        *ps = NULL;
        return ret;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147

    逻辑阅读

    逻辑执行步骤01
    	AVFormatContext *s = *ps;
        if (!s && !(s = avformat_alloc_context()))
            return AVERROR(ENOMEM);
    
    • 1
    • 2
    • 3
    这个地方就是判断传入的ps是否为空,如果为空,就会分配默认的上下文(我的理解就是容器)
    
    • 1
    逻辑执行步骤02
     FFFormatContext *si;
     si = ffformatcontext(s);
    
    • 1
    • 2

    在这里插入图片描述
    思考:为什么可以这样这样操作?
    》》》》 查看FFFormatContext 结构体所知:原来是FFFormatContext 的第一个成员就是AVFormatContext ,(结构体的作用就是将数据包装一层,并且结构体的地址为第一个变量的首地址)

    在这里插入图片描述

    执行逻辑03

    if (!s->av_class) {
            av_log(NULL, AV_LOG_ERROR, "Input context has not been properly allocated by avformat_alloc_context() and is not NULL either\n");
            return AVERROR(EINVAL);
        }
        if (fmt)
            s->iformat = fmt;
    
        if (options)
            av_dict_copy(&tmp, *options, 0);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    这里就是一些参数的赋值操作

    执行逻辑04

    if (s->pb) // must be before any goto fail
            s->flags |= AVFMT_FLAG_CUSTOM_IO;
    
    • 1
    • 2

    这里就是对应头文件里面注释的提示,若果自定义了AVIOContext,这边会设置一个标志

    执行逻辑05

    		// 设置参数配置
        if ((ret = av_opt_set_dict(s, &tmp)) < 0)
            goto fail;
    	 // 判断输入url是否为空
        if (!(s->url = av_strdup(filename ? filename : ""))) {
            ret = AVERROR(ENOMEM);
            goto fail;
        }
    		// 初始化IO流(重点)
        if ((ret = init_input(s, filename, &tmp)) < 0)
            goto fail;
        s->probe_score = ret;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    执行逻辑06

    if (!s->protocol_whitelist && s->pb && s->pb->protocol_whitelist) {
            s->protocol_whitelist = av_strdup(s->pb->protocol_whitelist);
            if (!s->protocol_whitelist) {
                ret = AVERROR(ENOMEM);
                goto fail;
            }
        }
    
        if (!s->protocol_blacklist && s->pb && s->pb->protocol_blacklist) {
            s->protocol_blacklist = av_strdup(s->pb->protocol_blacklist);
            if (!s->protocol_blacklist) {
                ret = AVERROR(ENOMEM);
                goto fail;
            }
        }
    
        if (s->format_whitelist && av_match_list(s->iformat->name, s->format_whitelist, ',') <= 0) {
            av_log(s, AV_LOG_ERROR, "Format not on whitelist \'%s\'\n", s->format_whitelist);
            ret = AVERROR(EINVAL);
            goto fail;
        }
    
        avio_skip(s->pb, s->skip_initial_bytes);
    
        /* Check filename in case an image number is expected. */
        if (s->iformat->flags & AVFMT_NEEDNUMBER) {
            if (!av_filename_number_test(filename)) {
                ret = AVERROR(EINVAL);
                goto fail;
            }
        }
    
        s->duration = s->start_time = AV_NOPTS_VALUE;
    
        /* Allocate private data. */
        if (s->iformat->priv_data_size > 0) {
            if (!(s->priv_data = av_mallocz(s->iformat->priv_data_size))) {
                ret = AVERROR(ENOMEM);
                goto fail;
            }
            if (s->iformat->priv_class) {
                *(const AVClass **) s->priv_data = s->iformat->priv_class;
                av_opt_set_defaults(s->priv_data);
                if ((ret = av_opt_set_dict(s->priv_data, &tmp)) < 0)
                    goto fail;
            }
        }
    
        /* e.g. AVFMT_NOFILE formats will not have an AVIOContext */
        if (s->pb)
            ff_id3v2_read_dict(s->pb, &si->id3v2_meta, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta);
    
        if (s->iformat->read_header)
            if ((ret = s->iformat->read_header(s)) < 0) {
                if (s->iformat->flags_internal & FF_FMT_INIT_CLEANUP)
                    goto close;
                goto fail;
            }
    
        if (!s->metadata) {
            s->metadata    = si->id3v2_meta;
            si->id3v2_meta = NULL;
        } else if (si->id3v2_meta) {
            av_log(s, AV_LOG_WARNING, "Discarding ID3 tags because more suitable tags were found.\n");
            av_dict_free(&si->id3v2_meta);
        }
    
        if (id3v2_extra_meta) {
            if (!strcmp(s->iformat->name, "mp3") || !strcmp(s->iformat->name, "aac") ||
                !strcmp(s->iformat->name, "tta") || !strcmp(s->iformat->name, "wav")) {
                if ((ret = ff_id3v2_parse_apic(s, id3v2_extra_meta)) < 0)
                    goto close;
                if ((ret = ff_id3v2_parse_chapters(s, id3v2_extra_meta)) < 0)
                    goto close;
                if ((ret = ff_id3v2_parse_priv(s, id3v2_extra_meta)) < 0)
                    goto close;
            } else
                av_log(s, AV_LOG_DEBUG, "demuxer does not support additional id3 data, skipping\n");
            ff_id3v2_free_extra_meta(&id3v2_extra_meta);
        }
    
        if ((ret = avformat_queue_attached_pictures(s)) < 0)
            goto close;
    
        if (s->pb && !si->data_offset)
            si->data_offset = avio_tell(s->pb);
    
        si->raw_packet_buffer_size = 0;
    
        update_stream_avctx(s);
    
        if (options) {
            av_dict_free(options);
            *options = tmp;
        }
        *ps = s;
        return 0;
    
    close:
        if (s->iformat->read_close)
            s->iformat->read_close(s);
    fail:
        ff_id3v2_free_extra_meta(&id3v2_extra_meta);
        av_dict_free(&tmp);
        if (s->pb && !(s->flags & AVFMT_FLAG_CUSTOM_IO))
            avio_closep(&s->pb);
        avformat_free_context(s);
        *ps = NULL;
        return ret;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109

    这里就是对逻辑05执行的IO数据读入的一个解析过程,和释放分配的资源

  • 相关阅读:
    从转载阿里开源项目 Egg.js 技术文档引发的“版权纠纷”,看宽松的 MIT 许可该如何用?
    磁盘阵列之RAID
    (n, 0)维NumPy数组 和 (n, 1)维NumPy数组 有什么区别?
    JIRA 在 2024 年完全停止服务器版本支持
    【MyBatis笔记09】MyBatis中常用的几个动态SQL标签
    大数据培训技术自定义组件Source案例测试
    LangChain:打造自己的LLM应用 | 京东云技术团队
    FlinkSql详解
    Web前端vueDemo—实现天气预报功能(四)
    非线性在深度模型中的意义
  • 原文地址:https://blog.csdn.net/weixin_44742767/article/details/127814595