VOD 深度剖析第 12 部分:在 AWS 上构建 VOD —— 服务、架构与成本
完整的 AWS VOD 参考:MediaConvert、MediaPackage、CloudFront、S3、Step Functions、SPEKE DRM 集成、Terraform IaC、真实成本拆解、常见陷阱,以及一份生产落地路线图。
这是 VOD 流媒体深度剖析 系列的第 12 部分。
AWS 媒体服务家族
AWS 将其 VOD 与直播服务统一归于 AWS Elemental 品牌之下。核心四剑客:
| 服务 | 用途 |
|---|---|
| AWS Elemental MediaConvert | 基于文件的转码(离线、VOD) |
| AWS Elemental MediaLive | 实时编码(直播) |
| AWS Elemental MediaPackage | 流封装(HLS/DASH/CMAF)+ DRM |
| AWS Elemental MediaTailor | 服务端广告插入(SSAI) |
配套服务:
| 服务 | 用途 |
|---|---|
| Amazon S3 | 对象存储 |
| Amazon CloudFront | CDN |
| AWS Lambda | 触发器、轻量处理 |
| AWS Step Functions | 工作流编排 |
| Amazon DynamoDB | 资产元数据存储 |
| Amazon SNS / SQS | 事件通知、异步队列 |
| Amazon CloudWatch | 监控与告警 |
参考架构
AWS 官方的 Video on Demand on AWS 解决方案:
┌──────────────┐ ┌──────────────┐
│ Viewer │ │ Creator │
│ │ │ (Uploader) │
└──────────────┘ └──────────────┘
▲ │
│ ⑥ HTTPS │ ① PutObject
│ ▼
┌──────────────┐ ┌──────────────┐
│ CloudFront │ │ S3 Source │
│ (CDN) │ │ Bucket │
└──────────────┘ └──────────────┘
▲ │
│ ⑤ origin fetch │ ② EventBridge
│ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ MediaPackage │◄────│ S3 Dest │◄──────────────────│ Step │
│ -VOD │ │ Bucket │ │ Functions │
│ (packaging) │ └──────────────┘ └──────────────┘
└──────────────┘ ▲ │
│ │ ▼
│ SPEKE │ ┌──────────────┐
▼ └───────────────────────────│ MediaConvert │
┌──────────────┐ │ (Transcode) │
│ DRM License │ └──────────────┘
│ Server │ │
│ (EZDRM etc) │ ▼
└──────────────┘ ┌──────────────┐
│ Lambda │
│ (post-proc, │
│ notify) │
└──────────────┘
流程:
- 创作者将源文件上传到 S3 源桶
- S3 事件通过 EventBridge 触发 Step Functions
- Lambda 探测并校验文件
- Step Functions 调用 MediaConvert 进行转码
- 转码输出写入 S3 目标桶
- MediaPackage-VOD 封装 HLS/DASH + DRM
- CloudFront 作为 CDN 分发
- Lambda 发送完成通知
分步实操
步骤 1:设置 S3 桶
# Source bucket (raw uploads)
aws s3 mb s3://my-vod-source --region us-east-1
# Destination bucket (transcoded output)
aws s3 mb s3://my-vod-dest --region us-east-1
# Enable EventBridge notifications
aws s3api put-bucket-notification-configuration \
--bucket my-vod-source \
--notification-configuration '{
"EventBridgeConfiguration": {}
}'
步骤 2:为 MediaConvert 创建 IAM 角色
MediaConvert 需要对 S3 的读写权限:
aws iam create-role --role-name MediaConvertRole \
--assume-role-policy-document file://trust-policy.json
aws iam attach-role-policy --role-name MediaConvertRole \
--policy-arn arn:aws:iam::aws:policy/AmazonS3FullAccess
步骤 3:MediaConvert 作业模板
{
"Role": "arn:aws:iam::123456789012:role/MediaConvertRole",
"Settings": {
"Inputs": [{
"FileInput": "s3://my-vod-source/episode01.mov",
"AudioSelectors": {
"Audio Selector 1": {"DefaultSelection": "DEFAULT"}
}
}],
"OutputGroups": [{
"Name": "CMAF",
"OutputGroupSettings": {
"Type": "CMAF_GROUP_SETTINGS",
"CmafGroupSettings": {
"Destination": "s3://my-vod-dest/ep01/",
"SegmentLength": 4,
"FragmentLength": 2,
"WriteHlsManifest": "ENABLED",
"WriteDashManifest": "ENABLED"
}
},
"Outputs": [
{
"NameModifier": "_1080p",
"VideoDescription": {
"Width": 1920, "Height": 1080,
"CodecSettings": {
"Codec": "H_264",
"H264Settings": {
"RateControlMode": "QVBR",
"QvbrSettings": {"QvbrQualityLevel": 8},
"MaxBitrate": 5000000,
"GopSize": 2, "GopSizeUnits": "SECONDS"
}
}
},
"AudioDescriptions": [{
"CodecSettings": {
"Codec": "AAC",
"AacSettings": {
"Bitrate": 128000,
"SampleRate": 48000,
"CodingMode": "CODING_MODE_2_0"
}
}
}]
}
]
}]
}
}
关键参数:
QvbrQualityLevel: 8—— QVBR 目标质量(1–10,越高越好)MaxBitrate: 5000000—— 峰值码率上限GopSize: 2 SECONDS—— 2 秒 GOPSegmentLength: 4—— 4 秒分片FragmentLength: 2—— 2 秒片段(CMAF 的子分片)
提交作业:
aws mediaconvert create-job --cli-input-json file://job.json \
--endpoint-url https://abc.mediaconvert.us-east-1.amazonaws.com
步骤 4:通过 SPEKE 添加 DRM
在 CMAF OutputGroup 中加入:
"Encryption": {
"EncryptionMethod": "SAMPLE_AES",
"SpekeKeyProvider": {
"ResourceId": "episode01",
"SystemIds": [
"edef8ba9-79d6-4ace-a3c8-27dcd51d21ed",
"94ce86fb-07ff-4f43-adb8-93d2fa968ca2",
"9a04f079-9840-4286-ab92-e65be0885f95"
],
"Url": "https://speke.ezdrm.com/v2",
"CertificateArn": "arn:aws:acm:..."
}
}
三个 SystemIds 分别对应 Widevine、FairPlay 和 PlayReady。Url 指向 DRM 服务商的 SPEKE 端点。
步骤 5:CloudFront 分发
aws cloudfront create-distribution \
--distribution-config file://cf-config.json
关键配置:
- Origin:S3 目标桶
- Viewer Protocol Policy:将 HTTP 重定向到 HTTPS
- Allowed Methods:GET、HEAD
- Cache Policy:针对未压缩对象优化
- Origin Access Control:启用(阻止直接访问 S3)
- Signed URLs:需要时启用 Trusted Key Groups
步骤 6:生成签名 URL
后端在播放时按需生成短时效的签名 URL:
from datetime import datetime, timedelta
from botocore.signers import CloudFrontSigner
import rsa
def generate_signed_url(video_path, key_id, private_key, expires_in=3600):
url = f"https://d1234.cloudfront.net/{video_path}"
expires = datetime.utcnow() + timedelta(seconds=expires_in)
def rsa_signer(message):
return rsa.sign(message, private_key, 'SHA-1')
signer = CloudFrontSigner(key_id, rsa_signer)
return signer.generate_presigned_url(url, date_less_than=expires)
步骤 7:Step Functions 编排
用状态机把整条流水线串联起来(参见第 11 部分)。
基础设施即代码:Terraform
# Source bucket
resource "aws_s3_bucket" "source" {
bucket = "my-vod-source"
}
# Destination bucket
resource "aws_s3_bucket" "dest" {
bucket = "my-vod-dest"
}
# EventBridge rule: trigger Step Functions on S3 upload
resource "aws_cloudwatch_event_rule" "upload" {
name = "vod-upload-trigger"
event_pattern = jsonencode({
source = ["aws.s3"]
detail-type = ["Object Created"]
detail = {
bucket = { name = [aws_s3_bucket.source.bucket] }
}
})
}
# Step Functions state machine
resource "aws_sfn_state_machine" "vod_pipeline" {
name = "vod-pipeline"
role_arn = aws_iam_role.sfn_role.arn
definition = file("${path.module}/vod_pipeline.asl.json")
}
resource "aws_cloudwatch_event_target" "sfn" {
rule = aws_cloudwatch_event_rule.upload.name
arn = aws_sfn_state_machine.vod_pipeline.arn
role_arn = aws_iam_role.event_bridge_role.arn
}
# CloudFront distribution
resource "aws_cloudfront_distribution" "vod" {
origin {
domain_name = aws_s3_bucket.dest.bucket_regional_domain_name
origin_id = "s3-vod-dest"
s3_origin_config {
origin_access_identity = aws_cloudfront_origin_access_identity.vod.cloudfront_access_identity_path
}
}
enabled = true
default_root_object = ""
default_cache_behavior {
allowed_methods = ["GET", "HEAD"]
cached_methods = ["GET", "HEAD"]
target_origin_id = "s3-vod-dest"
viewer_protocol_policy = "redirect-to-https"
cache_policy_id = data.aws_cloudfront_cache_policy.caching_optimized.id
trusted_key_groups = [aws_cloudfront_key_group.vod.id]
}
restrictions {
geo_restriction {
restriction_type = "none"
}
}
viewer_certificate {
cloudfront_default_certificate = true
}
}
成本拆解
MediaConvert 转码
按 输出分钟数 x 编码复杂度 计费:
| 档位 | 价格(us-east-1) |
|---|---|
| Basic SD(< 30fps、< 720p、AVC) | $0.0075 / 输出分钟 |
| Basic HD(< 30fps、720p–1080p、AVC) | $0.015 / 输出分钟 |
| Pro HD(60fps 或 HEVC 或 HDR) | $0.030 / 输出分钟 |
| Pro UHD(4K+) | $0.060 / 输出分钟 |
| AV1 | 约 2–3 倍溢价 |
示例 —— 90 分钟电影,6 档输出(4 档 H.264 + 2 档 H.265):
90 min x 4 tiers x $0.015 = $5.40 (H.264)
90 min x 2 tiers x $0.030 = $5.40 (H.265)
Total transcode cost ≈ $10.80 per movie
S3 存储
- 标准存储:$0.023/GB/月(us-east-1)
- 单部电影的全部版本:约 3 GB → $0.07/月
- 大型片库(10 万小时):约 1 PB → $23,000/月
- 冷内容放到 S3 Glacier 可节省约 80%
CloudFront 带宽
按区域为 $0.005–$0.08/GB,并有分档量折扣。完整成本模型见第 7 部分。
MediaPackage JIT 封装
约 $0.065/GB 源流量。对于频繁访问的热门内容更划算。
月度账单估算(2M DAU 平台)
假设:2M DAU、日均观看 30 分钟、720p 主码流。
| 类别 | 月度成本区间 |
|---|---|
| S3 存储(10 PB) | $50K–$150K |
| MediaConvert(每天 100 集新内容) | $1K–$5K |
| CloudFront(24 PB/月) | $400K–$800K |
| MediaPackage JIT | $50K–$150K |
| Lambda + Step Functions | $5K–$20K |
| DynamoDB + RDS | $10K–$30K |
| DRM SaaS | $20K–$80K |
| 合计 | $500K–$1.2M |
CDN 带宽是成本的主导项。三个最有效的降本抓手:更优的编解码器、按标题编码(per-title encoding),以及多 CDN 竞价。
跨云对应关系
| AWS | GCP | Azure |
|---|---|---|
| S3 | GCS | Blob Storage |
| MediaConvert | Transcoder API | Media Services |
| MediaPackage | Media CDN | Media Services DRM |
| CloudFront | Cloud CDN / Media CDN | Azure CDN |
| Step Functions | Workflows | Durable Functions |
| Lambda | Cloud Functions | Azure Functions |
选云建议:
- 全球受众、主要在海外:AWS(覆盖最广)
- 中国大陆:阿里云 / 腾讯云
- 多地区:多云分发
常见陷阱
CloudFront 签名 URL 缓存命中率跌到 0%
原因:签名参数(Signature、Expires、Key-Pair-Id)被计入了缓存键。
修复:将 Cache Policy 配置为仅以路径作为缓存键。签名查询字符串应从缓存键中 排除。
MediaConvert 输出文件异常庞大
原因:未设置 QVBR 或 MaxBitrate。CRF 过低,导致峰值码率失控暴涨。
修复:设置 QvbrQualityLevel: 7-8 + MaxBitrate 约束。
iOS 上 HLS 卡在加载中
原因:主播放列表(Master Playlist)缺少 CODECS 属性。
修复:确保每个 EXT-X-STREAM-INF 都包含 CODECS="avc1.xxxxxx,mp4a.40.2"。
跨境分发延迟高
原因:CloudFront 回源到 us-east-1 的 S3 —— 对亚洲用户延迟很高。
修复:在源站附近启用 CloudFront Origin Shield,或使用 S3 跨区域复制,让 CloudFront 从最近的区域回源。
生产落地路线图:从零到上线
第 1 周:最小可行 Demo
- 开通 AWS 账户,启用 S3、MediaConvert、CloudFront
- 手动运行一个 MediaConvert 作业,产出 HLS 输出
- 在 Safari 中打开 m3u8 —— 能够播放
第 2–4 周:自动化流水线
- Step Functions:S3 上传 → MediaConvert → S3 → CloudFront
- 后端生成签名 URL
- iOS/Android 应用集成播放器(AVPlayer / ExoPlayer)
第 2–3 个月:生产级特性
- 多档码率梯度 + 按标题编码(per-title encoding)
- DRM 集成(EZDRM / PallyCon)
- 通过 Mux 或自建流水线采集 QoE 数据
- 多 CDN 路由
第 4–6 个月:规模化
- H.265 / AV1 档位
- LL-HLS(若加入直播)
- 多地区部署 + 跨区域复制
- 成本优化(Spot 实例、Glacier 冷归档)
关键要点
- AWS 核心四剑客:MediaConvert + MediaPackage + CloudFront + S3。
- 用 Step Functions 做编排,用 Lambda 做轻量处理。
- 用 SPEKE 对接托管 DRM 服务。
- 生产环境应采用 Terraform 进行声明式基础设施管理。
- CDN 带宽是主导成本 —— 其次是存储,转码相对便宜。
- 每个主流云都有对应服务 —— 根据你的市场做选择。
- 从 MVP 到生产:通常需要 2–6 个月。
上一篇: 第 11 部分:端到端工作流
至此,VOD 流媒体深度剖析系列全部完结。如果还没读过开篇,请从第 1 部分:视频基础开始。
参考资料
- AWS Elemental MediaConvert User Guide — AWS Documentation
- AWS Elemental MediaPackage User Guide — AWS Documentation
- Amazon CloudFront Developer Guide — AWS Documentation
- Secure Packager and Encoder Key Exchange (SPEKE) — AWS Documentation