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