← All posts

Serverless / Lambda 跑 FFmpeg:那些坑和怎么绕

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

FFHub·2026-05-02
Serverless / Lambda 跑 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 Functions50 MB压缩后
Cloudflare Workers10 MB(付费版)不支持原生二进制
Google Cloud Functions500 MB(源码)相对宽松

AWS Lambda zip 部署模式下,光 FFmpeg 就吃掉 50 MB 预算的大半,业务代码只能在剩下的缝隙里挤。

2. 执行超时 — 顶多 15 分钟

AWS Lambda 最长 15 分钟。其他平台还更短:

平台最长执行时间
AWS Lambda15 分钟
Google Cloud Functions9 分钟(第 2 代 60 分钟)
Vercel Serverless5 分钟(Pro),60 秒(Hobby)
Cloudflare Workers30 秒(标准),15 分钟(Workflows)

15 分钟听上去挺多——直到看一眼 FFmpeg 实际要跑多久:

任务1080p、10 分钟视频2 vCPU 耗时
提缩略图1 帧< 1 秒
提音频流复制< 5 秒
转 H.264CRF 23、medium8-12 分钟
转 H.265CRF 28、medium15-25 分钟
烧字幕重新编码10-15 分钟
转 VP9CRF 3020-40 分钟

缩略图、音频提取这种轻活完全够用。但凡是几分钟以上视频的真转码,都要么直接超时,要么贴着超时擦边走。关于编码参数怎么影响耗时,见我们的视频压缩最佳实践

3. 内存限制 — 大文件根本塞不下

视频处理特别吃内存。FFmpeg 要缓冲输入输出,复杂滤镜还会同时持有多帧。

平台最大内存
AWS Lambda10 GB
Google Cloud Functions32 GB(第 2 代)
Vercel Serverless3 GB
Cloudflare Workers128 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.8s0.5s0.3s
提音频2s1.5s1.5s
H.264 CRF 234.5 分钟3.2 分钟25s
H.265 CRF 289 分钟7 分钟35s
VP9 CRF 3012 分钟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 怎么写,看那两篇。

延伸阅读

Serverless / Lambda 跑 FFmpeg:那些坑和怎么绕 | FFHub