🎬 第十一章 体验FFmpeg 5.0
📄 一句话概括

命令从 4.x 到 7.x 一个字没变,能不能跑却取决于编译时有没有带 freetype。 「装了 5.0」跟「能用 5.0 的 drawtext」是两件事。

📝 本节和前面几节的关系

第六章做过跑马灯,第十章讲过 \, 转义,这一节补最后一块: 为什么同一套命令,在别人机器上能跑、在自己机器上连滤镜都找不到。

一、命令还是那条命令

第六章与第十章那条:

ffmpeg -i in.mp4 -vf "drawtext=text='Hello':x=w-mod(t*100\,w+tw):y=10" \
  out.mp4

语法从 4.x 到 7.x 没动过——drawtext 的选项名、表达式语法、\, 的规矩,全都一样。 所以这一节在「命令怎么写」上没有新东西。

新的是另一件事:这条命令在你机器上根本可能跑不了, 而且报的错跟「参数写错」长得完全不一样。

二、滤镜在不在,是编译时决定的

实测本机这套二进制里,跟文字有关的三个滤镜一个都没有:

滤镜 本机有吗 没有意味着什么


drawtext 没有 文字画不进画面 subtitles 没有 字幕烧不进画面 ass 没有 ASS 字幕也烧不了 overlay 有 贴图这条路还在

原因不在版本,在编译开关: drawtext 要靠 freetype 和 fontconfig,subtitles 与 ass 还要 libass。 这套二进制编的时候没带它们,所以 468 个滤镜里就是没有这几个。

换 5.0、换 7.0、换 git master,只要编译时不带 freetype,drawtext 就永远不在。 这就是「版本新 ≠ 功能全」最实在的一个例子: 决定你能不能用某个滤镜的,是编译选项,不是版本号。

报错长什么样也值得先看一眼:不是「参数不对」,而是 No such filter: 'drawtext'——它在说「根本没有这个滤镜」。 很多人看到这句会去怀疑拼写、怀疑版本、怀疑引号, 实际上该做的是查一下清单里有没有。

三、所以第六章那条 Pillow 退路,不是凑数

第六章在沙箱里做跑马灯时,没有走 drawtext, 而是用 Pillow 把字画成一张带透明通道的图,再用 overlay 贴上去。

当时看起来像是在「绕路」,现在有了清单就清楚了: 那不是绕路,那是这套环境下唯一能走通的路。 overlay 在、scale 在、hflip 在,所以「先画成图再贴」这条路全程无障碍。

更普遍地说:当一个滤镜不存在时,有两条对策—— 换一个带了它的构建,或者用「现成的滤镜 + 自己算」拼出同样的效果。 前者改环境,后者改方案; 在别人的机器上交付脚本时,后者往往更可靠,因为你控制不了对方的编译开关。

四、硬字幕、软字幕与字体依赖

drawtext 画的是硬字幕:像素烤进画面,播放器关不掉。 软字幕是另一条流,播放器可以选、可以关。

两条路对环境的依赖完全不同:

做法 依赖什么 缺了会怎样


drawtext 硬字幕 freetype + fontconfig 滤镜整个不存在 subtitles 烧进画面 libass 滤镜整个不存在 软字幕流 只要容器支持 mp4 只收 mov_text

第三行是「依赖最少」的一条:只要目标容器认这种字幕格式, 就不需要任何额外库。 代价是播放器得支持——观众那边可能什么都不显示。

五、查法:一条命令,比试错快十倍

别猜,直接问:

ffmpeg -filters | grep drawtext

在 Python 里等价的一句是 "drawtext" in av.filter.filters_available。 查到了才有后面所有事;查不到就别再折腾参数了—— 该做的是换一个带 freetype 的构建,或者像第六章那样用 Pillow 绕过去。

这条「先查清单再写命令」的习惯,能省掉一大半试错。 它对所有滤镜都成立:gblur、transpose、overlay,一个道理。

六、动手 Demo:查清单:文字类滤镜到底有几个

下面这段把跟文字/字幕有关的滤镜逐个查一遍。 结果比任何解释都直接:没有就是没有,跟参数无关。

import av.filter

F = set(av.filter.filters_available)
print("滤镜总数 %d" % len(F))
for nm in ("drawtext", "subtitles", "ass", "overlay",
           "scale", "hflip", "transpose", "gblur"):
    print("  %-10s %s" % (nm, "有" if nm in F else "没有"))

实跑输出:

--------------------------------------------------------------------
11.3 使用 FFmpeg 5.0 实现文字跑马灯
--------------------------------------------------------------------

**① 先查一件事:这套二进制里到底有没有 `drawtext`**

**第六章花了不少篇幅绕开 `drawtext`,改用 Pillow 画字再 `overlay`。**
**现在有了完整清单,可以直接把原因摆出来:**

  滤镜          本机有吗    没有意味着什么
  --------------------------------------------------------
  drawtext      没有        文字画不进画面
  subtitles     没有        字幕烧不进画面
  ass           没有        ASS 字幕也烧不了
  overlay       有          贴图这条路还在

**`drawtext` 要靠 freetype 和 fontconfig 编进来,`subtitles` 还要 libass。**
**这套二进制编的时候没带这三个,所以 468 个滤镜里就是没有它们。**

**这跟版本号一点关系都没有——换 5.0、换 7.0、换 git master,**
**只要编译时不带 freetype,`drawtext` 就永远不在。**
**这就是「版本新 ≠ 功能全」最实在的一个例子:**
**决定你能不能用某个滤镜的,是编译选项,不是版本号。**

**② 所以「5.0 上的跑马灯命令」该怎么看**

**书上那条命令本身没错,语法从 4.x 到 7.x 一个字都没变:**

    ffmpeg -i in.mp4 -vf "drawtext=text='Hello':x=w-mod(t*100\,w+tw):y=10" \
      out.mp4

**但它能不能跑,取决于你手上那个 ffmpeg 是不是带 freetype 编的。**
**所以「装了 5.0」跟「能用 5.0 的 drawtext」是两件事——**
**这跟第九章那条「打开成功不等于能用」是同一类陷阱,只是发生在编译期。**

**③ 查法只有一条,记牢**

    ffmpeg -filters | grep drawtext

**在 Python 里等价的一句是 `"drawtext" in av.filter.filters_available`。**
**查到了才有后面所有事,查不到就别再折腾参数了——**
**该做的是换一个带 freetype 的构建,或者像第六章那样用 Pillow 绕过去。**

前三行全是「没有」,后五行全是「有」——这个分界不是偶然: 前三个都要外部库(freetype/libass),后五个都是 FFmpeg 自己实现的。

这条分界可以直接当判据用:一个滤镜如果依赖外部库, 那你换一台机器就得重新查一遍;如果它是自带的,基本跟着版本走。

30 秒实验

  1. 把 drawtext 换成 gblur 跑一遍:看同一个查法在两种情况下输出什么。
  2. 在 Linux 上装 libfreetype6-dev 再编译一次:看 drawtext 是否出现。
  3. 查 fc-list | wc -l:字体装了多少个,跟 drawtext 能不能用没关系。

常见误区

  1. 以为 No such filter 是参数写错。它的意思是这个滤镜不存在。
  2. 以为装了新版就一定有 drawtext。要看编译时有没有 --enable-libfreetype。
  3. 在别人机器上交付依赖 drawtext 的脚本。对方的构建不一定带 freetype。
自测三问(点开对答案) 1. `drawtext` 依赖哪两个库? —— freetype 和 fontconfig;`subtitles` 与 `ass` 还要 libass。 2. 「版本新 ≠ 功能全」这句话怎么解释? —— 能不能用某个滤镜由编译开关决定,跟版本号高低没有必然关系。 3. 滤镜不存在时,除了换构建还有什么办法? —— 用现成滤镜拼:比如 Pillow 画字 + overlay 贴上去,效果等价。

相关笔记