- 技術 💻
m3u8とは|中身の見方・再生方法・MP4変換とエラーの直し方を実例で解説
結論
m3u8とは、動画を細かく分けて配信するHLSの「再生リスト」のテキストファイルです。動画の本体は、リストに書かれた .ts などの別ファイルです。
この記事でわかること
- m3u8のタグ16種の早見表と、実物の中身の読み方
- VLC・Safari・hls.jsでの再生方法とMP4にまとめる手順
- 再生できないときのエラー原文11例と直し方
プレーヤーは、まず m3u8 を読み、そこに書かれた順番どおりに短い動画ファイルを取ってきて、つなげて再生します。
flowchart TD
P[プレーヤー<br>Safari・VLC・hls.js] -->|1 読む| M[master.m3u8<br>画質の一覧]
M -->|2 画質を選ぶ| V[720p/index.m3u8<br>区切りの一覧]
V -->|3 順に取る| S[seg_000.ts<br>seg_001.ts …<br>動画の本体]
S -->|4 つないで再生| P
急いでいる人向けに、よく調べられることの答えを先にまとめます。
| 知りたいこと | 答え | 詳しい節 |
|---|---|---|
| m3u8 とは | HLS の再生リスト。中身は文字だけ | m3u8とは |
| 何で再生する? | VLC・Safari・hls.js を使ったページ | m3u8の開き方・再生方法 |
| ダブルクリックで見られない | m3u8 は動画ではなく一覧。URL で開く | m3u8の開き方・再生方法 |
| MP4 にしたい(自分の動画) | ffmpeg -i index.m3u8 -c copy out.mp4 |
自分のm3u8をMP4にまとめる |
| 再生できない | CORS・404・コーデックの順に見る | 再生できないときのエラーと直し方 |
| m3u との違い | m3u8 は文字コードが UTF-8 の M3U | m3uとm3u8の違い |
| 中身の意味 | #EXTINF は区切り1つの秒数 など |
m3u8のタグ早見表 |
| 作り方 | ffmpeg の -f hls で作れる |
ffmpegでm3u8と.tsを作る |
確認環境:Ubuntu 24.04.4 LTS。FFmpeg 6.1.1(Ubuntu の apt パッケージ 6.1.1-3ubuntu5)の testsrc2(試験用の映像を作る機能)と sine(正弦波の音)で、1280×720・30fps・20秒の H.264+AAC の MP4(7,140,429 バイト)を作り、これを元に HLS(m3u8 と .ts・fMP4)を1画質・3画質・ライブ形式で作りました。127.0.0.1 だけで待ち受ける HTTP サーバー(Python 3.11.15 の http.server と nginx 1.24.0)から配り、Chromium 141.0.7390.37(Playwright 1.56.0 で操作)と hls.js 1.7.3 で再生・エラーを確かめ、ffmpeg で MP4 にまとめました。m3u8 の決まりは RFC 8216 と改訂案(draft-pantos-hls-rfc8216bis-22)、Apple 機器向けの推奨値は Apple の HLS Authoring Specification と HLS の開発者向けドキュメント、hls.js の設定とエラー名は公式の README と API.md、VLC のメニュー名は VLC 3.0 のソースと日本語訳ファイル、ブラウザのコーデックは Chromium プロジェクトと MDN、Android は Android Developers で確かめました(確認日 2026年10月23日)。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「m3u8とは|中身の見方・再生方法・MP4変換とエラーの直し方を実例で解説」
m3u8とは(意味・読み方・「8」の意味)
この節では、m3u8 ファイルが何のファイルで、なぜそれだけでは動画が見られないのかが分かります。
m3u8 は、HLS(HTTP Live Streaming)という動画配信の仕組みで使う「再生リスト(プレイリスト)」のファイルです。 読み方は「エムスリーユーエイト」です。HLS は Apple が作った配信の方式で、動画を数秒ずつの短いファイルに分け、ふつうの Web サーバーから HTTP で配ります。m3u8 には「どのファイルを、どの順番で、何秒ずつ再生するか」が書かれています。
1中身は文字だけ
メモ帳で開ける数百バイトのテキスト。この記事の見本は 258 バイトだった。
2動画の本体は別
本体は .ts や .m4s の短いファイル。m3u8 だけを持っていても再生できない。
38 は UTF-8 の 8
文字コードを UTF-8 に決めた M3U。音楽の再生リスト M3U から生まれた。
名前の「8」について、HLS の初期の仕様案(draft-pantos-http-live-streaming-07、2011年9月30日)は、名前が .m3u8 で終わるファイルは UTF-8、.m3u で終わるファイルは US-ASCII で書かれていると決めていました。2017年8月の RFC 8216 では、HLS の再生リストは .m3u8 でも .m3u でも UTF-8 で書くこと、先頭に BOM(文字コードの目印)を付けないことが決められています。
RFC 8216 は、この形式が音楽の再生リスト M3U から作られ、#EXTM3U と #EXTINF の2つのタグを受け継いでいると説明しています。それ以外の #EXT-X- で始まるタグは HLS で足されたものです。
m3u8 が使われる場面:動画配信サービス、ニュースやスポーツのライブ配信、オンライン授業の動画、監視カメラの映像など、ブラウザやスマホで「途中から見る」「回線に合わせて画質が変わる」動画の多くで使われています。Apple のドキュメントでは、HLS はライブとオンデマンド(録画)の両方、複数の画質、回線に合わせた切り替え、暗号化に対応すると書かれています。
m3u8の中身を見てみる(テキストファイル)
この節では、実際の m3u8 を開いたときに何が書かれているのかを、実物で確かめられます。
自分で作った20秒のテスト動画を、ffmpeg で4秒ずつに分けて HLS にしました(作り方は後の節)。できた index.m3u8 を cat で表示した中身です。
$ cat index.m3u8
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:4
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-PLAYLIST-TYPE:VOD
#EXTINF:4.000000,
seg_000.ts
#EXTINF:4.000000,
seg_001.ts
#EXTINF:4.000000,
seg_002.ts
#EXTINF:4.000000,
seg_003.ts
#EXTINF:4.000000,
seg_004.ts
#EXT-X-ENDLIST
# で始まる行が「タグ(説明)」、# の無い行が「ファイルの場所」です。 上から読むと、「4秒の seg_000.ts、4秒の seg_001.ts …を順に再生し、#EXT-X-ENDLIST で終わり」という意味になります。
| 行 | 意味 |
|---|---|
#EXTM3U |
この行で始まるのが決まり。拡張された M3U であることを示す |
#EXT-X-VERSION:3 |
HLS の決まりの版(互換性のため) |
#EXT-X-TARGETDURATION:4 |
区切り1つの最大の秒数 |
#EXT-X-MEDIA-SEQUENCE:0 |
最初の区切りの通し番号 |
#EXT-X-PLAYLIST-TYPE:VOD |
録画(あとから増えない) |
#EXTINF:4.000000, |
次の行のファイルが 4 秒 |
seg_000.ts |
区切りのファイル(m3u8 からの相対の場所) |
#EXT-X-ENDLIST |
これで全部。ライブ中は付かない |
同じフォルダには、m3u8 と5つの .ts ができました。ファイルの種類を file コマンドで調べると、m3u8 はテキスト、.ts は動画のデータでした。
$ ls -l
-rw-r--r-- 1 root root 258 Oct 23 21:22 index.m3u8
-rw-r--r-- 1 root root 1480688 Oct 23 21:22 seg_000.ts
-rw-r--r-- 1 root root 1473168 Oct 23 21:22 seg_001.ts
-rw-r--r-- 1 root root 1457752 Oct 23 21:22 seg_002.ts
-rw-r--r-- 1 root root 1477680 Oct 23 21:22 seg_003.ts
-rw-r--r-- 1 root root 1487644 Oct 23 21:22 seg_004.ts
$ file index.m3u8 seg_000.ts
index.m3u8: M3U playlist, ASCII text
seg_000.ts: data
m3u8 は 258 バイト、動画の本体(.ts)は合わせて約7.4MB です。 m3u8 のファイルサイズが小さいのは、一覧しか入っていないためです。
.ts ファイルの中身(MPEG-2 TS)
.ts は MPEG-2 TS(トランスポートストリーム)という、放送でも使われる動画の入れ物です。先頭を16進数で見ると、188 バイトごとに 47 という同じ値が現れました。これが TS の「パケット」の区切りの目印です。
$ od -A d -t x1 -N 4 seg_000.ts
0000000 47 40 11 10
$ od -A d -t x1 -j 188 -N 4 seg_000.ts
0000188 47 40 00 10
seg_000.ts の 1,480,688 バイトは、188 で割り切れて 7,876 パケットでした。ffprobe で調べると、1つの .ts の中に映像(h264)と音声(aac)の両方が入っていました。
$ ffprobe -v error -show_entries format=format_name,duration:stream=codec_name -of default=nw=1 seg_001.ts
codec_name=h264
codec_name=aac
format_name=mpegts
duration=4.053334
拡張子 .ts は TypeScript のファイルと同じなので、間違えやすい点です。中身は全く別物です。
HLSの仕組み(m3u8と.tsで動画が届く流れ)
この節では、プレーヤーが m3u8 を読んでから再生するまでに、どんな順番でファイルを取りに行くのかが分かります。
HLS では、プレーヤーが「一覧(m3u8)」→「区切りのファイル」の順に、ふつうの HTTP の GET で1つずつ取ってきます。 特別な配信サーバーは要らず、Web サーバーに置くだけで配れます。Apple のドキュメントも、HLS は普通の Web サーバーや CDN(配信用のキャッシュサーバー)で配れると説明しています。
127.0.0.1 の HTTP サーバーに HLS(3画質)を置き、Chromium と hls.js で再生したときに、サーバーに届いたリクエストの記録です。
"GET /player.html?src=/vp9/master.m3u8 HTTP/1.1" 200 -
"GET /hls.min.js HTTP/1.1" 200 -
"GET /vp9/master.m3u8 HTTP/1.1" 200 -
"GET /vp9/720p/index.m3u8 HTTP/1.1" 200 -
"GET /vp9/720p/init_0.mp4 HTTP/1.1" 200 -
"GET /vp9/720p/seg_000.m4s HTTP/1.1" 200 -
"GET /vp9/720p/seg_001.m4s HTTP/1.1" 200 -
"GET /vp9/720p/seg_002.m4s HTTP/1.1" 200 -
"GET /vp9/720p/seg_003.m4s HTTP/1.1" 200 -
"GET /vp9/720p/seg_004.m4s HTTP/1.1" 200 -
マスタープレイリストを読む
master.m3u8 に、用意されている画質(解像度とビットレート)の一覧が書かれている。
画質を1つ選び、その再生リストを読む
720p/index.m3u8 に、その画質の区切りのファイルが順に書かれている。
区切りのファイルを順に取る
seg_000 から順に取り、再生しながら次を読み込む。回線が遅くなれば別の画質の区切りに切り替える。
#EXT-X-ENDLIST で終わる
ライブ配信ではこのタグが無いので、プレーヤーは m3u8 を読み直して新しい区切りを探し続ける。
動画を1本の大きなファイルで配る方法との違いは、「少しずつ取る」「画質を途中で変えられる」「ライブにも使える」ことです。 そのかわり、ファイルの数が多くなり、ライブでは数秒〜数十秒の遅れが出ます(後の「ライブ配信の m3u8」の節)。
マスタープレイリストとメディアプレイリストの違い
この節では、m3u8 に2種類あることと、その見分け方が分かります。
m3u8 には、画質の一覧を書く「マスタープレイリスト」と、区切りのファイルを書く「メディアプレイリスト」の2種類があります。 RFC 8216 では Master Playlist と Media Playlist と呼び、改訂案(draft-pantos-hls-rfc8216bis-22、2026年5月1日)ではマスターを Multivariant Playlist と呼んでいます。
flowchart TD
M[マスター<br>master.m3u8] --> A[720p<br>メディア]
M --> B[480p<br>メディア]
M --> C[360p<br>メディア]
A --> S[seg_000〜<br>004.ts]
図の「720p メディア」は 720p/index.m3u8 のファイルです。480p・360p にも、それぞれのメディアプレイリストと区切りのファイルがあります。
同じテスト動画を 720p・480p・360p の3画質で HLS にしたときの master.m3u8 です。
#EXTM3U
#EXT-X-VERSION:6
#EXT-X-STREAM-INF:BANDWIDTH=3440800,RESOLUTION=1280x720,CODECS="avc1.64001f,mp4a.40.2"
720p/index.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=1680800,RESOLUTION=854x480,CODECS="avc1.64001f,mp4a.40.2"
480p/index.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=800800,RESOLUTION=640x360,CODECS="avc1.64001e,mp4a.40.2"
360p/index.m3u8
書いてあるもの
マスターには動画のファイル名が出てこない
見分けるタグ
#EXT-X-STREAM-INF がある#EXTINF と #EXT-X-TARGETDURATION があるRFC 8216 では、2種類のタグを1つのファイルに混ぜてはいけない
1本の動画に
プレーヤーに渡す URL はマスターのほう
BANDWIDTH は、その画質を途切れずに見るのに要る速さ(ビット毎秒)です。 RFC 8216 では #EXT-X-STREAM-INF に BANDWIDTH が必須、CODECS は書くべきもの(SHOULD)とされています。Apple の HLS Authoring Specification は、Apple 機器向けには CODECS と、映像なら RESOLUTION を必ず書くよう求めています。
この ffmpeg 6.1.1 では、BANDWIDTH は(映像のビットレート+音声のビットレート)×1.1 の値でした。720p は(3,000k+128k)×1.1=3,440,800 です。
CODECS の avc1.64001f は「H.264 の High プロファイル・レベル 3.1」、mp4a.40.2 は「AAC-LC」を表す文字列です。プレーヤーはこれを見て、再生できる画質だけを選びます。
m3u8のタグ早見表(#EXTM3U・#EXTINF・#EXT-X-STREAM-INF)
この節では、m3u8 に出てくるタグの意味を一覧で引けます。
RFC 8216 の4.3節のタグを、使う場所ごとに並べました。
| タグ | 意味 | 書く場所 |
|---|---|---|
#EXTM3U |
1行目に必ず書く目印 | 両方 |
#EXT-X-VERSION |
決まりの版(互換性) | 両方 |
#EXTINF:秒数, |
次の行の区切りの長さ | メディア |
#EXT-X-TARGETDURATION |
区切りの最大秒数(整数) | メディア |
#EXT-X-MEDIA-SEQUENCE |
最初の区切りの通し番号 | メディア |
#EXT-X-PLAYLIST-TYPE |
VOD(録画)か EVENT(追記だけのライブ) | メディア |
#EXT-X-ENDLIST |
これ以上増えない(終わり) | メディア |
#EXT-X-MAP |
fMP4 の初期化ファイル(init.mp4)の場所 | メディア |
#EXT-X-BYTERANGE |
1つのファイルの一部分を区切りとして使う | メディア |
#EXT-X-DISCONTINUITY |
ここで映像の形式や時刻がつながらない | メディア |
#EXT-X-PROGRAM-DATE-TIME |
区切りの実際の日時 | メディア |
#EXT-X-KEY |
区切りの暗号化の方式と鍵の場所 | メディア |
#EXT-X-STREAM-INF |
画質1つ分(BANDWIDTH・RESOLUTION・CODECS) | マスター |
#EXT-X-MEDIA |
別の音声・字幕・カメラアングル | マスター |
#EXT-X-INDEPENDENT-SEGMENTS |
どの区切りも単独で再生を始められる | 両方 |
#EXT-X-START |
再生を始める位置の希望 | 両方 |
#EXT-X-TARGETDURATION の決まり:RFC 8216 では、各区切りの #EXTINF の秒数を四捨五入した値が、#EXT-X-TARGETDURATION 以下でなければいけません。Apple の HLS Authoring Specification は、区切りの長さを6秒にすることを勧めています。
低遅延の HLS(LL-HLS)では、改訂案で #EXT-X-PART(部分的な区切り)や #EXT-X-SERVER-CONTROL などのタグが足されています。改訂案は決まりの版13を説明しています。
1つのファイルを区切って使う(#EXT-X-BYTERANGE)
ffmpeg の -hls_flags single_file を付けると、.ts を1本だけ作り、その中の「何バイト目から何バイト」を区切りとして m3u8 に書きました。
#EXTM3U
#EXT-X-VERSION:4
#EXT-X-TARGETDURATION:4
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-PLAYLIST-TYPE:VOD
#EXTINF:4.000000,
#EXT-X-BYTERANGE:1480688@0
index.ts
1480688@0 は「0 バイト目から 1,480,688 バイト」という意味です。この数は、分けて作った seg_000.ts の大きさと同じでした。
m3u8の開き方・再生方法(VLC・Safari・iPhone・Android)
この節では、m3u8 を「中身を見る」「動画として再生する」ための道具と手順が分かります。
m3u8 は一覧なので、中身を見るならテキストエディタ、動画として見るなら HLS に対応したプレーヤーで「URL」を開きます。 パソコンに m3u8 だけを保存しても、そこに書かれた .ts が同じ場所に無ければ再生できません。
| したいこと | Windows・Linux | Mac・iPhone | Android |
|---|---|---|---|
| 中身(文字)を見る | メモ帳・VS Code | テキストエディット・VS Code | テキストを表示できるアプリ |
| URL を再生する | VLC | Safari・VLC | VLC・HLS に対応したアプリ |
| Web ページに埋め込む | hls.js(Chrome・Edge・Firefox) | Safari は video 要素だけで再生 | hls.js |
| アプリに組み込む | ― | AVKit・AVFoundation | Media3 ExoPlayer |
VLC で開く(Windows・Linux)
VLC 3.0 のメニューのソースと日本語訳では、URL を開く項目は「メディア」メニューの「ネットワークストリームを開く」で、ショートカットは Ctrl+N です。
「ネットワークストリームを開く」を選ぶ
VLC の「メディア」メニューから「ネットワークストリームを開く」を選ぶか、Ctrl+N を押す。
m3u8 の URL を入れる
http:// か https:// で始まる m3u8 の URL を入れて「再生」を押す。マスターの URL を入れると、VLC が画質を選ぶ。
パソコンの中の m3u8 は「ファイルを開く」
m3u8 と .ts が同じフォルダにそろっていれば、「メディア」>「ファイルを開く」(Ctrl+O)でも再生できる。
Safari・iPhone・iPad で開く
Safari は HLS をそのまま再生できます。 Apple のドキュメントでは、HLS の対応は iOS 3.0・Safari 4.0 からで、iPhone・iPad・Apple TV・Mac で再生できると説明されています。Safari のアドレス欄に m3u8 の URL を入れるか、Web ページの <video src="….m3u8"> で再生されます。Apple の「Delivering Video Content for Safari」も、動画の配信には HLS を使うよう案内しています。
Android で開く
Android のアプリでは、Android Developers のドキュメントによると Media3 の ExoPlayer が HLS に対応しており、media3-exoplayer-hls のモジュールを足して使います。対応する入れ物は MPEG-TS・fMP4(CMAF)・ADTS(AAC)・MP3 です。見るだけなら、Android 版の VLC などの HLS に対応したプレーヤーで URL を開きます。
Chrome・Edge・Firefox で開く
Chrome・Edge・Firefox では、長い間、HLS をそのままでは再生できず、hls.js などの JavaScript のライブラリを使って再生してきました。 動画配信サービス Mux の障害報告(2025年10〜11月)では、Chrome が 141 の一部の利用者から HLS の標準での再生を順に有効にし、142 では既定で有効になったと書かれています。
確認に使った Chromium 141.0.7390.37 では、m3u8 の URL をアドレス欄で開くと再生されず、ファイルのダウンロードが始まりました。
Page.goto: Download is starting
download: index.m3u8
<video src="master.m3u8"> だけのページでは、次のエラーになりました。
video error code=4 message=DEMUXER_ERROR_COULD_NOT_OPEN: FFmpegDemuxer: open context failed
ブラウザごとに対応が違うので、Web ページでは次の節の hls.js の書き方(使えるなら hls.js、使えなければ標準の再生)が広く使われています。
ブラウザでm3u8を再生する(hls.jsの使い方)
この節では、Web ページに HLS の動画を載せるための hls.js の最小のコードと、実際に動かした結果が分かります。
hls.js は、HLS を読み込んで、ブラウザの Media Source Extensions(MSE。JavaScript から動画のデータを渡す仕組み)で再生する JavaScript のライブラリです。 README では、MSE に対応したブラウザ(Chrome 47 以上・Firefox 51 以上・Edge・macOS の Safari 10 以上など)で動き、iOS の Safari 17.1 以上では Managed Media Source を使って v1.5.0 から対応したと書かれています。2026年10月23日時点の npm の最新版は 1.7.3(2026年9月11日公開)でした。
<video id="video" controls muted width="640"></video>
<script src="https://cdn.jsdelivr.net/npm/hls.js@1"></script>
<script>
const src = "/vp9/master.m3u8";
const video = document.getElementById("video");
if (Hls.isSupported()) {
const hls = new Hls();
hls.on(Hls.Events.ERROR, (e, d) => console.log(d.type, d.details, d.fatal));
hls.loadSource(src);
hls.attachMedia(video);
} else if (video.canPlayType("application/vnd.apple.mpegurl")) {
video.src = src; // Safari などの標準の再生
}
</script>
README は、まず Hls.isSupported() を確かめ、使えないときだけ標準の再生(canPlayType)に回す順番を勧めています。標準の再生に対応していると答えても、一部の配信がうまく再生できないブラウザがあるためです。
Chromium と hls.js で再生した結果
hls.js 1.7.3 を手元の HTTP サーバーに置き、Chromium 141 で再生して、hls.js のイベントを記録しました。
MANIFEST_PARSED levels=360p/800800,480p/1680800,720p/3440800
FRAG_LOADED seg_000.m4s level=2
FRAG_LOADED seg_001.m4s level=2
playing
LEVEL_SWITCHED 720p
FRAG_LOADED seg_002.m4s level=2
FRAG_LOADED seg_003.m4s level=2
FRAG_LOADED seg_004.m4s level=2
(8.69 秒の時点で再生中・videoWidth 1280 × videoHeight 720)
levels は、マスターに書かれた3画質を、hls.js がビットレートの低い順に並べたものです。何も指定しないと、マスターの先頭に書かれた 720p から始まりました。
確認に使った Chromium は H.264 と AAC を再生できない版です。 Chromium プロジェクトの説明では、H.264・AAC は Google Chrome に限られ、オープンソースの Chromium は VP9・AV1・Opus などに対応します。そのため、ここでは同じテスト動画を VP9(映像)+Opus(音声)の fMP4 にした HLS を再生しました。H.264 の HLS を渡したときの結果は、後のエラーの節に載せています。MDN の対応表では、H.264 は Chrome・Edge・Firefox・Opera・Safari の全版が対応しています(Firefox は OS のコーデックを使う)。
ffmpegでm3u8と.tsを作る(自作のテスト動画から)
この節では、手元の動画から HLS(m3u8 と区切りのファイル)を作るコマンドと、区切りがどう決まるかが分かります。
元にする動画は、ffmpeg が自分で作るテスト映像です。他人の動画を使わずに試せます。
# 1280x720・30fps・20秒のテスト映像と 440Hz の音(H.264+AAC、2秒ごとにキーフレーム)
ffmpeg -f lavfi -i "testsrc2=size=1280x720:rate=30:duration=20" \
-f lavfi -i "sine=frequency=440:sample_rate=48000:duration=20" \
-c:v libx264 -preset veryfast -pix_fmt yuv420p -g 60 \
-c:a aac -b:a 128k -shortest test.mp4
# 4秒ずつの HLS にする(再エンコードしない)
ffmpeg -i test.mp4 -c copy -f hls -hls_time 4 -hls_playlist_type vod \
-hls_segment_filename 'seg_%03d.ts' index.m3u8
| オプション | 意味 | ffmpeg 6.1.1 の既定値 |
|---|---|---|
-f hls |
出力を HLS にする | ― |
-hls_time 4 |
区切りの目標の秒数 | 2 |
-hls_playlist_type vod |
録画として全部を1つの m3u8 に書く | 指定なし |
-hls_list_size 0 |
m3u8 に残す区切りの数(0 は全部) | 5 |
-hls_segment_filename |
区切りのファイル名の形 | 出力名+番号 |
-hls_segment_type fmp4 |
区切りを fMP4(.m4s)にする | mpegts |
-hls_flags |
independent_segments・delete_segments など | 0 |
既定値は ffmpeg -h muxer=hls の出力で確かめました。-hls_playlist_type vod を付けないと -hls_list_size の既定値 5 が効き、m3u8 に最後の5つしか残りません。
区切りはキーフレームの位置でしか切れない
-c copy(再エンコードしない)で HLS にすると、区切りはキーフレーム(その1枚だけで画を作れるフレーム)の位置でしか切れません。 キーフレームの間隔を10秒(-g 300)にした動画を -hls_time 4 で分けると、4秒ではなく10秒ずつになりました。
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:10
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-PLAYLIST-TYPE:VOD
#EXTINF:10.000000,
seg_000.ts
#EXTINF:10.000000,
seg_001.ts
#EXT-X-ENDLIST
2秒ごとにキーフレームがある動画(-g 60)で -hls_time を変えたときの結果です。
| -hls_time | 区切りの数 | 各区切りの秒数 | .ts の合計(バイト) |
|---|---|---|---|
| 2 | 10 | 2秒×10 | 7,378,060 |
| 4 | 5 | 4秒×5 | 7,376,932 |
| 6 | 4 | 6・6・6・2秒 | 7,377,308 |
| 10 | 2 | 10秒×2 | 7,376,556 |
区切りの長さを変えても、合計の大きさはほとんど変わりませんでした。 短くすると、ファイルの数と HTTP のリクエストの数が増え、ライブでは遅れが小さくなります。Apple の HLS Authoring Specification は、区切りは6秒、キーフレームは2秒ごとを勧めています。
ffmpeg の基本の使い方(変換・圧縮・切り出し)は、関連記事「ffmpegの使い方|MOV→MP4変換・圧縮・切り出しを実測で解説」で扱っています。
複数の画質のm3u8を作る(アダプティブビットレート)
この節では、回線の速さに合わせて画質が切り替わる HLS(マスタープレイリスト付き)の作り方と、切り替わる様子が分かります。
画質を切り替えられるようにするには、画質ごとに動画を作り直し、それぞれのメディアプレイリストと、まとめのマスタープレイリストを作ります。 ffmpeg では -var_stream_map と -master_pl_name で1回のコマンドで作れます。
ffmpeg -i test.mp4 \
-filter_complex "[0:v]split=3[v1][v2][v3];[v1]scale=1280:720[o1];[v2]scale=854:480[o2];[v3]scale=640:360[o3]" \
-map "[o1]" -map 0:a -map "[o2]" -map 0:a -map "[o3]" -map 0:a \
-c:v libx264 -preset veryfast -pix_fmt yuv420p -g 60 -keyint_min 60 -sc_threshold 0 \
-b:v:0 3000k -maxrate:v:0 3300k -bufsize:v:0 6000k \
-b:v:1 1400k -maxrate:v:1 1540k -bufsize:v:1 2800k \
-b:v:2 600k -maxrate:v:2 660k -bufsize:v:2 1200k \
-c:a aac -b:a 128k -ac 2 \
-f hls -hls_time 4 -hls_playlist_type vod -hls_flags independent_segments \
-master_pl_name master.m3u8 \
-var_stream_map "v:0,a:0,name:720p v:1,a:1,name:480p v:2,a:2,name:360p" \
-hls_segment_filename 'abr/%v/seg_%03d.ts' abr/%v/index.m3u8
%v には name: に書いた名前が入ります。-sc_threshold 0 は場面の切り替わりで余計なキーフレームを入れない指定で、3画質の区切りの位置をそろえるために使いました。できたファイルの並びと大きさです。
abr/master.m3u8
abr/720p/index.m3u8 abr/720p/seg_000.ts 〜 seg_004.ts
abr/480p/index.m3u8 abr/480p/seg_000.ts 〜 seg_004.ts
abr/360p/index.m3u8 abr/360p/seg_000.ts 〜 seg_004.ts
画質が切り替わる様子
hls.js の startLevel: 0(いちばん低い画質から始める)を指定して再生すると、最初の区切りだけ 360p を取り、2つ目から 720p に切り替わりました。サーバーに届いたリクエストの順番です。
"GET /vp9/master.m3u8 HTTP/1.1" 200 -
"GET /vp9/360p/index.m3u8 HTTP/1.1" 200 -
"GET /vp9/360p/init_2.mp4 HTTP/1.1" 200 -
"GET /vp9/360p/seg_000.m4s HTTP/1.1" 200 -
"GET /vp9/720p/index.m3u8 HTTP/1.1" 200 -
"GET /vp9/720p/init_0.mp4 HTTP/1.1" 200 -
"GET /vp9/720p/seg_001.m4s HTTP/1.1" 200 -
"GET /vp9/720p/seg_002.m4s HTTP/1.1" 200 -
同じパソコンの中の通信は速いので、hls.js は1つ目の区切りを読んだ速さを見て、すぐ高い画質に上げました。 回線が遅ければ、低い画質のまま続けたり、途中で下げたりします。画質を途中で変えられるのは、どの画質も同じ時刻(4秒ごと)で区切られているためです。
fMP4(.m4s)の HLS
-hls_segment_type fmp4 を付けると、区切りが .ts ではなく fMP4(断片化した MP4)になり、メディアプレイリストに #EXT-X-MAP(初期化ファイルの場所)が入りました。
#EXTM3U
#EXT-X-VERSION:7
#EXT-X-TARGETDURATION:4
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-MAP:URI="init_0.mp4"
#EXTINF:4.000000,
seg_000.m4s
init_0.mp4 は 1,303 バイトで、コーデックの設定などが入っています。Apple の HLS Authoring Specification では、H.264 は fMP4 か MPEG-2 TS、HEVC と AV1 は fMP4 だけを使うと決められています。VP9+Opus の fMP4 で作ったときは、ffmpeg 6.1.1 のマスターに CODECS が書かれませんでした(hls.js は区切りを読んで判断し、再生できました)。
ライブ配信のm3u8(#EXT-X-ENDLISTが無い)
この節では、ライブ配信の m3u8 が録画とどう違うのかを、書き換わる様子で確かめられます。
ライブの m3u8 には #EXT-X-ENDLIST が無く、新しい区切りができるたびに書き換わります。 プレーヤーは m3u8 を何度も読み直して、増えた区切りを取りに行きます。ffmpeg の -re(実際の速さで出力)で16秒のテスト映像を流し、-hls_time 2 -hls_list_size 3 -hls_flags delete_segments で、前の区切りを消しながら3つだけを m3u8 に残しました。
[開始から9秒後]
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:2
#EXT-X-MEDIA-SEQUENCE:1
#EXTINF:2.000000,
seg_001.ts
#EXTINF:2.000000,
seg_002.ts
#EXTINF:2.000000,
seg_003.ts
[終わった後]
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:2
#EXT-X-MEDIA-SEQUENCE:5
#EXTINF:2.000000,
seg_005.ts
#EXTINF:2.000000,
seg_006.ts
#EXTINF:2.000000,
seg_007.ts
#EXT-X-ENDLIST
#EXT-X-MEDIA-SEQUENCE が 1 → 5 と増え、一覧が「窓」のように後ろへずれていきました。 終わった後は seg_004.ts〜seg_007.ts の4つだけがフォルダに残り、それより前は消されていました。ffmpeg は配信を終えるときに #EXT-X-ENDLIST を足しました。
| 種類 | #EXT-X-PLAYLIST-TYPE | #EXT-X-ENDLIST | 一覧の変わり方 |
|---|---|---|---|
| 録画(VOD) | VOD | ある | 変わらない |
| ライブ(後から見返せる) | EVENT | 終わるまで無い | 後ろに足されるだけ |
| ライブ(窓をずらす) | 書かない | 終わるまで無い | 前のものが消え、新しいものが足される |
hls.js の API.md では、ライブの再生位置は liveSyncDurationCount(既定値 3)で決まり、最新の区切りから3つ前(#EXT-X-TARGETDURATION の3倍)の位置から再生を始めると説明されています。区切りが2秒なら約6秒、6秒なら約18秒、本当の「今」より遅れて映る計算です。遅れの意味と測り方は、関連記事「レイテンシとは|意味・目安・測り方を遅延0〜300msの実測で解説」で扱っています。
サーバーにm3u8を置いて配信する設定(MIMEタイプ・CORS・nginx)
この節では、自分の HLS を Web サーバーで配るときに必要な設定が分かります。
HLS はファイルを置くだけで配れますが、MIME タイプ(ファイルの種類の宣言)と、別のドメインのページから読むときの CORS の設定が要ります。 Apple のドキュメントは、MIME タイプを次のように設定するよう案内しています。
| 拡張子 | MIME タイプ(Apple の表) | 補足 |
|---|---|---|
.m3u8 |
vnd.apple.mpegURL |
RFC 8216 では application/vnd.apple.mpegurl |
.ts |
video/MP2T |
TypeScript と拡張子が同じ |
.mp4 |
video/mp4 |
fMP4 の初期化ファイル |
.m3u |
audio/mpegURL |
MIME を自由に設定できないサーバー向け |
Apple のドキュメントは、m3u8 はテキストなので gzip で圧縮して配るとよいこと、ライブでは m3u8 がよく書き換わるのでキャッシュの時間を短くすることも勧めています。
Python の http.server は、何も設定しないと .ts を text/vnd.trolltech.linguist(翻訳ファイルの形式)として返す設定になっていました。 Ubuntu の /etc/mime.types で ts がその種類に結び付いているためです。nginx 1.24.0 の mime.types には、m3u8 は application/vnd.apple.mpegurl、ts は video/mp2t が入っていました。
nginx で HLS を配り、ほかのドメインのページからも読めるようにした設定です。
server {
listen 127.0.0.1:39510;
root /var/www/hls;
location ~ \.(m3u8|ts|m4s|mp4)$ {
types {
application/vnd.apple.mpegurl m3u8;
video/mp2t ts;
video/iso.segment m4s;
video/mp4 mp4;
}
add_header Access-Control-Allow-Origin * always;
add_header Cache-Control no-cache always;
}
}
$ curl -sI http://127.0.0.1:39510/abr/master.m3u8
HTTP/1.1 200 OK
Content-Type: application/vnd.apple.mpegurl
Access-Control-Allow-Origin: *
Cache-Control: no-cache
$ curl -sI http://127.0.0.1:39510/abr/360p/seg_000.ts | grep -iE "content-type|access-control"
Content-Type: video/mp2t
Access-Control-Allow-Origin: *
この nginx を、別のポート(別のオリジン)のページから hls.js で読むと、3画質を読み込んで再生されました。Access-Control-Allow-Origin: * はどのサイトからでも読めるという意味です。MDN の CORS の説明では、資格情報(Cookie など)を含むリクエストでは * は使えず、許可するオリジンを1つ書きます。限られたサイトだけに見せたいときは、* の代わりに自分のサイトのオリジンを書きます。location の書き方は、関連記事「nginx location書き方|優先順位とtry_filesをcurlで検証」で扱っています。
- 1m3u8 が UTF-8・BOM なしで、1行目が #EXTM3Uエディタで保存し直したときに BOM が付きやすい
- 2m3u8 に書いた場所に、区切りのファイルが全部ある相対の場所は m3u8 のある場所が基準
- 3MIME タイプが m3u8・ts・m4s に設定されているcurl -I で Content-Type を見る
- 4別のドメインから読むなら CORS のヘッダーがあるm3u8 だけでなく区切りのファイルにも付ける
- 5マスターに CODECS と RESOLUTION があるApple 機器向けには必須
- 6録画なら最後に #EXT-X-ENDLIST がある無いとライブとして扱われ、読み直しが続く
自分のm3u8をMP4にまとめる(ffmpeg)
この節では、自分で作った HLS(m3u8 と .ts)を、1本の MP4 にまとめる方法が分かります。
m3u8 を入力にして -c copy で MP4 に書き出すと、区切りがつながった1本の MP4 になります。 再エンコードしないので画質は変わらず、すぐ終わります。
$ ffmpeg -i index.m3u8 -c copy out.mp4
$ ls -l out.mp4
-rw-r--r-- 1 root root 7144410 Oct 23 21:25 out.mp4
$ ffprobe -v error -show_entries format=duration:stream=codec_name -of default=nw=1 out.mp4
codec_name=h264
codec_name=aac
duration=20.032000
.ts の合計 7,376,932 バイトが、MP4 では 7,144,410 バイトになりました。TS は 188 バイトのパケットごとに目印を持つので、MP4 より少し大きくなります。
画質を選んでまとめる(マスターの m3u8)
マスターの m3u8 を入力にすると、ffprobe は3画質を Program 0〜2 として見せました。
$ ffprobe abr/master.m3u8
Program 0
Stream #0:0: Video: h264 (High) … 1280x720 …
Program 1
Stream #0:2: Video: h264 (High) … 854x480 …
Program 2
Stream #0:4: Video: h264 (High) … 640x360 …
何も指定しないと、ffmpeg はいちばん解像度の高い 1280×720 の映像を選びました。360p にしたいときは -map 0:p:2(Program 2)を指定します。
ffmpeg -i abr/master.m3u8 -c copy def.mp4 # 1280x720 になった
ffmpeg -i abr/master.m3u8 -map 0:p:2 -c copy p2.mp4 # 640x360 になった
自分のサーバーにある m3u8 をまとめる
自分のサーバーに置いた m3u8 の URL を入力にすると、ffmpeg は区切りを1つずつ取りに行きました。
$ ffmpeg -i http://127.0.0.1:39500/single/index.m3u8 -c copy http_out.mp4
[hls @ 0x55a4eaea7fc0] Opening 'http://127.0.0.1:39500/single/seg_000.ts' for reading
[hls @ 0x55a4eaea7fc0] Opening 'http://127.0.0.1:39500/single/seg_001.ts' for reading
…
[out#0/mp4 @ 0x55a4eaf27400] video:6642kB audio:319kB subtitle:0kB …
.ts をつなげてから MP4 にする
MPEG-2 TS は、ファイルをそのまま後ろにつなげても1本の TS として読めます。cat でつなげた joined.ts(7,376,932 バイト)を MP4 にすると、m3u8 から作ったものと同じ 7,144,410 バイト・20.032 秒になりました。
cat seg_*.ts > joined.ts
ffmpeg -i joined.ts -c copy joined.mp4
ファイル名の番号が seg_9.ts・seg_10.ts のように桁がそろっていないと、seg_*.ts の並び順が崩れます。m3u8 を入力にすれば、m3u8 に書かれた順番でつながります。
m3u8 の長さと区切りの数を数える(Python)
m3u8 の #EXTINF を足すと、動画の長さが分かります。マスターなら画質ごとに数えます。
import sys, pathlib
def read(path):
lines = pathlib.Path(path).read_text(encoding="utf-8").splitlines()
if not lines or lines[0] != "#EXTM3U":
raise SystemExit(f"{path}: 先頭が #EXTM3U ではありません")
return lines
def media(path):
total, n = 0.0, 0
for l in read(path):
if l.startswith("#EXTINF:"):
total += float(l[8:].split(",")[0]); n += 1
return n, total
path = sys.argv[1]; lines = read(path)
if any(l.startswith("#EXT-X-STREAM-INF") for l in lines):
for i, l in enumerate(lines):
if l.startswith("#EXT-X-STREAM-INF"):
n, t = media(pathlib.Path(path).parent / lines[i + 1])
print(f"{lines[i + 1]:18} {n}本 {t:.1f}秒")
else:
n, t = media(path)
print(f"{path}: セグメント {n}本・合計 {t:.1f}秒")
$ python3 dur.py abr/master.m3u8
720p/index.m3u8 5本 20.0秒
480p/index.m3u8 5本 20.0秒
360p/index.m3u8 5本 20.0秒
$ python3 dur.py single/index.m3u8
single/index.m3u8: セグメント 5本・合計 20.0秒
m3uとm3u8の違い(音楽の再生リスト)
この節では、よく似た m3u と m3u8 の違いと、それぞれの使われ方が分かります。
m3u は、もともと音楽プレーヤーの再生リストの形式で、m3u8 はその文字コードを UTF-8 にしたものです。 HLS の再生リストは m3u8 で書かれますが、音楽の再生リストを m3u8 で保存することもあります。
音楽の再生リスト(m3u・m3u8)
パソコンの中の mp3 などの場所を並べる
#EXTINF に「秒数,曲名」を書く
m3u は Shift_JIS などで保存されていることがある
HLS の再生リスト(m3u8)
Web 上の区切りのファイルを並べる
#EXT-X-TARGETDURATION などの HLS のタグがある
UTF-8・BOM なしと決まっている
自分で作った2つの音(mp3)を並べた音楽の再生リストを、UTF-8(list.m3u8)と Shift_JIS(list.m3u)で保存しました。
#EXTM3U
#EXTINF:3,テスト - ドの音
01_テスト音.mp3
#EXTINF:4,テスト - ミの音
02_ミの音.mp3
$ file list.m3u list.m3u8
list.m3u: M3U playlist, Non-ISO extended-ASCII text
list.m3u8: M3U playlist, Unicode text, UTF-8 text
$ python3 -c "print(open('list.m3u',encoding='utf-8').read())"
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x83 in position 18: invalid start byte
Shift_JIS の m3u を UTF-8 として読むと、日本語の曲名のところでエラーになりました。 cp932(Windows の Shift_JIS)を指定すると読めました。文字化けの原因の切り分けは、関連記事「UnicodeDecodeErrorの原因と直し方|cp932・utf-8・BOM」で扱っています。
ffmpeg 6.1.1 は、この音楽の再生リストを UTF-8 の m3u8 でも Invalid data found when processing input で開きませんでした。ffmpeg の HLS の読み込みは HLS のタグがある m3u8 を前提にしているためで、拡張子が .m3u8 でも、中身が HLS とは限らないことが分かります。
m3u8が再生できないときのエラーと直し方(CORS・404・コーデック)
この節では、m3u8 が再生できないときに出るエラーの原文から、原因と直し方を引けます。
再生できないときは、「m3u8 が取れているか」→「区切りが取れているか」→「コーデックに対応しているか」の順に見ます。 自分で作った HLS で、hls.js 1.7.3(Chromium 141)と ffmpeg 6.1.1 のエラーをわざと起こしました。
flowchart TD
A[再生できない] --> B{m3u8 が<br>取れる?}
B -->|404| C[URL と<br>置き場所]
B -->|status 0| D[CORS か<br>file://]
B -->|取れる| E{区切りが<br>取れる?}
E -->|404| C
E -->|取れる| F[コーデックを<br>確かめる]
hls.js(ブラウザ)のエラー
別のオリジン(ポート 39501)のページから、CORS のヘッダーが無いサーバー(ポート 39500)の m3u8 を読んだときです。
ERROR type=networkError details=manifestLoadError fatal=true code=0
msg=A network error (status 0) occurred while loading manifest
console: Access to XMLHttpRequest at 'http://127.0.0.1:39500/vp9/master.m3u8' from origin
'http://127.0.0.1:39501' has been blocked by CORS policy: No 'Access-Control-Allow-Origin'
header is present on the requested resource.
hls.js のエラーは code=0(status 0)で、本当の理由はコンソールの CORS の文言にありました。 サーバーに Access-Control-Allow-Origin を足すと再生されました。hls.js の README も、HLS のファイルはすべて GET を許す CORS のヘッダー付きで配るよう書いています。
H.264+AAC の HLS を、H.264 を再生できない Chromium で読んだときです。
ERROR type=mediaError details=manifestIncompatibleCodecsError fatal=true
reason=one or more CODECS in variant not supported: ["avc1.64001f,mp4a.40.2","avc1.64001e,mp4a.40.2"]
msg=no level with compatible codecs found in manifest
区切りのファイルが1つ無い(404)ときは、その手前まで再生して止まりました。fragLoadError(fatal=false)が4回出て、読み込めた 8 秒分の手前の 7.96 秒で止まりました。
FRAG_LOADED ../vp9/720p/seg_001.m4s level=0
ERROR type=networkError details=fragLoadError fatal=false code=404 msg=HTTP Error 404 File not found
(同じエラーがあと3回)
ERROR type=mediaError details=bufferStalledError fatal=false msg=Playback stalling at @7.960205 due to low buffer
| エラーの原文(抜粋) | 原因 | 直し方 |
|---|---|---|
manifestLoadError … status 0 と、コンソールに blocked by CORS policy |
別のドメインのページから読み、サーバーに CORS のヘッダーが無い | m3u8 と区切りの両方に Access-Control-Allow-Origin を付ける |
manifestLoadError … status 0 と、コンソールに Cross origin requests are only supported for protocol schemes |
ページを file:// で開いた(ダブルクリック) | HTTP サーバー経由で開く(python3 -m http.server) |
manifestLoadError … code=404 |
m3u8 の URL の誤り・置き忘れ | URL をブラウザで開いて取れるか確かめる |
fragLoadError … code=404 |
m3u8 に書いた区切りのファイルが無い・場所の誤り | 相対の場所は m3u8 のある場所が基準。ファイルをそろえる |
bufferStalledError |
区切りが届かず、読み込んだ分を使い切った | 前の fragLoadError を直す。回線が遅いなら低い画質を用意 |
manifestIncompatibleCodecsError |
ブラウザがそのコーデック(H.264・HEVC など)を再生できない | 対応するコーデックの画質を足す。CODECS の書き間違いも見る |
DEMUXER_ERROR_COULD_NOT_OPEN |
HLS に標準で対応しないブラウザで video の src に m3u8 を入れた | hls.js を使う |
file:// で開いたときのコンソールの原文は、次のとおりでした。
Access to XMLHttpRequest at 'file:///…/vp9/master.m3u8' from origin 'null' has been blocked by CORS policy:
Cross origin requests are only supported for protocol schemes: chrome, chrome-extension, chrome-untrusted, data, http, https, isolated-app.
CORS の仕組みとエラー文ごとの直し方は、関連記事「CORSエラーの原因と解決方法|エラーメッセージ別に解説」で、404 などのステータスコードの意味は、関連記事「HTTPステータスコード 一覧|全コードの原因と対処・curl実例つき」で扱っています。
ffmpeg で m3u8 を読むときのエラー
| エラーの原文 | 原因 | 直し方 |
|---|---|---|
Invalid data found when processing input |
1行目が #EXTM3U でない・先頭に BOM・HLS のタグが無い |
UTF-8(BOM なし)で保存し、1行目を #EXTM3U にする |
Protocol 'http' not on whitelist 'file,crypto,data'! |
パソコンの中の m3u8 に http:// の区切りが書いてある | 自分のファイルなら -protocol_whitelist file,http,tcp を付ける |
Filename extension of '…dat' is not a common multimedia extension, blocked for security reasons. |
区切りの拡張子が動画のものではない | 区切りの名前を .ts・.m4s などにする |
先頭に BOM を付けた m3u8 は、file コマンドでは UTF-8 (with BOM) text と表示され、ffmpeg は Invalid data found when processing input で開きませんでした。BOM を取ると開けました。
自分のパソコンの m3u8 に自分のサーバーの http:// の区切りを書いた例では、-protocol_whitelist file,http,tcp を付けると 7,144,410 バイトの MP4 にまとめられました。
VLC で再生できないとき
VLC で開けないときも、原因は同じ順番で切り分けます。m3u8 の URL をブラウザで開いて文字が表示されるか(404 でないか)、m3u8 と同じ場所に区切りのファイルがあるかを確かめます。VLC 3.0 のソースと日本語訳では、「ツール」メニューに「メッセージ」(Ctrl+M。読み込みのエラーの記録)と「コーデック情報」(Ctrl+J。再生中の映像と音声のコーデック)があります。
m3u8の動画を保存してもいい?(著作権・利用規約)
この節では、他人が配信している m3u8 の動画を保存することについて、知っておくべき決まりが分かります。
この記事では、自分で作ったテスト映像だけを使い、他人が配信している動画を保存する方法は扱いません。 配信の多くは、利用規約でダウンロードを認めていません。暗号化(#EXT-X-KEY)や DRM(著作権を守るための技術)で守られている配信もあります。
文化庁の説明では、令和2年の著作権法の改正で、違法にアップロードされたものと知りながらダウンロードすることの違法化が、音楽・映像から著作物全般に広げられました(令和3年1月1日施行)。動画を手元に残したいときは、配信元が用意しているダウンロードの機能や、権利者が認めた方法を使います。
よくある質問
Q. m3u8 とは何ですか? A. HLS(HTTP Live Streaming)という動画配信で使う再生リストのファイルです。中身は文字だけで、動画を分けた短いファイル(.ts など)の場所と長さが順に書かれています。
Q. m3u8 ファイルの開き方は? A. 中身を見るならメモ帳・テキストエディット・VS Code で開きます。動画として見るなら、VLC の「メディア」>「ネットワークストリームを開く」(Ctrl+N)か Safari に、m3u8 の URL を入れます。
Q. m3u8 をダブルクリックしても再生されません。 A. m3u8 は一覧で、動画の本体は別のファイルです。書かれた区切りのファイルが同じ場所に無いと再生できません。URL のまま VLC や Safari で開きます。
Q. Chrome で m3u8 を再生できますか?
A. 長い間、Chrome は hls.js などのライブラリを使って再生してきました。Mux の障害報告では、Chrome 142 で HLS の標準での再生が既定で有効になったと書かれています。Web ページでは Hls.isSupported() を先に確かめる書き方が使われています。
Q. iPhone で m3u8 を再生できますか? A. Safari は HLS に対応しています。Apple のドキュメントでは iOS 3.0・Safari 4.0 から対応しています。
Q. m3u8 を MP4 に変換するには?
A. 自分の HLS なら ffmpeg -i index.m3u8 -c copy out.mp4 で1本の MP4 にまとまります。再エンコードしないので画質は変わりません。
Q. m3u8 と .ts の違いは? A. m3u8 は一覧(再生リスト)、.ts は動画の本体を数秒ずつに分けたファイル(MPEG-2 TS)です。
Q. m3u と m3u8 の違いは? A. m3u8 は文字コードを UTF-8 にした M3U です。HLS の再生リストは、RFC 8216 で .m3u8 でも .m3u でも UTF-8 と決められています。
Q. .ts ファイルを結合するには?
A. MPEG-2 TS は cat でつなげても1本の TS として読めます。順番を確実にするなら、m3u8 を ffmpeg の入力にします。
Q. m3u8 の動画をダウンロードしてもいいですか? A. 他人の配信は、利用規約や著作権法の決まりを確かめる必要があります。この記事では保存の方法は扱いません。配信元が用意した方法を使います。
Q. m3u8 が「再生できない」ときは何を見ればいいですか?
A. m3u8 が取れるか(404・CORS)、区切りが取れるか(404)、コーデックに対応しているか、の順に見ます。hls.js なら ERROR イベントの details に原因の名前が入ります。
まとめ
- m3u8 は、HLS の再生リスト。中身は文字だけで、動画の本体は .ts・.m4s の短いファイル。「8」は UTF-8 の M3U という意味。
- マスタープレイリスト(
#EXT-X-STREAM-INFで画質の一覧)と、メディアプレイリスト(#EXTINFで区切りの一覧)の2種類がある。 - 再生は VLC(ネットワークストリームを開く・Ctrl+N)、Safari、Web ページなら hls.js。Chromium 141 では URL を開くとダウンロードになり、hls.js で再生できた。
- ffmpeg の
-f hlsで自分の動画から HLS を作れる。-c copyでは区切りはキーフレームの位置で切れる。-var_stream_mapで3画質とマスターを1回で作れた。 - ライブの m3u8 は
#EXT-X-ENDLISTが無く、#EXT-X-MEDIA-SEQUENCEが増えながら書き換わる。 - 自分の HLS は
ffmpeg -i index.m3u8 -c copy out.mp4で MP4 にまとまる。.ts は cat でつなげても読めた。 - 再生できないときは CORS(status 0)・404・コーデック(manifestIncompatibleCodecsError)の順に見る。
参考・出典
確認日はいずれも 2026年10月23日です。
- IETF「RFC 8216 HTTP Live Streaming」:https://datatracker.ietf.org/doc/html/rfc8216
- IETF「HTTP Live Streaming 2nd Edition(draft-pantos-hls-rfc8216bis)」:https://datatracker.ietf.org/doc/draft-pantos-hls-rfc8216bis/
- IETF「HTTP Live Streaming(draft-pantos-http-live-streaming-07)」:https://datatracker.ietf.org/doc/html/draft-pantos-http-live-streaming-07
- Apple Developer「HTTP Live Streaming」:https://developer.apple.com/documentation/http-live-streaming
- Apple Developer「HLS Authoring Specification for Apple Devices」:https://developer.apple.com/documentation/http-live-streaming/hls-authoring-specification-for-apple-devices
- Apple Developer「Deploying a Basic HTTP Live Streaming (HLS) Stream」:https://developer.apple.com/documentation/http-live-streaming/deploying-a-basic-http-live-streaming-hls-stream
- Apple Developer「Delivering Video Content for Safari」:https://developer.apple.com/documentation/webkit/delivering-video-content-for-safari
- GitHub「video-dev/hls.js」:https://github.com/video-dev/hls.js
- GitHub「hls.js API.md」:https://github.com/video-dev/hls.js/blob/master/docs/API.md
- FFmpeg「FFmpeg Formats Documentation」:https://ffmpeg.org/ffmpeg-formats.html
- VideoLAN「Documentation:Open Media」:https://wiki.videolan.org/Documentation:Open_Media
- GitHub「videolan/vlc(3.0.x)modules/gui/qt/menus.cpp」:https://github.com/videolan/vlc/blob/3.0.x/modules/gui/qt/menus.cpp
- Android Developers「HLS(Media3 ExoPlayer)」:https://developer.android.com/media/media3/exoplayer/hls
- The Chromium Projects「Audio/Video」:https://www.chromium.org/audio-video/
- MDN「Web video codec guide」:https://developer.mozilla.org/en-US/docs/Web/Media/Guides/Formats/Video_codecs
- MDN「オリジン間リソース共有 (CORS)」:https://developer.mozilla.org/ja/docs/Web/HTTP/Guides/CORS
- Mux「Investigating elevated playback errors on Chrome version 141.0.7390.123 and later」:https://status.mux.com/incidents/g4wcv5cm02sg
- 文化庁「令和2年通常国会 著作権法改正について」:https://www.bunka.go.jp/seisaku/chosakuken/hokaisei/r02_hokaisei/
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「m3u8とは|中身の見方・再生方法・MP4変換とエラーの直し方を実例で解説」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。