延遲優化失敗最常見的原因是在錯的環節用力。把編碼延遲從 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-DASH1.5–3×體育、電競賽事
超低延遲< 1 秒WebRTC10× 以上荷官、拍賣、雲端遊戲、連麥

成本不是線性上升的,原因在前兩級還能用 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 對請求數另外計價,而低延遲把請求數放大一個量級。

每個請求如果都要回中心查權限,鑑權系統會先倒。 這是安全設計上的硬約束:邊緣必須能在本地完成驗簽與狀態檢查,不能每個媒體請求都跨區域查一次中心資料庫。這一點在 案例拆解 的容量模型裡被明確算出來。

七、常見的「延遲降不下來」診斷順序

照這個順序查,九成的案例在前三步就找到原因:

  1. 量 glass-to-glass,不要信設定值。 碼表對鏡頭,截圖相減。
  2. 拆出播放器緩衝佔多少。 多數播放器可以讀取當前 buffer 長度;如果它佔了大半,後面都不用查了。
  3. 確認分片/part 長度是否真的生效。 看實際產出的清單檔,不要看設定介面。
  4. 確認 LL-HLS 全鏈路都支援。 特別是 CDN 有沒有正確處理 blocking playlist request——很多 CDN 會把它當成逾時而切斷。
  5. 檢查 GOP 是否 ≤ 分片長度。 不是的話分片切不出來,實際分片會比設定長。
  6. 檢查是否有 B 幀。 低延遲設定裡它不該存在。
  7. 最後才查編碼 preset 與網路 RTT。 這兩項通常是最小的貢獻。

🧠 記

延遲是預算,打包與播放器緩衝佔 80% 以上,兩者都由分片長度主導。

延遲分級的成本不是線性的,因為次秒級必須放棄 HTTP CDN。

低延遲的真實代價是卡頓率,而卡頓對 QoE 的傷害通常大於延遲。

清單檔 TTL 設錯是最常見的 CDN 配置錯誤;請求合併比命中率更關鍵。

LL-HLS 把請求數放大一個量級,這同時是成本問題與安全架構的硬約束。


✍️ 實踐

建一張自己系統的延遲歸因表,約 40 分鐘:

  1. 量 glass-to-glass(碼表對鏡頭法)。
  2. 在播放器 console 讀出當前 buffer 秒數(HLS.js 可用 hls.media.buffered,video 元素可用 video.buffered)。
  3. 抓一次實際的 .m3u8,看 #EXT-X-TARGETDURATION 與實際分片長度——確認它跟你的設定一致。
  4. 把各環節填進上面那張預算表,算出未解釋的殘差。
  5. 針對最大的那一項做一次改動,重測,確認改動幅度與預期一致。

殘差很大(例如超過總延遲 20%)通常意味著某個環節有你不知道的緩衝,那本身就是最有價值的發現。


🔗 延伸學習


相關