WWDC Quick Look 💓 By SwiftGGTeam
Deliver reliable streams with HLS Content Steering

Deliver reliable streams with HLS Content Steering

观看原视频

Highlight

HLS Content Steering 在 2022 年新增 Pathway Cloning 能力——Steering Server 可以动态向已有客户端宣告新 CDN,无需重新下发主播放列表;配合 bucket-based 无状态规则,用 12 个均匀随机桶即可实现精细的全局流量调度,让流媒体运维从”改 playlist 重启推流”进化到”改 JSON 规则实时生效”。


核心内容

流媒体分发有一个老问题:CDN 出问题时怎么办?传统做法是改主播放列表(multivariant playlist),把出问题的 CDN 行删掉、加上备用的,然后等客户端下次请求时生效。这套流程慢、需要客户端重新加载,而且没法做细粒度的流量调度——比如”把 30% 的美国用户导到新 CDN 上灰度验证”。(02:15

HLS Content Steering 解决的就是这个问题。它的核心思路是把 CDN 抽象为 Pathway——每个 Pathway 对应一组结构相同、内容相同的变体流。客户端不需要关心 CDN 的物理地址,只需要从 Steering Server 拿到 Pathway 优先级列表,按顺序尝试。(04:30

Zheng Naiwei 在这场 session 里讲了两件事:一是 2021 年引入的 Content Steering 基础架构是怎么运作的,二是 2022 年的两项新能力——Pathway Cloning 和 bucket-based 规则。前者解决”怎么在不改主播放列表的前提下加新 CDN”,后者解决”怎么用无状态的方式做全局流量调度”。(06:00

Pathway Cloning 的设计很巧妙。当一个 CDN 被动态创建(比如某个区域的边缘节点刚上线),所有已经拿到旧播放列表的客户端都不知道它的存在。传统做法是等客户端下次请求主播放列表时把新 CDN 加进去——但这意味着所有现存用户都要经历一次重新加载。Pathway Cloning 让 Steering Server 在 Steering Manifest 里声明克隆规则:基于某个已有 Pathway(BASE-ID),修改 HOST 和参数,生成一个新的 Pathway(ID)。客户端收到后自己完成 URI 替换,无缝切换到新 CDN。(12:30

Bucket-based 规则是另一个重点。Steering Server 需要决定”哪些客户端走哪个 CDN”,但又不能维护每个客户端的状态——那不可扩展。做法是在 RELOAD-URI 里嵌入一个 1 到 12 的随机整数作为 bucket 编号。每个客户端在第一次请求时被随机分配一个 bucket,后续所有 Steering Manifest 请求都带着这个编号。Server 端只需要根据 bucket 号返回不同的 Pathway 优先级列表,就能实现任意比例的流量分配。50/50 就是 1-6 走 CDN1、7-12 走 CDN2;10/30/60 就是 1 走 CDN1、2-4 走 CDN2、5-12 走 CDN3。完全无状态,可水平扩展。(18:00


详细内容

Content Steering 基础架构回顾

03:00)Content Steering 的核心概念只有三个:Pathway、Steering Server、Steering Manifest。

Pathway 是一组结构相同的变体流的逻辑分组,通常对应一个 CDN。主播放列表中用 PATHWAY-ID 标记每个变体流属于哪个 Pathway:

#EXT-X-CONTENT-STEERING: SERVER-URI="/steering", PATHWAY-ID="CDN1"
#EXT-X-STREAM-INF: PATHWAY-ID="CDN1", BANDWIDTH=6000000
https://cdn1.example.com/high.m3u8
#EXT-X-STREAM-INF: PATHWAY-ID="CDN1", BANDWIDTH=3000000
https://cdn1.example.com/low.m3u8
#EXT-X-STREAM-INF: PATHWAY-ID="CDN2", BANDWIDTH=6000000
https://cdn2.example.com/high.m3u8
#EXT-X-STREAM-INF: PATHWAY-ID="CDN2", BANDWIDTH=3000000
https://cdn2.example.com/low.m3u8

SERVER-URI 指向 Steering Server 的地址,客户端定期(默认每 300 秒或根据 RELOAD-URI 中的参数)向这个地址请求 Steering Manifest。

Steering Manifest 是 Server 返回的 JSON 文档,结构非常简单:

{
  "VERSION": 1,
  "TTL": 300,
  "RELOAD-URI": "https://steering.example.com/api/v1/steering?bucket=3",
  "PATHWAY-PRIORITY": ["CDN1", "CDN2"]
}

客户端按 PATHWAY-PRIORITY 的顺序尝试连接:先试 CDN1,CDN1 不可用时自动降级到 CDN2。整个过程对播放器层透明——AVPlayer 在切换 Pathway 时不会中断播放,因为不同 Pathway 的变体流内容完全相同。(07:45

Pathway Cloning:动态引入新 CDN

11:00)Pathway Cloning 的前提假设是:新旧 Pathway 的结构完全相同(相同的变体流数量、相同的 codec 和分辨率组合),只是 URI 不同。在这个前提下,Server 只需告诉客户端”把 CDN1 的 URI 按以下规则修改,就得到了 CDN3 的 URI”。

Steering Manifest 中的克隆声明:

{
  "VERSION": 1,
  "PATHWAY-PRIORITY": ["CDN3", "CDN1", "CDN2"],
  "PATHWAY-CLONES": [
    {
      "BASE-ID": "CDN1",
      "ID": "CDN3",
      "URI-REPLACEMENT": {
        "HOST": "cdn3.example.com",
        "PARAMS": { "token": "abc123" }
      }
    }
  ]
}

BASE-ID 是客户端已知的 Pathway,ID 是新 Pathway 的名称,URI-REPLACEMENT 定义了替换规则。客户端把 CDN1 所有变体流的 HOST 替换为 cdn3.example.com,再加上 ?token=abc123 参数,就得到了 CDN3 的完整 URI 集合。(14:20

对于更精细的控制,可以使用 PER-VARIANT-URISPER-RENDITION-URIS。前者针对特定变体流(如 4K DV 流)指向不同的服务器,后者针对特定 rendition(如英语 AC-3 音轨):

{
  "BASE-ID": "CDN1",
  "ID": "CDN3",
  "URI-REPLACEMENT": {
    "HOST": "cdn3.example.com",
    "PER-VARIANT-URIS": {
      "video-4k-dv": "https://faster.example.com/4k.m3u8"
    },
    "PER-RENDITION-URIS": {
      "audio-en-ac3": "https://faster.example.com/ac3.m3u8"
    }
  }
}

这里用到了 STABLE-VARIANT-IDSTABLE-RENDITION-ID——在主播放列表中给每个变体流和 rendition 分配一个稳定的标识符,Steering Server 通过这个 ID 来定位具体的 URI。(16:10

Bucket-Based 全局流量调度

17:30)Bucket 机制的核心设计目标是:Server 端完全无状态,所有调度决策仅基于 bucket 编号。

工作流程:

  1. 客户端首次请求 Steering Manifest,Server 在 RELOAD-URI 中嵌入一个随机 bucket 编号(1-12)
  2. 客户端后续请求都带着这个 bucket 编号
  3. Server 根据 bucket 编号查表,返回对应的 Pathway 优先级

实现示例:

# Steering Server 伪代码
BUCKET_MAP = {
    # 50/50 流量分配
    "cdn_split_50_50": {
        range(1, 7):  ["CDN1", "CDN2"],   # buckets 1-6 优先 CDN1
        range(7, 13): ["CDN2", "CDN1"],   # buckets 7-12 优先 CDN2
    },
    # 10/30/60 三路分配
    "cdn_split_10_30_60": {
        range(1, 3):  ["CDN1", "CDN2", "CDN3"],   # buckets 1-2: 约 17%
        range(3, 6):  ["CDN2", "CDN3", "CDN1"],   # buckets 3-5: 约 25%
        range(6, 13): ["CDN3", "CDN1", "CDN2"],   # buckets 6-12: 约 58%
    }
}

def handle_steering_request(bucket, known_pathways):
    rules = get_current_rules()
    priority = rules.lookup(bucket)

    # 确定需要克隆的 Pathway
    clones = compute_clones(known_pathways, priority)

    return {
        "PATHWAY-PRIORITY": priority,
        "PATHWAY-CLONES": clones
    }

12 个 bucket 的粒度足够大多数场景。如果需要更精细的控制(比如 1% 的灰度),可以结合客户端属性(地理位置、设备类型)在 Server 端做额外判断,但 bucket 本身保持不变。(20:30

Steering Server 的决策逻辑

22:00)Server 在生成 Steering Manifest 时需要回答两个问题:

  1. 这个 bucket 的客户端应该优先走哪个 Pathway? ——查 bucket 映射表。
  2. 需要克隆哪些 Pathway? ——客户端在请求中会带上它已知的 Pathway 列表(通过 SERVER-URI 的查询参数),Server 把当前可用的全部 Pathway 减去客户端已知的,差值就是需要克隆的。
def compute_clones(known_pathways, desired_priority):
    needed = set(desired_priority)
    known = set(known_pathways)
    to_clone = needed - known

    clones = []
    for pathway_id in to_clone:
        # 选择 BASE-ID:从 known 中找一个结构相同的 Pathway
        base = find_matching_base(pathway_id, known)
        clones.append({
            "BASE-ID": base,
            "ID": pathway_id,
            "URI-REPLACEMENT": get_replacement_rules(base, pathway_id)
        })
    return clones

这种设计的好处是 Server 永远不需要知道客户端播放列表的具体内容——它只操作 Pathway ID 的集合运算,URI 替换规则是预配置的。(24:00


核心启发

  • 做什么:为你的 HLS 流媒体服务引入 Content Steering,用 Pathway 抽象替代硬编码 CDN 地址。
    为什么值得做:一旦 CDN 出现故障,Steering Server 可以实时切换所有客户端的流量路径,无需修改主播放列表或等待客户端重新加载。对于有 SLA 要求的直播和点播服务,这是一个架构层面的可靠性提升。
    怎么开始:在主播放列表中添加 #EXT-X-CONTENT-STEERING 标签,部署一个返回 Steering Manifest 的 HTTP 端点,先用静态的 PATHWAY-PRIORITY 验证客户端能正确切换 Pathway,再逐步引入动态规则。

  • 做什么:利用 Pathway Cloning 实现 CDN 的动态上下线,特别是边缘节点的快速部署。
    为什么值得做:传统方式加一个 CDN 需要修改主播放列表、等待 CDN 缓存刷新、等待所有客户端重新请求——整个过程可能长达数十分钟。Pathway Cloning 把这一步缩短到 Steering Manifest 的一次响应,客户端在下一个 reload 周期即可感知新 CDN。
    怎么开始:确保新旧 CDN 上的变体流结构完全一致(相同的 codec、分辨率、比特率组合),在 Steering Server 中为新 CDN 配置 URI-REPLACEMENT 规则,通过 PATHWAY-CLONES 字段下发。

  • 做什么:用 bucket-based 规则替代有状态的流量调度,把 Steering Server 做成纯函数。
    为什么值得做:有状态的调度需要存储和同步每个客户端的状态,带来一致性和扩展性问题。Bucket 机制把状态编码在 URL 里,Server 可以水平扩展而不需要共享存储。12 个 bucket 提供约 8.3% 的调度粒度,对大多数场景已经足够。
    怎么开始:在 Steering Manifest 的 RELOAD-URI 中嵌入随机 bucket 编号(1-12),Server 端维护一个 bucket 到 Pathway 优先级的映射表,按业务需求(灰度比例、地域分布、CDN 容量)配置映射规则。


关联 Session

评论

GitHub Issues · utterances