提示

返回博客列表

下载了外语视频没字幕?用 Whisper 本地部署 AI 语音转字幕,批量生成中文字幕

下载了外语视频没字幕?用 Whisper 本地部署 AI 语音转字幕,批量生成中文字幕

你下载了一批 YouTube 上的技术演讲,全是英文没字幕,听得一知半解。或者你录了几小时的会议音频,需要整理成文字。传统方案要么花钱(讯飞/阿里云语音识别 API 按分钟计费),要么效果差(免费在线工具的识别准确率惨不忍睹)。OpenAI 开源的 Whisper 模型改变了这个局面——它在本地运行,不花一分钱,识别准确率吊打绝大多数付费服务,而且支持 99 种语言。这篇文章从安装到批量处理,教你用 Whisper 给下载的视频自动生成字幕,包括中英文混合识别、时间轴对齐和字幕格式转换。

TL;DR:Whisper 是 OpenAI 开源的语言识别模型,本地运行、免费、支持 99 种语言。推荐用 faster-whisper(C++ 重写版,速度提升 4 倍,内存减半)。安装:pip install faster-whisper。核心代码三行:加载模型 → model.transcribe("audio.mp3") → 输出带时间戳的文本。生成字幕后用 FFmpeg 烧录到视频或导出 SRT 文件。批量处理建议写脚本遍历目录,配合 ffmpeg 先提取音频再识别。

目录

一、Whisper 是什么,为什么选它

1.1 和其他方案对比

方案 费用 准确率 语言支持 离线运行 速度
Whisper (large-v3) 免费 ⭐⭐⭐⭐⭐ 99 种 需要 GPU
讯飞语音识别 ¥3-8/小时 ⭐⭐⭐⭐ 中文为主
阿里云语音识别 ¥2-5/小时 ⭐⭐⭐⭐ 中英日韩
剪映自动字幕 免费 ⭐⭐⭐ 中英 ❌(需联网)
网易见外 免费 ⭐⭐ 中英
YouTube 自动字幕 免费 ⭐⭐⭐ 多语言

Whisper 的核心优势: - 完全本地运行,隐私安全(你的音频不会上传到任何服务器) - 对带口音的英语、方言中文的识别效果远超商业 API - 开源社区的活跃度极高,各种优化版本层出不穷

1.2 Whisper 的几个版本

版本 速度 准确率 显存需求 推荐场景
whisper (原版) 基准 ⭐⭐⭐⭐ 10GB (large) 简单使用
faster-whisper 4x ⭐⭐⭐⭐ 5GB (large) 推荐
whisper.cpp 6x ⭐⭐⭐ 2GB (medium) CPU 推理
whisperX 3x ⭐⭐⭐⭐⭐ 8GB 需要词级时间戳
insanely-fast-whisper 10x ⭐⭐⭐⭐ 6GB 超长音频

推荐 faster-whisper:速度是原版的 4 倍,显存只用一半,准确率几乎没损失。如果只有 CPU,用 whisper.cpp。

二、安装与模型选择

2.1 安装 faster-whisper

# 安装(会自动安装 CUDA 依赖)
pip install faster-whisper

# 如果只用 CPU
pip install faster-whisper
# 运行时指定 device='cpu'

2.2 模型选择

模型 参数量 显存需求 磁盘空间 英文准确率 多语言准确率 推荐场景
tiny 39M ~1GB ~150MB 快速测试
base 74M ~1GB ~300MB 中低 实时转录
small 244M ~2GB ~1GB 中高 一般使用
medium 769M ~5GB ~3GB 日常推荐
large-v3 1.55B ~10GB ~6GB 极高 极高 最佳质量
turbo 809M ~6GB ~3GB 极高 速度质量平衡
选择建议:
- 有 NVIDIA 显卡(6GB+ 显存):large-v3-turbo(速度快且质量接近 large-v3)
- 有 NVIDIA 显卡(4GB 显存):medium
- 只有 CPU:small + whisper.cpp(或者用 faster-whisper 的 int8 量化)
- 苹果 M1/M2/M3:large-v3(Metal 加速,速度很快)

2.3 验证安装

from faster_whisper import WhisperModel

# 首次运行会自动下载模型
model = WhisperModel("medium", device="cuda", compute_type="float16")
print("安装成功!")

三、基础用法:10 分钟上手

3.1 识别一个音频文件

from faster_whisper import WhisperModel

# 加载模型(首次运行会自动下载)
model = WhisperModel("medium", device="cuda", compute_type="float16")

# 转录音频
segments, info = model.transcribe("audio.mp3", language="zh")

print(f"检测到语言: {info.language} (概率: {info.language_probability:.2f})")
print(f"音频时长: {info.duration:.0f} 秒")
print()

# 输出带时间戳的文本
for segment in segments:
    print(f"[{segment.start:.2f}s → {segment.end:.2f}s] {segment.text}")

输出示例:

检测到语言: zh (概率: 0.99)
音频时长: 120 秒

[0.00s → 5.20s] 大家好 今天我们来聊聊视频编码
[5.50s → 12.30s] 你有没有想过 为什么同一个视频在不同平台上的清晰度不一样
[12.80s → 18.60s] 这背后涉及到编码标准 码率控制还有容器格式的选择

3.2 关键参数

segments, info = model.transcribe(
    "audio.mp3",

    # 语言设置
    language="zh",          # 指定语言(不指定则自动检测)
    # language=None,        # 自动检测

    # 识别精度
    beam_size=5,            # beam search 宽度(越大越准但越慢)
    best_of=5,              # 多次采样取最佳

    # 初始提示(提高专业术语识别率)
    initial_prompt="这是一个关于视频编码的演讲,包含术语:H.264, H.265, VP9, AV1, 码率, 帧率",

    # VAD 过滤(跳过静音段)
    vad_filter=True,
    vad_parameters=dict(
        min_silence_duration_ms=500,  # 最少静音时长
    ),

    # 其他
    temperature=0.0,        # 0.0=最确定, 1.0=更多样
    condition_on_previous_text=False,  # 避免幻觉重复
)

四、实战:给视频自动生成字幕

4.1 完整流程

import subprocess
import os
from faster_whisper import WhisperModel
from pathlib import Path


def video_to_subtitle(video_path, output_srt=None, language=None,
                      model_size="medium"):
    """给视频自动生成字幕(SRT 格式)"""
    if output_srt is None:
        output_srt = str(Path(video_path).with_suffix('.srt'))

    # 1. 从视频中提取音频
    audio_path = str(Path(video_path).with_suffix('.wav'))
    extract_audio(video_path, audio_path)

    # 2. 用 Whisper 识别
    print(f"正在识别: {video_path}")
    model = WhisperModel(model_size, device="cuda", compute_type="float16")

    segments, info = model.transcribe(
        audio_path,
        language=language,
        beam_size=5,
        vad_filter=True,
    )

    print(f"语言: {info.language}, 时长: {info.duration:.0f}s")

    # 3. 生成 SRT 字幕文件
    with open(output_srt, 'w', encoding='utf-8') as f:
        for i, segment in enumerate(segments, 1):
            # SRT 时间格式: HH:MM:SS,mmm
            start = format_timestamp(segment.start)
            end = format_timestamp(segment.end)
            text = segment.text.strip()

            f.write(f"{i}\n")
            f.write(f"{start} --> {end}\n")
            f.write(f"{text}\n\n")

    # 4. 清理临时音频
    os.remove(audio_path)

    print(f"字幕已保存: {output_srt}")
    return output_srt


def extract_audio(video_path, audio_path):
    """从视频中提取音频(16kHz 单声道 WAV,Whisper 推荐格式)"""
    subprocess.run([
        'ffmpeg', '-i', video_path,
        '-vn',                     # 不要视频
        '-acodec', 'pcm_s16le',    # 16-bit PCM
        '-ar', '16000',            # 16kHz 采样率
        '-ac', '1',                # 单声道
        audio_path,
        '-y'
    ], capture_output=True, check=True)


def format_timestamp(seconds):
    """秒数 → SRT 时间戳格式"""
    hours = int(seconds // 3600)
    minutes = int((seconds % 3600) // 60)
    secs = int(seconds % 60)
    millis = int((seconds % 1) * 1000)
    return f"{hours:02d}:{minutes:02d}:{secs:02d},{millis:03d}"

4.2 生成不同格式的字幕

def generate_vtt(segments, output_path):
    """生成 WebVTT 格式(用于 HTML5 播放器)"""
    with open(output_path, 'w', encoding='utf-8') as f:
        f.write("WEBVTT\n\n")
        for segment in segments:
            start = format_timestamp(segment.start).replace(',', '.')
            end = format_timestamp(segment.end).replace(',', '.')
            f.write(f"{start} --> {end}\n")
            f.write(f"{segment.text.strip()}\n\n")


def generate_ass(segments, output_path, title="Whisper 生成"):
    """生成 ASS 格式(用于播放器,支持样式)"""
    with open(output_path, 'w', encoding='utf-8') as f:
        f.write("[Script Info]\n")
        f.write(f"Title: {title}\n")
        f.write("ScriptType: v4.00+\n\n")
        f.write("[V4+ Styles]\n")
        f.write("Format: Name, Fontname, Fontsize, PrimaryColour, "
                "OutlineColour, Bold\n")
        f.write("Style: Default,Microsoft YaHei,20,&H00FFFFFF,"
                "&H00000000,0\n\n")
        f.write("[Events]\n")
        f.write("Format: Layer, Start, End, Style, Name, "
                "MarginL, MarginR, MarginV, Effect, Text\n")

        for segment in segments:
            start = format_timestamp(segment.start).replace(',', '.')
            end = format_timestamp(segment.end).replace(',', '.')
            text = segment.text.strip()
            f.write(f"Dialogue: 0,{start},{end},Default,,0,0,0,,{text}\n")

五、批量处理脚本

import os
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor


def batch_generate_subtitles(video_dir, output_dir=None, language=None,
                              model_size="medium", workers=1):
    """批量给目录下所有视频生成字幕"""
    video_exts = {'.mp4', '.mkv', '.webm', '.flv', '.avi', '.mov'}
    output_dir = output_dir or video_dir

    # 收集待处理视频
    tasks = []
    for filepath in Path(video_dir).iterdir():
        if filepath.suffix.lower() in video_exts:
            srt_path = Path(output_dir) / f'{filepath.stem}.srt'
            if srt_path.exists():
                print(f"跳过(已有字幕): {filepath.name}")
                continue
            tasks.append(filepath)

    if not tasks:
        print("没有需要处理的视频")
        return

    print(f"待处理: {len(tasks)} 个视频")

    # 单线程处理(Whisper 本身会充分利用 GPU,多线程反而可能更慢)
    for i, video_path in enumerate(tasks, 1):
        print(f"\n[{i}/{len(tasks)}] {video_path.name}")
        try:
            output = str(Path(output_dir) / f'{video_path.stem}.srt')
            video_to_subtitle(str(video_path), output, language, model_size)
        except Exception as e:
            print(f"  失败: {e}")

    print(f"\n完成!共处理 {len(tasks)} 个视频")


if __name__ == '__main__':
    batch_generate_subtitles(
        './videos/',
        language='zh',       # 中文视频
        model_size='medium',
    )

六、中英文混合识别的技巧

6.1 不指定语言(自动检测)

Whisper 的语言自动检测在大多数情况下很准确。但如果视频是中英文混杂(比如中文技术演讲里夹杂英文术语),自动检测可能摇摆:

# 不指定语言,让 Whisper 自动判断
segments, info = model.transcribe("audio.mp3", language=None)
print(f"检测到: {info.language}")

6.2 用 initial_prompt 引导识别

对于专业术语多的视频,用 initial_prompt 能显著提高准确率:

# 技术视频的提示词
prompt = (
    "这是一段技术演讲,包含以下术语:"
    "API, SDK, Docker, Kubernetes, CI/CD, "
    "微服务, 容器化, 负载均衡, 数据库, "
    "FFmpeg, H.264, H.265, VP9, AV1"
)

segments, info = model.transcribe(
    "audio.mp3",
    language="zh",
    initial_prompt=prompt,
)

initial_prompt 的工作原理:给模型一个"上下文",让它在解码时倾向于生成这些词。不是强制替换,而是提高概率。

6.3 中英混合字幕的分词优化

Whisper 生成的中文字幕默认没有标点。可以用 punctuation 后处理:

# 安装:pip install zhconv
import re
import zhconv

def post_process_zh(text):
    """中文后处理:添加标点、繁简转换"""
    # 繁→简
    text = zhconv.convert(text, 'zh-cn')

    # 简单的标点修复(空格处添加逗号)
    text = re.sub(r'(?<=[\u4e00-\u9fff])\s+(?=[\u4e00-\u9fff])', ',', text)

    # 中英文之间加空格
    text = re.sub(r'([\u4e00-\u9fff])([a-zA-Z])', r'\1 \2', text)
    text = re.sub(r'([a-zA-Z])([\u4e00-\u9fff])', r'\1 \2', text)

    return text

七、字幕后处理:翻译、校对、烧录

7.1 机器翻译字幕

Whisper 本身不做翻译(原版 whisper 有 translate 模式但效果差)。如果要翻译,用独立翻译工具:

# 方案 1:使用 translatepy(免费,Google 翻译后端)
# pip install translatepy

from translatepy import Translator

def translate_srt(input_srt, output_srt, target_lang='Chinese'):
    """翻译 SRT 字幕文件"""
    translator = Translator()

    with open(input_srt, 'r', encoding='utf-8') as f:
        content = f.read()

    # 只翻译文本行(跳过序号和时间轴)
    lines = content.split('\n')
    translated_lines = []

    for line in lines:
        stripped = line.strip()

        # 跳过序号、时间轴、空行
        if (stripped.isdigit() or
            '-->' in stripped or
            not stripped):
            translated_lines.append(line)
        else:
            try:
                result = translator.translate(
                    stripped, target_lang=target_lang
                )
                translated_lines.append(result.result)
            except Exception:
                translated_lines.append(line)

    with open(output_srt, 'w', encoding='utf-8') as f:
        f.write('\n'.join(translated_lines))

    return output_srt

7.2 烧录字幕到视频

# 烧录 SRT 字幕(硬字幕,不可关闭)
ffmpeg -i input.mp4 -vf "subtitles=subtitle.srt" output.mp4

# 烧录 ASS 字幕(支持样式)
ffmpeg -i input.mp4 -vf "ass=subtitle.ass" output.mp4

# 指定字体
ffmpeg -i input.mp4 \
  -vf "subtitles=subtitle.srt:force_style='FontName=Microsoft YaHei,FontSize=20,PrimaryColour=&H00FFFFFF,OutlineColour=&H00000000'" \
  output.mp4

7.3 添加软字幕(可开关,不重编码)

# MKV 格式(支持 SRT/ASS)
ffmpeg -i input.mp4 -i subtitle.srt \
  -c copy -c:s mov_text \
  -metadata:s:s:0 language=zh \
  output.mp4

# 更推荐:封装为 MKV(原生支持 SRT)
ffmpeg -i input.mp4 -i subtitle.srt \
  -c copy output.mkv

八、性能优化与硬件选择

8.1 不同硬件下的速度实测

测试音频:1 小时英文播客,medium 模型

硬件 处理时间 实时倍数
NVIDIA RTX 4090 45s 80x
NVIDIA RTX 3060 (12GB) 1m30s 40x
NVIDIA GTX 1060 (6GB) 3m 20x
Apple M2 Pro 2m15s 27x
Apple M1 4m 15x
Intel i7-13700K (CPU) 18m 3.3x
AMD Ryzen 5600 (CPU) 25m 2.4x

关键发现: - GPU 比 CPU 快 6-20 倍 - Apple Silicon 的 Metal 加速很给力,M2 接近 RTX 3060 - 如果只有 CPU,用 compute_type="int8" 量化能再提速 30-50%

8.2 长音频的分段处理

超过 1 小时的音频,建议分段处理以避免 OOM:

def transcribe_long_audio(audio_path, model, segment_duration=600):
    """分段处理超长音频(每 10 分钟一段)"""
    import librosa

    audio, sr = librosa.load(audio_path, sr=16000)
    total_duration = len(audio) / sr

    all_segments = []
    for start in range(0, int(total_duration), segment_duration):
        end = min(start + segment_duration, int(total_duration))
        chunk = audio[start * sr:end * sr]

        # 保存为临时文件
        import tempfile
        with tempfile.NamedTemporaryFile(suffix='.wav', delete=False) as f:
            import soundfile as sf
            sf.write(f.name, chunk, sr)
            temp_path = f.name

        segments, _ = model.transcribe(temp_path)
        # 调整时间偏移
        for seg in segments:
            seg.start += start
            seg.end += start
            all_segments.append(seg)

        os.remove(temp_path)
        print(f"  已处理: {start//60}-{end//60} 分钟")

    return all_segments

九、合规与温馨提示

  • Whisper 是 OpenAI 开源的合法工具(MIT 许可证),可以自由使用
  • 本地运行意味着音频不会上传到任何服务器,隐私安全
  • 使用 Whisper 转录他人视频内容用于再分发,可能侵犯原作者的著作权
  • 生成的字幕文件同样受版权保护——不要批量生成他人视频的字幕并公开发布
  • 更多讨论见 下载视频算侵权吗?聊聊个人备份与版权的那条线

Whisper 是我用过最省心的 AI 工具之一——装好之后就是三行代码的事,不用注册账号、不用买 API Key、不用盯着用量配额。我现在的习惯是:下载完外语视频后跑一遍 Whisper,生成中文字幕,然后导入 Jellyfin,全家人都能看懂。如果你有 NVIDIA 显卡,推荐直接上 large-v3-turbo,速度快且准确率很高;如果没有显卡,medium + int8 量化在 CPU 上也能用,就是多等一会儿。

本文由 VidDown 技术博客原创发布。VidDown 桌面客户端内置了 Whisper 字幕生成功能——下载视频后可以一键生成中英文字幕,自动烧录或导出 SRT 文件。访问 VidDown 了解更多。

想亲手试试?用 VidDown 一键解析下载

粘贴视频链接即可解析,多平台支持、网页端即用;下载桌面客户端解锁海外平台本地解析,开通会员更享不限次下载。

顶部