nginx + nginx-rtmp-module で RTMP(S) to HLS 配信
nginx を使い、カメラの映像を認証つき RTMP(S) で受信して HLS で配信する。

参考
- https://docs.peer5.com/guides/setting-up-hls-live-streaming-server-using-nginx/
- https://qiita.com/khagi/items/b99f5a36846d9ab65daa#nginx-rtmp-module%E6%A6%82%E8%A6%81
確認環境
- Ubuntu 20.04 LTS
- ffmpeg 4.2.4-1ubuntu0.1
- nginx 1.20.0 (Stable)
- nginx-rtmp-module afd350e (v1.2.1 相当)
 2017 年に v1.2.1 リリース後コンパイルエラーの修正が入ったがリリースされていないため、修正版を使用。
インストール
nginx コンパイル用のパッケージをインストールする。
sudo apt-get install -y build-essential libpcre3 libpcre3-dev libssl-dev libz-dev
nginx + nginx-rtmp-module をソースからコンパイルしてインストールする。
curl -LO http://nginx.org/download/nginx-1.20.0.tar.gz
curl -LO https://github.com/arut/nginx-rtmp-module/archive/afd350e0d8b7820d7d2cfc3fa748217153265ce6.tar.gz
tar xvzf nginx-1.20.0.tar.gz
tar xvzf afd350e0d8b7820d7d2cfc3fa748217153265ce6.tar.gz
cd nginx-1.20.0
./configure --with-http_ssl_module --add-module=../nginx-rtmp-module-afd350e0d8b7820d7d2cfc3fa748217153265ce6 --with-debug
make
sudo make install
nginx を起動
sudo /usr/local/nginx/sbin/nginx
設定
設定箇所は大きくふたつ。
- rtmp.server : RTMP サーバーとして外部から映像を受信し、HLS 用のファイルを保存するパート (保存パラメーターやトランスコードの設定)
- http.server : 保存されたファイルを HTTP で配信するパート (Content-Type や CORS の設定)

配信用のディレクトリを作成しておく。
sudo mkdir -p /var/local/www/hls
sudo chmod 777 /var/local/www/hls
/usr/local/nginx/conf/nginx.conf に以下のように設定する。
rtmp {
    server {
        listen 1935;
        chunk_size 4000;
        application app {
            live on;
            record off;
            allow publish all;          # RTMP push 許可
            deny play all;              # RTMP pull 無効
            # HLS 配信用ファイルを保存する
            hls on;
            hls_type event;
            hls_path /var/local/www/hls/;
            hls_fragment 3;
            hls_playlist_length 60;
        }
    }
}
http {
    sendfile off;
    tcp_nopush on;
    #aio on;
    directio 512;
    default_type application/octet-stream;
    server {
        listen 8080;
        location / {
            # Disable cache
            add_header 'Cache-Control' 'no-cache';
            # CORS setup
            add_header 'Access-Control-Allow-Origin' '*' always;
            add_header 'Access-Control-Expose-Headers' 'Content-Length';
            # allow CORS preflight requests
            if ($request_method = 'OPTIONS') {
                add_header 'Access-Control-Allow-Origin' '*';
                add_header 'Access-Control-Max-Age' 1728000;
                add_header 'Content-Type' 'text/plain charset=UTF-8';
                add_header 'Content-Length' 0;
                return 204;
            }
            types {
                application/dash+xml mpd;
                application/vnd.apple.mpegurl m3u8;
                video/mp2t ts;
            }
            root /var/local/www/;
        }
    }
}
配信
ストリーム名(streamName)は任意の文字列で、配信側と再生側で同じものを指定する。
OBS で配信する
ストリーミング先に以下を設定する。
- Server: rtmp://(インスタンスの IP アドレス)/app1
- Stream Key: streamName
ffmpeg で RTSP から中継

RTSP_URL=rtsp://...
RTMP_URL=rtmp://(サーバーのIPアドレス)/app1/streamName
ffmpeg -re -rtsp_transport tcp -i "${RTSP_URL}" -codec:v libx264 -g 3 -f flv "${RTMP_URL}"
nginx 側で変換をおこなわない場合、-g オプションでセグメント間隔ごとにキーフレームを入れる必要がある。
再生
HLS プレイヤー(VLCPlayer など)で以下の URL を指定する。
- http://(サーバーのIPアドレス)/hls/streamName.m3u8
トランスコーディング

nginx でトランスコードする場合、外部から受信した映像に対して FFmpeg を実行して変換後再度 RTMP で出力し、別の application で受信することで実現する。
- 外部からの映像を受信して FFmpeg を実行する application
- 出力先を複数指定することで、複数の品質に対応することができる。
 
- 受信した映像を HLS に保存する application
インストール
ffmpeg をインストールする。
sudo apt-get install -y ffmpeg
設定
H.264 に再エンコードしてキーフレームを 3 秒間隔にする場合。
rtmp {
    ...
    server {
        application app1 {
            live on;
            record off;
            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/app2/${name};
        }
        application app2 {
            live on;
            record off;
            allow publish 127.0.0.1;    # RTMP push 許可 (ffmpeg からのみ)
            deny play all;              # RTMP pull 無効
            # HLS 配信用ファイルを保存する
            hls on;
            hls_path /var/local/www/hls/;
            hls_fragment 3;
            hls_playlist_length 60;
        }
    }
}
この場合、カメラから送信する映像のコーデックは無変換で良い(はず)。エッジの負荷軽減になる。
ffmpeg -re -rtsp_transport tcp -i "${RTSP_URL}" -codec:v copy -f flv "${RTMP_URL}"
RTMPS
nginx-rtmp-module 自体に RTMPS の機能はないため、stunnel で中継する。

インストール
stunnel をインストールする。
sudo apt-get install -y stunnel4
Let's Encrypt で SSL 証明書を発行するため、certbot をインストールする。
sudo apt-get install -y certbot
設定
SSL 証明書を発行する。(DNS サーバーを設定して適当なホスト名で外部からサーバーにアクセスできるようにしておく。)
$ sudo certbot certonly --webroot -d (ホスト名)
Input the webroot for ...  /usr/local/nginx/html
stunnel から nginx への中継をおこなう設定をおこなう。 (/etc/stunnel/stunnel.conf)
[rtmps]
accept = 11935
connect = 127.0.0.1:1935
cert=/etc/letsencrypt/live/(ホスト名)/fullchain.pem
key=/etc/letsencrypt/live/(ホスト名)/privkey.pem
stunnel を起動する。
sudo systemctl restart stunnel4
nginx 側は外部から直接接続できないようにしておく。
rtmp {
    server {
        application app1 {
            allow publish 127.0.0.1;    # RTMP push 許可 (stunnel からのみ)
            ...
配信
映像の送信先は以下のようにする。
- rtmps://(サーバーのIPアドレス):11935/app1/streamName
認証

URL に特定の認証キーを付与することで認証をおこなう単純な方法。 ※OBS には別途認証の設定があるが、この認証を使う方法は見当たらなかった。
参考)
nginx-rtmp-module に対して映像配信時に内部で特定の WebAPI を叩き成功(200応答)の場合のみ配信を許可する仕組みがあるので、認証用に Python でローカルに Web サーバーを立てて認証キーのチェックをおこなう。
インストール
Flask をインストールする。
sudo apt-get install -y python3-pip
sudo python3 -m pip install flask
設定
以下のような認証サーバーを記述する。(例: /usr/local/nginx/rtmp-auth/auth.py)
from flask import Flask, request
import config
app = Flask(__name__)
@app.route("/auth", methods=["GET", "POST"])
def auth():
    if request.values.get("name") is None or request.values.get("tcurl") is None:
        return "", 400
    stream_name = request.values.get("name")
    auth_key = request.values.get("tcurl").split("?")[-1]
    if auth_key == config.auth_key:
        return "", 200
    else:
        return "", 403
if __name__== "__main__":
    app.run()
認証キーを設定する。(例: /usr/local/nginx/rtmp-auth/config.py)
auth_key = "AUTHKEY"
認証サーバーを起動する。
cd /usr/local/nginx/rtmp-auth
python3 auth.py
nginx-rmtp-module に対して映像配信時に認証エンドポイントを叩く設定をおこなう。
rtmp {
    server {
        application app1 {
            on_publish http://localhost:5000/auth;
            ...
配信
配信先 URL に「?(認証キー)」を付与する。
- rtmps://(サーバーのIPアドレス)/app1?AUTHKEY/streamName