【FFmpeg】avformat_alloc_output_context2函数

【FFmpeg】avformat_alloc_output_context2函数

  • 1.avformat_alloc_output_context2
    • 1.1 初始化AVFormatContext(avformat_alloc_context)
    • 1.2 格式猜测(av_guess_format)
      • 1.2.1 遍历可用的fmt(av_muxer_iterate)
      • 1.2.2 name匹配检查(av_match_name)
      • 1.2.3 扩展匹配检查(av_match_ext)
  • 2.小结

参考:
FFmpeg源代码简单分析:avformat_alloc_output_context2()

示例工程:
【FFmpeg】调用ffmpeg库实现264软编
【FFmpeg】调用ffmpeg库实现264软解
【FFmpeg】调用ffmpeg库进行RTMP推流和拉流
【FFmpeg】调用ffmpeg库进行SDL2解码后渲染

流程分析:
【FFmpeg】编码链路上主要函数的简单分析
【FFmpeg】解码链路上主要函数的简单分析

结构体分析:
【FFmpeg】AVCodec结构体
【FFmpeg】AVCodecContext结构体
【FFmpeg】AVStream结构体
【FFmpeg】AVFormatContext结构体
【FFmpeg】AVIOContext结构体
【FFmpeg】AVPacket结构体

函数分析:
【FFmpeg】avformat_open_input函数
【FFmpeg】avformat_find_stream_info函数

avformat_alloc_output_context2内函数调用关系如下
在这里插入图片描述

1.avformat_alloc_output_context2

函数的主要功能是分配一个输出的context,利用输入的format和filename来确定output format,主要工作流程为:
(1)初始化AVFormatContext(avformat_alloc_context)
(2)如果没有指定输出format,需要根据输入信息来猜测一个format(av_guess_format)
(3)根据输出的format,来对AVFormatContext中的priv_data初始化

int avformat_alloc_output_context2(AVFormatContext **avctx, const AVOutputFormat *oformat,
                                   const char *format, const char *filename)
{
    AVFormatContext *s = avformat_alloc_context();
    int ret = 0;

    *avctx = NULL;
    if (!s)
        goto nomem;
	// 没有指定输出format
    if (!oformat) {
    	// 但是指定了格式名称,例如rtp,flv,udp等,则进行输出format的猜测
        if (format) {
            oformat = av_guess_format(format, NULL, NULL);
            if (!oformat) {
                av_log(s, AV_LOG_ERROR, "Requested output format '%s' is not known.\n", format);
                ret = AVERROR(EINVAL);
                goto error;
            }
        } else {	// 也没有指定格式名称,使用filename进行输出format的猜测
            oformat = av_guess_format(NULL, filename, NULL);
            if (!oformat) {
                ret = AVERROR(EINVAL);
                av_log(s, AV_LOG_ERROR,
                       "Unable to choose an output format for '%s'; "
                       "use a standard extension for the filename or specify "
                       "the format manually.\n", filename);
                goto error;
            }
        }
    }
	// 根据确定的输出format,对AVFormatContext中priv_data信息进行初始化
    s->oformat = oformat;
    if (ffofmt(s->oformat)->priv_data_size > 0) {
        s->priv_data = av_mallocz(ffofmt(s->oformat)->priv_data_size);
        if (!s->priv_data)
            goto nomem;
        if (s->oformat->priv_class) {
            *(const AVClass**)s->priv_data= s->oformat->priv_class;
            av_opt_set_defaults(s->priv_data);
        }
    } else
        s->priv_data = NULL;

    if (filename) {
        if (!(s->url = av_strdup(filename)))
            goto nomem;

    }
    *avctx = s;
    return 0;
nomem:
    av_log(s, AV_LOG_ERROR, "Out of memory\n");
    ret = AVERROR(ENOMEM);
error:
    avformat_free_context(s);
    return ret;
}

1.1 初始化AVFormatContext(avformat_alloc_context)

函数用于初始化AVFormatContext结构体

AVFormatContext *avformat_alloc_context(void)
{
    FFFormatContext *const si = av_mallocz(sizeof(*si));
    AVFormatContext *s;

    if (!si)
        return NULL;

    s = &si->pub;
    // 初始化函数指针
    s->av_class = &av_format_context_class;
    s->io_open  = io_open_default;
    s->io_close2= io_close2_default;
	
    av_opt_set_defaults(s);
	// 分配packet的空间
    si->pkt = av_packet_alloc();
    si->parse_pkt = av_packet_alloc();
    if (!si->pkt || !si->parse_pkt) {
        avformat_free_context(s);
        return NULL;
    }

#if FF_API_LAVF_SHORTEST
    si->shortest_end = AV_NOPTS_VALUE;
#endif

    return s;
}

1.2 格式猜测(av_guess_format)

该函数用于猜测一个format,类型为AVOutputFormat,

const AVOutputFormat *av_guess_format(const char *short_name, const char *filename,
                                      const char *mime_type)
{
    const AVOutputFormat *fmt = NULL;
    const AVOutputFormat *fmt_found = NULL;
    void *i = 0;
    int score_max, score;

    /* specific test for image sequences */
    // 用于图像序列的特定测试
#if CONFIG_IMAGE2_MUXER
    if (!short_name && filename &&
        av_filename_number_test(filename) &&
        ff_guess_image2_codec(filename) != AV_CODEC_ID_NONE) {
        return av_guess_format("image2", NULL, NULL);
    }
#endif
    /* Find the proper file type. */
    // 寻找合适的文件类型
    score_max = 0;
    // 遍历每个可用的fmt
    while ((fmt = av_muxer_iterate(&i))) {
        score = 0;
        // 如果当前封装格式匹配,则置信度增加100
        if (fmt->name && short_name && av_match_name(short_name, fmt->name))
            score += 100;
        // 如果fmt的mime类型与mime_type匹配,则置信度增加10
        if (fmt->mime_type && mime_type && !strcmp(fmt->mime_type, mime_type))
            score += 10;
        if (filename && fmt->extensions &&
            av_match_ext(filename, fmt->extensions)) {
            // 如果扩展名匹配,则置信度增加5
            score += 5;
        }
        if (score > score_max) {
            score_max = score;
            fmt_found = fmt;
        }
    }
    return fmt_found;
}

其中,fmt中name、mime_type和extension的定义如下(以FLV格式为例)。在新版本FFmpeg中,FLV级别封装的格式不再是AVOutputFormat而是FFOutputFormat,但是FFOutputFormat之中的信息可以转换成AVOutputFormat,下面的.p就是AVOutputFormat的指针。对于FLV格式而言,name=“flv”,mime_type=“video/x-flv”,extensions=“flv”

const FFOutputFormat ff_flv_muxer = {
    .p.name         = "flv",
    .p.long_name    = NULL_IF_CONFIG_SMALL("FLV (Flash Video)"),
    .p.mime_type    = "video/x-flv",
    .p.extensions   = "flv",
    .priv_data_size = sizeof(FLVContext),
    .p.audio_codec  = CONFIG_LIBMP3LAME ? AV_CODEC_ID_MP3 : AV_CODEC_ID_ADPCM_SWF,
    .p.video_codec  = AV_CODEC_ID_FLV1,
    .init           = flv_init,
    .write_header   = flv_write_header,
    .write_packet   = flv_write_packet,
    .write_trailer  = flv_write_trailer,
    .deinit         = flv_deinit,
    .check_bitstream= flv_check_bitstream,
    .p.codec_tag    = (const AVCodecTag* const []) {
                          flv_video_codec_ids, flv_audio_codec_ids, 0
                      },
    .p.flags        = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS |
                      AVFMT_TS_NONSTRICT,
    .p.priv_class   = &flv_muxer_class,
};

1.2.1 遍历可用的fmt(av_muxer_iterate)

const AVOutputFormat *av_muxer_iterate(void **opaque)
{
    static const uintptr_t size = sizeof(muxer_list)/sizeof(muxer_list[0]) - 1;
    uintptr_t i = (uintptr_t)*opaque;
    const FFOutputFormat *f = NULL;
    uintptr_t tmp;
	// 从muxer的list中获取一个fmt,list当中有180个muxer
    if (i < size) {
        f = muxer_list[i];
    } else if (tmp = atomic_load_explicit(&outdev_list_intptr, memory_order_relaxed)) {
        const FFOutputFormat *const *outdev_list = (const FFOutputFormat *const *)tmp;
        f = outdev_list[i - size];
    }
	// 如果找到了fmt,i = i + 1
    if (f) {
        *opaque = (void*)(i + 1);
        return &f->p;
    }
    return NULL;
}

1.2.2 name匹配检查(av_match_name)

/**
 * Match instances of a name in a comma-separated list of names.
 * List entries are checked from the start to the end of the names list,
 * the first match ends further processing. If an entry prefixed with '-'
 * matches, then 0 is returned. The "ALL" list entry is considered to
 * match all names.
 *
 * @param name  Name to look for.
 * @param names List of names.
 * @return 1 on match, 0 otherwise.
 */
// 在逗号分隔的名称列表中匹配名称的实例(字符串匹配)
// 从名字列表的开始到结束检查列表条目,第一个匹配结束进一步处理。
//		如果匹配前缀为'-'的条目,则返回0。“ALL”列表项被认为匹配所有名称
int av_match_name(const char *name, const char *names)
{
    const char *p;
    size_t len, namelen;

    if (!name || !names)
        return 0;

    namelen = strlen(name);
    while (*names) {
        int negate = '-' == *names;
        p = strchr(names, ','); // 因为有多个,分隔的字号
        if (!p)
            p = names + strlen(names);
        names += negate;
        len = FFMAX(p - names, namelen);
        if (!av_strncasecmp(name, names, len) || !strncmp("ALL", names, FFMAX(3, p - names)))
            return !negate;
        names = p + (*p == ',');
    }
    return 0;
}

1.2.3 扩展匹配检查(av_match_ext)

函数会提取出扩展名,然后调用av_match_name进行扩展名的匹配

/**
 * @file
 * Format register and lookup
 */

int av_match_ext(const char *filename, const char *extensions)
{
    const char *ext;

    if (!filename)
        return 0;

    ext = strrchr(filename, '.');
    if (ext)
        return av_match_name(ext + 1, extensions);
    return 0;
}

2.小结

avformat_alloc_output_context2函数是FFmpeg使用过程中重要的函数,它创建了输出的AVFormatContext,能够用于数据的输出,需要注意的是,新版本的FFmpeg中对上层格式的封装不再使用AVOutputFormat而是使用FFOutputFormat,将AVOutputFormat封装到了FFOutputFormat之中

CSDN : https://blog.csdn.net/weixin_42877471
Github : https://github.com/DoFulangChen

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/749030.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

正版软件 | DupInOut Duplicate Finder:智能清理,让数据井然有序

在信息爆炸的时代&#xff0c;我们经常面临数据管理的挑战。DupInOut Duplicate Finder 是一款专为Windows 设计的重复文件查找工具&#xff0c;帮您快速识别并删除重复的文档、音乐、视频和照片&#xff0c;让您的计算环境更加清洁、有序。 精准查找&#xff0c;一键删除 DupI…

DM达梦数据库转换、条件函数整理

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; &#x1f49d;&#x1f49…

硬件开发笔记(二十二):AD21软件中创建元器件AXK5F80337YG原理图库、封装库和3D模型

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/140007117 长沙红胖子Qt&#xff08;长沙创微智科&#xff09;博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV…

怎么扫描图片变成pdf格式?办公人士值得收藏的宝藏工具

将图片扫描并转换为PDF格式可以通过多种途径实现&#xff0c;无论是使用专业的扫描仪还是智能手机&#xff0c;都有相应的方法。 PDF 是什么&#xff1f; PDF&#xff0c;全称为 Portable Document Format&#xff08;便携式文档格式&#xff09;&#xff0c;是由Adobe System…

使用宝塔安装ModstartCMS (非一键安装)

操作系统 Linux Windows 推荐 Linux 操作系统&#xff0c;性能比较好 软件环境 稳定版 PHP 5.6 PHP 7.0 MySQL >5.0 PHP Extension&#xff1a;Fileinfo Apache/Nginx Laravel 9.0 版本 PHP 8.1 MySQL >5.0 PHP Extension&#xff1a;Fileinfo Apache/Ngin…

DLS策略洞察:如何应对AI数据中心网络交换机市场的爆发式增长?

摘要&#xff1a; 随着AI技术的发展和应用&#xff0c;AI数据中心对网络交换机的需求日益增加。摩根士丹利预计&#xff0c;2023-2026年间&#xff0c;AI数据中心网络交换机的收入复合年增长率&#xff08;CAGR&#xff09;将达到55%。本文将详细分析AI数据中心网络交换机市场…

Springboot + Mybatis-Plus代码生成指南

使用 Spring Boot 和 MyBatis-Plus 生成代码&#xff0c;可以大大简化开发流程&#xff0c;可以保持编码的规范性&#xff0c;生成单元测试等。以下是详细步骤&#xff1a; 配置pom.xml <dependency><groupId>com.baomidou</groupId><artifactId>myb…

Arcgis 计算经纬度坐标并补齐6位小数

工作中我们经常需要在Arcgis中计算点的经纬度或者线的起点、终点坐标&#xff0c;为确保数据的准确性&#xff0c;我们必须保留6位小数&#xff0c;但我们在默认计算的时候偶尔会遇到算出来的经纬度坐标小数位不足6位&#xff0c;那我们应该如何补齐呢&#xff0c;这里我将方法…

如何不改变 PostgreSQL 列类型#PG培训

开发应用程序并在其背后操作数据库集群时&#xff0c;会遇到一个意想不到的问题是实践与理论、开发环境与生产之间的差异。这种不匹配的一个完美例子就是更改列类型。 #PG考试#postgresql培训#postgresql考试#postgresql认证 关于如何在 PostgreSQL&#xff08;以及其他符合 SQ…

Hugo Barra对Apple Vision Pro 硬件和软件的详细评述

原文&#xff1a;hugo.blog/2024/03/11/vision-pro 这篇文章的作者是Hugo Barra。Hugo Barra曾是Meta公司&#xff08;前身为Facebook&#xff09;旗下Oculus VR/AR团队的负责人。他在2017年至2020年期间领导了Oculus的团队&#xff0c;参与了多个VR头显的开发和发布。Hugo Bar…

MySQL详细介绍:开源关系数据库管理系统的魅力

学习总结 1、掌握 JAVA入门到进阶知识(持续写作中……&#xff09; 2、学会Oracle数据库入门到入土用法(创作中……&#xff09; 3、手把手教你开发炫酷的vbs脚本制作(完善中……&#xff09; 4、牛逼哄哄的 IDEA编程利器技巧(编写中……&#xff09; 5、面经吐血整理的 面试技…

未来20年人工智能将如何塑造社会

照片由Brian McGowan在Unsplash上拍摄 更多资讯&#xff0c;请访问 2img.ai “人工智能会成为我们的救星还是我们的末日&#xff1f;” 几十年来&#xff0c;这个问题一直困扰着哲学家、科学家和科幻爱好者。 当我们踏上技术革命的边缘时&#xff0c;是时候透过水晶球&#x…

从0开始编写VS1053音频解码芯片的底层驱动代码(适用于任何单片机)

VS1053是一个高性能的音频解码器芯片,它是干什么的? 他有两个功能:()用来解码音频文件播放音乐的。(2)将麦克风听到的声音编码成音频文件数据,配合单片机可以保存到SD卡。 单片机加上一个VS1053就可以轻松做一个音乐播放器、一个录音机 等项目。 VS1053支持两种协议:…

Visio文件编辑查看工具:Visio Viewer for Mac 激活版

Visio Viewer 软件通过该软件&#xff0c;用户可以在没有Visio软件的情况下查看使用Visio创建的绘图和图表&#xff0c;方便用户对复杂信息的可视化、分析和交流。Visio Viewer 2007是一个功能强大的软件&#xff0c;它可以帮助IT和商务专业人员轻松地可视化、分析和交流复杂信…

AI论文降重:一键操作,让你的论文查重率瞬间下降

高查重率是许多毕业生的困扰。通常&#xff0c;高查重率源于过度引用未经修改的参考资料和格式错误。传统的降重方法&#xff0c;如修改文本和增添原创内容&#xff0c;虽必要但耗时且成效不一。 鉴于此&#xff0c;应用AI工具进行AIGC降重成为了一个高效的解决方案。这些工具…

PyCharm 2024.1最新变化

PyCharm 2024.1 版本带来了一系列激动人心的新功能和改进&#xff0c;以下是一些主要的更新亮点: Hugging Face 模型和数据集文档预览&#xff1a;在 PyCharm 内部快速获取 Hugging Face 模型或数据集的详细信息&#xff0c;通过鼠标悬停或使用 F1 键打开文档工具窗口来预览。 …

基于springboot、logback的日志脱敏组件

Logback⽇志数据脱敏⼯具&#xff1a;隐私和安全的守护者 概述 在涉及敏感数据的⽇志记录环境中&#xff0c;数据保护和个⼈隐私⽆疑是⾄关重要的领域。确保敏感数据不被泄露&#xff0c;脱敏处理成为必不可少的⼀步。数据脱敏是⼀种技术⼿段&#xff0c;其将敏感信息转换为不…

注意!流量卡的禁区并不一样,请看清楚后再下单!

大家好&#xff0c;我是搜卡之家&#xff0c;今天我又来给大家科普了&#xff01; 今天科普的内容是关于流量卡禁区&#xff01; 首先要说一下&#xff0c;流量卡为什么会有禁区&#xff1f;运营商设立禁区主要是为了应对电信诈骗和违法使用电话卡的行为&#xff0c;确保网络…

Studying-代码随想录训练营day21| 669.修建二叉搜索树、108.将有序数组转换为二叉搜索树、538.把二叉搜索树转换为累加树、二叉树总结

第21天&#xff0c;二叉树最后一篇&#xff0c;冲&#x1f4aa; 目录 669.修建二叉搜索树 108.将有序数组转换为二叉搜索树 538.把二叉搜索树转换为累加树 二叉树总结 669.修建二叉搜索树 文档讲解&#xff1a;代码随想录修建二叉搜索树 视频讲解&#xff1a;手撕修建二叉…

NAT和内网穿透

NAT&#xff08;Network Address Translation&#xff0c;网络地址转换&#xff09;是一种广泛应用于计算机网络的技术&#xff0c;其主要目的是为了解决IPv4地址空间的短缺问题&#xff0c;并且增强网络安全。NAT技术允许一个私有网络内的多个设备共享一个或几个全局唯一的公共…