VOD 深度剖析第 12 部分:在 AWS 上构建 VOD —— 服务、架构与成本

完整的 AWS VOD 参考:MediaConvert、MediaPackage、CloudFront、S3、Step Functions、SPEKE DRM 集成、Terraform IaC、真实成本拆解、常见陷阱,以及一份生产落地路线图。

zhuermu··22 分钟
vodstreamingawsmediaconvertcloudfrontterraform

这是 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 CloudFrontCDN
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)     │
                                                        └──────────────┘

流程

  1. 创作者将源文件上传到 S3 源桶
  2. S3 事件通过 EventBridge 触发 Step Functions
  3. Lambda 探测并校验文件
  4. Step Functions 调用 MediaConvert 进行转码
  5. 转码输出写入 S3 目标桶
  6. MediaPackage-VOD 封装 HLS/DASH + DRM
  7. CloudFront 作为 CDN 分发
  8. 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 秒 GOP
  • SegmentLength: 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 竞价。


跨云对应关系

AWSGCPAzure
S3GCSBlob Storage
MediaConvertTranscoder APIMedia Services
MediaPackageMedia CDNMedia Services DRM
CloudFrontCloud CDN / Media CDNAzure CDN
Step FunctionsWorkflowsDurable Functions
LambdaCloud FunctionsAzure 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 冷归档)

关键要点

  1. AWS 核心四剑客:MediaConvert + MediaPackage + CloudFront + S3
  2. Step Functions 做编排,用 Lambda 做轻量处理。
  3. SPEKE 对接托管 DRM 服务。
  4. 生产环境应采用 Terraform 进行声明式基础设施管理
  5. CDN 带宽是主导成本 —— 其次是存储,转码相对便宜。
  6. 每个主流云都有对应服务 —— 根据你的市场做选择。
  7. 从 MVP 到生产:通常需要 2–6 个月

上一篇: 第 11 部分:端到端工作流

至此,VOD 流媒体深度剖析系列全部完结。如果还没读过开篇,请从第 1 部分:视频基础开始。

参考资料

  1. AWS Elemental MediaConvert User Guide — AWS Documentation
  2. AWS Elemental MediaPackage User Guide — AWS Documentation
  3. Amazon CloudFront Developer Guide — AWS Documentation
  4. Secure Packager and Encoder Key Exchange (SPEKE) — AWS Documentation