@hyperframes/engine 渲染引擎深度解析:用 Puppeteer + FFmpeg 把可寻址 Web 页面渲染成视频
发布时间:2026/9/9 23:22:37来源:尧图网络
hyperframes/engine 渲染引擎深度解析用 Puppeteer FFmpeg 把可寻址 Web 页面渲染成视频【免费下载链接】hyperframesWrite HTML. Render video. Built for agents.项目地址: https://gitcode.com/GitHub_Trending/hy/hyperframeshyperframes/engine是 HyperFrames 技术栈中位于底层的可寻址seekable网页渲染引擎它打开你的 HTML 合成页面逐帧seek到精确时刻截屏再交给 FFmpeg 编码成视频。它不是录屏器而是一套确定性的帧调度管线并且与动画框架无关——只要页面实现了统一的window.__hfseek 协议GSAP、Lottie、Three.js、CSS 动画都能被渲染。读完本文你将掌握引擎的架构、九大核心服务、window.__hf页面协议、EngineConfig 配置体系、捕获与编码流程以及何时该直接用引擎而不是上层 Producer/CLI。引擎是什么以及它为什么不是录屏hyperframes/engine的定位在仓库中写得很直白Seekable web-page-to-video rendering engine built on Puppeteer and FFmpegpackages/engine/package.json。录屏器依赖墙上时钟机器负载高时可能丢帧引擎则要求页面暴露window.__hf通过duration计算总帧数、在每帧截屏前调用seek(time)因此帧调度是可复现的慢机器也不会掉帧。但要注意精确像素仍可能随 Chrome 版本、字体、编解码器、GPU 行为与宿主环境变化当需要逐字节级别的视觉可复现性时需要把依赖钉死——这一话题在仓库文档 确定性渲染 中有更完整的讨论。从渲染路径看引擎会启动 headless Chrome 实例用 Chrome 的HeadlessExperimental.beginFrameCDP 接口逐帧推进画面截取截图再交给 FFmpeg 编码为视频。这条链路也解释了为什么引擎要求 Node.js ≥ 22、Chrome/ChromiumPuppeteer 自动下载以及 FFmpeg 三样运行时依赖。整体架构九大核心服务引擎的实现被拆分为一组职责单一的服务各自拥有独立的源码文件与测试覆盖入口统一从 packages/engine/src/index.ts 导出。下表来自 packages/engine/README.md对应源码位置如下服务职责对应源码browserManager启动与池化 headless Chromechrome-headless-shellbrowserManager.tsframeCapture管理捕获会话——seek、截屏、buffer 生命周期frameCapture.tsscreenshotService基于 BeginFrame 的 CDP 截屏screenshotService.tschunkEncoderFFmpeg 编码分块 concat、GPU 检测、faststartchunkEncoder.tsstreamingEncoder实时把帧管道化送入 FFmpeg磁盘上不落中间 PNGstreamingEncoder.tsaudioMixer解析audio元素并用 FFmpeg 混音audioMixer.tsvideoFrameExtractor从video元素抽取帧用于合成videoFrameExtractor.tsparallelCoordinator跨 worker 进程切分帧区间并行渲染parallelCoordinator.tsfileServer用 Hono 把本地 HTML 提供给浏览器fileServer.ts这套服务矩阵本身就是网页渲染成视频问题的领域建模先有浏览器browserManager再逐个捕获帧frameCapture screenshotService同时为视频/音频原生能力做补偿videoFrameExtractor / audioMixer编码chunkEncoder / streamingEncoder加速parallelCoordinator最后还有一个本地静态服务来喂页面fileServer。页面协议任何框架只要实现window.__hf引擎与页面之间唯一的契约是window.__hf即HfProtocol接口定义于 packages/engine/src/types.ts。引擎不关心页面背后是什么动画框架——GSAP、Framer Motion、CSS 动画、Three.js 都行只要seek()在给定时间点能产出确定的视觉效果export interface HfProtocol { /** 合成片段的时长秒 */ duration: number; /** seek 到特定时间点必须产出确定的视觉输出 */ seek(time: number): void; /** 可选引擎应处理的媒体元素 */ media?: HfMediaElement[]; /** 可选着色器转场元数据由 hyperframes/shader-transitions 填充 */ transitions?: HfTransitionMeta[]; }types.ts中对全局Window做了声明增强declare global把__hf?: HfProtocol挂到window上。由此引擎读duration计算总帧数、每次捕获前调用seek(time)、利用media处理视频帧注入与音频混音。media中的每个元素由 HfMediaElement 描述。之所以需要显式声明是因为BeginFrame 模式下的 headless Chrome 无法播放video、也无法产生音频引擎必须先抽取这些媒体并把画面/声音在合成阶段自动注入与混合。字段包括字段含义elementIdvideo或audio元素的 DOM idsrc源文件路径或 URLstartTime/endTime该元素在合成中的出现/消失时间秒mediaOffset可选源文件内的偏移秒默认 0volume可选音量 0–1默认 1hasAudio可选该元素是否含有需要抽取的音频transitions则供包含 shader 转场的合成使用每个转场记录time、duration、shader、GSAPease、fromScene/toScene让上层 Producer 能预计算场景区间、按场景捕获 buffer再做 HDR 感知的合成。安装与运行环境npm install hyperframes/engine运行时要求Node.js 22见 packages/engine/package.json 的engines字段Chrome/Chromium由 Puppeteer 自动下载FFmpeg含ffprobe。代码采用 ESMtype: module开发态直接以 TS 源文件作为导出入口main/exports指向./src/index.ts发布时再编译到dist。包内还按子路径导出./alpha-blit与./shader-transitions两个工具集。FFmpeg 与 ffprobe 的二进制路径可通过环境变量指定见 ffmpegBinaries.ts 的FFMPEG_PATH_ENV/FFPROBE_PATH_ENV并有assertConfiguredFfmpegBinariesExist做启动期校验。使用流程从启动浏览器到逐帧捕获README 给出的四步流程是最小可用骨架启动浏览器 → 建立捕获会话 → 逐帧捕获 → 清理。不过要注意当前仓库源码frameCapture.ts与更完整的包级文档 docs/packages/engine.mdx 中createCaptureSession/captureFrame的实际签名已经演进为(serverUrl, outputDir, options)与(session, frameIndex, time)形态——README 中的对象式写法属于概念示意。基于源码的真实调用如下import { captureFrame, closeCaptureSession, createCaptureSession, getCompositionDuration, initializeSession, } from hyperframes/engine; const fps { num: 30, den: 1 }; // fps 是精确有理数整数帧率 {num:30, den:1} const session await createCaptureSession( http://localhost:3000/my-composition.html, // 页面必须实现 window.__hf ./frames, // 输出帧目录自动创建 { width: 1920, height: 1080, fps, format: jpeg, }, ); try { await initializeSession(session); // 等页面就绪、预热 BeginFrame const duration await getCompositionDuration(session); const totalFrames Math.ceil(duration * 30); for (let frame 0; frame totalFrames; frame 1) { const time frame / 30; await captureFrame(session, frame, time); // seek 截屏 落盘 } } finally { await closeCaptureSession(session); // teardown 永不抛错 }几个实现细节值得展开createCaptureSessionframeCapture.ts内部会解析 headless shell 路径、决策捕获模式、解析 GPU 模式调用方通常不需要自己acquireBrowser——浏览器生命周期已被会话接管。它还支持传入BeforeCaptureHook与部分EngineConfig。captureFrame(session, frameIndex, time)frameCapture.ts内部完成 seek 截屏并通过writeCapturedFrame按frame_000000.jpg|png的规范命名写入会话输出目录最后返回CaptureResultframeIndex、量化后的time、path、captureTimeMs。当下一阶段需要内存中的 Buffer 而非磁盘文件时改用captureFrameToBuffer()——这正是 streaming encode 路径把帧直接送进 FFmpeg stdin 的支撑。捕获模式BeginFrame、截图与 drawElement引擎在把一帧 DOM 状态变成一张位图这一步有多种路径会话初始化时会基于平台与配置自动决策最终模式记录在CapturePerfSummary.captureModedrawelement | screenshot | beginframe。核心事实包括BeginFrame 模式依赖HeadlessExperimental.beginFrame这一 CDP 调用一次调用完成一个 layout-paint-composite 周期并返回截图与hasDamage布尔screenshotService.ts。它要求chrome-headless-shell并需要--enable-begin-frame-control与--deterministic-mode启动参数。源码注释明确BeginFrame flags 只在 Linux 上生效macOS 存在 Chromium 结构性限制crbug.com/40656275因此相关绕行方案如 page-side compositing是 Mac 用户的关键杠杆。BeginFrame 不吃 alpha需要透明 PNG 输出时应同时设置config.forceScreenshot truedeviceScaleFactor 1超采样也会回退到截图路径因为 BeginFrame 的截屏不遵循视口的 DPR 缩放。drawElement 快速捕获在 macOS / Windows 硬件 GPU 浏览器上可启用useDrawElement直接从合成根读取 paint records 做捕获并带有自校验网——初始化时抓取 ground-truth 样本、运行期对每帧做 PSNR 比对任何不兼容或损坏的渲染都会自动回退到 screenshot 捕获相关验证逻辑见 drawElementService.ts 与psnrDb。无变化帧复用BeginFrame 上报hasDamagefalse时复用上一帧缓存避免在暂停的合成器上调用会超时的Page.captureScreenshot截图路径则有静态帧去重staticFrameDedup可用HF_STATIC_DEDUPfalse关闭。screenshotService 中还有一个值得注意的健壮性设计sendBeginFrame对Another frame is pending做指数退避重试最多 5 次超限后给出CPU 被并行渲染打满请降低并发或使用 --docker 隔离的明确报错——这说明帧捕获并非总是瞬时成功的上层必须容忍瞬态错误。引擎配置体系EngineConfig 与默认值引擎把过去散落的PRODUCER_*环境变量收敛为结构化的 EngineConfig 接口同时保留环境变量作为向后兼容的回退统一由resolveConfig()解析。核心默认值config.ts配置默认值说明fps3024 / 30 / 60quality/formatstandard/jpegjpegQuality默认 80concurrencyauto基于 CPU 核数启发式决定 worker 数coresPerWorker2.5每个 worker 分配的 CPU 核minParallelFrames120低于该帧数不启用并行 workerlargeRenderThreshold1000触发大渲染启发式的帧数阈值browserGpuModesoftwaresoftware(SwiftShader) /hardware/auto(探测后回退)enableBrowserPooltrue浏览器池browserTimeout/protocolTimeout120_000 / 300_000 msstaticFrameDeduptrue截图路径的静态帧去重useDrawElementtrue快速捕获运行期会被平台门控钳制enableChunkedEncodefalse分块编码chunkSizeFrames默认 360enableStreamingEncodetrue流式编码超过streamingEncodeMaxDurationSeconds(240s) 不适用ffmpegEncodeTimeout/ffmpegProcessTimeout/ffmpegStreamingTimeout600s / 300s / 600s流式超时按无帧到达的间隙计时hdrfalseHDR 输出传输函数hlg/pqhdrAutoDetect默认 trueaudioGain1音频增益pageNavigationTimeout60_000 ms入口页须在此内到达domcontentloadedenv 回退PRODUCER_PAGE_NAVIGATION_TIMEOUT_MSlowMemoryMode按主机自动低内存宿主上收拢管线跳过校准浏览器、固定单 worker、优先截图捕获extractCacheMaxBytes2 GiB视频帧抽取的内容寻址缓存预算browserGpuMode的三种取值对渲染影响巨大softwareSwiftShader纯 CPU、总是可用但约慢 5–50 倍hardware走平台原生 ANGLE 后端Metal/D3D11/EGL无可用 GPU 时直接报错auto会在进程内首次启动时做一次 WebGL 探测额外成本约 1–2 秒、结果缓存不可用则回退软件渲染。此外resolveConfig会把低内存检测getSystemTotalMb/isLowMemorySystem、Windows 软件 GPU 复合启发式等自动决策落到字段上供可观测性层区分自动关闭与用户显式关闭。错误处理约定三类错误、三种策略引擎面向编排方与库调用者错误语义必须可预期。index.ts 的文档注释给出了全局约定编排类服务失败即抛异常浏览器启动、会话初始化、帧捕获、CDP 操作frameCapture、browserManager、screenshotService、videoFrameExtractor.extractVideoFramesRange——调用方应捕获处理FFmpeg 进程包装器返回结果对象而非 reject编码、mux、混音、流式编码等返回{ success, error? }chunkEncoder、audioMixer、streamingEncoder清理与 teardown 永不抛错releaseBrowser、closeCaptureSession、临时目录清理等通过.catch(() {})吞掉错误避免掩盖最初的失败可选查找返回T | undefined/nullresolveHeadlessShellPath、getFrameAtTime、detectGpuEncoder等可能合法地找不到的函数返回空值而不是抛错。会话初始化还会产出结构化的CaptureWarningcode 类型如media_readiness_timeout、media_load_failed、audio_processing_failed、sub_timeline_readiness_timeout、live_map_detected捕获性能摘要CapturePerfSummary则给出 p50 / p95 / p99 的单帧耗时——p50 对预热鲁棒p95/p99 用于识别长尾尖峰这些都是判断某台机器渲染快慢的关键信号。媒体管线BeginFrame 缺陷的补偿机制由于 headless Chrome 在 BeginFrame 模式下不原生播放video、不产生音频引擎发展出了一整套外带处理媒体管线视频帧抽取与注入parseVideoElements/extractVideoFramesRange先从源video抽帧建立FrameLookupTablegetFrameAtTime按时间取帧再由 videoFrameInjector.ts 的createVideoFrameInjector在截屏时把对应帧按元素包围盒注入 DOM。抽取结果带内容寻址缓存默认tmpdir/hyperframes-extract-cache-uid键为 pathmtimesize媒体区间fpsformat可用HYPERFRAMES_EXTRACT_CACHE_DIR覆盖或设off/none/false/0关闭。就绪等待的例外页面默认要等video.readyState 1才开拍对走外带帧注入的视频含原生 HDR 抽取通过skipReadinessVideoIds跳过检查并用videoMetadataHints提前告知 FFmpeg 探测到的原始尺寸避免height:auto这类依赖媒体固有比例的布局跑偏。音频混音parseAudioElements(html)在 HTML 中解析音频元素processCompositionAudio把各轨道按时间轴、音量、淡入淡出曲线混成单一MIXED_AUDIO_FILENAMEaudioVolumeEnvelope.ts 提供了包络 walker 与对 WAV 的音量包络应用另有音频 FX 渲染audioFxRender.ts 的readWav/writeWav/applyAudioFxChain。HDR需要 HDR 输出时hdr配置选hlg或pq通过 hdrCapture.ts 的专用浏览器参数与 readbackfloat16ToPqRgb等拿到宽色域帧编码时附带DEFAULT_HDR10_MASTERING主控元数据。编码器分块 concat、流式管道与 GPU 加速编码层分成两种互补的路径chunkEncoderchunkEncoder.ts从帧目录按序编码encodeFramesFromDir或做分块编码后再 concatencodeFramesChunkedConcatchunkSizeFrames控制块大小最后muxVideoWithAudio合成音视频、applyFaststart把 moov 挪到文件头以便流式播放。detectGpuEncoder探测宿主 GPU 编码器ENCODER_PRESETS/getEncoderPreset提供预设VP9 通过vp9CpuUsed-8..8默认值见 vp9Options.ts 的DEFAULT_VP9_CPU_USED调节速度/质量权衡FFmpeg 参数按精确有理数帧率原样输出。streamingEncoderstreamingEncoder.tsspawnStreamingEncoder启动 FFmpeg 并持续从writeFrame喂入帧磁盘上不落中间 PNG显著降低 IO 与临时空间占用createFrameReorderBuffer用于需要重排序如 B 帧的场景。其流式超时是帧间静默时长不是总渲染时长。并行渲染与本地文件服务parallelCoordinatorparallelCoordinator.ts负责把帧区间分发给多个 worker 进程calculateOptimalWorkers/computeWorkerSizing按 CPU 与内存启发式定尺寸distributeFramesInterleaved做交错分发以平衡负载各 worker 结果由mergeWorkerFrames合并。多 worker 的 drawElement 捕获会抬高自校验采样数deVerifySamples——N 个并发硬件 GPU 浏览器会放大合成器瓦片逐出等损坏面而每个 worker 只消化约 1/N 的共享采样网格正好在风险峰值时把覆盖密度摊薄了所以必须加密采样补偿。fileServerfileServer.ts用 Hono 起一个本地静态服务器createFileServer({ projectDir, compiledDir?, port?, headScripts?, bodyScripts?, stripEmbeddedRuntime })返回{ url, port, close }。.html请求会走注入脚本逻辑——往head//body前注入 runtime 脚本并剥离内嵌运行时默认index.html才做注入其他扩展名按 MIME 表直出找不到文件返回 404。它是本地 HTML 合成 → 无头浏览器之间那个不起眼但必要的桥梁。何时直接使用引擎何时用上层封装引擎是低层原语集合文档与 docs/packages/engine.mdx 都反复强调大多数集成应该走hyperframes/producer或hyperframesCLI仅在需要自己掌控帧捕获、编码、媒体抽取或浏览器管理时才直接使用引擎。仓库内的关系图如下packages/core —— 类型、解析器、帧适配器engine 的quantizeTimeToFrame等直接复导出自 corepackages/producer —— 构建在本引擎之上的高层渲染管线对应包文档 docs/packages/producer.mdxpackages/cli —— 命令行入口docs/packages/engine.mdx —— 引擎的完整包级文档含可运行示例。一句话总结技术选型要画面正确、帧可复现、媒体可混、可并行加速的视频化能力就直接依赖hyperframes/engine的九大服务与window.__hf协议要一句话把整条管线跑完则交给它上层的 Producer 与 CLI。对想深入源码的读者建议从 types.ts协议契约→ config.ts默认值与门控→ frameCapture.ts会话与捕获主流程→ screenshotService.tsBeginFrame 截屏与视频帧注入这条主线读起每一环都有同目录下大量.test.ts用例可以对照验证行为边界。【免费下载链接】hyperframesWrite HTML. Render video. Built for agents.项目地址: https://gitcode.com/GitHub_Trending/hy/hyperframes创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网