ABRを再生できるプレイヤーを用意する
さて、いよいよWebブラウザでHLSを再生してみましょう。勘が良い人は「これまで通りvideoタグのsrcにplaylist.m3u8を指定したら動くんじゃない?」と思うかもしれません。それは半分あっていて、半分間違っています。
というのも、実はWebブラウザ自身の機能としてHLSを読み込むことができるものは多くありません。HLSは実はAppleが開発したものであり、Appleのブラウザ(要はSafari)ではネイティブでサポートしているのですが、それ以外のブラウザ(パソコンのChromeやFirefox)ではサポートしていません(最近のAndroidスマートフォンのChromeではHLSをネイティブでサポートされていることがあります)。
では、どのように再生するのでしょうか? 実はvideoタグにはMSE/EMEという強力な動画再生にまつわるAPIが提供されており、それを用いると動画の実データを投げ込んで再生させることができたりします。
ということで、プレイリストやチャンクをAjax等で読み込み、それらをMSE/EMEに投げ込んでくれる、再生ライブラリが多数存在しており、それらを用いることで様々なWebブラウザ再生が実現できます。
MSE/EMEについては次回以降で詳しく説明することとして、今回はとりあえず再生してみましょう。今回はhls.jsというライブラリを利用します。
hls.jsには強力なデモサイトが存在し、こちらのサイトにHLSのURLを指定することでも簡単に再生検証が可能です(サイトがHTTPSのため、HLSのURLもHTTPSである必要があり、ローカルリソースを参照させるには不適切)。
今回はcdnjsに設置されたhls.jsを利用してみることにします。
これまで使ってきたindex.htmlを下記のように編集してみましょう。
<!DOCTYPE html> <html> <head> <title>Test streaming player</title> </head> <body> <script src="https://cdnjs.cloudflare.com/ajax/libs/hls.js/0.5.14/hls.min.js"></script> <video src="output_1080p.mp4" height="800" controls></video><br /> <video src="output_1080p_gop30.mp4" height="800" controls></video><br /> <video id="hls_video" height="800" controls></video> <script> var video = document.getElementById("hls_video"); if (Hls.isSupported()) { var hls = new Hls({ "debug": true }); hls.loadSource("playlist.m3u8"); hls.attachMedia(video); } else if (video.canPlayType("application/vnd.apple.mpegurl")) { video.src = "playlist.m3u8"; } </script> </body> </html>
このコードで、hls.jsがサポートしておらず(=MSEに対応しておらず)かつHLSにネイティブで対応しているプレイヤーではvideoタグにplaylist.m3u8をそのまま設定し、それ以外ではhls.jsを利用して再生するように振る舞います。
前編で行った通信速度設定をしながら画質が切り替わるか見てみましょう。どのファイルがダウンロードされているかはNetworkタブを見る事で確認できます。

また、Mediaタブを利用することで、どういった動画が再生されているかを確認することもできます(Mediaタブの表示方法はこちらを参照)。

バッファがある程度確保されているため、ネットワーク速度変更後にすぐ切り変わらないこともあります。その際は数分先や数分前にシークしてみるとすぐ効果を確認することができます。
コラム:チャンク/セグメントの長さ(秒数)とGOP長の関係
HLSにおいて、シーク時にはどのような挙動をしているか考えてみましょう。シークすると、バッファにその再生位置のデータが無ければ、その再生位置に対応するチャンクをプレイリストの情報から割り出し、チャンクファイルがロードされ、再生が実行されます。
そのためチャンクの長さがシークのパフォーマンスに繋がります(短くすればシーク時のバッファリングは少なくなる傾向にある)。
ではこのチャンクの長さは自由に決められるでしょうか? 実はそういうわけではありません。前編で紹介したGOPがここで登場します。
GOPの切れ目以外のフレーム位置で別ファイルに分割したらどうなるでしょうか? 分割後の後ろのファイルにおいて、冒頭デコードするために必要なキーフレームが存在しないフレームが登場してしまいます。それを再生しようとすると、当然ながら正しくデコードできずに破綻した絵が表示されてしまうことになります。
これを避けるためにファイル分割はGOPの切れ目で行う必要があり、それはすなわちチャンクの長さはGOP長の整数倍である必要があるということになります。
なおffmpegは賢いため、「-hls_time」が4秒に指定してあった場合「4秒付近でGOPの切れ目の位置」を自動的に検知して切り刻んでくれるため、雑なGOP設定であっても一応問題なく切り刻めるはずです。
まとめ
前編/後編合わせて、かなりいろいろな情報を詰め込んで紹介しました。ここまで連載を読んでくださった方は、Webブラウザに対して動画配信ができるようになっていると思います。
本稿までの内容を組み合わせれば、実はそれなりの商用配信に耐えうる動画配信が実現できたりします。いわゆる「コピペで動画配信サイトが作れた!」みたいな状態にはなれるでしょう。
「えっ? ほんとに?」と思うかもしれませんが、本当です。第1回で紹介した、エンコード、パッケージング、配信、プレイヤーについては、シンプルな形ですが全てご紹介することができました。深掘りはいくらでもできますが、これが基礎と思ってもらえて相違ありません。どうですか? 思ったよりシンプルだと思いませんか? そう思っていただければ幸いです。
以降の回では、それぞれの項目について少し掘り下げてご紹介していこうと思います。次回はMSE/EMEを利用した再生について詳しく解説していきます。