ブログに戻る

HLS ストリーミングの仕組み

HLS、M3U8 プレイリスト、動画セグメント、Safari のネイティブ再生、HLS.js、ブラウザでのテスト観点を実務寄りに整理します。

HLS は、かなり現実的な HTTP の使い方

HLS は HTTP Live Streaming の略です。Apple が 2009 年に作りました。もともとは iPhone や Safari で動画を安定して配信するための仕組みで、専用プロトコルよりも、普通の HTTP と小さなファイルの組み合わせを選んでいます。

プレーヤーはまずテキストのプレイリストを読み、そこに書かれた動画セグメントを順番に取りに行きます。ライブ配信なら、プレイリストが少しずつ更新されます。VOD なら、最後まで並んだリストを読み進めます。

Anoiona Tools の M3U8 プレーヤーに URL を貼ると、最初に問題になるのもここです。その M3U8 をブラウザが読めるか。そこに書かれたセグメントへアクセスできるか。動画のデコードより前で止まっているケースはかなり多いです。

M3U8 は動画本体ではなく案内板

M3U8 は UTF-8 のプレイリストです。先頭に #EXTM3U があり、#EXT-X- で始まるタグがストリームの情報を持ちます。

よく見るのは master playlist です。これは複数の品質をまとめる入口になります。

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-STREAM-INF:BANDWIDTH=900000,RESOLUTION=640x360
360p/index.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=2800000,RESOLUTION=1280x720
720p/index.m3u8

もう一段進むと media playlist があります。こちらは実際のセグメントを並べます。

#EXTM3U
#EXT-X-TARGETDURATION:6
#EXT-X-MEDIA-SEQUENCE:1200
#EXTINF:6.000,
segment-1200.ts
#EXTINF:6.000,
segment-1201.ts

VOD では最後に #EXT-X-ENDLIST が入ることが多いです。ライブでは入らず、プレーヤーが数秒おきに同じ media playlist を読み直します。#EXT-X-MEDIA-SEQUENCE が増えていれば新しいセグメントが出ています。止まっていれば、配信側が更新できていないかもしれません。

セグメントが実際の映像を運ぶ

M3U8 は地図で、映像データはセグメントにあります。昔から多いのは .ts の MPEG-TS セグメントです。最近の構成では fMP4 を使い、初期化セグメントと .m4s のチャンクに分かれることもあります。

セグメント長は体感に響きます。6 秒前後はよくある設定です。短くするとライブ遅延を下げやすい一方で、HTTP リクエストが増えます。長くすると CDN には優しいことがありますが、品質切り替えやライブ追従は鈍くなります。

テストでは、プレイリストが読めるだけで安心しないほうがいいです。セグメントだけ CORS で止まる、署名 URL が期限切れになる、Cookie や Referer が必要になる、ブラウザがコーデックを扱えない。こういう失敗は普通に起きます。

Safari と HLS.js の役割分担

Safari と iOS のブラウザは HLS をネイティブに扱えます。つまり、<video> に M3U8 URL を直接入れるだけで再生できることがあります。

const video = document.querySelector("video");
video.src = "https://example.com/live/master.m3u8";
await video.play();

これは素直で速い方法です。ブラウザ内部の HLS 実装に任せられるので、余計な JavaScript の処理が少なく済みます。ただし、細かな品質制御や診断情報は取りにくくなります。

Chrome、Edge、Firefox のデスクトップ環境では、M3U8 をそのまま再生できないことがよくあります。そこで HLS.js を使います。HLS.js は playlist と segment を読み、必要に応じてメディアを変換し、Media Source Extensions 経由で video 要素に渡します。

import Hls from "hls.js";

const hls = new Hls();
hls.loadSource("https://example.com/live/master.m3u8");
hls.attachMedia(video);

Anoiona Tools の M3U8 プレーヤーもこの考え方です。Safari のようにネイティブ HLS が使える場合は直接再生します。そうでない場合は HLS.js を使い、manifest の解析結果やエラーを見ながら状態を表示します。

プレーヤー側で見ている細かいところ

入力された URL は HTTP または HTTPS か確認します。再生が始まると、ローカルの履歴、共有リンク、iframe 用コードをブラウザ内で作ります。履歴は localStorage なので、サーバーに再生リストを保存する設計ではありません。

master playlist に複数の variant があれば、品質選択を表示します。Auto と具体的な解像度、ビットレートを選べる形です。単一の media playlist だけなら選ぶものがないため、UI は出しません。

もう少し低いレイヤーでは、MPEG-TS の修復リトライもあります。一部の .ts セグメントは、先頭に余計なバイトが混ざっていて、TS パケットの同期バイト 0x47 が期待位置に来ません。標準再生が致命的な media/parsing エラーになったとき、プレーヤーは一度だけ、先頭付近を走査して 188 バイト周期の同期位置を探すローダーで再試行します。壊れた配信を何でも直すものではありませんが、テスト用の粗いストリームでは役に立つ場面があります。

実際にテストするときの見方

最初は M3U8 を直接確認します。

curl -I "https://example.com/live/master.m3u8"
curl "https://example.com/live/master.m3u8" | head

200 が返り、本文が #EXTM3U で始まるかを見ます。相対パスがある場合は、Web ページの URL ではなく、M3U8 ファイル自身の場所を基準に解決されます。

次に、media playlist に書かれたセグメントをひとつ選んで確認します。

curl -I "https://example.com/live/720p/segment-1201.ts"

ここで 403404、ログインページへのリダイレクト、CORS エラーが出るなら、プレーヤーの前に配信条件を直す必要があります。ブラウザの DevTools では m3u8tsm4s で Network を絞ると、どのリクエストが止まっているか見つけやすいです。

ライブ配信では、数回リロードして media sequence が進むかも見ます。進まないライブ playlist は、プレーヤーから見ると待つしかありません。

HLS は仕組みとしては扱いやすいです。ただ、ひとつの URL ではなく、プレイリスト、セグメント、レスポンスヘッダー、コーデックの連鎖で動いています。どこで切れているかを分けて見ると、原因はかなり絞れます。