🐣

Raspberry Pi + Webカメラ + WebRTC Native Client Momo で赤ちゃん用の見守りカメラ作ってみた

2023/02/02に公開

こんにちは、ソフトウェアエンジニア兼、一児のパパ @konnyaku256 です。

先日、Raspberry Pi + Web カメラ + WebRTC Native Client Momo で赤ちゃん用の見守りカメラシステム「mimamori」を作ってみました。

実際に我が家の育児支援ツールとして運用してみたところ、けっこう活躍してくれています。

この記事では、そんな mimamori を支えている技術スタックやそれらを用いた実装例を紹介したいと思います。

ソースコードやシステムの使い方などは GitHub で公開しています。
気に入ったら Star いただけると嬉しいです。

https://github.com/konnyaku256/mimamori

mimamori の技術概要

主要コンポーネント

システム構成図

技術スタック

  • Web Frontend
    • HTML
    • Tailwind CSS
    • JavaScript
  • Backend
    • WebRTC サーバー
      • WebRTC Native Client Momo
    • Web カメラ制御 API サーバー
      • Golang
        • Gin Web Framework
        • os/exec
      • v4l2-utils (v4l2-ctl)
    • 画面キャプチャ & LINE 通知
      • Python
        • opencv-python
        • requests
        • python-dotenv
      • LINE Notify
    • ビデオデバイス多重化
      • v4l2loopback
      • FFmpeg
    • サービス化
      • Systemd

Web Frontend の実装

mimamori の Web Frontend は WebRTC Native Client Momo が WebRTC で配信する映像と音声をストリーミングします。
また、後述する API サーバーのクライアントとしても機能します。

Momo のリポジトリやリリースに含まれている以下のサンプルコードを参考に実装しました。
https://github.com/shiguredo/momo/tree/develop/html

test.html はファイル名やボタンなどの UI を変更し、
Tailwind CSS で最低限のスタイリングを当てました。

https://github.com/konnyaku256/mimamori/blob/main/frontend/mimamori.html

webrtc.js は全く変更せずにそのまま使用しました。

mimamori-exec-client.js は後述する Web カメラ制御 API サーバーのクライアント JavaScript です。

https://github.com/konnyaku256/mimamori/blob/main/frontend/mimamori-exec-client.js

WebRTC サーバーの実装

Web カメラの映像と音声を取得して WebRTC で配信します。

Momo のバイナリを test モードで実行しているだけです。
https://github.com/shiguredo/momo/releases

Web カメラ制御 API サーバーの実装

Web カメラ制御 API サーバー(mimamori-exec-server)は次の 2 つの処理を外部から実行できるようにするための API サーバーです。

  • カメラモード「昼」または「夜」の設定
    • v4l2-utils に含まれる v4l2-ctl コマンドを使用して、Web カメラの設定を制御します。
  • 画面キャプチャ
    • 後述する画面キャプチャ & LINE 通知スクリプト(mimamori-capture-screen)を実行します。

このサーバーは Golang で実装しました。
ルーティングや HTTP のハンドリングは Gin Web Framework で行い、
標準モジュールの os/exec を用いて Raspberry Pi 上の外部コマンドを実行できるようにしました。

https://github.com/konnyaku256/mimamori/blob/main/backend/mimamori-exec-server/main.go#L10-L24

https://github.com/konnyaku256/mimamori/blob/main/backend/mimamori-exec-server/handler/handler.go#L9-L18

https://github.com/konnyaku256/mimamori/blob/main/backend/mimamori-exec-server/exec_command/camera_mode.go#L8-L23

https://github.com/konnyaku256/mimamori/blob/main/backend/mimamori-exec-server/exec_command/exec_command.go#L8-L13

画面キャプチャ & LINE 通知スクリプトの実装

画面キャプチャ & LINE 通知スクリプト(mimamori-capture-screen)は Web カメラが捉えている画像を読み取り、LINE Notify を経由して指定したトークルームに画像を送信する Python スクリプトです。

OpenCV で Web カメラの画像を読み取り、requests で LINE Notify の API にアクセストークンを付与して画像送信のリクエストをしています。

https://github.com/konnyaku256/mimamori/blob/main/scripts/mimamori-capture-screen/main.py

ビデオデバイス多重化の実装

Linux では、通常 1 台のウェブカメラ(Video4Linux デバイス)を複数のアプリケーション(mimamori では WebRTC サーバーと画面キャプチャ & LINE 通知スクリプト)で同時に使うことはできないため、v4l2loopback で複数の仮想ビデオデバイスを作成し、FFmpeg で物理ビデオデバイスの映像を仮想ビデオデバイスに転送することで、この問題を回避しています。

v4l2loopback を使って次のようなコマンドを実行することで、仮想ビデオデバイスを作成できます。

sudo modprobe v4l2loopback video_nr=2,3

上記のコマンドで作成される仮想ビデオデバイスの例

v4l2-ctl --list-devices

Dummy video device (0x0000) (platform:v4l2loopback-000):
	/dev/video2

Dummy video device (0x0001) (platform:v4l2loopback-001):
	/dev/video3

また、FFmpeg を使って、物理ビデオデバイスの映像を先ほど作成した仮想ビデオデバイスに転送できます。

ffmpeg \
  -f video4linux2 -input_format mjpeg -video_size 1280x720 -framerate 30 -i /dev/video0 \
  -f video4linux2 -vcodec copy /dev/video2 \
  -f video4linux2 -vcodec copy /dev/video3

v4l2loopback と FFpmeg を使ったコマンドは ffmpeg-virtual-stream.sh としてシェルスクリプト化しています。

https://github.com/konnyaku256/mimamori/blob/main/scripts/ffmpeg-virtual-stream/ffmpeg-virtual-stream.sh

あとは、WebRTC サーバーと画面キャプチャ & LINE 通知スクリプトがそれぞれ別の仮想ビデオデバイスを参照するようにしています。

サービス化の実装

Raspberry Pi の起動時に WebRTC サーバーが自動的に起動されるように Systemd を用いてサービス化しています。

具体的には次の 3 つの Systemd のサービスを /etc/systemd/system 下に配置しています。

WebRTC Native Client Momo のバイナリを test モードで実行するサービスです。

https://github.com/konnyaku256/mimamori/blob/main/etc/systemd/system/mimamori-webrtc-server.service

Web カメラ制御 API サーバーのバイナリを実行するサービスです。
mimamori-capture-screen(Python スクリプト)の実行に必要なモジュールをインストールした環境で実行できるようにするため、実行ユーザーは pi を指定しています。

https://github.com/konnyaku256/mimamori/blob/main/etc/systemd/system/mimamori-exec-server.service

ffmpeg-virtual-stream.sh を実行するサービスです。

https://github.com/konnyaku256/mimamori/blob/main/etc/systemd/system/ffmpeg-virtual-stream.service

[おまけ 1] Fire TV で閲覧する

Fire TV とは Amazon が展開しているテレビ向け映像出力デバイスです。
Fire TV には Fire OS と呼ばれる Android をベースとした Fire TV 独自の OS が搭載されています。
また、Fire TV などの Amazon 製デバイスでは Amazon Silk と呼ばれる専用のウェブブラウザが利用可能です。

そこで、我が家では、Fire TV にインストールした Fire Silk から mimamori にアクセスすることで、テレビからも配信を閲覧できるようにしています。

[おまけ 2] 技術選定で見送った技術

「ラズパイ カメラ 配信」などのキーワードでネット検索すると次のような技術を使った例がよく出てきました。

映像を配信するだけなら、MJPG-Streamer を使う例が最も多く、動体検知も行う場合は Motion がよく使われているようでした。
また、映像と音声を両方配信できるものとしては WebRTC を採用した Janus の例が多く見つかりました。

この記事の執筆時点(2023 年 2 月 2 日)での GitHub のスター数を比較すると、Janus(7k stars)、Motion(3.3k stars)、MJPG-Streamer(2.6k stars)の順で人気のようでした。

mimamori では 映像と音声の両方を配信したかったため、MJPG-Streamer や Motion は真っ先に選択肢から外れました。
そのため、WebRTC を使った Janus が有力となりましたが、Janus は
依存関係をインストールしたり、Janus 自体をソースコードからビルドする必要があったりして、導入の手間を感じました。

もっと楽に導入できて、使いやすいソフトウェアはないものかと探したところ、WebRTC Native Client Momo を発見しました。
Momo は WebRTC で配信できて、導入も簡単だったためこちらを使うことにしました。

おわりに

いかがでしたか?
このように、mimamori は WebRTC や Golang、Python などの様々な技術を活用して開発しました。
見守りカメラは既製品を買うことで簡単に導入できますが、自作すれば運用中の「こんな機能も欲しい」という要望にすぐ対応できていいなと思っています。

見守りカメラを自作してみたい方の参考になれば幸いです。

GitHubで編集を提案

Discussion