HLSの仕組み
HLS(HTTP Live Streaming)は、Appleが開発した動画配信の方式です。回線の速さに合わせて、画質を切り替えながら再生できます(アダプティブストリーミング)。今では、ほぼすべての環境が対応しています。FFmpegでは -f hls を指定すると、HLS用のファイルを書き出せます。
HLSの出力は、次の2種類のファイルです。
| ファイル種別 | 拡張子 | 説明 |
|---|---|---|
| プレイリスト | .m3u8 |
セグメントのURLを並べた目次ファイル |
| セグメント | .ts |
動画と音声を短く区切ったファイル(通常2〜10秒) |
Webサーバーは、これらのファイルをそのままHTTPで配信するだけです。プレーヤー(ブラウザの hls.js、iOS/macOS の標準プレーヤー、Android)は、まずプレイリストを読み込み、再生に必要なセグメントを順に取得します。
最小限のHLS出力
ffmpeg -i input.mp4 -c:v libx264 -c:a aac -f hls /tmp/playlist.m3u8
初期設定のままで、/tmp/playlist.m3u8 と、番号付きのセグメントファイル(/tmp/playlist0.ts、/tmp/playlist1.ts など)ができます。
セグメント長の制御
-hls_time N で、セグメントの長さの目標を秒で指定します(デフォルトは 2)。
ffmpeg -i input.mp4 -c:v libx264 -crf 23 -c:a aac -f hls -hls_time 6 /tmp/playlist.m3u8
セグメントは、キーフレーム(単独で表示できるフレーム)の位置でしか区切れません。libx264 の初期設定では、キーフレームの間隔が最大250フレーム(30fpsで約8.3秒)です。そのため -hls_time 6 を指定しても、8秒を超えるセグメントができることがあります。長さをそろえるには、キーフレームの間隔を固定します。
ffmpeg -i input.mp4 -c:v libx264 -crf 23 -g 60 -keyint_min 60 -sc_threshold 0 -c:a aac -f hls -hls_time 6 /tmp/playlist.m3u8
-g 60 で、60フレームごとにキーフレームを入れます(30fpsなら2秒ごと)。-sc_threshold 0 は、場面の切り替わりで余分なキーフレームを入れない指定です。-hls_time 6 と組み合わせると、1つのセグメントにキーフレームの区間がちょうど3つ入ります。
プレイリストにすべてのセグメントを保持する
-hls_list_size のデフォルトは 5 で、プレイリストには最新の5セグメントしか載りません(ライブ配信向けの設定です)。VOD(録画済みの動画を、見たいときに再生できる配信)では 0 にして、すべてのセグメントを載せます。
ffmpeg -i input.mp4 -c:v libx264 -crf 23 -c:a aac -f hls -hls_time 6 -hls_list_size 0 /tmp/playlist.m3u8
セグメントファイル名のカスタマイズ
-hls_segment_filename で、セグメントファイルの保存先と名前の付け方を指定します。
ffmpeg -i input.mp4 -c:v libx264 -crf 23 -c:a aac -f hls -hls_time 6 -hls_list_size 0 -hls_segment_filename "/tmp/segment_%03d.ts" /tmp/playlist.m3u8
%03d の部分には、0で埋めた3桁の連番が入ります(segment_000.ts、segment_001.ts など)。フォルダーを含めたパスも指定できます。
ffmpeg -i input.mp4 -c:v libx264 -crf 23 -c:a aac -f hls -hls_time 6 -hls_list_size 0 -hls_segment_filename "/tmp/hls/seg_%04d.ts" /tmp/hls/playlist.m3u8
出力先のフォルダーは、先に作っておいてください。FFmpegは自動では作りません。
VOD用の完全な例
動画ファイルをVOD用のHLSに変換する、実用的なコマンド例です。
ffmpeg -i input.mp4 -c:v libx264 -preset slow -crf 22 -c:a aac -b:a 128k -f hls -hls_time 6 -hls_list_size 0 -hls_segment_filename "/tmp/hls_vod/seg_%04d.ts" /tmp/hls_vod/index.m3u8
/tmp/hls_vod/ フォルダーをまるごとHTTPサーバーに置き、プレーヤーには index.m3u8 のURLを指定します。
ライブストリーミング — 古いセグメントの自動削除
ライブ配信では、プレイリストから外れた古いセグメントは使いません。-hls_flags delete_segments を付けると、自動で削除されます。
ffmpeg -i input.mp4 -c:v libx264 -crf 23 -c:a aac -f hls -hls_time 6 -hls_list_size 5 -hls_flags delete_segments /tmp/playlist.m3u8
-hls_list_size 5 でプレイリストには5セグメントを載せます。プレイリストから外れたセグメントは自動で消えますが、最後に外れた1つだけは残るので、ディスクには6つのセグメントが残ります。残す数は -hls_delete_threshold で変えられます(初期値は 1)。
アダプティブビットレートラダー(概要)
ABRラダー(用意する画質の段階の組み合わせ)を作るには、同じ動画を複数のビットレート・解像度でエンコードします。そのうえで、それぞれの画質(バリアント)を指すマスタープレイリストを作ります。手順は次のとおりです。
- ビットレート・解像度ごとに、別々のHLSとしてエンコードする
#EXT-X-STREAM-INFタグで各バリアントを並べた、マスタープレイリスト(.m3u8)を作る
FFmpeg では、1つの -f hls 出力に -var_stream_map(例: "v:0,a:0 v:1,a:1")と -master_pl_name を指定し、出力名に %v を入れる(例: stream_%v.m3u8)と、各画質のプレイリストとマスタープレイリストを1回の実行でまとめて書き出せます。ただし、出力には画質ごとに映像と音声のストリームが必要です。2段階なら -map 0:v -map 0:a -map 0:v -map 0:a で映像と音声を2回ずつ割り当て、-s:v:1 1280x720 -b:v:1 3M のように、ストリームごとに解像度やビットレートを指定します。本番のABR配信では、AWS MediaConvert や、FFmpegをもとにした専用の変換ツールを使うほうが現実的です。
主要オプション一覧
| オプション | デフォルト | 説明 |
|---|---|---|
-hls_time N |
2 | セグメントの長さの目標(秒) |
-hls_list_size N |
5 | プレイリストの最大セグメント数(0=全保持) |
-hls_segment_filename パターン |
(自動) | セグメントファイルの命名パターン |
-hls_flags delete_segments |
オフ | プレイリストから除外されたセグメントを自動削除 |
-hls_flags append_list |
オフ | 既存プレイリストに追記(上書きせず) |
-start_number N |
0 | セグメントの開始シーケンス番号 |
よくある問題
セグメントがプレイリストに含まれていない
VODで -hls_list_size 0 を付け忘れると、プレイリストには最新のN件のセグメントしか載りません。再生はできますが、最後の部分だけになります。
出力ディレクトリが存在しない
セグメントの保存先のフォルダーがないと、Failed to open file というエラーが出て、セグメントが書き出されません。FFmpeg 8.1 では、1つ目のセグメントを書き終える時点で Conversion failed! と表示されて止まります。動画が短くてセグメント1つに収まるときは、最後まで進んで終了コード 0 で終わりますが、この場合もセグメントはできません。先にフォルダーを作ってから実行してください。
mkdir -p /tmp/hls_output
ffmpeg -i input.mp4 -c:v libx264 -crf 23 -c:a aac -f hls -hls_time 6 -hls_list_size 0 -hls_segment_filename "/tmp/hls_output/seg_%04d.ts" /tmp/hls_output/playlist.m3u8
プレーヤーで再生できない
HTTPサーバーが、正しいMIMEタイプ(ファイルの種類を伝える情報)を返しているか確認してください。
.m3u8→application/vnd.apple.mpegurlまたはapplication/x-mpegURL.ts→video/mp2t
サーバーによっては、この設定を自分で追加する必要があります。
関連記事
よくある質問
セグメント時間は何秒がいい?
VOD なら 6〜10 秒(CDN のコストを抑えやすい)、ライブなら 2〜4 秒(遅延が小さい)が目安です。Apple は以前は 10 秒を勧めていましたが、今は短くする傾向です。
master playlist と media playlist の違いは?
master playlist は、複数の画質を並べた「索引」です。media playlist は、実際のセグメントの一覧を持つ「中身」です。画質を切り替える配信(アダプティブビットレート)には、master が必要です。
音声と映像を別々にセグメント化したい
-var_stream_map で、音声を別のグループ(agroup)にします。複数の言語の音声に対応するときの土台になります。"v:0,a:0" と書いた場合は、映像と音声が同じセグメントに入った1つの m3u8 になり、音声は分かれません。
ffmpeg -i input.mp4 -map 0:v -map 0:a -c:v libx264 -c:a aac -f hls -hls_time 6 -hls_list_size 0 -var_stream_map "v:0,agroup:aud a:0,agroup:aud" -master_pl_name master.m3u8 /tmp/stream_%v.m3u8
映像だけの stream_0.m3u8、音声だけの stream_1.m3u8、両方をまとめる master.m3u8 ができます。
セグメントが大きすぎる
キーフレームの間隔を -g 60(30fps で 2 秒ごと)にして、-hls_time の区切りに合わせます。これをしないと、区切りたい位置にキーフレームがなく、セグメントが大きくなりがちです。
HLS の最小遅延(LL-HLS)はどう違う?
通常の HLS は遅延が 15〜30 秒、LL-HLS は 2〜6 秒です。LL-HLS には、partial segment(セグメントをさらに細かく分けたもの)と blocking response(更新が出るまでサーバーが応答を待つ仕組み)が必要です。詳しくは低遅延HLS(LL-HLS)の記事を見てください。