FEATURED · 精选文章

本地视频中文字幕生产全流程:Whisper+ffmpeg实战指南

发布时间 / 2026/9/2 8:20:49
来源 / 创域科博编辑部
栏目 / 资讯中心
本地视频中文字幕生产全流程:Whisper+ffmpeg实战指南 这次我们不看模型评测也不搭 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并且已经配置到系统 PATHGit用于克隆部分工具仓库可选NVIDIA 显卡驱动 CUDA提升 Whisper 推理速度检查 ffmpeg 是否可用ffmpeg -version如果没有 ffmpegUbuntu/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 streamindex,codec_type,codec_name,language -of defaultnoprint_wrappers1 videos/input.mp4确认有日语原声音轨后提取为 WAVffmpeg -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 是目前比较通用的音源分离工具可以分离出人声、鼓、贝斯和其他伴奏。现场版特别适合做人声分离因为观众噪声和乐器混响会干扰语音识别。安装 Demopip install demucs执行人声分离demucs --two-stemsvocals 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.pyimport 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, deviceauto, compute_typeint8) segments, info model.transcribe( audio_path, languagelanguage, beam_size5, vad_filterTrue, vad_parametersdict(min_silence_duration_ms500), initial_promptgirugamesh, イシュタル, ライブ, ) with open(asr/output.srt, w, encodingutf-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.pyimport re import json import sys def parse_srt(path): with open(path, r, encodingutf-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_asciiFalse, indent2))运行python scripts/parse_srt.py asr/output.srt asr/output.json7.2 调用大模型翻译这里给出一个通用模板翻译接口需要根据实际使用的服务进行调整。假设你可以访问一个兼容 OpenAI 格式的翻译 API保存为scripts/translate.pyimport 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: fBearer {API_KEY}, Content-Type: application/json } try: resp requests.post(API_URL, jsonpayload, headersheaders, timeout120) resp.raise_for_status() data resp.json() translated data[choices][0][message][content].strip() except Exception as e: translated f[翻译失败] {text} print(fWARN segment {item[index]}: {e}, filesys.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, encodingutf-8) as f: items json.load(f) with ThreadPoolExecutor(max_workers4) 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(keylambda x: x[index]) with open(out_path, w, encodingutf-8) as f: json.dump(results, f, ensure_asciiFalse, indent2) if __name__ __main__: main()需要说明的是这里的API_URL、API_KEY、model都只是占位符。实际接入时以你选择的翻译服务文档为准。如果不想连外部 API也可以接入本地部署的 Qwen、DeepSeek 等开源模型只要接口格式兼容逻辑基本一致。7.3 翻译结果转回 SRT/ASS翻译并人工确认后需要把 JSON 再转成 SRT 或 ASS。ASS 的好处是支持更精细的样式控制比如不同说话人、不同颜色、歌词逐句显示。保存为scripts/build_ass.pyimport json import sys def format_timestamp(seconds): # 这里直接解析 SRT 时间或者从 JSON 中重新计算 pass def build_ass(data_path, out_path): with open(data_path, r, encodingutf-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(fDialogue: 0,{start},{end},Default,,0,0,0,,{text}) with open(out_path, w, encodingutf-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_chars16): 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 asstranslated/zh.ass:fontsdirfonts -c:v libx264 -preset medium -crf 18 -c:a copy final/girugamesh_ishitar_zh.mp4这里的fontsdirfonts指向你存放中文字体文件的目录目的是让 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 languagechi 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-stemsvocals $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_filterTrue跳过无声片段能节省不少时间。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 目录加入 PATHWhisper 模型下载失败网络问题或磁盘空间不足检查网络和磁盘设置代理或换源预留足够磁盘CUDA 相关报错显卡驱动与 CUDA 版本不匹配nvidia-smi与 PyTorch 版本检查更新驱动或改用devicecpu显存不足模型过大或float32模式nvidia-smi观察显存切换到int8或换 small 模型日语识别成乱码或大量缺词现场嘈杂、模型太小检查音频质量和识别文本做人声分离改用 medium/large-v3翻译接口超时API 服务不稳定或并发过高看脚本报错日志和响应码降低并发增加超时时间硬字幕乱码/方框字体缺失或字体名不匹配播放器截图检查字体指定fontsdir确认字体名称字幕时间轴整体偏移音频提取时延或视频源头偏移用播放器对比音频在 ASS 中用Dialogue时间或后期整体平移批量任务某一步卡住单条视频解码异常查看日志定位卡住的文件给任务加超时和重试机制翻译结果太“机器”提示词不充分或模型温度过高对比多段歌词翻译调整 system prompt降低 temperature12. 最佳实践与使用建议这套流程里最容易翻车的地方不是模型本身而是“素材混乱”。源视频的音轨选择错误、路径写错、目录不一致都会造成流程中断。建议第一次先拿一小段视频比如 30 秒到 1 分钟跑通整条链路然后再处理完整歌曲。第二个建议是保留一套最小可运行配置。把你验证过的 Python 版本、faster-whisper 版本、ffmpeg 参数、字体目录全部写进项目 README。下次换机器或者升级依赖后可以直接对照排查。第三个建议和字幕质量有关。机器翻译的歌词初稿好用但不能直接发布。至少做一轮人工校审重点看日语歌词中的人名、曲名、乐队名是否保留中文译文是否符合句子原意而不是逐字直译英文混排是否能被中文字体正常渲染MC 口播和歌词是否需要区分不同字幕样式版权合规方面强烈建议只在本地处理自己有权处理的素材。公开发布带中文字幕的 Live 视频前要确认已经获得视频权利人和音乐版权方的许可。涉及人像、肖像的内容同样需要授权。接口服务这块也要注意安全边界。如果翻译 API 是本机的服务不要随意暴露到公网。如果用的是外部 API不要把密钥提交到 Git 仓库。批量处理大量素材前先评估 API 成本和限流策略。13. 总结与下一步整条链路最值得尝试的地方在于它把“听写、翻译、压制”这三件原本非常耗时的事情全部变成可控的本地自动化流程。你不需要精通日语也能在几分钟内得到一条带中文字幕初稿的视频。对经常处理日语素材的字幕组和视频创作者来说这套方法可以当作生产力工具来用。最容易踩的坑有三个一是现场音频不分离就识别导致错误率偏高二是字体缺失导致硬字幕乱码三是翻译 API 并发设置过高触发限流。建议第一次先跑小样记录每一步的时间和资源占用。如果你后续想继续扩展方向有这几个把流程封装成 WebUI 或一键脚本加入 VAD 切片和并行推理用字幕断句模型优化中文阅读体验接入本地大模型做离线翻译彻底摆脱外部 API 依赖。先跑通单曲再扩展到批量最后再谈优化。这套能力的价值就会越来越明显。
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻