HTTP Live Streaming(HLS)配信の基本的な手順をまとめます。
去年の記事 「NginxのHTTP Pseudo-Streamingを試す」 ではNginxの疑似ストリーミング配信モジュールを試してみましたが、機能不足のため実サービスで使うのは難しいです。そのためWebサーバでストリーミング配信を行いたい場合は今回紹介するHLSなどの利用が推奨されます。
HTTP Live Streaming(HLS)とは
Apple公式のドキュメントを読む方が理解は進むと思いますが、一応ここでも簡単に概要を。
HTTP Live Streaming (also known as HLS) is an HTTP-based media streaming communications protocol implemented by Apple Inc.
HTTP Live Streaming(HLS)はHTTPベースのストリーミング配信プロトコルでAppleが開発しました。iOS 3.0/Android 4.0以降を搭載しているデバイスで利用可能です。
アーキテクチャは上図のように”Server”/”Distribution”/”Client”の3つで構成されています。まず、”Server”コンポーネントで元素材をMPEG-2 TS形式にエンコードし、ストリームセグメンタと呼ばれるツールを使って細かく分割されたメディアセグメントファイル(一連の低容量TSファイル)とインデックスファイル(M3U8プレイリスト)を生成します。そのファイル群を”Distribution”コンポーネント(一般的なWebサーバ)にデプロイします。ここまでで配信準備は完了です。あとはHLSに対応している”Client”ソフトウェア(動画再生プレイヤー)が”Distribution”に設置されたインデックスファイルにアクセスすれば配信が開始されます。
インデックスファイル(M3U8プレイリスト)
HLSのインデックスファイルはM3U形式(MP3プレイリスト)を拡張したM3U8プレイリストです。簡単な例を以下に示します。これはストリーム全体が各10秒の3つのメディアファイルに分割されているプレイリストになります。
[bash]
#EXTM3U
#EXT-X-TARGETDURATION:10
#EXT-X-MEDIA-SEQUENCE:1
#EXTINF:10,
media/segment0.ts
#EXTINF:10,
media/segment1.ts
#EXTINF:10,
media/segment2.ts
#EXT-X-ENDLIST
[/bash]
HLSはRTSPやMMSなどのストリーミング用プロトコルではなくHTTPを使うため、特別なストリーミングサーバを調達する必要がなく、ApacheやNginxなどの一般的なWebサーバで動画像を配信できるのでコスト面でも大きなメリットがあります。
HLS配信対応のよくあるシチュエーション
これまでPC向けにFlash Videoによる映像配信サービスを提供していたが、今の時代、スマートフォンやタブレットPCへも確実にリーチする必要がある。そのため、Flashを再生できないiOS/Android端末向けにはHTTP Live Streamingによる配信を行うことになった。
環境
CentOS 5.8 (x86_64, 仮想2コア) さくらのVPS 1Gプラン
GCC 4.4.7 (rpm: gcc44-4.4.7-1.el5/gcc44-c++-4.4.7-1.el5)
FFmpeg 1.1.1
Nginx 1.3.0
検証デバイス: iPhone 5(iOS 6.1), iPad 2(iOS 6.0), AQUOS PHONE ZETA SH-02E(Android 4.0.4)
CentOSのバージョンは5.x系としました。大規模配信インフラを運用している現場だと6.x系への移行はまだ難しいのでは、という判断からです。そう考えるとGCC4.4.7も新しいですけど、最適なバイナリを作るために一部のコンパイルオプションをどうしても使いたかったので4.4.7としました。また、動画像の配信を行うHTTPサーバはなんでも良いです。ここではNginxを使いますが、もちろんApacheでも問題なくHLS対応可能です。
FFmpegのインストール
MPEG-2 TSファイルへの変換やセグメント分割、プレイリスト(M3U8)作成にはFFmpegを利用しました。以前はオープンソースのストリームセグメンタ(segmenter)を併用することが多かったですが、現在ではFFmpeg単体でプレイリスト作成まで可能です。
yumでインストールする場合
yumでインストールする場合はRPMForgeリポジトリを追加しておきます。
[bash]
## もし追加していなければRPMForgeリポジトリを追加
$ wget http://pkgs.repoforge.org/rpmforge-release/rpmforge-release-0.5.2-2.el5.rf.x86_64.rpm
$ rpm -ivh rpmforge-release-0.5.2-2.el5.rf.x86_64.rpm
## ffmpeg, ffmpeg-develのインストール
$ yum install –enablerepo=rpmforge ffmpeg.x86_64 ffmpeg-devel.x86_64
$ ffmpeg
FFmpeg version 0.6.5, Copyright (c) 2000-2010 the FFmpeg developers
built on Jan 29 2012 23:55:02 with gcc 4.1.2 20080704 (Red Hat 4.1.2-51)
configuration: –prefix=/usr –libdir=/usr/lib64 –shlibdir=/usr/lib64 –mandir=/usr/share/man –incdir=/usr/include –disable-avisynth –extra-cflags=’-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector –param=ssp-buffer-size=4 -m64 -mtune=generic -fPIC’ –enable-avfilter –enable-avfilter-lavf –enable-libdirac –enable-libfaac –enable-libfaad –enable-libfaadbin –enable-libgsm –enable-libmp3lame –enable-libopencore-amrnb –enable-libopencore-amrwb –enable-libx264 –enable-gpl –enable-nonfree –enable-postproc –enable-pthreads –enable-shared –enable-swscale –enable-vdpau –enable-version3 –enable-x11grab
libavutil 50.15. 1 / 50.15. 1
libavcodec 52.72. 2 / 52.72. 2
libavformat 52.64. 2 / 52.64. 2
libavdevice 52. 2. 0 / 52. 2. 0
libavfilter 1.19. 0 / 1.19. 0
libswscale 0.11. 0 / 0.11. 0
libpostproc 51. 2. 0 / 51. 2. 0
Hyper fast Audio and Video encoder
usage: ffmpeg [options] [[infile options] -i infile]… {[outfile options] outfile}…
[/bash]
バージョンが0.65と古いです。ストリームセグメンタも実装されていないので前述のセグメンタを別途インストールする必要があります。
[bash]
## segmenterのコンパイル/インストール
## (古いバージョンの libavformat に対応しているので注意)
$ svn co http://svn.assembla.com/svn/legend/segmenter
$ cd segmenter
$ make
$ sudo make install
[/bash]
ソースからコンパイルしてインストールする場合
最新安定版のFFmpegを使いたい場合はソースコードからコンパイルしてインストールします。せっかく自前でコンパイルするなら環境に合わせて最適なバイナリを作ると良いです。また、ディスクスペースは100MB以上必要になりますので注意してください。
[bash]
## FFmpegのソースコードを取得
$ wget http://ffmpeg.org/releases/ffmpeg-1.1.1.tar.bz2
$ tar jxf ffmpeg-1.1.1.tar.bz2
$ cd ffmpeg-1.1.1
## CentOSでgcc4.4系を使いたい場合は –cc=gcc44 の指定を忘れずに
## 最適化オプションは各自環境に合わせてください
$ ./configure –prefix=/usr/local –cc=gcc44 –extra-cflags=’-march=native -mfpmath=sse -msse2′ –optflags=’-O2 -finline-functions’ –d
isable-avisynth –enable-avfilter –enable-libfaac –enable-libgsm –enable-libmp3lame –enable-libopencore-amrnb –enable-libopencore-amrwb
–enable-libtheora –enable-libvorbis –enable-libx264 –enable-postproc –enable-pthreads –enable-shared –enable-swscale –enable-vdpau
–enable-x11grab –enable-gpl –enable-nonfree –enable-version3
$ make -j2
$ sudo make install
$ ffmpeg
ffmpeg version 1.1.1 Copyright (c) 2000-2013 the FFmpeg developers
built on Feb 3 2013 12:56:16 with gcc 4.4.7 (GCC) 20120313 (Red Hat 4.4.7-1)
configuration: –prefix=/usr/local –cc=gcc44 –extra-cflags=’-march=native -mfpmath=sse -msse2′ –optflags=’-O2 -finline-functions’ –disable-avisynth –enable-avfilter –enable-libfaac –enable-libgsm –enable-libmp3lame –enable-libopencore-amrnb –enable-libopencore-amrwb –enable-libtheora –enable-libvorbis –enable-libx264 –enable-postproc –enable-pthreads –enable-shared –enable-swscale –enable-vdpau –enable-x11grab –enable-gpl –enable-nonfree –enable-version3
libavutil 52. 13.100 / 52. 13.100
libavcodec 54. 86.100 / 54. 86.100
libavformat 54. 59.106 / 54. 59.106
libavdevice 54. 3.102 / 54. 3.102
libavfilter 3. 32.100 / 3. 32.100
libswscale 2. 1.103 / 2. 1.103
libswresample 0. 17.102 / 0. 17.102
libpostproc 52. 2.100 / 52. 2.100
Hyper fast Audio and Video encoder
usage: ffmpeg [options] [[infile options] -i infile]… {[outfile options] outfile}…
[/bash]
最新版のFFmpegではストリームセグメンタが実装されているので、セグメンタの別途インストールは不要です。
上記オプションで作られたバイナリは libfaac を組み込んでいるため再配布はできません。
配布を考えないのであれば --march=native
をつけてコンパイルして良いかと思います。これを付けると環境に合わせて最適なオプションに展開してくれます。ただし、古いGCCだとこのオプションは効かないので注意してください。ちなみに僕の環境(さくらのVPS 1Gプラン)では以下のように展開されるようです。
-march=core2 -msahf --param l1-cache-size=32 --param l1-cache-line-size=64 --param l2-cache-size=4096 -mtune=core2
各種コーデック/コンテナライブラリの紹介
FFmpegに別途組み込んだコーデック/コンテナライブラリと各プロダクトへのリンクを載せておきます。基本的に ./configure make make install
でインストールできます。
参考: Install FFMPEG & x264 on CentOS
FFmpegは大きなソフトウェアなのでスムーズにコンパイルできないこともよくあります。僕の環境で発生したエラーとその対応方法を載せておきます。
* 「yasm not found, use –disable-yasm for a crippled build」 とエラーが出る場合
–disable-yasm は使わずにちゃんとyasmをインストールしておきます。yumで入るものは古すぎてx264のビルドができないため、こちらもソースからコンパイルして入れます。また、yasmのビルドにはCMakeを使うので、もし入っていなければ予めインストールしておきます(最近はCMake対応しているプロダクトが増えてきましたね)。
[bash]
## もしCMakeが入っていなければインストール
$ sudo yum install cmake.x86_64
## yasmのソースコードを取得
$ git clone git://github.com/yasm/yasm.git
$ cd yasm
## CentOSでgcc4.4系を使いたい場合は gcc44/g++44 をオプション指定する
$ cmake -DCMAKE_C_COMPILER=gcc44 -DCMAKE_CXX_COMPILER=g++44 .
$ make
$ sudo make install
$ /usr/local/bin/yasm –version
yasm 1.2.0.22.g607fe
Compiled on Jan 19 2013.
Copyright (c) 2001-2011 Peter Johnson and other Yasm developers.
Run yasm –license for licensing overview and summary.
[/bash]
* 「Unknown option “–enable-XXXX”.」とエラーが出る場合
古いバージョンのFFmpegで使われていたオプションを指定している可能性が高いです。configureオプションから外しておきます。
* 「ERROR: libx264 must be installed and version must be >= XXXX」とエラーが出る場合
x264のバージョンが古いです。もしyumでインストールしてしまっている場合は削除して、こちらもソースからコンパイルして入れます。
[bash]
$ git clone git://git.videolan.org/x264.git
$ cd x264
## CentOSでgcc4.4系を使いたい場合はconfigureファイルを編集する
$ vim configure ## 変数CCに gcc44 を指定
414c414
< CC="${CC-${cross_prefix}gcc}"
---
> CC=”${CC-${cross_prefix}gcc44}”
$ ./configure –enable-shared –enable-pic –extra-cflags=”-march=native -mfpmath=sse -msse2″
$ make
$ sudo make install
$ /usr/local/bin/x264 –version
x264 0.129.2245M bc13772
built on Jan 19 2013, gcc: 4.4.7 20120313 (Red Hat 4.4.7-1)
configuration: –bit-depth=8 –chroma-format=all
x264 license: GPL version 2 or later
[/bash]
--extra-cflags
オプションは各自環境に合わせて指定してください。
エンコード/プレイリスト作成
FFmpegを利用してHLS配信用に動画像ファイルのエンコードとプレイリストの作成を行います。
* MPEG-2 TS 1280×720(16:9) 映像: H.264/AVC [email protected] 24fps 256kbps, 音声:AAC 128kbps
* HLS: VOD, 10秒毎に分割指定 (segment_time), キャッシュ許可
[bash]
## セグメントファイル群を置くディレクトリ作成
$ mkdir streamfiles
$ ls
streamfiles/ test.mp4
## エンコード、プレイリスト作成
$ ffmpeg -i test.mp4 -threads 2 -codec:v libx264 -s:v 1280×720 -aspect:v 16:9 -b:v 256k -re -fpre:v libx264-hls.ffpreset -codec:a libfaac -ar:a 44100 -b:a 128k -ac:a 2 -map 0 -f segment -segment_format mpegts -segment_time 10 -segment_list stream.m3u8 streamfiles/stream%03d.ts
[/bash]
x264のプリセットは以下のように品質重視の設定にしました。今回の検証デバイスは全てHigh Profileに対応してるのですが、ここではMain Profile Level 3.1で作っています。詳細なエンコードパラメータについてはこれから煮詰めていこうと思いますが、実際にはエンコードサーバの資源(CPU/RAMなど)や配信開始までの猶予時間、再生保障するデバイスなどいろいろ折り合いがあるでしょうし、なかなか難しいところです。。
* libx264-hls.ffpreset
[bash]
vprofile=main
coder=1
level=31
crf=22
qcomp=0.6
qmin=10
qmax=51
qdiff=4
i_qfactor=0.71
maxrate=14000k
bufsize=14000k
g=250
keyint_min=25
sc_threshold=40
me_method=umh
me_range=16
subq=6
refs=4
trellis=1
bf=16
b_strategy=1
partitions=+pi8x8+pi4x4+pp8x8+pb8x8
cmp=chroma
flags=+loop-global_header
deblock=0:0
[/bash]
生成されたM3U8プレイリストは以下のような内容になりました。FFmpegで指定できる分割時間は目安程度のものなんでしょうか? ここでは10秒指定にしたのですがTARGETDURATIONは15秒になりました。
[bash]
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-ALLOWCACHE:1
#EXTINF:14.750000,
streamfiles/stream000.ts
#EXTINF:8.791667,
streamfiles/stream001.ts
#EXTINF:9.500000,
streamfiles/stream002.ts
#EXTINF:7.000000,
streamfiles/stream003.ts
#EXTINF:10.750000,
streamfiles/stream004.ts
#EXTINF:9.875000,
streamfiles/stream005.ts
#EXTINF:10.000000,
streamfiles/stream006.ts
… 省略
#EXTINF:9.916667,
streamfiles/stream095.ts
#EXTINF:9.500000,
streamfiles/stream096.ts
#EXTINF:1.834956,
streamfiles/stream097.ts
#EXT-X-TARGETDURATION:15
#EXT-X-ENDLIST
[/bash]
Nginxの設定
mime.typesファイルにMIME Typeを以下のように2つ追加しておきます。
* mime.types
types { ... 省略 application/x-mpegURL m3u8; video/MP2T ts;
これだけ、簡単ですね。Apacheの場合も同様にMIME Typeの設定だけでOKです。
## Apache2.xの場合 AddType application/x-mpegURL .m3u8 AddType video/MP2T .ts
クライアントWebページの作成
HTML5 videoタグを利用してクライアントWebページ(プレイヤー)を作ります。videoタグのsrc属性にはプレイリストファイルを指定してください。
* /test/hls/index.html
[html]
HTTP Live Streaming Test
[/html]
再生試験
* 構成
test/hls |-- stream.m3u8 |-- index.html `-- streamfiles |-- stream000.ts |-- stream001.ts |-- stream002.ts ... 省略 |-- stream095.ts |-- stream096.ts `-- stream097.ts
あとは、/test/hls/index.html にアクセスしてプレイヤーの再生ボタンを押すだけです。無事にコンテンツが再生されたら成功。
* アクセスログ例
Nginxのアクセスログを確認。クライアントは約10秒ごとに対応したTSファイルをリクエストしていることがわかります(以下のログはNginxのデフォルトフォーマットのログをIPなど一部情報を削って載せています)。
[27/Jan/2013:20:49:35 +0900] "GET /test/hls/streamfiles/stream013.ts HTTP/1.1" 200 586936 "-" "stagefright/1.2 (Linux;Android 4.0.4)" [27/Jan/2013:20:49:44 +0900] "GET /test/hls/streamfiles/stream014.ts HTTP/1.1" 200 599720 "-" "stagefright/1.2 (Linux;Android 4.0.4)" [27/Jan/2013:20:49:55 +0900] "GET /test/hls/streamfiles/stream015.ts HTTP/1.1" 200 678116 "-" "stagefright/1.2 (Linux;Android 4.0.4)"
今回使った検証デバイスは新しい(2013/02現在)デバイスということもあり、高品質な設定でエンコードしたHD動画でもスムーズに再生してくれました(^^ エンコードパラメータの調整には時間をかけましたが、再生確認までは特につまづくところもありませんでした。あとは暗号化によるコンテンツ保護や帯域制御などを試していきたいと思います。

余談 「ITU、次世代コーデックのHEVC/H.265を承認」
つい先日、次世代ビデオコーデックのHEVC(High Efficiency Video Coding)/H.265がITUに承認されました。今ではインターネット業界でのサービス開発はスマートフォン向けにシフトしましたが、低帯域のモバイルネットワークでも高品質の映像をユーザーに届けられるというのは素晴らしいと思います。以前AdobeがFlashは「プレミアムビデオとゲーム市場にフォーカスする」と言ってましたが(参照: Flashランタイムのロードマップ)、もし本当ならいち早く対応してほしいなぁと願うところです。
* 参考:
HTTP Live Streaming Resources – Apple Developer
Install FFMPEG & x264 on CentOS
[iPhone] HTTP Live Streaming
HTTP Live Streaming(Encrypt)
One thought