🗂

Raspberry Piを使ってストリーミング配信システムを作ってみる

2024/05/17に公開

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