如果你手头有几百个视频或音频文件需要转成 srt 字幕,又不想把素材传到云端做识别,那么“本地AI字幕工具”应该能给你提供一个新的思路。这个方向不是要你从零训练语音模型,而是把现成的开源语音识别模型和一点工程化手段组合起来,解决批量转写和长音频切分的问题。
这篇文章会围绕一个真实场景展开:本地部署、批量转写、长音频自动切分、断点续跑。我会直接给出可运行的做法和脚本思路,重点解释为什么需要做工程化处理,而不是简单调用一条命令行。
1. 这篇文章真正要解决的问题
先看一个非常常见的处理流程:某位做课程录制的朋友,一个月内攒下 80 段视频,每段大约 40 到 90 分钟,现在需要全部转成 srt 字幕文件,用于后续二次剪辑和字幕包装。他最开始的想法是手动分段上传到在线工具,结果试了几段之后发现几个问题:
- 上传和下载耗时太长,高峰期还要排队。
- 在线工具对单段音频有时长限制,超过 30 分钟就被拒绝。
- 部分长录音识别到一半断掉,又要重新上传重新转写。
- 隐私顾虑比较明显,内部课程的录音不方便长期留在第三方服务器上。
这些问题其实很有代表性。批量音视频转文字需求经常出现在课程制作、会议录音整理、播客剪辑、短视频字幕包装和内容审核等场景,但这些场景并不都适合使用云端在线服务。本地 AI 字幕工具要解决的正是这一类问题:在不上传素材的前提下,利用本地开源模型把音频文件批量转成 srt 字幕,同时通过工程手段处理长音频和意外中断的问题。
这篇文章的目标读者是:有批量转写需求、介意数据隐私、想省去云端排队时间、并且有一定命令行基础的音视频工作者或后端开发者。读完本文,你可以得到一个经过拆解的本地转写方案,包括环境准备、模型选型、长音频切分思路、续跑机制和一个简单可用的批处理脚本。
我的判断是:本地 AI 字幕工具最大的价值不是“免费”或者“离线”,而是把转写过程重新变成一种可控的资源消耗。你拥有自己机器的完整控制权,可以随时暂停、续跑、调整参数、批量重试,这是在线工具很难做到的。
2. 本地AI字幕工具的核心概念与适用场景
开始写脚本之前,有几个概念必须先理清楚。如果这里的概念混了,后面做批量处理和参数调优的时候就容易踩坑。
2.1 什么是 SRT 字幕文件
srt 是一种非常常见的字幕文件格式,全称是 SubRip Text,本质上就是一个带时间轴标记的纯文本文件。一个标准 srt 文件的内容大致是这样的:
1 00:00:00,000 --> 00:00:03,200 大家好,今天我们来讲解本地AI字幕工具的部署方法。 2 00:00:03,200 --> 00:00:07,800 首先你需要准备一台安装了Python的电脑。可以看到,每个字幕块由序号、时间轴和字幕文本三部分组成。时间轴的格式是“小时:分钟:秒,毫秒”,箭头两侧分别表示开始时间和结束时间。srt 文件可以直接被市面上绝大多数播放器识别,也可以导入剪映、Premiere Pro、Final Cut Pro 等剪辑软件使用。
这里需要提醒一个容易混淆的点:很多做直播推流的朋友会经常看到“SRT”这个词,那是 Secure Reliable Transport 协议,是一种视频传输协议,而字幕文件 srt 是 SubRip Text。两者缩写一样,但用途完全不同,不要在搜索资料和查阅文档时搞混。
2.2 本地语音识别模型的工作原理
本地 AI 字幕工具的核心是语音识别模型。目前使用最广泛的开源模型是 OpenAI 开源的 Whisper 系列模型,它能够将音频文件转录为文本,并且支持多语言识别。Whisper 模型的思路和传统语音识别不太一样:它把音频切分成 30 秒左右的窗口,内部通过编码器提取音频特征,再由解码器逐步生成对应的文本内容。
以 faster-whisper 为例,这是 Whisper 模型的一个高效复现版本,底层使用 CTranslate2 推理框架进行加速,相比原始实现有更高的推理速度和更低的内存占用。faster-whisper 支持按需加载模型,既可以选择 CPU 运行,也可以使用 GPU 加速,非常适合本地批量任务。
不过这里要强调一个关键认知:模型本身只能完成“听到声音并转成文字”这一步。如果音频文件很长,直接把一段 90 分钟的音频丢给模型,不一定能在合理时间内跑完,甚至可能因为内存或显存超限导致失败。这就是长音频切分存在的意义——把一个大的转写任务拆成多个可以独立完成的子任务。
2.3 长音频切分的两种主流思路
处理长音频有两种常见方案。
第一种方案是时间轴切分法。先用 FFmpeg 将长音频按固定时间切分,例如每 30 秒或每 5 分钟一段,然后逐段调用语音识别模型进行转写,最后再把识别出的文字片段按原来的时间轴拼接回完整的 srt 文件。这种方案的好处是逻辑简单、容易实现、出错后只需要重跑某个失败的片段;缺点是可能在一个片段中间切断一句话,对识别结果有一定影响。
第二种方案是静音检测切分法。FFmpeg 的 silencedetect 滤镜可以检测音频中的静音段,然后根据静音位置切分音频,尽量保证一句话或者一个语义单元不被截断。这种方法在播客、讲座录音等说话停顿明显的场景中效果很好,但如果素材是密集对话或者背景噪音很大,静音检测的准确率会下降。
第三种方案,也是更稳妥的方案:使用模型自带的 VAD 功能直接处理长音频。faster-whisper 内置了 Silero VAD 支持,开启之后模型会先检测长音频中的语音片段,跳过静音段,再逐步转录。这种方式不需要手动切音频,时间轴也能自动化对齐,是目前工程上推荐的做法。
在实际项目中,这三种方案并不互斥。如果音频平均长度在 30 分钟内,我会优先考虑 faster-whisper 的 VAD 能力,直接跑完整音频。如果音频非常长,且机器内存有限,或者需要并行处理多段音频,则可以先按固定时长切分,再配合 VAD 做语义级识别。
2.4 续跑机制为什么重要
批量转写任务通常耗时很长。一个 30 分钟的视频,在使用 small 或 base 级别模型时,CPU 模式下可能需要 4 到 10 分钟才能完成转写,如果使用 large 模型,时间会更久。一旦某个文件因为断电、内存不足、显存压低或临时故障中断,如果没有续跑机制,可能就要从头开始把所有文件重新跑一遍。
续跑的工程思路其实很简单:在遍历所有文件之前,先检查输出目录中是否已经存在对应的 srt 文件;如果存在,则认为这个文件已经处理完成,直接跳过;如果不存在,才处理该文件。这种方式也叫“幂等处理”,它是批处理任务中最基础也最有效的容错手段之一。更精细一点的续跑方案是把已经处理完的文件名记录到一个 progress.log 文件中,即使 srt 文件因为某种原因被删除,也能知道哪些任务已经完成。
2.5 适用场景与不适合的场景
适合使用本地 AI 字幕工具的场景:
- 批量处理课程视频、会议录音、采访音频、播客音频,生成带时间轴的字幕文件。
- 对数据隐私要求严格,不允许将音视频上传到外部服务器的企业内部项目。
- 需要配合剪辑软件二次编辑,要求输出标准 srt 格式。
- 需要在无网环境或内网环境中完成转写的场景。
- 批量任务量大,在线服务有单条时长限制或收费限制的场景。
不适合使用本地 AI 字幕工具的场景:
- 需要实时字幕或极低延迟转写,本地模型暂时不适合这类需求。
- 音频质量非常差、多人重叠说话严重,此时即使使用 large 模型,识别效果也可能不理想。
- 要求百分之百准确率,由于背景噪音和口音影响,AI 转写仍需人工校对。
- 完全不熟悉命令行并且也没有意愿接触 Python 环境,这类用户更适合使用图形化工具。
3. 环境准备与前置条件
本机的运行环境需要满足以下基础条件。
硬件方面,CPU 或 GPU 都可以运行,但如果素材量大或音频较长,建议至少 16GB 内存,GPU 方面 NVIDIA 显卡搭配 CUDA 环境可以明显加速。操作系统的差异不大,Windows、macOS、Linux 都能跑通,本文以 Windows 和 Linux 通用的命令行操作为例。
软件方面,需要提前安装:
- Python 3.9 或更高版本,具体版本以官方文档为准。
- FFmpeg,用于音频提取和音频信息探测。
- faster-whisper 包,通过 pip 安装。
- 可选安装 CUDA 和 cuDNN,用于 GPU 加速。
3.1 安装 faster-whisper
打开终端,执行以下命令:
pip install faster-whisper如果网络环境不稳定,可以使用国内镜像源加速:
pip install faster-whisper -i https://pypi.tuna.tsinghua.edu.cn/simple安装完成后可以验证一下版本:
python -c "import faster_whisper; print(faster_whisper.__version__)"这里的重点是,faster-whisper 默认会在第一次调用时从 Hugging Face 下载模型权重。如果你的网络无法直接访问,需要提前配置好代理或设置镜像环境变量。如果你所在的网络环境无法访问外部模型库,也可以手动下载模型权重文件,放入本地缓存目录,或者修改模型加载路径指向本地目录。这个内容后面会单独说明。
3.2 安装 FFmpeg
FFmpeg 是音视频处理的事实标准工具集。在 Windows 上,可以从 FFmpeg 官网下载对应的 release 包,解压后把 bin 目录加入系统 PATH 环境变量;在 Linux 上可以使用包管理器安装:
sudo apt update sudo apt install ffmpegmacOS 上可以用 Homebrew 安装:
brew install ffmpeg安装完成后,确认 ffmpeg 命令可用:
ffmpeg -version如果能看到版本号输出,说明安装成功。
3.3 模型选型建议
faster-whisper 支持多种模型尺寸,从小的 tiny、base,到中等大小的 small、medium,再到大型的 large 系列。模型越大,识别准确率越高,但推理时间和资源占用也越大。这里给出一个选型参考:
| 模型大小 | 参数量级 | 内存占用 | 识别速度 | 推荐场景 |
|---|---|---|---|---|
| tiny | 很小 | 低 | 极快 | 快速测试、预览效果 |
| base | 较小 | 低 | 很快 | 简单音频测试、噪音较低的环境 |
| small | 中等 | 中等 | 较快 | 清晰录音、中文普通话 |
| medium | 较大 | 较高 | 中等 | 多语言混排、口音稍重的音频 |
| large-v3 | 很大 | 高 | 较慢 | 对准确率要求高的正式素材 |
中文识别通常建议从 small 或 medium 起步,如果发现错字率偏高,再升级到 large-v3。如果只是验证流程,先用 tiny 或 base 跑通整个链路,以便快速看到效果。这个建议非常重要,因为很多第一次接触本地转写的人,直接下载 large-v3 模型,结果发现运行速度慢到难以接受。
3.4 验证前置条件是否满足
在真正开始批量转写前,建议先手动准备一个 10 秒左右的测试音频,用最简短的命令验证环境是否正常。如果这一关过不去,后面成百上千的文件就别批处理了,先解决环境问题更实际。
ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 test.mp3如果命令返回音频的时长秒数,说明 FFmpeg 链路正常。接下来再用一小段音频跑通 faster-whisper,再进入全量任务。
4. 核心流程拆解
整个本地 AI 字幕工具的批处理流程可以拆成四个阶段。
4.1 扫描输入目录
第一步是扫描全部需要处理的音视频文件。这个阶段主要负责确定素材类型和数量。常见的输入格式包括 mp3、wav、m4a、flac、mp4、mov、mkv 等。大多数音视频文件的音频轨道都可以被提取出来做语音识别,视频画面本身并不参与转写,所以输入目录中放视频文件或音频文件都可以。
建议在项目目录下建立两个子目录,一个用于存放原始素材,一个用于存放输出字幕。素材放一个目录,输出放一个目录,避免脚本把已经生成的 srt 文件当成输入文件再处理一遍。
这里最核心的工程决策是“通过输出文件是否存在来判断任务是否完成”。脚本每处理完一个文件,就给这个文件生成一个同名 .srt 文件,放在输出目录里。下次脚本启动时,只需要检查输出目录里有没有对应的 .srt 文件,如果有就跳过,如果没有就处理。这个机制保证了断电、异常退出、人为终止之后,重新运行脚本能从断点继续跑,不需要从头开始。
为了方便兼容不同播放器和剪辑软件,建议输出文件与输入文件保持同名,仅扩展名不同。例如 test.mp4 对应 test.srt,这就避免了后期改名和对应的问题。
4.2 提取音频并获取元数据
在调用语音识别模型之前,需要先把视频中的音频提取出来,放到临时目录中。如果输入本身就是音频文件,这个步骤可以跳过,但是用 FFmpeg 统一处理也没有什么问题。提取音频时建议统一转为 wav 格式,采样率保持在 16kHz,这种格式对后续识别最省资源。
同时要获取音频的时长信息,便于判断是否需要做长音频切分。如果音频时长超过某一个阈值,例如 30 分钟,就自动启用切分策略;如果小于阈值,则直接交由语音模型处理。
这里的一个技术细节是:FFmpeg 提取出来的临时音频和原始视频的时间轴是严格对齐的。这个时间轴在后续生成 srt 字幕时非常关键,因为字幕的时间必须对应到原始文件的播放时间轴,而不能只是临时音频的时间轴。如果临时音频是从零开始截取的,那两者时间轴一致;如果对原始视频做了裁剪,那么需要额外加上裁剪偏移量。
4.3 执行语音识别并生成 srt
把音频输入 faster-whisper 模型后,模型会返回若干个带时间轴的文本片段。每个片段包含开始时间、结束时间和识别文本。接下来要做的就是把片段列表按顺序格式化输出为 srt 文件内容。
在这个过程中可以调整几个参数:
- language:指定音频语言,例如 zh 表示中文,en 表示英文。
- beam_size:解码时的搜索宽度,通常设为 5。
- vad_filter:开启 VAD,跳过静音段,提升长音频的处理效率。
- word_timestamps:如果需要在剪辑软件中做逐字对齐,可以开启此选项。
生成 srt 文件时需要注意格式化规则:时间不得重叠、字幕序号连续递增、时间字符串必须补零到三位毫秒。
4.4 错误处理与断点续跑
每个文件转写完成后,脚本立刻在输出目录中生成完整的 srt 文件。如果某个文件识别失败,脚本不应中断,而是把错误信息写入 error.log,继续处理下一个文件。这样可以最大限度减少人工干预时间。
续跑的另一个关键点是确保文件的读写是完整的。如果一个 srt 文件只写入了一半就发生进程崩溃,下次扫描时该文件存在但内容不完整,脚本会误判为已完成。因此,建议使用“先写入临时文件,再重命名”的方式。具体做法是:先把字幕内容写入 output_dir/test.srt.tmp,只有当写入成功并完成刷盘后,再将 test.srt.tmp 重命名为 test.srt。通过这个方式,可以避免不完整文件被误判的问题,让续跑更加可靠。
5. 完整示例代码实现
这一部分直接给出一个可运行的最小示例脚本。脚本命名为 batch_srt.py,放在项目根目录下。为了后续维护方便,建议把这几个功能模块分开写:文件扫描、时长获取、字幕生成、主流程控制。
5.1 文件扫描函数
这个函数负责遍历输入目录,筛选出常见的音视频文件扩展名,并检查哪些文件已经生成过 srt 字幕。
# 文件路径:batch_srt.py import os import subprocess import sys from pathlib import Path # 常见音视频扩展名,可以根据实际素材类型扩充 AUDIO_EXTENSIONS = {".mp3", ".wav", ".m4a", ".flac", ".aac", ".ogg", ".opus"} VIDEO_EXTENSIONS = {".mp4", ".mov", ".mkv", ".avi", ".flv", ".wmv", ".ts"} SUPPORTED_EXTENSIONS = AUDIO_EXTENSIONS | VIDEO_EXTENSIONS def scan_input_files(input_dir): """ 扫描输入目录,返回尚未生成 srt 字幕的文件列表。 判断依据:输出目录中是否存在同名 .srt 文件。 """ input_path = Path(input_dir) files = [] for file in input_path.iterdir(): if file.is_file() and file.suffix.lower() in SUPPORTED_EXTENSIONS: files.append(file) return files def build_srt_path(output_dir, media_path): """ 根据媒体文件路径构造对应的 srt 输出路径。 例如 test.mp4 -> output_dir/test.srt """ return Path(output_dir) / (media_path.stem + ".srt")这个模块的设计重点是“同名 srt 作为完成标记”。后续如果需要改变输出目录,只需修改 build_srt_path 函数即可,不会影响主流程。
5.2 获取音频时长函数
这个函数使用 ffprobe 读取媒体的时长信息。与直接使用 Python 的音频解析库相比,ffprobe 更通用,几乎所有主流格式都能直接解析。
def get_media_duration(media_path): """ 使用 ffprobe 获取媒体文件时长(单位:秒)。 如果获取失败,返回 0 并打印错误信息。 """ cmd = [ "ffprobe", "-v", "error", "-show_entries", "format=duration", "-of", "default=noprint_wrappers=1:nokey=1", str(media_path) ] try: result = subprocess.run(cmd, capture_output=True, text=True, check=True) return float(result.stdout.strip()) except subprocess.CalledProcessError as e: print(f"[ERROR] 获取时长失败: {media_path}, {e.stderr}") return 0.0ffprobe 命令输出的是媒体容器总时长,单位是秒,通常是十进制小数。
5.3 语音识别与字幕生成
这里使用 faster-whisper 完成核心转写任务。为了减少重复加载模型的耗时,整个脚本只加载一次模型,而不是每个文件都重新加载一遍。模型对象在应用启动时初始化,之后循环处理各个文件时复用同一份模型权重,能大幅缩短批量任务的整体耗时。
def transcribe_and_write_srt(model, media_path, srt_path, language="zh", beam_size=5): """ 执行语音识别,并将识别结果写入 srt 文件。 采用临时文件 + 原子重命名方式,避免不完整文件被误判为已完成。 """ temp_srt_path = srt_path.with_suffix(srt_path.suffix + ".tmp") segments, info = model.transcribe( str(media_path), language=language, beam_size=beam_size, vad_filter=True, ) with open(temp_srt_path, "w", encoding="utf-8") as f: index = 1 for segment in segments: start = format_srt_time(segment.start) end = format_srt_time(segment.end) text = segment.text.strip() if not text: continue f.write(f"{index}\n") f.write(f"{start} --> {end}\n") f.write(f"{text}\n\n") index += 1 # 写入成功后再重命名为正式 srt 文件,保证原子性 os.replace(temp_srt_path, srt_path) print(f"[OK] 已生成: {srt_path.name}, 字幕段数: {index - 1}")时间格式化函数是 srt 文件最基础的部分,需要注意毫秒补零。
def format_srt_time(seconds): """ 将秒数格式化为 srt 时间轴格式:HH:MM:SS,mmm 例如 3661.5 -> 01:01:01,500 """ millis = int(round(seconds * 1000)) hours = millis // 3600000 minutes = (millis % 3600000) // 60000 secs = (millis % 60000) // 1000 msecs = millis % 1000 return f"{hours:02d}:{minutes:02d}:{secs:02d},{msecs:03d}"这里的重点在于时间格式化必须补零到位。srt 标准要求小时至少两位,毫秒必须三位。很多初写者漏掉毫秒补零,导致某些播放器和剪辑软件无法正确解析时间轴。
5.4 主流程控制
主函数负责初始化模型、扫描文件、循环处理,并将处理结果汇总输出。
def main(): input_dir = sys.argv[1] if len(sys.argv) > 1 else "input" output_dir = sys.argv[2] if len(sys.argv) > 2 else "output" language = sys.argv[3] if len(sys.argv) > 3 else "zh" os.makedirs(output_dir, exist_ok=True) # 初始化模型,只加载一次 from faster_whisper import WhisperModel model = WhisperModel("small", device="cpu", compute_type="int8") files = scan_input_files(input_dir) pending_files = [] already_done = 0 for media_path in files: srt_path = build_srt_path(output_dir, media_path) if srt_path.exists(): already_done += 1 continue pending_files.append(media_path) print(f"[INFO] 输入目录扫描完成,共 {len(files)} 个文件,其中已完成 {already_done} 个,待处理 {len(pending_files)} 个。") failed = [] for media_path in pending_files: try: srt_path = build_srt_path(output_dir, media_path) transcribe_and_write_srt(model, media_path, srt_path, language=language) except Exception as e: print(f"[FAILED] {media_path.name}: {e}") failed.append(media_path.name) print("[INFO] 全量任务结束。") if failed: print(f"[WARN] 失败 {len(failed)} 个文件:") for name in failed: print(f" - {name}") else: print("[OK] 没有失败文件。") if __name__ == "__main__": main()5.5 运行脚本
终端进入项目目录,执行命令:
python batch_srt.py input output zh参数说明:
- input:原始素材目录。
- output:字幕输出目录。
- zh:语言代码,表示按中文识别。
运行过程中,终端会逐步输出每个文件的处理状态。如果中断了,直接再次执行同样的命令,脚本会自动跳过已经生成 srt 的文件。
如果想把输出完整保存到日志,方便后续排查,可以这样运行:
python batch_srt.py input output zh > run.log 2>&1这样一来,即使终端关闭或者 SSH 断开,任务也会正常继续执行,日志记录在 run.log 中。
6. 运行结果与效果验证
脚本第一次运行结束之后,需要验证三件事。
6.1 验证 srt 文件是否生成
进入 output 目录:
ls -la output/正常情况应该能看到若干个 .srt 文件,例如:
first-lesson.srt second-lesson.srt third-lesson.srt6.2 验证 srt 文件内容格式
用文本编辑器打开任意一个 srt 文件,确认时间轴和文本结构是否正常。一个标准 srt 文件头部应该类似这样:
1 00:00:00,000 --> 00:00:03,240 今天我们来讲一下本地AI字幕工具的部署方法。 2 00:00:03,240 --> 00:00:07,880 首先你需要有一台安装了Python的电脑。如果时间轴出现了负数或者文本为空,说明识别或格式化环节有异常,需要查看该文件对应的原始音频。
6.3 验证批量续跑是否正常
再次运行同一条命令:
python batch_srt.py input output zh终端应该输出类似信息:
[INFO] 输入目录扫描完成,共 80 个文件,其中已完成 80 个,待处理 0 个。 [INFO] 全量任务结束。 [OK] 没有失败文件。这就验证了续跑机制生效。更真实的中断场景测试方法是:在处理一批大文件时手动按 Ctrl+C 中断程序,然后再次执行命令。脚本会精确跳过已经写完 srt 的文件,只处理剩下的部分。如果你想验证原子性保护的作用,可以在程序运行过程中强制 kill 进程,然后查看 output 目录中是否有 .tmp 残留文件。如果有,说明中断发生在写入临时文件阶段,正式 srt 仍未生成,下次运行会重新处理该文件,从而避免把损坏文件误判为完成。
如果每个文件都处理失败,首先检查模型是否加载成功,常见原因是模型权重下载失败或内存不足。其次检查音频时长是否获取为 0,这通常意味着 FFmpeg 没有正确安装或文件损坏。
7. 常见问题与排查思路
批量转写任务在真实运行中会出现各种意想不到的问题。下面这张表格列出了一些高频问题以及对应的排查方式。
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 程序启动后一直卡在模型加载阶段 | 模型权重下载缓慢或网络受限 | 查看终端日志,确认卡在 download 阶段 | 手动下载模型到本地缓存,或设置镜像源 |
| 识别结果全是乱码或空白 | 音频语言与 language 参数不匹配 | 检查音频语言,对照中文普通话和粤语的区别 | 修改 language 参数,或使用自动检测 |
| 所有视频都识别为 0 秒 | FFmpeg / ffprobe 未安装或未加入 PATH | 执行 ffprobe -version 检查 | 安装 FFmpeg 并配置环境变量 |
| 某个长视频转写时内存占用过高 | 音频未被切分,模型一次性处理过长片段 | 观察任务管理器中的内存曲线 | 开启 VAD,或对音频做固定时长预切分 |
| 中途按 Ctrl+C 结束后,重新运行仍从头开始 | 脚本未正确判断“已完成”状态 | 检查 output 目录中 srt 文件是否存在 | 确认 srt 文件写入完成,而不是存在 .tmp 残留 |
| srt 文件无法导入剪辑软件 | 时间轴格式不正确,或文本中包含非法字符 | 用文本编辑器打开 srt 检查时间格式 | 统一使用 HH:MM:SS,mmm 格式 |
| 中文识别错别字较多 | 模型选型过小,或音频噪音偏大 | 用一小段标准普通话测试不同模型 | 升级到 medium 或 large-v3,训练前先做降噪 |
这里重点解释两个问题。
第一个是模型权重下载失败。faster-whisper 默认从 Hugging Face 下载模型,如果你的机器无法访问该网站,程序会长时间卡住或直接报错。可行的解决办法是提前在能访问网络的机器上下载模型目录,然后拷贝到本地指定目录,使用时修改加载路径:
model = WhisperModel("/local/path/to/model", device="cpu", compute_type="int8")或者设置 Hugging Face 镜像环境变量:
export HF_ENDPOINT=https://hf-mirror.com第二个是 GPU 加速不生效。如果你的机器有 NVIDIA 显卡,且安装了 CUDA,可以将 device 参数改为 "cuda":
model = WhisperModel("small", device="cuda", compute_type="float16")但是要注意:只有当你确认 PyTorch、cuDNN 和显卡驱动与当前 CUDA 版本兼容时,才建议使用 GPU 模式。否则可能会出现“程序找不到 CUDA 设备”的报错,反而比 CPU 模式更难排查。
8. 最佳实践与工程建议
这一部分的内容是把本地 AI 字幕工具从“能跑”提升到“稳定、可维护、适合工程化使用”的关键。
8.1 做好目录规划
建议的项目目录结构如下:
project/ ├── input/ # 原始素材,只放待处理的音视频 ├── output/ # 生成的 srt 字幕 ├── temp/ # 临时音频文件,处理完成后可清理 ├── logs/ # 运行日志和错误日志 └── batch_srt.py # 批处理脚本input 和 output 必须严格分开,避免脚本把已经生成的 srt 文件当作素材处理。temp 目录用于存放 FFmpeg 提取出的临时音频,任务完成后可以用脚本统一清理,也可以定期手工删除。
8.2 添加重试机制
上面的脚本已经处理了“跳过已完成”的情况,但还缺少对“单文件失败重试”的支持。更健壮的方法是:当某个文件转写失败时,把它记入 failed_list.txt;下一轮运行时,脚本读取该文件,只处理这些失败项。这个机制可以用一个简单的 txt 文件完成,不需要引入数据库。
8.3 日志分级与进度记录
批量任务跑一次可能几个小时,如果没有任何日志,用户无法知道当前进度。建议在脚本中加入进度条或定期输出统计信息。例如每处理完一个文件,输出:
[INFO] 已完成 12/80,当前文件:speech_012.mp4,耗时 32 秒同时把每条处理记录追加写入 logs/process.log,方便事后审计。如果任务在凌晨自动跑,日志是第二天排查问题的主要依据。
8.4 采样率与格式统一
FFmpeg 提取音频时,建议统一转换为 wav 格式,采样率 16kHz,单声道。这样做的好处是:
- 减少 Audio 数据量,降低识别耗时。
- 避免原始视频中多声道混音导致的语义混乱。
- 保证转写系统输入格式稳定,避免某些模型对特定格式支持不良的问题。
提取音频的命令示例如下:
ffmpeg -i input.mp4 -ar 16000 -ac 1 -vn temp/input.wav在批量脚本中,可以在调用 model.transcribe 之前先执行这段 FFmpeg 命令,以统一音频格式。
8.5 使用批量重命名工具配合字幕归档
批量转写完成后,经常需要对字幕文件做规范化命名,例如将 test.mp4 对应的 test.srt 重命名为 “第01集.srt” 或者加上日期前缀。此时可以配合常用的批量重命名工具或 Python 脚本一次性完成。建议在命名时保持同一个批次内文件名排序一致,避免剪辑软件导入顺序错乱。
8.6 关于临时文件的清理
长音频批处理过程中会产生大量临时音频文件,如果磁盘空间有限,建议在每一个文件处理完成之后,立即删除该文件对应的临时 wav 音频,只保留原始素材和 srt 字幕。删除临时文件可以使用 Python 的 os.remove,但要注意放在错误处理之后,不要因为删除临时文件出错导致整个任务中断。
8.7 安全与隐私提醒
本地转写方案的最大优势是素材不离开本机。但你仍然需要注意以下几点:
- 如果使用第三方镜像下载模型,注意检查模型来源是否可信,避免被投毒。
- 不要在共享服务器或公共环境中处理包含敏感信息的音频而不加访问权限。
- 生成的 srt 文件同样包含语音内容,如果原始音频涉及隐私,srt 也不应该随意外传。
- 生产环境或团队协作时,建议以最小权限原则配置服务器账号,只开放必要的目录读写权限。
- 涉及数据库、认证、权限等与本文无关的操作时,同样应该先在测试环境验证再执行,避免直接在生产环境做破坏性行为。
- 批量删除临时文件或旧字幕前,先看一遍过滤规则,确认不会误删还未处理的原始文件。
8.8 模型缓存与版本管理
如果你需要同时管理多个模型版本,可以在项目目录下建立 models 目录,把不同版本的模型分别存放:
models/ ├── small/ ├── medium/ └── large-v3/然后脚本中通过命令行参数指定模型路径:
python batch_srt.py input output zh --model models/medium这样可以在不同任务之间灵活切换模型,也方便团队成员共用同一套模型缓存,避免每台机器都重复下载几个 GB 的权重文件。
9. 关于本地AI字幕工具的实际经验与后续方向
回到最初的问题:如果手头有 80 个视频需要批量转成 srt,这套方案能不能解决?
从实际部署的角度看,能解决,而且方式可复现、可扩展。你不需要在每台机器上都配置一套相同的环境,只要把 Python 脚本、FFmpeg 和模型目录拷贝过去,就可以在另一台机器上跑同样的任务。批量任务中最重要的不是模型选择的“最优解”,而是“可恢复性”和“可观察性”——任务跑了三个小时后中断,你不希望从头再来,这就是续跑机制在工程上的核心价值。
这套方案的前提是,你需要接受“AI 转写不完美”这个事实。即使是 large-v3 模型,在背景噪音、口音、专业术语较多的情况下,也会出现少量错字。对字幕要求较高的场景,仍需要加一道人工校对环节。校对时可以配合 srt 文件编辑工具或常见的字幕调整软件,把时间轴和文本一起检查。
另外值得说明的是,本地语音识别模型的生态还在持续迭代。模型量化、推理框架加速、VAD 改进、标点恢复、说话人分离这些能力都在逐步完善。如果只是做“音频到文字”这一步,faster-whisper 已经足够稳定;如果你想进一步做到“多说话人分段”,或者“字幕样式批量美化”,那就要在转写完成之后,针对 srt 文件做第二轮处理了。这些场景中,批量处理思想是相通的:先对单个文件处理建立模板,再把“文件是否存在”作为任务是否完成的标记,最后统一检查输出结果的质量。
在真正开始处理长音频之前,建议先用一段含明确标题和章节结构的音频测试一遍,比如一个 5 分钟的课程片段。确认模型识别质量、srt 时间轴和剪辑软件兼容性都没问题,再触发全量批量任务。这样可以避免花大量时间处理完一批文件后,才发现原来格式或模型参数选错了,造成重复劳动。
如果你准备把这个方案接入自己的日常生产流程,可以考虑把脚本封装成带命令行参数的独立工具,增加进度日志、失败重试、语言自动检测和字幕格式校验功能。然后就可以把它当作一条“音频进来,字幕出去”的基础管线来用了。