@hyperframes/producer

完整的 HTML 到视频渲染管道,支持编码、音频混音和 Docker。

producer 包将 engine 的帧捕获与 FFmpeg 编码结合,提供完整的 HTML 到视频渲染管道。它支持 MP4 (h264) 和 WebM (VP9 带 alpha 透明度),并处理运行时注入、就绪门控、音频混音和可选的基于 Docker 的确定性渲染。

npm install @hyperframes/producer

何时使用

在以下场景使用 @hyperframes/producer

  • 在 Node.js 中以编程方式将合渲染为 MP4 或 WebM(例如在后端服务或 CI 流水线中)
  • 构建具有细粒度管道控制的自定义渲染服务
  • 针对黄金基线运行视觉回归测试
  • 在不同配置下基准测试渲染性能

如果需要以下功能,请使用其他包:

  • 不写代码从命令行渲染 — 使用 CLInpx hyperframes render
  • 在浏览器中预览合成 — 使用 CLIstudio
  • 不编码直接捕获帧 — 使用 engine
  • 检查或解析合成 HTML — 使用 core

💡 Tip

如果你正在构建只需要渲染视频的 Web 应用或脚本,CLI 是最快的路径。producer 包适用于需要在 Node.js 中进行编程控制的场景。

功能说明

producer 编排整个渲染管道:

加载合成 HTML

读取你的 index.html 和所有引用的子合成。

注入 Hyperframes 运行时

添加管理时间轴跳转、clip 生命周期和媒体播放的运行时脚本。

等待就绪门控

轮询 window.__playerReadywindow.__renderReady,确保所有资源(字体、图片、视频)在捕获开始前加载完毕。

通过 engine 捕获帧

使用 engine 的 BeginFrame 管道将每一帧捕获为像素缓冲区。

通过 FFmpeg 编码为 MP4 或 WebM

将帧缓冲区通过管道送入 FFmpeg,使用选定的质量预设。MP4 使用 h264;WebM 使用 VP9 并支持 alpha 透明度。

混音音频轨道

从视频 clip 和音频元素中提取音频,应用 data-volumedata-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
Docker 模式需要 Docker 已安装并运行。运行 `npx hyperframes doctor` 验证你的环境。有关 Docker 模式确定性的详细信息,请参阅[确定性渲染](/concepts/determinism)。

质量预设

预设分辨率编码使用场景
draft原始快速 CRF快速迭代,预览编辑
standard原始平衡 CRF生产渲染,分享
high原始高质量 CRF最终交付,存档

GPU 编码

producer 支持硬件加速编码以获得更快的渲染:

平台编码器选择方式
NVIDIANVENC自动检测
macOSVideoToolbox自动检测
LinuxVAAPI自动检测
IntelQSV自动检测
Windows 上的 AMDAMF自动检测

启用 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_CONFIGProducer 配置
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 序列化。defaultLoggerlevel="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 通过以下方式处理这些资源:

  1. 检测。 在编译期间,HTML 编译器遍历每个 [src] / [href]<style> 中的每个 url(...)。解析到 projectDir 之外文件的路径被收集到 externalAssets 映射中。
  2. 清理的键名。 每个绝对路径被转换为安全的、跨平台的相对键名,前缀为 hf-ext/。Windows 驱动器字母冒号被去除(D:\foo\x.wavhf-ext/D/foo/x.wav),这样 path.join(compileDir, key) 在每个操作系统上都保持在编译目录内。
  3. 复制 + 重写。 编排器将文件复制到 <compileDir>/hf-ext/... 下,HTML 被重写为指向清理后的键名。文件服务器随后从同一根目录提供项目内部和外部资源。

包含性检查使用 path.relative() 而非硬编码的分隔符,因此外部资源在 macOS、Linux 和 Windows 上的行为完全相同。参见 packages/producer/src/utils/paths.ts 了解辅助函数。

相关包

  • CLI 命令行界面,封装了 producer 用于渲染、预览等功能。
  • Engine producer 用于抓取帧的底层捕获管道。
  • Core producer 所依赖的类型、运行时和 Linter。
  • Studio 在使用 producer 渲染之前构建合成的可视化编辑器。