技术选型不是难点,许可才是。把「字幕 + 口播 + 动效」拆成七段之后,每段都有成熟可自托管的方案;真正会让项目卡死的是动效渲染层的许可、几个被误认为 MIT 的依赖,以及最容易被低估的词级对齐。
这篇给出七段结构、每段的验收断言与失败降级,字幕可读性用一手标准(BBC 与 Netflix 原文数值),并附缓存键设计——它决定你每月花多少钱。
最重要的一个发现是反直觉的:在动效渲染这一层,许可最干净、且被五万星验证过的选项是 HeyGen 的 HyperFrames(Apache-2.0),而不是 Remotion。
结论:技术选型不是难点,许可才是。
把「字幕 + 口播 + 动效」拆成七段之后,每一段都有成熟、免费、可自托管的方案,组合起来并不难。真正会让项目在半年后卡死的是三件事:动效渲染层的许可(Remotion 对 4 人以上公司收费,且自动化按次计费)、几个被误认为 MIT 的依赖其实是 GPL/LGPL、以及词级时间对齐这个最容易被低估的环节。
而这次调研最大的收获是一个反直觉的发现:在动效渲染这一层,许可最干净、且被 5 万星验证过的选项,是 HeyGen 的 HyperFrames(Apache-2.0),不是 Remotion。
标注说明:第一方=我亲自抓取的官方页面/仓库/registry,推断=我的判断,未验证=明确没做。本文是设计方案,不是实测报告——我没有在本机跑通整条流水线(原因见文末)。工具对比部分由 上篇延伸而来,两篇互相引用。
HyperFrames 与 Remotion:许可落差是数量级的
这一节是全文最该先看的部分,因为它决定你能否把产品做成生意。
| 维度 | HeyGen HyperFrames | Remotion |
|---|---|---|
| 许可 | Apache-2.0 | source-available(GitHub 判为 NOASSERTION,官方自认非 OSI 开源) |
| 免费阈值 | 无商用阈值 | 个人 / ≤3 人营利组织;≥4 人必须购买公司授权 |
| 人数如何计算 | — | 官方原文:合作方/承包方人数合并计算进 4 人阈值 |
| 自动化/批量计费 | 无按次费用 | $0.01 / render,$100/月最低消费 |
| 席位价 | — | $25/席/月(Creators) |
| 企业档 | — | $500/月起 |
| 多租户 SaaS | 允许(Apache-2.0) | 有条件允许;禁止让用户上传自己的 Remotion 工程来渲染 |
| 社区规模 | 50,496★ / 4,607 fork | 59,000+★ |
| 确定性承诺 | 官方原文「same input, same frames, same output」 | 逐帧渲染,确定性良好 |
两个项目我都一手核实过(第一方,2026-09-16):
- HyperFrames:仓库
heygen-com/hyperframes,Apache-2.0,50,496 星 / 4,607 fork,2026-03-10 创建且当日仍在推送;自我描述是 “Write HTML. Render video. Built for agents.” - Remotion:官方定价页(页面标注 2026-09-15 更新)明确写出免费档、
$0.01/render + $100/mo的 Automators 档、$25/mo/seat的 Creators 档、以及$500/mo起的 Enterprise。
四个会咬人的依赖
这部分独立于选型,任何人都可能踩到。
| 组件 | 常见误解 | 实际情况 | 影响 |
|---|---|---|---|
piper1-gpl(TTS) |
Piper 是 MIT | 新的 piper1-gpl 是 GPL-3.0;原 rhasspy/piper 已归档 |
GPL 传染风险,闭源分发前必须处理 |
edge-tts |
是 MIT | 主体是 LGPLv3 | 可作为独立进程/服务隔离调用;静态链接进闭源产物则需注意 |
| Coqui XTTS(TTS) | 开源可商用 | Coqui Public Model License;其条款页面实测 404,无法核实 | 条款不可核实=不可依赖 |
| FFmpeg | 免费所以随便用 | 默认 LGPLv2.1+;一旦 --enable-gpl 且含 libx264/libx265 即 GPL;--enable-nonfree 不可再分发 |
风险不在钱,在传染性与 H.264/AAC 专利池 |
顺带一提上一篇的结论在这里也成立:Hypit 自身是修改版 Apache-2.0,禁止多租户服务与商业再分发,而它底层的 HyperFrames 是干净的 Apache-2.0。要做产品,走上游。
七段结构:每段都要有可验收的出口
我把这条链路设计成七段,每段都有明确的输入、产物和验收断言。断言的意义是:出错时你能定位到段,而不是重跑整条。
| 段 | 输入 | 产物 | 验收断言 | 失败降级 |
|---|---|---|---|---|
| 2 口播 | 脚本分段 | 音频文件(wav 48k) | 响度落在目标区间;无削波(真峰 < −1 dBTP) | 换音色重试;整段失败则切分重试 |
| 3 对齐 | 音频 + 已知文本 | 词级 JSON | 词数等于输入词数;单调递增无重叠;每词置信度高于阈值 | 降级为句级时间轴并告警(不要静默产出错时间) |
| 4 字幕 | 词级 JSON | ASS / SRT | CPS ≤ 上限;每行 ≤ N 字;行数 ≤ N;落在安全区内 | 自动重断行 / 拆条;仍超限则报错 |
| 5 动效 | 词级 JSON + 结构 | 帧可求值的合成 | 同一帧号必得同一画面(确定性) | 关掉非必要动效,保字幕 |
| 6 渲染 | 合成 + 音频 | mp4 | 时长一致;无黑帧/丢帧;分辨率与宽高比正确 | 降低并发;降分辨率重渲 |
| 7 验收 | mp4 + 断言集 | 通过/不通过 + 报告 | 全部自动断言通过;抽样人工确认首尾帧与字幕 | 定位到段重跑,不整片重来 |
字幕可读性:用一手标准,别用社区经验值
这一节的所有数字我都从一手文档亲自核实并保留了原文引文。
BBC Subtitle Guidelines(竖屏有官方数值)
第一方——我抓取 bbc.co.uk 官网页原始 HTML 逐条核对。该版本变更日志明确写着新增了「9:16 竖屏的尺寸与位置指南」。
| 项目 | 16:9 横屏 | 9:16 竖屏 |
|---|---|---|
| 安全区 | 垂直中央 90%、水平中央 75% | 垂直中央 75%、水平中央 90%(对调) |
| 每行宽度 | 画面宽度的 68% | 画面宽度的 90% |
| 每行字符(换算指南) | 37 字符 | ≈25 字符 |
| 最大行数 | 2 行 | 3 行 |
| 行高(字号) | 画面高的 7%–8% | 画面高的 3.9%–4.5% |
BBC 原文两句关键引文(保留原文以免我转述失真):
"As a guide, the equivalent to 37 characters in a 75% width region of a
16:9 (landscape) video is 25 characters in a 90% width region of a
9:16 (vertical) video."
"For 9:16 video in portrait or vertical mode, this is reversed: subtitles
should not be placed outside the central 75% vertically and the central
90% horizontally."
阅读速度方面,BBC 给的是 160–180 词/分钟(即 0.33–0.375 秒/词)。
工程含义:按 1080×1920 折算,水平安全区 90% → x ∈ [54, 1026],垂直安全区 75% → y ∈ [240, 1680]。这两个像素值可以直接写进你的 QC 断言(推断,由 BBC 百分比换算)。
Netflix Timed Text Style Guide(简体中文有硬约束)
第一方——我抓取了简体中文规范原文。对中文口播最相关的几条:
- 每行 16 字符、最多 2 行(SDH 可放宽到 18 字符)。
- 阅读速度上限 9 字符/秒(儿童 7;SDH 成人 11)。
- 「不要用逗号和句号,改用单个空格」——原文 “Do not use commas or periods. Use one single space instead.” 这一条和中文写作直觉相反,很容易踩。
- 省略号用 U+2026,不支持 U+22EF;不用斜体;数字用半角、1–10 尽量写汉字;不写「星期2」。
- 行形态偏好「上短下长」的倒金字塔,避免顶行只有一两个词。
边界说明:Netflix 的规则是为 Netflix 交付制定的,不是平台通用规范。用它当阈值属于「借用更严的行业标准」,安全但要明白它不是为短视频设计的。推断
词级对齐:先有文本,再合成语音
这是七段里最容易被低估、也最容易毁掉成品观感的一段。
核心设计决策:让对齐发生在「已知文本」上,而不是让 ASR 去猜文本。
| 路线 | 做法 | 问题 |
|---|---|---|
| ASR-first | 先有音频 → Whisper 转写 + 词级时间戳 | Whisper 的词级时间戳是启发式(由 token 时间与注意力对齐推出),并不保证与音素边界一致;还会改写文本、漏词、合并数字与单位 |
| script-first | 先有脚本 → TTS 合成 → 用已知文本做强制对齐 | 需要真正的强制对齐工具,多一步;但文本 100% 正确,时间边界可靠 |
强制对齐的可用工具:WhisperX(在 Whisper 之上接 wav2vec2 做音素级对齐,社区最常用)、Montreal Forced Aligner(学术标准,准确但需准备发音词典与声学模型)、ctc-forced-aligner(轻量)。第三方资料——这几个工具的定位来自其官方仓库说明,我没有做实测精度对比。
输出契约建议钉死成这样一个结构,字段少而硬:
{
"words": [
{ "w": "改稿", "start": 1.240, "end": 1.605, "conf": 0.94 },
{ "w": "即", "start": 1.605, "end": 1.720, "conf": 0.88 }
],
"audio": { "path": "vo.wav", "sr": 48000, "lufs": -14.2, "truePeak": -1.4 },
"scriptHash": "sha256:..."
}
动效必须由帧号驱动,不能由真实时间驱动
这一条是「能不能做视频」和「能不能做可复现的视频」的分界线。
视频渲染的本质是:对第 n 帧求值一次,得到一张图。因此动画函数必须是纯函数:frame → 画面。
// 正确:帧号驱动,同帧必得同画面
const t = frame / fps;
const y = interpolate(t, [0, 0.6], [40, 0], { easing: easeOut });
// 错误:依赖真实时间,同一帧在快慢机器上结果不同
const t = (Date.now() - startTime) / 1000;
const y = spring(t); // 结果不可复现,还可能丢帧
为什么 requestAnimationFrame 在视频渲染里是错的:它绑定的是真实时钟,渲染时若某帧耗时过长,动画状态会「跳」过去,于是同一份源码在不同机器上产出不同画面——这会让你无法做视觉回归测试,也让「改一句台词」的 diff 变得不可信。推断(这是渲染确定性的通行工程共识)
两种动效范式各有位置:
- 时间轴驱动:适合片头、转场、装饰性动效——它只关心「过了多久」。
- 事件驱动(词锚定):适合字幕高亮、强调、B-roll 切换——它关心「说到哪个词」。这是口水视频里收益最高的一类,因为它让「改台词」不再需要「重对时间」。
上篇拆解的 Hypit 正是把第二类做成了语言级原语(@name / @name! 的附着极性)。这个思路不需要它的整套框架也能借用:在你的词级 JSON 上定义锚点,让动效订阅锚点而不是秒数。
缓存键:决定你每月花多少钱
这一节是整条流水线的经济学。目标只有一个:改一句台词时,只重跑必要的那几段。
做法是内容寻址 + 分段键。每一段的缓存键由「该段的全部有效输入」组成:
| 段 | 缓存键应包含 | 改台词后是否失效 |
|---|---|---|
| 2 口播 | 文本 + 音色 id + 语速/情感参数 + 模型版本 + 音频格式 | 失效(必然重生成) |
| 3 对齐 | 音频哈希 + 文本哈希 + 对齐器版本 + 语言 | 失效 |
| 4 字幕 | 词级 JSON 哈希 + 字幕样式版本 + 平台规格 | 失效 |
| 5 动效 | 结构源码哈希 + 词级 JSON + 资源版本 | 局部失效(只重算受影响区间) |
| 6 渲染 | 合成定义哈希 + 编码参数 + 分辨率 | 局部失效(只重渲帧区间) |
关键设计:把便宜的段放在贵的前面,让贵的段输入尽量少。第 4/5 段是纯本地计算,改一百次都不花钱;第 2 段是花钱的。所以:
- 不要把样式参数写进第 2 段的键——换个字幕颜色不该重新生成配音。这正是上篇里
.svs(外观)与.svml(内容)分开的价值。 - 模型版本必须进键——服务方静默升级模型会让同样的输入产出不同声音,这是最隐蔽的不可复现来源。
- 渲染支持帧区间(像 Hypit 的
start-frame/end-frame-exclusive),审片就能只渲第 8–12 秒。
build-record 就整片重生成。你的流水线如果也做显式复用,请务必在改稿流程里把它变成默认动作,而不是等人想起来。更好的做法是自动脏传播:由内容哈希自动决定失效范围,作者不用做任何声明。
六个真实会踩的坑
| 故障 | 检测方式 | 降级策略 |
|---|---|---|
| 音频与字幕整体偏移 | 抽取首词时间与音频首帧做交叉验证;对拍一次已知音频 | 整体平移校正;偏移过大则回退句级时间轴并告警 |
| 中文显示成豆腐块 | 渲染前检查字体是否可解析(fc-list / 字体文件存在性) |
内置一份 CJK 字体随产物分发,不依赖系统字体 |
| Chromium 版本漂移导致视觉回归 | 固定浏览器版本并在 CI 里比对基准帧 | 锁版本;把浏览器当构建依赖而非系统依赖 |
| TTS 非确定性 | 同输入重复请求两次,比对音频哈希 | 把首次结果落盘缓存并复用;把它记进缓存键 |
| 字幕被平台 UI 遮挡 | 按 BBC 竖屏安全区断言坐标边界 | 整体上移到下三分之一的安全位置 |
| 响度不达标 / 真峰削波 | 测量整合响度与 true peak | 用响度归一化处理到目标值(-14 LUFS 一类平台目标) |
关于响度目标要提醒一句:不同平台的目标值不同,而且我没有逐一核实各平台的官方口径,这里不给出具体数字。请以你实际投放平台的官方文档为准,并把它做成可配置项而不是硬编码。未验证
最小可用技术栈与成本
| 段 | 推荐 | 许可 | 为什么不是别的 |
|---|---|---|---|
| 2 口播 | 商用 TTS API(按需)或 edge-tts(隔离调用) | LGPLv3(隔离) | 开源本地方案中,piper1-gpl 的 GPL-3.0 会让闭源分发变复杂 |
| 3 对齐 | WhisperX / faster-whisper | BSD-2 / MIT | 社区最成熟,词级精度足够;MFA 更准但部署重 |
| 4 字幕 | 自研排版器输出 ASS/SRT | 自有 | ASS 支持逐词高亮;排版规则要按上面的标准自己实现 |
| 5+6 动效与渲染 | HeyGen HyperFrames | Apache-2.0 | 无商用阈值、无按次费用、确定性承诺 |
| 6 编码 | FFmpeg(LGPL 构建) | LGPLv2.1+ | 不用 --enable-gpl 以避开 GPL 传染 |
成本结构(推断,取决于你的用量):软件许可可以是 $0(全 Apache-2.0/LGPL/BSD 组合);真实成本在① TTS 按量计费、② 你自己的机器或云算力、③ 如果选 Remotion 且团队 ≥4 人,则是持续的月费或按次费。
这条流水线我没有实测
- 本机没有 ffmpeg、没有 TTS、没有对齐库,我尝试安装但沙箱不允许写入系统目录,因而没有跑通任何一段。本文是设计,不是实测报告。未验证
- WhisperX / MFA / ctc-forced-aligner 的精度对比我没有实测,只给出定位。
- 响度目标值没有逐平台核实,故意不写具体数字。
- Remotion 的多租户细则引用自官方标注为「Upcoming」的 v5.0 Terms;当前生效版本正文我未能抓取,用例边界建议向官方书面确认。未验证
- Coqui XTTS 的许可原文页面实测 404,条款不可核实,因此不推荐依赖。
- 本文所有价格为 2026-09-16 快照,许可政策会变,落地前请回官方页面复核。
写作方式是「能核实的一手证据 + 明确标注的推断 + 诚实列出的空白」。把这套设计当成起点,而不是结论。