ラズパイカメラ + nginx で hls(RTMP)サーバ

ここのところカメラでストリーミングの実験をいろいろしているのですが、今回は hls サーバの実験です。RTMPサーバと呼ぶべきなのかもしれませんが何となくあやふやです。

内容的には IoT PLUS さんの内容のほぼまんまですが、ffmpegのパラメータとsystemd での起動に変更したのでさらします。
まあ、誰かの役に立つかもしれないがわからない。

それにしても ffmpeg のコマンドラインは私にとって鬼門です。ドキュメント読む気にイマイチならないのよ。誰か分かりやすい解説ページつくってくれないでしょうか ?

手順

必要な物は、ffmpeg と nginx です。apache は追ってないですが nginx に rtmp モジュールがあってお手軽なのでその例があるのだと思います。

自分の例では http basic auth も設定していないので apache2-utils は不要です。php-fpm も入れません。

インストール

$ sudo apt update
$ sudo apt install ffmpeg nginx libnginx-mod-rtmp

出力フォルダの準備

こちらも丸パクリですが、/dev/shm のリンクを作成します。

$ sudo mkdir -p /var/www/html/live
$ cd /var/www/html/live
$ sudo ln -s /dev/shm hls

index.html の準備

/var/www/html/index.html を作成します。中身はとりあえずパクリです。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8"/>
  <script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
</head>

<body>
  <video id="video" controls width="100%"></video>
  <script>
    if(Hls.isSupported()) {
      var video = document.getElementById('video');
      var hls = new Hls();
      hls.loadSource('live/hls/stream.m3u8');
      hls.attachMedia(video);
      hls.on(Hls.Events.MANIFEST_PARSED,function() {
      video.play();
    });
   }
  </script>
<p>
  iPhoneなどで再生されない場合は、<a href="live/hls/stream.m3u8">こちら</a>をクリック。
</p>
</body>
</html>

nginx の設定

/etc/nginx/rtmp.conf ファイルを作成し、そいつを /etc/nginx/nginx.conf でインクルードしてやります。

もともと nginx.conf のなかで. conf.d の中にある *.conf ファイルは読み込まれるのですが、設定ファイルの位置的にまずいらしくエラーになるのでこの構成にしてます。

更に、/var/www/html のロケーションのハンドラを /etc/nginx/conf.d/default.conf に書きます。

rtmp {
    server {
        listen 1935;
        chunk_size 4096;
        allow play all;
        access_log /var/log/nginx/rtmp_access.log;

        application live {
            live on;
            hls on;
            record off;
            hls_path /var/www/html/live/hls;
            hls_fragment 1s;
            hls_type live;
        }
    }
}

これを下の nginx.conf でインクルードしてやります。

events {
----
}
http {
----
}

# http ブロックの外になるように以下の行を追加
include /etc/nginx/rtmp.conf;

/etc/nginx/conf.d/default.conf はこうなります。リッスンポートは 8080 に変更してます。

server {
    listen 8080;
    proxy_set_header   X-Forwarded-For     $proxy_add_x_forwarded_for;
    access_log /var/log/nginx/access.log combined;
    error_log /var/log/nginx/error.log warn;

    location = /favicon.ico {
        access_log off;
        empty_gif;
        expires 30d;
    }

    location / {
        #auth_basic "Web Cam Streaming";
        #auth_basic_user_file /var/www/.htpasswd;

        root /var/www/html;
        index index.html;
        set_real_ip_from    127.0.0.1;
        real_ip_header      X-Forwarded-For;
    }
}

nginx を起動しておきます

$ sudo systemctl daemon-reload
$ sudo systemctl restart nginx

ffmpeg の起動テスト

一旦、ffmpeg で動画を見れるかテストします。今回の環境ではマイクがないので、音声関連のパラメータはざっくり削除しています。

$ ffmpeg -f v4l2 -thread_queue_size 8192 -s 720x480 -i /dev/video0 \
    -r 15 -c:v h264_omx -b:v 512k \
    -vf "rotate=180*PI/180" \
    -f flv rtmp://localhost/live/stream

パラメータの説明

-s 720×480: 解像度
-r 15: フレームレート(15 フレーム / sec)
-vf “rotate=180*PI/180”: 画像を 180度 ローテーションする。(ラジアンに変換)

すべてがうまくいっていれば、こんなふうにブラウザに動画が表示されます。

systemd の設定

参考にしたページでは起動スクリプトで終わっていましたが、systemd に登録することにします。まずは先ほどの ffmpeg 起動コマンドをスクリプト化します。

さっきは、直打ちしていたパラメータを環境変数に逃がしてあげます。更にちょっと乱暴ですが起動時に ffmpeg のプロセスを一旦全部殺します。ロックファイルで制御するのが丁寧だとおもいますが、面倒なのでよしとします。

#!/bin/bash 

ROTATE=180
BITRATE=512k
FRATE=15
RES=720x480
# RES=1920x1080


# kill other instances of ffmpeg
pkill ffmpeg

# start camera server
ffmpeg -f v4l2 -thread_queue_size 8192 -s $RES -i /dev/video0 -r $FRATE \
    -c:v h264_omx -b:v $BITRATE \
    -vf "rotate=$ROTATE*PI/180" \
    -f flv rtmp://localhost/live/stream > /dev/null 2>&1 </dev/null&

これを呼び出す systemd ユニットを作成します。名前はrtmp-camera.service としましたが好きなものに変えて構いません。

ExecStart に 上のシェルスクリプトのフルパスを指定します。systemd unit ファイルでは フルパスを指定します。そうでないとサービス起動時にエラーになります。

パスはシェルスクリプトを作成した場所に適宜変更してください。

[Unit]
Description=RTMP camera server with ffmpeg.

[Service]
ExecStart=/opt/rtmp/start.sh
Restart=always
#Type=simple
Type=oneshot
RemainAfterExit=yes
User=pi

[Install]
WantedBy=multi-user.target

2024-4-12 Type=oneshot に修正し、RemainAfterExit=yes を追加。

systemctl で有効にしていきます。

$ sudo systemctl enable /opt/rtmp/rtmp-camera.service
$ sudo systemctl daemon-reload
$ sudo systemctl start rtmp-camera.service

daemon-reload はもしかしたら不要かもしれません。とにかく enable した後、ちゃんと起動しているか確かめてください。上手く動いていればブラウザでカメラで撮影している動画が流れているはずです。

参考

超簡単! ffmpeg + rtmp + nginxを使ってraspberry pi + webカメラのストリーミング環境を作る(raspbian buster版) | IoT PLUS

子供の笑顔と笑い声を聞く為に ffmpeg + Nginx + RTMP on RaspberryPI – 長生村本郷Engineers’Blog

【ffmpeg】簡単ネットワークカメラの作り方
ここの例は、hls ではなく jpg 画像を更新し続けているのでクライアントからすると motionJPEG 形式になるのだと思う。