首页
/ OpenMontage Remotion 资源引用指南:public 目录、staticFile() 与远程素材的正确接入方式

OpenMontage Remotion 资源引用指南:public 目录、staticFile() 与远程素材的正确接入方式

2026-09-08 14:48:38作者:滑思眉Philip

本文系统讲解 OpenMontage 项目中 Remotion 组合(composition)如何引用图片、视频、音频与字体等资产:静态资源统一放入项目根目录的 public/ 文件夹,通过 staticFile() 引用以获得可正确部署到子目录的编码 URL;并配合仓库源码与测试,说明一套兼容"远程 URL / 绝对文件路径 / public 相对路径"的资源解析层如何在真实代码中落地。

规则文档的来源与角色

本篇文章围绕的技能规则文件位于 .agents/skills/remotion-best-practices/rules/assets.md,它是 OpenMontage 的 remotion-best-practices 技能包(SKILL.md)下的一条独立规则,同时在 .claude/skills/remotion-best-practices/rules/assets.md 存有镜像副本,供不同编码助手(Agent)按各自约定的技能目录加载。

OpenMontage 是一套"把 AI 编码助手变成视频制作工作室"的开源项目,而 Remotion 承担了其中组合式渲染的职责。仓库中的 remotion-composer 目录就是一个真实可运行的 Remotion 工程,负责将节目按场景类型渲染成最终视频。因此,"资产如何进入 Remotion、如何被引用"是每一个需要写 Remotion 代码的 Agent 都必须遵守的基础契约——这正是本规则存在的意义:凡是处理 Remotion 代码,都应加载本技能获取领域知识,而 assets 规则给出了资产引用部分的统一标准。

资产引用的三条原则速览

在深入细节前,先把规则的核心结论提炼如下,后文逐一展开:

  1. 静态资产统一放在项目根目录的 public/ 文件夹,任何 import 语句不应直接去 node_modules 或任意磁盘路径取资源;
  2. 引用 public/ 内的文件必须使用 staticFile() 包裹,它返回一个编码后的 URL,能在部署到子目录时依然正确解析;
  3. 远程 URL(http/https)可以直接传给组件,无需 staticFile() 处理。

public 文件夹:静态资产的唯一归宿

Remotion 约定,任何希望在运行时被组合引用的静态文件——Logo、照片、剪辑好的视频片段、背景音乐、字体文件——都应放置在项目根目录下的 public/ 文件夹中。

在 OpenMontage 的 remotion-composer 工程里,这个目录即 remotion-composer/public。例如当前仓库中该目录下存放了 demo-props/ 等演示数据;而实际流水线(pipeline)产出的媒体,则会被前序阶段**组装(staging)**进该目录,供 Remotion 渲染时通过固定前缀路径读取。这一点在测试注释中得到了印证:tests/tools/test_remotion_media_staging.py 的开头说明中写道,若某一步骤假设"assets 已在 remotion-composer/public/ 中"而实际未被前序阶段安置,将导致运行时 404。

为什么强调"项目根目录的 public/",而不是随便某个子目录?因为 Remotion 打包时以工程根目录为准来计算静态资源根路径;staticFile() 的参数一律是从 public/ 内部开始计量的相对路径。

用 staticFile() 引用 public 内的文件

规则文档强调了一条强制要求:必须使用 staticFile() 引用 public/ 文件夹中的文件,而不是手写相对路径或直接拼接字符串。

import { Img, staticFile } from "remotion";

export const MyComposition = () => {
  return <Img src={staticFile("logo.png")} />;
};

staticFile() 的核心价值在于:它返回一个经过正确编码的 URL,即使最终站点被部署到某个子路径下(而非域名根路径),资源也能被正确加载——这与 Web 开发中"不要写死绝对根路径"的原则一脉相承。

在 OpenMontage 的 remotion-composer 源码中,可以看到该函数被大量真实使用。例如 fixtures.ts 在定义组合演示数据时,为背景音乐、视频片段统一包裹 staticFile()

import { staticFile } from "remotion";

export const signalFromTomorrowWithMusicFixture = {
  soundtrack: {
    src: staticFile("music/signal-from-tomorrow/cinematic_time_hans_zimmer_style.mp3"),
    volume: 0.42,
    fadeInSeconds: 1.5,
    fadeOutSeconds: 2.5,
  },
  scenes: [
    {
      id: "sc1",
      kind: "video",
      startSeconds: 0,
      durationSeconds: 4,
      src: staticFile("video/signal-from-tomorrow/sample_observatory_veo31_ref.mp4"),
    },
    // ... 更多场景
  ],
};

可见约定路径形如 music/...mp3video/...mp4,均相对 remotion-composer/public/ 解析。

四类核心资产的组件接入模板

规则文档按媒体类型给出了可直接复制的模板,本文完整保留并稍作注释说明。

图片(Img)

import { Img, staticFile } from "remotion";

export const MyComposition = () => {
  return <Img src={staticFile("photo.png")} />;
};

图片是 Remotion 组合中最常见的静态资产。OpenMontage 中许多场景组件都用到了此模式,例如 ProductReveal.tsx 在展示产品图时,将 productImage 以 staticFile(productImage) 形式传入 <Img>。针对图片还有专门的规则文档 images.md 可进一步参考(GIF 则有 gifs.md)。

视频(Video)

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

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

说明:原规则示例从 @remotion/media 导入 Video 组件。在 OpenMontage 实际工程中,视频轨通常用 OffthreadVideo(从 remotion 导入)承担渲染,例如 TitledVideo.tsxsrc 传入统一解析函数的结果。视频相关更完整的玩法(裁剪、音量、倍速、循环、音调)参见 videos.md

音频(Audio)

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

export const MyComposition = () => {
  return <Audio src={staticFile("music.mp3")} />;
};

音乐与音效是 Remotion 组合的常客。需要更丰富处理(时长获取、裁剪、音量控制)时可查阅 audio.md@remotion/media-utilsgetVideoMetadata 等工具还能在渲染前异步读取媒体元数据——TitledVideo 的元数据计算就依赖它,见下文源码分析。

字体(FontFace)

import { staticFile } from "remotion";

const fontFamily = new FontFace("MyFont", `url(${staticFile("font.woff2")})`);
await fontFamily.load();
document.fonts.add(fontFamily);

字体的特殊之处在于它不是组件,而是需要先注册到浏览器的 FontFace 体系:用 staticFile() 拼出字体文件 URL,构造 FontFace,等待 load() 完成后通过 document.fonts.add() 注入,之后才能在样式中以 fontFamily 名称使用。OpenMontage 仓库 assets 目录(如 assets/patrickhand.ttf)存在真实字体文件,且 ink-theater 子项目使用同类 TTF 作为手写风字幕字体。关于字体的更多细节参见专门规则 fonts.md

远程 URL:免 staticFile 直接使用

当素材托管在远程服务器上时,src 可以直接使用完整 URL,不需要(也不应该)用 staticFile() 包裹:

<Img src="https://example.com/image.png" />
<Video src="https://remotion.media/video.mp4" />

远程 URL 透传逻辑在 OpenMontage 的源码中也有对应实现:下面的解析函数会先判断 URL 是否以 http://https://data: 开头,是则原样返回,不再做任何本地路径处理。

两个必须牢记的细节

规则文档以"Important notes"收尾,提出两条对渲染正确性至关重要的注意事项:

  1. Remotion 组件(<Img><Video><Audio>)会确保资产在渲染前被完全加载。这意味着你不要在组件外手动做加载竞态处理——组件体系已内置"先加载完再进入渲染帧"的语义,这也是为什么必须使用 Remotion 提供的组件而非裸 <img>/<video> 标签。
  2. 文件名中的特殊字符(如 #?&)会被自动编码。因此即便资源文件名含有上述字符,也只需在 staticFile() 中按原始文件名书写即可,URL 编码由框架自动完成,无需手工 encodeURIComponent

仓库源码印证:从规则到统一解析层

规则解决"怎么写得对",而 OpenMontage 工程中还沉淀了一层统一资源解析(resolveAsset),把本规则进一步扩展为对"public 相对路径 / 远程 URL / 磁盘绝对路径"三种输入的兼容处理。这段实现位于 remotion-composer/src/lib/resolveAsset.ts

import { staticFile } from "remotion";

const isRemoteAsset = (src: string): boolean =>
  src.startsWith("http://") ||
  src.startsWith("https://") ||
  src.startsWith("data:");

const isWindowsAbsolutePath = (src: string): boolean =>
  /^[A-Za-z]:[\\/]/.test(src);

/** Resolve public assets and absolute filesystem paths consistently. */
export function resolveAsset(src: string): string {
  if (isRemoteAsset(src)) {
    return src;                       // 1) 远程 URL 直接透传
  }

  const withoutScheme = src.replace(/^file:\/\//i, "");
  const clean = /^\/[A-Za-z]:[\\/]/.test(withoutScheme)
    ? withoutScheme.slice(1)
    : withoutScheme;

  if (clean.startsWith("/") || isWindowsAbsolutePath(clean)) {
    const posix = clean.replace(/\\/g, "/");
    return posix.startsWith("/") ? `file://${posix}` : `file:///${posix}`;
  }                                   // 2) 绝对路径 -> file:// 协议

  return staticFile(clean);           // 3) 其余一律走 public/ + staticFile()
}

逐行对照本规则:

  • 远程 URLresolveAssethttp://https://data: 前缀做白名单判定并原样返回——正是"远程 URL 可直接使用"这条规则的工程化实现;
  • public 相对路径:不以 / 开头、也不是 Windows 盘符绝对路径的输入,最终落入 staticFile(clean)——即"public 内的文件必须用 staticFile() 引用";
  • 绝对文件路径:额外把本地磁盘路径转换为 file:// 协议,这是对规则在"流水线产物与前端 public 资产混合场景"下的必要扩展,仍以兼容静态文件系统为底线。

resolveAsset 的实际消费点在 TitledVideo.tsx

<OffthreadVideo
  src={resolveAsset(videoSrc)}
  // ...
/>

同时其元数据计算函数 calculateTitledVideoMetadata 也通过 getVideoMetadata(resolveAsset(props.videoSrc)) 在渲染前获取视频时长,从而动态计算组合的帧数与 fps

export const calculateTitledVideoMetadata: CalculateMetadataFunction<
  TitledVideoProps
> = async ({ props }) => {
  const meta = await getVideoMetadata(resolveAsset(props.videoSrc));
  // ... 用 meta 计算 durationInFrames / fps 并返回
};

这展示了一个完整链路:资产引用 → 统一解析 → Remotion 组件消费 → 元数据驱动组合时长,与本规则文档的静态约定一一对应。

测试如何守护这条契约

仓库的契约测试同样约束了"资产必须可被 public 路径寻址"这一前提。在 tests/tools/test_remotion_media_staging.py 中,测试断言了诸如 anime_scene.images[]screenshot_scene.backgroundImage 这类字段会被当作 staticFile() 语义下的 public 资产来对待;文件头注释还点出:这些资产若依赖"前序流水线阶段已把媒体写入 remotion-composer/public/"而实际缺失,则渲染时必然 404。也就是说,资产放置(public staging)与资产引用(staticFile)是同一契约的两端,写 Remotion 代码的 Agent 需要同时对两端负责。

Agent 使用指引:何时读取这条规则

作为技能规则,assets.md 的触发时机非常明确:

编码助手(Claude Code、Cursor、Codex 等)会依据各自约定的技能目录(.agents/.claude/)加载同一份规则内容,从而保证不同 Agent 在生成 Remotion 代码时,对资产引用的写法保持一致:文件进 public/,引用走 staticFile(),远程素材直连 URL

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

项目优选

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