# VOD 深度剖析 第 11 部分：端到端工作流——从上传到播放

> 完整的 10 步 VOD 生产流水线：上传、内容审核、探测、转码、封装、发布、CDN 预热，以及使用 Step Functions 和 Temporal 进行编排、灾难恢复。

- 作者: zhuermu
- 发布: 2026-05-10
- 网页版: https://zhuermu.com/blog/vod-deep-dive-part-11-end-to-end-workflow/

---
> *本文是 [VOD 流媒体深度剖析](/blog/vod-deep-dive-part-1-video-fundamentals/) 系列的第 11 部分。*

---

## 完整流水线概览

一段视频从文件交付到全球播放，至少要经历 10 个步骤：

```
①Upload  →  ②Content Scan  →  ③Probe  →  ④Transcode  →  ⑤Package+Encrypt
   ↓            ↓                ↓          ↓               ↓
   │        NSFW / copyright   ffprobe   Multi-rate       CMAF +
   │        fingerprint /      resolution H.264+H.265     DRM
   │        virus scan                   +AV1             Signing
   ↓
⑥Publish to Storage → ⑦Write Metadata → ⑧CDN Pre-warm → ⑨Notify → ⑩Monitor
   ↓                    ↓                 ↓               ↓         ↓
   S3 / Object        MySQL / ES        Major region    Push /     QoE
   Storage             Index             edge nodes     Homepage   Regression
```

每一步都可能失败。成熟的平台会在每个阶段都配备 **重试、告警和人工兜底方案**。

---

## 步骤 1：上传——把大文件送上云

**挑战**：

- 源文件往往有 5–50 GB（ProRes / DNxHD）
- 网络不可靠（跨境、办公室 WiFi）
- 用户可能在上传中途关闭笔记本或断网

### 分片上传（Multipart Upload）

把文件切分成 5–100 MB 的分片，并发上传：

```
Large file ────► [Chunk 1] [Chunk 2] [Chunk 3] ... [Chunk N]
                     │         │         │             │
                     ▼         ▼         ▼             ▼
                S3 Multipart concurrent upload (5-10 parallel)
                              │
                              ▼
                    Merge after all complete
```

**优势**：并行加速、失败可续传（只需重试失败的分片）、S3/GCS/Azure Blob 原生支持。

### 去重（MD5/CRC 预校验）

上传前，客户端先计算文件的 MD5 哈希：

```
Client → Backend: "Uploading file, md5=abc123"
Backend → Client: "Already have it, skip upload"   ← instant
              or
               "Not found, please upload"
```

### S3 分片上传 API（三步）

```python
import boto3

s3 = boto3.client('s3')

# 1. Initiate multipart upload
resp = s3.create_multipart_upload(Bucket='my-bucket', Key='video.mov')
upload_id = resp['UploadId']

# 2. Upload each part
parts = []
for i, chunk in enumerate(chunks, start=1):
    resp = s3.upload_part(
        Bucket='my-bucket', Key='video.mov',
        UploadId=upload_id, PartNumber=i,
        Body=chunk)
    parts.append({'PartNumber': i, 'ETag': resp['ETag']})

# 3. Complete and merge
s3.complete_multipart_upload(
    Bucket='my-bucket', Key='video.mov',
    UploadId=upload_id,
    MultipartUpload={'Parts': parts})
```

---

## 步骤 2：内容扫描——合规审核

上传的文件先进入 **隔离桶（quarantine bucket）**（而非生产存储）。内容必须通过审核后才能晋升到生产环境。

### 必要检查项

| 检查项 | 工具 |
|-------|-------|
| **病毒扫描** | ClamAV、VirusTotal API |
| **NSFW 检测** | AWS Rekognition Content Moderation |
| **暴力 / 血腥 / 政治敏感内容** | 同上 |
| **版权指纹识别** | Audible Magic（音乐）、ACRCloud |
| **人脸识别（如有需要）** | AWS Rekognition |
| **字幕合规** | 关键词过滤、区域适配 |

### 人工审核

AI 只是第一道过滤。高价值或处于边界的内容会进入人工审核队列：

- 审核员观看（抽样或完整观看）
- 标记为合规或不合规
- 记录判断理由

---

## 步骤 3：探测（Probe）——转码前先做校验

**ffprobe** 扫描文件以提取元数据：

```bash
ffprobe -v error -show_format -show_streams -of json input.mov > probe.json
```

可提取：分辨率、帧率、编解码器、音轨、采样率、时长、可变帧率标记、HDR 元数据。

**校验规则**：

- 时长 < 30 秒 → 拒绝（不是有效的剧集）
- 分辨率低于 720p → 拒绝（质量不足）
- 帧率不在 {23.976, 25, 29.97, 30, 50, 60} 之内 → 告警
- 标记为 HDR 但色彩空间不是 BT.2020 → 标记待修正或拒绝

---

## 步骤 4：转码——核心生产环节

### 产物清单

单个源文件通常会产出：

```
/vod/ep-001/
  mezz/original.mov              ← Source backup (cold storage)
  v_360p.mp4                     ← H.264 360p
  v_480p.mp4                     ← H.264 480p
  v_720p.mp4                     ← H.264 720p
  v_1080p.mp4                    ← H.264 1080p
  v_720p_hevc.mp4                ← H.265 720p
  v_1080p_hevc.mp4               ← H.265 1080p
  v_720p_av1.mp4                 ← AV1 720p (flagship devices)
  audio_en.mp4                   ← English AAC
  audio_zh.mp4                   ← Chinese AAC
  subs_en.vtt                    ← English subtitles
  subs_zh.vtt                    ← Chinese subtitles
  thumbnails.vtt + sprite.jpg    ← Thumbnail preview (scrub bar)
```

### 并行加速

单集视频可能需要 10 多个输出档位。有两种并行策略：

**按档位并行（简单）**：

```
Transcode cluster:
  Worker 1: Source → 360p H.264
  Worker 2: Source → 720p H.264
  Worker 3: Source → 1080p H.264
  Worker 4: Source → 720p H.265
  ...all workers run simultaneously
```

**按 GOP 切分并行（复杂但更快）**：

```
Split source at IDR boundaries into N segments
Each segment sent to a different worker for independent transcoding
Concatenate bitstreams at the end
```

Netflix 和 YouTube 会启动数百台云实例并行转码一部电影，大约一小时即可完成。

### 转码服务选型

| 服务 | 特点 |
|---------|----------------|
| **自建 ffmpeg 集群** | 控制力最强、大规模下成本最低、运维负担最重 |
| **AWS MediaConvert** | 按分钟计费、QVBR、集成 HDR/DRM |
| **Bitmovin / Mux** | 对开发者友好、企业级 |

### 成本优化

- **Spot / 抢占式实例** + 检查点续跑：可省 70–80%
- **长尾内容**：只做 H.264（节省转码与存储成本）
- **热门内容**：追加 H.265/AV1 档位

---

## 步骤 5：封装与加密

转码后的 MP4 需要转换成流媒体格式。参见 [第 5 部分（协议）](/blog/vod-deep-dive-part-5-streaming-protocols-hls-dash/) 和 [第 8 部分（DRM）](/blog/vod-deep-dive-part-8-drm-content-protection/)：

```
Transcoded MP4s
       │
       ▼
Shaka Packager / MediaPackage / mp4box
       │
       ▼
Output:
  CMAF fMP4 segments
  HLS master.m3u8 + media.m3u8
  DASH manifest.mpd
  (Optional) CENC/CBCS encrypted segments + license metadata
```

---

## 步骤 6–7：发布到存储 + 写入元数据

转码/封装后的资产上传到 **生产存储**（通常是一个独立的 S3“生产桶”）：

```
/vod-production/ep-001/
  init.mp4
  seg_*.m4s
  master.m3u8
  manifest.mpd
  thumbnails/...
```

同时写入业务数据库：

```sql
INSERT INTO episodes (
    episode_id, show_id, title, duration_sec,
    manifest_url, thumbnail_url,
    status, publish_at, ...
) VALUES (...);
```

更新搜索索引（Elasticsearch / Algolia）以便被检索到。

---

## 步骤 8：CDN 预热

参见 [第 7 部分：CDN 分发](/blog/vod-deep-dive-part-7-cdn-distribution/)。

**不要等第一波用户来触发回源拉取。** 对新内容和预期会火的内容进行预热，把 init 分片和前 5–10 个媒体分片提前推送到全球边缘节点。

---

## 步骤 9：通知与上线

- CMS 状态从“处理中”切换为“就绪”
- 向订阅者推送通知：“你关注的剧集更新了”
- 更新首页推荐 / 排行榜
- 激活广告素材（如为商业化内容）

---

## 步骤 10：监控与回归

上线后持续监控：

- 播放成功率
- QoE 指标是否在正常区间内（参见 [第 10 部分](/blog/vod-deep-dive-part-10-qoe-metrics/)）
- 标记异常内容（例如某一集卡顿率异常偏高）
- 发现问题 → 重新转码或回滚

---

## 编排：把各步骤串联起来

上述 10 个步骤必须按顺序可靠执行，并支持 **重试、并行和失败分支**。这些逻辑不可能硬编码进一个脚本里。

### 常见编排工具

| 工具 | 特点 | 适用场景 |
|------|----------------|----------|
| **AWS Step Functions** | Serverless、可视化、与 AWS 深度集成 | AWS 原生场景，AWS VOD 方案的默认选择 |
| **Temporal** | 分布式工作流、强一致性、类型安全 | 自管控制面 / 多云 |
| **Airflow** | 批量 ETL、定时任务 | 数据处理、离线分析 |
| **Argo Workflows** | Kubernetes 原生 | K8s 团队 |
| **自研（Kafka 驱动）** | 事件驱动，每一步发布下一个事件 | 追求最大定制化 |

### Step Functions 示例（简化版）

```json
{
  "StartAt": "Probe",
  "States": {
    "Probe": {
      "Type": "Task",
      "Resource": "arn:aws:lambda:...:ProbeVideo",
      "Next": "ParallelTranscode"
    },
    "ParallelTranscode": {
      "Type": "Parallel",
      "Branches": [
        {"StartAt": "Transcode360p", "States": {
          "Transcode360p": {"Type": "Task",
            "Resource": "arn:aws:mediaconvert:...", "End": true}}},
        {"StartAt": "Transcode720p", "States": {
          "Transcode720p": {"Type": "Task",
            "Resource": "arn:aws:mediaconvert:...", "End": true}}},
        {"StartAt": "Transcode1080p", "States": {
          "Transcode1080p": {"Type": "Task",
            "Resource": "arn:aws:mediaconvert:...", "End": true}}}
      ],
      "Next": "Package"
    },
    "Package": {
      "Type": "Task", "Resource": "...Package...", "Next": "Prewarm"
    },
    "Prewarm": {
      "Type": "Task", "Resource": "...Prewarm...", "Next": "Notify"
    },
    "Notify": {
      "Type": "Task", "Resource": "...Notify...", "End": true
    }
  }
}
```

Step Functions 内置了重试、超时、分支以及可视化的执行流程图。

---

## 灾难恢复与回滚

### 多区域备份

- 源文件（mezzanine 母版）通过 S3 跨区域复制（Cross-Region Replication）跨区备份
- 热门内容存储在多个区域（就近分发 + 故障转移）

### 回滚场景

- 新上线的内容出现问题 → 一键下架 + CDN 缓存失效
- 转码版本有 bug → 把 manifest 指针切回上一个版本

### 数据备份

- 元数据数据库：每日备份
- 用户观看进度（数据量大）→ 分级存储：热数据放 Redis，冷数据归档到 S3 Glacier

---

## 一个典型的时间线

一部 60 分钟的电影从上传到全球可用：

```
T+0min    Creator uploads mezzanine to quarantine bucket
T+5min    Upload complete → triggers Step Functions
T+7min    NSFW + virus scan complete
T+8min    ffprobe validation passes
T+10min   Parallel transcode starts (H.264 x4 + H.265 x2 + AV1 x1)
T+70min   All transcodes complete (~1x real-time, parallel tiers)
T+72min   Packager generates HLS + DASH + DRM
T+73min   Assets uploaded to production S3
T+74min   CDN pre-warming complete (NA / EU / APAC)
T+75min   Metadata written, search index updated, notifications sent
T+75min   Live ✅
```

短视频/短剧应用（每集 60–90 秒）能在几分钟内跑完整条流水线。

---

## 关键要点

1. VOD 流水线包含 10 个主要步骤——每一步都可能失败，都需要重试 + 告警。
2. **上传**：分片 + MD5 去重。**存储**：隔离桶 + 内容扫描。
3. 转码前必须做 **ffprobe** 校验。
4. **转码** 是最耗时的步骤——按档位或 GOP 并行 + 使用 Spot 实例。
5. **封装 + DRM** 是发布前的最后一步。
6. **CDN 预热** 对新内容上线至关重要。
7. **Step Functions / Temporal** 是首选的编排工具。
8. 从第一天起就要规划好 **灾难恢复和回滚**。

---

*上一篇：* [第 10 部分：QoE 指标](/blog/vod-deep-dive-part-10-qoe-metrics/)

*下一篇：* **[第 12 部分：在 AWS 上构建 VOD](/blog/vod-deep-dive-part-12-building-vod-on-aws/)**

---

## 常见问题

### 一个视频从上传到能播放，中间要经过哪些环节？

至少 10 个步骤：上传（分片上传 + MD5 去重）、内容扫描（病毒、NSFW、版权指纹）、ffprobe 探测校验、多档位转码、封装与 DRM 加密、发布到生产存储、写入元数据和搜索索引、CDN 预热、上线通知，最后是持续的 QoE 监控。每一步都可能失败，成熟的平台会在每个阶段配备重试、告警和人工兜底方案。

### 几十 GB 的视频源文件怎么可靠地上传到 S3？

用分片上传（Multipart Upload）：把文件切成 5–100 MB 的分片，5–10 个并发上传，既能并行提速，失败时也只需重试失败的分片——这对跨境或办公室 WiFi 下传 5–50 GB 母版文件至关重要。再配合 MD5 预校验，服务端已有的文件可直接跳过上传。S3、GCS、Azure Blob 都原生支持，S3 的 API 分三步：创建、上传分片、合并完成。

### 一部电影转码上线到底要多久？

以 60 分钟的电影为例，典型流水线全程约 75 分钟：内容扫描和 ffprobe 校验在最初几分钟完成，并行转码（H.264、H.265、AV1 多档位同时跑）约等于实时时长（约 1 小时），随后封装、DRM、上传生产存储、CDN 预热再花几分钟。Netflix 和 YouTube 会启动数百台实例按 GOP 切分并行；短剧类应用每集 60–90 秒，几分钟就能跑完整条流水线。


---

## 参考资料

- [AWS Step Functions Developer Guide](https://docs.aws.amazon.com/step-functions/latest/dg/welcome.html) — AWS Documentation
- [Temporal documentation](https://docs.temporal.io/) — Temporal
- [AWS Elemental MediaConvert User Guide](https://docs.aws.amazon.com/mediaconvert/latest/ug/what-is.html) — AWS Documentation
