FFmpeg通过libavfilter库实现滤镜功能,滤镜(filter)处理的是未压缩的原始音视频数据(RGB/YUV视频帧,PCM音频帧等)。比较常用的滤镜有:scale、trim、overlay、rotate。scale滤镜用于缩放,trim滤镜用于帧级剪切,overlay滤镜用于视频叠加,rotate滤镜实现旋转。
滤镜语法
滤镜链是由逗号分隔的滤镜(filter)序列组成:
ffmpeg -i input.mpg -vf filter1,fiter2,filter3,... output.mp4
滤镜图通常是由分号分隔的滤镜链组成。滤镜图分为简单滤镜图和复杂滤镜图。
一个滤镜由一个字符串表单表示:
[in_link_1]...[in_link_N]filter_name=arguments[out_link_1]...[out_link_M]
filter_name是滤镜类名字,用于指定滤镜实例(它注册于程序中)。其后的=arguments用于指定滤镜选项。arguments是用于初始化滤镜的参数,它可能有下面两类表单中的一个:
- 一个“:”分隔的key=value列表
- 一个“:”分隔的列表value值,在这种情况下,键(key)假定为选项名声明顺序,如fade滤镜按顺序声明了3个选项type、start_frame和nb_frames,则参数 in:0:30 意味着type为in,start_frame为0,nb_frames为30。
- 前面二者的混合。这种情况下,键值对必须在前,然后接遵循相同约束的若干值。
如果选项的值本身就是一个列表(例如format滤镜有一个像素格式列表选项),则这种列表通常用“|”分隔。
列表参数可以被’(单引号)包含起来。字符\作为转义字符用于引号包含的文本。否则参数字符串在遇到特殊字符(例如’[]=;,’)处被看作终止。
标签in_link_1 … in_link_N作为滤镜的输入端,out_link_1 … out_link_M作为滤镜的输出端。
当中一个滤镜链图中找到相同名字的连接标签时,一个在相应输入端和输出端之间的连接被创建。
如果一个输出端没有命名标签,它默认连接到滤镜链上后面滤镜中第一个没有命名标签的输入端。例如:
nullsrc, split[L1], [L2]overlay, nullsink
这里split有两路输出,overlay有两路输入,split的第一路输出被命名为标签”L1”,overlay的第一路输入被命名为标签”L2”。则split的第二路输出链接到overlay的第二路输入(它们都没有用标签命名)。
在一个滤镜描述中,如果第一个滤镜的输入没有指定,则假定为”in”,如果最后一个滤镜输出没有指定,则假定为”out”。
如果滤镜没有输入端,则被称作“源”,如果滤镜没有输出端则被称作“槽”(这样的滤镜用于描述/测试等场景,而不用于实际处理)。
在一个完整的滤镜链上,所有滤镜的输入和输出都被连接则被认为是有效的。
滤镜链图成分需要包含多层的转义:
- 第一层的转义效果在每个滤镜选项值
- 第二层的转义在整个滤镜描述
- 第三层的转义在shell命令行上描述滤镜链图时
例如下面的字符需要嵌入drawtext滤镜描述的text值
this is a 'string': may contain one, or more, special characters
这个字符串包含’字符需要被转义,还有:这里也需要被转义,方法是:
text=this is a \'string\'\: may contain one, or more, special characters
当嵌入滤镜描述时发生第二层的转义,为了转义所有的滤镜链图特殊字符,需要按下例处理:
drawtext=text=this is a \\\'string\\\'\\: may contain one\, or more\, special characters
最终当在shell命令行中写这个滤镜链图时再次转义。例如需要对每个特殊字符和转义处理的\等进行转义,最终为:
-vf "drawtext=text=this is a \\\\\\'string\\\\\\'\\\\: may contain one\\, or more\\, special characters"
简单滤镜图
简单滤镜图只能处理单路输入流和单路输出流,而且要求输入和输出具有相同的流类型。
简单滤镜图由-filter选项指定,-filter:v选项(同-vf)表示使用视频滤镜, -filter:a选项(同-af)表示使用音频滤镜。
简单滤镜图示意图如下:
_______ _____________________ ________
| | | | | |
| input | ---> | simple filter graph | ---> | output |
|_______| |_____________________| |________|
举例如下:
[main]
input --> split ---------------------> overlay --> output
| ^
|[tmp] [flip]|
+-----> crop --> vflip -------+
在这个滤镜链图中,利用split滤镜把输入流分离成了两路流,其中一路通过crop滤镜和vfilp滤镜的同一路级联应用,再同另外一路一起通过overlay滤镜处理的流合成进行输出。则可以采用如下的命令行实现:
ffmpeg -i INPUT -vf "split [main][tmp]; [tmp] crop=iw:ih/2:0:0, vflip [flip]; [main][flip] overlay=0:H/2" OUTPUT
同一路的滤镜间用逗号(‘,’)进行分割,不同路的滤镜间用分号(‘;’)进行分割。在这个例子里面crop和vflip是在同一路中的滤镜,split和overlay则不是同一路的。可以通过在方括号(’[]’)中的标签名来命名处理的链路。这个例子里,split滤镜生成了两路就通过[main]和[tmp]进行了标签命名以方便后续处理。
复杂滤镜图
复杂滤镜图用于简单滤镜图处理不了的场合。比如,多路输入流和(或)多路输出流,或者输出流与输入流类型不同。
有些特殊的滤镜(filter)本身就属于复杂滤镜图,用-filter_complex选项或-lavfi选项指定,如overlay滤镜就是复杂滤镜图:
ffmpeg -i cuc_short.flv -i clock.avi -filter_complex overlay=0:0 compare.mp4