用 Tauri + FFmpeg + Whisper.cpp 从零打造本地字幕生成器

背景:

最近开始尝试做自媒体,录点视频。刚开始就遇到了字幕的问题,于是想先搞个字幕生成工具(为了这点醋才包的这顿饺子😄):SubGen

这个工具用 Tauri + Rust 做外壳,把 FFmpegWhisper.cpp 集成进去,能一键把视频转成 SRT 字幕

这篇文章记录下笔者做这个工具的过程,也分享下用到的核心组件和代码结构。


架构设计

SubGen 采用分层架构,核心组件的交互关系如下:

scss 复制代码
┌─────────────┐        ┌──────────────┐
│   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 到资源目录,调用它来:

  1. 从视频里抽出音频。
  2. 统一格式(16kHz,单声道 WAV),让 Whisper 可以直接处理。

Rust 这边的调用很简单:

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 也可以跑出来结果,只是稍微慢点。这对于没有好的显卡的童鞋很有用!

调用命令大概是这样:

bash 复制代码
whisper-cli -m ggml-small.bin -f audio.wav -osrt -otxt

最后会输出一个 output.srt,直接能用。

Rust 里调用也是 Command::new() 一把梭:

rust 复制代码
Command::new("resources/whisper-cli")
    .args(["-m", "resources/models/ggml-small.bin", "-f", "audio.wav", "-l", "zh", "--output-srt"])
    .status()
    .expect("Whisper 执行失败");

代码结构和流程

笔者的项目大概是这样分层的:

bash 复制代码
subgen/
├── src/                     # 前端 React + TypeScript
│   └── main.tsx             # UI入口
├── src-tauri/               # Tauri + Rust
│   ├── commands.rs          # Rust命令逻辑
│   ├── resources/           # ffmpeg、whisper二进制、模型文件
│   └── main.rs              # 程序入口

前端用 @tauri-apps/apiinvoke 调 Rust:

ts 复制代码
import { invoke } from '@tauri-apps/api';

async function handleGenerate(videoPath: string) {
  const result = await invoke<string>('extract_subtitles', { videoPath });
  console.log('字幕生成完成:', result);
}

Rust 后端的核心命令:

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/...

相关推荐
IT_陈寒1 分钟前
SpringBoot 项目启动慢?这5个优化技巧让你的应用快50%
前端·人工智能·后端
GISer_Jing11 分钟前
React核心语法:组件化与声明式编程
前端·react.js·前端框架
DJ斯特拉11 分钟前
文件上传(UUID防止重名文件&&阿里云实现云端上传&&MultipartFile接收前端文件)
前端
Alan Lu Pop16 分钟前
React 表单提交关键词意外触发刷新
前端·javascript·react.js
掘金安东尼19 分钟前
企业级Claw落地避坑指南:70%项目失败的真实原因
前端·面试·github
这儿有一堆花19 分钟前
从技术标准到营销概念:深度解析 HTML5 与 H5 的演变与区别
前端·html·html5
我命由我1234519 分钟前
React - 创建 React 项目、React 项目结构、React 简单案例、TodoList 案例
前端·javascript·react.js·前端框架·ecmascript·html5·js
SuperEugene21 分钟前
Vue3 Pinia 状态管理规范:何时用 Pinia 何时用本地状态|状态管理与路由规范篇
开发语言·前端·javascript·vue.js·前端框架
Moment22 分钟前
TypeScript 要换芯了,6.0 竟是旧编译器的最后一舞
前端·javascript·github
Cg1362691597427 分钟前
Element-入门
前端