コンテンツにスキップ

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 の設定)

nginx

配信用のディレクトリを作成しておく。

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 から中継

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 で受信することで実現する。

  1. 外部からの映像を受信して FFmpeg を実行する application - 出力先を複数指定することで、複数の品質に対応することができる。
  2. 受信した映像を 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

インストール

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 には別途認証の設定があるが、この認証を使う方法は見当たらなかった。

参考)

  • https://github.com/Nesseref/nginx-rtmp-auth

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