架构设计
Sandcut 的核心原则很简单:控制逻辑在边缘,执行逻辑在沙箱。
请求链路
text
Client
↓
Worker API
↓
输入大小预探测
↓
Durable Object 租约调度器
↓
Warm 容器会话
↓
FFmpeg 执行
↓
结果流式写入 R2
↓
返回 CDN URL控制平面负责什么
Worker 负责:
- Bearer Token 鉴权
- JSON 请求体校验
- 带 SSRF 防护的输入 URL 校验
- 结构化 seek 标准化
- 输入大小预探测
- 容器会话租约申请与释放
- R2 上传编排
这让 Worker 本身保持轻量、确定、容易维护。
执行平面负责什么
容器负责:
- FFmpeg 命令规划
- 真正的媒体处理
- 处理结果的流式输出
这样用户提供的媒体工作负载会被限制在沙箱边界里。
Seek 模型
FFmpeg 的一个核心现实是:性能与精度经常不能同时最大化。Sandcut 直接把这个差异显式暴露出来:
fast:把-ss放在-i前面accurate:把-ss放在-i后面hybrid:前置粗跳 + 后置细修
对外 API 不允许直接透传原始 -ss,而是统一通过结构化 seek 对象来表达。
流式链路
当前实现刻意避免了早期那种高内存路径:
- 容器用
fs.createReadStream()输出结果文件 - Worker 直接把
response.body传给OUTPUT_BUCKET.put()
这意味着 Worker 不需要把整个处理结果先完整读进内存再上传。
调度器设计
Durable Object 调度器基于“租约”工作,优先选择:
- 活跃任务数更少的会话
- 在活跃任务数相同的情况下,更久未被使用的会话
相比只用 KV 轮询,这种方式在并发下更稳定。
已知权衡
输入大小预探测在源站提供 Content-Length 或支持 Range 时效果很好;如果源站两者都不提供,那么要做到字节级硬切断,仍然需要一个受控下载层放在 FFmpeg 前面。