文章目录
1. 什么是滤镜
滤镜(filter)是指将未经过处理的原始音频帧(如PCM)
或视频帧(如YUV、RGB)
经过滤镜器处理后,得到具体“特殊效果
”的音频帧或视频帧,比如音频帧被添加回声、视频帧被旋转、缩放、添加水印等等。需要注意的是,滤镜处理的是原始音视频帧数据,输出的仍然是原始数据,因此不会造成数据损伤。FFmpeg的libavfilter库中提供了很多的内置滤镜,我们可以单独使用一个滤镜进行数据处理,也可以将多个滤镜连接起来组合使用,其中一个滤镜的输出可以连接到另一个滤镜的输入,因此滤镜分为简单滤镜
和复杂滤镜
。在FFmpeg中,滤镜模块支持多路输入和多路输出,其提供了两种方式使用滤镜,即命令
和API
,首先我们来看下在命令中定义一个滤镜
,语法如下:
[input_link_lable1][input_link_lable2]... filter_name=parameters [output_link_lable1][output_link_lable12]...
其中,[input_link_lable*]
是该滤镜的输入连接符号(link label),可以有多个,表示滤镜的输入;[output_link_lable*]
是该滤镜的输出连接符号,可以有多个,表示滤镜的输出;filter_name
表示该滤镜的具体名称,如缩放滤镜"scale";parameters
表示滤镜参数。
注:如果连接符号没标明,则说明上一个滤镜的输出就是当前滤镜的输入。
1.1 简单滤镜(滤镜链)
简单滤镜通常是指处理的滤镜中包含一个或多个滤镜,当包含多个滤镜时,每个滤镜以逗号
分隔构成一个滤镜序列,这样的滤镜序列被称之为滤镜链(filterchain)。语法如下:
filter1,fiter2,filter3,...,filterN-2,filterN-1,filterN
需要注意的是,滤镜链中如果有空格,需要将滤镜链用双引号括起来,因为命令行中空格是分隔参数用的。举例如下:
ffmpeg -i src.mp4-vf hqdn3d,pad=2*iw dest.mp4
命令参数说明:
-
-i src.mp4
:指定输入音/视频源; -
-vf
:指定简单视频滤镜,“-vf”等同“-filter:v”, 如果处理音频,该参数应为"-af",且“-af”等同“-filter:a”;
-
"hqdn3d,pad=2*iw"
:表示包含两个滤镜的滤镜链,其中"hqdn3d"滤镜用于降噪、"pad=2*iw"滤镜用于将图像的宽度填充到输入宽度的2倍; -
dest.mp4
:输出视频,为输入视频经过降噪、填充宽度后的输出结果。
效果图:
_______ _____________ _______ ________
| | | | | | | |
| input | ---> | hqdn3 | ---> | pad | ---> | output |
|_______| |_____________| |_______| |________|
1.2 复杂滤镜(滤镜图)
复杂滤镜通常是指滤镜图(filter graph)
,用处简单滤镜处理不了的场合。滤镜图(filterchain)由滤镜链(filterchain)序列组成,滤镜链之间用分号
分割,整个滤镜图需要用双引号括起来。语法如下:
"filter1;fiter2;filter3;...;filterN-2;filterN-1;filterN"
根据输入、输出的数量,滤镜图有可分为简单滤镜图(simple filter graph)
和复杂滤镜图(complex filter graph)
。其中,简单滤镜图只能处理单路输入流和输出流,且要求输入和输出具有相同的流类型;而复杂滤镜图支持多路输入流和(或)多路输出流,或者输出流与输入流类型不同的场合,比如overlay滤镜和amix滤镜就是复杂滤镜图。
- 简单滤镜图
_______ _____________________ ________
| | | | | |
| input | ---> | simple filter graph | ---> | output |
|_______| |_____________________| |________|
- 复杂滤镜图
_________
| |
| input 0 |\ __________
|_________| \ | |
\ _________ /| output 0 |
\ | | / |__________|
_________ \| complex | /
| | | |/
| input 1 |---->| filter |\
|_________| | | \ __________
/| graph | \ | |
/ | | \| output 1 |
_________ / |_________| |__________|
| | /
| input 2 |/
|_________|
- 示例如下:
ffmpeg -i INPUT -lavfi "split [main][tmp]; [tmp] crop=iw:ih/2:0:0, vflip [flip]; [main][flip] overlay=0:H/2" OUTPUT
参数说明:
-
-i
:指定输入流; -
-lavfi
:指定复杂滤镜图,"-lavfi"等价于"-filter_complex";2 -
"split [main][tmp]; [tmp] crop=iw:ih/2:0:0, vflip [flip]; [main][flip] overlay=0:H/2"
:复杂滤镜图,由三个滤镜链(用分号分割)组成。第一个滤镜链包含一个split滤镜,该滤镜产生两个输出[main]和[tmp];第二个滤镜链包含crop滤镜和vflip滤镜,输入为[tmp],输出为[flip];第三个滤镜链包含一个overlay滤镜,[main] [flip]作为输入。整行命令实现的功能是:**将输入分隔为两路,其中一路经过裁剪和垂直翻转后,再与另一路混合,生成输出文件。**示意图如下所示:[main] input --> split ---------------------> overlay --> output | ^ |[tmp] [flip]| +-----> crop --> vflip -------+
滤镜、滤镜链、滤镜图区别与联系?
从广义角度,滤镜指图像处理中的一个功能,通常以滤镜链和滤镜图的形式使用;从狭义角度,滤镜指滤镜链的单个特例。滤镜图由滤镜链序列组成,滤镜链由多个滤镜特例序列组成。一个滤镜图可以只包含一个滤镜链,而一个滤镜链也可以只包含一个滤镜特例,这种特例情况下,一个滤镜图仅有单个滤镜特例构成,即滤镜图=滤镜链=单个滤镜。
2. 滤镜API介绍与使用
2.1 滤镜API介绍
2.1.1 结构体
AVFilter
:表示一个滤镜器(Filter),位于…/libavfilter/avfilter.h。
该结构体用于定义一个滤镜,包含滤镜的名称、
typedef struct AVFilter {
// 滤镜器名称,非空且唯一
const char *name;
// 滤镜器描述信息,可为NULL
const char *description;
// List of inputs, terminated by a zeroed element
const AVFilterPad *inputs;
// List of outputs, terminated by a zeroed element.
const AVFilterPad *outputs;
const AVClass *priv_class;
// 前缀为AVFILTER_FLAG_*的标志
int flags;
// private API
...
} AVFilter;
AVFilterContext
:表示一个滤镜器(Filter)的实例(instance),位于…/libavfilter/avfilter.h。
struct AVFilterContext {
const AVClass *av_class;
// 该实例对应的滤镜器(Filter)
const AVFilter *filter;
// 滤镜器实例名称
char *name;
AVFilterPad *input_pads; ///< array of input pads
AVFilterLink **inputs; ///< array of pointers to input links
unsigned nb_inputs; ///< number of input pads
AVFilterPad *output_pads; ///< array of output pads
AVFilterLink **outputs; ///< array of pointers to output links
unsigned nb_outputs; ///< number of output pads
void