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-URIS 和 PER-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-ID 和 STABLE-RENDITION-ID——在主播放列表中给每个变体流和 rendition 分配一个稳定的标识符,Steering Server 通过这个 ID 来定位具体的 URI。(16:10)
Bucket-Based 全局流量调度
(17:30)Bucket 机制的核心设计目标是:Server 端完全无状态,所有调度决策仅基于 bucket 编号。
工作流程:
- 客户端首次请求 Steering Manifest,Server 在
RELOAD-URI中嵌入一个随机 bucket 编号(1-12) - 客户端后续请求都带着这个 bucket 编号
- 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 时需要回答两个问题:
- 这个 bucket 的客户端应该优先走哪个 Pathway? ——查 bucket 映射表。
- 需要克隆哪些 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
- Create a great video playback experience — 打造出色的视频播放体验,涵盖播放器 UI 设计、画中画、系统播放器集成等最佳实践。
- Explore media metadata publishing and playback interactions — 深入媒体元数据发布与播放交互,了解 NowPlaying 和 MPNowPlayingInfoCenter 的最新能力。
- Display EDR content with Core Image, Metal, and SwiftUI — 用 Core Image、Metal 和 SwiftUI 展示 EDR 内容,释放现代显示器的全部色彩和亮度范围。
- Improve DNS security for apps and servers — 提升 App 与服务器的 DNS 安全性,涵盖 DNSSEC、DNS-over-HTTPS 和服务发现最佳实践。
- Create a more responsive media app — 用 AVFoundation 构建流畅的媒体 App,减少播放中断和延迟,提升用户沉浸感。
评论
GitHub Issues · utterances