在 Cloudflare Workers 上用 ffmpeg:调用它,而不是打包它。
Workers 跑在 V8 isolate 里——没有文件系统,不能执行原生二进制,CPU 时间还有硬性上限。你的 Worker 通过 HTTPS 把一个 URL 交给 Fotovid,编码在 isolate 之外完成,结果以托管链接返回,全程不占用你的 CPU 配额。
export default {
async fetch(req: Request, env: { FOTOVID_API_KEY: string }): Promise<Response> {
const { videoUrl, logoUrl } = await req.json();
const res = await fetch("https://api.fotovid.co/v1/video/watermark", {
method: "POST",
headers: {
Authorization: `Bearer ${env.FOTOVID_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
source_url: videoUrl,
params: { type: "image", watermark_image_url: logoUrl, position: "bottom-right" },
}),
});
const { url } = await res.json();
return Response.json({ url });
},
};为什么 ffmpeg 在你的 Worker 里跑不起来
Cloudflare Workers 不跑在容器里,而是跑在 V8 isolate(隔离环境)中。这里没有文件系统可以写临时文件,没法调起子进程去执行原生二进制,每个请求的 CPU 时间还有严格上限。ffmpeg 二进制在这种环境里根本无处安放。WASM 版本理论上能加载,但会被 isolate 的内存和 CPU 配额卡住,只要超出玩具级的短片段就直接挂掉。托管 API 是唯一现实可行的路。
- 没有文件系统:ffmpeg 读输入、写输出都要靠临时文件,而 Workers 一个都不给。
- 不支持原生二进制:在 V8 isolate 里,你既装不了也执行不了 ffmpeg 二进制。
- WASM 版 ffmpeg 能加载,但受限于 isolate 的内存和 CPU 上限——碰到真实视频不是卡住就是超时。
- 每个请求的 CPU 配额卡得极死,凡是转码量级的任务从一开始就不可能跑通。
团队通常怎么在 Cloudflare Workers 上跑 ffmpeg
在 Fotovid 出现之前,你只有这几个选择——每一个都是你并不想扛的活。
| Option | What it costs you | Verdict |
|---|---|---|
| 把 ffmpeg 打包进你的 Cloudflare Workers 函数 | Workers 根本不能运行原生二进制 | |
| 自己托管容器或虚拟机 | 常驻开销,外加打补丁、扩容和监控 | |
| 搭一套任务队列 + 工作进程 | 多出一整套基础设施:队列、工作进程、重试、死信处理 | |
| 在浏览器里转码(WASM) | 慢、吃内存,在手机上还会直接崩溃 | |
| 调用 Fotovid API | 一个 HTTPS 请求;没有任何东西要运行、打包或扩容 |
三步完成第一次调用
- 1领一个免费 API 密钥
注册后创建密钥,你会拿到一个 Authorization: Bearer p6_<key_id>:<secret> 令牌,用它给发往 https://api.fotovid.co 的每一次调用做鉴权。不用准备基础设施,也不用编译 ffmpeg——加个请求头就行。
- 2在 Worker 里 POST 你的视频
用统一的请求格式调用 POST /v1/video/watermark:source_url 指向输入视频的 HTTPS URL,params 对象里把 type 设为 image、text 或 combo,再配上 position(例如 bottom-right)、opacity 和 scale。这在 Worker 内部就是一次普通的 fetch,完美契合 isolate 的请求模型。
- 3直接用托管的结果 URL
Fotovid 返回扁平的 JSON:{ id, type, url, expires_at, duration }。其中 url 指向托管在 Fotovid 上、已加好水印的成片。你可以把它直接交给客户端,也可以转存到自己的存储桶里长期保留——托管结果是临时的。
