Docker + nginxでマルチ配信を構築

仕事でマルチ配信の話が出たため、Dockerを用いて一つのライブ出力から複数のRTMPサーバにデータを転送する中継サーバを構築してみました。その際に色々と調べたため備忘録として本記事にまとめます。

## 背景

そもそものきっかけは

1. アプリで配信したライブを自社製のライブPlayer以外にYoutubeやFacebookに対して同時配信したい
2. 複数配信先はCMSなどで自由に指定できるようにしたい
3. RTMPの出力はアプリからで一つに制限されているため、RTMPを複数の配信先に中継するサーバが必要
4. そもそもそれは可能なの??

から始まり、勉強がてらdockerを用いてローカルに中継サーバを構築してみることにしました。

## 成果物・構成

成果物は以下の通りです。

◆ソースコード

https://github.com/y-kakinuma-paronym/multi-rtmp

◆構成

<img src="https://user-images.githubusercontent.com/70195754/153760143-4a0d3230-dda1-444f-9eae-9d94bd1ef7f5.png">

`stream server` にRTMPを渡すために `ffmpeg` を使用する構成になっています。

## Dockerfile

```docker
FROM ubuntu:20.04

RUN apt update
RUN apt upgrade -y
RUN apt auto-remove -y

RUN apt install -y language-pack-ja
RUN update-locale LANG=ja_JP.UTF8

RUN apt install -y build-essential libpcre3 libpcre3-dev libssl-dev libz-dev zlib1g-dev unzip git curl wget vim
WORKDIR /usr/local/src/
RUN curl -LO http://nginx.org/download/nginx-1.20.0.tar.gz
RUN curl -LO https://github.com/arut/nginx-rtmp-module/archive/afd350e0d8b7820d7d2cfc3fa748217153265ce6.tar.gz
RUN tar xvzf nginx-1.20.0.tar.gz
RUN tar xvzf afd350e0d8b7820d7d2cfc3fa748217153265ce6.tar.gz
WORKDIR /usr/local/src/nginx-1.20.0
RUN ./configure --with-http_ssl_module --add-module=../nginx-rtmp-module-afd350e0d8b7820d7d2cfc3fa748217153265ce6 --with-debug
RUN make
RUN make install
COPY conf/nginx.conf /usr/local/nginx/conf/nginx.conf
COPY conf/nginx_single.conf /usr/local/nginx/conf/nginx_single.conf
WORKDIR /var/local/www/hls
RUN chmod 777 /var/local/www/hls

# ffmpeg install
WORKDIR /usr/local/src/
RUN wget https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz \
      && tar Jxvf ffmpeg-release-amd64-static.tar.xz \
      && cp ffmpeg-*-amd64-static/ffmpeg /usr/bin/

WORKDIR /var/local/www/hls
```

## マルチ配信の仕組み

RTMPの通信はnginxの [nginx-rtmp-module](https://github.com/arut/nginx-rtmp-module) を用いています。`Dockerfile` のnginxとnginx-rtmp-moduleのインストールしてる箇所は以下の通りです。

```docker
RUN apt install -y build-essential libpcre3 libpcre3-dev libssl-dev libz-dev zlib1g-dev unzip git curl wget vim
WORKDIR /usr/local/src/
RUN curl -LO http://nginx.org/download/nginx-1.20.0.tar.gz
RUN curl -LO https://github.com/arut/nginx-rtmp-module/archive/afd350e0d8b7820d7d2cfc3fa748217153265ce6.tar.gz
RUN tar xvzf nginx-1.20.0.tar.gz
RUN tar xvzf afd350e0d8b7820d7d2cfc3fa748217153265ce6.tar.gz
WORKDIR /usr/local/src/nginx-1.20.0
RUN ./configure --with-http_ssl_module --add-module=../nginx-rtmp-module-afd350e0d8b7820d7d2cfc3fa748217153265ce6 --with-debug
RUN make
RUN make install
```

RTMPを受信し、複数のRTMPサーバへ転送するための設定ファイルは以下の通りです。

```nginx
# ~~~省略~~~~

rtmp {
    server {
        # ~~~省略~~~~

        application live {
            # ~~~省略~~~~

            allow publish all;          # RTMP push 許可
            allow play 127.0.0.1;       # RTMP pull 有効(ffmpegに対して)

            # 変換コマンド
            exec ffmpeg -re -i rtmp://localhost/${app}/${name} -codec:v libx264 -g 3 -f flv rtmp://localhost/stream1/${name};
            exec ffmpeg -re -i rtmp://localhost/${app}/${name} -codec:v libx264 -g 3 -f flv rtmp://localhost/stream2/${name};
        }

        # ~~~省略~~~~
    }
}

http {
    # ~~~省略~~~
}
```

処理の順番は以下の通りです。

- `allow play 127.0.0.1;` で受信したRTMPを再度localhostに転送
- localhostに転送したものを2プロセスのffmpegを用いてそれぞれのストリームサーバ `rtmp://localhost/streamN/${name};` に転送
  - `exec ffmpeg -re -i rtmp://localhost/${app}/${name} -codec:v libx264 -g 3 -f flv rtmp://localhost/stream1/${name};`
  - `exec ffmpeg -re -i rtmp://localhost/${app}/${name} -codec:v libx264 -g 3 -f flv rtmp://localhost/stream2/${name};`

## 感想

勉強目的で作ってみましたが、大部分は `nginx` と `ffmpeg` の機能を用いて実現しているためコーディングはせずに構築することができました。
今回の成果物では配信先は同じコンテナ内のRTMPサーバに対してですが、`ffmpeg` コマンドを書き換えることでYoutubeやFacebookに対して配信することは可能です。

勉強後に後から知ったのですが[AWS Elemental MediaLiveでRTMP出力が可能](https://dev.classmethod.jp/articles/aws-elemental-medialive-adds-support-for-rtmp-and-rtmps-outputs/)であることが判明しました。実サービスでマルチ配信を実現する際は負荷などを考慮すると素直にAWSに頼るのが良さそうです。

## 参考サイト

https://notes.yh1224.com/tech/nginx-rtmp-hls/