首页
/ Remotion 视频嵌入与处理:在 React 合成中使用 @remotion/media 实现裁剪、变速、循环与变调

Remotion 视频嵌入与处理:在 React 合成中使用 @remotion/media 实现裁剪、变速、循环与变调

2026-09-07 21:45:56作者:薛曦旖Francesca

导读

本文围绕 Remotion 官方技能文档(packages/skills/skills/remotion-markup/embedding-videos.md)展开,系统讲解如何通过 @remotion/media 包中的 <Video> 组件,在 React 合成中嵌入本地与远程视频,并完成时间裁剪(trim)、延迟出场(Sequence)、尺寸定位、音量控制、变速播放、无限循环以及不改变语速的变调(pitch)处理。读完本文后,你将掌握以帧为粒度控制视频媒体行为的完整 API 用法,并了解这些 props 在 Remotion 源码中的校验逻辑与底层实现路径,可直接应用到剪辑类或数据可视化类视频项目的搭建中。

@remotion/media 是什么,何时使用它

@remotion/media 是 Remotion 生态中的一个实验性媒体标签包,其核心导出即 <Audio><Video> 组件(见 packages/media/src/index.ts)。在 packages/media/package.json 中,该包被描述为 "Experimental WebCodecs-based media tags",当前版本为 4.0.521,依赖 remotionmediabunnyzod

与传统 <OffthreadVideo> / HTML5 <Video> 相比,它基于 WebCodecs 做解码与帧调度,为精准的逐帧编辑、loop 播放、变速与循环裁剪提供了更可控的时间模型。包内目录结构也印证了这一点:src/video/ 下同时存在 video-for-preview.tsxvideo-for-rendering.tsx,分别承担预览与最终渲染两条不同的执行路径。

安装与版本对齐

使用 <Video> 前必须先安装 @remotion/media。文档给出了四种常见包管理器的安装命令,可任选其一:

npx remotion add @remotion/media          # 项目使用 npm
bunx remotion add @remotion/media         # 项目使用 bun
yarn remotion add @remotion/media         # 项目使用 yarn
pnpm exec remotion add @remotion/media    # 项目使用 pnpm

安装时需要注意两个工程约束(来自 packages/media/package.json):

  • 保持 remotion 与所有 @remotion/* 包的版本一致,避免版本漂移导致的运行时冲突;
  • 建议使用精确版本号(去掉 ^),例如 npm install @remotion/media --save-exact

基本用法:本地文件与远程 URL

@remotion/media 导入 <Video>,用 src 指定来源即可把视频嵌入合成。配合 staticFile() 可以引用 public/ 目录下的静态资源:

import { Video } from "@remotion/media";
import { staticFile } from "remotion";

export const MyComposition = () => {
  return <Video src={staticFile("video.mp4")} />;
};

远程 URL 同样受支持,@remotion/media 组件会通过网络加载媒体流:

<Video src="https://remotion.media/video.mp4" />

从源码看,srcVideoProps 中的必填属性,类型为 string;若传入非字符串会直接抛出类型错误(见 packages/media/src/video/video.tsx)。audioStreamIndex 默认取 0,即默认使用第一个音频轨道。

预览与渲染双路径:组件如何工作

<Video> 实际是 Interactive.withSchema() 包装后的组件(见 packages/media/src/video/video.tsx),内部通过 useRemotionEnvironment() 判断当前所处环境:

  • 处于服务端渲染environment.isRendering === true)时,走 VideoForRendering,由离屏(Offthread)渲染管线逐帧输出;
  • 处于预览/交互环境时,走 VideoForPreview,依赖浏览器的媒体播放与迭代器同步(相关实现位于 video-preview-iterator.ts)。

理解这条分叉很重要:它解释了后文提到的 变调(pitch)仅在渲染阶段生效,也解释了为什么预览与最终成片对 loop、trim 后的音量曲线表现一致——两组 props 会被同一份校验逻辑处理后再分发到两条路径。

裁剪视频:trimBeforetrimAfter

使用 trimBeforetrimAfter 可以裁掉视频首尾的片段。注意:这两个值的单位是"帧"而不是秒,因此通常结合 useVideoConfig() 取出的 fps 换算:

const { fps } = useVideoConfig();

return (
  <Video
    src={staticFile("video.mp4")}
    trimBefore={2 * fps}  // 跳过前 2 
    trimAfter={10 * fps}  // 在第 10 秒处结束
  />
);

校验规则定义在 packages/core/src/validate-start-from-props.tsVideopackages/media/src/video/video.tsx 中调用 validateMediaTrimPropsresolveTrimProps 完成统一校验,违反以下任一条件都会抛错:

  • trimBefore 必须是非负的有限数值(不能为 NaNInfinity,不能小于 0);
  • trimAfter 必须是大于 0 的数值;
  • trimAfter 必须严格大于 trimBefore
  • 不能同时使用已废弃的 startFrom / endAttrimBefore / trimAfter

此外,源码还提供了 getVideoSequenceDuration()(见 get-video-sequence-duration.ts)来计算裁剪与变速后的有效时长,包内测试 loop-trim-time-calculation.test.tstrim-change-seek.test.ts 对 trim 与 loop 叠加后的时间计算做了大量回归验证。

延迟出现:用 <Sequence> 包装

让视频延后数秒再出现,只需把它放进 <Sequence> 并设置 from。例如从第 1 秒开始播放:

import { Sequence, staticFile } from "remotion";
import { Video } from "@remotion/media";

const { fps } = useVideoConfig();

return (
  <Sequence from={1 * fps}>
    <Video src={staticFile("video.mp4")} />
  </Sequence>
);

<Sequence> 会平移其内部所有内容的时间起点,因此该视频相当于从第 1 * fps 帧才开始可见。结合文档《Remotion 视频剪辑思路》中的建议,在多片段剪辑场景里每个 <Video> 都应保持独立的 JSX 节点,直接传入自己的 fromtrimBeforedurationInFrames,而不是用 .map() 批量生成,这样才便于在 Studio 时间轴中进行独立的编辑操作。

尺寸与位置控制:styleobjectFit

<Video> 透传标准 React style,可精确控制尺寸与绝对定位:

<Video
  src={staticFile("video.mp4")}
  style={{
    width: 500,
    height: 300,
    position: "absolute",
    top: 100,
    left: 50,
  }}
  objectFit="cover"
/>

objectFit 的可选值与 CSS object-fit 对齐,定义在 packages/media/src/video/props.ts

取值 行为
fill 拉伸填满容器,可能变形
contain 完整显示,保持宽高比(默认值
cover 铺满容器并裁掉超出部分
none 保持原始尺寸
scale-down nonecontain 中较小的结果

源码中 objectFit 的默认值为 'contain'(见 packages/media/src/video/video.tsx),如果设成 cover 而容器没有按视频宽高比设计,超出部分会被裁掉——这是预览与成片都遵循的一致行为。

音量控制:静态值、动态回调与静音

设置固定音量(0 到 1):

<Video src={staticFile("video.mp4")} volume={0.5} />

也可以传回调函数,按当前帧号动态计算音量。配合 interpolate 即可实现淡入淡出:

import { interpolate } from "remotion";

const { fps } = useVideoConfig();

return (
  <Video
    src={staticFile("video.mp4")}
    volume={(f) =>
      interpolate(f, [0, 1 * fps], [0, 1], { extrapolateRight: "clamp" })
    }
  />
);

上面例子让音量在第 1 秒内从 0 线性过渡到 1。volume 的类型是 VolumeProp(即固定数字或 (frame: number) => number)。

彻底静音用 muted

<Video src={staticFile("video.mp4")} muted />

两点补充说明(来自 video.tsx 中的 schema 定义与默认值):

  • volume 默认值为 1,schema 中标记的范围是 020(步进 0.01),但常规做法仍建议保持在 0~1 区间;
  • 配合 muted 时底层仍会保留对音量状态的管理,便于在 Studio 时间轴中展示媒体轨道的音量信息。

播放速度:playbackRate

playbackRate 直接控制播放倍率,无需修改 fpsdurationInFrames 即可整体变速:

// 2 倍速
<Video src={staticFile("video.mp4")} playbackRate={2} />
// 半速(慢动作)
<Video src={staticFile("video.mp4")} playbackRate={0.5} />

相关约束与实现事实:

  • 校验在 validateMediaProps 中执行,schema 规定 playbackRate 最小值 0.1、步进 0.01、默认 1,且不可逐帧关键帧化(keyframable: false);
  • 反向播放不受支持,即 playbackRate 不能为负数;
  • 变速会联动影响 trimBefore / trimAfter / loop 组合下的总时长,getVideoSequenceDuration 会把这些参数统一折算;
  • 包内 html5-playback-rate.test.tsx 对该行为做了专门验证。

无限循环:looploopVolumeCurveBehavior

使用 loop 让视频无限循环:

<Video src={staticFile("video.mp4")} loop />

循环时若还想用基于帧号的音量回调做"跨多轮循环"的包络(如整体淡出),必须指定 loopVolumeCurveBehavior 以控制循环期间帧号如何递增:

  • "repeat":每轮循环都把帧号重置回 0(volume 回调在每个循环周期内重新播放曲线),这是默认值
  • "extend":帧号跨循环持续递增,音量曲线可跨越多轮循环计算。
<Video
  src={staticFile("video.mp4")}
  loop
  loopVolumeCurveBehavior="extend"
  volume={(f) => interpolate(f, [0, 300], [1, 0])} // 跨多轮循环逐渐淡出
/>

这个例子中 loopVolumeCurveBehavior="extend"f 从 0 一路累加,volume 曲线在 300 帧内从 1 衰减到 0,实现跨越多次循环的整体淡出。若改用默认的 "repeat",音量会在每轮循环内反复重置。loopVolumeCurveBehavior 的默认值与 loop 的默认值(false)都定义在 packages/media/src/video/video.tsx,对应测试见 looping.test.tslooping-audio-timestamps.test.tsplayer-loop-frame-accuracy.test.tsx(后者专门校验 player 中 loop + trim 的逐帧精度)。

变调(不改变语速):toneFrequency

toneFrequency 用来在不改变播放速度的前提下调整音调,取值范围为 0.0121 表示原始音调,小于 1 变低沉、大于 1 变尖细:

<Video
  src={staticFile("video.mp4")}
  toneFrequency={1.5} // 提高音调
/>
<Video
  src={staticFile("video.mp4")}
  toneFrequency={0.8} // 降低音调
/>

校验逻辑位于 packages/media/src/validate-tone-frequency.tstoneFrequency 必须是介于 0.012 之间的有限数值,否则 <Video>(或 <Audio>)会抛出类型错误。相关实现由 pitch-shift.ts 完成,包内 pitch-shift.test.tspitch-shift-preview.test.tsx 分别覆盖了离线处理与预览路径。

关键限制:变调仅在服务端渲染阶段生效(对应 VideoForRendering 路径),在 Remotion Studio 实时预览或 <Player /> 中不会生效。这正是前文"渲染/预览双路径"设计带来的行为差异,排片时若发现预览与成片音调不一致,属预期行为而非 bug。

Props 速查总表

综合本文档与 props.ts / video.tsx,核心 props 一览:

Prop 类型 / 默认值 说明
src string(必填) 视频源:staticFile() 本地路径或远程 URL
trimBefore number,默认 undefined 从开头裁掉的帧数(单位:帧)
trimAfter number,默认 undefined 在此时刻结束(单位:帧)
volume number | (f) => number,默认 1 静态或随帧变化的音量
muted boolean,默认 false 完全静音
playbackRate number,默认 1 播放倍率,最小 0.1,不支持反向
loop boolean,默认 false 是否无限循环
loopVolumeCurveBehavior "repeat" | "extend",默认 "repeat" 循环时帧号是否跨轮累计
toneFrequency number,默认 1,范围 0.01~2 变调倍率,仅渲染阶段生效
objectFit 'fill'|'contain'|'cover'|'none'|'scale-down',默认 'contain' 画面适配方式
style React.CSSProperties 尺寸、位置、透明度等样式
audioStreamIndex number,默认 0 选择视频内的音频轨道

关联文档与源码延伸

掌握以上 API 后,你已经可以在 Remotion 中实现剪辑软件级别的视频时间轴:逐帧裁剪素材、控制出场时机、叠加音量和淡入淡出、变速与循环,甚至在渲染阶段独立调整音调——所有行为都保持"预览所见即成片所得"(除变调这一明确标注的例外)。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.13 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.8 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
529
593
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
915
1.83 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.58 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.35 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.01 K
515
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
388