ffmpeg代码中对滤波器的管理

本文详细介绍了FFmpeg中的滤波器图(filtergraph)概念,包括filtergraph的创建、全局变量、AVFilterGraph和AVFilterContext的作用及关系,以及如何通过滤波器链表描述各种滤波器类型。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

创建滤波器图 ( filter graph )

filter graph简单说明

根据上层应用的指令,创建一个filter graph,一个典型的filter graph例子如下图:

                [main]
input --> split ---------------------> overlay --> output
            |                             ^
            |[tmp]                  [flip]|
            +-----> crop --> vflip -------+

关于这个 graph 的解释,请参考这个链接的说明:http://blog.youkuaiyun.com/hunterhuang2013/article/details/61614859

命令行为:

ffmpeg.exe -i pig.mp4 -vf "split [main][tmp];[tmp] crop=iw:ih/2:0:0, vflip[flip];[main][flip] overlay=0:H/2" pig_out.mp4

生成的 filter graph如下图所示:

这里写图片描述

filter graph全局变量

图中顶上的第一个框,是ffmpeg.c中定义的全局变量,与之相关的是如下两个定义:

FilterGraph **filtergraphs;  
int        nb_filtergraphs;

前者是一个指针数组,含有 nb_filtergraphs 个 struct FilterGraph 类型的指针。其中每一个指针,指向 struct FilterGraph 的类型的变量,用来表述一个滤波器图。

其中,struct FilterGraph定义如下:

typedef struct FilterGraph {
    int            index;
    const char    *graph_desc;

    AVFilterGraph *graph;   ///////////
    int reconfiguration;

    InputFilter   **inputs;
    int          nb_inputs;
    OutputFilter **outputs;
    int         nb_outputs;
} FilterGraph;

其中 AVFilterGraph *graph (即上图中顶上第二个框) 指向一个描述具体滤波器级联关系的图。

其中

InputFilter   **inputs;
int          nb_inputs;

OutputFilter **outputs;
int         nb_outputs;

其中,InputFilter 和 OutputFilter 定义如下:

typedef struct InputFilter {
    AVFilterContext    *filter;
   ........
} InputFilter;

typedef struct OutputFilter {
    AVFilterContext     *filter;
       ........
} OutputFilter;

分别指向滤波器级联关系图中的输入节点和输出节点,指向了 AVFilterContext 类型的变量。如图中最上面的框图伸出的两条线,分别指向了第一个和最后一个滤波器。

AVFilterGraph *graph 的说明

这个变量是 struct FilterGraph 中的一个成员变量,指向 AVFilterGraph, 其定义如下(省略了与本主题无关的变量):

typedef struct AVFilterGraph {
    ........
    AVFilterContext **filters;  //////////////
    unsigned nb_filters;
    ........
} AVFilterGraph;

其中,filters 指向了包含 nb_filters 个 AVFilterContext 类型指针的数组。即filters处有 nb_filters 个滤波器。就是图中除了顶上两个方块之外的其它所有方块。

AVFilterContext 的说明

AVFilterContext 构成了滤波器级联图中关键的节点,即滤波器的定义和级联的信息,AVFilterContext 定义如下(省略无关):

struct AVFilterContext {
    AVFilterLink **inputs;          
    unsigned    nb_inputs;          
    AVFilterLink **outputs;        
    unsigned    nb_outputs;        
};

其中, inputs 和 outputs,分别指向输入 link 和输出 link,其类型是 AVFilterLink 。如图中圆形的那些节点。

我们看一下 AVFilterLink 的具体定义(省略无关),就比较清楚了:

struct AVFilterLink {
    AVFilterContext *src;       ///< source filter
    AVFilterContext *dst;       ///< dest filter
    ........
}

其中 src 指向 source 节点,即输入节点;dst 指向 destination节点,即输出节点。

如此,一个完整的滤波器级联图就可以建立起来了。

哪些是ffmpeg lib库中定义的?

图中除了上面两个方块图以外,其它的是在 lib 中定义的。

如果你自己开发基于 ffmpeg lib 库的程序,则需要熟练掌握 AVFilterContext 和 AVFilterLink 。

“描述滤波器类型”的链表

全局变量

首先,定义了一个全局链表,在文件avfilter.c (\libavfilter目录下)中定义,如下:

static AVFilter *first_filter;
static AVFilter **last_filter = &first_filter;

登记

然后,再初始化时,调用函数 avfilter_register_all 对所有支持的滤波器种类进行登记。这个函数的实质是创建 AVFilter 的实例,然后挂到这个链表中。

下面展开对 avfilter_register_all 这个函数的分析,以我目前正在看的 anequalizer 滤波器为例。

void avfilter_register_all(void)
{
    static int initialized;

    if (initialized)
        return;
    initialized = 1;

    ......
    REGISTER_FILTER(AMIX,           amix,           af);
    REGISTER_FILTER(ANEQUALIZER,    anequalizer,    af);
    ......
}

其实,有很多很多 REGISTER_FILTER 项,上面的代码只是摘抄了两行。

REGISTER_FILTER 的定义如下:

#define REGISTER_FILTER(X, x, y)                 \                         
                            {                    \                                                
        extern AVFilter ff_##y##_##x;            \                       
        if (CONFIG_##X##_FILTER)                 \                       
            avfilter_register(&ff_##y##_##x);    \                       
    }

去调用 avfilter_register 这个函数做登记(创建 AVFilter 的实例并加入到链表中),只是在登记之前检查了一下“是否需要登记”,如果在工程配置的时候,不支持这种类型的滤波器,则不做登记。因此,执行了这
行代码 REGISTER_FILTER(ANEQUALIZER, anequalizer, af); 就把 ff_af_anequalizer 挂到滤波器链表中了。

每种滤波器类型的描述

ff_##y##_##x 这是一个全局变量,经过宏替换后为 ff_af_anequalizer.

这个全局变量,是在对应滤波器的具体C代码中定义的:
在 af_anquealizer.c (\libavfilter) 中定义了 ff_af_anequalizer:

AVFilter ff_af_anequalizer = {
    .name          = "anequalizer",
    .description   = NULL_IF_CONFIG_SMALL("Apply high-order audio parametric multi band equalizer."),
    .priv_size     = sizeof(AudioNEqualizerContext),
    .priv_class    = &anequalizer_class,
    .init          = init,
    .uninit        = uninit,
    .query_formats = query_formats,
    .inputs        = inputs,
    .outputs       = NULL,
    .flags         = AVFILTER_FLAG_DYNAMIC_OUTPUTS,
    .process_command = process_command,
};

上个结构中,anequalizer_class 定义是使用如下宏定义来完成的:

#define AVFILTER_DEFINE_CLASS(fname)            \
    static const AVClass fname##_class = {      \
        .class_name = #fname,                   \
        .item_name  = av_default_item_name,     \
        .option     = fname##_options,          \
        .version    = LIBAVUTIL_VERSION_INT,    \
        .category   = AV_CLASS_CATEGORY_FILTER, \
    }

说明:这个链表并不描述滤波器的具体特征,例如系数是啥等具体信息是不在这个结构中的。这个结构主要描述某个特定类型滤波器,其名称、描述、初始化函数、处理数据的函数等。

与具体滤波器节点(AVFilterContext )的关系

在第一章节描述 AVFilterContext 这个结构的时候,省略了 AVFilter * filter这个成员:

struct AVFilterContext {
     AVFilter  * filter;
     AVFilterLink **inputs;          
    unsigned    nb_inputs;          
    AVFilterLink **outputs;        
    unsigned    nb_outputs;        
    void *priv;
};

其中,filter 用来表述滤波器类型的。只是说明了 滤波器的总体特征(名字,描述),并不包含具体信息(如系数)。

滤波器的具体系数/参数

在哪里保存具体的系数和参数?

图中除了顶上两行外的其他方块,是每个滤波器,在以上描述时,省略掉了一个 void *priv 成员变量没有被描述,加上后描述如下(省略无关):

struct AVFilterContext {
    AVFilterLink **inputs;          
    unsigned    nb_inputs;          
    AVFilterLink **outputs;        
    unsigned    nb_outputs;        
    void *priv;
};

其中, priv 就是具体每个滤波器的私有结构,根据滤波器类型的不同,这个指针指向不同类型的结构。

以anequalizer为例

下面以anequalizer这个音频滤波器为例。 priv 指向的是 AudioNEqualizerContext, 例如,在draw_curves这个函数中,把 priv 强制为 AudioNEqualizerContext 类型:

static void draw_curves(AVFilterContext *ctx,
AudioNEqualizerContext *s = ctx->priv;

其中, AudioNEqualizerContext 定义如下:

typedef struct AudioNEqualizerContext {
    const AVClass *class;
    char *args;
    char *colors;
    int draw_curves;
    int w, h;

    double mag;
    int fscale;
    int nb_filters;
    int nb_allocated;
    EqualizatorFilter *filters;
    AVFrame *video;
} AudioNEqualizerContext;

其中的成员变量 EqualizatorFilter *filters,指向了 EqualizatorFilter 类型的结构:

typedef struct EqualizatorFilter {
    int ignore;
    int channel;
    int type;

    double freq;
    double gain;
    double width;

    FoSection section[2];
} EqualizatorFilter;

滤波器最最底层的描述

最后,在 FoSection 中定义滤波器的系数和历史数据:

typedef struct FoSection {
    double a0, a1, a2, a3, a4;
    double b0, b1, b2, b3, b4;

    double num[4];      //x的历史数据,即以前的输入数据
    double denum[4];    //y的历史输出,即以前的输出数据
} FoSection;

这是最底层的了,不会再指向其它任何数据结构了。最后做实际计算的时候,要用到这些数据,具体代码如下:

static inline double section_process(FoSection *S, double in)
{
    double out;

    out = S->b0 * in;
    out+= S->b1 * S->num[0] - S->denum[0] * S->a1;
    out+= S->b2 * S->num[1] - S->denum[1] * S->a2;
    out+= S->b3 * S->num[2] - S->denum[2] * S->a3;
    out+= S->b4 * S->num[3] - S->denum[3] * S->a4;

    S->num[3] = S->num[2];
    S->num[2] = S->num[1];
    S->num[1] = S->num[0];
    S->num[0] = in;

    S->denum[3] = S->denum[2];
    S->denum[2] = S->denum[1];
    S->denum[1] = S->denum[0];
    S->denum[0] = out;

    return out;
}

公式其实如下:

y[n] = b0 * x[n] + b1 * x[n-1] + b2 * x[n-2] + b3 * x[n-3] + b4 * x[n-4]
                 - a1 * y[n-1] - a2 * y[n-2] - a3 * y[n-3] - a4 * y[n-4]

总结

1)对滤波器类型,建立了一个全局变量,进行登记并描述。每种滤波器在数组中占一个位置,即每种滤波器有且仅有一个描述。虽然,在具体应用中,每个滤波器可能被使用多次,但是,多次使用以及在什么地方使用,是由filter graph和滤波器级联图来决定的。

2)滤波器级联图,用滤波器和link,描述了一个级联图,滤波器节点中保存滤波器的系数和历史数据。

总的来讲,第一点类似于一个结构体的定义;第二点,类似于用这个结构体的定义,声明一个或者多个实例(变量或者常量)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值