FFmpeg 是如何实现多态的?

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

前言

众所周知,FFmpeg 在解码的时候,无论输入文件是 MP4 文件还是 FLV 文件,或者其它文件格式,都能正确解封装、解码,而代码不需要针对不同的格式做出任何改变,这是面向对象中很常见的多态特性,但 FFmpeg 是用 C 语言编写的,那么它是如何使用 C 语言实现了多态特性的呢?

要解决这个问题,首先需要从函数 av_register_all 说起。

av_register_all

av_register_all 是几乎所有 FFmpeg 程序中第一个被调用的函数,用于注册在编译 FFmpeg 时设置了 --enable 选项的封装器、解封装器、编码HX器、解码HX器等。源码如下:

#define REGISTER_MUXER(X, x)                                            \{                                                                   \extern AVOutputFormat ff_##x##_muxer;                           \if (CONFIG_##X##_MUXER)                                         \av_register_output_format(&ff_##x##_muxer);                 \}#define REGISTER_DEMUXER(X, x)                                          \{                                                                   \extern AVInputFormat ff_##x##_demuxer;                          \if (CONFIG_##X##_DEMUXER)                                       \av_register_input_format(&ff_##x##_demuxer);                \}#define REGISTER_MUXDEMUX(X, x) REGISTER_MUXER(X, x); REGISTER_DEMUXER(X, x)static void register_all(void)
{// 注册编解码HX器avcodec_register_all();// 注册封装器、解封装器/* (de)muxers */REGISTER_MUXER   (A64,              a64);REGISTER_DEMUXER (AA,               aa);REGISTER_DEMUXER (AAC,              aac);REGISTER_MUXDEMUX(AC3,              ac3);REGISTER_MUXDEMUX(FLV,              flv);REGISTER_MUXDEMUX(GIF,              gif);.../* image demuxers */REGISTER_DEMUXER (IMAGE_BMP_PIPE,        image_bmp_pipe);REGISTER_DEMUXER (IMAGE_JPEG_PIPE,       image_jpeg_pipe);REGISTER_DEMUXER (IMAGE_SVG_PIPE,        image_svg_pipe);REGISTER_DEMUXER (IMAGE_WEBP_PIPE,       image_webp_pipe);REGISTER_DEMUXER (IMAGE_PNG_PIPE,        image_png_pipe);.../* external libraries */REGISTER_MUXER   (CHROMAPRINT,      chromaprint);...
}void av_register_all(void)
{static AVOnce control = AV_ONCE_INIT;ff_thread_once(&control, register_all);
}

define 里的 ## 用于拼接两个字符串,比如 REGISTER_DEMUXER(AAC, aac) ,它等效于:

extern AVInputFormat ff_aac_demuxer;
if(CONFIG_AAC_DEMUXER) av_register_input_format(&ff_aac_demuxer);

可以看出,编译 ffmpeg 时类似于 "--enable-muxer=xxx" 这样的选项在此时发挥了作用,它决定是否注册某个格式对应的(解)封装器,以便之后处理该格式的时候找到这个(解)封装器。

av_register_input_format

av_register_input_format、av_register_output_format 源码如下:

/** head of registered input format linked list */
static AVInputFormat *first_iformat = NULL;
/** head of registered output format linked list */
static AVOutputFormat *first_oformat = NULL;static AVInputFormat **last_iformat = &first_iformat;
static AVOutputFormat **last_oformat = &first_oformat;void av_register_input_format(AVInputFormat *format)
{AVInputFormat **p = last_iformat;// Note, format could be added after the first 2 checks but that implies that *p is no longer NULLwhile(p != &format->next && !format->next && avpriv_atomic_ptr_cas((void * volatile *)p, NULL, format))p = &(*p)->next;if (!format->next)last_iformat = &format->next;
}void av_register_output_format(AVOutputFormat *format)
{AVOutputFormat **p = last_oformat;// Note, format could be added after the first 2 checks but that implies that *p is no longer NULLwhile(p != &format->next && !format->next && avpriv_atomic_ptr_cas((void * volatile *)p, NULL, format))p = &(*p)->next;if (!format->next)last_oformat = &format->next;
}

从代码中可以看到,这两个注册方法会把指定的 AVInputFormat、AVOutputFormat 加到链表的尾部。

AVInputFormat

接着看 AVInputFormat 的定义:

typedef struct AVInputFormat {/*** A comma separated list of short names for the format. New names* may be appended with a minor bump.*/const char *name;/*** Descriptive name for the format, meant to be more human-readable* than name. You should use the NULL_IF_CONFIG_SMALL() macro* to define it.*/const char *long_name;/*** Can use flags: AVFMT_NOFILE, AVFMT_NEEDNUMBER, AVFMT_SHOW_IDS,* AVFMT_GENERIC_INDEX, AVFMT_TS_DISCONT, AVFMT_NOBINSEARCH,* AVFMT_NOGENSEARCH, AVFMT_NO_BYTE_SEEK, AVFMT_SEEK_TO_PTS.*/int flags;/*** If extensions are defined, then no probe is done. You should* usually not use extension format guessing because it is not* reliable enough*/const char *extensions;.../*** Tell if a given file has a chance of being parsed as this format.* The buffer provided is guaranteed to be AVPROBE_PADDING_SIZE bytes* big so you do not have to check for that unless you need more.*/int (*read_probe)(AVProbeData *);/*** Read the format header and initialize the AVFormatContext* structure. Return 0 if OK. 'avformat_new_stream' should be* called to create new streams.*/int (*read_header)(struct AVFormatContext *);/*** Read one packet and put it in 'pkt'. pts and flags are also* set. 'avformat_new_stream' can be called only if the flag* AVFMTCTX_NOHEADER is used and only in the calling thread (not in a* background thread).* @return 0 on success, < 0 on error.*         When returning an error, pkt must not have been allocated*         or must be freed before returning*/int (*read_packet)(struct AVFormatContext *, AVPacket *pkt);...
} AVInputFormat;

可以看到,这个结构体除了 name 等变量外,还具备 read_probe、read_header 等函数指针。

以前面提到的 ff_aac_demuxer 为例,这里看一下它的实现:

AVInputFormat ff_aac_demuxer = {// 名称.name         = "aac",.long_name    = NULL_IF_CONFIG_SMALL("raw ADTS AAC (Advanced Audio Coding)"),// 把函数指针指向能够处理 aac 格式的函数实现.read_probe   = adts_aac_probe,.read_header  = adts_aac_read_header,.read_packet  = adts_aac_read_packet,.flags        = AVFMT_GENERIC_INDEX,.extensions   = "aac",.mime_type    = "audio/aac,audio/aacp,audio/x-aac",.raw_codec_id = AV_CODEC_ID_AAC,
};

总结

根据以上代码的分析,此时我们就能得出问题的答案了:

FFmpeg 之所以能够作为一个平台,无论是封装、解封装,还是编码、解码,在处理对应格式的文件/数据时,都能找到对应的库来实现,而不需要修改代码,主要就是通过结构体 + 函数指针实现的。具体实现方式是:首先设计一个结构体,然后创建该结构体的多个对象,每个对象都有着自己的成员属性及函数实现。这样就使得 FFmpeg 具备了类似于面向对象编程中的多态的效果。

PS:avcodec_register_all 也是一样的,有兴趣的可以看看 AVCodec 的声明以及 ff_libx264_encoder 等编解码HX器的实现。



作者:zouzhiheng
链接:https://www.jianshu.com/p/c12e6888de10
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

转载于:https://my.oschina.net/u/4000302/blog/3021134

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

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

相关文章

基于easyui开发Web版Activiti流程定制器详解(五)——Draw2d详解(一)

&#xfeff;&#xfeff;背景&#xff1a; 小弟工作已有十年有余&#xff0c;期间接触了不少工作流产品&#xff0c;个人比较喜欢的还是JBPM&#xff0c;因为出自名门Jboss所以备受推崇&#xff0c;但是现在JBPM版本已经与自己当年使用的版本&#xff08;3.X&#xff09;大相径…

seaborn 子图_Seaborn FacetGrid:进一步完善子图

seaborn 子图Data visualizations are essential in data analysis. The famous saying “one picture is worth a thousand words” holds true in the scope of data visualizations as well. In this post, I will explain a well-structured, very informative collection …

基于easyui开发Web版Activiti流程定制器详解(六)——Draw2d的扩展(一)

&#xfeff;&#xfeff;题外话&#xff1a; 最近在忙公司的云项目空闲时间不是很多&#xff0c;所以很久没来更新&#xff0c;今天补上一篇&#xff01; 回顾&#xff1a; 前几篇介绍了一下设计器的界面和Draw2d基础知识&#xff0c;这篇讲解一下本设计器如何扩展Draw2d。 进…

深度学习网络总结

1.Siamese network Siamese [saiə mi:z] 孪生 左图的孪生网络是指两个网络通过共享权值实现对输入的输出&#xff0c;右图的伪孪生网络则不共享权值(pseudo-siamese network)。 孪生神经网络是用来衡量两个输入的相似度&#xff0c;可以用来人脸验证、语义相似度分析、QA匹配…

异常检测时间序列_时间序列的无监督异常检测

异常检测时间序列To understand the normal behaviour of any flow on time axis and detect anomaly situations is one of the prominent fields in data driven studies. These studies are mostly conducted in unsupervised manner, since labelling the data in real lif…

python设计模式(七):组合模式

组合&#xff0c;将对象组合成树状结构&#xff0c;来表示业务逻辑上的[部分-整体]层次&#xff0c;这种组合使单个对象和组合对象的使用方法一样。 如描述一家公司的层次结构&#xff0c;那么我们用办公室来表示节点&#xff0c;则总经理办公司是根节点&#xff0c;下面分别由…

存款惊人_如何使您的图快速美丽惊人

存款惊人So, you just finished retrieving, processing, and analyzing your data. You grab your data and you decide to graph it so you can show others your findings. You click ‘graph’ and……因此&#xff0c;您刚刚完成了数据的检索&#xff0c;处理和分析。 您获…

pytest自动化6:pytest.mark.parametrize装饰器--测试用例参数化

前言&#xff1a;pytest.mark.parametrize装饰器可以实现测试用例参数化。 parametrizing 1. 下面是一个简单是实例&#xff0c;检查一定的输入和期望输出测试功能的典型例子 2. 标记单个测试实例为失败&#xff0c;例如使用内置的mark.xfail&#xff0c;则跳过该用例不执行直…

基于easyui开发Web版Activiti流程定制器详解(六)——Draw2d详解(二)

&#xfeff;&#xfeff;上一篇我们介绍了Draw2d整体结构&#xff0c;展示了组件类关系图&#xff0c;其中比较重要的类有Node、Canvas、Command、Port、Connection等&#xff0c;这篇将进一步介绍Draw2d如何使用以及如何扩展。 进入主题&#xff1a; 详细介绍一下Draw2d中几个…

Ubuntu16.04 开启多个终端,一个终端多个小窗口

Ubuntu16.04 开启多个终端&#xff0c;一个终端多个小窗口 CtrlShift T,一个终端开启多个小终端 CtrlAlt T 开启多个终端 posted on 2019-03-15 11:26 _孤城 阅读(...) 评论(...) 编辑 收藏 转载于:https://www.cnblogs.com/liuweijie/p/10535904.html

敏捷 橄榄球运动_澳大利亚橄榄球迷的研究声称南非裁判的偏见被证明是错误的

敏捷 橄榄球运动In February 2020, an Australian rugby fan produced a study, claiming to show how South African rugby referees were exhibiting favorable bias towards South African home teams. The study did not consider how other countries’ referees treat So…

activiti 部署流程图后中文乱码

Activiti工作流引擎使用 1.简单介工作流引擎与Activiti 对于工作流引擎的解释请参考百度百科&#xff1a;工作流引擎 1.1 我与工作流引擎 在第一家公司工作的时候主要任务就是开发OA系统&#xff0c;当然基本都是有工作流的支持&#xff0c;不过当时使用的工作流引擎是公司一些…

Luogu 4755 Beautiful Pair

分治 主席树。 设$solve(l, r)$表示当前处理到$[l, r]$区间的情况&#xff0c;我们可以找到$[l, r]$中最大的一个数的位置$mid$&#xff0c;然后扫一半区间计算一下这个区间的答案。 注意&#xff0c;这时候左半边是$[l, mid]$&#xff0c;而右区间是$[mid, r]$&#xff0c;我…

网络传播动力学_通过简单的规则传播动力

网络传播动力学When a single drop of paint is dropped on a surface the amount of space that the drop will cover depends both on time and space. A short amount of time will no be enough for the drop to cover a greater area, and a small surface will bound the…

BPMN2.0-概要

BPMN2.0-概要 作者&#xff1a;AliKevin2011&#xff0c;发布于2012-6-27 一、BPMN简介 BPMN&#xff08;Business Process Model And Notation&#xff09;- 业务流程模型和符号 是有BPMI&#xff08;Business Process Management Initiative&#xff09;开发的一套变准的业务…

Activiti 简易教程

一搭建环境 1.1 JDK 6 activiti 运行在版本 6以上的 JDK上。转到 Oracle Java SE下载页面&#xff0c;点击按钮“下载 JDK”。网页中也有安装说明。要核实安装是否成功&#xff0c;在命令行上运行 java–version。将打印出安装的 JDK的版本。 1.2 Ant 1.8.1 从 Ant[http://…

koa2 中使用 svg-captcha 生成验证码

1. 安装svg-captcha $ npm install --save svg-captcha 2. 使用方法 生成有4个字符的图片和字符串const svgCaptcha require(svg-captcha)const cap svgCaptcha.create({size: 4, // 验证码长度width:160,height:60,fontSize: 50,ignoreChars: 0oO1ilI, // 验证码字符中排除 …

iris数据集 测试集_IRIS数据集的探索性数据分析

iris数据集 测试集Let’s explore one of the simplest datasets, The IRIS Dataset which basically is a data about three species of a Flower type in form of its sepal length, sepal width, petal length, and petal width. The data set consists of 50 samples from …

Oracle 12c 安装 Linuxx86_64

1)下载Oracle Database 12cRelease 1安装介质 官方的下载地址&#xff1a; 1&#xff1a;http://www.oracle.com/technetwork/database/enterprise-edition/downloads/index.html 2&#xff1a;https://edelivery.oracle.com/EPD/Download/get_form?egroup_aru_number16496…

python初学者_初学者使用Python的完整介绍

python初学者A magical art of teaching a computer to perform a task is called computer programming. Programming is one of the most valuable skills to have in this competitive world of computers. We, as modern humans, are living with lots of gadgets such as …