長生村本郷Engineers'Blog

千葉県長生村本郷育ちのエンジニアが書いているブログ

子供の笑顔と笑い声を聞く為に ffmpeg + Nginx + RTMP on RaspberryPI

f:id:kenzo0107:20180815181451p:plain

概要

RaspberryPI 上で rtmp モジュール付きの nginx をビルドし
WebCamera で撮影した 動画+音声付き を HLS 配信する際の手順をまとめました。

経緯

はじめは外出中にペットのうさぎ用に mjpeg-streamer でモニターしていました。
子供が生まれると、子供が元気にしてるかな、とふとモニターするようになりました。

ですが、うさぎは鳴きませんが、子供は泣き叫びます。
mjpeg-streamer では表情こそわかりますが、我が子の声が聞こえてきません。

その為、動画+音声付きで低負荷で 動画+音声 配信ができないものかと探していた際に ffmpeg に出会いました。*1

購入したもの

LOGICOOL HDウェブカム フルHD動画対応 C615

LOGICOOL HDウェブカム フルHD動画対応 C615

Nginx

http://nginx.org/en/download.html で現時点で最新バージョンをダウンロードしました。

各種ダウンロード

以下ダウンロードしています。
nginx-http-auth-digest は Nginx で Digest 認証すべくモジュール追加しました。

  • Nginx
  • nginx-rtmp-module
  • openssl
  • nginx-http-auth-digest
  • ffmpeg
// home ディレクトリで作業するとします。
pi$ cd ~

// nginx
pi$ wget http://nginx.org/download/nginx-1.15.2.tar.gz

// nginx-rtmp-module
pi$ wget -O rtmp.zip https://github.com/arut/nginx-rtmp-module/archive/master.zip
pi$ wget -O ssl.zip https://github.com/openssl/openssl/archive/master.zip

// nginx-http-auth-digest
pi$ git clone https://github.com/samizdatco/nginx-http-auth-digest.git
pi$ cd nginx-http-auth-digest
pi$ git clone https://gist.github.com/frah/3921741
pi$ patch -u < 3921741/patch-ngx_http_auth_digest_module.diff

// ffmpeg
pi$ git clone git://source.ffmpeg.org/ffmpeg.git
pi$ wget ftp://ftp.alsa-project.org/pub/lib/alsa-lib-1.1.6.tar.bz2

解凍

pi$ tar xvzf nginx-1.15.2.tar.gz
pi$ unzip rtmp.zip
pi$ unzip ssl.zip
pi$ tar xjvf alsa-lib-1.1.6.tar.bz2

Nginx ビルド

pi$ cd nginx-1.15.2/
pi$ sudo ./configure --with-http_ssl_module --with-http_realip_module --add-module=../nginx-rtmp-module-master --with-openssl=../openssl-master --add-module=../nginx-http-auth-digest
pi$ sudo make
pi$ sudo make install

Nginx Version 確認

pi$ /usr/local/nginx/sbin/nginx -V

nginx version: nginx/1.15.2
built by gcc 4.9.2 (Raspbian 4.9.2-10+deb8u1)
built with OpenSSL 1.1.1-pre9-dev  xx XXX xxxx
TLS SNI support enabled
configure arguments: --with-http_ssl_module --with-http_realip_module --add-module=../nginx-rtmp-module-master --with-openssl=../openssl-master --add-module=../nginx-http-auth-digest

Nginx をシンボリックリンクでパスが通っている場所から参照できるようにする。

pi$ sudo ln -s /usr/local/nginx/sbin/nginx /usr/bin/nginx

pi$ which nginx

ffmpeg ビルド

pi$ sudo apt-get install libomxil-bellagio-dev

pi$ cd alsa-lib-1.1.6
pi$ ./configure --prefix=/home/pi/ffmpeg
pi$ sudo make
pi$ sudo make install

pi$ cd /home/pi/ffmpeg
pi$ sudo ./configure  --enable-gpl  --enable-nonfree --enable-mmal --enable-omx-rpi --enable-omx --extra-cflags="-I/home/pi/ffmpeg/include" --extra-ldflags="-L/home/pi/ffmpeg/lib" --extra-libs=-ldl
pi$ sudo make -j4
pi4 sudo make install

sudo apt-get install libomxil-bellagio-dev を実行していない場合に以下のエラーが出ました。

ERROR: OMX_Core.h not found

録音してみる

動画撮影デバイス一覧確認

pi$ v4l2-ctl --list-device

HD Webcam C615 (usb-3f980000.usb-1.3):
        /dev/video0

上記コマンド実行時に以下のようなエラーが出る時は、

Failed to open /dev/video0: No such file or directory

以下コマンドを試してください。

pi$ sudo pkill /dev/video0

音声入力デバイス一覧確認

自分の場合は
カード 1 は WebCam
カード 2 は マイク、
です。

pi$ arecord -l

**** ハードウェアデバイス CAPTURE のリスト ****
カード 1: C615 [HD Webcam C615], デバイス 0: USB Audio [USB Audio]
  サブデバイス: 1/1
  サブデバイス #0: subdevice #0
カード 2: Device [USB PnP Sound Device], デバイス 0: USB Audio [USB Audio]
  サブデバイス: 1/1
  サブデバイス #0: subdevice #0

いざ録音

カード 2 が入力デバイスである為、 hw:2 としました。

pi$ ffmpeg -f alsa -ac 1 -i hw:2 -f v4l2 -s 640x480 -i /dev/video0 output.mpg

生成された output.mpg ファイルを mac 上にダウンロードし再生を試してみてください。

これで再生されれば、ffmpeg が問題なく動作していることを確認できたことになります。

次は配信する為の設定です。

Nginx 設定

設定ファイル格納用ディレクトリ作成

pi$ sudo mkdir -p /usr/local/nginx/conf.d

HLS ファイル生成用ディレクトリ作成

pi$ sudo mkdir -p /var/www/html/live/hls

HLS 配信用 index.html

  • hls.min.js 取得
pi$ cd /var/www/html
pi$ wget https://cdn.jsdelivr.net/hls.js/latest/hls.min.js
  • /var/www/html/index.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8"/>
  <script src="./hls.min.js"></script>
</head>

<body>
  <video id="video"></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>
</body>
</html>

Digest 認証設定

pi$ cd /var/www
pi$ sudo htdigest -c .htdigest 'digest AuthNginx' hoge
password: <enter password>

各種設定ファイル配置

  • /usr/local/nginx/conf.d/default.conf
server {
    listen 8090;
    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_digest "digest AuthNginx";
        auth_digest_user_file /var/www/.htdigest;

        root /var/www/html;
        index index.html;
        set_real_ip_from    127.0.0.1;
        real_ip_header      X-Forwarded-For;
    }
}
  • /usr/local/nginx/conf.d/rtmp
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;
        }
    }
}
  • /usr/local/nginx/conf/nginx.conf
user  www-data;
worker_processes  1;

pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include           mime.types;
    default_type      application/octet-stream;
    sendfile          on;
    keepalive_timeout 65;
    include /usr/local/nginx/conf.d/*.conf;
}

include /usr/local/nginx/conf.d/rtmp;

Nginx 起動設定ファイル

  • /lib/systemd/system/nginx.service
[Unit]
Description=The NGINX HTTP and reverse proxy server
After=syslog.target network.target remote-fs.target nss-lookup.target

[Service]
Type=forking
PIDFile=/var/run/nginx.pid
ExecStartPre=/usr/local/nginx/sbin/nginx -t
ExecStart=/usr/local/nginx/sbin/nginx
ExecReload=/usr/local/nginx/sbin/nginx -s reload
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true

[Install]
WantedBy=multi-user.target

Nginx 起動

pi$ sudo systemctl daemon-reload
pi$ sudo systemctl start nginx
pi$ sudo systemctl status nginx

この時はまだ HLS ファイルが生成されていませんので
http://<RaspberryPI IP>:8090 にアクセスしても HLS 配信されていません。

f:id:kenzo0107:20180815172215p:plain

ffmpeg を起動することで /var/www/html/live/hls/ ディレクトリ以下に stream.m3u8 が生成されます。

ffmpeg 起動

pi$ sudo ffmpeg \
-f alsa -ac 1 -thread_queue_size 8192 -i hw:2 \
-f v4l2 -thread_queue_size 8192 -input_format yuyv422 -video_size 432x240 -framerate 30 -i /dev/video0 \
-c:v h264_omx -b:v 768k -bufsize 768k -vsync 1 -g 16  \
-c:a aac -b:a 128k -ar 44100 \
-af "volume=30dB" \
-f flv rtmp://localhost/live/stream;

アクセスしてみる

http://<RaspberryPI IP>:8090 にアクセスしてみます。 Digest 認証を問われるので設定した ID/PW を入力します。

f:id:kenzo0107:20180815172119p:plain

HLS 配信されていることを確認できました!

f:id:kenzo0107:20180815173337p:plain

負荷状況としては CPU 25 - 30% 程度に収まっています。

まとめ

ffmpeg + Nginx + RTMP で HLS 配信を RaspberryPI 上に構築できました。

実際の運用では、常時起動してはおらず、見たいときだけ起動するような仕組みにしており、負荷は極力抑えています。 監視は Mackerel の無料プランで今のところ十分です。

育児ハックの一環として参考になれば何よりです。

*1:勿論のことですが、家族の了承を得た上で設定しています。