播放器如何在 4G/5G/WiFi 之间无缝切换画质?五种经典 ABR 算法详解。
假设你在地铁上看剧,网络在 4G / 弱 4G / 5G 之间频繁切换。
- 如果 APP 锁定 1080p 不变 → 网差时会卡到转圈圈。
- 如果 APP 锁定 360p 不变 → 网好时画面糊得心疼。
理想状态:APP 能自己根据网速和缓冲情况切换清晰度,让你不间断看完。
这就是 ABR(Adaptive Bitrate)自适应码率 要做的事。
这个循环每 1-4 秒跑一次(每下完一片触发)。
- 最近吞吐量(Throughput):最近几片的实际下载速度
- 缓冲占用(Buffer Level):播放器已缓存但还没播的秒数
- 当前码率(Current Bitrate)
- 可选码率集合(Manifest 列出的 bitrate ladder)
最直觉的做法:
- 最近下载速度 5 Mbps
- × 0.8 = 4 Mbps
- 在 360p(0.5) / 720p(2.5) / 1080p(5) / 4K(15) 中选 ≤ 4 Mbps 的最高档 → 720p
| 维度 | 分析 |
|---|---|
| 优点 | 简单直观,实现成本低 |
| 缺点 | HTTP/TCP 层测速噪声大(慢启动、队头阻塞、TLS 握手…);频繁切换,画面一会儿清一会儿糊 |
| 代表实现 | 早期 hls.js 的 bandwidthFraction 策略 |
另一种思路:不管网速,只看缓冲区还有多少秒。
代表方案:BBA(Buffer-Based Adaptation),Huang 等人在 Stanford / Netflix 2014 年 SIGCOMM 论文 A Buffer-Based Approach to Rate Adaptation 提出。
| 维度 | 分析 |
|---|---|
| 优点 | 对网速噪声免疫(不看网速);稳定,不频繁切换 |
| 缺点 | 启动阶段缓冲不足时很激进地降档;没充分利用带宽(网好时可能选档偏低) |
BOLA = Buffer Occupancy based Lyapunov Algorithm
- 作者:Spiteri、Urgaonkar、Sitaraman
- 2016 年 INFOCOM 论文 BOLA: Near-Optimal Bitrate Adaptation for Online Videos
- dash.js 默认算法之一
- 理论可证接近最优
- 仅看缓冲,无需精确测网速
- 已在 dash.js 生产环境跑了 10+ 年
dash.js 把 BOLA 和 ThroughputRule 动态组合:
- 缓冲很低(启动时):用 ThroughputRule
- 缓冲充足:用 BOLA
MPC(Model Predictive Control)
- 作者:Yin, Jindal, Sekar, Sinopoli
- 2015 年 SIGCOMM 论文 A Control-Theoretic Approach for Dynamic Adaptive Video Streaming over HTTP
与其"只看当下决定",不如预测未来几秒的网速,联合优化未来 K 个切片的选择:
常用做法:
- Harmonic Mean(调和平均):1 / mean(1/throughput_i) —— 比算术平均对慢样本更敏感
- EWMA(指数加权移动平均)
- 比 BOLA 更智能(看未来)
- 计算量稍大
- 网速预测不准时容易翻车
Pensieve
- 作者:Mao, Netravali, Alizadeh (MIT)
- 2017 年 SIGCOMM Neural Adaptive Video Streaming with Pensieve
用强化学习(A3C 算法)训练神经网络做 ABR 决策。
| 维度 | 内容 |
|---|---|
| 输入特征 | 过去 K 秒吞吐量、当前缓冲、上一个码率、剩余切片数量、Manifest 里各档码率 |
| 输出 | 下一个切片的码率选择 |
| 训练 | 在海量真实/模拟网络轨迹上跑仿真,神经网络自己学出最优策略 |
在特定测试集上超越 BOLA 和 MPC。但——
- 泛化性:训练时没见过的网络模式下表现不一定好
- 可解释性差:出问题不好调
- 需要持续 online fine-tune
- 头部公司(Netflix、YouTube)自研了类似的学习型 ABR
- 中小平台用 BOLA 或 MPC 已经足够
- 客户端侧用类似 BBA 的策略
- 服务端给 hint(例如"当前 CDN 负载高,建议你降档")
- 结合 VMAF 决定 bitrate ladder
- 自研,结合"用户观看时长预测"做联合优化
- 复杂的 A/B 测试体系
- 直接用开源播放器(hls.js、Shaka Player、ExoPlayer)自带的 ABR
- 参数调优:maxBufferLength、bandwidthFraction、码率档位设置
和长视频不同,短剧/短视频场景 ABR 需求变了:
- 首帧时间(TTFF)最重要 ——用户滑动 300ms 没画面就走了
- 单集短:完播 90 秒,ABR 可能还没来得及向上切就结束了
- 预载多集:可能同时下载 N+1、N+2 的切片
- 启动档位更低(保证快速起播)
- 向上切换更慢(防止刚起播就变清导致下载卡顿)
- 预载时下低档(省流量)
- 正在播放的下高档(画质优先)
大概率是:
1. 网速实测下降(4G 转弱信号、家里路由器切换了信道)
2. CDN 边缘节点出问题(换到另一个节点时慢)
3. 设备热控(手机过热,降频导致解码跟不上)
- 启动策略太保守
- 测速累计样本还是慢的
- Manifest 里没有高档位(转码时没生成 1080p)
- 码率档位设得太密(每档差距小)
- 算法对吞吐噪声敏感(用 Throughput-based 但没滤波)
- 解决:码率档位间距拉大到 1.5x 左右、加切换惩罚(switching penalty)、用 BOLA 替代 Throughput-based
Per-title encoding 就是要给每部片子定制最优 ladder。
| 档位 | 分辨率 | H.264 码率 | H.265 码率 |
|---|---|---|---|
| 1 | 360p | 400 kbps | 250 kbps |
| 2 | 480p | 800 kbps | 500 kbps |
| 3 | 720p | 2.0 Mbps | 1.2 Mbps |
| 4 | 1080p | 4.5 Mbps | 2.8 Mbps |
| 5 | 4K | 15 Mbps | 9 Mbps |
1. 打开 https://hls-js.netlify.app/demo/
2. 粘贴一个 m3u8 URL(或用默认的 Apple 测试流)
3. 按 F12 打开 DevTools → Network 面板
4. 播放,你能看到每个 .m4s / .ts 切片的下载时间、大小
5. Network 面板打开 Throttling 选成 "Slow 3G",观察 hls.js 自动降档
hls.js Stats 面板会显示:
- bandwidthEstimate:当前带宽估计
- level:当前档位 index
- nextAutoLevel:下一个切片会用哪档
| 算法 | 核心依据 | 优势 | 劣势 | 代表 |
|---|---|---|---|---|
| Throughput | 网速 | 简单直观 | 噪声大、频繁切换 | 早期 hls.js |
| BBA | 缓冲区 | 稳定、抗噪声 | 启动慢、利用率低 | Netflix 客户端 |
| BOLA | 缓冲+优化 | 理论最优、生产可用 | 需与 Throughput 组合 | dash.js 默认 |
| MPC | 预测未来 | 前瞻性强 | 预测不准就翻车 | 学术论文 |
| Pensieve | AI 学习 | 特定场景超越其他 | 泛化差、不可解释 | Netflix/YouTube 自研 |