首页
/ 用 Remotion 程序化生成字幕:@remotion/install-whisper-cpp 安装与调用 Whisper.cpp 完整实战指南

用 Remotion 程序化生成字幕:@remotion/install-whisper-cpp 安装与调用 Whisper.cpp 完整实战指南

2026-09-07 22:49:08作者:齐添朝

本指南以 Remotion 官方 monorepo 中的 @remotion/install-whisper-cpp 包为核心,系统讲解如何在 Node.js/Remotion 项目中自动化安装 Whisper.cpp(C 语言版语音转写引擎)、下载 GGML 模型、执行转写,并把结果转换为可用于 Remotion 视频字幕渲染的 Caption 数据。读完本文,你将掌握从"零环境"到"输出逐词对齐字幕数据"的完整调用链,并理解其中每个 API 的参数含义与底层实现逻辑。

该包目前版本为 4.0.521package.json 中将其描述为 "Helpers for installing and using Whisper.cpp",声明依赖仅有 @remotion/captions(字幕数据基础类型),自身不包含任何二进制运行时,而是通过子进程调度系统上的 gitmakepowershell 与 Whisper.cpp 可执行程序完成工作。

一、包能做什么:四个核心 API 与整体数据流

从包的唯一入口 src/index.ts 可以看到对外导出的全部能力:

  • installWhisperCpp() —— 在本地安装 Whisper.cpp 可执行程序;
  • downloadWhisperModel()WhisperModel 类型 —— 下载指定语音模型(GGML 权重文件);
  • transcribe()TranscriptionJsonTranscribeOnProgress —— 调用 whisper-cli 把音频转写成结构化 JSON;
  • toCaptions() —— 把转写 JSON 映射为 @remotion/captionsCaption[]
  • Language 类型与 convertToCaptions() —— 语言枚举,以及自 v4.0.216 起被标记为 @deprecated 的旧版转换函数。

一条典型的端到端数据流是:

Node 脚本
  ├─ installWhisperCpp()   → 得到 whisper.cpp 可执行文件(git clone / 下载 ZIP + 编译)
  ├─ downloadWhisperModel()→ 得到 ggml-<model>.bin 权重文件
  ├─ transcribe()          → spawn whisper 二进制,产出完整 JSON(含逐词时间戳)
  └─ toCaptions()          → 映射为 Caption[](text / startMs / endMs / timestampMs / confidence)
                              └─ @remotion/captions 的 createCaptions()/createTikTokStyleCaptions() 二次加工

二、安装与版本对齐

README 给出的安装命令是:

npm install @remotion/install-whisper-cpp --save-exact

Remotion 对版本一致性有严格要求:安装任何 Remotion 包时,都要让所有 remotion@remotion/* 包保持同一版本;使用 --save-exact(或将版本号中的 ^ 去掉)可以锁定精确版本,避免因包管理器解析到不同次要版本而出现运行时版本不匹配。在本仓库的开发环境下,该包以 workspace:* 形式依赖 @remotion/captions,确保两个包同步迭代。

注意:该包只负责"调用"Whisper.cpp。真正的转写引擎、模型文件都需要在运行时由下面两个 API 首次落地,因此首次使用往往伴随较大的下载量(模型体积见第四节)。

三、installWhisperCpp:一键获取可执行的 Whisper.cpp

函数签名位于 install-whisper-cpp.ts

const {alreadyExisted} = await installWhisperCpp({
  version: '1.5.5', // 要安装的 Whisper.cpp release 版本
  to: './whisper.cpp', // 安装目标目录(相对 process.cwd())
  printOutput: true, // 是否把子进程输出转发到控制台,默认 true
  signal, // AbortSignal,用于中途取消安装
});

返回值与幂等逻辑

返回值只有 {alreadyExisted: boolean}。实现上是幂等的:如果 to 目录已存在且其中已包含目标版本的可执行文件,会打印 Whisper already exists at <to> 并返回 {alreadyExisted: true},不会重复下载;但如果目录存在而可执行文件缺失,则会抛出错误提示"目录存在但可执行文件缺失,请删除后重试"。值得注意的细节是:printOutput: false 时这类异常会以返回值 {alreadyExisted: false} 的形式静默吞掉(见源码 L187-L206),适合在无日志需求的自动化流程中使用。

不同平台的安装策略

源码中安装逻辑按操作系统分支:

  • macOS 与 Linux(Unix):依次执行 git clone https://github.com/ggerganov/whisper.cpp.git <to>git checkout <ref>make(见 install-whisper-cpp.ts)。ref 的取值规则是:如果 version 形如语义化版本(正则 /^[\d]{1}\.[\d]{1,2}\.+/ 判定),则拼成 v1.5.5 这种 tag 去 checkout;否则把整个字符串当作 git ref 使用,也就是说你既可以用 '1.5.5' 也可以用 'main' 这样的分支名。
  • Windows:不支持从源码构建,改为下载预编译产物 whisper-bin-x64.zip,再调用 powershell.exe 执行 Expand-Archive 解压到目标目录(见 install-whisper-cpp.ts)。Windows 分支对版本格式更严格——非语义化版本会直接抛错,因为必须从发布资产中取对应的 ZIP。
  • 其他平台:抛出 Unsupported platform: <platform>

版本如何决定可执行文件名

getWhisperExecutablePath()install-whisper-cpp.ts)揭示了一个易踩坑的版本差异:

  • Whisper.cpp 低于 1.7.4 时,可执行文件是仓库根目录下的 main(Windows 为 main.exe),注释明确指出 main 已被官方弃用;
  • 1.7.4 及以上版本改用 whisper-cli,且可执行文件被移动到 build/bin/ 子目录下(Windows 为 whisper-cli.exe)。

版本比较由自研的 utils.ts 中的 compareVersions() 完成(逐段解析 x.x.x 并补齐段位比较)。因此传入的 version 不仅决定从哪里取安装源,还决定后续 transcribe() 去哪里找二进制,务必与实际安装结果保持一致。

仓库在 Windows 上专门有一个集成测试 install-whisper-cpp.test.ts,验证在包含空格、$&、单引号与反引号等 PowerShell 特殊字符的目录路径下安装仍能成功,且临时下载的 whisper-bin-x64.zip 会被清理——这正是真实项目路径中容易出问题的场景。

四、downloadWhisperModel:选择并下载语音模型

安装好引擎后还需权重模型。downloadWhisperModel() 位于 download-whisper-model.ts,签名:

const {alreadyExisted} = await downloadWhisperModel({
  model: 'base.en', // 模型名,见下表
  folder: './whisper.cpp/models', // 存放目录(相对 process.cwd())
  printOutput: true,
  onProgress: (downloaded, total) => void, // 下载进度回调
  signal,
});

支持的模型与真实体积

WhisperModel 是一个字面量联合类型,包含官方 openai/whisper 全系模型。源码的 modelSizes 表中精确记录了每个模型的字节数,可供下载后校验文件完整性:

模型 字节数 说明
tiny / tiny.en 约 77,691,713 / 77,704,715(≈74 MiB) 最快、最省内存,精度最低
base / base.en 约 147,951,465 / 147,964,211(≈141 MiB) 通用英文常用起点
small / small.en 约 487,601,967 / 487,614,201(≈465 MiB) 精度/速度折中
medium / medium.en 约 1,533,763,059 / 1,533,774,781(≈1.4 GiB) 高精度、资源占用高
large-v1 / large-v2 / large-v3 各约 3,094,6xx,xxx–3,095,0xx,xxx(≈2.9 GiB) 最大系列
large-v3-turbo 约 1,624,555,275(≈1.5 GiB) large 精度、turbo 速度

模型文件统一命名为 ggml-<model>.bin,下载源是 whisper.cpp 官方发布的模型仓库(getModelPath() 也暴露了"文件名即约定"这一实现事实)。

幂等与校验逻辑

  • 若目标文件已存在且字节数与上表完全一致,直接打印 Model already exists at ... 并返回 {alreadyExisted: true}
  • 若已存在但字节数不一致,printOutput 为 true 时抛错提示"预期 N 字节、实际 M 字节,请删除重试";
  • 传入不在列表中的模型名会抛错并列出全部可用值。

进度打印由 download.tsdownloadFile() 承担:基于 fetch 流式下载,printOutput 开启时每累计 10 MiB 打印一次百分比;onProgress(downloadedBytes, totalBytes) 与打印无关,始终会按流写入节奏回调,可用来驱动自己的进度条。模型文件巨大,建议优先选 base.en/small.en 这类小体积模型验证流程。

五、transcribe:把音频交给 whisper-cli 并拿到 JSON

核心转写函数位于 transcribe.ts。完整参数如下:

const json = await transcribe({
  inputPath: './audio.wav', // 必填,输入音频,必须是 WAV
  whisperPath: './whisper.cpp', // 必填,安装目录
  whisperCppVersion: '1.7.4', // 必填,用于定位 main/whisper-cli
  model: 'base.en', // 必填,须已通过 downloadWhisperModel 下载
  tokenLevelTimestamps: true, // 必填,开启逐词时间戳(泛型开关,会影响返回类型)
  modelFolder: './whisper.cpp/models', // 可选,模型目录,默认同 whisperPath
  translateToEnglish: false, // 可选,-tr,把非英语翻译成英语
  printOutput: true, // 可选,透传子进程 stdout/stderr
  tokensPerItem: null, // 可选,每段最大 token 数;tokenLevelTimestamps 模式下不可用(类型层面禁止)
  language: 'auto', // 可选,语言名或代码,见 Language
  splitOnWord: false, // 可选,--split-on-word,按词切分
  signal, // 可选,AbortSignal
  onProgress: (p: number) => void, // 可选,0~1 转写进度
  flashAttention: false, // 可选,--flash-attn true
  additionalArgs: [], // 可选,string[] 或 [string,string][],追加原生 CLI 参数
});

输入音频的硬性要求

函数开头做了三道校验,直接决定你能否跑通:

  1. whisperPath 必须存在,否则提示检查是否调用过 installWhisperCpp()
  2. inputPath 必须存在;
  3. inputPath 必须以 .wav 结尾(按扩展名判断),且 Whisper 要求 16 kHz 采样率——源码捕获到 stderr 包含 must be 16 kHz 时会抛出专门错误信息。文档内给出的标准转换命令是:
ffmpeg -i input.mp4 -ar 16000 output.wav -y

底层是如何驱动 whisper 的

实现上 transcribe() 先写入一个临时 JSON 路径(./tmp),然后 spawn 可执行文件并拼装如下 CLI 参数(见 transcribe.ts):

-f <音频> --output-file <临时路径> --output-json -ojf(完整JSON)
[-m <模型路径>] [-pp 打印进度] [-tr 翻译] [-l <language小写>]
[--dtw <dtw别名>] [--split-on-word <bool>] [--flash-attn true] [额外参数]

几个值得展开的点:

  • 逐词时间戳依赖 DTWtokenLevelTimestamps: true 时会附加 --dtw,并把模型名转换成 DTW 别名(modelToDtw()large-v3-turbo → large.v3.turbolarge-v3 → large.v3large-v2 → large.v2large-v1 → large.v1,其余模型原样返回)。这是官方 whisper.cpp main 示例用于对齐词级时间戳的模式,源码中注释了对应的上游实现出处。
  • 模型缺失提前报错:spawn 之前会检查 ggml-<model>.bin 是否存在,否则提示阅读 downloadWhisperModel() 文档先下载模型。
  • 进度解析:从子进程 stdout/stderr 中匹配 progress = 文本并解析浮点,回调 onProgress(progress/100)
  • 结束判定:以"预测的 JSON 输出文件已生成"为准(而不是进程退出码,因为 whisper 存在"即使出错也返回 0"的历史问题,源码注释引用了上游 PR 佐证);若因 AbortSignal 被杀会抛 Process was killed with signal ...。另外对 macOS Metal 不可用时的挂起场景有专门的规避逻辑(检测到 ggml_metal_free: deallocating 且已出现真实进度时才 kill 进程,避免误杀导致返回过期数据)。
  • 临时 JSON 在读取后会被 fs.unlinkSync() 清理,不污染工作目录。

返回结构 TranscriptionJson

返回的 TranscriptionJson<HasTokenLevelTimestamps>transcribe.ts)包含 systeminfomodelparamsresulttranscription 数组。当泛型参数为 true(开启逐词时间戳)时,每个元素还带 tokens,其中 t_dtw 是 DTW 对齐后的逐词毫秒级时间戳、p 是置信度;offsets.from/to 是片段起止时间。

六、toCaptions:把转写 JSON 映射为 Remotion Caption

拿到 JSON 后,用 to-captions.tstoCaptions() 转换为 @remotion/captions 的标准结构:

const {captions} = toCaptions({
  whisperCppOutput: json, // 需要 tokenLevelTimestamps: true 的输出
});
// captions: Caption[]
//   { text, startMs, endMs, timestampMs: number|null, confidence: number }

映射规则可以从实现精确还原:跳过空文本片段;每个片段生成一条字幕,startMs/endMs 取自 offsets.from/totimestampMs 取自第一个 token 的 t_dtw * 10,若 t_dtw === -1(无有效对齐)则为 nullconfidence 取第一个 token 的 p

转换出的 Caption[] 可以直接进入 @remotion/captions 的字幕流水线继续加工,例如调用其 createTikTokStyleCaptions() 等分页/组合函数生成逐屏字幕。仓库测试 convert-to-captions.test.ts 展示了这一典型接法:toCaptions() 的输出喂给 createTikTokStyleCaptions({captions, combineTokensWithinMilliseconds: 200}),并断言了每个字幕页面的 text/startMs/durationMs/tokens

注意弃用项 convertToCaptions

旧版 convertToCaptions({transcription, combineTokensWithinMilliseconds}) 仍以兼容导出存在(index.ts),但从 v4.0.216 起被 @deprecated,官方明确建议改用 toCaptions()。两者差异:旧的返回结构是 {text, startInSeconds}(以秒为单位的起始时间、按 token 阈值手工合并),且没有毫秒级结束时间与置信度,无法与 @remotion/captions 的类型无缝对接;测试中以 0ms 阈值调用它得到的是逐词 startInSeconds 列表。新项目一律使用 toCaptions()

七、语言参数:Language 联合类型

transcribe()language 参数类型来自 languages.ts,覆盖 Whisper 官方支持的全量语言,可传完整名称('Chinese''German'…)或 ISO 代码('zh''de'…),另有 'auto' 表示自动检测。底层会把该值 toLowerCase() 后作为 -l 参数传给 whisper-cli。如果你的音频非英语且不传语言,建议显式设置语言或 'auto',否则模型需自行探测语言,可能影响首次识别质量。

八、错误与边界情况速查

以上源码与测试可以总结出如下可预期的运行时行为:

场景 行为(printOutput 默认 true)
to 目录已存在且包含可执行文件 跳过安装,返回 {alreadyExisted: true}
to 目录已存在但缺可执行文件 抛错提示删除目录后重试
模型文件已存在且字节数一致 跳过下载,返回 {alreadyExisted: true}
模型文件已存在但字节数不符 抛错提示删除后重试
输入不是 .wav 抛错并给出 ffmpeg 转换命令
wav 采样率非 16 kHz 捕获 whisper stderr 后抛专门错误
模型未下载 在 spawn 前抛错,提示先调用 downloadWhisperModel()
进程被 AbortSignal 终止 Process was killed with signal ...

九、在 Remotion 生态中的实际应用场景

@remotion/install-whisper-cpp 解决的正是"程序化视频"生产线上"给素材生成配音字幕/网红式(TikTok 风格)字幕"这一环:开发者可以在构建 Remotion 视频的脚本或 Lambda/服务端渲染流程里,先安装引擎与模型,对配音 WAV 做转写,再通过 toCaptions() + @remotion/captions 的字幕工具把结果变成带时间轴的字幕数据,最终由 Remotion 组件逐屏渲染到画面上。整个过程无需手动运行 whisper 命令行、无需解析其自定义输出格式,全部由类型安全的 TypeScript API 封装,并支持 AbortSignal 取消、下载/转写双进度回调等长任务必备能力。

如果想继续深入,可阅读本包全部实现:download.ts(下载流与进度)、install-whisper-cpp.ts(平台安装)、transcribe.ts(whisper-cli 驱动)、to-captions.ts(字幕映射),以及两份测试 convert-to-captions.test.tsinstall-whisper-cpp.test.ts 中可运行的真实数据样例。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.8 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
594
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
916
1.83 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.58 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.01 K
516
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
388