HLSで遊ぶ - プレイヤーお試し, 音声切り替え, Adaptive Bitrate Streamingなど

動画を HLS で配信することについていろいろ書きます。実際に動く例もいっぱい作った！

# ソースコード

GitHub に上げておいたので(参考にして|遊んで)みてください。

```shell
git clone https://github.com/mkiuchi/hls-demos.git
cd hls-demos
make
python3 -m http.server 8080
```

ブラウザで以下のようにURLを開く

http://localhost:8080/static/videojs.html?file=/vod/test1-01.m3u8

その他のデモについては README.md を参照のこと。

# HLSとは, ABRとは

ここでごちゃごちゃ書くよりも、世の中にいろいろ素晴らしいドキュメントがあるので見てみてほしい。例えば以下のようなものなど。

- [HLSとは？：ストリーミング配信を実現する技術](https://blog.socialcast.jp/05/post-729/)
- [HTTP Live Streaming, HLSのまとめ　概要・仕組み・課題など](https://jp.vcube.com/sdk/blog/hls-http-live-streaming.html)

[MPEG-DASHというほぼ同じことをする違うフォーマット](https://qiita.com/gabby-gred/items/c1a3dbe026f83dd7e1ff)もある。個人的には見にくいので HLS の方がすき。MPEG-DASH じゃないと ABR できないと書かれているページもあったけど別にそんなことない HLS でも ABR できる。

ABR というのは [Adaptive BitRate streaming](https://en.wikipedia.org/wiki/Adaptive_bitrate_streaming) のこと。要するに通信環境の変化に合わせて動的に映像のビットレート(≒解像度)を変更すると UX 的にはいい感じですよね？ということ。 YouTube や Abema TV とかで使われているので世間的なイメージとしてはやってて当然的な雰囲気がある気がする。通勤電車の中でスマホで映像見たい、的な要件がある場合は必須だと思っていい。

あと一応書いておくと、HLS で使用するファイルはあくまで配信に特化したもので、もともとの映像ファイルの代わりに使えるような代物ではない。ある程度の期間で使い捨てるものだと思ったほうがいい。HLS に変換したからって元ファイルは削除しないほうがいい。

# 送出側に必要なもの

ffmpeg と web サーバがあれば個人用途では十分。用途によって(ライブストリーミングなど)はエンコード時間を短くしたい、低いビットレートで高解像度な映像を配信したいなどの要件があると思うので、新し目の Intel CPU とか nvenc に対応した NVIDIA GPU があるとなおいいとは思う(後述)。

ちなみに筆者の環境は以下のような感じ。これで 2021 年 1 月時点で概ね不満ないです。

- Intel Core i5 6500
- MEM 8GB
- 256GB SSD
- NVIDIA GeForce GTX 1650
- Ubuntu Server 20.04
    - nginx
    - ffmpeg(映像はnvenc, 音声はaac)
    - 自作の管理用ソフト

大規模配信したかったらそれなりにお金がかかる。主要3クラウドはそれぞれソリューションがある ( [AWS (Elemental MediaConvert, MediaLive など)](https://aws.amazon.com/jp/mediaconvert/), [GCP(Transcoder API) ](https://medium.com/google-cloud-jp/google-cloud-transcoder-api-44a8ad6f8791), [Azure (Azure Media Service) ](https://azure.microsoft.com/ja-jp/services/media-services/) )し、 SaaS系だと [Wowza](https://www.wowza.com/), [Zencoder(Brightcove)](https://www.brightcove.com/ja/zencoder) とかある。お値段はそれなりにかかるので個人だと厳しいんじゃないかな。別に非公開じゃないなら YouTube とか Twitch という選択肢もあるしね今は。

# HLSプレイヤーあれこれに関するお気持ち

世の中いろいろHLSに対応したプレイヤーがある。いくつか使ってみたので感想など書いてみる。

## Video.js

https://videojs.com/

![01.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/52237/2cea9e20-a81f-6f5e-42b1-4be207f56147.jpeg)

良くも悪くもザ・標準。ロード中の円がぐるぐる回るのが二重になってて何これバグ？って思うけど2週間も使ってると見慣れる。みんな使ってていろいろ情報があるけど古いものもあってとまどうこともしばしば。ちなみに今は Video.js 単体でも HLS に対応しているし ABR にも対応している(ただし意図的にストリームを選択することはできない)。ライブストリーム(=終了時間が定義されていない HLS マニフェスト)を読むとちゃんとライブモードになってシークバーとかが消えるんだよね。感心。UI のカスタマイズはそれなりにめんどくさい。

みんな使ってるので安心といえるかもしれないが、使っててつまんなくなる気もする。ちょっと UI 的にダサいというか。

メインの開発は [Brightcove 社](https://www.brightcove.com)。この業界だと老舗ですよね。そういえば昔は Brightcove+Akamai が業務用だと鉄板的な話を聞いたことある気もする。業界の人じゃないから知らんけど。今はどうなんだろうねよく知りません。

## Shaka

https://github.com/google/shaka-player

![02.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/52237/469bda37-0c86-f587-2c0b-78a4ba998fe5.jpeg)

Google 製！ABR はもちろんおおよそプレイヤーに期待されている動作(Video.js でできることに加えて解像度切り替え、音声切り替え、再生速度変更、字幕対応など)は全部入っていて魅力的だがいかんせんドキュメントがわかり辛い。[デモページ](https://shaka-player-demo.appspot.com/)見て気に入って使い始めるとハマる(ハマった)。もっと単純な例がほしいのに凝りすぎなんだよねこのデモ。私は UI をデモページと同じように動作させるようにするのに2日かかった。初期化も普通に作るとちゃんと成功せず、ちゃんと Promise とかで順列化しないといけないというハマりがある・・・。

UI はなんとなく YouTube っぽいと言えなくはないかも・・・というルックアンドフィール。カスタマイズもできるらしい。 Chromecast への配信とかもできるらしいですよ。試してないけど。

Video.js でいい感じだったライブストリーム読み込み時は終了時間がわけわからない数字になる。イマイチ。

ABR については Shaka の方がビットレートの変化に対して積極的にストリームの切り替えを行うように思われた。 Video.js のほうが小難しい判断をしているのかあまりアグレッシブに切り替えない。個人的には Shaka の挙動のほうが好み。

## hls.js(デモには含めていない)

https://github.com/video-dev/hls.js/

HLS のストリーム受信、制御に特化したライブラリ。表示や再生にはvideo , audio タグを使用してブラウザ標準のインターフェイスを使うか、自分で作り込む。UI の作り込みをしなければいけない or 作り込む意欲と時間がある、という場合に選択できるかも。

使った感じでは再生が始まるとネットワーク、ハードが許す限り全力で先読みするって感じ。多分設定で変えられる？映像配信で長い時間使ったことないので親和性がいいかはよくわかりません。

## そのほか(Plyrとかdash.jsとか)

使ったことないのでわかりません。

- Plyr : https://plyr.io/
- dash.js : https://github.com/Dash-Industry-Forum/dash.js

# HLSストリームを作るときのコツ

上でも書いたが作ること自体は ffmpeg があればできる。具体的な作り方はサンプルコードを参照してみてほしい。ただ HLS 用にいい感じに作るためにいくつかノウハウがある。

## キーフレームを細かく作る

これをしないとシークしたときにキーフレームを認識できずに映像が崩れる。だいたい60~90フレームの間で作るといい感じ。

## 複数音声ストリームにするときには音声だけのストリームを複数作ってマニフェストに定義する

[こんな感じにする](https://developer.apple.com/forums/thread/113240)。例えば DVD からリップした映像で複数言語の音声があったりするときは映像に mux せずに音声だけ取り出して複数別々のストリームを作る。m3u8 上で各ストリームの言語を指定すれば shaka のような対応しているプレイヤーではちゃんと見えて、切り替えできるようになる。

日本でよくあるデュアルモノ(右が日本語で左が英語というような音声)の場合、[ブラウザでも頑張ればできなくない](https://developer.mozilla.org/ja/docs/Web/API/StereoPannerNode)がお作法的にはエンコード時に左と右で別ストリームにしたほうが素直なアプローチだとは思う。

## コーデックは実質的に H.264 or VP9+H.265 の二択(2021年1月時点)

いちおう建前上はハード, OS, ブラウザに合わせて動的に使えるコーデックが変わるようだけど、PC/スマホ, Chrome/Firefox/Safari一貫して対応する場合は実質的に H.264 一択。今となっては既に古めのコーデックになってしまったけど互換性を重視するなら H.264 にする。別々のクライアントに向けて最適なコーデックでエンコードする余裕がない場合、例えばライブストリームとかでは H.264 になるんですかね。[サポートされているプロファイル](https://shaka-player-demo.appspot.com/support.html)があるので注意。また HD 映像ではそれなりに帯域を食うのできれいな HD 映像を H.264 でやるのはしんどいと思う。LTE でハイビットレートの配信をするとギガも減るしね・・・。

VOD 的な用途でそれなりにきれいな絵を出したいときにはプラットフォームに合わせて最適化すると帯域の割にいい絵が出て気分がよくなるかもしれない。そのときには1つのコーデックでどこでも出せるわけではない。

PC/Android では VP9 を使うと帯域を節約できてきれいな絵を出すことができる。[最近の(Kaby Lake以降) Intel の CPU では VP9 のエンコードをハードウェア支援してくれる](https://trac.ffmpeg.org/wiki/Hardware/QuickSync)のでそんなに待つ必要はないかもしれない。

Apple 系では VP9 は使えず、代わりに HEVC(H.265) に対応しているので別途エンコードしなければいけない。ただこっちも[最近の NVIDIA であれば nvenc が使える](https://trac.ffmpeg.org/wiki/HWAccelIntro#CUDANVENCNVDEC)ので割と妥当な時間でエンコードできる。ひとつ注意点としてはそれなりにいい GPU じゃないとエンコード速度がそんなに速くならない。個人的には GeForce GTX 1650 程度はあってもいいんじゃないかな、と。

この辺のコーデック系は [Apple(+映像業界) と Apple 以外が CPU ベンダも巻き込んで絶賛競争中](https://bloggeek.me/av1-vs-hevc-webrtc-codec/)なので、1年単位で変化するかもしれない。今後 AV1 とかあるしね。

## 新しいからって MPEG-DASH にしなくてもいいと思う

[まだサポートされていないところもあるらしい](https://www.366service.com/jp/qa/783cd1193dcfd41aac30451853044ea6)し、正直MPEG-DASHじゃないとできないことがない(気がする)ので、今のところはHLS使っておけばいいようにも思います。

どっちを使うにしても手でプレイリスト書くわけではないし、個人的にはHLSの方が可読性がいいのでうまく行かないときは最悪エディタで編集してトライアンドエラーできるのは魅力的。頭に血が登ってるときにXMLなんか読んでられないっつうの。

あとプレイヤーが Video.js の場合、私が試した範囲では MPEG-DASH では再生できませんでした。プラグインとか？でできるのかもしれない。知らんけど。

## Fragmented MP4 コンテナは別に頑張らなくてもいいかもしれない

かつては映像を伝送するときは実質的に MPEG-TS 一択だったが、最近は [Fragmented MP4 (fMP4)](https://developer.apple.com/videos/play/wwdc2016/504/) というコンテナも使えるようになった。なったんだけど、なにがいいのかよくわからない。[一応いろいろ比較している人もいるようだけど](https://blogs.iis.net/samzhang/fragmented-mp4-vs-mpeg2-ts)。

両方 ffmpeg で作れるので好みでもいいのかもしれない。サンプルは TS のものと fMP4 のものと混ぜて作っている。

## 低ビットレートの映像も作っておいたほうがいいと思う

意外と日本にはまだ低ビットレートの地域がある。そのときにも最低限荒い映像が流れていたほうがユーザ体験としてはいい。こんな低い解像度の映像使わないだろと思うような解像度も一応用意しておくといいかもしれない。具体的にはUターンラッシュ中の東名高速の大井松田インター付近の渋滞時に 480p 映像が 5 秒ごとに 2,3 秒止まるのは UX としてはよくない、と個人的には思う。

もう1つ考慮してもいいかもしれないのはスマホのギガが切れて低速モードになったときや、いわゆるお仕置きタイムへの対応。回線キャリアにもよるけど 128Kbps - 1Mbps 程度に制限されることが多いようだ。私の経験だとその帯域めいいっぱい使うことは現実的に難しいと思うので、まあ半分くらいの帯域、つまり 64Kbps - 512Kbps 程度のストリームを作っておくとお仕置きタイム中もいちおう映像は流れますよ、という状況が作れるかもしれない。いい絵を見たかったら Wifi スポットに行けばいいわけだし。

Shaka にはオフラインバッファに映像を溜めておける方法もあるようだ。まだ試してないけど。

# さいごに

特に言うことはないですが、HLS はけっこうハマりポイントがあるので参考になれば幸いです。

一応参考リンクなど。というか調べてる人だと引っかけてると思う。

- ニコラボさんの ffmpeg の記事: https://nico-lab.net/category/ffmpeg/
    - ffmpeg でちょっと凝ったことしようとすると必ずひっかかるページ。お世話になっております。ニコニコ動画3年前くらいに有料会員抜けちゃったでのアレですけど・・・
- Voluntasさんのページ: https://voluntas.medium.com/ とか https://github.com/voluntas とか
    - HLS の人じゃなくて WebRTC の人だけど最近のコーデック系の話を調べてるとひっかかる。業界動向系の話も書いてくれてるから参考になります。