664 lines
23 KiB
Markdown
664 lines
23 KiB
Markdown
# SynthMind - 视频音频提取与转录工具
|
||
|
||
将视频文件转换为文字稿的自动化工具链,为后续 AI 内容分析与总结提供数据基础。
|
||
|
||
**流水线**:视频文件 → MP3 音频 → TXT 文字稿 → *(后续 AI 分析)*
|
||
|
||
---
|
||
|
||
## 目录
|
||
|
||
- [功能特性](#功能特性)
|
||
- [环境要求](#环境要求)
|
||
- [安装](#安装)
|
||
- [快速开始](#快速开始)
|
||
- [脚本一:extract_audio.py](#脚本一extract_audiopy)
|
||
- [脚本二:transcribe_audio.py](#脚本二transcribe_audiopy)
|
||
- [多模型精度对比](#多模型精度对比)
|
||
- [Manifest 文件说明](#manifest-文件说明)
|
||
- [高级用法](#高级用法)
|
||
- [故障排查](#故障排查)
|
||
- [测试报告](#测试报告)
|
||
|
||
---
|
||
|
||
## 功能特性
|
||
|
||
### 通用特性
|
||
- ✅ 支持单文件或递归扫描目录
|
||
- ✅ 完善的**断点续传**:中断后重新运行自动跳过已完成项
|
||
- ✅ **JSON manifest** 进度追踪,两个脚本共享同一文件
|
||
- ✅ **失败自动记录 + 重试机制**(`--retry-failed`)
|
||
- ✅ **状态查询**(`--status`)实时查看进度
|
||
- ✅ 完全支持**含空格、中文的文件名**
|
||
- ✅ 详细的时间戳日志
|
||
|
||
### extract_audio.py 特性
|
||
- 使用 FFmpeg 提取音频:**MP3 / 64kbps / 22050Hz / 单声道**(语音场景优化,体积小)
|
||
- 支持视频**切分模式**:超大视频按时长切分为多段分别处理
|
||
- 支持多种输入格式:`.mp4/.mkv/.avi/.mov/.flv/.wmv/.webm/.m4v`
|
||
|
||
### transcribe_audio.py 特性
|
||
- 基于 **openai-whisper** 命令行工具
|
||
- 支持 4 种模型:**`tiny / base / small / medium`**(详见 [Whisper 模型选择](#whisper-模型选择))
|
||
- **多模型批量转录(`--all-models`)**:一次运行对每个音频用 4 个模型分别转录,输出 `<stem>.<model>.txt` 便于精度对比
|
||
- **模型预下载(`--preload-models`)**:一次性下载全部支持的模型到本地缓存
|
||
- **自定义输出后缀(`--output-suffix`)**:便于同一模型多次转录(不同参数)不覆盖
|
||
- 支持指定语言(`--language zh/en/...`)或自动检测
|
||
- 支持音频**切分转录**:超长音频切分后逐段转录再合并
|
||
- 支持多种音频格式:`.mp3/.m4a/.wav/.flac/.ogg/.aac`
|
||
- **Manifest 嵌套 transcriptions**:同一视频/音频记录里,多模型多参数结果按 `<model>[:<suffix>]` 为 subkey 共存,互不覆盖
|
||
|
||
---
|
||
|
||
## 环境要求
|
||
|
||
| 依赖 | 版本 | 用途 |
|
||
|------|------|------|
|
||
| Python | 3.8+ | 脚本运行 |
|
||
| FFmpeg | 4.0+ | 音视频处理(含 `ffprobe`)|
|
||
| openai-whisper | 最新 | 音频转录 |
|
||
|
||
**当前实测环境**:Ubuntu 22.04 (WSL2) + Python 3.13 + FFmpeg 6.0 + Whisper 20250625
|
||
|
||
---
|
||
|
||
## 安装
|
||
|
||
### 1. 安装 FFmpeg
|
||
|
||
```bash
|
||
sudo apt update
|
||
sudo apt install -y ffmpeg
|
||
ffmpeg -version # 验证
|
||
```
|
||
|
||
### 2. 安装 Whisper
|
||
|
||
```bash
|
||
pip install openai-whisper
|
||
whisper --help # 验证
|
||
```
|
||
|
||
### 3. 预下载所有 Whisper 模型(推荐)
|
||
|
||
首次运行前,建议一次性下载所需的 4 个模型(共约 2.1GB):
|
||
|
||
```bash
|
||
python3 transcribe_audio.py --preload-models
|
||
```
|
||
|
||
**下载耗时参考**(本机测试):
|
||
```
|
||
tiny (~75MB) → 数秒
|
||
base (~140MB) → 数秒
|
||
small (~460MB) → 数十秒
|
||
medium (~1.5GB) → 75s
|
||
```
|
||
|
||
模型会缓存到 `~/.cache/whisper/`。
|
||
|
||
### 4. 下载脚本
|
||
|
||
将 `extract_audio.py` 和 `transcribe_audio.py` 放到同一目录即可,无需额外配置。
|
||
|
||
---
|
||
|
||
## 快速开始
|
||
|
||
### 场景 1:处理某目录下所有视频(简单流水线)
|
||
|
||
```bash
|
||
# Step 1: 提取音频(生成 .mp3)
|
||
python3 extract_audio.py /path/to/videos/
|
||
|
||
# Step 2: 转录文字(生成 .txt,默认 base 模型)
|
||
python3 transcribe_audio.py /path/to/videos/ --language zh
|
||
```
|
||
|
||
### 场景 2:对比 4 个模型的精度
|
||
|
||
```bash
|
||
# 一次运行,每个音频用 4 个模型都跑一遍
|
||
python3 transcribe_audio.py /path/to/audio/ --all-models --language zh
|
||
```
|
||
|
||
输出(每个 mp3 生成 4 个 txt):
|
||
```
|
||
audio1.mp3
|
||
audio1.tiny.txt
|
||
audio1.base.txt
|
||
audio1.small.txt
|
||
audio1.medium.txt
|
||
```
|
||
|
||
### 场景 3:高精度转录(推荐配置)
|
||
|
||
```bash
|
||
python3 transcribe_audio.py /path/to/audio/ --model medium --language zh
|
||
```
|
||
|
||
---
|
||
|
||
## 脚本一:extract_audio.py
|
||
|
||
### 用法
|
||
|
||
```bash
|
||
python3 extract_audio.py <source> [选项]
|
||
```
|
||
|
||
### 参数说明
|
||
|
||
| 参数 | 简写 | 默认 | 说明 |
|
||
|------|------|------|------|
|
||
| `source` | - | 必填 | 视频文件路径 或 包含视频的目录 |
|
||
| `--output` | `-o` | 与源同目录 | MP3 输出目录 |
|
||
| `--manifest` | `-m` | source 目录下 | manifest.json 路径 |
|
||
| `--split-size` | - | 不切分 | 视频时长超过此秒数则切分(单位:秒) |
|
||
| `--split-segment` | - | 600 | 每个切分片段时长(单位:秒) |
|
||
| `--retry-failed` | - | false | 重新处理上次失败的文件 |
|
||
| `--status` | - | false | 仅查看进度状态,不执行提取 |
|
||
|
||
### 使用示例
|
||
|
||
**1. 处理目录下所有视频**
|
||
```bash
|
||
python3 extract_audio.py /home/user/videos/
|
||
```
|
||
|
||
**2. 处理单个视频**
|
||
```bash
|
||
python3 extract_audio.py /home/user/videos/lesson1.mp4
|
||
```
|
||
|
||
**3. 指定 MP3 输出目录**
|
||
```bash
|
||
python3 extract_audio.py /home/user/videos/ -o /home/user/audio/
|
||
```
|
||
|
||
**4. 超过 1 小时的视频自动切分为 10 分钟片段**
|
||
```bash
|
||
python3 extract_audio.py /home/user/videos/ --split-size 3600 --split-segment 600
|
||
```
|
||
|
||
**5. 查看进度**
|
||
```bash
|
||
python3 extract_audio.py /home/user/videos/ --status
|
||
```
|
||
|
||
**6. 重试失败的文件**
|
||
```bash
|
||
python3 extract_audio.py /home/user/videos/ --retry-failed
|
||
```
|
||
|
||
### 输出规格(FFmpeg 参数)
|
||
|
||
```
|
||
-vn 去除视频轨道
|
||
-acodec libmp3lame MP3 编码
|
||
-ab 64k 比特率 64kbps
|
||
-ar 22050 采样率 22050Hz
|
||
-ac 1 单声道
|
||
```
|
||
|
||
> **场景优化**:这套参数针对语音课程/讲座优化,体积约为原视频的 **20%**,同时保留清晰的人声。
|
||
|
||
---
|
||
|
||
## 脚本二:transcribe_audio.py
|
||
|
||
### 用法
|
||
|
||
```bash
|
||
python3 transcribe_audio.py <source> [选项]
|
||
python3 transcribe_audio.py --preload-models # 仅下载模型(source 可省略)
|
||
```
|
||
|
||
### 参数说明
|
||
|
||
| 参数 | 简写 | 默认 | 说明 |
|
||
|------|------|------|------|
|
||
| `source` | - | 见备注 | MP3 文件路径 或 包含音频的目录 |
|
||
| `--output` | `-o` | 与源同目录 | TXT 输出目录 |
|
||
| `--manifest` | `-m` | source 目录下 | manifest.json 路径 |
|
||
| `--model` | - | `base` | Whisper 模型:`tiny/base/small/medium`(4 选一) |
|
||
| `--language` | - | 自动检测 | 语言代码。推荐 **`zh`**(中文)或 **`en`**(英文) |
|
||
| `--all-models` | - | false | 每个音频用**全部 4 个模型**转录,输出 `<stem>.<model>.txt` |
|
||
| `--output-suffix` | - | 空 | 输出 txt 附加后缀:`<stem><suffix>.txt` |
|
||
| `--split-size` | - | 不切分 | 音频超过此秒数则切分(需要 ffmpeg) |
|
||
| `--split-segment` | - | 1800 | 每个切分片段时长(单位:秒) |
|
||
| `--retry-failed` | - | false | 重新处理上次失败的文件 |
|
||
| `--status` | - | false | 仅查看进度状态 |
|
||
| `--preload-models` | - | false | 预下载全部支持模型,无需 source |
|
||
|
||
> **备注**:`--preload-models` 或 `--status` 模式下可省略 `source`。
|
||
|
||
### Whisper 模型选择
|
||
|
||
| 模型 | 参数量 | 磁盘 | 显存 | CPU 转录 890s 音频 | 中文精度 | 语言输出 |
|
||
|------|--------|------|------|------|------|------|
|
||
| `tiny` | 39M | ~75MB | ~1GB | **47s** | ⭐⭐ | 繁体 |
|
||
| `base` | 74M | ~140MB | ~1GB | **58s** | ⭐⭐⭐ | 简体 |
|
||
| `small` | 244M | ~460MB | ~2GB | **93s** | ⭐⭐⭐⭐ | 简体 |
|
||
| `medium` | 769M | ~1.5GB | ~5GB | **170s** | ⭐⭐⭐⭐⭐ | 简体 + 标点 |
|
||
|
||
> **实测数据来源**:本项目 [测试报告](#测试报告)(14 分 50 秒中文课程视频)
|
||
> **推荐**:日常速稿用 `base`,重要内容用 `medium`。
|
||
|
||
### 使用示例
|
||
|
||
**1. 转录目录下所有 MP3(默认 base 模型)**
|
||
```bash
|
||
python3 transcribe_audio.py /home/user/audio/
|
||
```
|
||
|
||
**2. 中文课程使用 medium 模型(高精度)**
|
||
```bash
|
||
python3 transcribe_audio.py /home/user/audio/ --model medium --language zh
|
||
```
|
||
|
||
**3. 一次跑 4 个模型对比精度**
|
||
```bash
|
||
python3 transcribe_audio.py /home/user/audio/ --all-models --language zh
|
||
```
|
||
|
||
**4. 预下载模型(首次安装后一次性下载全部)**
|
||
```bash
|
||
python3 transcribe_audio.py --preload-models
|
||
```
|
||
|
||
**5. 自定义输出后缀(避免覆盖)**
|
||
```bash
|
||
# 用 base 模型转录,输出为 <stem>_v1.txt
|
||
python3 transcribe_audio.py /home/user/audio/ --model base --output-suffix _v1
|
||
|
||
# 稍后再用 base + 不同参数转录,输出为 <stem>_v2.txt(不覆盖 v1)
|
||
python3 transcribe_audio.py /home/user/audio/ --model base --output-suffix _v2
|
||
```
|
||
|
||
**6. 超长音频(>30 分钟)切分转录**
|
||
```bash
|
||
python3 transcribe_audio.py /home/user/audio/ \
|
||
--model medium --language zh \
|
||
--split-size 1800 --split-segment 600
|
||
```
|
||
|
||
**7. 查看进度(含每个模型的记录)**
|
||
```bash
|
||
python3 transcribe_audio.py /home/user/audio/ --status
|
||
```
|
||
|
||
**8. 重试失败的文件**
|
||
```bash
|
||
python3 transcribe_audio.py /home/user/audio/ --retry-failed
|
||
```
|
||
|
||
---
|
||
|
||
## 多模型精度对比
|
||
|
||
### 实测样本
|
||
|
||
- **音频**:SketchUp 软件优化设置课程(14 分 50 秒,中文普通话)
|
||
- **命令**:`python3 transcribe_audio.py <mp3> --all-models --language zh`
|
||
|
||
### 精度对比(关键词识别)
|
||
|
||
| 关键词 | 原意 | tiny | base | small | medium |
|
||
|--------|------|------|------|-------|--------|
|
||
| SketchUp 中文名 | 草图大师 | 草毒大師 ❌ | 吵毒大师 ❌ | 草读大师 ❌ | 草毒大师 ❌ |
|
||
| 出厂设置 | 常规的设置 | 殘酷 ❌ | 残酷 ❌ | 常规 ✅ | 常规 ✅ |
|
||
| 行业 | 行业 | 含業 ❌ | 行业 ✅ | 行业 ✅ | 行业 ✅ |
|
||
| 轮廓线 | 轮廓线 | 能夠像 ❌ | 能扩限 ❌ | 轮廓线 ✅ | 轮廓线 ✅ |
|
||
| 场景 | 场景 | 殘景 ❌ | 场景 ✅ | 场景 ✅ | 场景 ✅ |
|
||
| 输出语言 | 简体中文 | **繁体** ⚠️ | 简体 ✅ | 简体 ✅ | 简体 ✅ |
|
||
| 标点符号 | 有 | 无 ❌ | 无 ❌ | 少量 ⚠️ | 有 ✅ |
|
||
|
||
### 各模型样例输出(首行)
|
||
|
||
**原音频**:"大家好,这一节我们对**草图大师**来进行一个软件的一些优化设置方面的一些操作"
|
||
|
||
| 模型 | 输出 | 备注 |
|
||
|------|------|------|
|
||
| tiny | 大家好,這一節我們對**草毒大師**來進行一個軟件的一些優化設置方面的一些操作 | 繁体,专有名词错 |
|
||
| base | 大家好,这一节我们对**吵毒大师**来进行一个软件的一些优化设置方面的一些操作 | 简体,专有名词错 |
|
||
| small | 大家好,这一节我们对**草读大师**来进行一个软件的一些优化设置方面的一些操作 | 简体,专有名词接近 |
|
||
| medium | 大家好,这一节我们对**草毒大师**来进行一个软件的一些优化设置方面的一些操作 | 简体,专有名词接近,有逗号 |
|
||
|
||
### 结论
|
||
|
||
| 场景 | 推荐模型 |
|
||
|------|----------|
|
||
| 快速内容概览、草稿 | **tiny** 或 **base**(快,可接受) |
|
||
| 日常转录、笔记 | **base** 或 **small**(平衡) |
|
||
| 正式转录、发布 | **medium**(最好精度) |
|
||
| 专有名词很多的领域 | 结合 `--initial_prompt` 或人工校对 |
|
||
|
||
> **提示**:所有 whisper 模型对**专有名词**(品牌名、人名、技术术语)识别都容易出错。生产环境建议:
|
||
> 1. 使用 medium 模型
|
||
> 2. 通过 `whisper --initial_prompt "本视频讨论 SketchUp / 草图大师"` 提示 whisper(本脚本暂未透传此参数,可直接调 whisper CLI)
|
||
> 3. 人工二次校对
|
||
|
||
---
|
||
|
||
## Manifest 文件说明
|
||
|
||
两个脚本**共享同一个 `manifest.json`**(schema v2)。每个视频文件在 manifest 中**只有一条记录**,`extract_audio.py` 与 `transcribe_audio.py` 分别写入其中的 `extraction` 与 `transcriptions` 两个子对象,互不覆盖。
|
||
|
||
### key 选择规则
|
||
|
||
| Key | 来源 | 场景 |
|
||
|-----|------|------|
|
||
| 视频绝对路径(如 `/path/to/x.mp4`)| `extract_audio.py` 产生的记录 | 完整流水线(视频 → 音频 → 转录)|
|
||
| 音频绝对路径(如 `/path/to/x.mp3`)| 直接对 mp3 跑 `transcribe_audio.py` | 独立音频转录(无对应视频)|
|
||
|
||
**反向查找**:当 `transcribe_audio.py` 处理某个 mp3 时,会先扫描 manifest,如果发现某条视频记录的 `extraction.audio_path` 等于该 mp3 路径,就把转录结果并入该视频记录,而不是新建 key。
|
||
|
||
### 完整示例(v2)
|
||
|
||
```json
|
||
{
|
||
"version": 2,
|
||
"files": {
|
||
"/path/to/lesson1.mp4": {
|
||
"extraction": {
|
||
"extracted": true,
|
||
"audio_path": "/path/to/lesson1.mp3",
|
||
"split_segments": [],
|
||
"error": null,
|
||
"updated_at": "2026-08-23T10:30:00"
|
||
},
|
||
"transcriptions": {
|
||
"tiny": {
|
||
"transcribed": true,
|
||
"txt_path": "/path/to/lesson1.tiny.txt",
|
||
"split_segments": [],
|
||
"model": "tiny",
|
||
"language": "zh",
|
||
"output_suffix": null,
|
||
"error": null,
|
||
"updated_at": "2026-08-23T10:35:00"
|
||
},
|
||
"medium": {
|
||
"transcribed": true,
|
||
"txt_path": "/path/to/lesson1.medium.txt",
|
||
"split_segments": [],
|
||
"model": "medium",
|
||
"language": "zh",
|
||
"output_suffix": null,
|
||
"error": null,
|
||
"updated_at": "2026-08-23T10:50:00"
|
||
},
|
||
"base:_v1": {
|
||
"transcribed": true,
|
||
"txt_path": "/path/to/lesson1_v1.txt",
|
||
"split_segments": [],
|
||
"model": "base",
|
||
"language": null,
|
||
"output_suffix": "_v1",
|
||
"error": null,
|
||
"updated_at": "2026-08-23T11:00:00"
|
||
}
|
||
}
|
||
},
|
||
"/path/to/standalone.mp3": {
|
||
"extraction": null,
|
||
"transcriptions": {
|
||
"medium": {
|
||
"transcribed": true,
|
||
"txt_path": "/path/to/standalone.medium.txt",
|
||
"split_segments": [],
|
||
"model": "medium",
|
||
"language": "en",
|
||
"output_suffix": null,
|
||
"error": null,
|
||
"updated_at": "2026-08-23T11:15:00"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### 字段说明
|
||
|
||
**顶层每条记录**
|
||
- `extraction` - `extract_audio.py` 的产物;`null` 表示没有走过提取(例如直接对 mp3 跑转录)
|
||
- `transcriptions` - `transcribe_audio.py` 的产物;以 `<model>` 或 `<model>:<suffix>` 为 subkey,允许同一音频用多种模型/参数并存
|
||
|
||
**`extraction` 字段**
|
||
- `extracted` - 是否已提取音频(布尔);`false` + 有 `error` 表示上次失败
|
||
- `audio_path` - 生成的 MP3 路径(切分模式下为第一个片段)
|
||
- `split_segments` - 切分片段列表(非切分模式为空 `[]`)
|
||
- `error` - 失败时的错误信息,成功为 `null`
|
||
- `updated_at` - 最后更新时间(ISO 8601)
|
||
|
||
**`transcriptions.<subkey>` 字段**
|
||
- `transcribed` - 是否已转录(布尔)
|
||
- `txt_path` - 生成的 TXT 路径
|
||
- `split_segments` - 切分片段列表
|
||
- `model` - 使用的 whisper 模型(`tiny/base/small/medium`)
|
||
- `language` - 使用的语言代码(`zh/en/...`)或 `null`(自动检测)
|
||
- `output_suffix` - 输出后缀(默认 `null`)
|
||
- `error` - 失败错误信息
|
||
- `updated_at` - 最后更新时间
|
||
|
||
### 断点续传逻辑
|
||
|
||
脚本判断是否需要处理某文件时,**同时校验**:
|
||
1. manifest 中标记为已完成(`extraction.extracted=true` 或 `transcriptions.<sk>.transcribed=true`)
|
||
2. 目标文件实际存在且非空
|
||
|
||
**任一条件不满足则重新处理**(例如手动删除了 mp3 后重跑,会重新提取)。
|
||
|
||
**多模型模式**:每个 (音频 × 模型 × 后缀) 组合独立跟踪,对应 `transcriptions` 里独立的 subkey。已完成的组合被跳过,未完成的继续。
|
||
|
||
### 从旧版 (v1) 升级
|
||
|
||
老版本使用扁平 schema:视频记录 key = 视频路径;转录记录 key = `transcribe:<model>[:<suffix>]:<音频路径>`。这些散落的记录在 v2 里会被合并到同一条视频记录里。
|
||
|
||
**升级方式**:脚本首次加载旧 manifest 时会打印警告并**以空 v2 schema 重新开始**(旧数据将被下次保存覆盖)。如果你想保留旧记录,运行升级前**先手动备份 manifest.json**。所有已提取的 mp3 和已转录的 txt 会在下次运行时被识别为"存在但 manifest 无记录"→ 重新处理并写入新 schema。
|
||
|
||
---
|
||
|
||
## 高级用法
|
||
|
||
### 1. 完整流水线脚本
|
||
|
||
```bash
|
||
#!/bin/bash
|
||
VIDEO_DIR="/path/to/videos"
|
||
|
||
echo "=== Step 0: 预下载模型(仅首次)==="
|
||
python3 transcribe_audio.py --preload-models
|
||
|
||
echo "=== Step 1: 提取音频 ==="
|
||
python3 extract_audio.py "$VIDEO_DIR" --split-size 3600 --split-segment 600
|
||
|
||
echo "=== Step 2: 转录文字(medium 模型高精度)==="
|
||
python3 transcribe_audio.py "$VIDEO_DIR" \
|
||
--model medium --language zh \
|
||
--split-size 1800 --split-segment 600
|
||
|
||
echo "=== Step 3: 查看最终状态 ==="
|
||
python3 extract_audio.py "$VIDEO_DIR" --status
|
||
python3 transcribe_audio.py "$VIDEO_DIR" --status
|
||
```
|
||
|
||
### 2. 多模型对比工作流
|
||
|
||
```bash
|
||
# 1) 提取音频
|
||
python3 extract_audio.py /videos/
|
||
|
||
# 2) 一次跑 4 个模型(此步骤会跑 4x 时长,请预留时间)
|
||
python3 transcribe_audio.py /videos/ --all-models --language zh
|
||
|
||
# 3) 查看所有结果
|
||
python3 transcribe_audio.py /videos/ --status
|
||
|
||
# 4) 对比不同模型输出(例如用 diff)
|
||
diff /videos/lesson1.base.txt /videos/lesson1.medium.txt
|
||
```
|
||
|
||
### 3. 独立分离 MP3 与 TXT 输出
|
||
|
||
```bash
|
||
python3 extract_audio.py /videos/ -o /audios/
|
||
python3 transcribe_audio.py /audios/ -o /transcripts/ --model medium
|
||
```
|
||
|
||
### 4. 自定义 manifest 位置(多套并行)
|
||
|
||
```bash
|
||
python3 extract_audio.py /videos/ -m /var/run/task1_manifest.json
|
||
python3 extract_audio.py /videos2/ -m /var/run/task2_manifest.json
|
||
```
|
||
|
||
### 5. 同一模型不同参数的多轮转录
|
||
|
||
```bash
|
||
# 第一版:自动检测语言
|
||
python3 transcribe_audio.py audio.mp3 --model base --output-suffix _auto
|
||
|
||
# 第二版:强制中文
|
||
python3 transcribe_audio.py audio.mp3 --model base --language zh --output-suffix _zh
|
||
|
||
# 两个 txt 独立保存,manifest 各自记录
|
||
ls audio_*.txt
|
||
# audio_auto.txt audio_zh.txt
|
||
```
|
||
|
||
---
|
||
|
||
## 故障排查
|
||
|
||
### FFmpeg 相关
|
||
|
||
| 症状 | 原因 | 解决 |
|
||
|------|------|------|
|
||
| 未找到 ffmpeg 命令 | 未安装 | `sudo apt install ffmpeg` |
|
||
| 视频时长为 0 | ffprobe 无法解析 | 检查视频是否损坏 `ffprobe <file>` |
|
||
| 切分片段为空 | 编码不兼容 codec copy | 手动尝试 `ffmpeg -i in.mp4 -c copy out.mp4` |
|
||
|
||
### Whisper 相关
|
||
|
||
| 症状 | 原因 | 解决 |
|
||
|------|------|------|
|
||
| 未找到 whisper 命令 | 未安装 | `pip install openai-whisper` |
|
||
| 首次转录卡住 | 正在下载模型 | 提前用 `--preload-models` 一次下完 |
|
||
| CUDA out of memory | 显存不足 | 使用更小模型;whisper 自动 fallback 到 CPU |
|
||
| 转录内容为繁体 | tiny 模型对中文倾向输出繁体 | 使用 `base` 及以上模型 |
|
||
| 专有名词识别错 | 模型未训练过 | 使用 `medium`;未来可支持 `--initial-prompt` |
|
||
|
||
### Manifest 相关
|
||
|
||
| 症状 | 解决 |
|
||
|------|------|
|
||
| 想强制重跑所有文件 | 删除 `manifest.json` |
|
||
| 想强制重跑某个文件 | 编辑 manifest 或删除对应 `.mp3/.txt` 文件 |
|
||
| 想重跑失败的文件 | 加 `--retry-failed` 参数 |
|
||
| 想同一音频用同模型换参数再跑一次 | 用 `--output-suffix` 区分 |
|
||
|
||
### 文件名相关
|
||
|
||
- 中文文件名:完全支持
|
||
- 空格文件名:完全支持(内部用 subprocess 参数列表,非 shell 拼接)
|
||
- 特殊字符(如 `&`、`|`):应可正常处理,遇到问题请提 issue
|
||
|
||
---
|
||
|
||
## 测试报告
|
||
|
||
### 测试环境
|
||
- OS: Ubuntu 22.04 (WSL2 on Windows)
|
||
- Python: 3.13
|
||
- FFmpeg: 6.0
|
||
- Whisper: 20250625 (全部 4 个模型)
|
||
|
||
### 测试用例
|
||
|
||
**测试视频**:`03-SU软件的优化设置.mp4`
|
||
- 时长:890 秒(14 分 50 秒)
|
||
- 大小:37 MB
|
||
- 视频:H.264 1280x720
|
||
- 音频:AAC 44.1kHz 立体声
|
||
- 语言:中文(普通话)
|
||
- 内容:SketchUp 软件优化设置教学
|
||
|
||
### 提取音频 & 通用功能测试
|
||
|
||
| # | 测试项 | 结果 | 耗时 | 备注 |
|
||
|---|--------|------|------|------|
|
||
| 1 | 基本音频提取 | ✅ | 4s | 输出 6.9MB MP3 (22050Hz mono 64kbps) |
|
||
| 2 | 断点续传(重跑跳过) | ✅ | <1s | manifest 校验通过 |
|
||
| 3 | `--status` 状态查看 | ✅ | <1s | 正确显示 1/1 完成 |
|
||
| 4 | 视频切分模式(500s 阈值) | ✅ | 4s | 3 片段完美衔接(修复 pipe:0 bug) |
|
||
| 5 | 失败重试 + 含空格文件名 | ✅ | - | 失败记录、跳过、`--retry-failed` 均正常 |
|
||
| 6 | 端到端流水线(视频→MP3→TXT) | ✅ | 54s | 从零开始生成完整产物 |
|
||
|
||
### 转录 & 多模型功能测试
|
||
|
||
| # | 测试项 | 结果 | 耗时 | 备注 |
|
||
|---|--------|------|------|------|
|
||
| 7 | `--preload-models` 预下载 4 模型 | ✅ | ~120s | 共 2.1GB,缓存到 `~/.cache/whisper/` |
|
||
| 8 | 单模型转录(tiny + zh) | ✅ | 47s | 输出 9KB TXT |
|
||
| 9 | 单模型转录(base + zh) | ✅ | 58s | 输出 9KB TXT |
|
||
| 10 | 单模型转录(small + zh) | ✅ | 93s | 输出 9KB TXT |
|
||
| 11 | 单模型转录(medium + zh) | ✅ | 170s | 输出 9KB TXT,有标点符号 |
|
||
| 12 | `--all-models` 4 模型一次跑 | ✅ | 368s | 生成 4 个独立 txt |
|
||
| 13 | 多模型 manifest 记录 | ✅ | - | 4 条独立 key,互不覆盖 |
|
||
| 14 | 多模型 `--status` 展示 | ✅ | <1s | 每个模型独立行显示 |
|
||
| 15 | 断点续传(重跑跳过) | ✅ | <1s | 4/4 全部跳过 |
|
||
| 16 | `--output-suffix` 自定义后缀 | ✅ | 46s | `_custom` 生成独立 key + 独立 txt |
|
||
| 17 | 音频切分转录 | ✅ | 59s | 3 片段并转录后合并成完整 TXT |
|
||
| 18 | manifest 共享验证 | ✅ | - | 提取+转录记录并存互不干扰 |
|
||
|
||
### 已修复 Bug
|
||
|
||
**BUG-01**:切分模式下 MP3 为空文件(148 字节)
|
||
- **原因**:使用 `cat <file> | ffmpeg -i pipe:0` 时,MP4 的 `moov` atom 位于文件末尾需要 seek,pipe 不支持 seek 导致 ffmpeg 静默失败
|
||
- **修复**:改用 `ffmpeg -i <file>` 直接读取本地文件,同时增加输出文件非空校验(> 1KB)
|
||
- **验证**:切分后 3 个片段合计 890.4s ≈ 原视频 890.2s
|
||
|
||
**BUG-02**:`--output-suffix` 被 manifest 误跳过
|
||
- **原因**:初版 manifest key 只含 `model + path`,未含 `output_suffix`,导致同一模型换 suffix 后被判定为已完成
|
||
- **修复**:key 结构改为 `transcribe:<model>[:<suffix>]:<path>`,suffix 参与 key 判定
|
||
- **验证**:同一 tiny 模型,无 suffix 和 `_custom` suffix 生成独立 manifest 条目和独立 txt
|
||
|
||
### 性能参考(本次测试环境,CPU 转录)
|
||
|
||
| 阶段 | 输入 | 输出 | 耗时 | 吞吐 |
|
||
|------|------|------|------|------|
|
||
| 音频提取 | 37MB / 890s 视频 | 6.9MB MP3 | 4s | 223x realtime |
|
||
| 转录(tiny) | 6.9MB / 890s 音频 | 9KB TXT | 47s | 19x realtime |
|
||
| 转录(base) | 6.9MB / 890s 音频 | 9KB TXT | 58s | 15x realtime |
|
||
| 转录(small) | 6.9MB / 890s 音频 | 9KB TXT | 93s | 9.6x realtime |
|
||
| 转录(medium) | 6.9MB / 890s 音频 | 9KB TXT | 170s | 5.2x realtime |
|
||
| **多模型一次跑(4 model)** | 6.9MB / 890s 音频 | 4 个 9KB TXT | 368s | - |
|
||
|
||
> 上述数据为 CPU 转录性能。GPU 转录速度可提升 5-10x。
|
||
|
||
---
|
||
|
||
## 项目路线图
|
||
|
||
- [x] Phase 1: 音频提取(extract_audio.py)
|
||
- [x] Phase 2: 音频转录(transcribe_audio.py)
|
||
- [x] 单模型转录
|
||
- [x] 多模型批量对比(`--all-models`)
|
||
- [x] 模型预下载(`--preload-models`)
|
||
- [x] 输出后缀自定义(`--output-suffix`)
|
||
- [x] 语言指定(`--language zh/en`)
|
||
- [ ] Phase 3: 内容归纳整理(summarize.py)- 待定
|
||
- AI 分析文字内容 + 视频内容
|
||
- 生成脑图、结构化文档
|
||
|
||
---
|
||
|
||
## 相关脚本
|
||
|
||
- `extract_audio.py` - 本地通用视频音频提取(本项目 Phase 1)
|
||
- `transcribe_audio.py` - 本地音频转录(本项目 Phase 2)
|