延遲優化失敗最常見的原因是在錯的環節用力。把編碼延遲從 200 ms 砍到 80 ms,在一個播放器緩衝 18 秒的系統裡等於什麼都沒做。這一篇把延遲當成一份預算逐項拆開,標出每一項的典型大小與可壓縮程度,再談 CDN 與 ABR 怎麼跟延遲互相牽制。
📖 學
一、CDN 在直播裡實際做什麼
CDN 對直播的價值只有一個:把「一份內容被 N 個人要」變成「源站只被要一次」。
機制是分層快取。觀眾打到最近的 edge 節點,edge 沒有就往上層(mid-tier/shield)要,再沒有才回源站。對直播而言這有個特別的性質:同一個分片在同一瞬間會被成千上萬人要,所以「請求合併」(request collapsing/coalescing)比快取命中率本身更重要——沒有合併的話,一萬個同時到達的 miss 會變成一萬個回源請求,直接打爆源站。
直播的快取邏輯與點播相反。 點播的檔案永久不變、TTL 可以設很長;直播的清單檔(.m3u8/.mpd)每隔幾秒就變,TTL 必須極短(甚至 0),而分片檔一旦產生就不變、可以設長 TTL。把清單的 TTL 設錯是直播最常見的 CDN 配置錯誤:設太長,觀眾拿到舊清單、永遠落後;設太短又沒有請求合併,源站被清單請求打爆。
WebRTC 不走這套。 它需要專門的媒體邊緣(SFU),每一路都是獨立有狀態連線。一般 HTTP CDN 幫不上忙。所以混合架構裡「HTTP CDN 服務 LL-HLS、專用媒體邊緣服務 WebRTC、兩者共用控制面」是標準切法。
二、延遲預算:逐項拆開
| 環節 | 典型 | 可壓縮性 | 怎麼壓 |
|---|---|---|---|
| 擷取 + 前處理 | 20–60 ms | 低 | 換硬體、關閉不必要的濾鏡 |
| 編碼 | 50–300 ms | 高 | 關 B 幀、降 lookahead、調 preset |
| 第一哩傳輸 | 50–200 ms | 中 | SRT latency 參數、選近的接入點 |
| 轉碼 | 100–500 ms | 中 | 少階梯、硬體編碼 |
| 打包(分片累積) | 分片長度 × 1 | 高 | 縮短分片/改用 part |
| CDN 分發 | 20–100 ms | 低 | 選對 PoP |
| 播放器緩衝 | 分片長度 × 緩衝數 | 最高 | 減少緩衝分片數 |
| 解碼 + 顯示 | 20–50 ms | 低 | — |
兩個粗體項(打包與播放器緩衝)通常合計佔總延遲的 80% 以上,而且都由同一個參數主導:分片長度。
一個具體的算術:6 秒分片、播放器緩衝 3 個分片。
打包累積:6 秒(必須等整個分片編完)
播放器緩衝:6 × 3 = 18 秒
其他環節合計:約 0.5 秒
總計:約 24.5 秒
改成 2 秒分片、緩衝 3 個:
2 + 6 + 0.5 ≈ 8.5 秒
改成 LL-HLS,part 0.5 秒、緩衝約 3 個 part:
0.5 + 1.5 + 0.5 ≈ 2.5 秒
可以看到每一次下降都來自同一個地方。 這就是為什麼「延遲優化從最大的那塊開始」不是建議而是算術。
三、延遲分級與它們的成本
| 分級 | 範圍 | 技術路線 | 相對成本 | 典型場景 |
|---|---|---|---|---|
| 一般延遲 | 10–30 秒 | HLS/DASH,長分片 | 1× | 演唱會、新聞、佈道 |
| 低延遲 | 2–6 秒 | LL-HLS/LL-DASH | 1.5–3× | 體育、電競賽事 |
| 超低延遲 | < 1 秒 | WebRTC | 10× 以上 | 荷官、拍賣、雲端遊戲、連麥 |
成本不是線性上升的,原因在前兩級還能用 HTTP CDN(邊際成本極低),第三級必須換成有狀態的媒體伺服器(邊際成本隨人數線性成長)。
所以第一個該問的問題不是「能多低」,而是「業務真的需要多低」。 一場演唱會直播把延遲從 20 秒降到 2 秒沒有任何商業價值,卻可能讓成本翻倍、卡頓率上升。反過來,真人荷官的下注截止時間如果比畫面晚兩秒,整個遊戲的公平性就垮了——那裡 500 ms 是硬需求。
四、延遲與穩定性是同一個旋鈕的兩端
緩衝存在的唯一理由是吸收抖動。網路不會穩定地每 16.7 ms 送一幀過來,它會突然遲到 300 ms 再一次補三幀。緩衝就是用來填平這個的。
緩衝變小 → 延遲下降 → 抗抖動能力下降 → 卡頓率上升
緩衝變大 → 延遲上升 → 抗抖動能力上升 → 卡頓率下降
這代表「低延遲」的真正代價是卡頓率,而卡頓對 QoE 的傷害通常大於延遲。 一個 2 秒延遲但每分鐘卡一次的體驗,幾乎一定比 8 秒延遲但不卡的體驗差。做低延遲之前應該先定義「可接受的卡頓率」,再往回推能給多少緩衝,詳見 QoE 指標。
行動網路使這件事更嚴重。 行動網路的抖動分布有長尾,偶爾出現數百毫秒的停頓是常態。在行動為主的受眾上做超低延遲,卡頓率會明顯高於桌面。
五、ABR 與延遲的交互作用
ABR 讓播放器根據當下頻寬選擇碼率階梯。它與延遲有兩個互相牽制的地方:
切換粒度受 GOP 限制。 播放器只能在 I 幀處切換碼率,所以 GOP 越長、切換越遲鈍。延遲低的設定(短 GOP)反而讓 ABR 更靈敏,這是少見的雙贏。
但低緩衝讓 ABR 更難做決定。 ABR 演算法要嘛看歷史吞吐(throughput-based)、要嘛看緩衝水位(buffer-based),而緩衝很小的時候,buffer-based 演算法幾乎沒有訊號可用,只能靠吞吐估計,而吞吐估計在抖動大的網路上很不準。結果是低延遲系統的 ABR 容易震盪(頻繁上下切換,畫質忽好忽壞)。
實務對策:低延遲場景下把階梯數減少(例如從 6 階降到 3–4 階)、拉開階梯之間的距離、並設定切換的遲滯(hysteresis)避免震盪。
六、LL-HLS 的隱藏成本:請求量
分片從 6 秒變成 0.5 秒的 part,請求數變 12 倍。以十萬同時觀看為例:
6 秒分片:100,000 ÷ 6 ≈ 16,667 請求/秒
0.5 秒 part:100,000 ÷ 0.5 = 200,000 請求/秒
再加上清單請求、音軌、重試,數字還要往上。這個量級帶來兩個直接後果:
CDN 計費模式可能從頻寬主導變成請求數主導。 很多 CDN 對請求數另外計價,而低延遲把請求數放大一個量級。
每個請求如果都要回中心查權限,鑑權系統會先倒。 這是安全設計上的硬約束:邊緣必須能在本地完成驗簽與狀態檢查,不能每個媒體請求都跨區域查一次中心資料庫。這一點在 案例拆解 的容量模型裡被明確算出來。
七、常見的「延遲降不下來」診斷順序
照這個順序查,九成的案例在前三步就找到原因:
- 量 glass-to-glass,不要信設定值。 碼表對鏡頭,截圖相減。
- 拆出播放器緩衝佔多少。 多數播放器可以讀取當前 buffer 長度;如果它佔了大半,後面都不用查了。
- 確認分片/part 長度是否真的生效。 看實際產出的清單檔,不要看設定介面。
- 確認 LL-HLS 全鏈路都支援。 特別是 CDN 有沒有正確處理 blocking playlist request——很多 CDN 會把它當成逾時而切斷。
- 檢查 GOP 是否 ≤ 分片長度。 不是的話分片切不出來,實際分片會比設定長。
- 檢查是否有 B 幀。 低延遲設定裡它不該存在。
- 最後才查編碼 preset 與網路 RTT。 這兩項通常是最小的貢獻。
🧠 記
延遲是預算,打包與播放器緩衝佔 80% 以上,兩者都由分片長度主導。
延遲分級的成本不是線性的,因為次秒級必須放棄 HTTP CDN。
低延遲的真實代價是卡頓率,而卡頓對 QoE 的傷害通常大於延遲。
清單檔 TTL 設錯是最常見的 CDN 配置錯誤;請求合併比命中率更關鍵。
LL-HLS 把請求數放大一個量級,這同時是成本問題與安全架構的硬約束。
✍️ 實踐
建一張自己系統的延遲歸因表,約 40 分鐘:
- 量 glass-to-glass(碼表對鏡頭法)。
- 在播放器 console 讀出當前 buffer 秒數(HLS.js 可用
hls.media.buffered,video 元素可用video.buffered)。 - 抓一次實際的
.m3u8,看#EXT-X-TARGETDURATION與實際分片長度——確認它跟你的設定一致。 - 把各環節填進上面那張預算表,算出未解釋的殘差。
- 針對最大的那一項做一次改動,重測,確認改動幅度與預期一致。
殘差很大(例如超過總延遲 20%)通常意味著某個環節有你不知道的緩衝,那本身就是最有價值的發現。
🔗 延伸學習
- Mux — Low-Latency Live Streaming for Developers——把 LL-HLS 的四個機制(part、blocking reload、preload hint、delta playlist)解釋得最清楚的一篇。
- Apple — HTTP Live Streaming 文件——LL-HLS 是 Apple 規範的一部分,清單標籤與行為要求以這裡為準。