首页
/ OpenMontage 字幕接入实战:使用 @remotion/captions 将 SRT 字幕导入 Remotion 合成

OpenMontage 字幕接入实战:使用 @remotion/captions 将 SRT 字幕导入 Remotion 合成

2026-09-08 21:24:47作者:滕妙奇

导读

本文讲解在 OpenMontage 的 Remotion 合成工作流(remotion-composer)中,如何把已有的 .srt 字幕文件通过 @remotion/captions 包导入并解析为标准 Caption 数据结构,从而复用整套字幕渲染工具链。读完本文,你将掌握依赖安装、staticFile() + fetch() + parseSrt() 的完整接入模式、useDelayRender() 的异步阻塞渲染技巧,以及解析结果与分词高亮、TikTok 风格分页等下游渲染能力的衔接方式。

背景:SRT 导入在 OpenMontage 字幕链路中的位置

OpenMontage 的字幕处理有一条清晰的链路:语音转写 → 字幕生成 → 字幕导入 → 字幕渲染。

  • 生成侧tools/subtitle/subtitle_gen.py(即 subtitle_gen 工具)负责把转写结果转换为 SRT、VTT 或带词级时间戳的 caption JSON(参见技能文档 skills/core/subtitle-sync.md);
  • 渲染侧remotion-composer 是 OpenMontage 的 Remotion 合成渲染器,其 remotion-composer/package.json 中直接声明了 "@remotion/captions": "^4.0.484",与 @remotion/cliremotion 等核心依赖版本对齐(均为 4.0.x);
  • 导入侧:当手头已经存在 FFmpeg、YouTube 上传、外部剪辑流程产出的 .srt 文件时,就需要本文讲解的 parseSrt() 导入路径,将 SRT 文本转换成 Remotion 可用的 Caption 数组。

SRT 是行业通用的字幕交换格式,但 Remotion 的字幕 API 统一以 JSON 形式的 Caption 对象为内部数据模型。因此导入的本质是一个格式归一化过程:.srt → 文本 → parseSrt()Caption[]。归一化之后,所有 @remotion/captions 的实用工具都可以直接消费这些数据。

前置条件:安装 @remotion/captions

首先需要安装 @remotion/captions 包。OpenMontage 的 remotion-composer 已将其列为正式依赖,若在全新项目中尚未安装,官方推荐通过 Remotion CLI 的 add 子命令安装,它会自动挑选与当前 remotion 主包兼容的版本:

npx remotion add @remotion/captions    # 项目使用 npm
bunx remotion add @remotion/captions   # 项目使用 bun
yarn remotion add @remotion/captions   # 项目使用 yarn
pnpm exec remotion add @remotion/captions # 项目使用 pnpm

以 OpenMontage 当前仓库为例,remotion-composer 使用的是 npm 生态,直接在 package.jsondependencies 中声明即可,remotion add 命令会自动同步该依赖。安装后可以从包中引入两个关键导出:解析函数 parseSrt() 与类型 Caption

第一步:将 .srt 文件放入 public 目录

Remotion 通过 staticFile() 引用 public/ 目录下的静态资源(图片、视频、音频、字体,以及字幕文件)。约定如下:

  1. 在项目的 public/ 目录中放置字幕文件,例如 public/subtitles.srt
  2. 组件中通过 staticFile("subtitles.srt") 得到可在浏览器与渲染进程中解析的静态 URL;
  3. 通过 fetch() 拉取该文件并读取文本内容,再交给 parseSrt()

staticFile() 的底层机制是:本地 remotion studio 预览与 remotion render 渲染时会自动把 public/ 目录映射为静态服务器根路径,因此该写法在开发预览与最终渲染两种场景下行为一致,不会出现路径漂移。

第二步:读取并解析 .srt(完整示例)

下面是原规则文档给出的完整接入代码,展示了标准的"延迟渲染(DelayRender)"异步模式:

import { useState, useEffect, useCallback } from "react";
import { AbsoluteFill, staticFile, useDelayRender } from "remotion";
import { parseSrt } from "@remotion/captions";
import type { Caption } from "@remotion/captions";

export const MyComponent: React.FC = () => {
  const [captions, setCaptions] = useState<Caption[] | null>(null);
  const { delayRender, continueRender, cancelRender } = useDelayRender();
  const [handle] = useState(() => delayRender());

  const fetchCaptions = useCallback(async () => {
    try {
      const response = await fetch(staticFile("subtitles.srt"));
      const text = await response.text();
      const { captions: parsed } = parseSrt({ input: text });
      setCaptions(parsed);
      continueRender(handle);
    } catch (e) {
      cancelRender(e);
    }
  }, [continueRender, cancelRender, handle]);

  useEffect(() => {
    fetchCaptions();
  }, [fetchCaptions]);

  if (!captions) {
    return null;
  }

  return <AbsoluteFill>{/* Use captions here */}</AbsoluteFill>;
};

关键模式拆解

  • useDelayRender():Remotion 的渲染是确定性的帧序列计算,不等待异步操作。delayRender() 会"暂停"渲染计时器,通知渲染器"还有异步数据未就绪";数据到达后调用 continueRender(handle) 放行渲染;一旦 fetch 或解析失败,则调用 cancelRender(e) 主动终止渲染并抛出错误。这是 Remotion 中加载外部资源的标准且必须的模式,否则字幕可能因竞态条件而缺失。
  • parseSrt({ input })@remotion/captions 提供的 SRT 解析函数,接受 { input: string },返回 { captions: Caption[] }。它会处理 SRT 的序号行、时间轴行(HH:MM:SS,mmm --> HH:MM:SS,mmm)与多行文本块,并合并时间相邻或语义连续的字幕片段。
  • 加载态渲染 null:在 captions 尚未就绪时渲染 null,避免把空数据传给下游渲染组件。

远程 URL 同样支持

staticFile() 并非唯一数据来源。规则文档明确指出,远程 URL 同样受支持——直接把 staticFile("subtitles.srt") 替换为远程字幕文件的完整 URL 即可,例如从 CDN、对象存储或字幕托管服务拉取:

const response = await fetch("https://example.com/subtitles.srt");

这使字幕数据可以与视频素材分离部署,适合 OpenMontage 这类由流水线动态产出素材的场景(素材由工具生成后上传到对象存储,字幕随之远程引用)。

第三步:解析结果——Caption 数据结构

parseSrt() 的输出统一为标准 Caption 类型。remotion-best-practices 技能的字幕规则(.agents/skills/remotion-best-practices/rules/subtitles.md)给出了完整定义:

type Caption = {
  text: string;          // 该字幕块的文本内容
  startMs: number;       // 起始时间(毫秒)
  endMs: number;         // 结束时间(毫秒)
  timestampMs: number | null;  // 单字/单 token 级时间戳(词级数据才有值)
  confidence: number | null;   // 识别置信度(转写来源才有值)
};

注意两点:

  • SRT 导入而言,SRT 文件通常只携带块级起止时间,因此 timestampMsconfidence 一般为 null
  • Whisper 转写transcribe() + toCaptions(),见 .agents/skills/remotion-best-practices/rules/transcribe-captions.md)而言,这两个字段会被填充——timestampMs 支撑词级高亮,confidence 支撑质量过滤。

也就是说,导入 SRT 后得到的 Caption[] 与转写生成的 Caption[] 在类型上完全同构,下游渲染代码无需区分数据来源。

第四步:使用导入的字幕

解析完成后,字幕即处于 Caption 格式,可以使用 @remotion/captions 的全部实用工具。典型的下游能力包括:

4.1 按时间过滤

在渲染某一段画面时,用起止时间过滤出"当前应该显示"的字幕子集,实现字幕与画面的精确对齐。

4.2 TikTok 风格分页与词级高亮

createTikTokStyleCaptions() 可将 Caption[] 按时间窗口聚合为"页面"(TikTokPage),每页包含若干 token,供逐词高亮使用(详见 .agents/skills/remotion-best-practices/rules/display-captions.md)。combineTokensWithinMilliseconds 控制单页单词数量——值越大每页词越多,越小越接近逐词跳动。

4.3 仓库内的同构实践:CaptionOverlay

OpenMontage 的 remotion-composer 已实现了一套与 TikTokPage 理念同构的自研字幕叠加组件 remotion-composer/src/components/CaptionOverlay.tsx,可作为"字幕数据如何渲染"的参考实现:

  • WordCaption 接口(word / startMs / endMs / pageBreakAfter)与 @remotion/captions 的 token 模型一致,其中 pageBreakAfter 专门服务于 CJK 字幕按句读分页;
  • buildPages()wordsPerPage 与分页标记把词流切成页,页的起止时间取自首词与末词;
  • 渲染层对每个词计算 isActive / isPast 状态:当前正在说的词用高亮色 + 光晕,已说过的词用主色,未到的词用半透明色,配合 spring() 入场动画;
  • 页面通过 <Sequence from durationInFrames> 挂载到时间轴,与规则文档中"用 Sequence 承载页面"的做法一致。

该组件在 remotion-composer/src/Root.tsx 中被注册为独立的 CaptionOverlayOnly 合成(composition),并集成进 remotion-composer/src/CinematicRenderer.tsxremotion-composer/src/TalkingHead.tsx 的主渲染流程中——即使数据来自外部 SRT 导入,也可以直接喂给这类 WordCaption[] 接口。

实操注意事项

  • 空白敏感Caption.text 是空白敏感的,SRT 文本中的空格会原样保留。渲染时若逐词拼接,应使用 whiteSpace: "pre"(或 pre-wrap)保留词间空格,避免单词粘连。CaptionOverlay 组件正是通过 whiteSpace: "pre-wrap" 配合 wordSeparator 参数处理空格语言与 CJK 两种场景。
  • 字幕后置处理:SRT 中可能包含 HTML 标签(如 <i><font>)或破行,parseSrt() 解析后建议按业务需要做清洗(去除标签、合并断行),再进入渲染。
  • SRT 与其他格式的分工:SRT 适合通用分发(FFmpeg、播放器、YouTube 上传);若需要词级高亮等精细能力,优先使用 subtitle_gen 产出的带 timestampMs 的 caption JSON(见 skills/core/subtitle-sync.md)。SRT 导入路径的价值在于复用存量字幕资产,避免重复转写。
  • 错误处理fetch 失败、SRT 语法非法都会触发 cancelRender(e),渲染任务会明确失败而非产出缺失字幕的成片——这符合 OpenMontage 对交付质量的确定性要求。

小结

在 OpenMontage 中接入存量 SRT 字幕的完整路径是:安装 @remotion/captions → 字幕文件放入 public/fetch(staticFile(...)) 读取 → parseSrt() 归一化为 Caption[] → 交给 @remotion/captions 工具链或 remotion-composerCaptionOverlay 渲染。整个过程与转写生成字幕共用同一数据模型,让"外部字幕资产"与"内部转写资产"在渲染层完全打通。需要生成而非导入字幕时,可继续阅读 .agents/skills/remotion-best-practices/rules/transcribe-captions.md;需要设计逐词高亮等展示细节时,可参考 .agents/skills/remotion-best-practices/rules/display-captions.md 与仓库内的 CaptionOverlay 实现

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

项目优选

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