[ffmpeg] find 编码器

背景

整理 ffmpeg 中,如何通过名字或者 id 找到对应编码器的。

具体流程

搜索函数

avcodec_find_encoder  // 通过 ID 搜索编码器
avcodec_find_encoder_by_name // 通过名字搜索编码器

源码分析

ffmpeg 中所有支持的编码器都会注册到 codec_list.c 文件中,保存在 codec_list 结构体中,既有编码器也有解码器,且该结构体最后一个是 NULL,这样方便 ffmpeg 内部的迭代算法使用。

static const FFCodec *codec_list[] = {&ff_a64multi_encoder,&ff_a64multi5_encoder,&ff_alias_pix_encoder,&ff_amv_encoder,...&ff_av1_decoder,NULL
};

搜索编码器用到的函数主要有这些,主要推测是一次遍历 codec_list 结构体,拿到结构体首先通过 av_codec_is_encoder 函数判断是不是编码器;然后在判断 id 和传入相同。 (avcodec_find_encoder_by_name 类似,只是最后一步是判断 name是否相等)
av_codec_iterate 写的方式很像 c++ 中的迭代器,index 不断加1,然后通过 codec_list 结构体最后的 NULL 作为结尾的判断。

// allcodecs.c 中
const AVCodec *avcodec_find_encoder(enum AVCodecID id)
{return find_codec(id, av_codec_is_encoder);
}
static const AVCodec *find_codec(enum AVCodecID id, int (*x)(const AVCodec *))
{const AVCodec *p, *experimental = NULL;void *i = 0;id = remap_deprecated_codec_id(id); //兼容代码,可先不管while ((p = av_codec_iterate(&i))) {if (!x(p))continue;if (p->id == id) {//兼容代码,可先不管if (p->capabilities & AV_CODEC_CAP_EXPERIMENTAL && !experimental) {experimental = p;} elsereturn p;}}return experimental;
}const AVCodec *av_codec_iterate(void **opaque)
{uintptr_t i = (uintptr_t)*opaque;const FFCodec *c = codec_list[i];//av_codec_init_static 只运行一次,兼容代码,可先不管ff_thread_once(&av_codec_static_init, av_codec_init_static);if (c) {*opaque = (void*)(i + 1);return &c->p;}return NULL;
}// 判断这个 avcodec 是不是编码器
int av_codec_is_encoder(const AVCodec *avcodec)
{const FFCodec *const codec = ffcodec(avcodec);return codec && (codec->cb_type == FF_CODEC_CB_TYPE_ENCODE     ||codec->cb_type == FF_CODEC_CB_TYPE_ENCODE_SUB ||codec->cb_type == FF_CODEC_CB_TYPE_RECEIVE_PACKET);
}

具体例子

该结构体在 aacenc.c 文件中
主要是 FF_CODEC_ENCODE_CB,表示这个 codec 是编码器。
其他:.p.xx 这些是设置 AVCodec 结构体

const FFCodec ff_aac_encoder = {.p.name         = "aac",CODEC_LONG_NAME("AAC (Advanced Audio Coding)"),.p.type         = AVMEDIA_TYPE_AUDIO,.p.id           = AV_CODEC_ID_AAC,.p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY |AV_CODEC_CAP_SMALL_LAST_FRAME,.priv_data_size = sizeof(AACEncContext),.init           = aac_encode_init,FF_CODEC_ENCODE_CB(aac_encode_frame),.close          = aac_encode_end,.defaults       = aac_encode_defaults,.p.supported_samplerates = ff_mpeg4audio_sample_rates,.caps_internal  = FF_CODEC_CAP_INIT_CLEANUP,.p.sample_fmts  = (const enum AVSampleFormat[]){ AV_SAMPLE_FMT_FLTP,AV_SAMPLE_FMT_NONE },.p.priv_class   = &aacenc_class,
};#define CODEC_LONG_NAME(str) .p.long_name = str
#define FF_CODEC_ENCODE_CB(func)                          \.cb_type           = FF_CODEC_CB_TYPE_ENCODE,         \.cb.encode         = (func)

细节推敲

为啥 AVCodec 可以强转为 FFCodec?

int av_codec_is_encoder(const AVCodec *avcodec)
{**const FFCodec *const codec = ffcodec(avcodec);**return codec && (codec->cb_type == FF_CODEC_CB_TYPE_ENCODE     ||codec->cb_type == FF_CODEC_CB_TYPE_ENCODE_SUB ||codec->cb_type == FF_CODEC_CB_TYPE_RECEIVE_PACKET);
}

看了一下 FFCodec 中的结构定义,AVCodec p 是定义在FFCodec 最前面的,所以如果当前使用的 AVCodec 是用FFCodec 创建的,直接强转就能找到对应的 FFCodec 对象。如果 AVCodec 是独立创建的,强转肯定是有问题的。感觉这块写的有点 hardcode,不按 ffmpeg 约定俗成的一些规则写会有比较难查的bug。

typedef struct FFCodec {/*** The public AVCodec. See codec.h for it.*/AVCodec p;/*** Internal codec capabilities FF_CODEC_CAP_*.*/unsigned caps_internal:29;/*** This field determines the type of the codec (decoder/encoder)* and also the exact callback cb implemented by the codec.* cb_type uses enum FFCodecType values.*/unsigned cb_type:3;// .../*** List of supported codec_tags, terminated by FF_CODEC_TAGS_END.*/const uint32_t *codec_tags;
} FFCodec;

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

毕设:《基于hive的音乐数据分析系统的设计与实现》

文章目录 环境启动一、爬取数据1.1、歌单信息1.2、每首歌前20条评论1.3、排行榜 二、搭建环境1.1、搭建JAVA1.2、配置hadoop1.3、配置Hadoop环境:YARN1.4、MYSQL1.5、HIVE(数据仓库)1.6、Sqoop(关系数据库数据迁移) 三、hadoop配置内存四、导…

笙默考试管理系统-MyExamTest----codemirror(45)

笙默考试管理系统-MyExamTest----codemirror(44) 目录 笙默考试管理系统-MyExamTest----codemirror(44) 一、 笙默考试管理系统-MyExamTest----codemirror 二、 笙默考试管理系统-MyExamTest----codemirror 三、 笙默考试…

【华为OD题库-085】路灯照明II-Java

题目 在一条笔直的公路上安装了N个路灯,从位置0开始安装,路灯之间间距固定为100米。 每个路灯都有自己的照明半径,请计算第一个路灯和最后一个路灯之间,无法照明的区间的长度和。输入描述 第一行为一个数N,表示路灯个数…

FFA 2023 明日开幕,Flink 智能诊断、小红书流批一体实践精彩来袭

近年来,流批一体的技术思想势头火热,即“将批处理和流处理相结合,实现更好的数据处理能力”,已成功从理论层面走进现实世界。Flink 是一款高吞吐量、低延迟的流处理引擎,具备统一接口、高性能、低延迟、容错性和可扩展…

绑定域名简单教程

📑打牌 : da pai ge的个人主页 🌤️个人专栏 : da pai ge的博客专栏 ☁️宝剑锋从磨砺出,梅花香自苦寒来 🌤️安装Nginx环境 &…

前端使用socket.io

前端使用socket.io 前言 socket.IO 是一个库,可以在客户端和服务器之间实现 低延迟, 双向 和 基于事件的 通信。使用socket.IO要确保客户端和服务端都是socket.IO构建的。socket.IO内部封装了很多现成的api,比如心跳监测,断开重来等&#x…

2024黑龙江省职业院校技能大赛信息安全管理与评估赛项规程

2024黑龙江省职业院校技能大赛暨国赛选拔赛 “GZ032信息安全管理与评估”赛项规程 极安云科专注技能竞赛,包含网络建设与运维和信息安全管理与评估两大赛项,及各大CTF,基于两大赛项提供全面的系统性培训,拥有完整的培训体系。团队…

【Qt开发流程】之元对象系统

描述 Qt的元对象系统(Meta-Object System)是Qt框架的核心机制之一,它提供了运行时类型信息(RTTI)和信号与槽(Signals and Slots)机制的支持。元对象系统在Qt中扮演了很重要的角色,它…

适合炎热天气的最佳葡萄酒有哪些?

每年的夏天,白葡萄酒和玫瑰红葡萄酒总会是葡萄酒爱好者的首选,这是为什么呢?随着春天的逝去,夏天悄悄地到来,空气变得炎热和沉重,树木变得越来越郁郁葱葱,白天的时间更长而晴朗了。多雨的五月变…

12.7作业

1. #include "mywidget.h"MyWidget::MyWidget(QWidget *parent): QWidget(parent) {//***********窗口相关设置***********//设置窗体大小this->resize(540,410);this->setFixedSize(540,410);//取消菜单栏this->setWindowFlag(Qt::FramelessWindowHint);/…

SuperMap iObject.NET三维场景拖拽框选实现详解及完整源代码(一)——环境准备及项目配置

作者:超图研究院技术支持中心-于丁1 SuperMap iObject.NET三维场景拖拽框选实现详解及完整源代码(一)——环境准备及项目配置   三维场景框选是一种在三维空间中进行选择和操作的功能,它可以让使用者通过鼠标拖动来创建一个矩形…

Verilog学习 | 用initial语句写出固定的波形

initial beginia 0;ib 1;clk 0;#10ia 1; #20ib 0;#20ia 0; endalways #5 clk ~clk; 或者 initial clk 0;initial beginia 0;#10ia 1; #40ia 0; endinitial beginib 1;#30 ib 0; endalways #5 clk ~clk;

新书推荐——《Copilot和ChatGPT编程体验:挑战24个正则表达式难题》

《Copilot和ChatGPT编程体验:挑战24个正则表达式难题》呈现了两方竞争的格局。一方是专业程序员David Q. Mertz,是网络上最受欢迎的正则表达式教程的作者。另一方则是强大的AI编程工具OpenAI ChatGPT和GitHub Copilot。 比赛规则如下:David编…

基于jsp+servlet的在线考试系统

基于jspservlet的在线考试系统, 演示地址:英语在线考试系统考生测试账号:用户名:stu,密码:stu,管理员测试账号用户名:admin,密码:admin (源码里包含数据库文件) 本系统分为两个角色,一个时考生,一个是管理员,考生可…

架构师一1.功能权限

1. RBAC 权限模型 系统采用 RBAC 权限模型&#xff0c;全称是 Role-Based Access Control 基于角色的访问控制。 简单来说&#xff0c;每个用户拥有多个角色&#xff0c;每个角色拥有多个菜单&#xff0c;菜单中存在菜单权限、按钮权限。这样&#xff0c;就形成了 “用户<-…

JAVA设计模式(二)-简单工厂

JAVA设计模式(二)-简单工厂 本篇文章主要讲下java 创建型设计模式中的简单工厂模式. 简单工厂模式又叫 静态工厂方法模式&#xff08;Static FactoryMethod Pattern&#xff09;,是通过专门定义一个类来负责创建其他类的实例&#xff0c;被创建的实例通常都具有共同的父类。 …

StringBoot常用注解(不断添加)

Configuration 定义配置类&#xff0c;之前的Spring配置都是写在xml配置文件里面的。在新的Spring版本里&#xff0c;建议首要选择把配置写道配置类中。 ComponeneScan 定义扫描路径。 Bean 默认方法名讲就是bean的id&#xff0c;返回类型就是方法返回的类型。也可以Bean("…

第二十一章网络通信总结博客

网络程序设计基础 局域网与互联网 为了实现两台计算机的通信&#xff0c;必须用一个网络线路连接两台计算机。如下图所示 网络协议 1.IP协议 IP是Internet Protocol的简称&#xff0c;是一种网络协议。Internet 网络采用的协议是TCP/IP协议&#xff0c;其全称是Transmissio…

Linux 环境变量 与 命令行参数

什么是环境变量 从结构上来看&#xff0c;环境变量就是操作系统维护的一组&#xff1a;key-value 的键值对。 不知道你是否有一个疑问&#xff1a;为什么我们写代码编译链接 形成的可执行程序要运行起来需要带路径呢&#xff1f;Linux 内置的命令也是可执行程序&#xff0c;为…

Builder设计模式

我们往往不想一下子从构造方法里面把参数传递过去&#xff0c;而是一个个参数构造。此时就用到Builder设计模式。 比如&#xff1a;属性计算中&#xff0c;我们构造各个方法。