尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

ffmpeg+Whisper:无字幕视频自动生成中文字幕完整流程

ffmpeg+Whisper:无字幕视频自动生成中文字幕完整流程 平时看视频内容时如果碰到一段没有字幕的日语视频想要自己动手做一个“中字版”很多人的第一反应是去网上找现成的字幕文件。但现实往往很骨感原始视频没有外挂字幕网上也搜不到对应翻译真正可行的办法是自己从音频里把字幕“挖”出来。这篇文章就把这条完整链路拆开讲清楚音频提取、语音识别、字幕翻译、人工校对、压制合成。全文提供可直接运行的命令和 Python 脚本适合想自己做字幕翻译、批量处理视频、或者单纯想了解字幕制作流程的读者。示例以一段日语视频为素材最终输出一个带中文字幕的 MP4 文件。1. 背景与核心概念1.1 什么是“中字视频”“中字”是中文互联网社区对“中文字幕”的简称。观众在观看日语、英语等外语视频时如果听不懂原声就需要依赖字幕来理解内容。中字视频通常指已经嵌入了中文字幕的视频文件可以直接播放观看也可以当作二次创作的素材。中字制作并不是简单地把文本翻译出来还需要考虑台词出现的起始时间和结束时间角色名、专有名词的翻译一致性字幕在画面中的位置、大小、字体、描边效果视频编码格式与字幕烧录方式的兼容性。一个规范的中字视频应该做到台词显示节奏准确、翻译通顺、风格统一并且在不同播放器上都能正常显示。1.2 字幕的三种存在形式字幕按存储和显示方式可以分成三种类型特点常见后缀使用场景外挂字幕字幕是独立文件播放时手动加载.srt、.ass视频原文件不带字幕需要额外加载内封字幕字幕封装进视频容器播放器可切换常见于 .mkv同一个视频内包含多条字幕轨道硬字幕字幕直接烧录进画面像素输出 MP4 等所有播放器都能看到无法关闭外挂字幕最灵活但观众需要额外操作内封字幕方便携带但依赖播放器支持硬字幕最“稳”适合分发也是很多中字视频最终采用的形态。本文实战部分会同时演示软字幕封装和硬字幕烧录。1.3 中字视频的完整制作链路把一段没有字幕的原始视频变成中字视频核心流程如下原始视频 │ ▼ ffmpeg 提取音频 │ ▼ Whisper 语音识别生成带时间轴的日文文本 │ ▼ 机器翻译为中文 │ ▼ 人工校对检查断句、术语、时间轴 │ ▼ 生成 SRT / ASS 字幕 │ ▼ ffmpeg 压制合成 │ ▼ 中字视频每个环节都有对应的工具和坑点。下面会按这个链路一步步完成实战。2. 环境准备与版本说明2.1 环境与版本本文示例以 Ubuntu 22.04 LTS 为运行环境Python 使用 3.10 或更高版本。Windows 和 macOS 的思路完全一致只是安装命令略有差异我会在关键位置补充说明。需要安装的软件ffmpeg用于音频提取、字幕压制Python 3用于运行语音识别和翻译脚本pip用于安装 Python 依赖包Aegisub可选用于人工校对字幕。版本需要根据你的项目实际情况调整本文示例以常见环境为例重点演示配置思路。只要 ffmpeg 版本不低于 4.xfaster-whisper 的接口在核心用法上都保持一致。2.2 安装 ffmpegUbuntu / Debian 系统可以使用 apt 安装sudo apt update sudo apt install -y ffmpegWindows 用户可以直接从 ffmpeg 官网下载编译好的二进制文件把bin目录加入系统 PATH。macOS 用户可以使用 Homebrewbrew install ffmpeg安装完成后验证版本ffmpeg -version如果能看到版本号说明安装成功。2.3 安装 Python 依赖本文使用 faster-whisper 做语音识别使用 deep-translator 做机器翻译初稿。pip install faster-whisper deep-translatorfaster-whisper是 OpenAI Whisper 的加速实现内存占用更小解码速度更快支持 CPU 和 GPU 推理。deep-translator是一个封装了多种翻译服务接口的 Python 库代码里可以直接调用。如果下载速度慢可以临时使用国内镜像源pip install faster-whisper deep-translator -i https://pypi.tuna.tsinghua.edu.cn/simple2.4 项目目录规划建议按下面的目录结构组织文件subtitle_workflow/ ├── input/ │ └── shiny_source.mp4 ├── audio/ │ └── audio.wav ├── srt/ │ ├── ja_raw.srt │ ├── zh_raw.srt │ └── zh_final.srt ├── output/ │ └── shiny_zh.mp4 └── scripts/ ├── transcribe.py ├── translate_srt.py └── run_all.shinput/放原始视频audio/放中间音频srt/放各阶段字幕output/放最终产物scripts/放 Python 脚本和 Shell 脚本。这样做的目的是避免中间产物混在一起。后文所有命令都基于这个目录结构。3. 核心工具与原理拆解3.1 ffmpeg音视频处理的瑞士军刀ffmpeg 是开源社区最常用的音视频处理工具几乎所有视频处理场景都和它有关。它的基本用法是ffmpeg -i 输入文件 [处理参数] 输出文件字幕制作中用到的核心参数-vn不处理视频流只保留音频-acodec pcm_s16le指定音频编码为 PCM 16 位小端格式适合后续识别-ar 16000设置采样率为 16kHz这是 Whisper 类语音识别模型最常用的采样率-ac 1合并为单声道可以避免背景音乐和立体声带来的识别干扰。这些参数在后面实战中会用到。理解它们的作用才能在自己遇到问题时灵活调整。3.2 Whisper带时间戳的语音识别Whisper 是 OpenAI 开源的语音识别模型支持包括日语、中文在内的多语言识别并且会输出每句话的时间戳。faster-whisper 是其 CTranslate2 加速版本对 CPU 和 GPU 都有更好的性能。faster-whisper 的核心用法from faster_whisper import WhisperModel model WhisperModel(small, devicecpu, compute_typeint8) segments, info model.transcribe(audio.wav, languageja, vad_filterTrue) for segment in segments: print(segment.start, segment.end, segment.text)模型大小有tiny、base、small、medium、large-v3等选择。模型越大识别准确率越高但推理速度和内存占用也越大。对一般视频来说small或medium是性价比比较高的选择。languageja表示指定源语言为日语如果不确定可以去掉language参数让模型自动检测。vad_filterTrue表示开启语音活动检测可以过滤掉没有语音的静音片段减少无效输出。首次使用某个模型时会自动下载模型文件需要保持网络正常下载后本地会缓存后续使用不用再次下载。3.3 SRT 字幕格式SRT 是最简单、兼容性最好的字幕格式。它的基本结构如下1 00:00:01,000 -- 00:00:04,000 こんにちは 2 00:00:05,000 -- 00:00:08,000 世界每个字幕条目包含三部分序号显示时间范围格式为时:分:秒,毫秒字幕文本内容。条目之间用空行分隔。SRT 文件通常使用 UTF-8 编码用记事本或代码编辑器打开时不容易乱码。ASS 格式比 SRT 复杂支持自定义字体、颜色、位置、滚动效果等适合对字幕样式有更高要求的场景。本文核心流程用 SRT最后会简单补充 ASS 的转换思路。3.4 翻译与校对机器翻译的结果只能看作“初稿”不能直接发布。原因有两个语音识别本身可能存在漏字、错字机器翻译对口语、角色语气、专有名词的处理往往生硬。正确做法是先用脚本完成机器翻译然后使用 Aegisub 等工具逐条人工校对。校对时重点关注人名、地名、语气词、断句位置。比如“桑山千雪”这类角色名一旦定了译名就要在全文保持统一。4. 完整实战案例4.1 准备输入视频假设原始视频文件名是心ノアリカ.mp4因为文件名包含特殊字符命令行处理时容易出问题建议先重命名mkdir -p input audio srt output scripts mv 心ノアリカ.mp4 input/shiny_source.mp4后面的命令统一使用input/shiny_source.mp4作为输入文件。4.2 提取音频使用 ffmpeg 从视频中提取 16kHz 单声道 WAV 音频ffmpeg -y -i input/shiny_source.mp4 \ -vn \ -acodec pcm_s16le \ -ar 16000 \ -ac 1 \ audio/audio.wav参数含义-y覆盖同名文件-vn丢弃视频流只保留音频-acodec pcm_s16le输出 PCM 16 位小端编码的 WAV-ar 16000采样率改为 16kHz-ac 1单声道。执行完成后可以查看音频信息确认ffprobe audio/audio.wavffprobe是 ffmpeg 附带的媒体信息查看工具。输出中应该能看到16000 Hz、mono等信息。4.3 语音识别生成日文 SRT在scripts/目录下创建transcribe.py# 文件路径scripts/transcribe.py import sys from faster_whisper import WhisperModel def format_ts(seconds: float) - str: 将秒数转换为 SRT 时间戳格式例如 00:00:03,500 millis int(round(seconds * 1000)) h, remainder divmod(millis, 3600000) m, remainder divmod(remainder, 60000) s, ms divmod(remainder, 1000) return f{h:02d}:{m:02d}:{s:02d},{ms:03d} def write_srt(segments, output_path: str) - None: 将语音识别片段写入 SRT 文件 with open(output_path, w, encodingutf-8) as f: for idx, segment in enumerate(segments, start1): start_ts format_ts(segment.start) end_ts format_ts(segment.end) text segment.text.strip() f.write(f{idx}\n) f.write(f{start_ts} -- {end_ts}\n) f.write(f{text}\n\n) def main(): audio_path sys.argv[1] if len(sys.argv) 1 else audio/audio.wav srt_path sys.argv[2] if len(sys.argv) 2 else srt/ja_raw.srt # 模型大小可以根据机器性能调整tiny/base/small/medium/large-v3 model WhisperModel(small, devicecpu, compute_typeint8) segments, info model.transcribe( audio_path, languageja, vad_filterTrue, ) print(f识别语言: {info.language}, 概率: {info.language_probability:.2f}) write_srt(segments, srt_path) print(f字幕已写入: {srt_path}) if __name__ __main__: main()运行cd subtitle_workflow python scripts/transcribe.py audio/audio.wav srt/ja_raw.srt首次运行会下载 Whisper 模型时间取决于网络状况。运行结束后打开srt/ja_raw.srt应该能看到类似下面的内容1 00:00:01,040 -- 00:00:04,120 心の在り処、探して 2 00:00:05,300 -- 00:00:08,760 私のまま、変わっていく这里需要说明识别结果依赖音质、背景音乐、说话人语速。如果结果不理想可以换用更大的模型或者对音频提前做降噪处理。4.4 字幕翻译为中文在scripts/目录下创建translate_srt.py# 文件路径scripts/translate_srt.py import re import sys import time from pathlib import Path from deep_translator import GoogleTranslator SRT_PATTERN re.compile( r(\d)\n r(\d{2}:\d{2}:\d{2},\d{3}) -- (\d{2}:\d{2}:\d{2},\d{3})\n r(.*?)(?\n\n|\Z), re.DOTALL, ) def parse_srt(path: str): 解析 SRT 文件返回字幕条目列表 text Path(path).read_text(encodingutf-8) entries [] for match in SRT_PATTERN.finditer(text): entries.append({ index: int(match.group(1)), start: match.group(2), end: match.group(3), content: match.group(4).strip().replace(\n, ), }) return entries def write_srt(entries, path: str) - None: 将翻译后的字幕写入新的 SRT 文件 with open(path, w, encodingutf-8) as f: for entry in entries: f.write(f{entry[index]}\n) f.write(f{entry[start]} -- {entry[end]}\n) f.write(f{entry[content]}\n\n) def safe_translate(translator, text: str, max_retries: int 3) - str: 翻译并处理异常失败时保留原文 for attempt in range(max_retries): try: return translator.translate(text) except Exception as e: print(f翻译失败正在重试: {text}, 错误: {e}) if attempt max_retries - 1: return text time.sleep(2 * (attempt 1)) return text def main(): input_srt sys.argv[1] if len(sys.argv) 1 else srt/ja_raw.srt output_srt sys.argv[2] if len(sys.argv) 2 else srt/zh_raw.srt entries parse_srt(input_srt) translator GoogleTranslator(sourceja, targetzh-CN) for entry in entries: entry[content] safe_translate(translator, entry[content]) print(f[{entry[index]}] {entry[content]}) write_srt(entries, output_srt) print(f翻译完成已写入: {output_srt}) if __name__ __main__: main()运行python scripts/translate_srt.py srt/ja_raw.srt srt/zh_raw.srt说明一点deep-translator默认调用的是公共翻译接口可能有频率限制。这里加上了重试逻辑翻译失败时会保留原文避免整个脚本中断。如果翻译量很大建议申请正规翻译服务的 API Key然后把translator替换成对应的实现代码结构不需要变。4.5 字幕校对与样式调整机器翻译完成后不要直接压制。先用 Aegisub 打开srt/zh_raw.srt逐条校对。校对主要看这几项时间轴是否明显错位译文是否通顺是否符合人物口吻专有名词是否统一每条字幕长度是否适合阅读。Aegisub 是开源的字幕编辑器支持直接编辑 SRT并且能在视频画面上预览字幕效果。如果希望字幕带样式比如字体、描边、阴影可以把 SRT 转换成 ASS或者在压制命令中通过force_style临时指定样式。转换工具可以使用 Python 的ass相关库也可以直接在 Aegisub 里“另存为 ASS”。校对完成后建议把文件另存为srt/zh_final.srt作为最终压制使用的字幕。4.6 压制合成中字视频压制分为硬字幕和软字幕两种方式。硬字幕直接烧录进画面ffmpeg -y -i input/shiny_source.mp4 \ -vf subtitlessrt/zh_final.srt:force_styleFontNameMicrosoft YaHei,FontSize20,PrimaryColourH00FFFFFF,OutlineColourH00000000,BorderStyle1,Outline1,Shadow0,MarginV30 \ -c:v libx264 -crf 20 -preset medium \ -c:a copy \ output/shiny_zh.mp4参数说明subtitles...ffmpeg 的字幕滤镜用于把字幕绘制到视频上FontNameMicrosoft YaHei指定中文字体Linux 上如果没有微软雅黑需要先安装中文字体或者改成系统中已经存在的字体FontSize20字号PrimaryColourH00FFFFFF字体颜色H00FFFFFF表示白色OutlineColourH00000000描边颜色黑色BorderStyle1描边样式MarginV30字幕距画面底部的垂直边距-c:v libx264使用 H.264 编码-crf 20视频质量参数数值越小质量越高文件越大-preset medium编码速度与压缩率平衡-c:a copy音频直接复制不重新编码。软字幕封装成 MKV保留多条字幕轨道ffmpeg -y -i input/shiny_source.mp4 \ -i srt/zh_final.srt \ -c copy \ -c:s srt \ output/shiny_zh.mkv这种方式不会修改画面字幕作为独立轨道封装进 MKV播放器可以切换或关闭字幕。4.7 一键串联如果流程已经跑通可以把命令写成一个 Shell 脚本# 文件路径scripts/run_all.sh #!/usr/bin/env bash set -euo pipefail INPUTinput/shiny_source.mp4 AUDIOaudio/audio.wav JA_SRTsrt/ja_raw.srt ZH_SRTsrt/zh_final.srt OUTPUToutput/shiny_zh.mp4 echo 1. 提取音频 ffmpeg -y -i $INPUT -vn -acodec pcm_s16le -ar 16000 -ac 1 $AUDIO echo 2. 语音识别 python scripts/transcribe.py $AUDIO $JA_SRT echo 3. 机器翻译 python scripts/translate_srt.py $JA_SRT $ZH_SRT echo 4. 压制中字视频 ffmpeg -y -i $INPUT \ -vf subtitles$ZH_SRT:force_styleFontNameMicrosoft YaHei,FontSize20 \ -c:v libx264 -crf 20 -preset medium \ -c:a copy \ $OUTPUT echo 完成: $OUTPUT需要给脚本增加执行权限chmod x scripts/run_all.sh ./scripts/run_all.sh注意这个链路是“半自动”的机器翻译后需要人工校对最好把zh_final.srt的人工校对穿插在第 3 步和第 4 步之间。实际使用时不要在机器翻译后直接压制除非你接受初稿质量。5. 常见问题与排查思路5.1 常见问题速查表问题现象常见原因解决思路ffmpeg 提示 command not foundffmpeg 未安装按系统安装 ffmpegWindows 检查 PATH音频提取后文件为空输入视频没有音轨用 ffprobe 查看音轨信息识别结果全是英文未指定语言参数设置 languageja识别结果为空背景音乐干扰或 VAD 过滤过强关闭 vad_filter或换用更大模型SRT 中文乱码文件编码不是 UTF-8用 UTF-8 编码保存 SRT压制时提示字体找不到系统缺少中文字体安装中文字体或改成系统已有字体名硬字幕中文显示为方块字体不支持中文使用中文字体如 Noto Sans CJK、微软雅黑翻译脚本频繁报错免费翻译接口限流增加重试间隔或换用付费翻译 API压制后音画不同步视频本身帧率异常先检查原始视频必要时加 -vsync 参数字幕时间轴整体偏移视频片头片尾裁剪过在 SRT 中整体调整时间偏移5.2 案例一识别结果为空如果transcribe.py执行完SRT 文件里没有任何字幕首先检查音频是否正常ffprobe audio/audio.wav确认文件大小不为零时长合理。然后尝试去掉vad_filtersegments, info model.transcribe( audio_path, languageja, vad_filterFalse, )有些视频背景音乐明显VAD 会把带人声的片段误判为噪声导致识别内容被过滤。关闭 VAD 后虽然可能会多出一些无效时间戳但至少能看到识别结果。5.3 案例二中文字幕在压制后乱码乱码一般是编码问题。SRT 文件必须保存为 UTF-8 编码。Windows 记事本默认可能是 GBK 编码可以用 VS Code 打开后检查右下角编码是否显示 UTF-8。另外ffmpeg 的subtitles滤镜在读取字幕文件时对文件路径的转义很敏感。Windows 路径中的反斜杠和盘符冒号需要额外处理建议把字幕路径改成相对路径并先运行一个简单命令测试ffmpeg -i input/shiny_source.mp4 -vf subtitlessrt/zh_final.srt -c:a copy output/test.mp4如果还是不显示就优先检查字体问题。5.4 案例三翻译结果太生硬机器翻译对口语化台词、隐藏含义、文化梗的处理能力有限。比如日语中的“よろしくお願いします”直译和意译差别很大机器容易译得僵硬。这种情况下人工校对是绕不开的。如果希望机器翻译质量更好可以把待翻译文本先交给大语言模型翻译接口再人工检查。但要注意翻译内容的隐私和合规不要上传敏感信息。6. 最佳实践与工程建议6.1 文件与命名规范字幕制作过程会产生大量中间文件命名规范非常重要。建议遵循原始视频、中间音频、各阶段字幕分目录存放文件名中不要带空格使用下划线代替字幕文件名标注语言和阶段例如ja_raw.srt、zh_raw.srt、zh_final.srt输出文件标注最终状态例如shiny_zh.mp4。如果要做多集视频可以再加一层集数目录避免文件互相覆盖。6.2 翻译质量工程化机器翻译只能作为初稿想提升质量可以从几个方向入手建立术语表把角色名、地名、专有名词整理成统一的对照表对台词按角色分开翻译保持人物语气一致识别后先清理无意义文本比如“あの”“えっと”等语气词可以根据语境决定是否保留校对时重点看时间轴与文本长度是否匹配字幕太长读者会跟不上。在脚本层面可以把翻译结果输出为 JSON 格式方便二次修改后再写回 SRT。6.3 字幕视觉规范压制硬字幕时注意字体选择推荐中文字体如 Noto Sans CJK、思源黑体、微软雅黑字号大小1080p 视频常用 16 到 22 号4K 视频需要适当放大边距字幕底部距画面边缘建议保留一定距离不要贴边描边深色描边或半透明底框可以提高可读性多行字幕单条字幕不要超过两行一行中文大约 16 到 20 个字为宜。如果视频画面中已经有原生日文或说明文字要注意中文字幕不要遮挡重要的画面信息。6.4 版权与合规制作中字视频时要特别注意版权问题。本文介绍的流程适用于满足以下条件之一的场景个人学习、研究用途已经获得版权方授权视频本身允许二次创作和翻译传播。不要大规模传播未授权的中字视频也不要用中字视频谋利。如果你为他人制作字幕应确认对方有合法使用该素材的权利。6.5 批量化与断点续跑如果处理多集视频不要简单套循环。建议在脚本中加入断点续跑机制每完成一步生成一个标记文件已识别过的音频不重复识别已翻译过的字幕不重复翻译除非删除标记文件每个步骤的日志分别记录方便定位失败点。思路如下if [ ! -f srt/ja_raw.srt ]; then python scripts/transcribe.py audio/audio.wav srt/ja_raw.srt fi if [ ! -f srt/zh_final.srt ]; then python scripts/translate_srt.py srt/ja_raw.srt srt/zh_final.srt fi这样即使中途失败重新执行脚本时不会从头开始能节省大量时间。7. 总结与学习路线这篇文章从一条完整的字幕生产链路出发完成了音频提取、语音识别、字幕翻译、人工校对和压制合成。你掌握了 ffmpeg 的基础用法、faster-whisper 的语音识别脚本、SRT 字幕格式的解析与生成以及硬字幕和软字幕的两种压制方式。下一步可以继续深入的方向包括ASS 字幕样式语法掌握更精细的排版和特效用大语言模型接口替换简单的机器翻译建立台词角色级翻译流程把语音识别和翻译封装成 Web 服务做成上传视频自动出字幕的小工具对中文识别场景做针对性调参处理中英混排字幕学习 ffmpeg 的滤镜链把音频降噪、响度统一集成进流程。最后给一个实际经验做批量视频字幕时先拿一段 30 秒左右的片段跑通全流程确认模型、字体、编码都没问题再投入完整视频处理。否则几十分钟素材跑完才发现字幕样式不对返工成本会很高。字幕制作没有一步到位的魔法耐心校对才是质量的关键。
返回列表