一、FFmpeg filter的原理
1. 引言及示例
FFmpeg中的libavfilter提供了一整套的基于filter的机制。
filter本身是一个插件的形式,可以快速的组装需要的效果。
比如下面的filter,可以实现视频的水平镜像效果。
ffplay.exe sample.rmvb -vf hflip
1.1 FFmpeg为什么重新定义filter API?
FFmpeg定义的libavcodec接口已经成为在编解码领域的事实上的行业标准。
但音视频filter并没有类似的标准,多个不同的多媒体项目(比如MPlayer、Xine、GStreamer等)都实现了自定义的filter系统。
为了统一filter库API接口,FFmpeg提出了参考DirectDraw实现了高质量、高效、灵活的音视频filter接口。
1.2 传统概念上filter是什么?
filter可以翻译成过滤器,滤镜、滤波器。物理概念上,常见的过滤器跟净化器概念重复,比如滤水器、空气净化器等。
- 在计算机程序中,filter是指一段代码,可用于检查输入或者输出,按照预定的规则处理并传递这些数据。
换种说法,filter是一种传递(pass-through)代码块,将输入数据做特定的变换并输出。
通常filter自身不做任何输入/输出。
举个例子,linux下的grep可以认为是一个filter,按照正则表达式匹配选择,从输入中选择输出数据。
- 在电信工程领域,filter通常指的用于信号处理的设备,
比如音频处理比较典型的低通滤波器、高通滤波器、带通滤波器、去噪滤波器等。
1.3 filter的分类
按照处理数据的类型,通常多媒体的filter分为:
- 音频filter
- 视频filter
- 字幕filter
另一种按照处于编解码器的位置划分:
- prefilters: used before encoding
- intrafilters: used while encoding (and are thus an integral part of a video codec)
- postfilters: used after decoding
FFmpeg中filter分为:
- source filter (只有输出)
- audio filter
- video filter
- Multimedia filter
- sink filter (只有输入)
除了source和sink filter,其他filter都至少有一个输入、至少一个输出。
1.4 示例
下面也是一个例子,使用filter实现宽高减半显示:
ffplay.exe sample.rmvb -vf scale=iw/2:ih/2
下面是使用mptestsrc的source filter作为ffplay输入,直接显示:
ffplay -f lavfi mptestsrc=t=dc_luma
ffplay -f lavfi life=s=300x200:mold=10:r=60:ratio=0.1:death_color=#C83232:life_color=#00ff00,scale=1200:800:flags=16
2. 基本原理
FFmpeg filter可以认为是一些预定义的范式,可以实现类似积木的多种功能的自由组合。
每个filter都有固定数目的输入和输出,而且实际使用中不允许有空悬的输入输出端。
使用文本描述时我们可以通过标识符指定输入和输出端口,将不同filter串联起来,构成更复杂的filter。这就形成了嵌套的filter。
当然每个filter可以通过ffmpeg/ffplay命令行实现,但通常filter更方便。
ffmpeg.exe、ffplay.exe能够通过filter处理原始的音视频数据。
ffmpeg将filtergraph分为simple filtergraph和complex filtergraph。
通常simple filtergraph只有一个输入和输出,ffmpeg命令行中使用-vf、-af识别,
基本原理图如下:
complex filtergraph,通常是具有多个输入输出文件,并有多条执行路径;
ffmpeg命令行中使用-lavfi、-filter_complex,基本原理图如下:
在libavfilter, 一个filter可以包含多个输入、多个输出。
下图是一个filtergraph的示例:
上图中filtergraph将输入 流(INPUT) 分成两个流(分别是main 和 tmp ),
其中一个(即tmp)通过crop filter和vflip filter,它们的输出命名为 (flip)
另一个(即main)不做处理;
然后通过overlay filter将这两个流(即 main 和 flip)合成一个流输出(OUTPUT)。
这个filtergraph可以用下面命令行表示:
ffmpeg -i INPUT -vf "split [main][tmp]; [tmp] crop=iw:ih/2:0:0, vflip [flip]; [main][flip] overlay=0:H/2" OUTPUT
3. Filter的语法
FFmpeg中filter包含三个层次,filter->filterchain->filtergraph。
具体可以参考下图:
上图中,
第一层是 filter 的语法;
第二层是 filterchain的语法;
第三层是 filtergraph的语法。
3.1 Filter 的语法
[input_link_label_1][input_link_label_2] ...
filter_name=param_name1=param_value1:param_name2=param_value2
[ouput_link_lable_1][output_link_label_2]...
其中:
[input_link_label_X] :指以 label 标识的Filter的输入,label可以随意命令,它是可选的,可以有多个,用方括号分隔;
filter_name : 是fliter的名称, 必须有;它的参数是可选 的,以":"分隔或"+"号,可以有参数名,也可以没有;
[output_link_label_X] : 指以 label 标识的Filter的输入,label可以随意命令,它是可选的, 可以有多个,用方括号分隔;
没有音频、视频输入的filter称为source filter;
没有音频、视频输出的filter称为sink filter。
例如:
ffmpeg -i video.avi
-filter_complex 'extractplanes=y+u+v[y][u][v]' -map '[y]' y.avi -map '[u]' u.avi -map '[v]' v.avi
这个抽取视频Y、U、V分量的filter,就有三个输出,分别是 [y][u][v], 抽取后,将不同的输出保存到不同的文件中;
例如:
ffmpeg -i video.avi -vf 'delogo=x=0:y=0:w=100:h=77:band=10' output.avi
3.2 filterchain的语法
filter1,filter2,....
它是多个filter的组合,以逗号分隔;并且每个filter是输入是前一个filter的输出;
例如:
ffmpeg -i audio.aac
-filter_complex "aresample=async=16000,adelay=316397,volume=1.0" -acodec libfdk_aac -y output.mp4
这里用到了三个filter,分别的aresample, adelay, volume, 组成一个filterchain;
3.3 filtergraph的语法
filterchain1;filterchain2;...
它是多个filterchain的组合,以分号";"分隔,
例如:
ffmpeg -i INPUT -filter_complex "split [main][tmp]; [tmp] crop=iw:ih/2:0:0, vflip [flip]; [main][flip] overlay=0:H/2" OUTPUT
这里用到了三个filterchain, 分别是:
split [main][tmp]; // 它只有一个filter,即 split; 它有一个默认的输入,即INPUT解码后的frame;
有两个输出, 以 [main], [tmp] 标识;
[tmp] crop=iw:ih/2:0:0, vflip [flip] // 它由两个filter组成,即crop和vflip;一个输入 [tmp], 一个输出[flip];
[main][flip] overlay=0:H/2 // 它由一个filter组成,即overlay, 有两输入,分别是[main][flip], 一个默认的输出;