Skip to content

这个工作流非常普遍,通常分为三步:

  1. 从视频中提取高质量的音频
  2. 使用带有时间戳功能的语音转文字(ASR)工具处理该音频文件,生成字幕文件(如 .srt.vtt
  3. 将字幕文件与原始视频合并

ffmpeg 在第一步和第三步中扮演着至关重要的角色,第二步可以考虑使用开源的语音转文字工具,如 OpenAI Whisper

核心概念:时间戳是如何工作的

首先要明确一点:ffmpeg 在提取音频时,默认就会保留原始的时间戳信息。视频文件(如 MKV)中的每一个音频帧和视频帧都有一个精确的播放时间点(Presentation Timestamp, PTS)。当 ffmpeg 将音频流提取并保存为一个独立的音频文件(如 WAV 或 M4A)时,这些音频帧的相对时间关系会被完整地保留下来。后续的字幕生成工具在分析这个音频文件时,会识别出语音的起止时刻,并根据音频文件本身的时间轴来生成带有 开始时间 --> 结束时间 格式的字幕。

因此,你需要做的就是正确地提取出音频,时间戳的问题 ffmpeg 会自动处理好。

步骤1.1:分析你的 MKV 文件(推荐)

在提取之前,最好先查看一下 .mkv 文件里包含了哪些流(视频、音频、字幕等),特别是当视频可能包含多条音轨(如不同语言的配音、评论音轨)时。

打开终端或命令提示符,运行以下命令:

bash
ffmpeg -i "你的视频文件.mkv"

注意:如果文件名包含空格,请用引号括起来。

ffmpeg 会打印出文件的详细信息。在输出中找到类似下面的部分:

Input #0, matroska,webm, from '你的视频文件.mkv':
  Metadata:
    ...
  Duration: 01:23:45.67, start: 0.000000, bitrate: 4531 kb/s
  Stream #0:0(eng): Video: h264 (High), yuv420p(progressive), 1920x1080, ...
  Stream #0:1(jpn): Audio: opus, 48000 Hz, stereo, fltp (default)
  Stream #0:2(chi): Audio: aac, 48000 Hz, stereo, fltp
  Stream #0:3(eng): Subtitle: ass

解读关键信息

  • Stream #0:1(jpn): 这是第一条音轨,语言是日语(jpn),编码是 Opus。它是 (default) 默认音轨。
  • Stream #0:2(chi): 这是第二条音轨,语言是中文(chi),编码是 AAC。

假设你想要提取中文音轨,那么它的标识符就是 0:2。如果只有一个音轨,通常是 0:1

步骤1.2:提取音频,提取为 WAV 格式(强烈推荐用于字幕生成)

WAV 是无损的未压缩音频格式,能为后续的语音识别提供最高的音频质量,从而生成更准确的字幕。

命令格式

bash
ffmpeg -i "你的视频文件.mkv" -map 0:a:N -c:a pcm_s16le -ar 16000 "输出音频.wav"

命令详解

  • -i "你的视频文件.mkv": 指定输入文件。
  • -map 0:a:N: 这是选择音轨的关键。
    • 0: 代表第一个输入文件。
    • a: 代表音频(audio)。
    • N: 代表音轨的索引(从0开始)。根据上一步分析的结果,如果要提取 Stream #0:2,这里的 N 就是 2。如果只有一个音轨 Stream #0:1N 就是 1。如果不确定,可以省略 -map 参数,ffmpeg 会自动选择它认为是“最佳”的音轨(通常是默认音轨)。
    • 例如,要提取中文音轨 Stream #0:2,就用 -map 0:a:1注意,ffmpeg 的流索引是从0开始算的,所以 #0:2 对应 -map 0:a:1 是不对的,应该是 #0:2 对应 -map 0:2 或者 -map 0:a:1 如果它是第二条音轨。为了避免混淆,使用 0:2 这种精确指定更安全,或者使用 0:a:1 表示第二条音频流)。最清晰的方式是指定流的绝对索引,如 -map 0:2
    • 为了简单起见,我们假设你要提取的是第二条音轨,即索引为1的音频流,使用 -map 0:a:1
  • -c:a pcm_s16le: 指定音频编码器。pcm_s16le 是生成标准 WAV 文件所用的编码,代表16位深度的无压缩音频。
  • -ar 16000: (可选,但推荐)设置音频采样率。许多语音识别模型(特别是开源模型)在 16000 Hz (16kHz) 的采样率下表现最佳。这也可以减小文件大小。如果你的源音频采样率已经是 16000Hz,ffmpeg 不会做任何事。
  • "输出音频.wav": 指定输出文件名。

步骤2:生成字幕

现在,你可以使用这个 输出音频.wav 文件和支持时间戳的语音识别工具来生成字幕。以目前非常流行的开源工具 OpenAI Whisper 为例:

bash
# 安装 whisper (如果还没安装)
# pip install -U openai-whisper

# 使用 whisper 生成字幕
whisper "输出音频.wav" --model medium --language English

运行完毕后,Whisper 会在同目录下生成 .txt, .vtt, 和 .srt 格式的字幕文件,这些文件里就包含了精确的时间戳。

步骤3:合并字幕与视频

现在手上有了原始视频文件(比如 你的视频文件.mkv)和生成的字幕文件(比如 输出字幕.srt),接下来,将字幕与视频合并,ffmpeg 同样是完成这个任务的完美工具。

合并字幕主要有两种方式:

  1. 软字幕 (Softsubs):将字幕文件作为一条独立的轨道封装进视频容器(如 MKV)中。

    • 优点:速度极快(因为不重新编码视频),不损失任何视频质量,观众可以在播放器中自由选择开启、关闭或切换字幕。这是最推荐的方式。
    • 缺点:需要播放器支持。不过,现代主流播放器(如 VLC, PotPlayer, MPV, Plex)都完美支持。
  2. 硬字幕 (Hardsubs):将字幕“烧录”或“压制”进视频画面中,使其成为视频图像的一部分。

    • 优点:在任何设备、任何播放器上都能显示,因为字幕已经和画面融为一体。
    • 缺点:过程很慢(需要重新编码整个视频),会造成视频质量损失,且字幕是永久性的,无法关闭。

除非你有特殊需求(例如,要在不支持字幕轨道的旧设备上播放,或上传到某些强制转码的社交平台),否则永远首选软字幕。这边以软字幕为例,这种方法本质上是把视频、音频和字幕这三个独立的“素材”打包到一个新的 MKV 文件里。

下面的命令会将原始视频的所有流(视频、所有音轨)和新的字幕流一起复制到新文件中。

bash
ffmpeg -i "你的视频文件.mkv" -i "输出字幕.srt" -c copy -map 0 -map 1 "带字幕的视频.mkv"

命令详解

  • -i "你的视频文件.mkv": 指定第一个输入文件(你的视频)。
  • -i "输出字幕.srt": 指定第二个输入文件(你的字幕)。
  • -c copy (或 -codec copy): 核心参数。告诉 ffmpeg 直接“复制”所有流,不进行任何重新编码。这就是为什么它速度飞快且无损质量。
  • -map 0: 映射第一个输入文件(你的视频文件.mkv)的所有流到输出文件。这能确保原始视频里的多条音轨等信息不会丢失。
  • -map 1: 映射第二个输入文件(输出字幕.srt)的所有流到输出文件。
  • "带字幕的视频.mkv": 输出文件名。MKV 是支持多轨道字幕的绝佳容器格式。

为了让字幕在播放器里显示得更友好(例如,标记为“中文”),我们可以添加一些元数据。下面的命令会为字幕轨道添加语言和标题信息。

bash
ffmpeg -i "你的视频文件.mkv" -i "输出字幕.srt" -c copy -map 0 -map 1 -c:s mov_text -metadata:s:s:0 language=eng -metadata:s:s:0 title="English (AI generated)" "带字幕的视频.mkv"

新增参数详解

  • -c:s mov_text: 将输入的 SRT 格式字幕转换为 mov_text 格式,它在 MKV 容器中有更好的兼容性。虽然 ffmpeg 通常会自动处理,但明确指定更稳妥。
  • -metadata:s:s:0 language=eng:
    • -metadata: 设置元数据。
    • :s:s:0: s:s 指的是字幕流(subtitle stream),:0 指的是输出文件中的第一个字幕流(因为我们只添加了一个)。
    • language=eng: 将该字幕轨道的语言标记为“英文”(使用 ISO 639-2 标准代码)。
  • -metadata:s:s:0 title="English (AI generated)": 为这个字幕轨道设置一个标题,在播放器的字幕选择菜单中会显示这个标题。

现在,用 VLC 或其他播放器打开 带字幕的视频.mkv,你就可以在字幕菜单里看到一个名为“English (AI generated)”的轨道了!