為什麼 HLS 直播會越播越慢,以及「回到直播」按鈕應該跳到哪裡
分別測量播放清單邊緣、牆上時鐘延遲、播放器安全距離、過期清單和低延遲 HLS 行為,準確診斷 HLS 直播延遲。
兩位觀眾觀看同一場 HLS 直播時,可能處在不同位置,但兩台播放器都顯示「直播中」。進度列即使已經到達最右端,也可能落後訊號來源數秒。如果「回到直播」按鈕跳到最後一個已公布的瞬間,而不是播放器支援的直播同步點,它甚至會讓播放更不穩定。
第一步是不要再把延遲當成一個數字。需要分別看訊號來源時間、播放清單可用性、播放位置與播放清單邊緣的距離、播放器選取的安全延遲,以及最終顯示時間。請只測試自己擁有或獲准檢查的串流,並從分享的記錄中移除簽章 URL、Cookie、觀眾 ID、IP 位址和私人主機名稱。
*驗證方法 — 2026 年 9 月 23 日:*我們使用 Node.js 24.12.0 對一份合成媒體播放清單執行確定性計算。該播放清單包含五個 6 秒片段,EXT-X-PROGRAM-DATE-TIME 從 UTC 16:00:00 開始。已公布視窗在 16:00:30 結束,觀察時間為 16:00:32。媒體時間為 6 的播放位置落後播放清單邊緣 24 秒,落後測試清單牆上時鐘 26 秒。採用刻意設定的 12 秒安全延遲後,受控「回到直播」目標為媒體時間 18:落後播放清單邊緣 12 秒,落後測試清單時鐘 14 秒。這只驗證腳本的播放清單計算。它沒有測量編碼器、網路、CDN、瀏覽器媒體管線、裝置時鐘、真實播放或端到端服務目標。腳本位於 scripts/test-hls-live-latency-budget.cjs。
比較之前,先替三個位置準確命名
「直播邊緣」常被用來指不同位置。應在記錄和監控面板中為每項觀察使用不同名稱。
| 位置或測量值 | 實用定義 | 證據來源 |
|---|---|---|
| 播放清單邊緣 | 目前向該用戶端公布的最新媒體結束位置 | 媒體播放清單以及片段或部分片段長度 |
| 可拖曳終點 | 媒體元素目前回報的最晚可拖曳時間軸位置 | video.seekable.end(...) |
| 直播同步點 | 播放器在邊緣之後考量安全策略選出的目標 | 播放器 API 或明確的應用程式設定 |
| 播放位置距離 | 播放清單或可拖曳邊緣減去目前播放位置 | 同一時間軸上的 currentTime 和對應邊緣 |
| 牆上時鐘延遲 | 觀察時間減去播放位置對應的估算節目時間 | 可信時鐘和一致的節目日期對映 |
| 擷取到顯示延遲 | 從訊號來源擷取到觀眾螢幕呈現 | 同步的訊號來源與顯示測量 |
這些數值不一致並不矛盾。播放器可能只落後其快取播放清單兩秒,但該播放清單本身已經遠遠落後來源站。最新播放清單可以很接近訊號來源,而觀眾可能在 DVR 視窗內暫停了十分鐘。節目日期標籤能識別內容時間,卻不能單獨證明攝影機何時擷取了某一影格。
先檢查播放器時間軸
在點選「回到直播」前後,記錄全部 seekable 範圍、目前播放時間、緩衝範圍、長度、播放速率和就緒狀態。
function mediaSnapshot(video) {
const copyRanges = ranges => Array.from(
{ length: ranges.length },
(_, index) => ({ start: ranges.start(index), end: ranges.end(index) })
);
return {
currentTime: video.currentTime,
duration: video.duration,
playbackRate: video.playbackRate,
seekable: copyRanges(video.seekable),
buffered: copyRanges(video.buffered),
readyState: video.readyState,
};
}
不要用 duration 取代目前直播邊緣。MDN 指出,直播媒體可能失去對過期內容的存取,而設定 currentTime 後,實際位置仍可能被調整到媒體支援的位置。如果存在多個可拖曳範圍,應全部記錄,不要假定最後一個範圍能代表所有軌道和呈現版本。
如果要求的位置已經離開直播視窗,請使用 HLS 拖曳和 DVR 診斷指南。只有在目前時間軸已經明確後,才開始診斷延遲。
為什麼一般 HLS 會落後最後一個已公布片段
直播用戶端需要保留足夠媒體,才能承受正常的播放清單重新載入和下載波動。RFC 8216 建議:對尚未結束的播放清單,開始一般播放時不要選擇起點距離尾端不足三個目標長度的片段,因為起得太靠後可能導致停頓。這是協定層級的安全建議,並不是每台播放器都會精確保持三個片段延遲的承諾。
片段產生、播放清單發布、從來源站傳播到 CDN、播放清單重新載入時機、物件下載、緩衝策略、解碼和算繪都會造成延遲。其中一些可以重疊,另一些則會累積。只改變播放器起點,無法消除媒體出現在播放清單之前已經花掉的時間。
| 層級 | 用來隔離它的證據 | 常見錯誤結論 |
|---|---|---|
| 擷取和編碼 | 畫面內時間碼或獨立觀察到的訊號來源時間 | EXTINF 能說明擷取延遲 |
| 封裝和發布 | 片段/部分片段完成時間及播放清單可用時間 | CDN 回應時間等於封裝時間 |
| CDN 和快取 | Age、快取狀態、最終 URL、重複的播放清單本文 | HTTP 200 代表取得最新播放清單 |
| 播放器載入 | 播放清單/片段要求時序和所選起點 | 進度列右端就是訊號來源邊緣 |
| 緩衝和復原 | 緩衝範圍、停頓、播放速率變化 | 每一秒額外延遲都是刻意保留的安全餘量 |
| 解碼和算繪 | 媒體事件加裝置層級測量 | 只憑 currentTime 就能得到擷取到顯示延遲 |
先測量第一個尚未被證實的層級。如果連續多次要求的播放清單本文,在預期更新週期之外仍完全相同,應先診斷過期交付,再調整播放器。我們的過期播放清單與遺失片段指南提供了判斷路徑。
計算兩種延遲,而不是一種
不需要牆上時鐘中繼資料,就能算出播放位置與播放清單邊緣的距離:
播放位置距離 = 播放清單時間軸終點 - 目前播放位置
這有助於控制播放器,但無法說明播放清單邊緣本身有多舊。如果有一致的 EXT-X-PROGRAM-DATE-TIME 對映和可信時鐘,再估算第二部分:
播放清單邊緣年齡 = 觀察時間 - 播放清單邊緣對應的節目時間
估算牆上時鐘延遲 = 播放清單邊緣年齡 + 播放位置距離
我們的測試清單把 2 秒的播放清單邊緣年齡與 24 秒的播放位置距離分開,受控時間軸上的總和為 26 秒。這些數字是測試輸入和輸出,不是對 M3U8Online 或正式環境廣播服務的測量。
RFC 8216 將 EXT-X-PROGRAM-DATE-TIME 定義為片段第一個取樣與絕對日期時間之間的對映,同時提醒播放清單日期可以表示內容產生時間,也可以表示與播放無關的其他時間。發布延遲指標前,請驗證時間戳記的實際含義、時區處理、時鐘同步,以及變體和不連續點之間的對映一致性。
「回到直播」應跳到同步點,而不是數學終點
可用時,可靠的「回到直播」操作應使用播放器文件明確說明的直播同步點。在 hls.js 中,liveSyncPosition 是直播邊緣減去所設定的安全延遲;maxLatency 則描述播放器可以向前跳到該同步點的延遲門檻。因此,播放器 API 比從其他服務照搬的固定減法更可靠。
如果播放路徑沒有公開直播同步目標,請在目前可拖曳範圍內定義符合產品要求的目標,並在支援的裝置上驗證。不要簡單執行 currentTime = seekable.end(last),然後假定最後一個已公布瞬間已經可以下載和解碼。
按鈕流程應該:
- 擷取目前可拖曳範圍和播放器延遲值。
- 選擇文件明確的播放器同步點,或經過驗證、位於最新可拖曳終點之前的目標。
- 將目標限制在目前可拖曳範圍內。
- 只要求一次拖曳,不要與播放器內建的延遲控制器對抗。
- 記錄
seeking、seeked、結果currentTime以及操作後的第一批要求。 - 確認結果後再更新按鈕狀態。
在確定性測試清單中,所選目標仍落後播放清單邊緣 12 秒。跳到該位置後,模型牆上時鐘延遲從 26 秒降到 14 秒,並沒有變為零。這正是保留 12 秒安全延遲和 2 秒邊緣年齡的預期結果。
明確「直播中」標記代表什麼
「直播中」是需要書面規定容差的產品決策。它可以表示觀眾接近播放器目標、接近播放清單邊緣,或接近可信節目時鐘。這三種承諾並不相同。
| 介面狀態 | 範例判斷條件 | 使用者操作 |
|---|---|---|
| 直播中 | 播放位置在直播同步點的驗證容差內 | 無需操作 |
| 落後直播 | 目前位置有效,但超出該容差 | 提供「回到直播」 |
| 已暫停 | 播放已經暫停,即使剛才仍在直播位置 | 分別提供繼續播放和回到直播 |
| 正在重新加入 | 播放器正在載入校正後的目標 | 顯示進度,暫不宣稱直播中 |
| 無可靠時鐘 | 已知邊緣距離,但不知道牆上時鐘延遲 | 不要顯示憑空編出的精確延遲 |
請提供無障礙文字標籤,不要只用紅點。如果觀眾主動在 DVR 視窗內回看,不要自動把他們拉回前方,除非產品明確承諾低延遲觀看並清楚說明這種行為。
診斷播放過程中持續增加的延遲
如果播放器開始時接近目標,後來卻越來越落後,請把偏離開始的時間與以下觀察進行比對:
- 播放清單重新載入遲到、內容不變,或來自非預期的舊快取。
- 片段下載時間接近或超過媒體長度。
- 緩衝復原時從較舊位置繼續,而不是直播同步點。
- 追趕策略改變播放速率後,速率一直低於 1。
- 行動裝置分頁進入背景或應用程式暫停後,一般載入停止。
- 廣告時段、時間戳記不連續、軌道切換或變體切換改變了時間軸。
- 應用程式狀態反覆把舊的
currentTime寫回播放器。 - 設定的最大延遲校正從未觸發,或與自訂程式碼衝突。
把播放清單、網路、播放器和媒體元素事件記錄在同一條單調時間軸上。如果串流反覆停頓,請先修復交付問題,再縮小緩衝;HLS 緩衝診斷會分別檢查傳輸量、片段可用性、解碼和播放器狀態。
低延遲 HLS 是一種端到端模式
不能透過替播放清單改名或加入一個查詢參數來啟用低延遲 HLS。Apple 文件把部分片段、伺服器控制、阻塞式播放清單重新載入、預先載入提示、呈現版本報告,以及特定伺服器/CDN 行為視為協同運作的組成部分。目前的編寫指南還對 Part Target Duration 和 PART-HOLD-BACK 提出限制。
應把編碼器或封裝器、來源站、CDN 快取行為、播放清單回應、播放器支援和備援路徑一起驗證。不支援低延遲功能的用戶端可能採用不同的要求模式;即使清單含有低延遲標籤,錯誤緩衝或快取播放清單要求的 CDN 也會抵消預期效益。
不要只根據 PART-TARGET 宣傳延遲數字。使用同步時鐘和支援的正式環境路徑,測量真實的訊號來源到顯示行為。未測試的瀏覽器和裝置必須標為未測試。
執行受控測試矩陣
對每條支援的播放路徑記錄:
- 原生 HLS 與 JavaScript/MSE(適用時兩者都測)。
- 標準直播與真實的低延遲 HLS 設定。
- 冷啟動、正常穩定播放、回到直播、暫停/繼續、DVR 回看,以及停頓後復原。
- 最新來源站回應、CDN 命中和刻意合成的舊播放清單測試。
- 低位元率和高位元率變體,以及替代音訊和字幕。
- 實體行動裝置上的前景播放和背景復原。
- 在正式支援的桌上型電腦、手機、平板和電視產品上播放同一條獲准測試的串流。
縮小桌面視窗並不等於測試行動裝置背景策略或解碼器。應如實記錄缺少的硬體。簽章媒體 URL 和觀眾識別碼不得出現在螢幕截圖與錯誤報告中。
用證據報告延遲預算
可執行的報告可以寫成:「UTC 16:00:32 時,最新公布的節目時間為 16:00:30。播放位置對映到 16:00:06,因此播放清單邊緣年齡為 2 秒,播放位置距離為 24 秒。點選『回到直播』後,要求了媒體時間 18 的播放器同步點,並在該位置完成。」這樣,封裝、CDN 和播放器負責人就能看清每一部分延遲屬於哪個層級。
報告中應包含時鐘來源與同步方式、節目日期語意、播放清單要求和快取資料、播放清單邊緣、可拖曳範圍、目前位置、播放器直播同步和最大延遲設定、校正後的第一批要求、停頓與播放速率變化,以及裝置/播放器版本。如果時間戳記對映或時鐘未經驗證,就不要提供牆上時鐘延遲。
主要參考資料
- RFC 8216:HTTP Live Streaming
- Apple:啟用低延遲 HLS
- Apple:Apple 裝置 HLS 編寫規範
- Apple:HTTP Live Streaming 概覽
- hls.js API:latency、liveSyncPosition、targetLatency 和 maxLatency
- MDN:HTMLMediaElement currentTime
請分別測量播放清單邊緣、邊緣年齡和播放位置距離,再讓「回到直播」跳到經過測試的同步點,而不是最後一個已公布瞬間。這樣既能保留安全策略,又能揭露過期交付,並防止介面承諾一個從未真正測量的延遲數字。