Serverless / Lambda 跑 FFmpeg:那些坑和怎么绕
在 AWS Lambda、Vercel、Cloudflare Workers 上跑 FFmpeg 为什么折磨人——二进制大、超时短、内存贵、冷启动慢,以及实用替代方案。

Serverless 卖的是无限弹性、零运维。所以一旦要处理视频——剪个片段、出张缩略图、转码上传文件——第一反应就是:丢 Lambda 上跑个 FFmpeg 不就完了?能有多难?
实际试过的都知道,挺难的。这篇把我们在 Serverless 上跑 FFmpeg 踩过的坑、常见的几种绕路办法、它们各自死在哪里,以及什么时候该直接换成专用 API,挨个讲清楚。
为什么 FFmpeg 在 Serverless 上水土不服
1. 二进制就 70MB+,还没开始写代码
带常见编码器(H.264、H.265、VP9、AAC、Opus)的静态 FFmpeg 二进制本身就 70-100 MB。再加上 libass(字幕)或 libfdk-aac(高质量 AAC),轻松 120 MB+。
平台的部署体积限制压得人难受:
| 平台 | 部署大小限制 | 包含什么 |
|---|---|---|
| AWS Lambda(zip) | 50 MB(解压 250 MB) | 代码 + 依赖 + 二进制 |
| AWS Lambda(容器) | 10 GB | 空间够,但冷启动更慢 |
| Vercel Serverless Functions | 50 MB | 压缩后 |
| Cloudflare Workers | 10 MB(付费版) | 不支持原生二进制 |
| Google Cloud Functions | 500 MB(源码) | 相对宽松 |
AWS Lambda zip 部署模式下,光 FFmpeg 就吃掉 50 MB 预算的大半,业务代码只能在剩下的缝隙里挤。
2. 执行超时 — 顶多 15 分钟
AWS Lambda 最长 15 分钟。其他平台还更短:
| 平台 | 最长执行时间 |
|---|---|
| AWS Lambda | 15 分钟 |
| Google Cloud Functions | 9 分钟(第 2 代 60 分钟) |
| Vercel Serverless | 5 分钟(Pro),60 秒(Hobby) |
| Cloudflare Workers | 30 秒(标准),15 分钟(Workflows) |
15 分钟听上去挺多——直到看一眼 FFmpeg 实际要跑多久:
| 任务 | 1080p、10 分钟视频 | 2 vCPU 耗时 |
|---|---|---|
| 提缩略图 | 1 帧 | < 1 秒 |
| 提音频 | 流复制 | < 5 秒 |
| 转 H.264 | CRF 23、medium | 8-12 分钟 |
| 转 H.265 | CRF 28、medium | 15-25 分钟 |
| 烧字幕 | 重新编码 | 10-15 分钟 |
| 转 VP9 | CRF 30 | 20-40 分钟 |
缩略图、音频提取这种轻活完全够用。但凡是几分钟以上视频的真转码,都要么直接超时,要么贴着超时擦边走。关于编码参数怎么影响耗时,见我们的视频压缩最佳实践。
3. 内存限制 — 大文件根本塞不下
视频处理特别吃内存。FFmpeg 要缓冲输入输出,复杂滤镜还会同时持有多帧。
| 平台 | 最大内存 |
|---|---|
| AWS Lambda | 10 GB |
| Google Cloud Functions | 32 GB(第 2 代) |
| Vercel Serverless | 3 GB |
| Cloudflare Workers | 128 MB |
AWS Lambda 10 GB 看上去够——但内存直接挂钩费用。Lambda 按 GB-秒计费。4 GB 跑 10 分钟,成本是 256 MB 跑 15 秒的 40 倍。
实际任务的内存占用:
| 任务 | 典型内存 |
|---|---|
| 提缩略图 | 100-200 MB |
| 提音频 | 50-150 MB |
| 1080p H.264 转码 | 500 MB - 1.5 GB |
| 4K H.264 转码 | 2-4 GB |
| 复杂滤镜链 | 1-4 GB |
4. 没有 GPU 加速
硬件加速编码(NVENC、QSV、VAAPI)在 H.264/H.265 上能快 5-10 倍。但 Serverless 平台不给 GPU。
只能 CPU 编码,意味着:
- 比 GPU 慢 5-10 倍
- 大批量场景下每个视频更贵
- 更容易撞超时上限
5. 冷启动税
每次冷启动都要把 FFmpeg 加载进运行环境。一个 70 MB+ 的二进制在平台自身初始化之外,再叠加 1-3 秒冷启动延迟。
容器版 Lambda(大体积二进制只能走这条路)冷启动能到 5-15 秒。
6. 临时存储紧张
Lambda 的 /tmp 默认只有 512 MB(可付费扩到 10 GB)。视频文件本身就大——10 分钟 1080p 就是 500 MB - 1 GB。输入输出都要落盘:
输入文件: 500 MB
输出文件: 300 MB
FFmpeg 临时: 200 MB
─────────────────
合计: 1 GB ← 超过默认 /tmp
7. 进程不复用
FFmpeg 在常驻进程下能省事——保持二进制热、维持解码缓存、复用连接。Serverless 函数全是无状态短命的,每次调用从零开始。
常见的几种绕法
虽然坑这么多,团队还是会硬上。下面是几种最常见的姿势。
Lambda Layers
AWS Lambda Layer 让你把 FFmpeg 单独打包,不和应用代码挤一起:
# 下载静态编译的 FFmpeg
wget https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz
tar xf ffmpeg-release-amd64-static.tar.xz
# 组装 Lambda Layer 目录结构
mkdir -p ffmpeg-layer/bin
cp ffmpeg-*-amd64-static/ffmpeg ffmpeg-layer/bin/
cd ffmpeg-layer && zip -r ../ffmpeg-layer.zip .
函数里这么用:
const { execSync } = require('child_process');
exports.handler = async (event) => {
// Layer 里的 FFmpeg 在 /opt/bin/ffmpeg
const result = execSync(
'/opt/bin/ffmpeg -i /tmp/input.mp4 -vf "scale=-2:720" -crf 23 /tmp/output.mp4',
{ timeout: 300000 }
);
return { statusCode: 200 };
};
问题:
- Layer 的体积仍然算进 250 MB 解压上限
- 你得砍掉用不到的编码器
- 自己维护 FFmpeg 构建是个长期负担
Docker 容器镜像
Lambda 容器镜像最大 10 GB,空间宽得多:
FROM public.ecr.aws/lambda/nodejs:20
# 装 FFmpeg
RUN yum install -y tar xz && \
curl -L https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz | \
tar xJ --strip-components=1 -C /usr/local/bin/ --wildcards '*/ffmpeg' '*/ffprobe'
COPY index.mjs ${LAMBDA_TASK_ROOT}/
CMD ["index.handler"]
问题:
- 冷启动显著变慢(5-15 秒)
- 镜像变大,ECR 存储费用更高
- 15 分钟超时和无 GPU 这两件事还是没法绕
ffmpeg.wasm — 浏览器/边缘版
ffmpeg.wasm 把 FFmpeg 编译成 WebAssembly,能在浏览器和边缘运行时跑:
import { FFmpeg } from '@ffmpeg/ffmpeg';
import { fetchFile } from '@ffmpeg/util';
const ffmpeg = new FFmpeg();
await ffmpeg.load();
await ffmpeg.writeFile('input.mp4', await fetchFile(videoUrl));
await ffmpeg.exec(['-i', 'input.mp4', '-vf', 'scale=-2:720', 'output.mp4']);
const data = await ffmpeg.readFile('output.mp4');
问题:
- 比原生 FFmpeg 慢 3-10 倍
- 编码器支持不全(不支持 H.265,滤镜也少)
- 浏览器里内存吃紧
- 几百 MB 的文件就处理不动了
- 大多数环境单线程
分片处理
把大视频切片、并行跑、再合并:
# 切成 2 分钟的片段
ffmpeg -i input.mp4 -c copy -segment_time 120 -f segment -reset_timestamps 1 chunk_%03d.mp4
// 并行调多个 Lambda 处理
const chunks = ['chunk_000.mp4', 'chunk_001.mp4', 'chunk_002.mp4'];
const results = await Promise.all(
chunks.map(chunk => lambda.invoke({
FunctionName: 'process-chunk',
Payload: JSON.stringify({ chunk })
}).promise())
);
// 合并结果
// (还得再调一次 Lambda)
问题:
- 编排复杂(Step Functions、SQS 等)
- 不是所有操作都能切片(比如字幕时间会跨片段)
- 合并步骤多一道延迟,分片边界还可能出瑕疵
- 总成本往往比一次性长跑更贵
砍小 FFmpeg 构建
只编你要的编码器,二进制能小不少:
./configure \
--disable-everything \
--enable-decoder=h264,aac,mp3 \
--enable-encoder=libx264,aac \
--enable-muxer=mp4,mp3 \
--enable-demuxer=mov,mp4,mp3 \
--enable-protocol=file,pipe \
--enable-filter=scale,overlay \
--enable-gpl --enable-libx264
能压到 15-25 MB,代价是功能砍光。下个月要 VP9?再编一次。
实测:Lambda vs 专用机
跑了一段 5 分钟 1080p 视频对比:
| 任务 | Lambda(3 GB,arm64) | EC2 c6g.large(2 vCPU) | EC2 g5.xlarge(GPU) |
|---|---|---|---|
| 缩略图 | 0.8s | 0.5s | 0.3s |
| 提音频 | 2s | 1.5s | 1.5s |
| H.264 CRF 23 | 4.5 分钟 | 3.2 分钟 | 25s |
| H.265 CRF 28 | 9 分钟 | 7 分钟 | 35s |
| VP9 CRF 30 | 12 分钟 | 9 分钟 | 不适用 |
1000 次 H.264 转码(5 分钟 1080p)的费用对比:
| 方案 | 单视频耗时 | 单视频费用 | 总费用 |
|---|---|---|---|
| Lambda(3 GB) | 4.5 分钟 | ~$0.022 | ~$22 |
| EC2 c6g.large(预留) | 3.2 分钟 | ~$0.004 | ~$4 |
| EC2 g5.xlarge(GPU) | 25s | ~$0.008 | ~$8 |
持续负载下,Lambda 大约比预留实例贵 5 倍。一旦每小时处理量上去几个,成本优势就没了。
什么时候 Serverless 真的合适
不是一棍子打死,确实有合适的场景:
- 生成缩略图 — 又快又省内存,根本不会撞限制
- 流复制提音频 — 几乎瞬完成
- 30 秒以内的短视频 — 转码也不会超时
- 抓元数据 — ffprobe 毫秒级
- 零散、不稳定的流量 — 几小时都没一次请求那种
# 这些任务在 Lambda 上跑得很舒服
ffmpeg -i input.mp4 -ss 00:00:05 -frames:v 1 thumbnail.jpg # < 1s
ffmpeg -i input.mp4 -vn -c:a copy audio.aac # < 2s
ffprobe -v quiet -print_format json -show_format -show_streams input.mp4 # < 1s
什么时候该直接换专用 API
Serverless 撑不住的场景:
- 超过 2-3 分钟的转码 — 超时风险高
- H.265 / VP9 编码 — 没 GPU 太慢
- 复杂滤镜链 — 字幕、水印、多输入合成
- 可预测的大批量处理 — 费用顶不住
- 完整编码器支持 — 砍小的二进制不够用
- 要 GPU — Serverless 没
这些场景下,专用视频处理 API 直接把基础设施那块的烦恼端走。在挑托管方案,可以参考 FFHub 与 AWS MediaConvert 的对比。
FFHub:不用自己撑基础设施的云端 FFmpeg
FFHub 就是冲着 Serverless 解决不了的那些场景设计的云 FFmpeg API。本地能跑的命令直接发过来,FFHub 在调好的硬件上执行。
# 不用再和 Lambda Layer、超时较劲:
curl -X POST https://api.ffhub.io/v1/command \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"command": "-i input.mp4 -c:v libx264 -crf 23 -preset medium -c:a aac output.mp4",
"inputs": ["https://storage.example.com/input.mp4"],
"webhook": "https://your-app.com/callback"
}'
相比 Serverless 跑 FFmpeg:
- 不用管二进制 — 完整 FFmpeg,所有编码器,版本始终跟得上
- 没有超时 — 视频多长都能处理
- 硬件调过 — 专门针对视频处理调优
- 命令一样 — 不用学新 API
- 按量付费 — 不养空机器
如果视频处理不是你的核心产品,这点尤其重要——你应该把工程时间花在做产品功能上,而不是修 Lambda Layer 和 FFmpeg 构建。
决策表
| 维度 | 用 Serverless | 用专用 API |
|---|---|---|
| 视频时长 | < 30 秒 | 任意 |
| 任务类型 | 缩略图、元数据、音频流复制 | 转码、字幕、滤镜 |
| 量级 | < 50/天 | 任意 |
| 编码器需求 | 仅 H.264 | 任意 |
| 要 GPU | 否 | 是(更快) |
| 团队配置 | 有 DevOps 团队 | 偏好托管 |
小结
Serverless 上跑 FFmpeg 能跑,但很折磨。二进制大、超时严、内存贵、没 GPU——只适合缩略图和音频提取这种轻活。
如果工作负载是真转码,请算总成本——不只是 Lambda 账单,还有维护自定义 FFmpeg 部署花的工程时间。对大多数团队来说,专用视频处理 API 在成本和可靠性上都赢自己硬撑 Serverless。想了解 FFHub 是个什么样的云 FFmpeg 方案,或者批量转码 API 怎么写,看那两篇。
延伸阅读
- 什么是 FFHub? - FFHub 怎么把基础设施那一摊子事接走
- 批量视频转码 API - 用 REST API 处理上千个视频
- FFHub vs AWS MediaConvert - FFHub 与 AWS 托管转码服务的客观对比