video-dubbing: 视频翻译配音与字幕同步
Purpose
把一段视频整体本地化为另一种语言:识别原语音、翻译、用 TTS 生成目标语言配音,在保持原始 时间戳的前提下合成音轨并把译文硬字幕烧进画面。产出是可直接发布的单文件视频,配音与字幕 天然同步,不需要二次对轴。
When to Use
- 用户要求把视频翻译成另一种语言,并要配音(不只是字幕)。
- 需要给视频加目标语言配音 + 同步字幕(双语或纯译文)。
- 触发词:视频翻译、视频配音、翻译字幕、生成外语配音、双语字幕、dub a video、translate a video、 foreign-language voiceover。
- 任何 ASR -> 翻译 -> TTS -> 视频合成的工作流。
When NOT to Use
- 只需转录、不需要翻译或配音 -> 交给
video-minutes或直接 whisper。 - 只需把文字合成为音频、不涉及视频 -> 交给
mlx-tts。 - 需要保留原背景音乐/音效并做专业混音(人声分离)-> 本技能默认不做 Demucs,需另行处理。
- 视频无音轨或音质极差到无法识别。
Workflow
端到端推荐直接用编排脚本 <skill>/scripts/dub.py(见 ## Scripts),它会依次执行下面各步并
在翻译处暂停等待 AI 翻译。手动执行时按以下步骤:
-
抽音轨(16kHz 单声道):
ffmpeg -y -i input.mp4 -ar 16000 -ac 1 -c:a pcm_s16le audio16k.wav -
ASR 转录(要逐段准确时间戳):
whisper audio16k.wav --model large-v3-turbo --language <源语言> --output_format srt --output_dir .产出audio16k.srt,时间戳与原视频天然对齐。想要更好的中文识别可改装 Qwen3-ASR (pip install qwen-asr)并用<skill>/scripts/qwen3_asr.py。 -
翻译字幕(由 AI 完成):读 SRT,逐段翻译。每个 SRT 段输出一行译文,严格保持顺序。 先数清段数(如 27 段 -> 27 行),存为
translated.txt,继续前核对行数一致。 -
生成配音:
python3 <skill>/scripts/dub_segments.py audio16k.srt translated.txt dubbing.wav subtitle_synced.srt --lang <目标语言>脚本会:用首段作音色参考保证音色一致;保留原始时间戳让字幕与画面同步;只对溢出槽位 的段做变速(atempo 0.88–1.20);把相邻小于 1s 的间隔桥接起来让听感自然。 -
合并配音音轨到视频:
ffmpeg -y -i input.mp4 -i dubbing.wav \ -map 0:v:0 -map 1:a:0 \ -c:v copy -c:a aac -b:a 192k -shortest output_temp.mp4然后确认音轨已被替换:
ffprobe -v error -show_entries stream=codec_type -of csv=p=0 output_temp.mp4(应显示video, audio)。 -
烧录硬字幕:
python3 <skill>/scripts/burn_subtitles.py output_temp.mp4 subtitle_synced.srt output_final.mp4把译文字幕永久渲染进画面。 -
清理(可选):删除
audio16k.wav、audio16k.srt、translated.txt、dubbing.wav、subtitle_synced.srt、output_temp.mp4等中间产物。
Decision Rules
- 想一键跑完 -> 用
dub.py;只想重跑某一步(如已有 SRT)-> 用dub.py --srt existing.srt跳步。 - ASR 引擎:默认
--asr whisper(large-v3-turbo);中文识别要求高 ->--asr qwen3。 - 不需要硬字幕 ->
dub.py --no-burn(只到音轨合并)。 - 目标语言由
--target-lang决定(默认zh);dub_segments.py的 TTS 语言由--lang决定。 - 翻译必须由 AI 完成且与 SRT 段严格 1:1;行数不符时脚本直接报错退出,不要绕过。
- 字幕没出现/中文变方框 -> 用
burn_subtitles.py(moviepy 渲染,自动找中文字体),不要依赖 ffmpeg 的subtitles=(libass 缺失时会静默失败)。 - 配音时长溢出槽位 -> 依赖脚本的分段 atempo,不要对整条音轨做全局变速。
Constraints
- 时间戳必须复用 ASR 原始结果,禁止对配音重新 ASR 取时间。
- 译文行数必须等于 SRT 段数(1:1),否则中止。
- 合并音轨必须显式
-map 0:v:0 -map 1:a:0,否则会保留原音轨。 - ASR 必须用
large-v3-turbo(或turbo),不要退回base/tiny。 - 变速只在段级别进行,范围 0.88–1.20x;禁止全局 atempo。
- 默认不做背景音乐人声分离;用户要求时应显式说明需要额外处理。
Verification
交付前逐项确认:
-
translated.txt行数与 SRT 段数完全一致。 -
dubbing.wav与subtitle_synced.srt均已生成。 -
ffprobe显示最终视频同时含 video 与 audio 流,且音频确实被替换。 - 字幕已硬烧进画面(不是外部软字幕),中文无方框。
- 配音与画面在若干随机点抽查时间戳对齐。
- 未删除用户提供的源文件。
Output
- Outcome:一个本地化后的视频文件(默认
output_final.mp4,dub.py下为<视频名>_<目标语言>.mp4),含目标语言配音与硬字幕;另可保留subtitle_synced.srt。 - Done when:音轨已替换、字幕已烧录、音画在抽查点同步、ffprobe 校验通过。
- Evidence:最终文件路径;SRT 段数与译文行数;ffprobe 的流清单;抽查的同步时间点。
References
按需加载,不要预先全读:
references/pipeline-design.md— 需要解释或调整流水线行为(为何保留时间戳、分段变速、 默认不做人声分离、产物与依赖清单)时读。references/troubleshooting.md— 实际报错、音画不同步、字幕没出现或中文变方框时读。
Scripts
<skill>/scripts/dub.py— 端到端编排。用法python3 <skill>/scripts/dub.py input.mp4 --source-lang en --target-lang zh [--asr whisper|qwen3] [--srt existing.srt] [-o out.mp4] [--font-size 28] [--no-burn] [--keep-temp]。 运行到翻译步时会生成to_translate.txt并要求 AI 翻译成translated.txt,然后重跑继续; 返回最终视频路径与字幕路径。<skill>/scripts/dub_segments.py— 分段 TTS 配音 + 时间戳对齐。用法python3 <skill>/scripts/dub_segments.py original.srt translated.txt out_dub.wav out_synced.srt [--lang zh] [--min-atempo 0.88] [--max-atempo 1.20] [--gap-bridge 1.0] [--keep-temp]。 返回对齐后的dubbing.wav与subtitle_synced.srt;译文行数不匹配时以退出码 1 中止。<skill>/scripts/burn_subtitles.py— 用 moviepy 烧录硬字幕。用法python3 <skill>/scripts/burn_subtitles.py input.mp4 subtitles.srt output.mp4 [--font <path>] [--font-size 28]。 返回烧好字幕的视频;--font缺省时自动探测中文字体。<skill>/scripts/qwen3_asr.py— whisper 兼容的 Qwen3-ASR CLI,中文识别更佳。用法python3 <skill>/scripts/qwen3_asr.py audio.wav [--model 0.6B|1.7B] [--language zh] [-f srt|vtt|txt|json|tsv|all] [-o 输出目录] [--device cuda:0|mps|cpu] [--dtype float16|bfloat16|float32]。 返回指定格式的字幕/文本文件。模型别名:turbo/tiny/base/small -> Qwen3-ASR-0.6B,medium/large -> Qwen3-ASR-1.7B。
Troubleshooting
- 配音没被替换 / 还是原声 -> 合并 ffmpeg 时漏了
-map 0:v:0 -map 1:a:0。 - 字幕没出现或中文方框 -> 改用
burn_subtitles.py,并用--font指定中文字体。 - 译文行数与 SRT 段数不符 -> 脚本会报错退出,先补齐到 1:1。
- 完整错误清单见
references/troubleshooting.md。
Related Skills
mlx-tts— 提供底层mlx_audio.tts.generate,本技能的配音节依赖它。video-minutes— 视频转结构化纪要;只需转录/总结时改用它。