HLSライブストリーミングサーバーの構築 (ABR対応)

こんにちは、動画チームのhagi＠streampackです。

#はじめに

HLSのストリーミングサーバーをAWS上で構築する手順を記載します。
最近では無料や低額で利用できるストリーミングサービスもありますが、ここはあえて自分で管理したいなー、広告入れたくない、完全公開はいやだなーといった人向けです。
またサーバー側で録画すれば撮影側で録画せずともハードコピーを残すことも可能です。
今回はAWSのEC2上に構築しABR（Adaptive Bitrate）の手順も記述します。

## ABR（Adaptive Bitrate）とは

日本デジタル・プロセシング・システムズ株式会社の[サイト](https://www.dpsj.co.jp/tech-articles/abr)より

>ABR（アダプティブ・ビットレート）は、動画コンテンツを視聴環境（デバイスや回線速度など）に合わせて、可能な限り品質高くスムースに再生できるようにするための動画配信技術です。具体的には視聴端末の処理能力や解像度、回線速度などに応じて届ける動画の品質を自動的に上げたり下げたりすることで、視聴者に可能な限り快適な視聴体験を提供します。

サーバー側の処理としては複数の異なるビットレートのストリームを一斉に提供しクライアント（プレイヤー）が視聴環境に合わせて選択できるようになる。

#環境

今回はAWSのEC2で作成します。

##EC2

インスタンスタイプ：t2.small 
OS:Amazon Linux AMI 2017.09

## 必要パッケージ

gcc pcre-devel openssl-devel

```
yum install gcc pcre-devel openssl-devel
```

*上記をインストールすれば依存関係でその他の必要パッケージもインストールされます。

##nginx

nginx-1.13.8 (1.16.2017時点 最新)

##nginx rtmp モジュール

nginxのrtmpモジュールは下記を利用。

[nginx-rtmp-moduleのgitページ](https://github.com/arut/nginx-rtmp-module) 

### nginx-rtmp-module概要

nginx-rtmp-moduleは1935ポートでrtmpストリームを受けてHLSやmpeg−dashに出力や録画することができます。

##ffmpeg

ffmpegはrtmpを加工して再配信するのに利用します。
今回の例だと入力ストリームを異なるビットレートのストリームを生成。

ffmpeg version: N-89773-g7fcbebbeaf-static (1.16.2017時点 最新)

#インストール

## nginx

### nginxソースダウンロード
```
# cd /usr/local/share
# wget http://nginx.org/download/nginx-1.13.8.tar.gz
# tar zxf nginx-1.13.8.tar.gz
```
### nginx rtmp module ダウンロード
```
# cd /usr/local/share/nginx-1.13.8
# wget https://github.com/arut/nginx-rtmp-module/archive/master.zip
# unzip -q master.zip
```
### 確認
```
# /usr/local/nginx/sbin/nginx -V
nginx version: nginx/1.13.8
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-11) (GCC)
built with OpenSSL 1.0.2k-fips  26 Jan 2017
TLS SNI support enabled
configure arguments: --with-http_ssl_module --with-http_stub_status_module --add-module=nginx-rtmp-module-master
```

##ffmpeg

ffmpegは常に更新されているためソースからコンパイルが推奨されていますが、ここでは比較的新しいコンパイル済み版(static)を利用します。

### static バージョンダウンロード

[FFmpeg Static Builds](https://www.johnvansickle.com/ffmpeg/)

```
# cd /usr/local/share/
# wget https://johnvansickle.com/ffmpeg/builds/ffmpeg-git-64bit-static.tar.xz
# tar Jxf ffmpeg-git-64bit-static.tar.xz
```

###インストール
cpやmvでも良さそうだが入れ替えることを考えシンボリックリンクにします。

```
# ln -s /usr/local/share/ffmpeg-git-20180111-64bit-static/ffmpeg /usr/bin/ffmpeg
```
###確認

```
# ffmpeg -version
ffmpeg version N-89773-g7fcbebbeaf-static https://johnvansickle.com/ffmpeg/  Copyright (c) 2000-2018 the FFmpeg developers
built with gcc 6.4.0 (Debian 6.4.0-11) 20171206
configuration: --enable-gpl --enable-version3 --enable-static --disable-debug --disable-ffplay --disable-indev=sndio --disable-outdev=sndio --cc=gcc-6 --enable-fontconfig --enable-frei0r --enable-gnutls --enable-gray --enable-libfribidi --enable-libass --enable-libvmaf --enable-libfreetype --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-librubberband --enable-librtmp --enable-libsoxr --enable-libspeex --enable-libvorbis --enable-libopus --enable-libtheora --enable-libvidstab --enable-libvo-amrwbenc --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxvid --enable-libzimg
libavutil      56.  7.100 / 56.  7.100
libavcodec     58.  9.100 / 58.  9.100
libavformat    58.  3.100 / 58.  3.100
libavdevice    58.  0.100 / 58.  0.100
libavfilter     7. 11.101 /  7. 11.101
libswscale      5.  0.101 /  5.  0.101
libswresample   3.  0.101 /  3.  0.101
libpostproc    55.  0.100 / 55.  0.100
```

#設定

上記手順でインストールした場合、設定ファイルの場所は`/usr/local/nginx/conf/nginx.conf`です。
rtmpモジュールに該当する部分は`rtmp{}`の部分です。

各ディレクティブについては[こちら](https://github.com/arut/nginx-rtmp-module/wiki/Directives)から確認

## 単ストリーム出力の場合

複数ストリームはインスタンスにも負荷がかかるし大体の視聴者にはこの設定で見てもらいたいときは下記設定を放り込んで終わり。

`/usr/local/nginx/conf/nginx.conf`

```
worker_processes 1;
error_log  logs/error.log error;

events {
        worker_connections  1024;
}

rtmp {
        server {
                listen 1935;
                allow play all;
                application live {
                        live on;
                        hls on;
                        hls_path /var/www/vhosts/live/;
                }
        }
}

http {

    server {
        listen 80;
        include       mime.types;
        default_type  application/octet-stream;
        server_name localhost;
        add_header Access-Control-Allow-Origin *;

        location /live {
            types {
                 application/vnd.apple.mpegurl m3u8;
            }
            root /var/www/vhosts/;
        }
    }
}
```

###rtmpディレクティブ

**live on｜off**: rtmp配信を受け取るかどうかの設定。未設定の場合port１９３５が空いていても処理が始まらない。デフォルトはoff
**hls on｜off**： hlsを有効にするかどうか。
**hls_path [directory]**： hlsのファイルをどこに格納するかどうかの設定hls onであれば必須。またディレクトリーが存在しない場合はエラー


## ABR設定 

rtmpモジュールではrtmpストリームを受け取り,ffmpegで複数のrtmpストリームを作成してからHLSファイルを作成しています。
ここで注意が必要なのはffmpegのフィルターやオプションです。配信するターゲットや内容を加味しながら設定する必要があります。
場合によってはフィルターやオプションが足りなかったりffmpegに負荷が必要以上に高くなる可能性があります。

```
worker_processes 1;
error_log  logs/error.log error;

events {
        worker_connections  1024;
}

rtmp {
        server {
                listen 1935;
                allow play all;
                application live {
                        live on;
                        exec ffmpeg -i rtmp://localhost/live/$name -async 1 -vsync cfr
                                -acodec copy -c:v libx264 -b:v 128K -f flv rtmp://localhost/live2/$name_low
                                -acodec copy -c:v libx264 -b:v 512k -f flv rtmp://localhost/live2/$name_mid
                                -acodec copy -c:v libx264 -b:v 1024k -f flv rtmp://localhost/live2/$name_high;
                }

                application live2 {
                        live on;
                        hls on;
                        hls_path /var/www/vhosts/live_stream;
                        hls_variant _low  BANDWIDTH=300000;
                        hls_variant _mid BANDWIDTH=700000;
                        hls_variant _high BANDWIDTH=1200000;
                }

        }
}


http {

    server {
        listen 80;
        include       mime.types;
        default_type  application/octet-stream;
        server_name localhost;
        add_header Access-Control-Allow-Origin *;

        location /live_stream {
            types {
                 application/vnd.apple.mpegurl m3u8;
            }
            root /var/www/vhosts/;
        }
    }
}
```

###rtmpディレクティブ

**live on｜off**: rtmp配信を受け取るかどうかの設定。未設定の場合port１９３５が空いていても処理が始まらない。デフォルトはoff
**exec コマンド**： ストリームに実行するコマンド。ここではrtmpストリームから３つのrtmpストリームを作成
**hls on｜off**： hlsを有効にするかどうか。
**hls_path [directory]**： hlsのファイルをどこに格納するかどうかの設定hls onであれば必須。またディレクトリーが存在しない場合はエラー
**hls_variant [接尾辞 EXT-X-STREAM-INF]**：tsやプレイリストの識別用接尾辞とEXT-X-STREAM-INFに記述する内容を記。

# 生成されるファイル

上記ABR設定だとhls_pathで指定したディレクトリーに下記のようなファイルが作成されます。

```
# ls -la /var/www/vhosts/live_stream/
total 11900
drwxr-xr-x 2 nobody root     12288 Jan 19 03:26 .
drwxr-xr-x 4 root   root      4096 Jan 17 08:07 ..
-rw-r--r-- 1 nobody nobody 1648760 Jan 19 03:25 obs_test_high-0.ts
-rw-r--r-- 1 nobody nobody 1674516 Jan 19 03:25 obs_test_high-1.ts
-rw-r--r-- 1 nobody nobody 1390448 Jan 19 03:25 obs_test_high-2.ts
-rw-r--r-- 1 nobody nobody 1713996 Jan 19 03:26 obs_test_high-3.ts
-rw-r--r-- 1 nobody nobody  364532 Jan 19 03:26 obs_test_high-4.ts
-rw-r--r-- 1 nobody nobody     179 Jan 19 03:26 obs_test_high.m3u8
-rw-r--r-- 1 nobody nobody  351372 Jan 19 03:25 obs_test_low-0.ts
-rw-r--r-- 1 nobody nobody  342536 Jan 19 03:25 obs_test_low-1.ts
-rw-r--r-- 1 nobody nobody  323548 Jan 19 03:25 obs_test_low-2.ts
-rw-r--r-- 1 nobody nobody  344980 Jan 19 03:26 obs_test_low-3.ts
-rw-r--r-- 1 nobody nobody  106032 Jan 19 03:26 obs_test_low-4.ts
-rw-r--r-- 1 nobody nobody     176 Jan 19 03:26 obs_test_low.m3u8
-rw-r--r-- 1 nobody nobody     225 Jan 19 03:26 obs_test.m3u8
-rw-r--r-- 1 nobody nobody  935112 Jan 19 03:25 obs_test_mid-0.ts
-rw-r--r-- 1 nobody nobody  911236 Jan 19 03:25 obs_test_mid-1.ts
-rw-r--r-- 1 nobody nobody  777380 Jan 19 03:25 obs_test_mid-2.ts
-rw-r--r-- 1 nobody nobody  964252 Jan 19 03:26 obs_test_mid-3.ts
-rw-r--r-- 1 nobody nobody  282376 Jan 19 03:26 obs_test_mid-4.ts
-rw-r--r-- 1 nobody nobody     176 Jan 19 03:26 obs_test_mid.m3u8
```

## マニフェスト

ストリーム名にｍ３ｕ８がついたファイルはマニフェスト(manifest)となります。
HLS動画(ABR)を再生する際プレイヤーが最初にみるファイルです。
この中に各プレイリスト情報が含まれます。

obs_test.m3u8

```
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=400000
obs_test_low.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=700000
obs_test_mid.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1200000
obs_test_high.m3u8
```

## プレイリスト

プレイリストには個々のストリーム再生に必要なデータファイル情報が記述されます。
プレーヤーは順次にこちらのデータファイル(ts)をダウンロードし再生します。
またマニフェストは更新されませんがフレイリストは新しいデータファイルが作成される都度更新されます。

単ストリームの場合はプレイリストのみとなります。
またプレイリストのみをプレイヤーで指定した場合再生は可能です。

obs_test_low.m3u8

```
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-TARGETDURATION:10
#EXTINF:10.000,
obs_test_low-0.ts
#EXTINF:10.000,
obs_test_low-1.ts
#EXTINF:10.000,
obs_test_low-2.ts
```

## データファイル

tsファイル：mpeg-2 tsファイル

# でどうやってrtmpを投げるの？

## エンコーダー

有料ではPCソフトウエアの[Wirecast](https://www.telestream.net/wirecast/overview.htm)やデバイスでは[liveshell](https://liveshell.cerevo.com/ja/)スマホアプリだと[BroadcastMe](https://play.google.com/store/apps/details?id=com.streamaxia.broadcastme&hl=en)などが使いやすくハードルが低いです。
無料の[OBS](https://obsproject.com)もありインターネット上に多くの利用方法が投稿されております。軽くですがOBSの利用方法を記します。使い方の詳細が気になる方は[obsproject](https://obsproject.com)をご参考ください。

## 配信の設定

配信を行う際、rtmpで投げるのですがOBSの場合は配信種別はカスタムストリーミングサーバーをご指定ください。Wirecastなど多くはrtmpサーバーとなっています。
今回の例では約1Mbps相当の配信を想定しています。

### エンコーダーから投げる先(URL)

エンコーダーの投げ先はこのようになります。必ずffmpegが実行(ABRの場合)されるapplicationをご指定ください。上記単ストリーム、ABRの場合applicationは"live"となります。

```
URL rtmp://[nginxのグローバルIP]:1935/live/
ストリーム名 [任意ストリーム名]
```

### 例

```
URL  rtmp://XXX.XXX.XXX.XXX:1935/live/
ストリーム名 　obs_test
```

# 視聴は？

safariやedgeの場合マニフェストやプレイリストを直接入力することで再生することが可能ですが他のブラウザーなどは場合によっては視聴に[videojs](https://videojs.com)などのプレイヤーを利用することが必要です。

### 単ストリーム
`http://XXX.XXX.XXX.XXX/live/obs_test.m3u8`

### ABR
`http://XXX.XXX.XXX.XXX/live_stream/obs_test.m3u8`