首页
/ OpenMontage Remotion API 完全参考:从核心 Hooks 到 Lambda 云端渲染的实战指南

OpenMontage Remotion API 完全参考:从核心 Hooks 到 Lambda 云端渲染的实战指南

2026-09-09 17:58:54作者:咎岭娴Homer

本指南以 OpenMontage 仓库中的 Remotion API 参考文档 为骨架,系统梳理 Remotion 的核心 Hooks、插值动画、媒体组件、渲染器、Player 与 Lambda 云端渲染 API,并结合 remotion-composer 目录下的真实实现(Explainer、CinematicRenderer、TalkingHead 等 13 个 Composition 及 20+ 个可复用组件)逐项印证每个 API 在生产级视频生成流水线中的用法。读完本文,你将掌握 Remotion 从"写一个帧动画"到"数据驱动批量渲染"再到"无服务器云端出片"的完整 API 能力栈,并能在 OpenMontage 中直接复用这些模式。

文档定位:OpenMontage 为什么需要一份 Remotion API 参考

OpenMontage 将 Remotion 设为所有最终渲染的默认合成引擎:视频片段(<OffthreadVideo>)、静态图片、动画场景、组件化图表、转场与混合内容,全部在一次 React 渲染通道中完成(见 skills/core/remotion.md)。而 .agents/skills/remotion/SKILL.md 明确指出:"核心 Remotion 框架知识(hooks、动画、渲染等)以 .agents/skills/remotion/reference.md 为准",它服务于两类读者:

  1. Agent 技能调用者:AI 编码助手在生成 Remotion 合成时,按此参考查 API 签名、参数默认值与约束;
  2. 人工开发者:在 remotion-composer 中编写或扩展 Composition 时的速查手册。

仓库的 package.json 锁定了 Remotion 4.0.484 与配套的 @remotion/transitions@remotion/player@remotion/google-fonts 等版本,本文所有 API 均以该版本为基准。

一、核心 Hooks:帧驱动的一切起点

1.1 useCurrentFrame()

const frame = useCurrentFrame();

返回当前帧号(从 0 开始计数)。在 <Sequence> 内部,返回的是相对该 Sequence 起始帧的帧号,而非全局帧号——这是理解 Remotion 时间模型的关键。

仓库证据Explainer.tsx 中的 AnimatedBackground 组件用 useCurrentFrame() 驱动背景渐变角度与浮动光球:

const frame = useCurrentFrame();
const angle1 = 135 + Math.sin(frame / (fps * 8)) * 30;

每个场景被包在 <Sequence> 中(Explainer.tsx),所以场景组件里拿到的 frame 天然是"本场景内第几帧",做入场动画时无需自己减去 from 偏移。

1.2 useVideoConfig()

const { width, height, fps, durationInFrames, id, defaultProps } = useVideoConfig();

返回当前 Composition 的全局配置。width/height/fps/durationInFrames 来自 <Composition> 注册值与 calculateMetadata 的动态覆盖。

关键陷阱(OpenMontage 在源码中专门标注为 #1 常见坑)durationInFrames 返回的是整个 Composition 的时长,而不是当前 <Sequence> 的时长。AnimeScene.tsx 的注释原样记录了这一坑:

CRITICAL: useVideoConfig().durationInFrames returns the FULL composition duration, not the Sequence duration.

修复方案是父组件显式传入 sceneDurationSeconds,子组件自行换算:Math.round(sceneDurationSeconds * fps)。在 Explainer.tsx 中即为 sceneDurationSeconds={cut.out_seconds - cut.in_seconds}

仓库证据TitledVideo.tsxEditorialTagline 同时消费 framefpsdurationInFrames 三者,实现片尾标语在最后 10 帧内淡出。

二、interpolate():帧到数值的万能映射

interpolate(
  input: number,
  inputRange: number[],
  outputRange: number[],
  options?: {
    extrapolateLeft?: 'extend' | 'clamp' | 'identity' | 'wrap',
    extrapolateRight?: 'extend' | 'clamp' | 'identity' | 'wrap',
    easing?: (t: number) => number
  }
): number

inputRangeoutputRange 必须等长且单调。四个外推策略的含义:

策略 行为
extend 输入超出范围时按斜率继续延伸(默认)
clamp 超出范围时钉死在端点值,防止数值跑飞
identity 超出范围时返回输入值本身
wrap 按周期回绕(较少用)

示例(来自原文档):

// Basic interpolation
interpolate(15, [0, 30], [0, 100]); // 50

// With clamping
interpolate(50, [0, 30], [0, 1], { extrapolateRight: 'clamp' }); // 1

// Multiple keyframes
interpolate(frame, [0, 20, 40, 60], [0, 1, 1, 0]);

// With easing
interpolate(frame, [0, 30], [0, 100], { easing: Easing.bezier(0.42, 0, 0.58, 1) });

OpenMontage 实践Explainer.tsxImageSceneinterpolate 实现帧级淡入淡出与 Ken Burns 缩放,且双端全部 clamp,这是仓库在 skills/core/remotion.md 中反复强调的硬性约束:"Always clamp interpolate() — use extrapolateLeft: 'clamp', extrapolateRight: 'clamp' to prevent values shooting past endpoints." 音乐淡入淡出音量曲线同样由 interpolate 计算(Explainer.tsx)。

三、spring():物理弹簧动画

spring({
  frame: number,
  fps: number,
  config?: {
    damping?: number,      // Default: 10
    mass?: number,         // Default: 1
    stiffness?: number,    // Default: 100
    overshootClamping?: boolean
  },
  from?: number,           // Default: 0
  to?: number,             // Default: 1
  durationInFrames?: number,
  durationRestThreshold?: number,
  delay?: number,
  reverse?: boolean
}): number

弹簧物理参数决定动画"性格":

配置 效果
{ damping: 5, stiffness: 200 } 高回弹(bouncy),适合活泼入场
{ damping: 20, stiffness: 100, overshootClamping: true } 无回弹,干净利落,适合专业/科技感
{ damping: 20, mass: 2 } 慢速沉稳,适合厚重感标题

仓库证据

  • TitledVideo.tsxspring({ frame: frame - 12, fps, config: { damping: 18, stiffness: 70 } }) 让下划线在文字出现 12 帧后"弹"出来——delay 通过把传入的 frame 减去 12 实现;
  • Explainer.tsxspring({ frame, fps, config: { damping: 18, stiffness: 80 } }) 做图片淡入,营造丝滑入场;
  • 主题系统把弹簧参数下沉到配置:THEMES 中每个主题都带 springConfig: { damping, stiffness, mass }(见 Root.tsx),例如 clean-professional{ damping: 20, stiffness: 120, mass: 1 }(低回弹、专业感),anime-ghibli{ damping: 18, stiffness: 60, mass: 1 }(松弛、梦幻感)。

四、measureSpring():测量弹簧动画时长

import { measureSpring } from 'remotion';

const duration = measureSpring({ fps: 30, config: { damping: 10 } });
// Returns number of frames until spring settles

spring() 在数学上理论上限无穷(只是振幅衰减到可忽略),因此若要把它放进 durationInFrames、或与转场时长对齐,就必须用 measureSpring 预先计算"到稳定需要多少帧"。典型用法:<Sequence durationInFrames={measureSpring({ fps, config })}>,确保动画播完再切场,避免被截断。

五、interpolateColors():颜色插值

interpolateColors(
  input: number,
  inputRange: number[],
  outputRange: string[],  // Hex, rgb(), rgba(), hsl()
  options?: { extrapolateLeft?, extrapolateRight? }
): string

interpolate 用法一致,但输出端是颜色字符串(支持 #RRGGBBrgb()rgba()hsl())。常用于根据进度在两种主题色之间渐变,例如图表强调色随动画进度从 #2563EB 过渡到 #F59E0B。注意 outputRange 各值必须同一颜色格式(不能混用 hex 与 rgba)。

六、Easing:缓动函数库

import { Easing } from 'remotion';

// Basic
Easing.linear
Easing.ease
Easing.quad
Easing.cubic

// In/Out/InOut variants
Easing.in(Easing.quad)
Easing.out(Easing.cubic)
Easing.inOut(Easing.ease)

// Cubic bezier
Easing.bezier(x1, y1, x2, y2)

// Other
Easing.circle
Easing.back(s?)      // Overshoot
Easing.elastic(bounciness?)
Easing.bounce
Easing.sin
Easing.exp
Easing.poly(n)       // Power of n

分类速记:

  • 多项式族quad/cubic/poly(n)n 越大加速越陡;
  • In/Out/InOut 包装器Easing.in(fn) 慢入、Easing.out(fn) 慢出、Easing.inOut(fn) 两头缓;
  • 曲线化Easing.bezier(x1,y1,x2,y2) 自定义贝塞尔,等价于 CSS cubic-bezier
  • 特效族back(s)(过冲回弹,s 控制幅度)、elastic(bounciness)(橡皮筋弹性)、bounce(落地弹跳)、circle/sin/exp

OpenMontage 的主题系统将缓动名写入 playbook:animationEasing: "easeInOutCubic" 等参数由样式 YAML(如 styles/anime-ghibli.yamlstyles/clean-professional.yaml)驱动,最终映射到组件内的 Easing 选择。

七、核心组件:搭建时间轴与画面

7.1 Composition:注册一个可渲染条目

<Composition
  id="MyVideo"
  component={MyComponent}
  // OR lazyComponent={() => import('./MyComponent')}
  durationInFrames={150}
  fps={30}
  width={1920}
  height={1080}
  defaultProps={{ title: 'Hello' }}
  calculateMetadata={async ({ props }) => ({
    durationInFrames: props.items.length * 30,
    props: { ...props, computed: true }
  })}
/>

要点:

  • id 是渲染 CLI 与 Studio 中查找该合成的唯一标识;
  • defaultProps 提供 Studio 预览与渲染兜底值,运行时 props 会被 --props 传入的 JSON 覆盖
  • calculateMetadata 在渲染前异步执行,可动态计算时长/分辨率/新 props。

仓库证据Root.tsx 注册了 Explainer 合成,defaultProps{ cuts: [], overlays: [], captions: [], audio: {} }calculateMetadata 根据 cuts 中最后一个 out_seconds 加 1 秒余量计算总时长(Root.tsx)。同一文件还注册了 CinematicRendererTalkingHeadTitledVideoHeroTitleProductRevealCaptionOverlayOnlyCollageBurstLyricOverlayEndTagEndTagOverlay 共 13 个 Composition,覆盖解释视频、电影感渲染、口播竖屏、片尾标签等场景。

7.2 Sequence:局部时间偏移

<Sequence
  from={30}                    // Start frame
  durationInFrames={60}        // Optional duration
  name="Intro"                 // Label in Studio timeline
  layout="none"                // "none" | "absolute-fill"
>
  <Child />
</Sequence>

layout 语义:"none"(默认)子元素按普通文档流排布;"absolute-fill" 子元素绝对定位填满全屏。在 OpenMontage 的 Explainer 中,每个场景即一个 Sequence:

{cuts.map((cut) => {
  const from = Math.round(cut.in_seconds * fps);
  const duration = Math.round((cut.out_seconds - cut.in_seconds) * fps);
  return (
    <Sequence key={cut.id} from={from} durationInFrames={duration}>
      <SceneRenderer cut={cut} theme={theme} />
    </Sequence>
  );
})}

Explainer.tsx)——scene_plan.json 中的秒级时间戳在此换算为帧号,正是 skills/core/remotion.md 中"Artifacts Map to Remotion Props"映射表的落地实现。

7.3 Series:无空隙顺序排布

<Series>
  <Series.Sequence durationInFrames={30} offset={-5}>
    <A />  {/* Frames 0-29 */}
  </Series.Sequence>
  <Series.Sequence durationInFrames={60}>
    <B />  {/* Frames 25-84 (offset caused overlap) */}
  </Series.Sequence>
</Series>

Series 自动把子 Sequence 首尾相接,offset 可为负值制造重叠(用于交叉淡入淡出)。与手写 from 相比,Series 避免了"前一个时长改了、后一个 from 忘了改"的连锁错误。

7.4 Loop:循环播放

<Loop
  durationInFrames={30}
  times={3}                    // Or Infinity
  layout="none"
>
  <Animation />
</Loop>

times={Infinity} 时循环至父级时长耗尽。适合背景动画、粒子层、循环水印等"不需要关心总时长"的内容。

7.5 AbsoluteFill:全屏绝对定位容器

<AbsoluteFill style={{ backgroundColor: '#000' }}>
  {/* Position: absolute, full width/height */}
</AbsoluteFill>

这是 Remotion 的 position: absolute; inset: 0 快捷容器,OpenMontage 几乎所有场景的最外层都使用它(例如 Explainer.tsxbackgroundColor: theme.backgroundColor 根容器,以及 CinematicRenderer.tsx 的场景背景层)。

7.6 媒体组件

Img(等待加载完成后才进入画面,避免闪烁):

<Img src={staticFile('photo.jpg')} style={{ width: '100%' }} />

Video / Html5Video

<Video
  src={staticFile('clip.mp4')}
  volume={0.5}                 // 0-1, or callback: (f) => f / 100
  playbackRate={1.5}
  muted={false}
  loop={false}
  startFrom={30}               // Skip first 30 frames of source
  endAt={120}                  // Stop at frame 120 of source
  acceptableTimeShiftInSeconds={0.2}
/>

startFrom/endAt源素材的时间裁剪(对应 OpenMontage 中 source_in_seconds),与 Sequence 的 from(时间轴位置)是两回事。volume 支持函数形式 (frame) => number,可实现逐帧音量曲线——OpenMontage 的音乐淡入淡出正是这样做的。

OffthreadVideo(推荐,性能更优):

<OffthreadVideo
  src={staticFile('clip.mp4')}
  volume={0.5}
  transparent={false}          // For videos with alpha
  toneMapped={true}           // HDR tone mapping
/>

<Video> 在渲染时通过 Chromium 实时播放视频并逐帧截屏,<OffthreadVideo> 则用 FFmpeg 预抽取帧,更快、更省内存、更确定。OpenMontage 的硬性最佳实践即"Use OffthreadVideo — Better performance than <Video> for complex compositions"。仓库证据Explainer.tsx 的视频场景、CinematicRenderer.tsx 的电影场景均使用 OffthreadVideo,后者还配合 trimBefore/trimAfter/playbackRate 实现源素材裁剪与变速。此外 skills/core/remotion.md 提醒:playbackRate 必须为常数,极端变速需先用 FFmpeg 预处理。

Audio

<Audio
  src={staticFile('music.mp3')}
  volume={0.8}
  startFrom={0}
  endAt={300}
  playbackRate={1}
/>

仓库证据:Explainer 用两个 <Audio> 叠放解说与音乐(Explainer.tsx),音乐的 startFromoffsetSeconds * fps 换算(跳过安静前奏)、loop 支持、音量是 (f) => Math.min(fadeIn, fadeOut) 的插值函数,完整对应 skills/core/remotion.mdaudio.music 配置的 offsetSeconds/loop/fadeInSeconds/fadeOutSeconds 四个字段。

AnimatedImage(GIF/APNG):

<AnimatedImage src={staticFile('animation.gif')} />

八、异步处理:delayRender / continueRender / cancelRender

Remotion 渲染是"按帧同步求值"的,异步数据(TTS 音频时长、远程资源、AI 生成结果)必须先阻塞再放行:

import { delayRender, continueRender, cancelRender } from 'remotion';

// Block render
const handle = delayRender('Loading data...');

// Unblock when ready
continueRender(handle);

// Cancel on error
cancelRender(new Error('Failed to load'));

// With timeout
const handle = delayRender('Loading...', { timeoutInMilliseconds: 30000 });

三条规则:

  1. 任何 fetch/异步计算开始前先 delayRender,完成后再 continueRender
  2. 出错必须 cancelRender,否则渲染会悬挂直到超时;
  3. 可选 timeoutInMilliseconds 显式设超时(默认 30 秒),防止静默卡死。

最佳实践delayRender 的调用点必须在渲染进程的同步阶段(模块顶层或组件首次渲染时),不能在 setTimeout 回调里注册——否则 Remotion 可能已判定"没有 pending delay"直接渲染出空帧。

九、静态文件与预取

import { staticFile, prefetch, getStaticFiles } from 'remotion';

// Reference file in public/
const url = staticFile('video.mp4');

// Prefetch for faster playback
const { free, waitUntilDone } = prefetch(url);
await waitUntilDone();
// Later: free() to release memory

// List all static files
const files = getStaticFiles();  // ['video.mp4', 'image.png', ...]
  • staticFile('x')public/x 解析为可用的 URL/路径,是引用静态资产的唯一正确方式;
  • prefetch 预取媒体加速 <Video> 播放,完成后务必 free() 释放内存;
  • getStaticFiles 在渲染期列出 public 下全部文件,可用于校验 staticFile() 引用是否有效(OpenMontage 的 composition_validator 正是做这类前置校验)。

仓库证据:OpenMontage 的资产解析不止于 public/,还需支持远程 URL 与绝对路径。resolveAsset.ts 实现了三级路由:远程 URL(http(s)://data:)原样返回;绝对路径转为 file:// 协议;其余走 staticFile(clean) 落入 public/

十、Input Props:渲染期注入数据

import { getInputProps } from 'remotion';

const props = getInputProps();  // Data passed via --props CLI flag

npx remotion render 时通过 --props=xxx.json 传入的数据,可在组件内用 getInputProps() 读取。与 <Composition defaultProps> 的关系:默认 props 兜底,--props 覆盖。OpenMontage 的渲染调用方式(skills/core/remotion.md):

npx remotion render Explainer \
  --props="public/demo-props/my-video.json" \
  --output=output/final.mp4 \
  --codec=h264 --crf=18

十一、环境检测:getRemotionEnvironment()

import { getRemotionEnvironment } from 'remotion';

const env = getRemotionEnvironment();
// { isStudio: boolean, isRendering: boolean, isPlayer: boolean }

用于区分代码运行在 Studio 预览、实际渲染、还是 <Player> 嵌入环境。典型用途:仅在渲染期执行昂贵的预取、在 Player 中跳过某些渲染专用逻辑、或在 Studio 中显示调试信息。

十二、random():确定性随机

import { random } from 'remotion';

const value = random('my-seed');      // 0-1, same every render
const value2 = random('seed', 0, 100); // 0-100
const value3 = random(null);           // Different each render

Remotion 渲染每帧都会重新执行组件,普通 Math.random() 会在不同帧/不同机器上产生不同结果,导致闪烁与不可复现。random(seed)按 seed 确定的伪随机数:同一 seed 在任何帧、任何机器上结果一致。传 null 则退化为每次调用都不同(适合 Player 交互场景)。OpenMontage 的粒子系统、图表散点等需要"看起来随机但每次渲染完全一致"的效果都依赖它。

十三、@remotion/renderer:程序化渲染

import { bundle } from '@remotion/bundler';
import {
  renderMedia,
  renderStill,
  selectComposition,
  getCompositions,
  renderFrames,
  stitchFramesToVideo
} from '@remotion/renderer';

13.1 完整渲染流程

// Bundle project
const bundleLocation = await bundle({
  entryPoint: './src/index.ts',
  webpackOverride: (config) => config,
});

// Get composition
const composition = await selectComposition({
  serveUrl: bundleLocation,
  id: 'MyComp',
  inputProps: {},
});

// Render video
await renderMedia({
  composition,
  serveUrl: bundleLocation,
  codec: 'h264',              // h264, h265, vp8, vp9, gif, prores
  outputLocation: 'out.mp4',
  inputProps: {},
  onProgress: ({ progress }) => console.log(`${progress * 100}%`),
  imageFormat: 'jpeg',        // jpeg or png
  jpegQuality: 80,
  scale: 1,
  frameRange: [0, 59],        // Optional: specific frames
  muted: false,
  audioBitrate: '128k',
  videoBitrate: '5M',
  crf: 18,                    // Quality (lower = better, bigger)
  concurrency: 4,
});

参数决策速查:

参数 说明 建议
codec 输出编码 通用 h264;透明通道用 prores 或带 alpha 的 WebM
crf 质量/体积权衡 数字越小质量越高文件越大;18 是高质量默认
imageFormat/jpegQuality 中间帧格式 有透明需求用 png,否则 jpeg 更快
frameRange 只渲染部分帧 预览、并行分片渲染时用
concurrency 并行度 每并发会起一个 Chromium,注意内存

13.2 渲染静态帧

// Render still image
await renderStill({
  composition,
  serveUrl: bundleLocation,
  output: 'thumbnail.png',
  frame: 30,
  imageFormat: 'png',
});

OpenMontage 的视觉质量检查流程(skills/core/remotion.md 的 Post-Render Verification Protocol)会在每个场景中点抽取静态帧做人工/视觉模型审查,renderStill 正是该协议的程序化实现基础。

十四、@remotion/lambda:无服务器云端渲染

import {
  deployFunction,
  deploySite,
  renderMediaOnLambda,
  renderStillOnLambda,
  getRenderProgress,
  downloadMedia,
} from '@remotion/lambda';

14.1 三步流程

第一步:部署 Lambda 函数与站点

// Deploy function
const { functionName } = await deployFunction({
  region: 'us-east-1',
  timeoutInSeconds: 120,
  memorySizeInMb: 2048,
});

// Deploy site
const { serveUrl } = await deploySite({
  entryPoint: './src/index.ts',
  region: 'us-east-1',
  siteName: 'my-video',
});

第二步:触发渲染并轮询进度

// Render
const { renderId, bucketName } = await renderMediaOnLambda({
  region: 'us-east-1',
  functionName,
  serveUrl,
  composition: 'MyComp',
  codec: 'h264',
  inputProps: {},
  framesPerLambda: 20,          // 每个 Lambda 实例渲染的帧数,决定并行度
});

// Check progress
const progress = await getRenderProgress({
  renderId,
  bucketName,
  region: 'us-east-1',
  functionName,
});

第三步:下载成品

// Download when done
if (progress.done) {
  await downloadMedia({
    bucketName,
    renderId,
    region: 'us-east-1',
    outPath: 'video.mp4',
  });
}

framesPerLambda 是关键并行参数:越小并发切分越细、出片越快,但冷启动成本越高。OpenMontage 在本地渲染的定位是"CPU 密集型但零 API 成本"(成本追踪器里 reserve: 0),而 Lambda 路径适合需要水平扩展、无固定渲染机的生产环境。

十五、@remotion/player:网页内嵌播放器

import { Player, PlayerRef } from '@remotion/player';

const playerRef = useRef<PlayerRef>(null);

15.1 声明式配置

<Player
  ref={playerRef}
  component={MyComp}
  // OR lazyComponent={() => import('./MyComp')}
  durationInFrames={150}
  fps={30}
  compositionWidth={1920}
  compositionHeight={1080}
  inputProps={{}}
  style={{ width: '100%' }}
  controls={true}
  autoPlay={false}
  loop={false}
  showVolumeControls={true}
  allowFullscreen={true}
  clickToPlay={true}
  doubleClickToFullscreen={true}
  spaceKeyToPlayOrPause={true}
  playbackRate={1}
  renderLoading={() => <div>Loading...</div>}
  errorFallback={({ error }) => <div>Error: {error.message}</div>}
  numberOfSharedAudioTags={5}
  initiallyShowControls={3000}
  renderPlayPauseButton={() => null}
  moveToBeginningWhenEnded={true}
/>
  • compositionWidth/compositionHeightwidth/height(CSS 尺寸)分离:前者是逻辑分辨率,后者是显示尺寸,二者可不同(响应式);
  • numberOfSharedAudioTags 控制浏览器并行音频标签数量,多个 <Audio> 场景需要调大以避免音画不同步;
  • moveToBeginningWhenEnded 播放结束后回到开头(而非停在最后一帧)。

15.2 命令式 API(PlayerRef)

playerRef.current?.play();
playerRef.current?.pause();
playerRef.current?.toggle();
playerRef.current?.seekTo(30);
playerRef.current?.getCurrentFrame();
playerRef.current?.isPlaying();
playerRef.current?.getVolume();
playerRef.current?.setVolume(0.5);
playerRef.current?.isMuted();
playerRef.current?.mute();
playerRef.current?.unmute();
playerRef.current?.requestFullscreen();
playerRef.current?.exitFullscreen();
playerRef.current?.isFullscreen();

15.3 事件回调

<Player
  onPlay={() => {}}
  onPause={() => {}}
  onEnded={() => {}}
  onError={(e) => {}}
  onSeeked={(frame) => {}}
  onTimeUpdate={({ frame }) => {}}
  onFullscreenChange={(isFullscreen) => {}}
/>

Player 是"同一份 Composition 代码,既渲染成视频文件又嵌入网页实时播放"的关键桥梁——OpenMontage 用它做效果画廊、交互式预览与最终交付页展示。

十六、calculateMetadata:动态化合成属性

export const calculateMetadata: CalculateMetadataFunction<Props> = async ({
  props,
  abortSignal,
  defaultProps
}) => {
  const data = await fetch('/api/data', { signal: abortSignal });

  return {
    durationInFrames: data.items.length * 30,
    fps: 60,
    width: 1920,
    height: 1080,
    props: { ...props, items: data.items },
  };
};

<Composition
  id="Dynamic"
  component={MyComp}
  calculateMetadata={calculateMetadata}
  // Base values (can be overridden by calculateMetadata)
  durationInFrames={1}
  fps={30}
  width={1920}
  height={1080}
/>

要点:

  • abortSignal 用于在渲染被取消时中止内部 fetch,避免泄漏;
  • 返回对象的字段全部可选——只返回需要覆盖的字段即可;
  • props 变了(比如远程数据注入),必须显式 props: { ...props, newField } 合并返回。

仓库证据:Explainer 合成的 calculateMetadata 根据 cuts 计算时长(Root.tsx),CinematicRendererTitledVideo 各有自己的 metadata 函数(calculateCinematicMetadatacalculateTitledVideoMetadata),后者还会用 @remotion/media-utilsgetVideoMetadata 探测源视频时长(TitledVideo.tsx),实现"视频多长,合成多长"的自动适配。

十七、从参考到落地:OpenMontage 的组合用法

17.1 主题系统与 springConfig

Root.tsxTHEMES 把"clean-professional / flat-motion-graphics / minimalist-diagram / anime-ghibli"四个主题各绑定一套 springConfigtransitionDuration,对应仓库 styles 目录下的四个样式 playbook。渲染时 resolveTheme 依据 props 中的 themeplaybook 名称选中主题(Root.tsx),也可直接传入 themeConfig 对象整体覆盖——这正是 defaultProps 与运行时 props 协作的典型场景。

17.2 场景时长陷阱的标准解法

AnimeScene 是"Sequence 时长坑"的标准解:父组件把 sceneDurationSeconds 作为 prop 传入(Explainer.tsx),组件内部以 Math.round(sceneDurationSeconds * fps) 作为插值上限(AnimeScene.tsx),所有 crossfade、镜头运动、灯光渐变都基于这个"真实场景时长",而不是 useVideoConfig().durationInFrames任何新增的 Remotion 场景组件都应复刻该模式

17.3 渲染前置校验

在调用 renderer API 或 CLI 之前,OpenMontage 强制先跑 tools/analysis/composition_validator.py,校验资产文件存在、旁白音频不超视频时长、音乐不短于视频、cut 时间合法——把"渲染到一半才发现缺图"的失败前置化。

十八、渲染调用与质量门禁

CLI 方式skills/core/remotion.md):

# Standard render (composition name is "Explainer", no entry point needed)
npx remotion render Explainer \
  --props="public/demo-props/my-video.json" \
  --output=output/final.mp4 \
  --codec=h264 --crf=18

# With specific media profile
npx remotion render Explainer \
  --width=1080 --height=1920 --fps=30 \
  --props="public/demo-props/my-video.json" \
  --output=output.mp4

注意:不要指定 src/index.ts 作为入口点,Remotion 会自动发现 Composition;--width/--height/--fps 会覆盖合成默认值,实现 YouTube 横屏 / TikTok 竖屏等媒体配置档的切换。Python 侧则由 video_compose.pybackend="remotion" 时通过 subprocess 调用。

渲染后的验证协议(所有流水线强制):先用 ffprobe -show_format -show_streams 确认视频流分辨率/帧率正确、音频流存在、时长偏差在 ±5% 内;再抽取各场景中间帧做视觉审查;最后用 WhisperX 转写成品音频,核对字幕词数与脚本匹配度。只有全部通过,才把成品交给用户。

结语

这份 API 参考覆盖了 Remotion 的完整能力面:useCurrentFrame/useVideoConfig 定义时间与配置上下文,interpolate/spring/interpolateColors/Easing 构成帧驱动动画语言,Composition/Sequence/Series/Loop/媒体组件搭建时间轴,delayRender 桥接异步数据,staticFile/getInputProps/getRemotionEnvironment/random 解决资产、数据注入、环境判断与确定性随机,而 @remotion/renderer/@remotion/lambda/@remotion/player 三件套分别覆盖本地渲染、云端渲染与网页播放三种交付形态。在 OpenMontage 中,这些 API 已被 Explainer.tsxCinematicRenderer.tsxRoot.tsx 等真实组件充分验证——阅读本文时对照这些源码,即可看到每个 API 在生产流水线中的最佳实践形态。

热门项目推荐
相关项目推荐

项目优选

收起
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.15 K
2.77 K
kernelkernel
deepin linux kernel
C
34
18
docsdocs
暂无描述
Markdown
900
5.83 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
929
1.85 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
860
1.36 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.94 K
1.03 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.37 K
1.47 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
534
603
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
398
leetcodeleetcode
🔥LeetCode solutions in any programming language | 多种编程语言实现 LeetCode、《剑指 Offer(第 2 版)》、《程序员面试金典(第 6 版)》题解
Markdown
77
23