给视频配字幕这件事,手动敲时间轴的时代早该结束了。这篇攻略把"一条视频 → 一份双语 SRT 字幕文件"的完整流程拆成六个步骤,全部可以脚本化,适合课程录制、团队分享、外刊精读这类需要长期产字幕的场景。
整体思路
字幕生成的本质是三件事:拿到带时间戳的转写文本、把文本按字幕节奏切块、套上 SRT 格式。双语版本在此基础上加一道翻译。流程图:
视频文件
→ ① 抽音频(ffmpeg)
→ ② 切段降噪(VAD 预处理)
→ ③ 分片转写(拿逐句时间戳)
→ ④ 字幕切块(按长度与停顿重切)
→ ⑤ 翻译(机器翻 + 抽查)
→ ⑥ 生成双语 SRT
第一步:抽音频,别直接喂视频
转写引擎吃的是音频,先用 ffmpeg 抽成 16kHz 单声道,体积小、上传快:
ffmpeg -i input.mp4 -vn -ac 1 -ar 16000 audio.wav
第二步:VAD 预处理
长视频里的静音段(片头片尾、停顿)没有转写价值还会拉长处理时间。用语音活动检测切出有效段再送转写,处理时长和音频体积都能压下来一大截。
第三步:分片转写,拿逐句时间戳
长音频别整段传。按 VAD 段聚合成 5~10 分钟的分片,异步提交转写。我用的是算邻坊的随心语音转文字服务,返回逐句时间戳——这是后面字幕对齐的命脉,没有句级时间戳的字幕生成都要靠猜。
segments = vad_merge(audio, max_len=600) # 聚合分片
results = [submit_and_wait(seg) for seg in segments]
第四步:字幕切块,最影响观感的一步
转写句不一定适合直接当字幕:一句 40 字的长句在屏幕上要占三行。切块规则两条上限:每条不超过 20 字(中文观感舒适线)、两条之间空一行的时间超过 0.4 秒(换行点尽量落在自然停顿上)。
def to_subtitle_lines(sentences, max_len=20, gap=0.4):
lines = []
buf = ""
for i, (text, start, end) in enumerate(sentences):
nxt = sentences[i+1] if i+1 < len(sentences) else None
if len(buf) + len(text) <= max_len:
buf += text
else:
lines.append(buf); buf = text
if nxt and nxt.start - end > gap and buf:
lines.append(buf); buf = ""
return lines
第五步:翻译与抽查
机翻打底,抽查关键词。字幕翻译的特殊性是长度约束:中文 18 字的句子,英文可能超一行,翻译后要再过一遍长度切块。术语和人名建议先建一个统一译名表喂给翻译接口,避免同一人名前后两种拼法。
第六步:生成双语 SRT
SRT 格式就四要素:序号、时间区间、文本、空行。双语版把译文放第二行:
def write_srt(lines, path):
with open(path, "w") as f:
for i, ln in enumerate(lines, 1):
f.write(f"{i}\n{fmt_time(ln.start)} --> {fmt_time(ln.end)}\n")
f.write(f"{ln.zh}\n{ln.en}\n\n")
常见坑
- 时间戳漂移:分片转写的时间戳是片内的,合并时要加回片偏移量,漏加会整体后移;
- 换行切在词中间:英文按空格切、中文按字切,别用一个逻辑;
- 烧录 vs 外挂:SRT 外挂适合平台分发(B 站可直接传),烧录进画面用 ffmpeg subtitle 滤镜,一条命令的事。
结语
六个步骤跑通后,一小时视频的双语字幕,人工投入只剩第五步的抽查——十来分钟。这套流程的价值不在单次省了多少时间,而在于它让"每个视频都配字幕"从负担变成默认动作。