🎬 第七章 FFmpeg命令行实现音视频特效及复杂滤镜应用
📄 一句话概括

同一条缩放命令有两条写法:-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 秒实验

  1. 换一条链:把 -vf scale=80:45 换成 scale=80:45,hflip,看还是一路进一路出。
  2. 去掉标签:把 [v] 删掉直接 -map 0:v,看命令是报错还是照样跑。
  3. 数出口:在分叉那段里再加一个 buffersink,看能不能接三路。

常见误区

  1. 以为复杂滤镜一定更好。单进单出用它,只是多敲几十个字符。
  2. 以为滤镜多就是复杂滤镜。几个滤镜串成一条线,仍然是简单滤镜。
  3. 忘了 -map。复杂滤镜产出的标签不会自动接上,不 map 就没有输出流。
自测三问(点开对答案) 1. 简单滤镜与复杂滤镜的本质区别是什么? —— 输入与输出的路数:简单滤镜只能 1 进 1 出,复杂滤镜可以任意多。 2. 一条 `-vf` 里串了五个滤镜,它算哪一档? —— 简单滤镜。滤镜个数不影响,进出都是 1 才是关键。 3. 什么时候必须用 `-filter_complex`? —— 需要第二个输入或第二个输出时,比如叠加、拼接、分叉。

相关笔记