FFmpeg为了统一filter库API接口,FFmpeg提出了参考DirectDraw实现了高质量、高效、灵活的音视频filter接口。可以实现类似积木的多种功能的自由组合。每个filter都有固定数目的输入和输出,而且实际使用中不允许有空悬的输入输出端。使用文本描述时我们可以通过标识符指定输入和输出端口,将不同filter串联起来,构成更复杂的filter。
ffmpeg、ffplay能够通过filter处理原始的音视频数据。
simple & complex graph
ffmpeg将filtergraph分为simple filtergraph
和complex filtergraph
。通常simple filtergraph只有一个输入和输出,ffmpeg命令行中使用-vf
、-af
识别,基本原理图如下:
_________ ______________
| | | |
| decoded | | encoded data |
| frames |\ _ | packets |
|_________| \ /||______________|
\ __________ /
simple _\|| | / encoder
filtergraph | filtered |/
| frames |
|__________|
complex filtergraph
,通常是具有多个输入输出文件,并有多条执行路径;ffmpeg命令行中使用-lavfi
、-filter_complex
,基本原理图如下:
_________
| |
| input 0 |\ __________
|_________| \ | |
\ _________ /| output 0 |
\ | | / |__________|
_________ \| complex | /
| | | |/
| input 1 |---->| filter |\
|_________| | | \ __________
/| graph | \ | |
/ | | \| output 1 |
_________ / |_________| |__________|
| | /
| input 2 |/
|_________|
filter的三个层次
从整体看,filte rgraph包含filter chain,而filter chain又包含了filter,所以可以分为是三个层次去理解。
- filter
- filter chain
- filter graph
filter graph是链接多个filter的有向图。它可以包含循环,各个Filter之间也可以有多个链接。每个链接有一个input pad
连接到一个filter并从那里获取输入,有一个output pad
连接到另一个filter
提供输出。所有的Filter都是已经注册在程序中的。没有输入的filter叫source filter
,没有输出的filter叫sink filter
。
每个filter graph
都有对应的结构化的文本表示:
- 对于命令ffmpeg来说有
-filter, -vf, -af
和filter_complex
这些选项 - 对于命令ffplay来说有
-vf, -af
这些选项
filter chain
:是由一个或多个filter线性连接而成,filter之间使用逗号,
来分隔
filter graph
:是由一个或多个filter chain组成,filter chain之间使用分号;
来分隔
filter的语法
filter的语法格式为:
[in_link_1]...[in_link_N]filter_name@id=arguments[out_link_1]...[out_link_M]
- filter_name就是你所要使用的filter的名称
- 后面的@id是可选的(id是自定义的,指定id在log中方便识别)
- Filter的名称和参数前后是允许放置一个或多个链接标签(
link label
: 别名)- 放在前面的链接标签
in_link_1
…in_link_N
被关联到Filter的input pad
- 后面的
out_link_1
…out_link_M
被关联到Filter的output pad
- 如果一个filter的
output pad
没被标记链接标签,则它会被默认链接到后续的filter中第一个未标记链接标签的input pad
- 放在前面的链接标签
=arguments
也是可选填的(有些filter并不需要参数)- arguments通常有如下格式:
- 用
:
来分隔一系列值 - 用
:
来分隔一系列的key=value的键值对 - 用
:
来分隔一系列值,之后跟着一系列的key=value的键值对,值必须在键值对前面 - 有时候value本身就是要填一系列参数值的,那么需要用|来拼接
- 用
- arguments通常有如下格式:
- 对于一个完整可用的
filter chain
来说,所有匿名的链接标签必须有被链接。 - 在处理filter graph过程中当遇到有格式转换的时候会自动插入scale这个filter,所以在filter graph描述中的可能会隐式包含scale的地方前面,需要加上要指定的缩放参数:
sws_flags=flags
如:
nullsrc, split[L1], [L2]overlay, nullsink
从所有filter里面找到nullsink:
ffmpeg -filters | grep nullsink
列出split的说明:
$ ffmpeg -h filter=split
Filter split
Pass on the input to N video outputs.
Inputs:
#0: default (video)
Outputs:
dynamic (depending on the options)
split AVOptions:
outputs <int> ..FVA...... set number of outputs (from 1 to INT_MAX) (default 2)
ffmpeg的命令行输出比较有限,了解filter怎么用,还是得从官网的文档里面学习:
比如,split的用法:
- 从同一输入创建两个单独的输出:
[in] split [out0][out1]
- 要创建3个或更多输出,需要指定输出数量,如:
[in] asplit=3 [out0][out1][out2]
视频合并淡入淡出效果
ffmpeg -i dog3.mp4 -i dog3.mp4 -i dog3.mp4 -i dog3.mp4 -i dog3.mp4 \
-filter_complex "\
[0:v]setpts=PTS-STARTPTS[v1]; \
[1:v]format=yuva420p,fade=in:st=0:d=1:alpha=1,setpts=PTS-STARTPTS+(4/TB)[v2];
[2:v]format=yuva420p,fade=in:st=0:d=1:alpha=1,setpts=PTS-STARTPTS+(8/TB)[v3];
[3:v]format=yuva420p,fade=in:st=0:d=1:alpha=1,setpts=PTS-STARTPTS+(12/TB)[v4];
[4:v]format=yuva420p,fade=in:st=0:d=1:alpha=1,setpts=PTS-STARTPTS+(16/TB)[v5];
[v1][v2]overlay[v12];
[v12][v3]overlay[v123];
[v123][v4]overlay[v1234];
[v1234][v5]overlay,format=yuv420p[v]" \
-map [v] result.mp4
加上audio,并且使用acrossfade:
ffmpeg -i dog3.mp4 -i dog3.mp4 -i dog3.mp4 -i dog3.mp4 -i dog3.mp4 \
-filter_complex "\
[0:v]setpts=PTS-STARTPTS[v1]; \
[1:v]format=yuva420p,fade=in:st=0:d=1:alpha=1,setpts=PTS-STARTPTS+(4/TB)[v2];
[2:v]format=yuva420p,fade=in:st=0:d=1:alpha=1,setpts=PTS-STARTPTS+(8/TB)[v3];
[3:v]format=yuva420p,fade=in:st=0:d=1:alpha=1,setpts=PTS-STARTPTS+(12/TB)[v4];
[4:v]format=yuva420p,fade=in:st=0:d=1:alpha=1,setpts=PTS-STARTPTS+(16/TB)[v5];
[v1][v2]overlay[v12];
[v12][v3]overlay[v123];
[v123][v4]overlay[v1234];
[v1234][v5]overlay,format=yuv420p[v]; \
[1][2]acrossfade=d=1[a12];
[a12][3]acrossfade=d=1[a123];
[a123][4]acrossfade=d=1[a]" \
-map [v] -map [a] result.mp4
这里的audio直接用[1],[2],[3],[4]表示,为了更直观点,应该写成:
ffmpeg -i dog3.mp4 -i dog3.mp4 -i dog3.mp4 -i dog3.mp4 -i dog3.mp4 \
-filter_complex "\
[0:v]setpts=PTS-STARTPTS[v1]; \
[1:v]format=yuva420p,fade=in:st=0:d=1:alpha=1,setpts=PTS-STARTPTS+(4/TB)[v2];
[2:v]format=yuva420p,fade=in:st=0:d=1:alpha=1,setpts=PTS-STARTPTS+(8/TB)[v3];
[3:v]format=yuva420p,fade=in:st=0:d=1:alpha=1,setpts=PTS-STARTPTS+(12/TB)[v4];
[4:v]format=yuva420p,fade=in:st=0:d=1:alpha=1,setpts=PTS-STARTPTS+(16/TB)[v5];
[v1][v2]overlay[v12];
[v12][v3]overlay[v123];
[v123][v4]overlay[v1234];
[v1234][v5]overlay,format=yuv420p[v]; \
[1:a][2:a]acrossfade=d=1[a12];
[a12][3:a]acrossfade=d=1[a123];
[a123][4:a]acrossfade=d=1[a]" \
-map [v] -map [a] result.mp4
前面的命令[0][1]直接写意思是input 0, input 1
[0:v] input 0 video stream
[0:a] input 0 audio stream
注意,在前面的filter_complex的最后一个标签后不能跟分号,跟了分号就报错,因为分号以为这后面还有filter chain,但是后面却是空的:
[AVFilterGraph @ 0x55a826aa5a60] No such filter: ''
Error initializing complex filters.
Invalid argument
lavfi
Libavfilter的输入虚拟设备,此输入设备从filter graph
的打开的输出pad读取数据。对于每个filtergraph打开输出,输入设备将创建一个对应的流,该流映射到生成的输出。
支持的选项:
-
graph
:指定用作输入的过滤图。 -
graph_file
:设置要读取的filter graph的文件名。 -
dumpgraph
:dump graph到标准错误。
用文件作为lavfi输入:
ffmpeg -f lavfi -i "amovie=chengdu.mp3" test.wav -v 56
创建三个不同的视频测试过滤源并播放它们:
ffplay -f lavfi -graph "testsrc [out0]; testsrc,hflip [out1]; testsrc,negate [out2]" test3
FFmpeg Filters翻译文档
FFmpeg filter简介
[ffmpeg] 滤波
[ffmpeg] 滤波格式协商
[ffmpeg] 定制滤波器
[ffmpeg] 多输入滤波同步方式(framesync)