创建滤波器图 ( 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,描述了一个级联图,滤波器节点中保存滤波器的系数和历史数据。
总的来讲,第一点类似于一个结构体的定义;第二点,类似于用这个结构体的定义,声明一个或者多个实例(变量或者常量)。