Remotion Canvas Capture 扩展详解:用 Chrome 实验性 HTML-in-canvas 把网页录制成高清 MP4/WebM
2026/9/8 21:19:00 网站建设 项目流程

Remotion Canvas Capture 扩展详解:用 Chrome 实验性 HTML-in-canvas 把网页录制成高清 MP4/WebM

【免费下载链接】remotion🎥 Make videos programmatically with React项目地址: https://gitcode.com/GitHub_Trending/re/remotion

Remotion Canvas Capture 是 Remotion 仓库中的一个 Chrome 扩展(packages/canvas-capture-extension),它基于 Chromium 的实验性 HTML-in-canvas 实现,可以将网页中的任意区域或整个页面录制为高分辨率的 H.264 MP4 或 VP9 WebM 视频。读完本文,你将掌握:如何在 macOS 上搭建该扩展所需的固定版本 Chrome for Testing 环境、开发与构建扩展的完整流程、录制工作流的交互细节,以及扩展底层的页面包装、离屏画布裁剪、Mediabunny 实时编码与"免下载直传 Convert"的源码级实现原理。

一、扩展定位与核心技术依赖

Canvas Capture 扩展的核心价值在于"在浏览器内直接录制网页":录制不依赖屏幕捕获,而是通过 Chromium 的 HTML-in-canvas 实验性 API(CanvasDrawElement特性)把 DOM 子树当作图像绘制到画布上,再逐帧送入硬件编码器。这带来两个关键特性:

  • 输出分辨率可超过屏幕显示尺寸:整页内容按显示器的原生像素密度绘制,再按用户选择的输出比例(scale)裁剪合成到OffscreenCanvas,因此可以录出高于屏幕物理分辨率的视频;
  • 输出即 Remotion Convert 的输入:录制结束后可以直接把视频送入 remotion.dev/convert 在线编辑,无需先下载到本地。

扩展的运行时依赖声明在 package.json 中:mediabunny(浏览器端媒体封装/编解码库)、React/React-DOM(录制器窗口 UI),开发依赖包括 WXT 0.21.4(Manifest V3 扩展框架)、Vite 7.3.6 与@vitejs/plugin-react

二、为什么必须使用固定版本的浏览器

该扩展依赖 Chromium 实验性的 HTML-in-canvas 实现,README 中明确指定了已知可用的精确版本:Chrome for Testing150.0.7842.0(revisionr1631007,且要求在 Apple Silicon 的 mac-arm64 构建上运行。选择该构建的原因有三个:

  1. 这是已知可运行所需 HTML-in-canvas 实现的确切 Chromium 版本
  2. Chrome for Testing不会自动更新,而 Chrome Canary 或普通 Chrome 会自动更新,可能移除或改动这个实验性 API;
  3. 与开源 Chromium 构建不同,Chrome for Testing内置专有编解码器支持,浏览器既能编码又能回放本扩展产出的 H.264 MP4。

构建脚本 rebuild-extension.sh 在运行时会校验--version输出必须严格等于Google Chrome for Testing 150.0.7842.0,版本不符直接报错退出——从源码结构看,这种"版本钉死"是刻意为之的兼容性保护。

安全前提:由于该浏览器被有意固定且不会接收安全更新,README 明确要求只用于可信网站

三、macOS 上固定浏览器的完整搭建步骤

1. 获取并固定浏览器

下载 Chrome for Testing150.0.7842.0(revisionr1631007)的 mac-arm64 压缩包,解压后把应用移动到一个持久化位置并重命名,例如:

/Users/jonathanburger/Applications/Recorder Chrome.app

2. 以专用 Profile 启动并开启 HTML-in-canvas 特性

'/Users/jonathanburger/Applications/Recorder Chrome.app/Contents/MacOS/Google Chrome for Testing' \ --user-data-dir='/Users/jonathanburger/Library/Application Support/Chrome for Testing Canvas Capture r1631007' \ --enable-features=CanvasDrawElement \ --enable-blink-features=CanvasDrawElement \ --disable-component-update \ --no-first-run \ --no-default-browser-check

各参数含义:

  • --user-data-dir:使用独立的专用 Profile,与日常浏览器互不干扰;
  • --enable-features=CanvasDrawElement--enable-blink-features=CanvasDrawElement:同时从 Chrome 层与 Blink 层开启 HTML-in-canvas 特性,这是扩展必需的前置条件;
  • --disable-component-update:禁用组件自动更新;
  • --no-first-run--no-default-browser-check:跳过首运行向导和默认浏览器检查。

上述参数与 wxt.config.ts 中webExt.chromiumArgs的定义完全一致,webExt.binaries.chrome也指向同一个Recorder Chrome.app路径,说明开发模式启动的就是这套固定环境。

3. 手动加载解包扩展

生产模式下,扩展安装在持久化目录:

/Users/jonathanburger/Applications/Remotion Canvas Capture Extension

chrome://extensions中开启Developer mode,选择Load unpacked,指向该目录即可。若 HTML-in-canvas 尚未启用,还需在chrome://flags/#canvas-draw-element中将 Canvas Draw Element 设为 Enabled 并完全重启浏览器。

四、开发工作流:WXT + Vite HMR

如果已经用 Canvas Capture 专用 Profile 启动了 Recorder Chrome,先将其关闭,然后:

cd packages/canvas-capture-extension bun run dev

dev脚本实际执行bunx --bun wxt(见 package.json)。WXT 会:

  1. 启动 Vite,把开发版扩展写入持久化目录~/Applications/Remotion Canvas Capture Extension Dev——这个路径由 wxt.config.ts 中的outDirnpm_lifecycle_event === 'dev'动态决定,生产构建则输出到dist
  2. 以所需的特性 flags 和持久化 Profile 启动固定版本的 Chrome for Testing,并自动加载扩展;
  3. 提供差异化热更新:在src/entrypoints/recorder下编辑 React 组件或 CSS 时走Vite HMR,无需重建或重新加载录制器窗口;而 background、capture、receiver 或 manifest 变更时,WXT 才会重建并重新加载对应的扩展上下文。

开发扩展与手动加载的生产扩展是两套独立实体,因此它们的路径和扩展 ID 在多个 git worktree 下保持稳定——这对依赖固定扩展 ID 的消息路由和 storage 命名空间很重要。

五、构建与安装

从仓库根目录运行构建脚本:

.agents/skills/canvas-capture-extension/scripts/rebuild-extension.sh --repo "$PWD"

脚本支持三个参数(见脚本头部 usage 与参数解析逻辑):

参数作用
--repo PATH指定包含packages/canvas-capture-extension的 Remotion 仓库路径;缺省时尝试git rev-parse --show-toplevel
--install-dir PATH覆盖默认安装目录(默认/Users/jonathanburger/Applications/Remotion Canvas Capture Extension
--browser-executable PATH浏览器不在默认路径时,指定 Chrome for Testing 可执行文件位置

脚本的执行逻辑(结合 rebuild-extension.sh 源码):

  1. 校验浏览器可执行文件存在且版本严格等于150.0.7842.0(revisionr1631007);
  2. 要求环境中有bun,然后进入包目录执行bun run make(即bunx --bun wxt build)生成生产 WXT 包;
  3. 校验dist产物完整性:manifest.jsonbackground.jscapture.jsrecorder.htmllogo.svgcontent-scripts/receiver.jsassets/下的 recorder CSS 缺一即报错;
  4. 把完整解包扩展安装到 worktree 之外的持久化目录。

安装后按 README 流程操作:打开chrome://extensions→ 启用 Developer mode → Load unpacked → 选择安装目录 → 确认chrome://flags/#canvas-draw-element已启用。

六、录制工作流:从选区到 Convert

在任意网页上点击扩展图标会打开录制器窗口(manifest 中action.default_popup指向recorder.html,见 wxt.config.ts)。完整交互流程:

  1. 选择格式:H.264 MP4 或 VP9 WebM(源码中对应CaptureFormat = 'mp4' | 'webm',见 messages.ts);
  2. 设置输出比例:scale 参数决定输出分辨率相对布局尺寸的倍率;
  3. 选择区域:拖拽选择一块区域(拖拽期间页面保持聚焦,录制器窗口仍然打开),或选择Whole page录整页。选区几何计算见 selection.ts 中的makeSelectionRectangle
  4. 按下 Record:录制器会显示圆整后的输出尺寸,并且只有当浏览器确认 Mediabunny 的"精确高质量实时"配置受支持后才会启用 Record 按钮;
  5. 录制中:即使关闭录制器窗口,录制也会继续进行;重新点击扩展图标可再次打开;
  6. 停止Stop and open in Convert会把录制结果直接加载到 remotion.dev/convert(无需先下载),或Stop and download直接保存文件。

已知限制(README 明确说明):

  • 录制期间页面内容会被临时放进一个layoutSubtree画布中,录制结束后恢复。依赖直接子代 CSS 选择器(如body > div)的站点在录制期间可能显示不同;
  • Chrome 自带页面(chrome://)与 Chrome 商店页面不允许扩展脚本注入,无法录制。

七、源码级原理:页面如何被"画"成视频

1. 整页包装:layoutSubtree画布

录制整页时,capture.ts 中的wrapWholePage()执行一次"DOM 搬家":

  • 创建一个<canvas>,设置canvas.layoutSubtree = truelayoutsubtree属性,使其内部子树按真实布局渲染;
  • <body>的全部子节点移入画布内的一个绝对定位<div>inset: 0),画布尺寸取自getWholePageSize()——即documentElement/bodyscrollWidth/scrollHeightwindow.innerWidth/innerHeight取最大值,从而覆盖完整可滚动页面而不仅是视口;
  • MutationObserver监听<body>childList变化:录制期间页面新加到 body 的节点会被动态移入内容容器,保证捕获不丢内容;
  • 结束时restore()把节点按原顺序移回 body 并移除画布——这就是 README 所说"临时放置、事后恢复"的实现,也解释了直接子代选择器为何会受影响(内容在录制期间从body的直接子代变成了画布内div的孙代)。

2. 每帧绘制:显示画布 + 离屏裁剪画布

PageCapture类维护两级画布(见 capture.ts):

  • 显示画布(包装用的layoutSubtreecanvas):syncDisplayCanvasSize将其内部分辨率设为width × devicePixelRatio,即按显示器原生像素密度绘制整页子树;
  • 捕获画布OffscreenCanvas):尺寸由syncCanvasSize设为"裁剪区域 × scale",且经getScaledCanvasSize向下取整到偶数(H.264/VP9 的宏块对齐要求)。

每帧的绘制流程由画布的paint事件(#onPaint)触发:先用context.drawElementImage(content, ...)把内容元素画进显示画布,再用canvas.captureElementImage(content)取出元素图像,在离屏画布上以 3 种底色(白、<html>背景、<body>背景)逐层铺底后drawElementImage裁剪绘制到目标尺寸,最后交给编码器。同时ResizeObserver监听内容尺寸变化并调用canvas.requestPaint()请求重绘。

尺寸上限validateCaptureSize强制执行:显示画布任一边不超过32767像素,编码帧任一边(按 scale 放大后)不超过32766(偶数)像素,超限抛出带提示的错误。

3. 编码:Mediabunny 实时高质量档

recorder.ts 中的CanvasCaptureRecorder负责编码:

  • 启动前调用assertCanEncodeCapture,即 Mediabunny 的canEncodeVideo,按QUALITY_HIGH码率、latencyMode: 'realtime'探测avc(MP4)或vp9(WebM)在该分辨率下是否可编码——这正是 UI 上"Record 按钮只在浏览器确认支持后才可用"的实现;
  • startRecording创建BufferTarget+OutputMp4OutputFormatWebMOutputFormat)+VideoSampleSource,每帧通过VideoFrame(带微秒级timestamp/duration)入队,编码循环串行消费、失败即标记hasEncodingError并丢弃后续帧;
  • 停止时finalizeRecording等待最后一帧编码完成,然后把录制元数据(起止时间、captureMetadata的 density/内容矩形/画布尺寸/视口滚动、鼠标移动轨迹mouseMovements、指针点击pointerClicks)序列化为 JSON,通过 Mediabunny 的Conversiontags.raw写入文件标签REMOTION_CAPTURE_DATA并重新封装——鼠标轨迹与点击不占用视频轨,而是随文件元数据一起交付。

4. 免下载直传 Convert:storage 分块 + 消息协议

"Stop and open in Convert" 的实现横跨三个文件:

  • handoff.ts:openCaptureInConvert生成随机captureId,把视频文件按1MBCHUNK_SIZE)切片,base64 编码后以remotion-canvas-capture:<id>:chunk:<index>键写入browser.storage.local(对应 manifest 的unlimitedStorage权限),另存一条 metadata(文件名、MIME 类型、分块数),最后发消息通知 background 打开 Convert 页面;失败时回滚已写入的键;
  • receiver.ts:注入到 Convert 页面的内容脚本,识别 URL 中的?canvas-capture=<id>参数,等页面发出 ready 消息后按序读出分块,经window.postMessagestart/chunk/complete三段式协议投递,投完即删除 storage 键并清理 URL 参数,全程校验 origin;
  • messages.ts:录制器 UI 与控制端之间的命令协议(get-stateset-optionsselect-areaselect-whole-pagestart-recordingstop-recordingopen-in-convertdownload-recording等)及控制器状态机(encoderSupport: 'unavailable' | 'checking' | 'supported' | 'unsupported'等字段)。

5. 扩展结构与权限

扩展为 Manifest V3(wxt.config.ts 中manifestVersion: 3),权限为activeTabscriptingstorageunlimitedStorage。源码入口组织如下:

文件职责
src/background.ts后台脚本:图标点击、打开 Convert 等运行时消息
src/capture.ts页面捕获核心:整页包装、画布绘制、尺寸校验、PageCapture
src/recorder.tsCanvasCaptureRecorder编码循环、HTML-in-canvas 能力检测、API 类型声明
src/content.ts / src/receiver.content.ts内容脚本:页面侧控制器与 Convert 侧接收端
src/entrypoints/recorder/录制器弹窗 UI(React +index.html
src/handoff.ts分块存储与直传 Convert
src/selection.ts选区矩形几何计算

八、适用前提与限制小结

  • 平台限定:README 描述的搭建流程面向 macOS(Apple Silicon + mac-arm64 Chrome for Testing);Linux/Windows 用户需自行寻找含相同 HTML-in-canvas 实现的构建,源码中的能力检测函数isHtmlInCanvasAvailable会在运行时校验requestPaintcaptureElementImagedrawElementImageVideoFrame是否齐备,不满足则抛出指引用户开启chrome://flags/#canvas-draw-element的错误;
  • 浏览器钉死在150.0.7842.0/r1631007:升级浏览器会使构建脚本直接失败;该浏览器无安全更新,仅限可信站点使用;
  • 录制期间页面 DOM 被临时改写:依赖直接子代选择器的站点外观可能变化,结束后自动恢复;chrome://页面与 Chrome 商店页面不可录制;
  • 输出上限:编码帧任一边 ≤ 32766 像素(偶数),显示画布任一边 ≤ 32767 像素,超出需降低 scale 或缩小选区;
  • 扩展包为private,随 Remotion monorepo 以 workspace 方式构建,版本与仓库其他包同步(当前4.0.521)。

总体而言,Canvas Capture 把"浏览器实验性 HTML-in-canvas API + Mediabunny 浏览器端编码 + Remotion Convert 在线编辑"串成了一条从"网页选区"到"可编辑视频工程"的完整链路,其源码(页面包装、离屏裁剪、元数据注入、分块直传)也展示了在纯浏览器环境内做高清录制的工程化做法。

【免费下载链接】remotion🎥 Make videos programmatically with React项目地址: https://gitcode.com/GitHub_Trending/re/remotion

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询