旋轉手機會讓 HLS 播放重新開始嗎?排查尺寸變化、全螢幕與播放器重建
區分手機方向變化造成的正常版面調整與真正的 HLS 重新載入;只有播放器確實需要重建時,才安全地還原播放狀態。
手機轉成橫向時,如果 HLS 影片暫停、跳播或重新載入,光憑旋轉動作還無法判斷原因。響應式版面可能只是改變尺寸;應用程式可能換掉了 <video> 元素;進入全螢幕也可能觸發另一種狀態變化;頁面也可能真的重新載入了媒體來源。
先確認究竟發生了什麼。比較直向與橫向時,盡量維持同一個獲授權測試串流、播放器版本、瀏覽器、播放位置與網路。不要只因旋轉一次手機就認定是瀏覽器缺陷,也不要在直播中不加判斷地還原舊時間戳。
驗證方法 — 2026 年 9 月 28 日: 我們查閱 MDN 關於 Screen Orientation API、視窗方向媒體查詢、HTMLMediaElement.currentTime、play() 與全螢幕的文件,也執行了確定性的 Node.js 測試夾具,涵蓋合成的尺寸變化、元件重建與狀態還原情境。這只代表文件查核與夾具邏輯測試:沒有測試實體手機、特定行動版 HLS 引擎或實際串流。夾具:scripts/test-hls-orientation-fixture.cjs。
區分版面變化與播放器重新啟動
不同訊號通常指向不同層面:
| 觀察到的情況 | 可能代表什麼 | 接下來要檢查什麼 |
|---|---|---|
| 播放器尺寸改變,但媒體時間持續前進 | 響應式版面或影片尺寸調整 | CSS/版面與 resize 處理邏輯 |
screen.orientation 觸發 change 事件 | 瀏覽器回報方向變化 | 檢查事件處理器是否也改動播放器狀態 |
| 影片節點或播放器執行個體 ID 改變 | 框架或應用程式重新掛載播放器 | 元件 key、條件式呈現、路由狀態 |
| 重新請求了播放清單,媒體時間也接近歸零 | 媒體來源重新載入或播放器重新初始化 | src 指派、播放器 load() 呼叫、effect 相依項目 |
| 播放暫停,但沒有新的播放清單請求 | 暫停策略、可見度處理、瀏覽器行為或使用者操作 | 記錄 paused、頁面可見度、全螢幕與媒體事件 |
CSS 的 orientation 媒體特性描述的是視窗形狀,並不能證明某個實體裝置感測器事件導致播放重新啟動。JavaScript 確實需要方向通知時,在支援的環境中優先使用 Screen Orientation API 的 change 事件,而非已棄用的 orientationchange。通常只要調整容器尺寸,不必重建影片元素。
修改程式碼前,先替播放器加入診斷記錄
進行簡短且獲授權的測試時,記錄帶有時間戳記的事件序列:
screen.orientation.type以及視窗寬高(若環境支援)。- 穩定的應用程式播放器執行個體 ID,以及實際
<video>節點是否更換。 currentSrc或已遮蔽資訊的來源識別、currentTime、paused、readyState與networkState。resize、visibilitychange、fullscreenchange、pause、playing、waiting、emptied、loadstart與loadedmetadata事件。- 是否重新請求了播放清單或媒體。只記錄已遮蔽資訊的主機/路徑類別;切勿儲存簽章查詢參數、Cookie 或授權要求標頭。
旋轉前記錄一次,等版面穩定後再記錄一次。如果影片節點與媒體來源都沒變,也沒有新的載入流程,就先調查版面或緩衝,不要急著歸因為播放器重啟。如果節點變了,找出替換它的呈現分支;如果媒體來源重新指派,便沿著對應程式碼路徑追查。
分開測試旋轉、全螢幕與頁面重新載入
用小型測試矩陣逐項排查,不要反覆旋轉再憑感覺判斷:
| 測試 | 只改變什麼 | 比較哪些證據 |
|---|---|---|
| 播放時從直向轉為橫向 | 視窗/裝置方向 | 節點識別、媒體時間、新請求、暫停狀態 |
| 暫停時從橫向轉為直向 | 保持暫停意圖,只改變方向 | 播放器是否意外開始播放或跳轉 |
| 不旋轉裝置,只進出全螢幕 | 全螢幕狀態 | 全螢幕 Promise/事件與媒體來源連續性 |
| 調整桌面瀏覽器視窗大小 | 版面尺寸 | 應用程式邏輯與行動裝置是否有不同反應 |
| 明確重新載入頁面 | 文件生命週期 | 新播放器執行個體與新的媒體來源請求是否符合預期 |
不要把模擬器或桌面瀏覽器的視窗模擬描述成實體裝置測試。如果你無法接觸出問題的手機/瀏覽器組合,應註明僅查核文件或尚未測試,並向回報者索取瀏覽器版本、作業系統版本、裝置類別及已遮蔽資訊的事件記錄。
盡可能維持媒體元素穩定
讓 CSS 調整播放器外框尺寸,同時保留同一個媒體元素與 HLS 執行個體。避免把方向相關值設為框架 key,或放進會讓播放器元件卸載的條件分支。除非引擎有明確記載的限制,否則也不要在每次 resize 時重建播放器。
確實無法避免拆除播放器時,只保存能安全還原的狀態:媒體來源識別、原先是否暫停、支援時的音軌/畫質偏好,以及最近觀察到的媒體時間。重新掛載媒體來源後,先等待中繼資料與可 seek 範圍,再確認目標時間是否仍有效。currentTime 是 seek 位置,不是直播內容的永久識別;播放器重建期間,直播 DVR 視窗可能已移動或過期。依產品預期,將目標限制在目前可用範圍,或回到直播邊緣。
只有在先前狀態與產品行為確實適合續播時,才呼叫 play()。它回傳的 Promise 可能遭拒絕,因此要處理失敗,並保留可用的播放按鈕。進入全螢幕同樣是非同步操作,也可能遭拒絕;應根據實際全螢幕事件更新介面,不要假設要求一定成功。
常見錯誤
- 每次
resize都重建播放器: 旋轉及瀏覽器工具列變化會觸發版面事件,但不表示媒體來源需要重新載入。 - 使用會變動的元件 key: 綁定寬度/方向的 key 可能主動替換整個影片子樹。
- 只監聽舊方向事件: 支援時使用現代 Screen Orientation API;若不需要方向資訊,也要保留合宜的降級處理。
- 無條件 seek 到直播的舊時間: 目前直播視窗可能已不再包含該位置。
- 呼叫
play()卻不處理 Promise: 使用者手勢要求或瀏覽器策略可能阻止自動恢復播放。 - 假設是全螢幕造成旋轉: 分開測試進出全螢幕與方向變化。
- 直接分享完整要求記錄: URL 查詢參數與標頭可能含有憑證,分享前務必遮蔽。
簡潔的錯誤回報
回報裝置與作業系統、瀏覽器/版本、播放器/引擎版本、串流類型(VOD 或直播/DVR)、開始時的播放狀態與精確測試步驟。說明影片節點或播放器執行個體是否更換、播放清單請求是否重新開始,以及 currentTime、paused 或可 seek 範圍是否改變。請遮蔽串流 URL、金鑰、權杖、Cookie 與私人主機名稱。如果只是根據文件推論、沒有在指定裝置重現,也要清楚說明。
也可參考行動裝置 HLS 播放指南、應用程式切至背景或鎖定螢幕後的 HLS 播放排查,以及自動播放遭封鎖時的診斷方法。
第一手參考資料
- MDN:ScreenOrientation 的
change事件 - MDN:管理螢幕方向
- MDN:
HTMLMediaElement.currentTime - MDN:
HTMLMediaElement.play() - MDN:
Element.requestFullscreen() - MDN:已棄用的
orientationchange事件
關鍵不是「直向還是橫向」,而是「只有版面改變,還是播放器/媒體來源的生命週期也變了」。先確認這條界線,再判斷是否需要還原播放狀態。