@hyperframes/producer
producer 包将 engine 的帧捕获与 FFmpeg 编码结合,提供完整的 HTML 到视频渲染管道。它支持 MP4 (h264) 和 WebM (VP9 带 alpha 透明度),并处理运行时注入、就绪门控、音频混音和可选的基于 Docker 的确定性渲染。
npm install @hyperframes/producer
何时使用
在以下场景使用 @hyperframes/producer:
- 在 Node.js 中以编程方式将合渲染为 MP4 或 WebM(例如在后端服务或 CI 流水线中)
- 构建具有细粒度管道控制的自定义渲染服务
- 针对黄金基线运行视觉回归测试
- 在不同配置下基准测试渲染性能
如果需要以下功能,请使用其他包:
- 不写代码从命令行渲染 — 使用 CLI(
npx hyperframes render) - 在浏览器中预览合成 — 使用 CLI 或 studio
- 不编码直接捕获帧 — 使用 engine
- 检查或解析合成 HTML — 使用 core
💡 Tip
如果你正在构建只需要渲染视频的 Web 应用或脚本,CLI 是最快的路径。producer 包适用于需要在 Node.js 中进行编程控制的场景。
功能说明
producer 编排整个渲染管道:
加载合成 HTML
读取你的 index.html 和所有引用的子合成。
注入 Hyperframes 运行时
添加管理时间轴跳转、clip 生命周期和媒体播放的运行时脚本。
等待就绪门控
轮询 window.__playerReady 和 window.__renderReady,确保所有资源(字体、图片、视频)在捕获开始前加载完毕。
通过 engine 捕获帧
使用 engine 的 BeginFrame 管道将每一帧捕获为像素缓冲区。
通过 FFmpeg 编码为 MP4 或 WebM
将帧缓冲区通过管道送入 FFmpeg,使用选定的质量预设。MP4 使用 h264;WebM 使用 VP9 并支持 alpha 透明度。
混音音频轨道
从视频 clip 和音频元素中提取音频,应用 data-volume 和 data-media-start 偏移,并将它们混入最终的 MP4。
编程方式使用
producer 使用两步 API:创建渲染任务配置,然后执行它。
import { createRenderJob, executeRenderJob } from '@hyperframes/producer';
const job = createRenderJob({
fps: 30,
quality: 'standard',
});
await executeRenderJob(job, './my-video', './output.mp4');
渲染配置
import { createRenderJob } from '@hyperframes/producer';
const job = createRenderJob({
fps: 30, // 整数,或 { num: 30000, den: 1001 } 用于 NTSC
quality: 'standard', // 'draft'、'standard' 或 'high'
format: 'mp4', // 'mp4'、'webm'、'mov' 或 'png-sequence'
workers: 4, // 并行渲染 worker 数量 (1-8)
useGpu: false, // GPU 加速编码
debug: false, // 调试日志
});
WebM 透明度
设置 format: 'webm' 使用 VP9 alpha 渲染透明背景:
const job = createRenderJob({
fps: 30,
quality: 'standard',
format: 'webm',
});
await executeRenderJob(job, './my-overlay', './overlay.webm');
当 format: 'webm' 时:
- 帧以 PNG 格式捕获(保留 alpha 通道)
- Chrome 的页面背景通过 CDP 设置为透明
- FFmpeg 使用 VP9 +
yuva420p像素格式编码 - 音频编码为 Opus(而非 MP4 的 AAC)
HDR 输出
设置 hdr: true 启用 HDR 检测。producer 会探测每个视频和图像源的 BT.2020 / PQ / HLG 颜色标签 — 如果发现任何 HDR 源,输出将使用 H.265 10-bit BT.2020 并带有 HDR10 静态元数据。仅 SDR 的合成不受影响。
const job = createRenderJob({
fps: 30,
quality: 'standard',
format: 'mp4',
hdr: true,
});
await executeRenderJob(job, './my-video', './output.mp4');
当 hdr: true 时:
- 通过
ffprobe探测源;当同时存在 PQ 和 HLG 时,PQ 优先 - HDR 视频和图像提取为 16 位线性光像素并原生合成
- SDR DOM 覆盖层从 sRGB 转换为 BT.2020 后叠加
- 输出使用
libx265配合yuv420p10le和 HDR10 母版/内容光照级别元数据 format必须为'mp4'—'mov'和'webm'会回退到 SDR- HDR
<img>支持仅限静态图片;动画 HDR 标签图片仅使用第一帧
有关源要求、回退规则和验证的完整详情,请参阅 HDR 渲染。
进度回调
import type { ProgressCallback, RenderStatus } from '@hyperframes/producer';
const onProgress: ProgressCallback = (status: RenderStatus) => {
console.log(`Status: ${status}`);
// 状态:"queued" | "preprocessing" | "rendering" | "encoding"
// | "assembling" | "complete" | "failed" | "cancelled"
};
取消
import { RenderCancelledError } from '@hyperframes/producer';
ry {
await executeRenderJob(job);
} catch (err) {
if (err instanceof RenderCancelledError) {
console.log(`Cancelled: ${err.reason}`);
// reason: "user_cancelled" | "timeout" | "aborted"
}
}
HTTP 服务器
producer 包含一个内置 HTTP 服务器,用于作为渲染服务运行:
import { startServer } from '@hyperframes/producer/server';
await startServer({ port: 8080 });
服务器端点
| 方法 | 路径 | 描述 |
|---|---|---|
POST | /render | 阻塞渲染 — 返回 JSON 结果 |
POST | /render/stream | 使用 Server-Sent Events 的流式渲染 |
POST | /lint | 检查合成的问题 |
GET | /health | 健康检查 |
GET | /outputs/:token | 下载渲染后的 MP4 |
要进行自定义服务器集成,使用更底层的处理器:
import { createRenderHandlers, createProducerApp } from '@hyperframes/producer/server';
// 获取单个请求处理器
const handlers = createRenderHandlers(options);
// 或获取完整的 Hono 应用
const app = createProducerApp(options);
Docker 渲染
为了获得确定性输出,producer 可以在 Docker 容器中渲染,使用固定的 Chrome 版本和字体集。这保证了跨机器的输出一致 — 对 CI 流水线和生产服务至关重要。
# 通过 CLI(推荐)
npx hyperframes render --docker --output output.mp4
质量预设
| 预设 | 分辨率 | 编码 | 使用场景 |
|---|---|---|---|
draft | 原始 | 快速 CRF | 快速迭代,预览编辑 |
standard | 原始 | 平衡 CRF | 生产渲染,分享 |
high | 原始 | 高质量 CRF | 最终交付,存档 |
GPU 编码
producer 支持硬件加速编码以获得更快的渲染:
| 平台 | 编码器 | 选择方式 |
|---|---|---|
| NVIDIA | NVENC | 自动检测 |
| macOS | VideoToolbox | 自动检测 |
| Linux | VAAPI | 自动检测 |
| Intel | QSV | 自动检测 |
| Windows 上的 AMD | AMF | 自动检测 |
启用 GPU 编码时,Hyperframes 会自动检测可用的 FFmpeg 硬件编码器。要检查你的系统能力:
npx hyperframes doctor
CLI 自动启用本地 Chrome/WebGL GPU 捕获,并支持 --no-browser-gpu 作为退出选项。直接使用 producer API 时,传入引擎配置覆盖:
import { resolveConfig } from '@hyperframes/producer';
const job = createRenderJob({
fps: 30,
quality: 'standard',
producerConfig: resolveConfig({ browserGpuMode: 'hardware' }),
});
附加导出
producer 还为了方便重新导出了关键的 engine 功能:
| 导出 | 描述 |
|---|---|
createCaptureSession() | 创建帧捕获会话 |
initializeSession() | 用合成初始化会话 |
captureFrame() / captureFrameToBuffer() | 捕获单个帧 |
closeCaptureSession() | 清理捕获会话 |
getCompositionDuration() | 获取总合成时长 |
getCapturePerfSummary() | 获取捕获性能指标 |
createFileServer() | 创建 HTTP 文件服务器以提供资源 |
createVideoFrameInjector() | 为页面创建视频帧注入器 |
resolveConfig() / DEFAULT_CONFIG | Producer 配置 |
createConsoleLogger() / defaultLogger | 日志工具 |
quantizeTimeToFrame() | 将时间转换为帧边界 |
resolveRenderPaths() | 解析渲染目录路径 |
prepareHyperframeLintBody() / runHyperframeLint() | Lint 工具 |
日志
producer 提供了一个小型可插拔的日志记录器,调用者可以注入 Pino、Winston 或任何结构化后端,而无需对其进行依赖。
export type LogLevel = "error" | "warn" | "info" | "debug";
export interface ProducerLogger {
error(message: string, meta?: Record<string, unknown>): void;
warn(message: string, meta?: Record<string, unknown>): void;
info(message: string, meta?: Record<string, unknown>): void;
debug(message: string, meta?: Record<string, unknown>): void;
isLevelEnabled?(level: LogLevel): boolean;
}
createConsoleLogger(level) 返回一个基于控制台的实现,按级别过滤并对可选的 meta 对象进行 JSON 序列化。defaultLogger 是 level="info" 的单例。
在热路径中跳过昂贵的元数据构建
isLevelEnabled 是可选的,这样现有的自定义日志记录器可以继续正常工作。当你在热循环中构建一个非平凡的 meta 对象只是为了附加到调试日志时,使用空值合并模式来保护构造过程,这样生产运行(level=info)不会产生任何开销,而没有此方法的日志记录器行为完全不变:
// 在编码管道的每帧循环中:
if (i % 30 === 0 && (log.isLevelEnabled?.("debug") ?? true)) {
const hdrEl = stackingInfo.find((e) => e.isHdr);
log.debug("[Render] HDR layer composite frame", {
frame: i,
ime: time.toFixed(2),
hdrElement: hdrEl
? { z: hdrEl.zIndex, visible: hdrEl.visible, width: hdrEl.width }
: null,
stackingCount: stackingInfo.length,
activeTransition: activeTransition?.shader,
});
}
?? true 回退意味着使用未实现 isLevelEnabled 的自定义日志记录器的调用者继续构建和传递 meta 对象 — 此优化是日志记录器实现的可选功能。
回归测试
producer 包含一个回归测试工具,用于将渲染输出与黄金基线进行比较。这在更改运行时、engine 或渲染管道时捕获视觉回归非常有用。
cd packages/producer
# 构建测试 Docker 镜像
bun run docker:build:test
# 运行回归测试(将输出与黄金基线进行比较)
bun run docker:test
# 在有意更改后重新生成黄金基线
bun run docker:test:update
基准测试
为你的硬件找到最优的渲染设置:
# 通过 CLI
npx hyperframes benchmark
# 直接从 producer 包
cd packages/producer
bun run benchmark
基准测试使用不同的质量和 FPS 设置运行多个合成,并报告每种组合的耗时。
外部资源(projectDir 外的文件)
合成可以引用项目目录之外的资源绝对路径 — ~/Downloads 中的本地旁白、共享驱动器上的图片、绝对路径上的生成 fixture。producer 通过以下方式处理这些资源:
- 检测。 在编译期间,HTML 编译器遍历每个
[src]/[href]和<style>中的每个url(...)。解析到projectDir之外文件的路径被收集到externalAssets映射中。 - 清理的键名。 每个绝对路径被转换为安全的、跨平台的相对键名,前缀为
hf-ext/。Windows 驱动器字母冒号被去除(D:\foo\x.wav→hf-ext/D/foo/x.wav),这样path.join(compileDir, key)在每个操作系统上都保持在编译目录内。 - 复制 + 重写。 编排器将文件复制到
<compileDir>/hf-ext/...下,HTML 被重写为指向清理后的键名。文件服务器随后从同一根目录提供项目内部和外部资源。
包含性检查使用 path.relative() 而非硬编码的分隔符,因此外部资源在 macOS、Linux 和 Windows 上的行为完全相同。参见 packages/producer/src/utils/paths.ts 了解辅助函数。