同一条缩放命令有两条写法:-vf 一条线,-filter_complex 一张网。 差别不在效果,在于「能画多复杂」。
第六章把单个滤镜拆开看过了,这一章开始把它们连起来用。 连之前得先认清命令行上的两条路:简单滤镜与复杂滤镜。
一、两条命令,同一个效果
先把两条命令并排放着看:
ffmpeg -i s7.mp4 -vf scale=80:45 o1.mp4
ffmpeg -i s7.mp4 -filter_complex "[0:v]scale=80:45[v]" -map "[v]" o2.mp4
上面那条是简单滤镜,下面那条是复杂滤镜。 两条命令产出的文件,尺寸、画质、大小都一样。
那为什么还要有两种写法?因为「能做的事」不一样。 简单滤镜只能是一根线,复杂滤镜可以是一张网。
二、简单滤镜:一路进、一路出
简单滤镜的硬约束只有一条:输入一路,输出一路。
项 值
输入尺寸 160×90 输出尺寸 80×45 输入路数 1 输出路数 1
命令行里凡是只需要一个 -vf 就能表达的需求,都落在这一档里:
缩放、裁剪、翻转、调色、加边框,全都能用一条链写完。
注意「一路」说的是整条链,不是单个滤镜。
-vf scale=80:45,hflip,crop=60:40 用了三个滤镜,仍然是一路进一路出。
所以它还是简单滤镜。
三、复杂滤镜:一条命令里分出两路
插一个 split,画面就分成两路,各走各的:
分支 做了什么 输出尺寸
左路 scale=80:45 80×45 右路 hflip 160×90
两路的结果都能单独取出来,这就是「一个输入、两个输出」。
这种结构 -vf 写不出来:它只有一根线,没有分号,也没有 [v] 这种标签。
命令行里对应的是:
-filter_complex "[0:v]split=2[a][b];[a]scale=80:45[v1];[b]hflip[v2]"
方括号里是标签,分号隔开不同的链路——这就是「图」的写法。 7.1.2 会把「滤镜、链、图」这三层彻底讲清。
四、什么时候必须换写法
判断标准只有一条:需不需要第二个输入,或者需不需要第二个输出。
要做的事 简单滤镜够不够
缩放、裁剪、翻转、调色 够 同一画面分两路各做一套 不够 两段视频拼一起、叠加水印 不够 音视频各挂一条链 不够
后面三行都要么多一个输入、要么多一个输出,-vf 全接不了。
只要沾上一个「多」,就得换成 -filter_complex。
这一章剩下的部分,两种写法会一直交替出现:
单进单出给 -vf,多路合成给 -filter_complex。
五、动手 Demo:同一个缩放,两种写法,两种能力
下面这段代码跑三件事:一条单链、一次分叉、一次四路合成。
分叉与合成这两件,-vf 永远做不到,只有图能表达。
import av
from fractions import Fraction
TB = Fraction(1, 25)
# 一、单链:一路进一路出
g = av.filter.Graph()
b = g.add_buffer(width=160, height=90, format=u"yuv420p", time_base=TB)
s = g.add(u"scale", u"80:45")
k = g.add(u"buffersink")
b.link_to(s)
s.link_to(k)
g.configure()
print(u"单链建图 OK")
# 二、分叉:一个输入,两个出口
g2 = av.filter.Graph()
b2 = g2.add_buffer(width=160, height=90, format=u"yuv420p", time_base=TB)
sp = g2.add(u"split", u"2")
l = g2.add(u"scale", u"80:45")
r = g2.add(u"hflip")
k1 = g2.add(u"buffersink")
k2 = g2.add(u"buffersink")
b2.link_to(sp)
sp.link_to(l, 0, 0)
sp.link_to(r, 1, 0)
l.link_to(k1)
r.link_to(k2)
g2.configure()
print(u"分叉建图 OK,两个出口")
# 三、四个内存缓冲直接接 xstack:建不起来
try:
g3 = av.filter.Graph()
x = g3.add(u"xstack", u"inputs=4:layout=0_0|w0_0|0_h0|w0_h0")
for i in range(4):
g3.add_buffer(width=160, height=90, format=u"yuv420p",
time_base=TB).link_to(x, 0, i)
g3.add(u"buffersink")
g3.configure()
print(u"四路合成 OK")
except Exception as e:
print(u"四路直接用内存缓冲:%s" % str(e)[:40])
实跑输出:
--------------------------------------------------------------------
7.1.1 简单滤镜和复杂滤镜案例入门
--------------------------------------------------------------------
**① 先记住两条命令行:一条用 -vf,一条用 -filter_complex**
`ffmpeg -i s7.mp4 -vf scale=80:45 o1.mp4`
`ffmpeg -i s7.mp4 -filter_complex "[0:v]scale=80:45[v]" -map "[v]" o2.mp4`
**上面那条是简单滤镜,下面那条是复杂滤镜。同一个缩放,两种写法。**
**差别不在效果上,而在「能画多复杂的图」上。**
**② 简单滤镜:一条链,一头进一头出**
项 值
-----------------------
输入尺寸 160×90
输出尺寸 80×45
输入路数 1
输出路数 1
**这就是「简单」的全部含义:一路进、一路出。**
**命令行里凡是只需要一个 `-vf` 就能表达的需求,都落在这一档里。**
**③ 复杂滤镜:同一条命令里分出两路,各走各的**
分支 做了什么 输出尺寸
-------------------------------------
左路 scale=80:45 80×45
右路 hflip 160×90
**一个输入分成两路,一路缩小、一路翻转,两路都能单独取出来。**
**这种「分叉」用 `-vf` 写不出来——`-vf` 只能是一条线,没有分号也没有 `[v]` 标签。**
**命令行里对应的是:**
`-filter_complex "[0:v]split=2[a][b];[a]scale=80:45[v1];[b]hflip[v2]"`
**方括号里是标签,分号隔开不同的链路,这就是「图」的写法。**
**④ 什么时候必须上复杂滤镜**
要做的事 简单滤镜够不够
-----------------------------------------------
缩放、裁剪、翻转、调色 够
同一画面分两路各做一套 不够
两段视频拼一起、叠加水印 不够
音视频各挂一条链 不够
**判断标准只有一条:需不需要第二个输入,或者需不需要第二个输出。**
**只要有一个「需要」,就得换成 `-filter_complex`。**
**⑤ 这一章剩下的部分,两种写法会一直交替出现**
**凡是单进单出的,给 `-vf` 写法;凡是多路合成的,给 `-filter_complex`。**
**沙箱里两条路都能真跑——多路那边靠 `movie` 补位,后面会讲怎么补。**
第一段只说明「能建起来」,尺寸对不对要看后面几节的表。
这里的重点是第三段的报错:四路全用内存缓冲,xstack 直接拒绝。
能跑的组合是「一路内存缓冲 + 其余用 movie 补」,7.1.3 会实测这个对比。 所以本节记住两条:单链与分叉的写法,以及「多路不能全用内存缓冲」这条限制。
30 秒实验
- 换一条链:把
-vf scale=80:45换成scale=80:45,hflip,看还是一路进一路出。 - 去掉标签:把
[v]删掉直接-map 0:v,看命令是报错还是照样跑。 - 数出口:在分叉那段里再加一个
buffersink,看能不能接三路。
常见误区
- 以为复杂滤镜一定更好。单进单出用它,只是多敲几十个字符。
- 以为滤镜多就是复杂滤镜。几个滤镜串成一条线,仍然是简单滤镜。
- 忘了 -map。复杂滤镜产出的标签不会自动接上,不 map 就没有输出流。
自测三问(点开对答案)
1. 简单滤镜与复杂滤镜的本质区别是什么? —— 输入与输出的路数:简单滤镜只能 1 进 1 出,复杂滤镜可以任意多。 2. 一条 `-vf` 里串了五个滤镜,它算哪一档? —— 简单滤镜。滤镜个数不影响,进出都是 1 才是关键。 3. 什么时候必须用 `-filter_complex`? —— 需要第二个输入或第二个输出时,比如叠加、拼接、分叉。- ☐ (本节完,下一节 7.1.2 滤镜图、滤镜链、滤镜的关系)