首页
/ OpenMontage Remotion 最佳实践:用 Mediabunny 精确读取音频时长(浏览器 / Node.js / Bun)

OpenMontage Remotion 最佳实践:用 Mediabunny 精确读取音频时长(浏览器 / Node.js / Bun)

2026-09-08 15:12:37作者:胡唯隽

本文是 OpenMontage 仓库内 Agent 技能集 remotion-best-practicesget-audio-duration 规则(原文档,并在 .claude 技能集中有一份同步副本)的深度展开。它讲解如何借助 Mediabunny 在任意运行环境里把一段 MP3/WAV 音频的时长精确地读取为秒数,并在 Remotion 合成中用 staticFile() 指向本地资源;文章同时结合本仓库 remotion-composer 的真实秒→帧换算与音频编排实践,说明拿到时长后如何驱动合成时长、音画对齐与自动校验。读完你将得到一份可直接复制进项目的音频测时工具函数,以及一套与 OpenMontage Remotion 渲染链路打通的使用方案。

Mediabunny 是什么,为什么用它测音频时长

Mediabunny 是一个跨运行时(browser、Node.js、Bun 均可用)的媒体探测与解析库,能够从音频文件容器中提取时长(duration)等元信息。它不需要依赖 FFmpeg 子进程即可工作,因此在 Remotion 的浏览器渲染环境、服务端渲染环境里表现一致。

在 OpenMontage 的 Remotion 工程中,Mediabunny 并不是需要单独手写的引入来源:remotion-composer/package.json@remotion/media^4.0.484)列为直接依赖,而在 remotion-composer/package-lock.json 中可以看到 mediabunny(解析版本 1.47.0)以及 @mediabunny/aac-encoder@mediabunny/mp3-encoder@mediabunny/flac-encoder 等编解码扩展被一并解析安装。也就是说,只要你的 Remotion 项目按 audio 规则 执行过:

npx remotion add @remotion/media

Mediabunny 的 InputUrlSourceFileSource 等 API 就已就绪,可直接 import 使用,无需额外声明新依赖。

最小可用示例:从 URL 读取音频时长

规则文档给出的核心函数是 getAudioDuration:构造一个 Input,声明接受所有媒体容器格式(ALL_FORMATS),数据源使用 UrlSource,然后调用 computeDuration() 获得以秒为单位的浮点时长。

import { Input, ALL_FORMATS, UrlSource } from "mediabunny";

export const getAudioDuration = async (src: string) => {
  const input = new Input({
    formats: ALL_FORMATS,
    source: new UrlSource(src, {
      getRetryDelay: () => null,
    }),
  });

  const durationInSeconds = await input.computeDuration();
  return durationInSeconds;
};

对这份代码的逐项说明:

  • formats: ALL_FORMATS:告知 Input 在探测时覆盖 Mediabunny 支持的全部容器/编码格式。规则文档的标签(mp3wav)表明常见的压缩与无损音频都在覆盖范围内;当你后续处理视频(同源 get-video-duration 规则)时,同样的配置也适用于含音轨的容器。
  • new UrlSource(src, { getRetryDelay: () => null })UrlSource 表示源是网络地址。回调 getRetryDelay 用于控制请求失败后的重试策略——这里始终返回 null,即不安排任何自动重试,让失败快速向上抛给调用方处理。
  • computeDuration():阻塞式完成容器解析并返回总时长(秒)。

调用与返回值

const duration = await getAudioDuration("https://remotion.media/audio.mp3");
console.log(duration); // e.g. 180.5 (seconds)

返回值是秒为单位的浮点数(示例中 180.5 表示 3 分 0.5 秒),这一点对后续秒→帧换算至关重要——不要把它当作毫秒或帧数。

在 Remotion 中指向 public/ 目录的资源:staticFile()

Remotion 约定将静态资源放在 public/ 目录,并在组件内通过 staticFile() 取得其编译期可知的 URL。测时长时若直接拼接 "audio.mp3" 这类相对路径,在 Studio 预览、打包渲染等不同环境下都可能解析失败,因此规则文档明确要求用 staticFile() 包一层:

import { staticFile } from "remotion";

const duration = await getAudioDuration(staticFile("audio.mp3"));

这与 Remotion 中加载音频资源的方式完全一致。以本仓库的音频层实现为例,OpenMontage 的渲染链路会把 narration、music 等素材按并行 <Audio> 轨组织(参见 skills/core/remotion.md 的 “Audio Layering” 一节),所有素材都通过 staticFile() 或已解析的绝对 URL 进入合成器;因此在测量“public 内本地音频”与“远端音频”时,唯一的差别就是这一层包装。

Node.js 与 Bun:改用 FileSource

浏览器环境无法随意读取用户本地磁盘路径,必须走 UrlSource(或用于上传场景的 BlobSource);而在 Node.js / Bun 这类具备完整文件系统能力的运行时里,规则文档推荐直接使用 FileSource

import { Input, ALL_FORMATS, FileSource } from "mediabunny";

const input = new Input({
  formats: ALL_FORMATS,
  source: new FileSource(file), // File object from input or drag-drop
});

const durationInSeconds = await input.computeDuration();

注意 FileSource 接收的是 File 对象(来自文件上传 <input>、拖拽等),并非裸路径字符串。若你希望在 Node 脚本中按磁盘路径读取,需要先经由对应的文件读取 API 产出 File/Blob 形态的对象再传入——这也保证了同一套 Input.computeDuration() 逻辑在三个运行时里行为一致。

三种数据源的选择时机

数据源 运行环境 适用场景
UrlSource 浏览器 / Node / Bun 远端 URL(CDN 音频、TTS 返回的临时链接)
FileSource Node / Bun / 浏览器 已上传或拖拽得到的 File 对象
BlobSource 浏览器为主 Blob 形态的临时音频数据(参考 can-decode 规则中的 BlobSource 用法)

同一 Input 只更换 source 实现,其余探测逻辑(formatscomputeDuration())完全复用,这正是把测时逻辑封装为独立工具函数、在合成前统一调度的价值所在。

拿到秒数之后:秒 → 帧换算与合成时长驱动

音频时长本身的最终用途通常是决定/校验 Remotion 合成的总时长。Remotion 的时间单位是帧,因此需要做 seconds × fps 换算。规则文档中的姊妹篇 calculate-metadata 给出了与 getVideoDuration 配合的经典写法(音频同理):

const calculateMetadata: CalculateMetadataFunction<Props> = async ({
  props,
}) => {
  const durationInSeconds = await getAudioDuration(props.audioSrc);

  return {
    durationInFrames: Math.ceil(durationInSeconds * 30),
  };
};

这里用 Math.ceil 向上取整,保证合成帧数至少覆盖音频全长,避免片尾被截断。与之对应,OpenMontage 的 remotion-composer 工程中到处可见这套“秒×fps→帧”的约定:CinematicRenderer.tsx 里场景排布使用 Math.round(scene.startSeconds * FPS)Math.round(scene.durationSeconds * FPS) 作为 <Sequence>fromdurationInFrames,并在 calculateCinematicMetadata 中以 Math.max(1, Math.ceil(totalSeconds * FPS)) 反推整片时长(见 CinematicRenderer.tsx)。可见“外部拿秒、内部换帧”是本仓库所有合成器的统一数据契约,getAudioDuration 的返回值可直接喂给这类元数据函数。

与 getVideoDuration 的区分

规则集中同时存在 get-video-duration 规则:两者的 API 形态完全一致,仅语义不同——视频时长含画面轨道与音轨的交叠总长,音频时长则是单条音轨的总长。当素材是“视频容器但只需取其配音时长”或“纯音频决定画面”时,按内容类型分别选用即可。

在 OpenMontage 渲染链路中的真实落地:音画对齐与自动校验

Mediabunny 的测时函数适合在合成器代码中动态计算;而在 OpenMontage 更高层的编排侧,音频时长还承担着“渲染前门禁校验”的职责。根据 skills/core/remotion.md 的说明:

  • TTS 旁白生成后,工具会返回 audio_duration_seconds;若旁白超过视频总时长,需要缩短脚本重新生成,或延长最后一个场景,否则会出现音画截断;
  • 背景音乐时长应 ≥ 视频时长(由 Remotion 的 fadeOutSeconds 负责收尾淡出),不足时开启 loop
  • 任何音频文件都可用 tools.analysis.audio_probe.probe_duration(path)(Python 侧实现)核对时长;
  • 渲染前的 composition_validator 会检查“旁白音轨长于视频”“音乐短于视频”等问题,result.data["valid"] 必须为 True 才允许进入渲染。

这给出了一条完整的技术分工:Python/编排侧用 ffprobe 类工具(audio_probe)做预渲染校验,Remotion/渲染侧用 Mediabunny 做动态元数据计算——两者互补,Mediabunny 方案的价值在于它不依赖外部二进制,在浏览器 Studio 预览与无头渲染两种场景下给出相同结果。当你需要把“探测、解码可用性、时长、分辨率”串成一套前端工具链时,可顺带参考同目录下的 can-decodeget-video-durationget-video-dimensions 规则,它们的 Input 构造骨架与本例高度一致。

使用注意事项小结

  1. 运行时差异只体现在 Source 类型上:浏览器/URL 用 UrlSource,Node/Bun 本地文件用 FileSource,上传场景用 BlobSourceInputformatscomputeDuration() 三处保持不变。
  2. 返回值是秒(浮点):进入 durationInFrames 前必须乘以 fps,并建议 Math.ceil 向上取整,避免音轨尾段超出合成范围。
  3. 不要用裸相对路径测 public/ 内资源:务必 staticFile("audio.mp3") 后再传入,保证 Studio 与渲染环境路径一致。
  4. 重试策略可关可开getRetryDelay: () => null 表示失败即抛;生产环境中若音频源位于不稳定网络,可改为返回递增延迟以换取鲁棒性,但需自行处理超时。
  5. 本规则属于技能路由体系remotion-best-practices 技能在 SKILL.md 中将本规则与 audio、get-video-duration、calculate-metadata 等并列编目。当 Agent 面对“音频时长/长度/时间(秒)”类诉求时,get-audio-duration(tags:duration、audio、length、time、seconds、mp3、wav)即是最佳匹配入口——这一定位正是本文件以规则卡(YAML frontmatter)形式存在于 Agent 技能仓库中的原因。
登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
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
899
5.82 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
920
1.85 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.8 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
532
596
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.02 K
521
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
392