ffmpeg filter 分析
filter 给我的感觉是一个复杂的,神奇的东西. 当我有了一定的积累之后,想彻底研究一下它了.
af_silencedetect.c 它就实现了一个音频静音检测的filter, 是阿,还有许多一个又一个的filter, 而其你也可以添加自己的filter.
怎样添加? 简单概括为4步.写文件,修改makefile,修改allfilter.c,重新configure.
1. 实现了自己的filter, 例如 af_mytest.c, 其中定义了AVFilter ff_af_mytest
2. 修改makefile, 添加自己的filter
OBJS-$(CONFIG_MYTEST_FILTER) += af_mytest.o
3. 在allfilter.c 中添加 AVFilter 实例.
extern AVFilter ff_af_mytest;
这句话是什么意思呢? 因为它下面有一个包含文件, 该文件是一个AVFilter 的列表, 它会引用 ff_af_mytest 的地址
#include "libavfilter/filter_list.c"
4. 重新运行 ./configure 文件, configure 后面跟你需要的参数.
我用git 跟踪了configure 后的结果, 发现其会根据libavfilter中Makefile 的更改,对2个文件进行修改.
a. 修改了config.h 文件, 添加了.
#define CONFIG_MYTEST_FILTER 1
b. 修改了"libavfilter/filter_list.c" 文件, 在AVFilter 指针数组中,添加了新定义的filter
static const AVFilter * const filter_list[] = {
&ff_af_abench,
&ff_af_acompressor,
&ff_af_acontrast,
&ff_af_acopy,
.....
&ff_af_mytest
}
正好与第3步 extern AVFilter ff_af_mytest 相衔接. 至此所有疑惑均已削除.
后面就可以编译了. make , make install 等.
注: 后来,当我折腾最新的ffmpeg 时, 又有点犯糊涂了, 我搜索了一下configure 文件,发现其确实
访问了allfilter.c, 生成了,此时又添加了config_components.h, 也生成了若干list文件,原来configure
就是这么干的,以后再遇configure问题,恐怕也有主意了吧!
下面我们就来说说filter了, 还是以af_silencedetect.c 为例吧.
所谓AVFilter, 就是要满足一定的结构.
例如它应该有名称,有描述. 重要的是它应该有一个输入脚,一个输出脚.它应该有一个类及若干函数指针可以回调.
看一看af_silencedetect.c 确实它都满足了. 所谓的输入脚,输出脚,也是一个地址, 它们指向的结构数据是AVFilterPad结构.
指向的filterClass 也是如此,可以理解为是一个filterClass的实例.
后面的概念就会多起来, 这些概念都会对应着一种结构, 而这种结构的一个实现就是一个实例. 概念很重要!
通过这个filter实例,我们可以得到名称,描述,引脚,类及其它一些信息.
如果你拿到silencedetect 类, 就能够拿到 silencedetect_options, 根据选项的名称,就可以知道选项的类型,数值范围,默认值及
选项在SilenceDetectContext结构中的偏移量,据此你可以把用户的输入存入context变量中.
这里又要问了, SilenceDetectContext 地址是从哪里来的?
它肯定是new 出来的,因为filter实例ff_af_silencedetect 中已经定义了它的大小
.priv_size = sizeof(SilenceDetectContext)
放下底层实现,我们看看上层调用吧.
首先创建了3个对象
AVFilterInOut *outputs = avfilter_inout_alloc();
AVFilterInOut *inputs = avfilter_inout_alloc();
AVFilterGraph *filter_graph = avfilter_graph_alloc(); /* 创建graph -- audio */
然后向graph中添加2个filter
添加src filter --> abuffer 它是有参数设置的,这里忽略了,其返回值在src_ctx中
AVFilter *buffersrc = (AVFilter *)avfilter_get_by_name("abuffer"); /* 输入buffer filter */
ret = avfilter_graph_create_filter(src_ctx, buffersrc, "in", args, NULL, filter_graph);
添加sink filter -->abuffersink, handle 在sink_ctx
AVFilter *buffersink = (AVFilter *)avfilter_get_by_name("abuffersink"); /* 输出buffer filter */
ret = avfilter_graph_create_filter(sink_ctx, buffersink, "out", NULL, NULL, filter_graph);
然后对inputs, outputs 输入输出进行一番设置(初始化), 就可以调用parse_ptr 了
ret = avfilter_graph_parse_ptr(filter_graph, filters_descr, &inputs, &outputs, NULL)
我们传入的字符串是 "silencedetect=n=-50dB:d=2"
这个silencedetect 就是我们的音频过滤器名字, 后面为参数.
问题: 从silencedetect 名称,怎样找到silencedetect filter 实例?
1. 首先从=下手,前面是filter name, 后面是参数. name=silencedetect opts=n=-50dB:d=2
然后创建filter
ret = create_filter(filt_ctx, graph, index, name, opts, log_ctx);
filt = avfilter_get_by_name(filt_name); // filt_name=name, 由名字枚举可得到filter
//根据filter 可以分配一个AVFilterContext filt_ctx
// 输入参数 ctx 是graph_ctx, inst_name 是"Parsed_silencedetect_0", 加了点修饰
*filt_ctx = avfilter_graph_alloc_filter(ctx, filt, inst_name); //后面还有详细分析.
//初始化filt_ctx, args="n=-50dB:d=2"
ret = avfilter_init_str(*filt_ctx, args);
问题: filt_ctx 是一个AVFilterContext 结构, 与SilenceDetectContext 是什么关系?
filt_ctx : 看一下它的重要结构. 它已经拿到了ff_af_silencedetect handle.
(gdb) p **filt_ctx
$19 = {
av_class = 0x7ffff6251640 <avfilter_class>,
filter = 0x7ffff62a2840 <ff_af_silencedetect>,
name = 0x7fffb417d120 "Parsed_silencedetect_0",
input_pads = 0x7fffb417d3c0,
inputs = 0x7fffb417d480,
nb_inputs = 1,
output_pads = 0x7fffb417d540,
outputs = 0x7fffb417d600,
nb_outputs = 1,
priv = 0x7fffb417d240,
graph = 0x7fffb417b080,
......
}
原来这个priv 指针就是一个AVClass 指针, 指向的正是&silencedetect_class,从这个类中,可以拿到silencedetect_options地址
把命令行参数写到了哪里?
这需要从底层实现来查找了.
从底层发现, 这个priv 指针, 实际上时SilenceDetectContext *, 它的第一个元素是AVClass 指针. 这样参数写到哪里就知道了.
AVFilterContext *ctx = inlink->dst;
SilenceDetectContext *s = ctx->priv;
当然是写到SilenceDetectContext 中了.
它何时分配的SilenceDetectContext 内存呢?
我们再来观察avfilter_graph_alloc_filter(ctx,filt,inst_name) 函数, 此时的输入参数filt 正是silencedetect
(gdb) p *filter
$17 = {
name = 0x7ffff5fc254e "silencedetect",
description = 0x7ffff5fc25f0 "Detect silence.",
inputs = 0x7ffff62449c0 <silencedetect_inputs>,
outputs = 0x7ffff6244a40 <silencedetect_outputs>,
priv_class = 0x7ffff6295b80 <silencedetect_class>,
uninit = 0x7ffff5c2bc42 <uninit>,
query_formats = 0x7ffff5c2bb80 <query_formats>,
priv_size = 88,
...
}
inst_name 为:Parsed_silencedetect_0
调用下边函数分配内存
s = ff_filter_alloc(filter, name);
关键代码分析:
ret = av_mallocz(sizeof(AVFilterContext));
ret->av_class = &avfilter_class;
ret->filter = filter; // silencedetect filter 实例
ret->name = inst_name ? av_strdup(inst_name) : NULL; //Parsed_silencedetect_0 名字
if (filter->priv_size) {
ret->priv = av_mallocz(filter->priv_size); //SilenceDetectContext
}
av_opt_set_defaults(ret); // 初始化AVFilterContext 各选项默认值
if (filter->priv_class) {
*(const AVClass**)ret->priv = filter->priv_class; // 把第一项填充为类指针
av_opt_set_defaults(ret->priv); //初始化SilenceDetectContext 的各选项默认值.
}
如此上下便贯通了.