HLS 是影音產品、直播平台、課程平台很常問的前端面試題。
HLS 是什麼?
HLS 全名是 HTTP Live Streaming,是 Apple 提出的串流協定。
核心想法:
把影片切成很多小片段,再用 playlist 告訴播放器要怎麼播放。
常見檔案:
| 檔案 | 說明 |
|---|---|
.m3u8 | playlist,描述影片片段、畫質版本與播放順序 |
.ts / .m4s | 實際影片 segment |
播放器不是一次下載整支影片,而是讀取 .m3u8 後,一段一段下載 segment。
HLS 為什麼適合串流?
HLS 支援 adaptive bitrate streaming。
播放器可以根據使用者網路狀況切換畫質:
網路穩定 → 播 1080p segment
網路變慢 → 降到 720p 或 480p
網路恢復 → 再升回高畫質這樣使用者比較不容易遇到影片整個卡死。
前端怎麼播放 HLS?
Safari 原生支援 HLS,可以直接用 <video>。
Chrome / Firefox 通常會搭配 hls.js。
import Hls from "hls.js";
import { useEffect, useRef } from "react";
export function HlsPlayer({ src }: { src: string }) {
const videoRef = useRef<HTMLVideoElement | null>(null);
useEffect(() => {
const video = videoRef.current;
if (!video) return;
if (video.canPlayType("application/vnd.apple.mpegurl")) {
video.src = src;
return;
}
if (Hls.isSupported()) {
const hls = new Hls();
hls.loadSource(src);
hls.attachMedia(video);
return () => hls.destroy();
}
}, [src]);
return <video ref={videoRef} controls playsInline />;
}前端整合時要注意什麼?
播放器能顯示影片,只代表最基本的串接成功。正式產品還要處理播放狀態、瀏覽器限制、網路錯誤與存取權限。
Loading、buffering、error state
這三個狀態看起來相似,但原因不一樣:
| 狀態 | 意義 | UI 常見處理 |
|---|---|---|
| Loading | playlist 或第一個 segment 還沒準備好 | 顯示初始 loading spinner 或 skeleton |
| Buffering | 已經開始播放,但後續資料來不及下載 | 暫停進度、顯示轉圈,網路恢復後繼續播放 |
| Error | manifest、segment、解碼或權限發生錯誤 | 顯示錯誤訊息、重試按鈕或替代內容 |
前端不能只放一個永遠轉動的 spinner,否則使用者不知道是在載入、網路太慢,還是真的播放失敗。
使用原生 <video> 時,可以監聽 loadstart、waiting、playing、canplay、error 等事件。使用 hls.js 時,還能進一步區分 manifest、network、media 等錯誤類型。
<video
onLoadStart={() => setStatus("loading")}
onWaiting={() => setStatus("buffering")}
onPlaying={() => setStatus("playing")}
onError={() => setStatus("error")}
/>如果是暫時性的 network error,可以限制次數後重試;如果是 media decode error,可以嘗試恢復播放器;如果 manifest 本身不存在,就應該停止重試並顯示明確錯誤。
Autoplay policy
現代瀏覽器通常不允許網頁在沒有使用者互動時,自動播放「有聲音」的影片。
比較容易成功的 autoplay 條件是:
- 影片為
muted - 使用者曾經點擊過頁面
- 瀏覽器根據使用者習慣允許該網站自動播放
<video autoPlay muted playsInline />即使有 autoPlay,呼叫 video.play() 仍可能回傳 rejected Promise,所以前端要準備 fallback,例如顯示播放按鈕:
video.play().catch(() => {
setShowPlayButton(true);
});面試時可以說:autoplay 不是前端加上 attribute 就一定成功,而是受到瀏覽器媒體政策限制。
Mobile playsInline
playsInline 表示影片要留在目前頁面內播放,不要自動切成系統全螢幕播放器。
<video playsInline controls />它對 iPhone 等行動裝置特別重要。如果沒有設定,某些裝置或舊版瀏覽器可能在播放時直接進入全螢幕,導致頁面上的字幕、商品資訊、聊天室或自訂控制列被蓋掉。
不過 playsInline 只是允許行內播放,不代表 autoplay 一定會成功;自動播放通常仍要搭配 muted。
HLS playlist CORS
播放器可能會依序請求:
master.m3u8
→ 720p/index.m3u8
→ segment-001.ts
→ segment-002.ts
→ encryption key(如果有加密)只要這些檔案和網站不是同一個 origin,CDN 或影片伺服器就要設定正確的 CORS header。
例如:
Access-Control-Allow-Origin: https://example.com常見錯誤是只有 .m3u8 可以跨域,但 segment 或 encryption key 沒有 CORS header,結果 playlist 載入成功,影片卻仍然播不出來。
如果 request 需要 cookie 或 Authorization header,還要一起確認 credentials、preflight 和允許的 request headers。
Segment 404 或 CDN cache 問題
HLS playlist 裡記錄的是 segment URL。只要 playlist 指向不存在的檔案,播放器就會收到 404,造成卡頓或直接播放失敗。
常見原因:
- encoder 還沒成功上傳 segment,playlist 就先更新了
- segment 已被清除,但 CDN 還快取著舊 playlist
- 部署後路徑或檔名改變
- signed URL 已過期
- 不同 CDN 節點的內容尚未同步
快取策略也不能全部設成一樣:
- VOD segment 通常內容不再改變,可以設較長快取
- 直播 playlist 會持續更新,需要較短快取或正確 revalidation
- 已產生完成的 segment 通常可以長時間快取
如果直播 playlist 被 CDN 快取太久,使用者看到的就會是舊片段,直播延遲也會持續增加。
切換清晰度的 UI
HLS 通常會在 master playlist 裡提供多個畫質,例如 360p、720p、1080p。播放器預設可以使用 ABR(Adaptive Bitrate)自動選擇畫質。
前端常見選項:
- Auto:交給播放器依頻寬與 buffer 自動切換
- 360p / 720p / 1080p:讓使用者鎖定偏好的畫質
UI 上最好顯示目前選擇的是 Auto 還是固定畫質,也要考慮使用者選了 1080p,但網路無法負荷時要怎麼提示。
實務上通常保留 Auto 作為預設,因為強制高畫質容易造成 buffering;手動切換則是提供給有明確需求的使用者。
直播延遲
直播畫面不會和現場完全同步,延遲通常來自:
攝影與編碼
→ 切成 segment
→ 更新 playlist
→ CDN 傳輸
→ 播放器預先 buffer
→ 顯示畫面segment 越長、播放器預先 buffer 越多,播放通常越穩定,但延遲也越高。segment 越短可以降低延遲,代價是 request 數量、encoder 和 CDN 壓力增加,也更容易受到網路抖動影響。
如果產品要求更低延遲,可以考慮 Low-Latency HLS(LL-HLS),但 encoder、CDN 和播放器都要支援,不能只靠前端設定解決。
DRM 或 tokenized URL
付費影音不能只把 .m3u8 URL 隱藏起來,因為使用者仍可能從 Network 面板找到 playlist 和 segment。
常見保護方式:
- signed URL:URL 帶有簽章與過期時間
- signed cookie:由 cookie 控制一組影片檔案的存取權限
- tokenized URL:每個 request 要帶有效 token
- DRM:透過授權伺服器取得解密內容所需的 license
DRM 通常會搭配瀏覽器的 Encrypted Media Extensions,以及不同平台的 DRM 系統,例如 Widevine 或 FairPlay。前端除了載入影片,還要處理 license request、權限失效和裝置支援。
tokenized URL 也有一個 HLS 特有問題:影片不是只發出一個 request,而是會持續請求 playlist 和許多 segments。如果 token 在播放途中過期,後面的 segment 就可能變成 401 / 403,因此要設計合理的有效時間或更新機制。
面試時能從播放狀態一路講到 CORS、CDN、延遲和存取控制,就能表現出你理解的不只是 hls.js API,而是完整的串流播放流程。
HLS 和 MP4 直接播放差在哪?
MP4 直播放通常比較像下載一個完整影片檔,雖然也可以支援 range request,但 adaptive bitrate 和直播能力不是它的主要優勢。
HLS 則是 playlist + segments 的模式,更適合:
- 長影片
- 直播
- 多畫質切換
- CDN 分發
- 不同網路環境的播放體驗
常見追問
m3u8 是影片嗎?
不是。.m3u8 是 playlist,它描述影片片段在哪裡、有哪些畫質、片段順序是什麼。
HLS 為什麼 CDN 友善?
因為它基於 HTTP,segment 是一個個靜態檔案,CDN 可以快取與分發。
直播延遲怎麼來?
HLS 需要切 segment、產生 playlist、播放器 buffer,所以通常會有延遲。segment 越短延遲越低,但請求數與系統壓力會增加。
前端怎麼處理播放失敗?
可以監聽播放器 error,區分 network error、media error、manifest load error,必要時重試、降畫質或顯示 fallback。
面試回答模板
HLS 是基於 HTTP 的串流協定,會把影片切成多個 segment,並用 m3u8 playlist 描述播放順序和不同畫質版本。播放器可以根據網路狀態動態切換畫質,所以適合直播和長影片。前端在 Safari 可以用原生 video,其他瀏覽器常搭配 hls.js。實作時要注意 CORS、buffering、error handling、autoplay policy、CDN cache 和直播延遲。