RTMP to MPEG-DASH でライブストリーミングを行う方法

# はじめに

定番の nginx-rtmp-module で RTMP to HLS 配信を行っていたのですが、
RTMP to MPEG-DASH にしてくれと言われてしまったのでその対応を行いました。

RTMP to HLS で配信する方法は沢山ウェブに溢れていたのですが、
中々 RTMP to MPEG-DASH で配信する方法については見つけられなかったため、
記事にして残しておこうと考え、やり方についてまとめました。

# nginx-rtmp-module で MPEG-DASH 配信を行うためのセットアップ (Docker)

RTMP to MPEG-DASH を行うための Dockerfile は ↓ になります。

```docker:Dockerfile
FROM tiangolo/nginx-rtmp

# Debian OS (64bit) に対応した static build をダウンロード & 解凍して、/usr/local/bin 以下に移動する
WORKDIR /tmp
RUN mkdir ffmpeg
RUN curl -L -O https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz
RUN tar -Jxvf ffmpeg-release-amd64-static.tar.xz -C ffmpeg --strip-components 1
RUN mv ./ffmpeg/ffmpeg /usr/local/bin/ffmpeg

# MPEG-DASH の配信ファイルを配置するためのフォルダ (.mpd, .m4s)
RUN mkdir -p /usr/local/nginx/dash
WORKDIR /usr/local/nginx/dash

# MPEG-DASH 配信のための設定を記載した nginx.conf で
# Docker イメージ内の nginx.conf を上書きする (nginx.conf の詳細は後述)
COPY nginx.conf /etc/nginx/nginx.conf

CMD nginx -g "daemon off;"
```

今回は [こちらの Docker イメージ](https://hub.docker.com/r/tiangolo/nginx-rtmp/) を使用しました。

MPEG-DASH 配信を行うため ffmpeg のバージョンは 4 以降を使用します。(デフォの `apt-get` でインストール出来る ffmpeg のバージョン(3.2) では MPEG-DASH をライブストリーミング配信するのに [必要な streaming オプション](https://nico-lab.net/dash_muxer_with_ffmpeg/) が使用出来ないため)

今回使用した nginx-rtmp-module の Docker イメージの OS には Debian OS (64bit) が使用されています。

ffmpeg をビルドするのは手間も時間もかかるため、[ffmpeg の公式サイト](https://johnvansickle.com/ffmpeg/)から Debian OS (64bit) で使用可能な static build をダウンロードしてきて使用しています。(ffmpeg-release-amd64-static.tar.xz)

# nginx.conf の内容

nginx.conf の中身は ↓ のようになりました。
重要なのは `application live` のスコープ内の `exec` 部分になります。

```nginx:nginx.conf
user root;
worker_processes auto;
rtmp_auto_push on;
events {}

http {
    include       mime.types;
    default_type  application/octet-stream;
    server_tokens off;

    sendfile        on;
    keepalive_timeout  65;

    server {
        listen       80;
        server_name  localhost;

        location /index.html {
            root   /usr/local/nginx/html;
        }
        location / {
            root   /usr/local/nginx/dash;

            # dash.js で MPEG-DASH の動作確認を行うための CORS を設定 (詳細は後述)
            location ~* \.(mpd)$ {
                add_header Access-Control-Allow-Origin *;
                add_header Access-Control-Allow-Methods "GET, OPTIONS";
                add_header Access-Control-Allow-Headers "Origin, Authorization, Accept";
                add_header Access-Control-Allow-Credentials true;

                if ($request_method = 'OPTIONS') {
                    return 204;
                }
            }

            # dash.js で MPEG-DASH の動作確認を行うための CORS を設定 (詳細は後述)
            location ~* \.(m4s)$ {
                add_header Access-Control-Allow-Origin *;
                add_header Access-Control-Allow-Methods "GET, OPTIONS";
                add_header Access-Control-Allow-Headers "Origin, Authorization, Accept";

                add_header Access-Control-Allow-Credentials true;
                if ($request_method = 'OPTIONS') {
                    return 204;
                }
            }
        }
    }
}

rtmp {
    server {
        listen 1935;
        listen [::]:1935 ipv6only=on;

        application live {
            live on;
            record off;

            wait_key on;
            wait_video on;

            # ffmpeg を使用して RTMP のストリームを MPEG-DASH に変換する
            # 変換した MPEG-DASH 関連のファイルは /usr/local/nginx/dash フォルダに生成される
            exec /usr/local/bin/ffmpeg -i rtmp://localhost:1935/$app/$name -c copy -r 30 -sc_threshold 0 -b_strategy 0 -streaming 1
                 -use_timeline 1 -use_template 1 -seg_duration 4 -window_size 5 -adaptation_sets "id=0,streams=v id=1,streams=a"
                 -init_seg_name $name\$RepresentationID\$.m4s -media_seg_name $name\$RepresentationID\$-\$Number%05d\$.m4s
                 -f dash /usr/local/nginx/dash/$name.mpd;
        }
    }
}
```

ffmpeg のオプションで `init_seg_name` と `media_seg_name` を明示的に指定しているのは、
複数の RTMP を受けたときに、生成される MPEG-DASH 関連のファイル名が重複しないようにするためです。

# 実際に動かしてみる

フォルダ構成は `Dockerfile` と同じ階層に `nginx.conf` を配置するだけです。

```shell
tree -L 2 nginx-rtmp-to-dash/ 
nginx-rtmp-to-dash/
├── Dockerfile
└── nginx.conf

0 directories, 2 files
```

ターミナルから `nginx-rtmp-to-dash` フォルダに入った後、
↓ のコマンドを実行して実際に動かしてみます。
↓ のような出力が確認できればビルドと実行が成功した状態です。

```shell
$ docker build -t nginx-rtmp-to-dash .
Sending build context to Docker daemon   5.12kB
Step 1/10 : FROM tiangolo/nginx-rtmp
 ---> ab0f3f3dcb6f
Step 2/10 : WORKDIR /tmp
 ---> Using cache
 ---> 46e748fb4d69
Step 3/10 : RUN mkdir ffmpeg
 ---> Using cache
 ---> 945870247325
Step 4/10 : RUN curl -L -O https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz
 ---> Using cache
 ---> f2ee9c7e8e70
Step 5/10 : RUN tar -Jxvf ffmpeg-release-amd64-static.tar.xz -C ffmpeg --strip-components 1
 ---> Using cache
 ---> fcf0ddca90f6
Step 6/10 : RUN mv ./ffmpeg/ffmpeg /usr/local/bin/ffmpeg
 ---> Using cache
 ---> 79a38f12d835
Step 7/10 : RUN mkdir -p /usr/local/nginx/dash
 ---> Using cache
 ---> 2d6aaeaa3bcb
Step 8/10 : WORKDIR /usr/local/nginx/dash
 ---> Using cache
 ---> 9968125c8fac
Step 9/10 : COPY nginx.conf /etc/nginx/nginx.conf
 ---> Using cache
 ---> b83b59608d28
Step 10/10 : CMD nginx -g "daemon off;"
 ---> Using cache
 ---> bb90e10e5c8e
Successfully built bb90e10e5c8e
Successfully tagged nginx-rtmp-to-dash:latest

$ docker run --name nginx-rtmp-to-dash -p 80:80 -p 1935:1935 nginx-rtmp-to-dash
# エラーが発生する等で処理が中断されなければ成功
```

[OBS](https://obsproject.com/ja) を使用して実際に `rtmp://localhost:1935/live/test` に RTMP 配信している状態で、
ブラウザから http://localhost/test.mpd にアクセスすると、
生成された MPEG-DASH ファイルにアクセスできることが確認できます。

しかし ↑ でファイルの生成は確認できると思うのですが、
実際に動画も再生してみて正常に見えてるか確認出来ないと不安なので、
今回は [dash.js](https://github.com/Dash-Industry-Forum/dash.js) を使用して実際に配信されている映像を見てみたいと思います。

html ファイルを用意して Chrome で開いた後、画面中央に出てくるローディングが終わった段階で再生ボタンをクリックすれば、MPEG-DASH の再生を確認出来ます。

動画再生の検証に使用した html ファイルは ↓ になります。

```html:index.html
<head>
  <title>RTMP to MPEG-DASH</title>
  <meta charset="UTF-8">
  <script src="https://cdn.dashjs.org/latest/dash.all.min.js"></script>
  <style>
    video {
      width: 640px;
      height: 360px;
    }
  </style>
</head>

<body>
  <div>
    <!-- rtmp://localhost:1935/live/test に配信した際に生成される MPEG-DASH の URLを指定 -->
    <video data-dashjs-player autoplay src="http://localhost/test.mpd" controls></video>
  </div>
</body>
```

[OBS](https://obsproject.com/ja) で `rtmp://localhost:1935/live/test` に RTMP 送信した際、Chrome で ↑ の HTML を開いたときの様子 ↓

<img width="1305" alt="スクリーンショット 2019-06-21 0.47.16.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86070/f98ad3a7-e2d7-1bce-5ff3-b7110a2e0fd4.png">

# おわりに

RTMP から MPEG-DASH に変換するための ffmpeg のコンフィグをいじって、
正常にコンバートから再生可能になるまでのオプション設定を見つけるまで
色々ハマってしまい、これだけでかなり時間を食ってしまいました。。

# 参考リンク

https://nico-lab.net/dash_muxer_with_ffmpeg/
https://github.com/Dash-Industry-Forum/dash.js?
https://qiita.com/mehdi/items/d5c3bb173ebbac5b0b53