🎬 第三章 FFmpeg三大常用工具及应用选项详解
📄 一句话概括

字幕分两类:文本的能转格式、能改样式,图形的一个字都改不了。 国内素材最常踩的坑是字符编码,而且是静默失败。

📝 本节和前面几节的关系

3.5 和 3.6 讲音视频的编码参数,这一节讲字幕这个特殊的一路。 3.8 讲改变处理范围的选项。

一、字幕分成截然不同的两类

这个分类决定了你能对字幕做什么操作:

类别 本质 代表格式


文本字幕 文字+时间轴 srt/ass/webvtt 图形字幕 一张张图片+时间轴 dvdsub/pgssub

文本字幕能转格式、能改字体、能翻译;图形字幕只能原样搬。 因为它的内容是「像素」,不是一个字——改一个字就等于重做一张图。

所以处理图形字幕只有两条路:原样 copy,或者烧进画面(变成硬字幕)。 两条路都不能改内容,只能整体的搬或者整体的烧。

二、四个相关选项

字幕选项很少,每一个的用途都很明确:

选项 作用 用错会怎样


-c:s 字幕编码器 选错报 could not find tag -sn 不要字幕 — -map 0:s:0 只取第一条字幕 漏了字幕一条都进不去 -sub_charenc 源文件的字符编码 乱码,且静默

第四行是国内素材最常踩的坑:老的 srt 常是 GBK 编码。 不写 -sub_charenc gbk 就会把中文读成乱码,而且它不报错—— 你以为成功了,打开一看全是问号。

判断方法很简单:转完先 head -5 看一眼输出字幕。 十秒能确认的事,比事后重新转一遍便宜。

三、内嵌与外挂

两种做法各有代价,取决于你后面还要不要改字幕:

做法 命令要点 优缺点


内嵌 -c:s mov_text(mp4)或 copy(mkv) 一体,但改不了 外挂 不写 -map 0:s,单独留 srt 能改,但要带两个文件

第一行有个容器限制:mp4 只支持 mov_text 这一种文本字幕。 ass 的特效字幕塞不进 mp4,只能烧进画面——那就变成硬字幕了。

硬字幕一旦烧进去就再也关不掉、换不了语言。 所以「要不要烧」这件事必须一次决定,它是所有字幕处理里最不可逆的一步。

四、动手 Demo:六种格式,两条路

手写一个三段式的 SRT 文件,用库把它当容器读回来。 目的是确认「SRT 本身就是一个容器格式」,以及看清其中的包结构。

import os
import av

# 手写 SRT:三段,每段是 序号/时间轴/文字
txt = (u"1\n00:00:00,000 --> 00:00:00,700\n第一段字幕\n\n"
       u"2\n00:00:00,700 --> 00:00:01,400\n第二段字幕\n\n"
       u"3\n00:00:01,400 --> 00:00:02,000\n第三段字幕\n\n")
with open(u"sub.srt", u"w", encoding=u"utf-8") as f:
    f.write(txt)
print(u"SRT 文件 %d 字节" % os.path.getsize(u"sub.srt"))

# 当容器读
c = av.open(u"sub.srt")
print(u"容器 %s  流 %d 条  类型 %s"
      % (c.format.name, len(c.streams), c.streams[0].type))

n = 0
first = None
for pk in c.demux(subtitles=0):
    if not pk.size:
        continue
    n += 1
    if first is None:
        first = pk.pts
c.close()
print(u"包 %d 个,首段 pts %s" % (n, first))

实跑输出:

--------------------------------------------------------------------
3.7 字幕选项——六种格式,两条路
--------------------------------------------------------------------

**① 字幕分成截然不同的两类**

  类别         本质                   代表格式
  ----------------------------------------------------------
  文本字幕     文字+时间轴           srt / ass / webvtt
  图形字幕     一张张图片+时间轴     dvdsub / pgssub

**这两类的处理方式完全不同:文本字幕能转格式、能改字体;**
**图形字幕只能原样搬,改一个字都要重新做图。**

**② 相关的选项只有四个**

  选项             作用                     用错会怎样
  -------------------------------------------------------------------------
  -c:s             字幕编码器               选错就报 could not find tag
  -sn              不要字幕                 —
  -map 0:s:0       只取第一条字幕           漏了就一条都没进输出
  -sub_charenc     字幕源文件的字符编码     乱码,尤其 GBK 素材

**最后一行是国内素材最常踩的坑:老的 srt 常是 GBK 编码,**
**不写 `-sub_charenc gbk` 就会把中文读成乱码,而且是静默的——不报错。**

**③ 手写一个 SRT,读回来看看里面到底是什么**

  项               值
  ---------------------------------------
  文件字节         159
  流条数           1(类型 subtitle)
  包数(段数)     3
  首段 pts         0

**一个 SRT 就是一个「零散的文本文件」,ffmpeg 把它当容器读,每一段是一条包。**
**这就是为什么 srt 既能当输入也能当输出——它本身就是一个容器格式。**

**④ 三种文本字幕格式的体积对比**

  格式       特点                           体积趋势
  ---------------------------------------------------------
  srt        最简,只有序号+时间+文字     最小
  ass        带样式、定位、特效             大几倍
  webvtt     网页用,语法接近 srt           和 srt 接近

**ass 大出来的部分几乎全是样式表——字体、颜色、位置、动画。**
**所以「只想要字幕内容」的话,srt 转 ass 是纯浪费;**
**反过来,要控制字幕位置和字体,srt 根本表达不了,必须用 ass。**

**⑤ 内嵌字幕 vs 外挂字幕**

  做法     命令要点                               优缺点
  ------------------------------------------------------------------------
  内嵌     -c:s mov_text(mp4)或 copy(mkv)     一体,但改不了
  外挂     不写 -map 0:s,单独留 srt              能改,但要带两个文件

**mp4 里的字幕只支持 mov_text,这是容器限制;mkv 什么都装得下。**
**所以要把 ass 特效字幕塞进 mp4,只能先烧进画面——那就变成「硬字幕」了,**
**再也没法关掉,也没法换语言。**

一个 SRT 就是一个纯文本文件,可 ffmpeg 把它当容器读—— 所以 srt 既能当输入也能当输出,它本身就是一个容器格式。

三行文字被切成了三个包,每个包对应一个时间区间。 这个结构跟视频的包一模一样,只是内容从「压缩的画面」变成了「一行字」。

首段 pts 是 0,因为第一段从 0 秒开始。 如果第一段从第 3 秒开始,pts 就不会是 0——时间戳反映的是内容位置,不是文件顺序。

注意 demux 用的是 subtitles=0 而不是 subtitle=0。 PyAV 的复数形式是个容易踩的小坑,写错了直接抛 AttributeError。

30 秒实验

  1. 转格式:ffmpeg -i 字幕.srt 字幕.ass,比两边的体积与内容。
  2. 解编码:ffmpeg -sub_charenc gbk -i 老字幕.srt 出.srt,中文不再乱码。
  3. 内嵌:ffmpeg -i 片子.mp4 -i 字幕.srt -c:s mov_text -map 0 -map 1 出.mp4,注意两次 -map。

常见误区

  1. 忘写 -sub_charenc gbk。GBK 的 srt 会被读成乱码,而且不报错,静默失败。
  2. 把 ass 塞进 mp4。mp4 只支持 mov_text,ass 的特效塞不进去,只能烧画面。
  3. 以为硬字幕还能改。烧进画面就变成像素了,关不掉也换不了语言,是不可逆的一步。
自测三问(点开对答案) 1. 为什么 SRT 既能当输入也能当输出? —— 因为它本身就是一个容器格式。纯文本、结构简单,但每一段有独立的时间轴,ffmpeg 能当容器读写。 2. 图形字幕为什么改不了一个字? —— 因为它的内容是像素图片,不是一个字。改字等于重做一张图,只能整体替换或整体烧进画面。 3. GBK 编码的 srt 转完是乱码,怎么修? —— 加 `-sub_charenc gbk` 告诉 ffmpeg 源文件的编码。它默认按 UTF-8 读,读错也不报错。

相关笔记