如何把 Remotion 视频渲染项目从 SSR 迁移到客户端渲染?
【免费下载链接】remotion🎥 Make videos programmatically with React项目地址: https://gitcode.com/GitHub_Trending/re/remotion
如果你有一个已经能跑通的 Remotion 视频渲染项目,现在想把渲染从 Node.js 服务端搬到浏览器里执行,需要完成三件事:确认浏览器环境可用、按文档要求修改现有代码中的全局 API 和媒体组件、然后用renderMediaOnWeb()发起渲染并验证结果。客户端渲染由@remotion/web-renderer包提供,API 自 v4.0.491 起稳定。它的渲染依赖 WebCodecs API,浏览器最低版本要求为:
| 浏览器 | 最低版本 |
|---|---|
| Chrome | 94+ |
| Firefox | 130+ |
| Safari | 26+ |
与 SSR 相比,客户端渲染不需要 Node 服务器或 Remotion Lambda,用 WebCodecs 加 Mediabunny 编码而不是 FFmpeg,且没有 bundling 步骤——渲染函数直接接收组件和配置。
迁移前先确认浏览器能否渲染
@remotion/web-renderer提供canRenderMediaOnWeb(),用于在实际渲染前检查配置是否可执行,文档明确说它适合向用户反馈浏览器兼容性和配置问题。安装@remotion/web-renderer后(安装方式见 包文档),可以用它做迁移前的第一道检查:
import {canRenderMediaOnWeb} from '@remotion/web-renderer'; const result = await canRenderMediaOnWeb({ container: 'mp4', videoCodec: 'h264', width: 1920, height: 1080, }); if (!result.canRender) { for (const issue of result.issues) { console.error(issue.message); } } else { console.log('Ready to render!'); console.log('Video codec:', result.resolvedVideoCodec); console.log('Audio codec:', result.resolvedAudioCodec); }canRender为false时,issues数组里每一项都有type和message。文档列出的问题类型包括webcodecs-unavailable(浏览器没有 WebCodecs API)、container-codec-mismatch、invalid-dimensions(H.264 和 H.265 要求宽高是 2 的倍数)、video-codec-unsupported、audio-codec-unsupported、transparent-video-unsupported(透明度需要 VP8 或 VP9)、webgl-unsupported(3D CSS transform 需要 WebGL)、output-target-unsupported。每一项的severity是"error"(阻塞)或"warning"(非阻塞,例如已应用回退)。
按迁移指南修改现有代码
官方迁移文档列出了四类必须处理的代码差异。逐条修改即可,不需要重写项目结构。
1. 用useRemotionEnvironment()替换getRemotionEnvironment()
getRemotionEnvironment()是全局 API,当同一页面存在多个 Remotion 实例(比如页面上挂着<Player>)时可能冲突。改用useRemotionEnvironment()hook,它把环境信息限定在调用它的组件上下文里。返回值包括isStudio、isRendering、isPlayer、isReadOnlyStudio,以及自 v4.0.344 起的isClientSideRendering(标识当前是否处于客户端渲染上下文):
import React from 'react'; import {useRemotionEnvironment} from 'remotion'; export const MyComp: React.FC = () => { const {isStudio, isPlayer, isRendering, isClientSideRendering} = useRemotionEnvironment(); if (isClientSideRendering) { return <div>Client-side render</div>; } // ... return <div>Hello World!</div>; };2. 用useDelayRender()替换全局delayRender()、continueRender()、cancelRender()
项目里如果有多个渲染同时发生、或同页存在<Player>,全局的delayRender()等函数可能互相冲突。useDelayRender()把这三个函数限定到单个 composition 作用域内,文档称其为推荐写法:
import {useDelayRender} from 'remotion'; const MyComp: React.FC = () => { const {delayRender, continueRender, cancelRender} = useDelayRender(); return <div>My component</div>; };把组件里直接 import 的delayRender、continueRender、cancelRender全部替换为从 hook 解构出来的同名函数,调用处代码不需要改动。
3. 确保所有资源可通过 CORS 访问
这是 SSR 与客户端渲染最容易被忽略的差异:SSR 在 Node.js 进程中下载音视频资源,图片即使 tainted 也能截图;客户端渲染则强制 CORS,图片和 canvas 不允许 tainted。如果你的图片 URL 没有Access-Control-Allow-Origin响应头,渲染时会看到这样的报错(文档示例):
Could not draw image with src="https://example.com/image.png" to canvas: The image is tainted due to CORS restrictions. The server hosting this image must respond with the "Access-Control-Allow-Origin" header.如果图片 URL 本身加载失败(例如 404),则报:
Could not draw image with src="https://example.com/image.png" to canvas: The image is in a broken state. This usually means the image failed to load - check that the URL is valid and accessible.迁移时需要逐一核对项目中图片、字体等资源所在服务器的 CORS 配置;两种报错分别对应"服务器缺少 CORS 头"和"资源本身不可达"。
4. 媒体组件替换为@remotion/media的<Video>和<Audio>
客户端渲染只支持@remotion/media包提供的<Video>和<Audio>。迁移文档要求把所有Html5Video、Html5Audio和<OffthreadVideo>标签替换掉,限制文档给出了明确对应关系:
<Html5Video>→<Video><Html5Audio>→<Audio><OffthreadVideo>→<Video><AnimatedEmoji>不支持 → 改用<Lottie>
替换示例:
import {Video} from '@remotion/media'; // 迁移前:<Html5Video src="https://example.com/video.mp4" /> // 迁移后: const MyComp: React.FC = () => { return <Video src="https://example.com/video.mp4" />; };音频的捕获方式是:挂载的<Audio>和<Video>元素的声音会被采集、混音后写入输出视频的音轨(见 工作原理文档)。
另外注意一个 API 差异:客户端渲染中不能使用getInputProps()读取输入参数,必须通过renderMediaOnWeb()的inputProps参数传入。
用renderMediaOnWeb()发起渲染
代码改完后,把原来走@remotion/renderer的渲染调用替换为renderMediaOnWeb()。它直接接收 React 组件和 composition 配置,没有 bundling 步骤。官方文档示例:
import {renderMediaOnWeb} from '@remotion/web-renderer'; import {Video} from '@remotion/media'; const Component: React.FC = () => { return <Video src="https://remotion.media/video.mp4" />; }; const {getBlob} = await renderMediaOnWeb({ composition: { component: Component, durationInFrames: 100, fps: 30, width: 1920, height: 1080, id: 'my-composition', }, }); const blob = await getBlob();composition必须包含id、component、durationInFrames、fps、width、height;如果提供calculateMetadata,则宽高帧率时长可以动态计算。把渲染结果落盘的文档示例(通过URL.createObjectURL触发浏览器下载):
const result = await renderMediaOnWeb({composition}); const blob = await result.getBlob(); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'video.mp4'; a.click();默认输出容器是mp4(视频h264、音频aac);webm容器默认vp8+opus。渲染过程中可以用onProgress回调观察进度,它接收encodedFrames、progress、renderEstimatedTime、doneIn等字段。如果希望渲染时页面保持响应,保持默认pageResponsiveness: "medium"即可;要渲染速度优先可以传"disabled"。
如何验证迁移成功
验证按文档给出的方式分三层:
- 渲染前:
canRenderMediaOnWeb()返回canRender: true,且没有severity: "error"的 issue; - 渲染中/后:
renderMediaOnWeb()正常 resolve,getBlob()返回可下载的视频Blob;若出现第 3 节中的 CORS 报错,说明资源配置尚未改对; - Studio 验证:从 v4.0.491 起 Remotion Studio 始终启用客户端渲染,使用 "Render in browser" 按钮即可在 Studio 里直接发起浏览器渲染,用于对照检查迁移后的 composition。
迁移后要清楚的限制
客户端渲染不是"同一套代码换个执行器"。由于浏览器无法直接截取视口,Remotion 是把元素按它在 DOM 中的位置手动绘制到 canvas 上,只有<canvas>、<img>、<video>、<svg>能原生捕获像素,其余样式用 Canvas 2D API 模拟,因此 只支持一个 CSS 子集。迁移后需要对照限制文档检查项目用到的样式,重点包括:
object-position、perspective、transform-style、writing-mode、backdrop-filter、mix-blend-mode均不支持;z-index不支持,层级要靠 DOM 中元素从后往前的书写顺序控制;filter的blur()、brightness()等函数支持,但Safari/WebKit 不支持 filters,需要滤镜效果时用 Chrome 或 Firefox;box-shadow基础形态支持,inset阴影和 spread 半径不支持。
另有两条运行边界:客户端渲染没有多线程并发;渲染所在的浏览器标签页切到后台时requestAnimationFrame会被浏览器节流,Remotion 会用 Web Worker 定时器兜底继续渲染,但后台渲染会比前台慢,渲染期间最好保持标签页可见。
最后提醒一点:客户端渲染每次渲染都会发送一条遥测事件,即使没有设置 license key。这与服务端渲染的行为不同,迁移后请确认这一点对你的部署环境可接受(详见文档中的 Telemetry 章节)。
相关文档
- 迁移指南
- 客户端渲染工作原理
- 客户端渲染限制
renderMediaOnWeb()APIcanRenderMediaOnWeb()API
【免费下载链接】remotion🎥 Make videos programmatically with React项目地址: https://gitcode.com/GitHub_Trending/re/remotion
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考