三层包含:图里有若干链,链里有若干滤镜。 改一个字母就能把「一条链」变成「两条链」——那个字母就是分号。
9.1.4 讲了单张图的形状,这一节讲一张图里能装几条链。 分清三层,命令行里的逗号与分号就不会用错。
一、三层是什么关系
从大到小,一层套一层。 层 是什么 数量
滤镜 最小单位,一个功能 本机 468 个 链 一串首尾相接的滤镜 图里有几条 图 若干条链的集合 通常一张
本机可用的滤镜一共 468 个,这个数是「这台机器编进了哪些模块」的指纹,换一台机器就不同。 链是「串」不是「组」:链上的滤镜是一个接一个传下去的,前一环的输出就是后一环的输入, 顺序不能乱。 图是「集」:图里的链互不相干,各走各的路,谁也不知道谁存在。
二、命令行里的逗号与分号
这两个符号就是三层的分界。 写法 含义 层级
scale=80:60,hflip 一条链上两个滤镜 链内 [0:v]scale[a];[a]hflip[b] 两条链 图内
逗号是「然后」,分号是「另外」。 记这两个词就够了。
方括号里的名字是「这条链的进出口叫什么」,
有了名字才能跨链引用——比如把第一条链的结果拿给第二条链用。
名字不是随便起的:[0:v] 指第 0 个输入文件的视频轨,[a]/[b] 是自己起的中转名,
最后 -map 决定哪一路落到输出。
三、一张图里两条不相干的链
实测:一条只翻转、一条只缩放,各接各的 sink。 链 做了什么 出帧宽
链一 hflip 左右翻 160 链二 scale 缩到 80×60 80
两条链完全独立:各有自己的源、自己的汇,互不影响。 这就是「一张图里能同时干几件不相干的事」的原因。 注意每条链都要有自己的 buffer,不能两链共用一个源。 一个源的输出只能接一个目标,要分两路得先用 split 复制一份。
四、什么时候需要多条链
三个典型场景,都是「同一份输入要变成不同样子」。 场景 为什么要多链
同时输出大小两版 两条链,各接一个编码器 画中画 一条链缩放副本,再与主图 overlay 一进多出做分析 split 分流,一路存盘一路统计
共同点是「一个输入要产出多种结果」。 只有一条链时,一个输入只能产出一个输出。 直播里这个需求特别多:同一条流要同时给手机小窗与电视大屏,就是两条链各缩各的、各编各的。
五、三层的边界在哪
几条容易混的边界,记清楚就少走弯路。 问题 答案
一个滤镜实例能在两张图里吗 不能,属于创建它的那张图 一条链能跨两张图吗 不能,链在图内 一张图能有几条链 任意条,各有各的头尾 一个缓冲源能接几个下游 只能一个,要分流先 split
「能接几个下游」这条最常踩:想一路原样存盘、一路缩放推流,很容易写成两条链共用同一个源,
结果 configure 就报错。
正确写法是先 split 出两路,再各接一条链。 split 就是这个「一分二」的官方工具。
六、动手 Demo:图、链、滤镜——三层包含关系
先数本机有多少滤镜,再在一张图里放两条互不相干的链。
import av
import av.filter
from fractions import Fraction
print("本机滤镜数", len(av.filter.filters_available))
def buf(g, name):
return g.add_buffer(width=160, height=120,
format="yuv420p",
time_base=Fraction(1, 25),
name=name)
g = av.filter.Graph()
# 链一:原尺寸左右翻
a = buf(g, "in1")
flip = g.add("hflip")
k1 = g.add("buffersink")
a.link_to(flip); flip.link_to(k1)
# 链二:缩到 80x60
b = buf(g, "in2")
sc = g.add("scale", "80:60")
k2 = g.add("buffersink")
b.link_to(sc); sc.link_to(k2)
g.configure() # 两条链一起配
a.push(make_frame(0))
b.push(make_frame(0))
print("链一出帧宽", k1.pull().width)
print("链二出帧宽", k2.pull().width)
实跑输出:
--------------------------------------------------------------------
图、链、滤镜——三层包含关系
--------------------------------------------------------------------
**① 三层是什么关系**
层 是什么 数量
-----------------------------------------------
滤镜 最小单位,一个功能 本机 468 个
链 一串首尾相接的滤镜 图里有几条
图 若干条链的集合 通常一张
本机可用的滤镜一共 **468 个**。
**「链」在命令行里就是逗号**:`-vf "scale=80:60,hflip"` 是两条?不,是**一条链上的两个滤镜**。
**「图」在命令行里是分号**:
`-filter_complex "[0:v]scale=80:60[a];[a]hflip[b]"` **分号隔开的就是两条链。**
**② 一张图里放两条互不相干的链**
配置成功。链一出帧宽 **160**(只翻转),链二出帧宽 **80**(缩放了)。
**两条链完全独立**:各有自己的源、自己的汇,互不影响。
**这就是「一张图里能同时干几件不相干的事」的原因。**
**③ 什么时候会需要多条链**
场景 为什么要多链
---------------------------------------------------------
同时输出大小两版 两条链,各接一个编码器
画中画 一条链缩放副本,再与主图 overlay
一进多出做分析 split 分流,一路存盘一路统计
**共同点是「同一份输入要变成不同样子」**。只有一条链时,一个输入只能产出一个输出。
两条链各接各的 sink,一起 configure 就能同时跑。
不能两条链共用一个 buffer,想分流得先 g.add("split")。
30 秒实验
- 把链二的
add_buffer删掉、改成a.link_to(sc),看 configure 报什么。 - 在链一前面插一个
split,让两链共用同一个源,跑通它。 - 打印
av.filter.filters_available里含split与overlay的名字。
常见误区
- 把逗号当成「两条链」。 逗号是同一链上的两个滤镜。
- 两条链共用一个 buffer。 一个源只能接一个下游。
- 以为一条链能跨两张图。 链是图内的东西。
自测三问(点开对答案)
1. 图、链、滤镜三者的包含关系是什么? —— 图包含若干链,链包含若干滤镜,滤镜是最小单位。 2. 命令行里逗号与分号分别代表什么? —— 逗号是同一链上的下一个滤镜,分号是图里的另一条链。 3. 想让两路处理共用一份输入,正确做法是什么? —— 先用 split 复制成两路,再各接一条链。- ☐ (本节完,下一节 9.1.6 FFmpeg滤镜相关的重要结构体)