Raspberry Piを使ってストリーミング配信システムを作ってみる
Raspberry PiからNGINXとRTMP/HLSを使って配信サーバを作成し、試したときの作業メモです。
サーバ側の準備
今回はOSとしてubuntu 22.04.3 LTSを用いました。
NGINXのインストール
nginxとrtmpモジュールをインストールします。
sudo apt install -y nginx
sudo apt install -y libnginx-mod-rtmp
インストールが終わったら、配信に使用するためのテンポラリ用のディレクトリを作成します。
sudo chmod -R 755 /tmp/hls
設定ファイルは以下のようにしました。ここではポート番号は8080に設定しています。
rtmpの設定のhls_pathは先ほど作成したディレクトリと合わせてください。
user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
worker_rlimit_nofile 65536;
events {
worker_connections 1024;
# multi_accept on;
}
http {
##
# Basic Settings
##
sendfile on;
tcp_nopush on;
types_hash_max_size 2048;
# server_tokens off;
# server_names_hash_bucket_size 64;
# server_name_in_redirect off;
include /etc/nginx/mime.types;
default_type application/octet-stream;
keepalive_timeout 65;
##
# SSL Settings
##
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE
ssl_prefer_server_ciphers on;
##
# Logging Settings
##
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
##
# Gzip Settings
##
gzip on;
# gzip_vary on;
# gzip_proxied any;
# gzip_comp_level 6;
# gzip_buffers 16 8k;
# gzip_http_version 1.1;
# gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
##
# Virtual Host Configs
##
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
server {
listen 8080;
server_name localhost;
location / {
root html;
index index.html index.htm;
}
location /hls {
types {
application/vnd.apple.mpegurl m3u8;
video/mp2t ts;
}
root /tmp;
add_header Cache-Control no-cache;
}
}
}
rtmp {
server {
listen 1935;
chunk_size 4096;
application live {
live on;
hls on;
hls_path /tmp/hls;
hls_fragment 1s;
hls_playlist_length 3s;
hls_continuous on;
hls_cleanup on;
}
}
}
ファイルディスクリプタの上限を変更
そのままだとファイルディスクリプタの上限に引っ掛かり、正常に動作しなかったので、OSの設定を変更します。
上限を確認
以下のコマンドを実行し、上限値を確認しておきます。
ulimit -n
デフォルトだと1024という数字になっていました。
/etc/security/limits.conf
の編集
/etc/security/limitis.conf
のNGINXユーザのファイルディスクリプタの上限を設定します。通常、NGINXはwww-data
ユーザとして実行されるので、このユーザに対して設定を行います。
具体的には以下の内容を追記します。
* soft nofile 65536
* hard nofile 65536
システム全体の設定を変更する
システム全体のファイルディスクリプタの上限を設定します。/etc/sysctl.conf
に以下内容を追記します。
fs.file-max = 100000
修正後、変更を反映させるために以下のコマンドを実行します。
sudo sysctl -p
PAMモジュールの設定
PAM(Pluggable Authentication Modules)でファイルディスクリプタの上限を有効にするために、/etc/pam.d/common-session
と/etc/pam.d/common-session-noninteractive
に以下の設定を追加します。
session required pam_limits.so
NGINXの設定を変更
NGINXの設定ファイルで、worker_rlimit_nofile
ディレクティブを追加して、ワーカープロセスごとのファイルディスクリプタの数を増やします。
具体的には以下のように修正します。(先ほどの設定ファイルは修正済み)
worker_processes auto;
worker_rlimit_nofile 65536;
events {
worker_connections 1024;
}
NGINXの再起動
設定を変更させるため、NGINXを再起動します。
sudo systemctl restart nginx
以上で配信サーバの設定は完了です。
表示するWebページの準備
今回はRTMPでRaspberry PIから画像データをストリーミングし、サーバでRTMPからHLSに変換してWebページに表示します。その表示側のページを作成します。
以下の内容をindex.htmlという名前で保存し、指定の場所に保存します。今回の環境の場合、デフォルトは/usr/share/nginx/html/
以下になります。var videoSRcで配信サーバのURLを指定するようにしてください。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ライブストリーム</title>
<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
</head>
<body>
<video id="video" controls autoplay></video>
<script>
var video = document.getElementById('video');
var videoSrc = 'http://(配信サーバーのアドレスとポート番号)/hls/stream.m3u8';
if (Hls.isSupported()) {
var hls = new Hls({
liveSyncDurationCount: 3, // バッファリングの量を減らす
liveMaxLatencyDurationCount: 5 // liveSyncDurationCountより大きくする
});
hls.loadSource(videoSrc);
hls.attachMedia(video);
hls.on(Hls.Events.MANIFEST_PARSED, function () {
video.play();
});
}
else if (video.canPlayType('application/vnd.apple.mpegurl')) {
video.src = videoSrc;
video.addEventListener('loadedmetadata', function () {
video.play();
});
}
</script>
</body>
</html>
配信デバイスの準備
今回はRaspberry Pi 5にLogicoolのUSBカメラを用いて行いました。以下の手順で準備を行います。
OSはBullseyeベースです。
必要なパッケージの追加
必要なパッケージを以下のコマンドで追加します。python3-picamera2は公式カメラを使用する際に必要になります。
sudo apt install ffmpeg python3-picamera2
配信プログラムの作成
サーバに配信するプログラムは以下のようになります。サーバのアドレスなどは先ほど設定したサーバのアドレスを適宜設定してください。今回はstream_usb.pyという名前で保存します。
import subprocess
import time
# NGINXサーバのIPアドレスを指定
server_ip = "アドレス"
stream_key = "live/stream"
# ffmpegコマンドを構築
ffmpeg_cmd = [
'ffmpeg',
'-f', 'v4l2', # V4L2のフォーマット指定
'-framerate', '30', # フレームレートを30に設定
'-video_size', '1280x720', # 解像度を1280x720に設定
'-i', '/dev/video0', # USBカメラデバイス
'-c:v', 'libx264', # エンコーダー
'-preset', 'ultrafast', # プリセット
'-tune', 'zerolatency', # 低遅延のチューニング
'-f', 'flv', # フォーマット指定
'-g', '1', # キーフレーム間隔
f'rtmp://{server_ip}/{stream_key}']
# ストリーミングを開始
try:
print("ストリーミングを開始します...")
process = subprocess.Popen(ffmpeg_cmd)
while True:
time.sleep(1)
except KeyboardInterrupt:
print("ストリーミングを終了します...")
finally:
process.terminate()
動作確認
ここまで準備できたら動作を確認してみます。配信を開始していない状況でWebページを見るとこのような画面になります。
次に、Raspberry Piで先ほどのスクリプトを実行します。
python3 stream_usb.py
実行すると以下のようにストリーミングが開始されます。
その状態でブラウザをリロードすると映像がストリーミングで配信されています。
以上でストリーミング配信が行えるようになりました。
Discussion