滤镜是「一个输入、一个输出、只干一件事」的小盒,串起来就是链。 这一章要用的三个滤镜在这台机器上缺了,原因在编译期,不在用法。
前五章一直在调「编码参数」,画面本身没被改过。 从这一章起才开始动画面里的像素——加水印、写字、抹掉东西。
一、滤镜是流水线上的工位
一个滤镜只做一件事:翻转、缩放、画框、叠层。 它有一个输入口、一个输出口,进出都是「一帧画面」,不碰编码码流。
正因为接口这么窄,它们才能任意拼接—— 前一个的输出喂给后一个,谁也不必知道上下游是谁。
这台机器上注册了 468 个滤镜。找功能时按动词搜前缀最快:
关键字 数量 这一族里有谁
draw 4 adrawgraph、drawbox、drawgraph、drawgrid scale 6 scale、scale2ref、scale_cuda… fade 4 fade、afade、xfade、acrossfade overlay 3 overlay、overlay_cuda、overlay_qsv
注意 draw 那一行:本该排在最前面的 drawtext 并不在里面。 缺滤镜就是这么缺的——不是报错,是清单里干脆没有它。
二、链的顺序就是数据的顺序
先画一个红框,再把画面水平翻转:
取样点 亮度 说明
x=30 y=15 102 翻转前是框内,翻转后成了空处 x=140 y=15 81 翻转前是空处,翻转后成了框内 x=70 y=5 102 两个位置都在框外
框原本占 x 从 10 到 49,翻转后跑到 110 到 149,位置正好对称。 这说明「先画框、后翻转」——框也跟着一起翻了。
把顺序倒过来,先翻后画,框就老老实实待在左边。
命令行里 -vf drawbox=...,hflip 的逗号顺序,就是数据流过的顺序。
这一点在长链上尤其要紧:一条十级的链,中间某一级放错位置, 画面能出来,只是结果不对,而且不报任何错。
三、本机缺了哪三个,为什么缺
这一章要用的三个滤镜,这台机器上一个都没有:
滤镜 本领 本机
movie 把文件当源读进来 有 drawbox 画方框 有 overlay 叠图层 有 drawtext 写字 没有 subtitles 烧字幕 没有 delogo 去水印 没有
原因不在 FFmpeg,而在这份构建编译时没接上 freetype 与 libass。 滤镜是「有条件才编进去」的:依赖库不在,这个滤镜干脆不注册。
所以「本机没有这个滤镜」和「命令写错了」是两件完全不同的事。 先用清单确认滤镜在不在,再去看参数——顺序反了,会白查很久。
四、动手 Demo:数清单、搭链、量像素
先数一遍滤镜总数,再看这一章要用的几个在不在。 然后搭一条 drawbox → hflip 的链,真喂一帧进去,量两个位置的亮度。
import av, numpy as np
from fractions import Fraction
fs = sorted(av.filter.filters_available)
print(u"滤镜总数 %d" % len(fs))
for n in (u"movie", u"drawbox", u"drawtext", u"delogo"):
print(u"%-10s %s" % (n, n in fs))
print(u"draw 族 %s" % u"、".join([x for x in fs if u"draw" in x]))
g = av.filter.Graph()
a = g.add_buffer(width=160, height=90, format=u"yuv420p",
time_base=Fraction(1, 25))
b1 = g.add(u"drawbox", u"x=10:y=10:w=40:h=20:color=red:t=fill")
b2 = g.add(u"hflip")
s = g.add(u"buffersink")
a.link_to(b1)
b1.link_to(b2)
b2.link_to(s)
g.configure()
fr = av.VideoFrame.from_ndarray(np.full((90, 160), 100, dtype=np.uint8),
format=u"gray")
fr = fr.reformat(format=u"yuv420p")
fr.pts = 0
a.push(fr)
y = s.pull().to_ndarray()
print(u"x=30 处 %d x=140 处 %d x=70 处 %d"
% (y[15][30], y[15][140], y[5][70]))
实跑输出:
--------------------------------------------------------------------
6.1 FFmpeg 的滤镜技术——清单、链、以及本机缺了谁
--------------------------------------------------------------------
**① 先数一遍滤镜,再看这一章要用的几个在不在**
**这台机器上的 libavfilter 一共注册了 468 个滤镜。**
滤镜 在本机
------------------------
movie 有
drawbox 有
overlay 有
scale 有
crop 有
drawtext 没有
subtitles 没有
delogo 没有
**后三个全是「没有」:drawtext、subtitles、delogo。**
**它们是这一章的三根支柱,drawtext 加文字、delogo 去水印、subtitles 烧字幕。**
**原因不在 FFmpeg 本身,而在这份构建编译时没接 freetype 与 libass 两个库——**
**滤镜是「有条件才编进去」的:依赖库不在,这个滤镜干脆就不注册。**
**② 名字里的规律:滤镜按「动作」命名**
关键字 数量 这一族里有谁
------------------------------------------------------------------
draw 4 adrawgraph、drawbox、drawgraph、drawgrid
scale 6 scale、scale2ref、scale_cuda、scale_d3d11
fade 4 acrossfade、afade、fade、xfade
overlay 3 overlay、overlay_cuda、overlay_qsv
**注意 draw 那一行:只剩 adrawgraph、drawbox、drawgraph、drawgrid 四个,**
**本该排在最前面的 drawtext 并不在里面——缺滤镜就是这么缺的。**
**所以要找某个功能,先想它的英文动词,再去清单里搜前缀——**
**比翻文档快,也比问人可靠。找到之后先数一眼同类里少没少东西,**
**少了的那几个,往往正是你要用的。**
**③ 滤镜链:上一级的输出直接喂下一级**
**画一个红框,再把画面水平翻转:**
取样点 亮度 说明
--------------------------------------------------------
x=30 y=15 102 翻转前是框内,翻转后成了空处
x=140 y=15 81 翻转前是空处,翻转后成了框内
x=70 y=5 102 两个位置都在框外
**框原本占 x 从 10 到 49,翻转后跑到 110 到 149,位置正好对称。**
**这条链在命令行里就是 `-vf drawbox=...,hflip`——**
**逗号隔开的顺序就是数据流过的顺序,写反了结果就完全不同。**
**④ 本章的写法:两条腿走路**
小节内容 命令行怎么写 本机怎么验
-------------------------------------------------------------------
加图片水印 -vf movie 读图 能真跑
加文字水印 -vf drawtext 缺滤镜,改用等价实验
去水印 -vf delogo 缺滤镜,改用等价实验
叠加图层 -filter_complex overlay 建图失败,改手算
**凡是本机跑不了的,都会写清「命令行怎么写」和「为什么这台机器跑不了」,**
**再用一段能手算的等价实验把原理验证掉——**
**这样命令行照抄就能用,原理也不靠猜。**
time_base 必须是 Fraction,给元组 (1, 25) 会报 returned 22。
这是 PyAV 的类型检查,不是 FFmpeg 的要求,但踩一次要查很久。
亮度 102 而不是 100,是因为灰度转 yuv420p 时做了取整。 量像素实验里,个位数的偏差不用管,看的是「框内 81 与框外 102」这个台阶。
链的中间结果不能跳过——b1.link_to(b2) 这一行才是「串」的动作。
少了它,两个滤镜各接一个 buffer,链是断的,配流就会失败。
30 秒实验
- 查清单:
ffmpeg -filters | findstr draw,看本机到底有哪些 draw 族。 - 换顺序:把
drawbox,hflip改成hflip,drawbox,重看两个取样点的亮度。 - 加一级:中间插一个
crop=w=80:h=90,看框还在不在画面里。
常见误区
- 把「缺滤镜」当成「参数写错」。先查清单,再看参数,顺序不能反。
- 以为滤镜能改码流。滤镜动的是解码后的像素,压缩参数是编码器的事。
- 链里少写 link。两个滤镜各自成链,配流直接失败,报错却很不直观。
自测三问(点开对答案)
1. 滤镜的输入输出是什么? —— 都是解码后的画面帧(或音频帧),不碰压缩码流。 2. 为什么这台机器上没有 drawtext? —— 编译时没接 freetype,属「有条件才编进去」的可选滤镜。 3. `-vf a,b` 里谁先执行? —— a 先,b 后。逗号顺序就是数据流过的顺序。- ☐ (本节完,下一节 6.2.1 -vf 的 movie 滤镜)