ffmpeg filter 分析

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 的各选项默认值.
    }

    如此上下便贯通了.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值