🎬 第九章 AVFilter过滤器层理论及案例实战
📄 一句话概括

三层包含:图里有若干链,链里有若干滤镜。 改一个字母就能把「一条链」变成「两条链」——那个字母就是分号。

📝 本节和前面几节的关系

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 秒实验

  1. 把链二的 add_buffer 删掉、改成 a.link_to(sc),看 configure 报什么。
  2. 在链一前面插一个 split,让两链共用同一个源,跑通它。
  3. 打印 av.filter.filters_available 里含 split 与 overlay 的名字。

常见误区

  1. 把逗号当成「两条链」。 逗号是同一链上的两个滤镜。
  2. 两条链共用一个 buffer。 一个源只能接一个下游。
  3. 以为一条链能跨两张图。 链是图内的东西。
自测三问(点开对答案) 1. 图、链、滤镜三者的包含关系是什么? —— 图包含若干链,链包含若干滤镜,滤镜是最小单位。 2. 命令行里逗号与分号分别代表什么? —— 逗号是同一链上的下一个滤镜,分号是图里的另一条链。 3. 想让两路处理共用一份输入,正确做法是什么? —— 先用 split 复制成两路,再各接一条链。

相关笔记