Skip to main content

nginx + nginx-rtmp-module で RTMP(S) to HLS 配信

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

構成

参考

確認環境

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

参考)

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