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

硬字幕烧进像素里、关不掉、改了要重编;软字幕是独立一条流、能关能改。 mp4 只能装 mov_text,想保留样式得用 mkv。

📝 本节和前面几节的关系

这是第七章最后一节的前半段:前面做画面,这里往画面上加文字。 字幕之所以要单独讲,是因为它牵扯到「流」与「像素」两种完全不同的存在方式。

一、两种字幕,一线之隔

字幕分成两大类,区别只有一句:文字有没有变成画面的一部分。

类型 存在形式 能不能关 改文字要不要重编码


硬字幕 烧进画面像素里 不能 要 软字幕 独立的字幕流 能 不用

硬字幕的优点是「谁都能看见」——发到任何平台都一样,不用观众去开字幕开关。 缺点是改不了,而且编码一次就定型了。

软字幕的优点是「随时能改、随时能关」,播放器自己渲染。 缺点是有些平台不认,尤其是短视频平台,上传之后字幕就没了。

一句话记:硬字幕是「画」,软字幕是「层」。 「画」改不了,「层」随时能换。

二、两种字幕文件

文件本身也分两种:

格式 扩展名 能写什么


SRT .srt 序号、起止时间、一行文字 ASS/SSA .ass SRT 的全部,加上字体、颜色、位置、动效

SRT 是最通用的:纯文本,几乎什么播放器都认,但样式全靠播放器自己定。 ASS 是「带样式的字幕」,卡拉 OK、双语、花字都靠它。

SRT 的一条长这样:三行一组,序号、时间、文字,中间空一行。 时间格式是 时:分:秒,毫秒,注意毫秒前面是逗号不是点——这里写错是最常见的失败原因。

上次字幕与下次字幕之间一定要空一行,不空行的话播放器会把两条当成一条。 有些播放器宽容,有些直接整条不显示,很难查。

三、软字幕的编码格式

字幕流也是要编码的,容器不同能装的格式不同:

编码 装在哪 特点


mov_text mp4 / mov 最通用,但不支持样式 subrip mkv 就是 SRT,纯文本 ass mkv 样式全保留 webvtt webm 网页播放器原生支持

mp4 装字幕只能用 mov_text,这是容器的限制,不是编码器的限制。 想保留 ASS 的花样,得用 mkv。

mov_text 只有「文字 + 时间」,字体、颜色、位置全丢。 所以「带样式的字幕要发到手机上看」,就只能烧成硬字幕。

四、怎么选

一句话:要给平台看,做硬字幕;要给自己留后路,做软字幕。 给客户交付时常常两份都给——带硬字幕的成片,加带软字幕的工程文件。

还有第三种做法:先做软字幕,最后一步再烧成硬字幕。 这样改字只改字幕文件,不用从头编一遍,返工成本低得多。

具体到这条课件的素材:两秒、160×90、25 帧,字幕带顶多占 18 行高。 算一下就知道,字幕区只占画面的五分之一——所以字幕字号不能太大, 超过一格行高就会盖住内容。

五、动手 Demo:滤镜有没有、SRT 长什么样

下面这段代码先查这个构建里有没有字幕相关滤镜, 再把软字幕常用的四种编码与一条 SRT 打印出来。

import av

print(u"--- 这个构建里有没有字幕相关滤镜")
for nm in (u"subtitles", u"ass", u"drawtext"):
    try:
        av.filter.Filter(nm)
        print(u"  %-10s 有" % nm)
    except Exception:
        print(u"  %-10s 没有" % nm)

print(u"--- 能用的字幕流编码(容器决定,不是滤镜)")
for nm in (u"mov_text", u"subrip", u"ass", u"webvtt"):
    print(u"  %s" % nm)

print(u"--- SRT 的一条长这样")
print(u"1")
print(u"00:00:01,000 --> 00:00:03,000")
print(u"第一句字幕")

实跑输出:

--------------------------------------------------------------------
7.17.1 字幕简介
--------------------------------------------------------------------

**① 两种字幕,一线之隔**

**字幕分成两大类,区别只有一句:文字有没有变成画面的一部分。**

  类型       存在形式           能不能关     改文字要不要重编码
  -----------------------------------------------------------------
  硬字幕     烧进画面像素里     不能         要
  软字幕     独立的字幕流       能           不用

**硬字幕的优点是「谁都能看见」——发到任何平台都一样。**
**缺点是改不了,而且编码一次就定型了。**

**软字幕的优点是「随时能改、随时能关」,播放器自己渲染。**
**缺点是有些平台不认,尤其是短视频平台,上传之后字幕就没了。**

**② 两种字幕文件格式**

  格式        扩展名     能写什么
  -----------------------------------------------------------------
  SRT         .srt       序号、起止时间、一行文字
  ASS/SSA     .ass       SRT 的全部,加上字体、颜色、位置、动效

**SRT 是最通用的:纯文本,几乎什么播放器都认,但样式全靠播放器自己定。**
**ASS 是「带样式的字幕」,卡拉 OK、双语、花字都靠它。**

**SRT 的一条长这样:三行一组,序号、时间、文字,中间空一行。**
**时间格式是 `时:分:秒,毫秒`,注意毫秒前面是逗号不是点。**

**③ 软字幕的编码格式**

**字幕流也是要编码的,容器不同能装的格式不同:**

  编码         装在哪        特点
  ---------------------------------------------------
  mov_text     mp4 / mov     最通用,但不支持样式
  subrip       mkv           就是 SRT,纯文本
  ass          mkv           样式全保留
  webvtt       webm          网页播放器原生支持

**mp4 装字幕只能用 `mov_text`,这是容器的限制,不是编码器的限制。**
**想保留 ASS 的花样,得用 mkv。**

**④ 怎么选**

**一句话:要给平台看,做硬字幕;要给自己留后路,做软字幕。**
**给客户交付时常常两份都给——带硬字幕的成片 + 带软字幕的工程文件。**

**还有第三种做法:先做软字幕,最后一步再烧成硬字幕。**
**这样改字只改字幕文件,不用从头编一遍。**

前三行是本节最重要的现实:这台机器上的 PyAV 没有 subtitles、ass、drawtext。 所以「烧字幕」在这里跑不了,下一节会用一个替代办法。

滤镜有没有,取决于 ffmpeg 编译时链了哪些外部库—— libass 管字幕、freetype 管画字,都链上了才有这几个滤镜。

后面几行打印的是「概念」不是「能力」:mov_text、subrip 这些是 字幕编码的名字,能不能用取决于容器,跟滤镜没关系。

30 秒实验

  1. 查环境:在你自己机器上跑 ffmpeg -filters | grep subtitles,看有没有。
  2. 写一条 SRT:用记事本写三行,存成 x.srt,注意毫秒前是逗号。
  3. 换个容器:同一份字幕封装成 mp4 与 mkv,比一比播放器里能不能关。

常见误区

  1. 把 SRT 时间的毫秒写成点。必须是逗号,写点整条不显示。
  2. 两条字幕之间不空行。会被当成一条,或者干脆全不显示。
  3. 以为 mp4 能装 ASS。只能装 mov_text,样式全丢。
自测三问(点开对答案) 1. 硬字幕与软字幕最本质的区别是什么? —— 文字有没有变成画面像素的一部分。 2. mp4 里能装哪种字幕编码? —— `mov_text`,不支持样式。 3. SRT 的时间格式里毫秒前是什么符号? —— 逗号,`00:00:01,000`。

相关笔记