本文讲下FFmpeg参数配置系统内部是怎么实现的,以及一些易犯的错误用法。
本文是在手机上靠记忆敲出来的,难免有笔误,特别是文件名变量名,见谅。
从API角度来说,FFmpeg参数配置仿佛平平无奇。你可以直接调用av_opt_set key value形式配置对象的参数,更上层的API会暴露一个dict,能够传一组key value进去。一个亮点是,av_opt_set可以控制是否搜索子对象的配置,如果对象没有对应的key的配置,可以自动搜索子对象是否有对应key的配置。avformat avcodec等API都用到了搜索子对象配置的功能。比如使用libx264编码器,你可以使用avcodec公共参数来配置码率和线程数,可以使用libx264模块的私有参数配置preset tune。公共参数在option_tables.c里,子对象私有参数在各个模块内部,关键字AVOption。
为FFmpeg某个已有模块加新参数也简单:
1. 在模块context里加上需要的成员变量,int, float,char *等类型都可以
2. 照葫芦画瓢,在描述模块参数的AVOption数组里加上新增参数的描述,包括参数名称,帮助信息,参数类型,默认值,上下限,所属分类等等
当你访问context里新增的成员变量时,你会发现它的值要么是默认值,要么是用户配置的值。你有没有好奇过,用户设置的字符串key value,是怎么自动更新到模块context的成员变量的?
这要从C语言基础说起。我们一步步来分析。
struct Foo {
struct Bar bar;
int n;
};
已知struct Foo *p,如何为n赋值?
p->n = value
假如成员变量不叫n,名字未知,怎么给这个名字未知的成员变量赋值呢?
问题有点绕。换一个说法,为一个变量赋值,需要哪些信息?思考两秒钟。
…
…
只需要两个信息:变量的地址,和变量的类型。
Foo里有个int型成员变量,名字未知。怎么给它赋值?这样赋值:
*(int *)((char *)p + offset) = value
offset是这个整型变量在struct Foo中的偏移量。
兜兜转转绕一个圈子,通过offset来赋值,why?
1. 模块context内存是ffmpeg框架层分配的,框架层知道context地址
2. 框架层不知道每个模块context的定义,所以框架层不知道模块context里有哪些成员变量以及它们的名字
3. 重点来了,框架层可以知道模块context成员变量的类型和offset。仔细看下AVOption定义,它里面描述了成员变量的offset和类型。每个模块对外导出一个全局变量,比如解码器导出一个FFCodec,FFCodec里有个AVClass,AVClass里有个模块定义的AVOption数组,AVOption里有变量类型和offset,赋值的条件具备了。
剩下的工作就是字符串转数值类型,检查有效范围等等。
再看参数配置系统是怎么实现自动配置公共参数和模块私有参数的。答案在AVClass的定义。AVClass里有两个带child关键字的函数指针,一个实现了输入父对象地址,返回子对象地址的功能,一个实现了输入父一级AVClass,返回子一级AVClass的功能。不论是父对象还是子对象,它们的第一个成员变量都是AVClass *,有了对象地址也就有了它的AVClass信息。前一个函数指针实现了search child做配置,后一个函数指针是为了在没创建对象实例的情况下,展示帮助信息。
列一些常见错误:
1. bool类型的配置,必须用int做成员变量,不能用bool,因为opt.c里是按照int去访问的,如果实际类型是bool,会访问越界。有趣的一点是ffmpeg bool类型配置可以有三个值,0,1,没配置。
2. 从字符串转int64类型的时候,会先转成double,再转成int64,数值过大会丢精度。一般情况没问题,但国内魔改ffmpeg拿来传指针,只能中午用,早晚crash。为减少魔改,这里就不说该用什么方式传指针了。
作者:quink
来源:Fun With FFmpeg
原文:https://mp.weixin.qq.com/s/nj2ddlO1imeo0ZciKU0X3Q
版权声明:本文内容转自互联网,本文观点仅代表作者本人。本站仅提供信息存储空间服务,所有权归原作者所有。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至1393616908@qq.com 举报,一经查实,本站将立刻删除。