拍视频容易,剪字幕难——这句话几乎是我做内容的真实写照。以前一期20分钟的教程视频,光对字幕时间轴就能耗掉一个晚上。后来我把mac自动给视频加字幕这套流程彻底跑顺了,核心工具就是ffmpeg和autosub:ffmpeg负责从视频里抽音频、最后把字幕封装回视频或直接烧录进画面;autosub负责把语音识别成带时间轴的字幕文件。整条链路免费、可脚本化、批量友好。想给vlog、课程录屏、会议回放快速加字幕的朋友,这篇就是我完整跑通后的实操记录。
先说说适用范围。如果你只是偶尔剪一两个片子,用剪辑软件自带的智能字幕可能更省事。但如果你跟我一样,每周要处理好几条视频,或者手上有大量讲座、培训、内部资料需要统一加字幕,命令行方案的优势就很明显了——不需要把素材导入某个工程,一条命令处理一个文件,几十个视频可以用脚本循环跑,晚上挂机,第二天直接收成品。下面我会从方案思路讲到环境安装,再到每一步的实操命令和踩坑点,尽量做到看完就能照着操作。
1. 项目思路与选型分析
1.1 为什么选 ffmpeg + autosub 这套组合
现在市面上能自动加字幕的工具挺多的,剪辑软件自带功能、在线字幕网站、各种AI工具都有。但它们大多有几个共通的问题:要么字幕时长或者次数有限制,要么必须把素材传上别人的服务器,要么很难和已有工作流打通。我个人的诉求比较明确:免费、本地运行、可批量、可脚本化。
ffmpeg 本身是视频处理界的“瑞士军刀”,切视频、抽音频、转格式、加滤镜、封装字幕全能干,而且完全本地执行。autosub 则是把“语音识别成字幕”这件事独立出来的命令行工具,它负责从音频里识别出文字,并生成带时间轴的 SRT 字幕文件。这两个工具一个管视频处理,一个管语音转写,刚好把整个流程闭环起来。
相比之下,用剪辑软件的自动字幕功能,虽然交互体验好、识别准确率也不错,但它依赖联网服务,而且素材要进入软件工程体系,想批量处理就得手动重复操作。命令行方案的核心理念是“流水线可拆解、可复用”,每一步都能单独调试,出了问题也知道去哪修。
1.2 自动加字幕的完整工作链路
很多人以为“自动加字幕”是一个按钮完成的事,实际背后是一条标准流水线:
视频文件进来,先用 ffmpeg 把音轨抽出来,处理成语音识别引擎最适应的格式;然后把音频交给识别引擎,生成带时间码的文字稿;最后再用 ffmpeg 把字幕文件封装回视频,或者作为独立字幕轨道放进去。
这条链路里,ffmpeg 出现在开头和结尾,中间是识别环节。每一步都可以单独换方案:开头抽音频的方式可以调整采样率、声道;中间识别引擎可以换成不同模型;结尾封装可以选择软字幕还是硬字幕。这种灵活度是剪辑软件给不了的,也是我推荐这套方案最重要的原因。
1.3 关于 autosub 的一个关键提醒
这里必须先打个预防针。老玩家提到的 autosub,指的是 GitHub 上那个用 Python 写的开源字幕生成工具,它通过调用语音识别 API 把音频转成字幕。想法很完整,但那是很多年前的项目,底层依赖偏老,对现在的 Python3 环境和某些系统库兼容性很差。你要是直接pip install autosub然后跑,十有八九会碰到一堆依赖报错。
所以我的实操建议是:把 autosub 当作整条链路里的“第二步”来理解。如果你能搞定它老旧的依赖,那么用起来没问题;搞不定也很正常,直接用 OpenAI 开源的 whisper 顶替第二步,后面生成 SRT、封装回视频的流程完全一样。这篇文章里我会把两条路都讲清楚,你可以根据情况选。
2. 环境准备:mac 上的安装与配置实录
2.1 先把 homebrew 打理好
mac 上安装工具,最省心的方式就是 homebrew。如果你已经装过,可以先跳过;没装过的话,去 homebrew 官网复制安装命令到终端执行就行。装的过程中最常见的坑是网络不稳、卡在Updating Homebrew或者下载文件超时。
我自己的处理方式是给 brew 设置国内镜像源,核心思路是配置HOMEBREW_API_DOMAIN和HOMEBREW_BOTTLE_DOMAIN这两个环境变量,然后重新执行brew update。另外还有个偷懒的参数:HOMEBREW_NO_AUTO_UPDATE=1,它可以让 brew 在安装软件时跳过自动更新,适合你不想每次都被漫长的更新卡住的情况。Apple Silicon 和 Intel 芯片的 mac 目录路径不一样,但换源思路完全一致。
这里有个小习惯:所有环境变量可以写进~/.zshrc,以后每次开终端自动生效。不然你这次设了变量能用,关掉终端下次又忘了。
2.2 安装 ffmpeg 并验证关键编码器
brew 就绪后,安装 ffmpeg 只需要一条命令:
brew install ffmpeg这条命令会连着一堆依赖一起装,包括视频编码库、音频编码库、字幕渲染库等等,耗时取决于网络情况。装完务必验证一下:
ffmpeg -version ffmpeg -encoders | grep 264第二条命令是为了确认系统里有 libx264 编码器。如果没有,后面做硬字幕烧录时会直接失败。brew 官方源编译的 ffmpeg 默认带上了大多数常用组件,正常情况不用自己手动编。
还有一个验证点,看看 ffmpeg 是否支持字幕滤镜:
ffmpeg -filters | grep subtitles只要能看到subtitles这一行,说明 libass 被编译进去了,中文字幕烧录才有保障。如果没看到,后面烧录硬字幕会遇到Unknown filter或者找不到字体之类的报错。
2.3 autosub 与它的替代者:whisper
autosub 的安装其实很简单:
pip install autosub问题在于,装完之后跑起来很容易报错。原项目依赖的语音识别 API 需要单独配置网络和密钥,而且好多 Python 依赖库已经和现在的环境冲突。我试了几次,最后果断转向了 whisper,反而清净了。
whisper 是本地运行的开源语音识别模型,安装方式也简单,建议单独建一个虚拟环境,避免污染系统 Python:
brew install python@3.11 python3 -m venv ~/.whisper-venv source ~/.whisper-venv/bin/activate pip install -U pip pip install -U openai-whisper第一次运行 whisper 时会自动下载模型文件,模型小则几百 MB,大则好几个 GB,会存放在~/.cache/whisper目录。我的建议是先用小模型跑通流程,确认整条链路没问题,再换大模型提升识别准确率。
如果你用的 mac 是 M 系列芯片,还可以考虑whisper-ctranslate2这个优化版,它在 Apple Silicon 上跑得更快,安装量也更小。这个后面可以单独聊。
2.4 顺手做一个环境自检
正式开始前,建议统一跑一遍自检命令:
which ffmpeg ffmpeg -version whisper --help如果你电脑上同时装过 conda、miniconda 或者其它 Python 环境,很容易出现ffmpeg或whisper指向了错误的路径。检查一下which ffmpeg的结果,应该指向/opt/homebrew/bin/ffmpeg(Apple Silicon 路径)或/usr/local/bin/ffmpeg(Intel 路径),如果不是,就要检查 PATH 变量顺序。
3. 核心实操:一条龙跑通自动加字幕
3.1 第一步:抽出干净的单声道音频
字幕识别引擎吃的是音频,不识视频画面。所以先用 ffmpeg 把音轨单独抽出来,同时把格式统一成识别引擎最适应的样子:
mkdir -p work ffmpeg -y -i input.mp4 -vn -ac 1 -ar 16000 -c:a pcm_s16le work/input.wav这条命令的参数逐个解释一下:
-y:如果输出文件已存在,直接覆盖,省得每次手动确认。-vn:忽略视频流,只处理音频。-ac 1:强制把声道合并成单声道。-ar 16000:把采样率统一成 16kHz。-c:a pcm_s16le:用无损 PCM 编码写成 WAV。
为什么要干这件事?因为手机、相机、录屏软件转出来的音频格式五花八门,有 48kHz、有双声道、有 AAC 压缩过的。语音识别模型一般在 16kHz 单声道的无损 WAV 上表现最稳定,识别速度和准确率都更好。这一步虽然看起来多余,但对后面的识别效果影响很大。
3.2 第二步:语音识别生成 SRT 字幕
如果你已经搞定了 autosub 的依赖,可以用它来生成:
autosub -i work/input.wav -o work/input.srt -S zh -D zh -F srt这里的参数分别指定输入音频、输出字幕文件、源语言中文、目标语言中文、输出格式 SRT。autosub 的思路是把音频切成片段,逐段调用语音识别接口,再把结果拼回完整字幕。问题在于它调用的接口需要单独配置密钥和网络,而且识别速度很慢,用起来实在费劲。
所以我日常主力用的还是 whisper:
whisper work/input.wav --language zh --task transcribe --output_format srt --output_dir work这条命令指定了输入音频、语言中文、任务是“转写”而不是“翻译”、输出 SRT 格式、输出目录放到 work 文件夹。运行之后,work 目录下会多出 SRT、TXT、VTT 等文件,核心需要的是 SRT。
模型怎么选?我实测下来,tiny和base模型速度最快但错误率偏高,适合快速验证流程;small模型在中文口语上的表现已经比较可用;medium准确率更好,但对内存和耗时要求更高。如果你视频本身音质清晰、普通话标准,small足够;如果夹杂大量专业术语或者说话人语速快,建议直接上medium。
3.3 第三步:把字幕封装回视频(软字幕)
SRT 文件到手后,有两种方式把它和视频合在一起。第一种是软字幕,也就是把字幕作为独立轨道封装进 MP4,不改变画面像素,速度快、画质无损:
ffmpeg -y -i input.mp4 -i work/input.srt -c copy -c:s mov_text -metadata:s:s:0 language=chi output.mp4-c copy表示视频流和音频流都直接复制,不重新编码,所以这条命令几十秒就跑完。-c:s mov_text是把字幕转成 MP4 支持的字幕格式。-metadata:s:s:0 language=chi设置字幕语言为中文,方便播放器按语言选择。
这种方式的好处是视频质量完全不变,字幕还可以随时关闭或重新替换。缺点是有兼容性风险,比如 QuickTime 默认不显示 mov_text 字幕,部分老播放器也不认。我自己常用的播放器是 IINA 和 VLC,都能正常显示。
3.4 第四步:把字幕烧进画面(硬字幕)
如果追求“任何播放器打开都能看到字幕”,那就直接把字幕烧录进画面里。这一步会重新编码视频,耗时相对较长:
ffmpeg -y -i input.mp4 -vf "subtitles=work/input.srt:force_style='FontName=PingFang SC,FontSize=18,PrimaryColour=&H00FFFFFF,Outline=1,Shadow=1'" -c:v libx264 -preset fast -crf 20 -c:a copy output_hardsub.mp4关键点是subtitles滤镜。它调用 libass 把字幕渲染成图形,再叠加到视频画面上。force_style后面指定的是字幕样式:字体用苹方、字号 18、白色字体、带描边和阴影,确保任何画面上都能看清。
这里有几个我踩过的坑:
字幕文件如果不在当前目录,路径要写对,且路径里有空格、中文的时候容易翻车。最简单的做法是先cd到字幕所在目录,再执行 ffmpeg,或者把路径用引号严格包住。
中文字体问题很常见。FontName=PingFang SC在 mac 上一般能用,但如果系统字体缓存异常,会报Fontconfig error。可以先执行fc-list :lang=zh看看系统识别到了哪些中文字体,再挑一个存在的名字填进去。
SRT 文件必须是 UTF-8 编码,最好不带 BOM,否则烧录时可能乱码。如果你从某些软件导出的字幕文件是 GBK 编码,先用文本编辑器转成 UTF-8 再操作。
3.5 把完整流程写成本机脚本
手动跑一遍两条命令还好,但每次都要敲一长串就烦了。我把整套流程固化成了一个脚本,放到~/bin目录下,以后给任何视频加字幕就一行命令的事:
#!/bin/bash input="$1" base="${input%.*}" mkdir -p work ffmpeg -y -i "$input" -vn -ac 1 -ar 16000 -c:a pcm_s16le "work/${base}.wav" whisper "work/${base}.wav" --language zh --task transcribe --output_format srt --output_dir work ffmpeg -y -i "$input" -i "work/${base}.srt" -c copy -c:s mov_text "${base}_sub.mp4" rm -rf work保存为auto_sub.sh,然后执行chmod +x ~/bin/auto_sub.sh,之后想给视频加字幕,跑一行auto_sub.sh 我的视频.mp4就行。脚本默认做软字幕封装,速度快;想烧录硬字幕,把最后那一条 ffmpeg 命令换成上一小节里的烧录命令即可。
4. 进阶玩法:批量处理与字幕校正
4.1 批量给一堆视频加字幕
命令行最大的优势就是批量。假设你有一个目录全是录屏课程,想全部加上字幕,直接一个循环搞定:
for f in *.mp4; do auto_sub.sh "$f" done或者用 find 更灵活,可以排除已经生成过的文件:
find . -name "*.mp4" -not -name "*_sub*" -exec auto_sub.sh {} \;不过提醒一下,whisper 很吃 CPU/GPU 资源。我在 M 系列芯片的 mac 上一次只跑一个任务,风扇就已经开始响了;Intel 老款 mac 更建议老老实实排队跑,一次开十个并行任务反而可能拖慢整体速度,甚至导致系统卡顿。
4.2 识别错字的快速修正
语音识别再强也会有错字,尤其是专有名词、人名、生僻词。SRT 本质是文本文件,批量修正直接用 sed 正则替换就行:
sed -i '' 's/某某某专业术语/正确写法/g' work/input.srtmac 的 sed 和 Linux 不同,-i后面必须跟一个参数,哪怕是空字符串'',不然会报错。这个细节当初卡了我好一会儿。
如果你的替换规则比较复杂,比如要把某个经常听错的人名全部纠正,也可以写个简单的 Python 脚本处理,逻辑就是读文件、替换、写回。这比在剪辑软件里一条一条改要快太多了。
4.3 时间轴偏移的整体调整
还有一种情况,字幕本身识别得没错,但和画面口型整体对不上,比如所有字幕都晚了 0.3 秒。不管是 whisper 还是 autosub,偶尔都会出现这种整体偏移的问题,尤其是在处理片头、片尾有静音段的视频时。
最简单的处理办法是把 SRT 的时间轴整体前移或后移。写个小脚本,解析 SRT 里的时间戳,把起止时间都加上或减去一个固定偏移,再写回文件。SRT 的时间格式是HH:MM:SS,mmm,处理思路就是把每个时间戳转成毫秒,加上偏移后再转回去。注意字幕结束时间不能超过视频总时长,否则部分播放器会解析异常。
4.4 顺带做一版英文或双语字幕
whisper 有个 translate 模式,可以把中文语音直接翻译成英文字幕输出:
whisper work/input.wav --language zh --task translate --output_format srt --output_dir work生成的英文字幕,翻译质量属于“大致能看懂”的水平,胜在速度快、完全免费。如果你想发布面更广的内容,可以用这条命令先出一版英文底稿,再人工润色一下,比从零翻译省力得多。
如果你需要的是中英双语字幕在同一画面显示,可以把中文 SRT 和英文 SRT 都准备好,再用 ffmpeg 的subtitles滤镜叠加两次,或者用字幕编辑软件合并,这里就不展开细讲了。
5. 常见问题排查与避坑记录
5.1 安装环节的问题
| 现象 | 可能原因 | 解决办法 |
|---|---|---|
brew install ffmpeg卡住不动 | 网络原因或 brew 自动更新太慢 | 配置国内镜像源,或临时设置HOMEBREW_NO_AUTO_UPDATE=1跳过自动更新 |
| ffmpeg 命令找不到 | 安装路径不在 PATH 里 | 执行which ffmpeg检查路径,必要时brew link ffmpeg |
pip install autosub报一堆依赖错误 | 原版 autosub 依赖老旧,与新环境不兼容 | 放弃原版,改用 whisper 方案 |
| whisper 命令找不到 | 虚拟环境没激活 | 重新执行source ~/.whisper-venv/bin/activate |
5.2 字幕生成环节的问题
| 现象 | 可能原因 | 解决办法 |
|---|---|---|
| 首次运行 whisper 下载模型失败 | 网络波动 | 多试几次,或手动下载模型放入~/.cache/whisper |
| 识别出来的文字大量乱码 | 音频采样率太高或声道混乱 | 确认抽音频步骤用了-ar 16000 -ac 1 |
| 识别结果断句奇怪 | 原视频语速太快或背景噪音大 | 尽量使用原声清晰的素材,必要时先做音频降噪 |
autosub 报ModuleNotFoundError | 缺依赖或调用的 API 库版本冲突 | 直接用 whisper 替代自动识别步骤 |
5.3 视频合成环节的问题
| 现象 | 可能原因 | 解决办法 |
|---|---|---|
| 中文字幕烧录成方块 | 字体缺失或字体名不对 | 用fc-list :lang=zh查看系统中文体,再指定正确字体 |
| 烧录时提示找不到字幕文件 | 路径包含空格或中文 | 将字幕文件放到当前目录,或正确转义路径引号 |
| SRT 中文乱码 | 编码不是 UTF-8 | 用文本编辑器把字幕另存为 UTF-8 无 BOM |
| 硬字幕编码非常慢 | 默认编码参数太保守 | 加-preset fast -crf 20,速度和画质比较平衡 |
| 软字幕在播放器里不显示 | mov_text 兼容性差 | 换用 IINA/VLC/PotPlayer,或者用硬字幕方案 |
| 输出 MP4 无法播放 | 播放器太老,不认识 mov_text 字幕轨道 | 换 MKV 容器封装,或者直接烧录硬字幕 |
这套流程我自己用了大半年,最直观的改变是一期视频从“剪完再花两小时对字幕”变成了“剪完挂一条命令,喝杯咖啡回来直接出片”。自动识别不是万能的,专有名词、口音重的地方依旧需要人工扫一遍,但整体效率提升非常明显。最后再分享一个小经验:正式处理长视频之前,一定先用 1 分钟左右的片段跑通全流程,确认字幕出来、合成没问题,再扔给整个素材跑,不然等了一个小时才发现字幕时间轴整体偏移,返工成本很高。