把视频项目接入生产系统
万象渲染为不同视频代码项目提供统一的项目管理、鉴权和异步任务 API。可以从内置模板快速生成首个视频,也可以接入已有项目并继续二创;渲染运行时的部署、扩缩容与结果存储由平台处理。
支持的项目
平台按项目类型选择对应的构建与渲染适配器。两种项目都是上传构建产物创建版本, 渲染运行时的部署与扩缩容由平台处理。
| 项目类型 | 输入 | 接入方式 | 运行时 |
|---|---|---|---|
| hyperframes | 项目入口 + 渲染参数 | 项目构建产物 | 项目适配器 |
| remotion | compositionId + inputProps | 版本化 bundle | remotion-fc |
计费方式
托管云渲染以最终输出的视频分钟数为用量单位。月消费达到更高档位后,当月全部渲染分钟使用对应单价,不采用累进分段计算。以下价格作为方案参考,实际开放档位以合同为准。
| 档位 | 月消费区间 | 单价 | 适合场景 |
|---|---|---|---|
| 按量起步 | < ¥5,000 | ¥0.30 / 分钟 | 试用与小规模生产 |
| 稳定增长 | ¥5,000–< ¥20,000 | ¥0.25 / 分钟 | 稳定业务调用 |
| 规模生产 | ¥20,000–< ¥50,000 | ¥0.20 / 分钟 | 高频批量生成 |
| 大规模调用 | ≥ ¥50,000 | ¥0.15 / 分钟 | 大客户与 API 批量任务 |
系统架构
服务层统一处理 API Key、项目和版本解析、参数校验、租户配额与任务状态;运行时层根据项目类型执行实际渲染。
Agent / App
鉴权 / 项目 / 任务
按项目类型路由
并行执行
状态 / 视频
快速开始
- 1
创建项目与版本
选择 hyperframes,并上传对应的构建产物。每次发布生成不可变的项目版本。
- 2
创建 API Key
API Key 只应保存在服务端环境变量或密钥管理服务中,不要暴露在浏览器代码里。
- 3
提交渲染任务
指定项目、版本、渲染入口和业务参数。接口异步返回 renderTaskId。
- 4
查询或接收结果
通过 renderTaskId 查询进度,完成后获得最终视频 URL;生产场景可以配置 Webhook。
认证方式
本地交互使用设备授权;Agent、CI 和服务端集成使用账号级 API Key,无需登录或打开 MassRender 网页。API Key 继承账号所属租户和账号权限;模板创建、更新与发布仍需对应模板管理权限。
CLI 登录
Authorization: Bearer <token>适合开发者本机,massrender login 自动保存凭据。
Agent / CI
X-API-KEY: <key>推荐自动化使用,只保存在密钥管理服务或环境变量。
Web 控制台
同源 Session Cookie仅供浏览器控制台使用,不应复制到自动化脚本。
export MASSRENDER_API_URL=/api/v1
export MASSRENDER_API_KEY=***
npx @massrender/cli auth status --json
npx @massrender/cli whoami --json
npx @massrender/cli capabilities --json公开 API
外部服务使用控制台生成的账号级 API Key,通过X-API-KEY请求头访问。任务归属于该账号所在租户,创建任务会从该租户结算费用;请勿在浏览器前端暴露密钥。
POST/api/v1/render-tasks
curl -X POST /api/v1/render-tasks \
-H "X-API-KEY: $MASSRENDER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"projectVersionId": "pv_01J...",
"compositionId": "DataReport",
"inputProps": { "headline": "2026 年度增长", "metricValue": 1280 }
}'GET/api/v1/render-tasks/:renderTaskId
curl /api/v1/render-tasks/rt_01J... \
-H "X-API-KEY: $MASSRENDER_API_KEY"
{
"renderTaskId": "rt_01J...",
"status": "rendering",
"progress": { "percent": 72, "completedChunks": 13, "totalChunks": 18 },
"output": null
}OPENAPI 3.1 · 46 ENDPOINTS
展开端点可查看认证、参数、请求字段、响应 Schema 与示例。
RenderTasks
11get/render-tasks游标分页查询渲染任务
post/render-tasks创建渲染任务
get/render-tasks/stats任务队列月度聚合指标
post/render-tasks/batch批量并行创建渲染任务
post/render-tasks/batch/import上传 Excel 与图片/视频素材并创建批量渲染
get/render-tasks/batches查询当前租户的批量渲染批次
get/render-tasks/batches/{renderBatchId}查询批次逐行状态
get/render-tasks/{renderTaskId}查询渲染任务
post/render-tasks/{renderTaskId}/cancel请求取消渲染任务
get/render-tasks/{renderTaskId}/download刷新渲染产物下载链接(实时重签 OSS URL,返回 200 + JSON)
post/render-tasks/upload-signature获取 bundle 上传签名(CLI 直传 OSS 用)
FfmpegTasks
2post/ffmpeg-tasks创建通用 FFmpeg 任务
get/ffmpeg-tasks/{taskId}查询 FFmpeg 任务状态
RenderTemplates
6get/render-templates列出可渲染的模板目录
post/render-templates创建或更新当前租户的私有渲染模板
get/render-templates/{templateId}查询单个模板详情(匿名可读)
put/render-templates/{templateId}更新当前租户的私有模板配置
delete/render-templates/{templateId}删除当前租户的私有模板
post/render-templates/{templateId}/publish发布模板(draft → published)
RenderProjects
4get/render-projects列出当前租户的项目
post/render-projects创建渲染项目(CTR-006 幂等:按 slug 找或创建)
get/render-projects/{projectId}按 ID 查询项目
get/render-projects/by-slug/{slug}按 slug 查项目(CTR-006 §4.2)
PreviewSessions
3post/preview-sessions创建短期 HyperFrames 预览会话
put/preview-sessions/{sessionId}更新预览变量
delete/preview-sessions/{sessionId}销毁预览会话
Marketplace
3get/marketplace/templates浏览已发布市场模板
get/marketplace/templates/{slug}按 slug 查询市场模板详情
get/marketplace/templates/{templateId}/releases/{releaseId}查询一个公开的不可变 Release
Auth
4post/auth/deviceCLI 设备授权 — 请求 device code
get/auth/device/statusCLI 轮询设备授权状态
post/auth/device/confirm用户在浏览器确认 CLI 授权
post/auth/device/logoutCLI 登出(注销 session)
Account
1get/account返回当前用户、租户与认证类型
Billing
4get/account/balance查询预付费余额与累计渲染消费
get/usage/summary查询本月、上月和近 30 天用量汇总
get/usage/daily查询最多 90 天的逐日用量
get/pricing查询当前租户生效的渲染价格
Capabilities
1get/capabilities发现服务端引擎、质量档位和自动化能力
TempAssets
7get/temp-assets列出当前租户未过期临时素材 + 配额
post/temp-assets上传临时素材(RFC 0004)
post/temp-assets/upload-token获取临时素材直传 OSS 凭证(大文件直传)
get/temp-assets/quota租户临时素材配额快照
post/temp-assets/{assetId}/complete完成临时素材直传注册
get/temp-assets/{assetId}查询单素材元数据(租户隔离,跨租户 404)
delete/temp-assets/{assetId}删除素材并释放配额(租户隔离,跨租户 404)
CLI 快速开始
万象渲染 CLI 让你从终端管理模板、上传 bundle 和发起渲染,自动处理鉴权和轮询。
0. 安装 CLI(首次使用)
推荐:直接用 npx,免安装免 PATH
npx @massrender/cli或全局安装:npm install -g @massrender/cli,安装后即可使用 massrender 命令 (当前版本 v0.2.2,要求 Node.js ≥ 22)。
1. 登录
npx @massrender/cli login # 打开浏览器完成授权2. 查看可用模板
npx @massrender/cli template list3. Agent 创建并等待任务
npx @massrender/cli render create \
--template data-report \
--props '{"headline":"2026 年度增长","metricValue":1280}' --json
npx @massrender/cli render wait <renderTaskId> --output=jsonl
npx @massrender/cli render download <renderTaskId> --file result.mp4 --jsonCLI 命令参考
下列资源命令都支持--output text|json|jsonl和 --json。
身份与能力
massrender auth status- 验证凭据,并返回认证来源和 API 地址
massrender whoami- 返回当前用户、租户与认证类型
massrender capabilities- 返回引擎、质量档位、功能和输出格式
模板与项目
massrender template list- 列出当前凭据可用的模板
massrender template get <id-or-key>- 按模板 ID 或 key 查询详情
massrender template schema <id-or-key>- 输出 inputProps 的 JSON Schema
massrender project list- 列出当前租户的渲染项目
massrender project get <id-or-slug>- 查询项目、composition 和最新可用版本
massrender project upload [dir]- 上传并验证项目,可选注册模板
渲染任务
massrender render create- 创建任务后立即返回,适合 Agent 编排
massrender render list- 按状态、项目和时间范围分页查询
massrender render get <id>- 获取完整任务快照
massrender render wait <id>- 轮询至完成、失败、取消或超时
massrender render download <id>- 刷新下载 URL,或保存到本地文件
massrender render cancel <id>- 请求取消任务(尽力而为,不保证一定停下)
massrender render run- 创建并等待完成的一体化交互命令
金额与用量
massrender billing balance- 查询预付费余额与累计渲染消费
massrender billing usage- 查询月度汇总;--daily 查询最多 90 天逐日明细
massrender billing pricing- 查询当前质量档位和每分钟价格
# 余额、价格与最多 90 天逐日用量
massrender billing balance --json
massrender billing pricing --json
massrender billing usage --daily --from 2026-07-01 --to 2026-07-30 --json
# 筛选任务;from/to 必须同时提供且范围不超过一个月
massrender render list --status completed --limit 50 --json
massrender render list --cursor <nextCursor> --json
# 每次创建都会新建一个渲染任务(后扣费,提交不冻结资金)
massrender render create --template data-report --json机器输出与错误处理
JSON 模式固定返回成功或失败信封。Agent 应先检查进程退出码,再读取ok和 error.retryable,不要解析人类文本。
成功
{
"ok": true,
"data": { "renderTaskId": "rt_01J...", "status": "queued" },
"meta": { "requestId": "...", "timestamp": "..." }
}失败
{
"ok": false,
"error": {
"code": "RATE_LIMITED",
"message": "请求过于频繁",
"retryable": true
},
"meta": { "requestId": "...", "timestamp": "..." }
}| 退出码 | 含义 | 建议 |
|---|---|---|
| 0 | 命令成功 | 继续后续步骤 |
| 1 | CLI 本地错误 | 检查参数、文件和本地环境 |
| 2 | API 拒绝且不可重试 | 修正参数、状态或权限 |
| 3 | 未认证或无权限 | 更新 API Key / Token |
| 4 | 可重试错误或等待超时 | 指数退避后重试 |
| 5 | 渲染失败或取消 | 读取任务 error 后决定是否新建任务 |
任务生命周期
任务失败时返回 failed 和结构化错误信息。客户端应使用指数退避轮询,并以 renderTaskId 作为幂等与问题排查标识。
模板参数不生效排查(HyperFrames)
编写 HyperFrames 模板 .html 时,如果模板里的 JS 读参数用错了 window 属性名, 会导致「预览里改参数生效、走正式渲染任务出来的视频没生效」的分裂表现。这一节说明成因和唯一需要的一行代码修复。
window.__hyperframes?.getVariables?.() || window.__hfVariables || window.__HV_VARS__ || {}症状:怎么判断踩到了这个坑
- 在 studio 预览 / 本地 preview 里改参数是生效的;
- 但走正式
/render-tasks出来的视频里,文本参数(title / subtitle / 文案 等)没变,还是默认值; - 有些颜色参数(accent、bg 等)却生效了——因为它们同时通过 CSS
var(--xxx)走了另一条通道; - 后端 API 请求体里
inputProps的值是对的(浏览器网络面板里能看到改后的值)。
原因:预览和正式渲染注入的名字不一样
@hyperframes/producer 在正式渲染时,会同时做两件事把 inputProps 注入到页面:
| 通道 | 面向谁 | 注入形式 |
|---|---|---|
| ① 编译期 | CSS var(--xxx) | <style data-hf-composition-variables>:root { --accent: <值>; ... }</style> |
| ② 运行时 | JS 代码 | window.__hfVariables = {...}(通过 evaluateOnNewDocument) |
而 studio / 本地预览 / ff-server 预览接口为了兼容历史模板,另外挂了两个别名。四个环境注入的 window 属性对照如下:
| 环境 | __hyperframes | __hfVariables | __HV_VARS__ |
|---|---|---|---|
| HyperFramesStory 本地预览 | ✓ | ✗ | ✓ |
| MassRender studio 预览 | ✓ | ✗ | ✓ |
| ff-server 预览接口 | ✗ | ✓ | ✓ |
| 正式渲染(producer) | ✗ | ✓ | ✗ |
如果模板脚本里只读 window.__HV_VARS__(很多老 demo 就是这样写的),正式渲染时 variables 就是空 {},所有 JS 里读的字段就都回退到默认值。 颜色能生效纯属侥幸——它同时借道通道 ① 的 CSS 自定义属性被注入了一遍。
解决方案:一行改动,三段兜底
❌ 错误写法(会踩坑)
// 只读一个别名,正式渲染下拿不到值
const variables = window.__HV_VARS__ || {};
// 更糟:不做保护,__hyperframes 不存在时会 TypeError
const variables = window.__hyperframes.getVariables();✅ 正确写法(推荐所有模板都用这一段)
// 变量注入通道优先级:
// 1. window.__hyperframes.getVariables() —— @hyperframes/producer runtime 官方 API
// (sub-composition 作用域 / 未来 runtime 版本)
// 2. window.__hfVariables —— 当前生产渲染管线 (evaluateOnNewDocument) 注入的名字
// 3. window.__HV_VARS__ —— HyperFramesStory 预览 shim / MassRender 预览注入的名字
const variables =
window.__hyperframes?.getVariables?.() ||
window.__hfVariables ||
window.__HV_VARS__ ||
{};
// 后面照常用 variables.title / variables.subtitle / variables.accent ...完整最小示例
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta data-composition-id="my-card" data-width="640" data-height="360">
<style>
body { margin: 0; background: #07111f; color: #fff; font-family: system-ui, sans-serif; }
#stage { width: 640px; height: 360px; display: grid; place-content: center; text-align: center; }
#badge { color: var(--accent); font-weight: 700; letter-spacing: .2em; }
h1 { color: var(--accent); margin: 0; font-size: 48px; }
p { color: #bfd7ea; }
</style>
</head>
<body>
<main id="stage"
data-composition-id="my-card"
data-duration="3"
data-composition-variables='[
{"id":"title","type":"string","label":"标题","default":"Hello"},
{"id":"subtitle","type":"string","label":"副标题","default":"World"},
{"id":"accent","type":"color","label":"强调色","default":"#38bdf8"}
]'>
<div>
<div id="badge">BRAND</div>
<h1 id="title">Hello</h1>
<p id="subtitle">World</p>
</div>
</main>
<script>
// ✅ 三段兜底:预览、正式渲染、未来 runtime 都能拿到值
const variables =
window.__hyperframes?.getVariables?.() ||
window.__hfVariables ||
window.__HV_VARS__ ||
{};
// CSS 变量:producer 在 <head> 里已经注入 :root { --accent: <值> },
// 这一行是给预览环境或旧运行时的兜底,保持写法一致。
document.documentElement.style.setProperty('--accent', variables.accent || '#38bdf8');
document.getElementById('title').textContent = variables.title || 'Hello';
document.getElementById('subtitle').textContent = variables.subtitle || 'World';
</script>
</body>
</html>发布前自检清单
<script>里读变量那一行,是否用了推荐的三段兜底?- 除了 CSS
var(--xxx),所有 JS 读的字段都通过variables.<id>取值,且给了|| '默认值'兜底? - 至少跑过一次正式 render(不是预览),确认 title / subtitle / 数组 等纯 JS 驱动的字段确实生效?
只在预览里跑不能证明模板参数在生产是好的——预览和生产的注入名字不一样。
通用 FFmpeg 执行器
除了模板渲染,万象渲染还提供通用的 FFmpeg 执行能力:你只传「外部 URL 输入」+ 「命令数组」+「输出文件名」,服务端把输入取到受控存储后在云函数上执行 ffmpeg。 适用于转码、加水印、截图、媒体信息查询等任意 ffmpeg 任务,无需预先制作模板。
安全模型
- • ffmpeg 进程永不直连网络,输入由服务端 fetch 到受控存储
- • 输入 URL 必须为公网 http(s);私网和元数据地址会被拒绝
- • 命令参数经从严白名单校验(option/filter/值域默认拒绝)
- •
-i只能引用已声明的输入文件名
异步与会员体系
- • 创建返回 202 + taskId,执行在云函数上异步进行
- • 用 GET 轮询拿终态;
callbackUrl回调不保证送达,不应作为唯一判定来源 - • 不依赖会员订阅,不因套餐过期或调用次数上限拦截
- • 计费机制以销售合同为准,本文档不承诺具体结算方式
FFmpeg 命令
FFmpeg 子命令在 massrender ffmpeg 命名空间下, 与渲染命令同样支持 --json 机器输出。
FFmpeg 任务
massrender ffmpeg create- 创建通用 FFmpeg 任务(异步提交后立即返回 taskId)
massrender ffmpeg get <taskId>- 查询任务状态、产物下载 URL 或 ffprobe 结果
massrender ffmpeg wait <taskId>- 轮询至完成或失败(--interval / --timeout 控制节奏)
ffmpeg create 参数
| 参数 | 取值 | 说明 |
|---|---|---|
--mode | ffmpeg | ffprobe | 执行模式;ffmpeg=转码/处理,ffprobe=媒体信息查询。默认 ffmpeg。 |
--profile | standard | highmem | gpu | 规格档位。目前只开放 standard(2 vCPU / 3GB / 600 秒),highmem 与 gpu 尚未开通,传这两个值会被拒绝。 |
--input | "name=url" | 输入文件声明,可重复;URL 必须为 http(s)。如 --input "main.mp4=https://cdn/a.mp4"。 |
--args | shell 字符串 | ffmpeg/ffprobe 参数(支持引号);-i 只能引用 --input 声明的文件名。 |
--output-file | 文件名 | 输出文件名(mode=ffmpeg 必填,且 --args 最后一个参数必须指向它)。 |
--callback-url | URL | 可选;任务进入终态时 POST 到该 URL 通知。 |
# 转码:1080p → 720p h264
massrender ffmpeg create \
--input "main.mp4=https://cdn.example.com/input.mp4" \
--args "-i main.mp4 -vf scale=-2:720 -c:a aac out.mp4" \
--output-file out.mp4
# 多输入加水印(--args 支持引号)
massrender ffmpeg create \
--input "main.mp4=https://cdn.example.com/input.mp4" \
--input "logo.png=https://cdn.example.com/logo.png" \
--args "-i main.mp4 -i logo.png -filter_complex [0:v][1:v]overlay=W-w-10:10 -c:a copy out.mp4" \
--output-file out.mp4
# ffprobe 查询媒体信息(无需 --output-file)
massrender ffmpeg create --mode ffprobe \
--input "main.mp4=https://cdn.example.com/input.mp4" \
--args "-v error -print_format json -show_format -show_streams -i main.mp4"
# 轮询至终态并以 JSON 输出
massrender ffmpeg wait <ffmpeg-id> --jsonFFmpeg RESTful API
外部服务通过 X-API-KEY或 Bearer Token 访问,认证方式与渲染任务一致。成功创建返回 202,任务异步执行; 用 GET 轮询状态。callbackUrl 回调不重试、不保证送达, 客户端应以 GET 轮询结果为准,不要只依赖回调。
POST/api/v1/ffmpeg-tasks
# ffmpeg 模式:转码
curl -X POST /api/v1/ffmpeg-tasks \
-H "X-API-KEY: $MASSRENDER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"inputs": {"main.mp4": "https://cdn.example.com/input.mp4"},
"commands": [["-i", "main.mp4", "-c:v", "libx264", "-pix_fmt", "yuv420p", "out.mp4"]],
"output": {"fileName": "out.mp4"},
"mode": "ffmpeg",
"profile": "standard"
}'
# → 202 Accepted
{"taskId": "ffmpeg-3f0e6a1f-...","status": "queued", "error": null}# ffprobe 模式:媒体信息查询(无 output 字段)
curl -X POST /api/v1/ffmpeg-tasks \
-H "X-API-KEY: $MASSRENDER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"inputs": {"main.mp4": "https://cdn.example.com/input.mp4"},
"commands": [["-v","error","-print_format","json","-show_format","-show_streams","-i","main.mp4"]],
"mode": "ffprobe",
"profile": "standard"
}'GET/api/v1/ffmpeg-tasks/:taskId
curl /api/v1/ffmpeg-tasks/ffmpeg-3f0e6a1f-... \
-H "X-API-KEY: $MASSRENDER_API_KEY"
{
"taskId": "ffmpeg-3f0e6a1f-...",
"status": "completed",
"finalHttpUrl": "https://signed.example.com/.../out.mp4",
"output": {
"sizeInBytes": 60098, "width": 1280, "height": 720,
"codec": "h264", "durationSeconds": 3.0
},
"probeResult": null,
"error": null, "errorCode": null
}错误码
| HTTP | code | 含义 |
|---|---|---|
| 400 | FFMPEG_INPUT_URL_FORBIDDEN | 输入 URL 协议非 http(s),或指向私网/元数据/localhost 地址 |
| 400 | FFMPEG_COMMAND_URL_FORBIDDEN | commands 里嵌入了网络 URL/协议(-i 只能引用已声明的输入文件名) |
| 400 | FFMPEG_VALIDATION_ERROR | 请求体或命令校验失败(option/filter 白名单、值域等) |
| 404 | FFMPEG_TASK_NOT_FOUND | 任务不存在,或归属校验不通过(任务归属另一租户) |
| 503 | RENDER_SERVICE_UNAVAILABLE | 渲染服务暂时不可用,可重试 |
任务进入 failed 终态时,errorCode字段携带任务级原因(FFMPEG_TIMEOUT、FFMPEG_INPUT_FETCH_FAILED、FFMPEG_EXEC_FAILED 等)。 下方结构化参考由 OpenAPI 规范自动生成。
临时素材(素材库)
除了「外部 URL」输入,万象渲染还提供临时素材能力: 客户先把生产素材(视频、图片等)上传,服务端返回一个内网oss://链接。该链接只能回传给 ffmpeg 任务的 inputs 或渲染任务的inputProps 媒体变量作为消费凭证, 不能当作 HTTP URL 在浏览器直接打开。
生命周期与配额
- • 素材存活默认 24h(TTL),过期后自动清理,不计入配额
- • 配额按租户计,默认 10G;上传超过配额返回 409
- • 单文件默认上限 2G,超过返回 413
- • 删除素材立即释放配额
安全模型
- • 素材按租户隔离,跨租户读取或删除一律返回 404
- • 文件名经服务端 sanitize:路径分隔符、控制字符、保留控制名会被拒绝
- •
oss://链接为内网消费凭证,bucket 私有,浏览器不可访问 - • 上传并发按租户限流,防止单租户占满 worker
素材 RESTful API
通过 X-API-KEY 或 Bearer Token 访问, 认证方式与渲染任务一致。租户由登录态自动注入,客户端无需也无法指定租户。
POST/api/v1/temp-assets
# 上传原始字节(raw body,不要 JSON 包装;文件名建议 percent-encode)
curl -X POST /api/v1/temp-assets \
-H "X-API-KEY: $MASSRENDER_API_KEY" \
-H "X-File-Name: hero.mp4" \
-H "Content-Type: video/mp4" \
--data-binary @hero.mp4
# → 201 Created
{
"assetId": "ta_0123456789abcdef0123456789abcdef",
"fileName": "hero.mp4",
"sizeBytes": 1048576,
"contentType": "video/mp4",
"sha256": "a".repeat(64),
"url": "oss://framefuture-video/remotion-fc-renders/temp-assets/tenant-xxx/ta_0123.../hero.mp4",
"expiresAt": "2026-08-07T08:00:00.000Z",
"quota": {"usedBytes": 1048576, "quotaBytes": 10737418240, "remainingBytes": 10736369664, "assetCount": 1}
}GET/api/v1/temp-assets · /api/v1/temp-assets/quota
# 列出未过期素材 + 配额
curl /api/v1/temp-assets -H "X-API-KEY: $MASSRENDER_API_KEY"
# 只取配额快照
curl /api/v1/temp-assets/quota -H "X-API-KEY: $MASSRENDER_API_KEY"GET/DELETE/api/v1/temp-assets/:assetId
# 单素材元数据
curl /api/v1/temp-assets/ta_0123456789abcdef0123456789abcdef -H "X-API-KEY: $MASSRENDER_API_KEY"
# 删除素材并释放配额
curl -X DELETE /api/v1/temp-assets/ta_0123456789abcdef0123456789abcdef \
-H "X-API-KEY: $MASSRENDER_API_KEY"
# → 200 {"deleted": true, "tenantRef": "tenant-xxx", "quota": {...}}消费方式
上传返回的 oss:// 链接可回传给:
# FFmpeg 任务:把素材作为输入(inputs 值直接用 oss:// 链接)
curl -X POST /api/v1/ffmpeg-tasks \
-H "X-API-KEY: $MASSRENDER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"inputs": {"main.mp4": "oss://framefuture-video/remotion-fc-renders/temp-assets/tenant-xxx/ta_0123.../hero.mp4"},
"commands": [["-i", "main.mp4", "-c:v", "libx264", "-pix_fmt", "yuv420p", "out.mp4"]],
"output": {"fileName": "out.mp4"},
"mode": "ffmpeg", "profile": "standard"
}'错误码
| HTTP | code | 含义 |
|---|---|---|
| 400 | VALIDATION_ERROR | 缺/非法租户头、非法文件名、空 body |
| 409 | TEMP_ASSET_QUOTA_EXCEEDED | 租户配额超限(默认 10G) |
| 413 | TEMP_ASSET_FILE_TOO_LARGE | 单文件超过上限(默认 2G) |
| 404 | TEMP_ASSET_NOT_FOUND | 素材不存在,或不属于当前租户 |
| 429 | TEMP_ASSET_UPLOAD_BUSY | 租户并发上传超过上限,稍后重试 |
| 502 | TEMP_ASSET_UPSTREAM_ERROR | ff-server 无法到达渲染服务(网络层失败),可重试 |
| 503 | RENDER_SERVICE_UNAVAILABLE | 渲染服务暂时不可用,可重试 |
下方结构化参考由 OpenAPI 规范自动生成。
prepare 阶段
很多视频在正式渲染前需要先做一步准备:抽音轨、做语音识别、按文稿打轴、算出字幕分页。 万象渲染把这步交给你自己的代码——项目里声明一个 prepare 入口, 平台在渲染前执行它,把它返回的 props 作为这次渲染的inputProps。平台在这个过程中提供 ffmpeg、 ASR、存储和受限 HTTP 四类调用,怎么组合、分页规则是什么,都由你的代码决定。
任务状态
- • 提交后平台先跑 prepare,这段时间任务对外是
queued,没有分片进度 - • prepare 跑完才进入渲染,之后的状态和进度与不含 prepare 的任务一致
- • prepare 期间不占用渲染并发,只占队列位置
- • 终态时
usage.prepare给出这次 prepare 的耗时、ffmpeg/ASR 调用次数、是否命中缓存和 props 体积 - • 不含 prepare 的项目行为不变
运行约束
- • 入口是随制品发布的 ESM 文件,不超过 2 MiB;制品里没有 node_modules,依赖要自己打包进文件
- • 单次预算 900 秒;超时或任务取消时
ctx.signal中止,进行中的调用一并停止 - • 平台凭证不会写进你的进程环境,能力调用由平台代为执行;但这层隔离挡不住恶意代码,不要执行来源不可信的 prepare 代码
- • prepare 按租户开通,未开通时提交直接返回 403
PREPARE_NOT_ENABLED
声明方式随项目类型不同:Remotion 项目写在 package.json 的massrender.prepare 里;HyperFrames 项目写在项目清单的entries[].prepare 里。
// package.json(Remotion)
{
"massrender": {
"prepare": "prepare/main.mjs",
"allowedHosts": ["api.example.com"]
}
}
// prepare/main.mjs —— 最小骨架
export default async (ctx) => {
const {video, script, language} = ctx.input;
// ① 抽音轨
const audio = await ctx.ffmpeg.run({
inputs: {src: video.url},
commands: [['-i', 'src', '-vn', '-c:a', 'aac', '-ar', '16000', '-ac', '1', 'narration.m4a']],
output: {fileName: 'narration.m4a'},
});
// ② 有稿子打轴,没稿子纯识别
const asr = script
? await ctx.asr.align({audio: audio.output.url, text: script, language})
: await ctx.asr.transcribe({audio: audio.output.url, language});
// ③ 返回的 props 直接成为渲染 inputProps
return {
props: {
video,
voiceover: audio.output,
pages: [{
text: asr.utterances.map((u) => u.text).join(''),
start: 0,
end: asr.audioSeconds ?? 0,
}],
},
usage: {audioSeconds: asr.audioSeconds},
};
};能力清单
四类调用的凭据都在平台侧,你的代码只拿到调用入口。每次调用都计入usage,费用以平台侧计数为准。
| 能力 | 作用 | 要点 |
|---|---|---|
ctx.input | 本次请求的 inputProps | 另有 ctx.entryId / ctx.renderId / ctx.tenantRef |
ctx.ffmpeg.runctx.ffmpeg.probe | 执行 ffmpeg 命令 / 只取媒体信息 | 输入为逻辑名 → URL,参数走服务端白名单;probe 不产出 URL |
ctx.asr.alignctx.asr.transcribe | 按文稿对齐 / 纯识别,返回词级时间戳 | 需要提供方可访问的公网音频地址,不建议直接传视频地址 |
ctx.storage.putJsonctx.storage.getJson | 按租户隔离的 JSON 键值存储,用来跨次运行保留昂贵中间结果(打轴、转写等) | 仅 JSON、单对象 ≤ 8 MiB、键为相对路径;只能读写本租户的数据,没有列举/删除接口,也不会自动过期——同一 key 重复写即覆盖 |
ctx.http.fetch | GET 外部接口 | 域名必须在声明里白名单化,未声明则完全禁用 |
ctx.logctx.signal | 写日志 / 感知取消与超时 | 长任务应监听 signal 提前收尾,避免被强制终止 |
putJson(key, value),读回getJson(key) 得原值、没有则null。数据按租户隔离,其他租户读不到;单次运行最多 4 个并发调用排队,超出即等待,等待可被ctx.signal 中止。视频、音频这类大文件请走 ffmpeg 输出或素材库,storage 不支持。output.url形态由引擎决定——Remotion 为有效期 7 天的预签名 HTTPS,HyperFrames 为默认 24 小时有效的租户临时素材oss://。写进 props 的应当是url 而不是objectKey,渲染阶段才能直接消费。返回值与缓存
return {
props, // 必填:这次渲染的 inputProps
cacheable: false, // 可选:是否允许平台缓存本次结果
usage: {audioSeconds: 42.5}, // 可选:仅为提示,计费以平台计数为准
};- • props 必须满足 entry 的 inputSchema。该 schema 由项目的默认参数反推,且不允许额外字段。prepare 多返回一个 key 就会判为
PREPARE_OUTPUT_INVALID,所以默认参数要覆盖 prepare 会返回的全部顶层字段(例如字幕组件返回voiceover,默认参数里就要有这个 key)。 - • 默认不缓存。设
cacheable: true才按「制品 + prepare 路径 + entry + 输入」缓存 30 天; 但产出过output.url的那次运行永不缓存(URL 有效期远短于缓存)。 要复用昂贵计算,用ctx.storage。 - • ASR 结果平台另有缓存(默认 30 天),命中时不再向识别服务提交,但仍按命中时长计费—— 缓存省的是延迟和上游配额,不是这笔费用。
命中条件很严格:缓存键包含音频地址本身。每次用ctx.ffmpeg.run重新抽音轨都会得到新地址, 即使音频内容相同也不会命中。要复用结果,二选一:把音频地址固定下来 (每轮都指向同一个稳定地址,而不是重新抽),或用ctx.storage自己按内容缓存。 - • props 超过 1 MiB 时平台会把它存到对象存储,任务详情里只保留引用。
错误码与排查
prepare 阶段的失败会让任务以 failed 结束,回调render.failed 的error.stage 为prepare,error.code 是下表之一。查询任务时读error.code 判断原因。
| 错误码 | 含义 | 处理 |
|---|---|---|
PREPARE_NOT_ENABLED | 租户未开通 prepare,提交即被拒绝 | 联系平台开通;或提交一个未声明 prepare 的项目版本(平台不会跳过 prepare 继续渲染) |
PREPARE_INPUT_INVALID | 传给平台的参数不合法:ffmpeg 的输入名/地址/输出名、ASR 缺音频或文稿、storage 的键,或 http.fetch 的请求本身 | 对照「能力清单」里对应那一行的要求逐项检查。域名不在白名单报的是 MEDIA_UNREACHABLE,不是这个码 |
PREPARE_ENTRY_NOT_FOUND | 声明了 prepare,但制品里没有这个文件 | 确认构建把入口打进了发布产物 |
PREPARE_CODE_FAILED | 代码抛错或没有返回结果 | 看任务日志里 ctx.log 的输出和错误尾部 |
PREPARE_OUTPUT_INVALID | props 不满足 entry 的 inputSchema | 补齐默认参数里的字段,去掉 schema 之外的多余 key |
PREPARE_LIMIT_EXCEEDED | 超出内存、体积或并发类上限,不可重试 | 减少一次性载入的数据量,分次写入 |
PREPARE_TIMEOUT | 900 秒预算耗尽,或任务被取消 | 减少 ffmpeg/ASR 调用次数;ASR 结果会缓存,重跑通常更快 |
PREPARE_SANDBOX_UNAVAILABLE | 执行环境异常,非你的代码问题 | 可重试;持续出现请联系平台 |
FFMPEG_TASK_FAILED | ffmpeg 执行失败(含输入地址返回 4xx) | 确认输入地址可被服务端访问、命令参数在白名单内 |
MEDIA_UNREACHABLE | 输入不可达,或域名不在白名单 | 改用公网可达地址;把域名加进 allowedHosts |
ASR_PROVIDER_FAILED | 语音识别上游失败 | 可重试;持续失败请联系平台 |
一个容易误判的场景:用命令行工具生成预签名地址做调试,如果工具把额外的计时行混进了输出,或把路径里的/ 编码成%2F,对象存储会返回 403SignatureDoesNotMatch,但在任务里看到的却是FFMPEG_TASK_FAILED: input URL returned HTTP 403。 判断方法:同一串地址用 curl 能取到、换另一个 HTTP 客户端却 403,问题在地址本身,不在权限。
FC 实现
使用阿里云函数计算 custom-container 运行时。编排器读取 composition metadata,并根据帧数动态拆分 chunk;多个 Worker 分片渲染,Combiner 使用 FFmpeg 合并最终 MP4。
无状态执行
bundle、job.json、chunk 和最终视频都保存在 OSS,函数实例可以弹性创建和回收。
动态并发
并发来自多个 FC Worker invocation;单个 Worker 内部保持顺序渲染,减少资源竞争。
浏览器复用
温实例复用 Chromium 进程,只为每次渲染创建页面,降低重复启动浏览器的固定开销。
可重入分片
稳定对象键和完成状态检查支持分片重试,Combiner 会根据 OSS 对象校正部分状态。
企业级 FC 自部署
对于要求数据不离开企业云账号、需要连接内网素材或希望独立管理资源成本的客户,万象渲染 可以协助将 Remotion FC 渲染链路部署到客户自己的阿里云环境。
部署范围
FC custom-container、ACR 镜像、OSS 路径、VPC 网络、API 接入、日志与基础监控。
交付服务
环境评估、容量规划、部署验证、使用培训、版本升级和约定周期的技术支持。
价格:商务报价
通常由一次性实施费用与年度支持服务组成,阿里云资源费由企业自行承担。
生产接入建议
- 业务后端负责保存 API Key,前端不直接调用渲染接口。
- 为每个项目使用不可变版本,发布新版本时保留回滚能力。
- 对提交接口设置租户配额、并发限制和输入参数校验。
- Webhook 接收方校验签名,并按 renderTaskId 实现幂等处理。
- 长耗时任务使用异步状态,不要保持同步 HTTP 连接等待视频完成。
准备接入你的第一个项目?
联系我们评估项目类型、运行依赖、并发目标和结果交付方式。