本地视频中文字幕生产全流程:Whisper+ffmpeg实战指南
2026/9/22 14:57:15 网站建设 项目流程

这次我们不看模型评测,也不搭 ComfyUI 工作流,而是走一条更完整的本地视频处理链路:把一段日系视觉系乐队 girugamesh 的《イシュタル》Live 现场影像,加工成带中文字幕的本地视频。标题里的“中文字幕”不是现成的字幕文件,而是我们要自己生产出来的结果。

整条链路其实可以拆成四步:音频分离与预处理、日语语音识别、字幕翻译、字幕烧录与封装。如果素材多,还可以串成批量任务。这篇文章会把这四步全部展开,给出可复制的命令、Python 脚本、参数说明和排错思路。读完你可以自己跑通一条“视频 → 中文字幕视频”的生产流程,不只是针对这一首 Live,也可以套用到其他日语视频、翻唱、访谈、纪录片片段上。

先说结论:这条链路完全可以在本地完成。核心工具是 ffmpeg、Whisper/faster-whisper、可选的 Demucs 人声分离,以及一个翻译用的大模型接口或本地模型。CPU 能跑,有 NVIDIA 显卡会明显更快。显存需求取决于你选哪个尺寸的 Whisper 模型,从 2GB 到 8GB 都有对应方案。下面按步骤来说。

1. 核心能力速览

能力项说明
项目类型本地视频字幕生产/翻译自动化流水线
输入素材带原声的 Live 视频、DVD/BDRip 片段、本地录屏等
输出结果中文字幕文件(SRT/ASS)、硬字幕压制版视频、软字幕封装版视频
核心模型Whisper / faster-whisper,可选 Demucs、UVR5 做音轨分离
翻译方案大模型 API 或本地部署模型,需要根据自己的接口地址调整
是否支持 CPU支持,Whisper 可以 CPU 推理,只是速度慢
显卡需求有 NVIDIA GPU 更好;模型尺寸不同,显存需求不同
是否支持批量支持,可以做成目录循环或任务队列
是否提供 APIWhisper 无自带 Web API,但可用 Python 脚本直接调用;翻译部分可对接远程 API
启动方式Python 脚本 + ffmpeg 命令行
适合场景日语视频中文化、音乐现场字幕制作、访谈/纪录片粗翻、批量字幕生产

需要注意,这里的“实测”更多是指工具链本身的通用表现。不同机器、不同 Whisper 模型、不同源视频质量,最终识别准确率和显存占用都会不一样。最稳妥的做法是先在单条视频上跑通,再决定模型档位和批量策略。

2. 适用场景与使用边界

这条流水线最适合以下三类人:

  • 字幕组或视频创作者:需要快速给现场 Live、MV、访谈视频生成一版中文粗字幕,再人工精修。
  • 本地党:不想把视频传到在线翻译平台,希望所有处理都在自己机器上完成。
  • 批量需求者:手上有几十段日语视频,想先全部过一遍自动识别和翻译,再筛选值得精修的内容。

但也要说清楚边界。现场音乐视频的版权归属通常是乐队、唱片公司或视频制作方。字幕制作和个人学习、研究用途可以理解,但公开传播、二次发布、商用,都应当先获得权利人授权。尤其是涉及歌手肖像、歌曲版权、现场录音版权时,风险会叠加。

另外,LLM 翻译歌词并不完美。视觉系歌词通常有隐喻、英文混排、语气词、MC 口播,机器翻译只能给出初稿,不能直接当最终成品发布。你需要有人工审校环节,尤其是公开字幕。

3. 环境准备与前置条件

建议在一台能联网的 Linux 或 Windows 机器上操作。macOS 也可以,但部分 ffmpeg 滤镜参数和字体路径要微调。

3.1 基础软件

  • Python 3.10 或更高版本
  • ffmpeg,并且已经配置到系统 PATH
  • Git,用于克隆部分工具仓库
  • 可选:NVIDIA 显卡驱动 + CUDA,提升 Whisper 推理速度

检查 ffmpeg 是否可用:

ffmpeg -version

如果没有 ffmpeg,Ubuntu/Debian 可以这样装:

sudo apt update sudo apt install ffmpeg

Windows 用户建议直接下载 ffmpeg 的 Windows 构建包,解压后把bin目录加入 PATH。

3.2 Python 虚拟环境

建议单独建一个虚拟环境,避免依赖冲突:

python -m venv venv source venv/bin/activate pip install --upgrade pip

Windows 下的激活命令是:

venv\Scripts\activate

3.3 磁盘空间

Whisper 模型文件大小参考:

模型磁盘占用约说明
small约 500MB速度快,准确率一般
medium约 1.5GB速度和准确率比较均衡
large-v3约 3GB准确率最高,速度最慢

原视频、音频中间文件、最终压制视频也要预留空间。建议一整条项目目录预留 20GB 以上。

3.4 项目目录规划

建议所有物料分目录管理,后面批量处理会省很多事:

project/ ├── videos/ # 原始视频 ├── audio/ # 提取出来的音频 ├── vocals/ # 分离后的人声 ├── asr/ # Whisper 识别结果 ├── translated/ # 翻译后的字幕 ├── final/ # 最终输出视频 └── scripts/ # 处理脚本

4. 第一步:从 Live 视频中提取音频

原始视频可能包含多音轨,建议先看一看到底有哪些流,避免提取到伴音轨或者评论音轨。

ffprobe -v error -show_entries stream=index,codec_type,codec_name,language -of default=noprint_wrappers=1 videos/input.mp4

确认有日语原声音轨后,提取为 WAV:

ffmpeg -i videos/input.mp4 -map 0:a:0 -vn -acodec pcm_s16le -ar 44100 -ac 2 audio/original.wav

这里-map 0:a:0选择第一个音轨。如果多个音轨,需要根据ffprobe的输出调整。44100 采样率、双声道对语音识别足够。

现场 Live 通常有观众欢呼、鼓点、贝斯、吉他混杂。直接丢给 Whisper 也能识别,但错误率会偏高。如果发现识别结果大量丢词或出现同音字错误,可以做人声分离。

5. 第二步:人声分离(可选,但建议先试一次)

Demucs 是目前比较通用的音源分离工具,可以分离出人声、鼓、贝斯和其他伴奏。现场版特别适合做人声分离,因为观众噪声和乐器混响会干扰语音识别。

安装 Demo:

pip install demucs

执行人声分离:

demucs --two-stems=vocals audio/original.wav -o separates

输出目录结构大致如下:

separates/ └── htdemucs/ └── original/ ├── vocals.wav └── no_vocals.wav

这里vocals.wav就是我们后续要喂给 Whisper 的文件。如果对分离效果不满意,可以用更高一档的模型,但耗时更长。另一个选择是 UVR5 图形界面,对 Windows 用户更友好,但我个人更推荐 Demucs,命令行和批量处理都方便。

值得说明的是,并不是所有视频都需要分离。如果源视频是录音棚 MV,人声本来就很干净,直接识别反而更快。现场 Live 则建议至少试一次分离前和分离后的识别效果,再决定是否保留这个步骤。

6. 第三步:日语语音识别

6.1 安装 faster-whisper

我建议优先用 faster-whisper,而不是原版 OpenAI Whisper。faster-whisper 基于 CTranslate2,推理速度在 CPU 和 GPU 上都有明显优势,显存占用也更低。

pip install faster-whisper

6.2 基础识别脚本

保存为scripts/transcribe.py

import sys from faster_whisper import WhisperModel audio_path = sys.argv[1] model_size = sys.argv[2] if len(sys.argv) > 2 else "medium" language = sys.argv[3] if len(sys.argv) > 3 else "ja" model = WhisperModel(model_size, device="auto", compute_type="int8") segments, info = model.transcribe( audio_path, language=language, beam_size=5, vad_filter=True, vad_parameters=dict(min_silence_duration_ms=500), initial_prompt="girugamesh, イシュタル, ライブ", ) with open("asr/output.srt", "w", encoding="utf-8") as f: idx = 1 for segment in segments: start = segment.start end = segment.end text = segment.text.strip() if not text: continue start_ts = format_timestamp(start) end_ts = format_timestamp(end) f.write(f"{idx}\n") f.write(f"{start_ts} --> {end_ts}\n") f.write(f"{text}\n\n") idx += 1

再加一个时间戳格式化函数:

def format_timestamp(seconds: float): ms = int((seconds % 1) * 1000) total_seconds = int(seconds) h = total_seconds // 3600 m = (total_seconds % 3600) // 60 s = total_seconds % 60 return f"{h:02d}:{m:02d}:{s:02d},{ms:03d}"

运行:

python scripts/transcribe.py vocals/vocals.wav medium ja

如果不想人声分离,就把路径换成audio/original.wav

6.3 Whisper 模型档位选择

模型参数量速度识别准确率建议场景
small244M一般CPU 机器、快速粗筛
medium769M中等较好6G 以上显存或 CPU 可接受等待
large-v31550M最好12G 显存、追求高质量粗字幕

现场 Live 建议至少用 medium。large-v3 对嘈杂环境、日语音调、语气词的处理更稳,但耗时明显增加。如果是 NVIDIA 显卡,且显存充足,可以把compute_type改成float16获得更快的速度。显存不够时,int8是最稳妥的方案。

6.4 识别结果分析

识别完成后,打开asr/output.srt,重点检查:

  • 是否有整段歌词被跳过
  • 是否有明显的日语同音字错误
  • MC 口播是否被误识别成歌词
  • 时间轴是否偏移,前后句是否错位

日语中“ウ”“ヴ”“ン”等音节在快速演唱时很容易被漏,这是正常现象。如果漏得太多,可以换更大模型,或者回到 Demucs 重新分离,再不行就在initial_prompt中补充歌名、乐队名和主题词。

7. 第四步:字幕翻译与中文断句

7.1 SRT 解析

先写一个脚本,把 SRT 解析成结构化的 JSON,方便后续调用翻译 API。

保存为scripts/parse_srt.py

import re import json import sys def parse_srt(path): with open(path, "r", encoding="utf-8") as f: content = f.read() blocks = re.split(r"\n\s*\n", content.strip()) items = [] for block in blocks: lines = block.strip().split("\n") if len(lines) < 3: continue index = int(lines[0]) time_line = lines[1] text = " ".join(lines[2:]).strip() items.append({ "index": index, "time": time_line, "text": text }) return items if __name__ == "__main__": items = parse_srt(sys.argv[1]) print(json.dumps(items, ensure_ascii=False, indent=2))

运行:

python scripts/parse_srt.py asr/output.srt > asr/output.json

7.2 调用大模型翻译

这里给出一个通用模板,翻译接口需要根据实际使用的服务进行调整。假设你可以访问一个兼容 OpenAI 格式的翻译 API:

保存为scripts/translate.py

import json import sys import time import requests from concurrent.futures import ThreadPoolExecutor, as_completed API_URL = "https://your-api-endpoint/v1/chat/completions" API_KEY = "your-api-key" def translate_segment(item): text = item["text"] prompt = ( "把下面的日语歌词/口播翻译成简体中文。保留原意,不要添加解释。" "如果包含英文,保留英文并给出中文提示。" "只输出译文,不要输出额外内容。\n\n" f"{text}" ) payload = { "model": "your-model-name", "messages": [ {"role": "system", "content": "你是专业日语歌词翻译,熟悉视觉系乐队表达方式。"}, {"role": "user", "content": prompt} ], "temperature": 0.3 } headers = { "Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json" } try: resp = requests.post(API_URL, json=payload, headers=headers, timeout=120) resp.raise_for_status() data = resp.json() translated = data["choices"][0]["message"]["content"].strip() except Exception as e: translated = f"[翻译失败] {text}" print(f"WARN segment {item['index']}: {e}", file=sys.stderr) return {**item, "translated": translated} def main(): srt_path = sys.argv[1] out_path = sys.argv[2] if len(sys.argv) > 2 else "translated/output.json" with open(srt_path, "r", encoding="utf-8") as f: items = json.load(f) with ThreadPoolExecutor(max_workers=4) as pool: futures = [pool.submit(translate_segment, item) for item in items] for future in as_completed(futures): # print progress pass results = [] for future in futures: results.append(future.result()) results.sort(key=lambda x: x["index"]) with open(out_path, "w", encoding="utf-8") as f: json.dump(results, f, ensure_ascii=False, indent=2) if __name__ == "__main__": main()

需要说明的是,这里的API_URLAPI_KEYmodel都只是占位符。实际接入时以你选择的翻译服务文档为准。如果不想连外部 API,也可以接入本地部署的 Qwen、DeepSeek 等开源模型,只要接口格式兼容,逻辑基本一致。

7.3 翻译结果转回 SRT/ASS

翻译并人工确认后,需要把 JSON 再转成 SRT 或 ASS。ASS 的好处是支持更精细的样式控制,比如不同说话人、不同颜色、歌词逐句显示。

保存为scripts/build_ass.py

import json import sys def format_timestamp(seconds): # 这里直接解析 SRT 时间,或者从 JSON 中重新计算 pass def build_ass(data_path, out_path): with open(data_path, "r", encoding="utf-8") as f: items = json.load(f) header = """[Script Info] ScriptType: v4.00+ PlayResX: 1920 PlayResY: 1080 [V4+ Styles] Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding Style: Default,Noto Sans CJK SC,62,&H00FFFFFF,&H000000FF,&H00000000,&H80000000,-1,0,0,0,100,100,0,0,1,2,0,2,80,80,40,1 [Events] Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text """ lines = [header] for item in items: time = item["time"] start = time.split(" --> ")[0] end = time.split(" --> ")[1] text = item.get("translated", item["text"]) text = text.replace("\n", "\\N") lines.append(f"Dialogue: 0,{start},{end},Default,,0,0,0,,{text}") with open(out_path, "w", encoding="utf-8") as f: f.write("\n".join(lines)) if __name__ == "__main__": build_ass(sys.argv[1], sys.argv[2])

转回 SRT 也类似,只是格式更简单。字幕文件生成后,建议先用播放器预览一遍,确认中文断句是否自然。

7.4 长句断行

视觉系歌词通常节奏密、句子长,尤其是 Live 中一句 MC 可能持续 10 秒以上。直接把整句翻译放在一条字幕里,屏幕会被塞满。建议按每行最多 15 到 20 个汉字来断行。

可以在 Python 脚本里按标点或字符数切开:

def split_line(text, max_chars=16): if len(text) <= max_chars: return text # 优先在标点处切 for sep in ["。", "、", "!", "?", " ", ",", "."]: if sep in text: parts = text.split(sep, 1) # 简单策略,实际需要更细致处理 return parts[0] + sep + "\\N" + parts[1] return text[:max_chars] + "\\N" + text[max_chars:]

注意,这只是一个简化示例。真实断行需要结合时间轴长度和中文字幕阅读速度决定,不建议完全自动。

8. 第五步:字幕烧录与视频封装

8.1 硬字幕烧录

“硬字幕”是把字幕直接画进视频画面,任何播放器都能看到,适合需要分发最终成品文件的场景。

使用 ffmpeg 烧录 ASS 字幕:

ffmpeg -i videos/input.mp4 -vf "ass=translated/zh.ass:fontsdir=fonts" -c:v libx264 -preset medium -crf 18 -c:a copy final/girugamesh_ishitar_zh.mp4

这里的fontsdir=fonts指向你存放中文字体文件的目录,目的是让 ffmpeg 能找到合适字体进行渲染。常见中文字体如“Noto Sans CJK SC”“微软雅黑”都能用于字幕渲染。

8.2 软字幕封装

如果不想改变原视频画面,可以把字幕封装成独立轨道:

ffmpeg -i videos/input.mp4 -i translated/zh.srt -map 0:v -map 0:a -map 1:0 -c copy -c:s srt -metadata:s:s:0 language=chi final/girugamesh_ishitar_zh.mkv

软字幕的好处是视频画质不被二次压缩,字幕随时可以关闭或更换。很多播放器都会自动识别 MKV 内嵌字幕。

8.3 检查字幕效果

建议用 PotPlayer、VLC、mpv 等播放器打开最终文件,重点检查:

  • 字幕字体是否清晰,有无乱码
  • 中文字幕与日语演唱的节奏是否对齐
  • 是否有超长字幕占满画面
  • 硬字幕是否发生过画面拉伸或模糊

字体问题是最常遇到的坑。Windows 下 ffmpeg 的 ASS 滤镜可能找不到中文字体,导致方框或乱码。解决办法是在fontsdir中显式指定字体文件,并且 ASS 样式中的Fontname必须与字体文件内部名称匹配。

9. 批量任务与工程化

如果只有一首歌,手动跑一遍完全够。但如果你需要处理一整场 Live 的多个曲目,或者一个系列 MV,就应该把流程脚本化。

推荐目录结构:

batch/ ├── 01_ishtar/ │ ├── input.mp4 │ └── output/... ├── 02_another_song/ │ ├── input.mp4 │ └── output/...

批量处理脚本可以这样设计:

for dir in batch/*/; do echo "Processing $dir" ffmpeg -i "$dir/input.mp4" -vn -acodec pcm_s16le "$dir/audio.wav" -y demucs --two-stems=vocals "$dir/audio.wav" -o "$dir/separates" || true python scripts/transcribe.py "$dir/separates/htdemucs/input/vocals.wav" medium ja # 注意:这里需要根据实际输出路径调整 cp asr/output.srt "$dir/output.srt" done

这个脚本只是一个模板,实际路径和文件命名要根据你的目录规划调整。关键点是每一步都要有日志。

更好的做法是写一个 Python 任务队列:

  • 遍历输入目录
  • 对每个视频执行完整流程
  • 每一步成功后再进入下一步
  • 失败则记录日志,不中断整个队列
  • 最后输出任务汇总

批量任务常见的问题有两个:一是某个视频的音频流格式特殊导致 ffmpeg 失败,二是 Whisper 在某个片段上识别耗时过长。处理方式是每条任务设置超时时间,失败后重试一次,仍失败就跳过并记录。

10. 资源占用与性能观察

10.1 如何观察显存占用

如果用的是 NVIDIA 显卡,可以用nvidia-smi实时查看显存占用:

watch -n 1 nvidia-smi

建议重点观察 Whisper 推理阶段的显存峰值,这是整条链路中占用最高的步骤。使用 faster-whisper 时,compute_type对显存影响很大:

  • float32:显存占用高
  • float16:中等,GPU 优化较好
  • int8:显存占用最低,速度不一定慢

如果你的显卡显存在 6G 以下,建议直接用int8。具体显存数字会因模型和输入音频时长波动,实测应以本机nvidia-smi显示为准。

10.2 CPU 推理与 GPU 推理差别

同样一个 medium 模型,CPU 推理在几十分钟音频上可能要跑很久,GPU 则快很多。如果你没有 NVIDIA 显卡,优先选择 small 或 medium 的int8模式,并开启vad_filter=True,跳过无声片段,能节省不少时间。

10.3 影响性能的因素

  • 音频时长:越长的音频推理越慢,这是线性关系。
  • 模型大小:large-v3 通常比 medium 慢数倍。
  • 是否启用 VAD:启用 VAD 会跳过静音区,但从另一个角度看也会额外增加计算开销。
  • 并行任务数:翻译 API 的并发数要控制,否则可能触发限流。

10.4 降低资源占用的方法

  • 使用 faster-whisper 而非原版 Whisper。
  • 使用int8量化。
  • 限制beam_size,例如从 5 降到 3。
  • 使用 VAD 跳过静音。
  • 对超长视频先切片再识别,最后合并字幕。
  • 翻译 API 只跑一次,失败重试时使用上次的输入,避免重复翻译。

11. 常见问题与排查方法

问题现象可能原因排查方式解决方案
ffmpeg 找不到命令未安装或未加入 PATHffmpeg -version安装 ffmpeg 并将 bin 目录加入 PATH
Whisper 模型下载失败网络问题或磁盘空间不足检查网络和磁盘设置代理或换源,预留足够磁盘
CUDA 相关报错显卡驱动与 CUDA 版本不匹配nvidia-smi与 PyTorch 版本检查更新驱动,或改用device="cpu"
显存不足模型过大或float32模式nvidia-smi观察显存切换到int8,或换 small 模型
日语识别成乱码或大量缺词现场嘈杂、模型太小检查音频质量和识别文本做人声分离,改用 medium/large-v3
翻译接口超时API 服务不稳定或并发过高看脚本报错日志和响应码降低并发,增加超时时间
硬字幕乱码/方框字体缺失或字体名不匹配播放器截图检查字体指定fontsdir,确认字体名称
字幕时间轴整体偏移音频提取时延或视频源头偏移用播放器对比音频在 ASS 中用Dialogue时间或后期整体平移
批量任务某一步卡住单条视频解码异常查看日志定位卡住的文件给任务加超时和重试机制
翻译结果太“机器”提示词不充分或模型温度过高对比多段歌词翻译调整 system prompt,降低 temperature

12. 最佳实践与使用建议

这套流程里最容易翻车的地方不是模型本身,而是“素材混乱”。源视频的音轨选择错误、路径写错、目录不一致,都会造成流程中断。建议第一次先拿一小段视频,比如 30 秒到 1 分钟,跑通整条链路,然后再处理完整歌曲。

第二个建议是保留一套最小可运行配置。把你验证过的 Python 版本、faster-whisper 版本、ffmpeg 参数、字体目录全部写进项目 README。下次换机器或者升级依赖后,可以直接对照排查。

第三个建议和字幕质量有关。机器翻译的歌词初稿好用,但不能直接发布。至少做一轮人工校审,重点看:

  • 日语歌词中的人名、曲名、乐队名是否保留
  • 中文译文是否符合句子原意,而不是逐字直译
  • 英文混排是否能被中文字体正常渲染
  • MC 口播和歌词是否需要区分不同字幕样式

版权合规方面,强烈建议只在本地处理自己有权处理的素材。公开发布带中文字幕的 Live 视频前,要确认已经获得视频权利人和音乐版权方的许可。涉及人像、肖像的内容同样需要授权。

接口服务这块也要注意安全边界。如果翻译 API 是本机的服务,不要随意暴露到公网。如果用的是外部 API,不要把密钥提交到 Git 仓库。批量处理大量素材前,先评估 API 成本和限流策略。

13. 总结与下一步

整条链路最值得尝试的地方在于,它把“听写、翻译、压制”这三件原本非常耗时的事情,全部变成可控的本地自动化流程。你不需要精通日语,也能在几分钟内得到一条带中文字幕初稿的视频。对经常处理日语素材的字幕组和视频创作者来说,这套方法可以当作生产力工具来用。

最容易踩的坑有三个:一是现场音频不分离就识别,导致错误率偏高;二是字体缺失导致硬字幕乱码;三是翻译 API 并发设置过高触发限流。建议第一次先跑小样,记录每一步的时间和资源占用。

如果你后续想继续扩展,方向有这几个:把流程封装成 WebUI 或一键脚本;加入 VAD 切片和并行推理;用字幕断句模型优化中文阅读体验;接入本地大模型做离线翻译,彻底摆脱外部 API 依赖。先跑通单曲,再扩展到批量,最后再谈优化。这套能力的价值就会越来越明显。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询