这次我们不看模型评测,也不搭 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 更好;模型尺寸不同,显存需求不同 |
| 是否支持批量 | 支持,可以做成目录循环或任务队列 |
| 是否提供 API | Whisper 无自带 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 ffmpegWindows 用户建议直接下载 ffmpeg 的 Windows 构建包,解压后把bin目录加入 PATH。
3.2 Python 虚拟环境
建议单独建一个虚拟环境,避免依赖冲突:
python -m venv venv source venv/bin/activate pip install --upgrade pipWindows 下的激活命令是:
venv\Scripts\activate3.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-whisper6.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 模型档位选择
| 模型 | 参数量 | 速度 | 识别准确率 | 建议场景 |
|---|---|---|---|---|
| small | 244M | 快 | 一般 | CPU 机器、快速粗筛 |
| medium | 769M | 中等 | 较好 | 6G 以上显存或 CPU 可接受等待 |
| large-v3 | 1550M | 慢 | 最好 | 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.json7.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_URL、API_KEY、model都只是占位符。实际接入时以你选择的翻译服务文档为准。如果不想连外部 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 找不到命令 | 未安装或未加入 PATH | ffmpeg -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 依赖。先跑通单曲,再扩展到批量,最后再谈优化。这套能力的价值就会越来越明显。