首页
/ Remotion 字幕转写实战:用 @remotion/install-whisper-cpp 将音频转成 Caption JSON

Remotion 字幕转写实战:用 @remotion/install-whisper-cpp 将音频转成 Caption JSON

2026-09-07 19:04:40作者:卓炯娓

本指南聚焦 Remotion 中"语音转字幕"的完整管线:如何下载并编译 Whisper.cpp、拉取模型、将音频转写为带词级时间戳的 JSON,再通过 toCaptions() 归一化为标准 Caption 结构供 Remotion 渲染使用。这是 AI 配音/口播类视频、TikTok 风格逐词高亮字幕项目的前置数据环节,读完你可以独立写出可复现的音频转写脚本并产出可用于 Remotion 的字幕 JSON 文件。

整体流程概览

字幕数据的生成不发生在 Remotion 组件内部,而是发生在"渲染之前"的一次性离线步骤中。典型链路为:

  1. 安装 Whisper.cpp 可执行文件(installWhisperCpp());
  2. 下载对应的 GGML 量化模型(downloadWhisperModel());
  3. 准备符合要求的 16kHz WAV 音频;
  4. 调用 transcribe() 让本地 Whisper.cpp 引擎完成语音转写,返回 TranscriptionJson
  5. toCaptions() 做推荐的后处理,得到标准 Caption[]
  6. 将结果写入 public/ 或作为 JSON 文件落盘,供 Remotion 组件 fetch(staticFile(...)) 加载展示。

其中第 5 步产出的 Caption 类型定义在 @remotion/captions 中,是整个 remotion-captions 系列能力(展示、分页、逐词高亮、SRT 导入)的公共数据契约。本技能配套的 remotion-captions 已完整描述了从转写到显示、再到 .srt 导入的完整工作流,本文聚焦其中的"转写"环节。

前置准备:安装 @remotion/install-whisper-cpp

本指南对应的转写能力由 @remotion/install-whisper-cpp 包提供。该包位于 packages/install-whisper-cpp,内部实现为"帮助安装与调用 Whisper.cpp 的一组合法方法",依赖 @remotion/captions(用于输出标准的 Caption 类型,见 package.json)。

在 Remotion 项目中通过以下命令安装:

npx remotion add @remotion/install-whisper-cpp

其对外导出的 API 集合(见 src/index.ts)包括:

导出 说明
installWhisperCpp 下载、编译(或解压)Whisper.cpp 可执行文件
downloadWhisperModel 从 Hugging Face 下载指定 GGML 模型到本地目录
transcribe 调用 Whisper.cpp 二进制对 WAV 执行转写,返回完整 JSON
toCaptions 官方推荐的转写结果 → Caption[] 归一化后处理
convertToCaptions 旧版后处理 API(自 Remotion v4.0.216 起已弃用,请改用 toCaptions()

三步拿到字幕 JSON

官方文档给出的是一个"一段式"的 Node.js 脚本(可放 .mjs / .ts 中通过 tsxbun 等运行),依次完成安装引擎、下载模型、转写并后处理:

import path from "path";
import {
  downloadWhisperModel,
  installWhisperCpp,
  transcribe,
  toCaptions,
} from "@remotion/install-whisper-cpp";
import fs from "fs";

const to = path.join(process.cwd(), "whisper.cpp");

await installWhisperCpp({
  to,
  version: "1.5.5",
});

await downloadWhisperModel({
  model: "medium.en",
  folder: to,
});

// 若音频不是 16kHz WAV,先用 ffmpeg 做一次转换:
// import {execSync} from 'child_process';
// execSync('ffmpeg -i /path/to/audio.mp4 -ar 16000 /path/to/audio.wav -y');

const whisperCppOutput = await transcribe({
  model: "medium.en",
  whisperPath: to,
  whisperCppVersion: "1.5.5",
  inputPath: "/path/to/audio123.wav",
  tokenLevelTimestamps: true,
});

// 可选用官方推荐的后处理,得到标准 Caption 结构
const { captions } = toCaptions({
  whisperCppOutput,
});

// 写入 public/ 目录,便于 Remotion 通过 staticFile() 获取
fs.writeFileSync("captions123.json", JSON.stringify(captions, null, 2));

如果有多段素材,就分别对每一段音频执行转写、生成各自的 JSON 文件——字幕与视频一一对应,每个视频应有一份独立的字幕 JSON。生成 JSON 之后如何把字幕真正渲染到画面上(分页、逐词高亮等),见本技能系列的 display-captions.md

步骤一:安装 Whisper.cpp 引擎

installWhisperCpp() 负责把 Whisper.cpp 本体弄到本地目录。签名(见 install-whisper-cpp.ts):

await installWhisperCpp({
  to: string;          // 目标安装目录(相对/绝对均可,内部会 path.resolve 到 process.cwd())
  version: string;     // Whisper.cpp 版本号,如 "1.5.5"
  printOutput?: boolean; // 默认 true,是否透传编译/下载输出
  signal?: AbortSignal | null; // 用于取消操作
});

平台差异是这里最容易踩的坑,从源码可以确认内部行为分为两条路径:

  • macOS / Linux:先 git clone https://github.com/ggerganov/whisper.cpp.gitto,再用 git checkout 切到 v{version},最后执行 make 编译出二进制(见 install-whisper-cpp.ts)。因此这类平台需要本机装有 gitmake
  • Windows:只支持语义化版本号,且版本为 1.5.5 时从 Remotion 维护的二进制镜像站下载 whisper-bin-x64-1-5-5.zip,其余版本从 whisper.cpp release 下载,然后调用 PowerShell Expand-Archive 解压(见 install-whisper-cpp.ts)。

版本影响可执行文件的查找路径:getWhisperExecutablePath() 中,当版本 >= 1.7.4 时使用新目录 build/bin/whisper-cli(旧版 main 已弃用),否则使用根目录的 main 可执行文件(见 install-whisper-cpp.ts)。

installWhisperCpp() 返回 { alreadyExisted: boolean }:若目标目录已存在且可执行文件完好,则跳过安装并返回 true;若目录存在但缺少可执行文件,会抛出错误提示删除该目录重试,避免使用残缺安装。这与下载模型时的行为互相配合,使脚本具备可重复执行的特性——第二次运行会直接跳过已完成的下载与编译步骤。

提示:Whisper.cpp 的编译下载属于重操作,建议把上述脚本作为独立的"数据准备"任务在渲染前运行一次,而不是在 Remotion 渲染流程内反复执行。

步骤二:下载 Whisper 模型

downloadWhisperModel() 会从 https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-${model}.bin 拉取 GGML 格式模型:

await downloadWhisperModel({
  model: "medium.en",  // 模型标识,见下方列表
  folder: to,          // 模型存放目录(须与 installWhisperCpp 的 to 一致)
  printOutput?: boolean, // 默认 true
  onProgress?: (progress: {downloaded: number; totalSize: number}) => void;
  signal?: AbortSignal | null;
});

可用的模型列表在 download-whisper-model.ts 中硬编码为常量:

tinytiny.enbasebase.ensmallsmall.enmediummedium.enlarge-v1large-v2large-v3large-v3-turbo

命名规律直观:.en 后缀表示仅英文专用模型(体积更小、英文场景精度更高),不带后缀的为多语言模型。模型越大精度越高、同时下载体积与推理耗时也越大——上述列表对应文件的字节数(modelSizes 表,见 download-whisper-model.ts)范围从约 74 MB(tiny)到约 2.9 GB(large-v3),medium.en 约 1.4 GB。官方示例使用 medium.en 是精度与资源消耗的常见折中。

值得注意的幂等设计:源码中若目标文件已存在且字节数与 modelSizes 记录的期望大小完全一致,则直接返回 { alreadyExisted: true } 跳过下载;若大小不符会抛错提示删除文件重试(见 download-whisper-model.ts)。这为批量生成多段字幕的重复执行提供了安全检查。

模型文件会以 ggml-${model}.bin 命名落盘(getModelPath()download-whisper-model.ts)。模型目录与后续 transcribe()whisperPath 一致时,transcribe() 会自动找到模型;也可以单独用 transcribe({ modelFolder }) 指向其他目录。

步骤三:准备 16kHz WAV 音频(关键约束)

transcribe() 对输入文件有硬性校验,违反即抛错。从 transcribe.ts 可以确认两条规则:

  1. 必须是 .wav 文件:源码通过扩展名判断(isWavFile),否则抛 Invalid inputFile type. The provided file is not a wav file!
  2. 采样率必须是 16kHz:whisper.cpp 在处理时若检测到非 16kHz 会报 wav file must be 16 kHz,该错误会在转写进程退出后被识别并转成可读的异常信息(见 transcribe.ts)。

因此任意来源的素材(mp4、mp3、m4a……)都需先经 ffmpeg 转成 16kHz 单声道 WAV:

ffmpeg -i input.mp4 -ar 16000 output.wav -y

官方脚本把这一步以注释形式嵌在示例里;也可以在管道上游用 @remotion/media-parser / WebCodecs 的音频重采样能力处理。

步骤四:执行转写并理解返回结构

const whisperCppOutput = await transcribe({
  model: "medium.en",        // 与下载的模型保持一致
  whisperPath: to,           // installWhisperCpp 的安装目录
  whisperCppVersion: "1.5.5", // 与安装时版本一致(决定可执行文件路径)
  inputPath: "/path/to/audio123.wav", // 16kHz WAV
  tokenLevelTimestamps: true, // 关键:生成词级时间戳,是逐词高亮的前提
});

完整的参数列表与默认值可以从 transcribe.ts 的类型定义中确认:

参数 类型 默认值 说明
inputPath string 必填 待转写音频,须为 16kHz WAV
whisperPath string 必填 Whisper.cpp 安装目录
whisperCppVersion string 必填 与安装时一致的版本号,决定二进制路径
model WhisperModel 必填 模型标识,须已下载
tokenLevelTimestamps boolean 必填 是否启用词级时间戳(DTW);true 时返回结构更丰富
modelFolder string undefined 模型目录,缺省用 whisperPath
translateToEnglish boolean false 转写为英文(对应 whisper -tr
printOutput boolean true 是否向终端输出转写过程日志
tokensPerItem number | null 与时间戳模式联动 每 JSON item 容纳的 token 数;开启词级时间戳时强制为 1
language Language | null null 指定音频语言(自动检测不可用时使用)
splitOnWord boolean false 是否按词切分
signal AbortSignal null 取消转写
onProgress (progress: number) => void null 进度回调,参数为 0~1 的浮点
flashAttention boolean false 是否启用 Flash Attention 加速
additionalArgs string[] | [string, string][] 追加透传给 whisper.cpp 的额外参数

底层实现细节(见 transcribe.ts)值得了解,有助于你排错:

  • 它通过 spawn 启动 whisper.cpp 二进制,参数构造包含 --output-json(JSON 输出)、-ojf(full JSON)、-pp(输出进度)等;若开启 tokenLevelTimestamps 还会追加 --dtw {modelDtw},其中 modelToDtw() 负责把 large-v3 等模型名映射成 DTW 所需的 large.v3 这类点分命名(见 transcribe.ts);
  • 转写采用"先写临时文件再读取"的方式:输出写到 process.cwd()/tmp 目录下的 JSON,成功后读取并 unlinkSync 删除(见 transcribe.ts);
  • 进度解析来自 stdout/stderr 中的 progress = 字样(数值归一化为 0~1),并针对 macOS Metal 初始化结束行(ggml_metal_free: deallocating)做特殊处理以避免进程卡死(见 transcribe.ts)。

返回的 TranscriptionJson 结构(transcribe.ts)包含 systeminfomodelparamsresult 与核心的 transcription 数组;开启 tokenLevelTimestamps 时每个 transcription item 会携带 tokens(词级 token,含 t_dtw 时间戳、p 置信度与起止时间),这是后续逐词高亮的原始数据来源。类型上,transcribe 是泛型函数:tokenLevelTimestamps: true 会让返回类型推断为 TranscriptionJson<true>,从而在下一步提供给 toCaptions() 时获得完整类型检查。

后处理:toCaptions() 归一化

Whisper.cpp 原始输出的 JSON 结构并不直接等于 Remotion 的 Caption 结构,因此需要后处理。官方推荐的是 toCaptions()

const { captions } = toCaptions({ whisperCppOutput });

从实现(to-captions.ts)看,它会遍历 transcription,跳过空文本 item,并把每个 item 映射成:

{
  text: string;              // 首个 item 会 trimStart() 去除前导空格
  startMs: number;           // item.offsets.from(毫秒)
  endMs: number;             // item.offsets.to(毫秒)
  timestampMs: number | null;// 首个 token 的 t_dtw * 10;t_dtw 为 -1 时取 null
  confidence: number | null; // 首个 token 的 p(模型置信度)
}

输出即 @remotion/captions 中定义的 Caption 类型——即 textstartMsendMstimestampMsconfidence 五字段(可含可选的 pageBreakAfter),这也是整个 captions 生态的统一中间格式。

兼容性说明:早期版本暴露的 convertToCaptions()(按毫秒阈值把 token 合并为"句子",见 convert-to-captions.ts)自 Remotion v4.0.216 起被标记为 deprecated(index.ts),新代码一律使用 toCaptions()

从 JSON 到画面:下一步衔接

本步骤产出的 captions123.json 应放入 public/(或任意可被 fetch 的位置)。Remotion 组件内通过 useDelayRender() 挂起渲染、fetch(staticFile("captions123.json")) 加载并 continueRender() 释放渲染锁,再交由 @remotion/captionscreateTikTokStyleCaptions() 分组、用 <Sequence> 逐页渲染并配合 token 级 fromMs/toMs 实现逐词高亮。

完整展示链路见 display-captions.md;若你手上已是现成的 .srt 字幕,则无需本地转写,可直接用 parseSrt() 导入(见 import-srt-captions.md)。转写、显示、导入三份指南共同构成 remotion-captions 技能的完整知识面,SKILL 索引见 SKILL.md

常见问题速查

  • 报错 Whisper does not exist at ...whisperPath 目录不存在或未执行 installWhisperCpp();请先安装再转写。
  • 报错 Model ... does not exist:模型未下载,或 modelFolder/whisperPathdownloadWhisperModel({folder}) 的目录不一致。
  • 报错 must be 16 kHz / Invalid inputFile type:输入不是 16kHz WAV,用 ffmpeg -i in.mp4 -ar 16000 out.wav -y 先转换。
  • 重复运行重复下载:不会。安装与模型下载均有幂等检查(按目标可执行文件是否存在 / 模型文件字节数是否与预期一致判定)。
  • 中文等多语言音频:使用不带 .en 的模型(如 mediumlarge-v3-turbo),并可通过 language 参数指定语种。

需要澄清的是,Whisper.cpp 的安装需要目标环境具备 gitmake(Linux/macOS)或 Windows PowerShell;大型模型下载较耗时且占用数 GB 磁盘,请在资源充足的环境中执行本流程。

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

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.13 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
529
593
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.35 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.01 K
515
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
388