HLS プロトコル入門
HLS プロトコルの基本的な仕組み、エンコードフロー、ファイル形式、再生モード、セキュリティ、プッシュ配信、およびトラブルシューティングを解説します。
HLS は HTTP Live Streaming の略で、Apple が提唱する HTTP ベースのストリーミングネットワーク転送プロトコルです。ライブ配信とオンデマンド配信の両方をサポートし、マルチビットレート、音声・動画のデュアルトラック、字幕などの機能も備えています。その仕組みは、1本の動画を複数の小さな動画セグメントに分割し、それらの断片をつなぎ合わせて再生を実現するというものです。
HLS プロトコルの仕様は以下のとおりです。
- 動画のコンテナフォーマットは
TSです。 - 動画のコーデックは
H264、音声のコーデックはMP3、AAC、またはAC-3です。 TS動画ファイル自体に加えて、再生を制御するためのm3u8ファイル(テキストファイル)も定義されています。
長所:
- 特殊な状況下でのファイアウォールによるブロックを回避できる
- サーバーの拡張が容易。
RTMPはステートフルなプロトコルであるため、再生中の各クライアントの状態を維持する必要があり、動画サーバーをスムーズに拡張するのが難しい。一方HLSはステートレスなプロトコル(HTTP)に基づいており、クライアントはサーバーに保存された通常のTSファイルを順番にダウンロードして使用するだけなので、負荷分散は通常のHTTPファイルサーバーの負荷分散と同様にシンプルである - ビットレート自适应(ABR)
短所:
- ライブ配信シナリオにおける高い遅延(録画配信には遅延の影響なし)
- 大量の
TSセグメントファイルがサーバーのストレージとリクエストに負荷をかける
HLS ライブ配信フロー
- AVInputs
音声・動画ソースの取り込み
- Server
サーバーコンポーネントは入力されたメディアストリームを取得し、メディアエンコーダで MPEG-4(H.264 ビデオと AAC オーディオ)形式にエンコードした後、ハードウェアで MPEG-2(MPEG-2 transport stream)トランスポートストリームにパッケージ化します。図に示されているように、トランスポートストリームは stream segmenter を通過し、ここで MPEG-2 トランスポートストリームが小さな断片に分割され、1つまたは複数のシリーズの .ts 形式のメディアファイルとして保存されます。この処理には Apple stream segmenter などのエンコードツールが必要です。
動画は fMP4 ファイル(新版)または ts ファイル(旧版)となり、音声のみの場合は通常 ADTS ヘッダー付きの AAC、MP3、または AC-3 形式の小さな音声セグメントにエンコードされます。
サーバー側ではハードウェアエンコードとソフトウェアエンコードの2つの方式があり、どちらも上記のルールに従って既存のメディアファイルをセグメント化し、インデックスファイルで管理する機能を提供します。ソフトウェアによるセグメント化には、通常 Apple 社が提供するツールまたはサードパーティの統合ツールが使用されます。
- Distribution
HTTP サービスを提供し、Server で作成された m3u8 インデックスと ts セグメントファイルを配信します。
- Clients
m3u8リソースをリクエストします。
HLS プロトコルには、インデックスファイルと ts/fMP4 ファイルの2種類のファイルが含まれます。
以下の2種類があります。
- Index file
- Alternate Index file
HLS Index
各 ts ファイルは複数の ts packet で構成され、各 ts packet は 188 バイトです。
理由:ATM(Asynchronous Transfer Mode) システムに適合させるため。
is motivated by the fact that the payload of the ATM Adaptation Layer-1 (AAL-1) cell is 47 bytes. Therefore, four AAL-1 cells can accommodate a single TS packet.
HLS TS
HLS fMP4
EXT-X-VERSION 7 で fMP4 がサポートされました。
fMP4 は MPEG-4 Part 12 に基づくストリーミングメディア形式で、mp4 と非常に似ていますがいくつかの違いもあります。fMP4 はストリーミング再生のニーズにより適しています。
fMP4 は h.265 をサポートしており、帯域幅を大幅に節約できるため、特に映像監視の分野で現在の動画の主流になりつつあります。
-
オンデマンド配信(VOD) 現在の時点ですべての
indexファイルとtsファイルを取得できます。2次indexファイルにはすべてのtsファイルのアドレスが記録されています。このモードでは、クライアントはすべてのコンテンツにアクセスできます。上記の例は、オンデマンド配信モードにおけるm3u8の構造です。 -
ライブ配信
M3U8とtsファイルがリアルタイムで生成されます。インデックスファイルは常に動的に変化するため、再生時には継続的に2次indexファイルをダウンロードして、最新のtsファイルを取得して動画を再生する必要があります。2次indexファイルの末尾に#EXT-X-ENDLISTマークがない場合、それはLive動画ストリームであることを示します。
暗号化情報:#EXT-X-KEY:METHOD=AES-128,URI=“xx.key”,IV=xxx
FairPlay Streaming
FairPlay Streaming is:
- A secure key delivery mechanism Content Key is protected on the network and on the client during playback
- Key delivery is transport agnostic Easy to integrate with existing key server infrastructure
- Requires protected HDMI for external output
フロー
HLS Fair Play
Conceptually, HTTP Live Streaming consists of three parts: the server component, the distribution component, and the client software. In a typical configuration, a hardware encoder takes audio-video input, encodes it as HEVC video and AC-3 audio, and outputs a fragmented MPEG-4 file or an MPEG-2 transport stream. A software stream segmenter then breaks the stream into a series of short media files, which are placed on a web server. The segmenter also creates and maintains an index file containing a list of the media files. The URL of the index file is published on the web server. Client software reads the index, then requests the listed media files in order and displays them without any pauses or gaps between segments.
ダウンロード先: https://developer.apple.com/download/all/?q=HLS
インストール後、go の example があることを確認
HLS Go Server
brew install go で go をインストールし、サービスを起動します。
# tony @ tonyMBP in ~/Desktop/hls_server [14:48:18]$ go run ll-hls-origin-example.goll-hls-origin-example.go:43:2: no required module provides package github.com/fsnotify/fsnotify: go.mod file not found in current directory or any parent directory; see 'go help modules'
# tony @ tonyMBP in ~/Desktop/hls_server [14:48:25] C:1$ go mod init hls_servergo: creating new go.mod: module hls_servergo: to add module requirements and sums: go mod tidy
# tony @ tonyMBP in ~/Desktop/hls_server [14:49:23]$ go buildll-hls-origin-example.go:43:2: no required module provides package github.com/fsnotify/fsnotify; to add it: go get github.com/fsnotify/fsnotify
# tony @ tonyMBP in ~/Desktop/hls_server [14:49:28] C:1$ go get github.com/fsnotify/fsnotifygo: downloading github.com/fsnotify/fsnotify v1.5.4go: downloading golang.org/x/sys v0.0.0-20220412211240-33da011f77adgo: added github.com/fsnotify/fsnotify v1.5.4go: added golang.org/x/sys v0.0.0-20220412211240-33da011f77ad
# tony @ tonyMBP in ~/Desktop/hls_server [14:49:48]$ go run ll-hls-origin-example.goListening on http://:8443/$ mediastreamsegmenter -w 499 -t 1 224.0.0.50:9121 -s 16 -D -T -f ~/Desktop/hls_server/hlsシステム内蔵カメラで音声・動画をキャプチャすることも、ローカル動画ファイルを指定することもできます。
$ ffmpeg -f avfoundation -list_devices true -i ""
[AVFoundation indev @ 0x7f924d904400] AVFoundation video devices:[AVFoundation indev @ 0x7f924d904400] [0] FaceTime高清摄像头(内建)[AVFoundation indev @ 0x7f924d904400] [1] Capture screen 0[AVFoundation indev @ 0x7f924d904400] AVFoundation audio devices:[AVFoundation indev @ 0x7f924d904400] [0] LarkAudioDevice[AVFoundation indev @ 0x7f924d904400] [1] 外置麦克风[AVFoundation indev @ 0x7f924d904400] [2] MacBook Pro麦克风
$ ffmpeg -f avfoundation -framerate 30 -pixel_format uyvy422 -i "0:" -c:v h264 -fflags nobuffer -tune zerolatency -f mpegts udp://192.168.1.5:9121$ ffmpeg -re -i "/Users/tony/Downloads/sample.mp4" -c:v h264 -fflags nobuffer -tune zerolatency -f mpegts udp://192.168.1.5:9121seekが不正確な場合の解決方法は?mp4は指定したタイムスタンプにseekできますが、tsはファイルの特定のpositionにseekするもので、指定した時点に直接seekすることはできません。ffplay.cのevent_loop関数にseekに関するコードが含まれています。tsの場合、具体的なseek操作の関数呼び出し関係はavformat_seek_file()=> av_seek_frame() => seek_frame_internal() => seek_frame_byte()です。mp4の場合、具体的なseek操作の関数呼び出し関係はavformat_seek_file()=> av_seek_frame() => seek_frame_internal() =>mov_read_seek()です。
ts の seek ロジックは次のとおりです。ファイルの位置が与えられると、ファイルポインタをその位置に直接移動します。その後 read_packet() を呼び出して ts パケット(188バイト)を読み取る際、以前に seek 操作を行っているため、ファイルポインタが ts packet のヘッダー位置(ヘッダーは 0x47 byte で始まる)を指していない可能性が高く、その場合は mpegts_resync() を呼び出して再同期を行いヘッダーを見つけてから、完全な ts packet を再度読み取る必要があります。
mp4 の seek 操作ロジックは次のとおりです。seek 対象のタイムスタンプが与えられると、mp4 内の各パケットのインデックス情報に基づいて、そのタイムスタンプに対応するパケットを見つけます。以下の mp4 のファイル構成からわかるように、Sample Table を利用することで、任意のタイムスタンプに対応する video/audio データパケットを素早く見つけることができます。
結論
mp4ファイルの場合、インデックステーブルがあるため、特定のタイムスタンプに対応するデータを素早く見つけることができ、seek操作は高速に完了します。tsファイルにはタイムスタンプとデータパケット位置の対応関係がないため、プレイヤーがseekのタイムスタンプts_seekを与えられた場合、まずファイルのビットレートからおおよその位置 pos を推定し、その位置のデータパケットのタイムスタンプts_actualを取得します。ts_actual<ts_seekの場合はさらに後方のデータパケットを読み続ける必要があり、ts_actual>ts_seekの場合は前方のデータパケットを読み、ts_seekに対応するデータパケットが見つかるまで続けます。そのためtsファイルの操作はより時間がかかる可能性があります。tsがCBRストリームの場合、ts_actualとts_seekの差は一般に小さく、seekは比較的速いですが、tsがVBRストリームの場合、ts_actualとts_seekが大きく異なる可能性があり、seekは比較的遅くなります。
HLS Seek
- 異なる解像度の
ts/fMP4ファイルの互換性問題 Android 端末でm3u8動画を再生する際に、画面が乱れる問題が発生しました。原因はtsの解像度が変化したことでした。 この場合は必ずhevc_mp4toannexbを追加する必要があります。元のプロトコルはh264_mp4toannexbのみをサポートしています。H.264/5のビットストリームにはAnnex-BとAVCCの2つの形式があります。AVCCは長さ情報でNALUを分割し、mp4やflvなどのコンテナ形式で使用されます。Annex-Bはstart code(0x000001 または 0x00000001)でNALUを分割し、mpegtsストリーミングメディアファイルで使用されます。hevc_mp4toannexbを追加すると、すべてのフレームで動画の幅と高さを解析できるようになり、解像度切り替え時の画面乱れの問題が発生しなくなります。
参考資料
- https://developer.apple.com/documentation/http_live_streaming/understanding_the_http_live_streaming_architecture
- https://en.wikipedia.org/wiki/MPEG_transport_stream
- http://anddymao.com/2021/08/03/2021-08-03-%E4%B8%80%E7%A7%8D%E4%B8%87%E8%83%BDhls%E5%8D%8F%E8%AE%AE%E8%A7%A3%E6%9E%90%E6%96%B9%E6%B3%95/