背景:
最近开始尝试做自媒体,录点视频。刚开始就遇到了字幕的问题,于是想先搞个字幕生成工具(为了这点醋才包的这顿饺子😄):SubGen。
这个工具用 Tauri + Rust 做外壳,把 FFmpeg 和 Whisper.cpp 集成进去,能一键把视频转成 SRT 字幕。
这篇文章记录下笔者做这个工具的过程,也分享下用到的核心组件和代码结构。
架构设计
SubGen 采用分层架构,核心组件的交互关系如下:
┌─────────────┐ ┌──────────────┐│ React UI │ │ Rust Core ││ (TypeScript)│ <----> │ (Tauri API) │└─────────────┘ └─────┬────────┘ │ ┌─────────────┴───────────────┐ │ │ ┌────▼────┐ ┌────▼────┐ │ FFmpeg │ │Whisper │ │ 提取音频 │ │ 离线识别 │ └─────────┘ └─────────┘
为什么用 Tauri?
最开始笔者也考虑过 Electron,但它打包太大了(动辄 100MB 起步),而且资源占用高。后来发现 Tauri,它用 Rust 做后端,前端还是用 React 或者任意 Web 技术,这样:
- 打包后体积很小(十几 MB)。跨平台方便(Windows / macOS / Linux)。Rust 调用本地二进制(FFmpeg 和 Whisper)非常顺手。
笔者主要是用 React + TypeScript 写了一个简单的 UI,用户选视频、点按钮,剩下的活就交给 Rust。
FFmpeg:用它来“扒”音频
FFmpeg 是老牌的音视频处理工具了,笔者直接内置了一个编译好的 ffmpeg.exe
/ffmpeg
到资源目录,调用它来:
- 从视频里抽出音频。统一格式(16kHz,单声道 WAV),让 Whisper 可以直接处理。
Rust 这边的调用很简单:
use std::process::Command;Command::new("resources/ffmpeg") .args(["-i", &video_path, "-ar", "16000", "-ac", "1", "audio.wav"]) .status() .expect("FFmpeg 执行失败");
这样一行命令就能把视频转成标准 WAV。
Whisper.cpp:核心的离线识别
笔者选的是 Whisper.cpp,因为它比 Python 版 Whisper 更轻量,直接编译一个 whisper-cli
就能用,不需要装乱七八糟的依赖。
更重要的一点是支持CPU运行,默认4个线程,即使用 ggml-large-v3 也可以跑出来结果,只是稍微慢点。这对于没有好的显卡的童鞋很有用!
调用命令大概是这样:
whisper-cli -m ggml-small.bin -f audio.wav -osrt -otxt
最后会输出一个 output.srt
,直接能用。
Rust 里调用也是 Command::new()
一把梭:
Command::new("resources/whisper-cli") .args(["-m", "resources/models/ggml-small.bin", "-f", "audio.wav", "-l", "zh", "--output-srt"]) .status() .expect("Whisper 执行失败");
代码结构和流程
笔者的项目大概是这样分层的:
subgen/├── src/ # 前端 React + TypeScript│ └── main.tsx # UI入口├── src-tauri/ # Tauri + Rust│ ├── commands.rs # Rust命令逻辑│ ├── resources/ # ffmpeg、whisper二进制、模型文件│ └── main.rs # 程序入口
前端用 @tauri-apps/api
的 invoke
调 Rust:
import { invoke } from '@tauri-apps/api';async function handleGenerate(videoPath: string) { const result = await invoke<string>('extract_subtitles', { videoPath }); console.log('字幕生成完成:', result);}
Rust 后端的核心命令:
#[tauri::command]fn extract_subtitles(video_path: String) -> Result<String, String> { // 1. 调 FFmpeg // 2. 调 Whisper.cpp // 3. 返回 SRT 路径 Ok("output.srt".to_string())}
用下来的感受
整个工具现在已经能做到“拖进视频 → 等几十秒 → 出字幕”这种体验了。
几个感受:
- Tauri 真香:比 Electron 清爽太多,Rust 后端很适合做这些底层调用。FFmpeg 是万能的,直接抽音频,性能还不错。Whisper.cpp 虽然 CPU 跑慢点,但好在准确率挺高,还不用联网。
后续想做的事
- 支持批量处理视频。集成一个简单的字幕编辑功能。尝试 GPU 加速 Whisper(Metal / Vulkan)。
截图
主界面:
生成的 SRT:
如果你也想做个自己的字幕工具,可以直接参考 SubGen 的架构,自己改改就能用。
代码已开源:github.com/byteroycai/…