ちょっと離れたところの様子がみたいことってありますよね。 たとえば、風が強い日の洗濯物👕の様子とか、もうすこしで花🌺が咲きそうなつぼみ、など。 たびたび見に行くのは面倒ですよね。
そんな時にご家庭にあるRaspberry Pi3とUSBカメラで映像配信して、スマホで見られたらいいなと思って、 生活に役立つ工作をしてみることにしました。
やりたいこと

イメージはこんな感じです😎
どうやって作るの?

安いWebカメラはMJPEG(Motion JPEG)形式で動画が出てくるものが多いです。 でも容量が大きくなってしまう欠点があるので、動画変換ツールFFmpegで高圧縮なH.264に変換します。 変換した動画をRTMPというプロトコルでNginxに送り込みます。
FFmpegはUSBカメラの取り込みとRTMP出力に対応しているので、ここまでFFmpegだけで完結できます。 さすがFFmpeg、万能すぎですね✨
スマホからの接続はWebサーバーのNginxが待ち受けます。 ただ、RTMPでの動画配信には対応していないブラウザが多いです。 そのため、NginxからはHLSという形式で動画配信します。 NginxのプラグインがHLSへの変換をしてくれます。
設定するだけでできるなんて、何とありがたいことでしょう。
これで、AndroidのChrome、iOSのSafari、Windows10のEdgeなど、多くのブラウザから見られます。
準備するもの
- Raspberry Pi3 + マイクロSDカード + 電源(USB5V2A以上)
- Webカメラ
Webカメラは、UVC (USB Video Class)対応のカメラならだいたい使えると思います。 今どきの安いWebカメラでほぼ大丈夫でしょう。 私は、家に転がっていたこちらを使いました。
Raspberry Pi3はRaspbian Buster Liteをインストールしました。 もしやり方がわからなければこちらをどうぞ。
以下、Raspberry Pi3にSSHで接続して作業します。
FFmpeg + Nginxのインストール
昔はRaspberry Pi3のハードウェアエンコードに対応するFFmpegがaptで配布されていなかったので、 自前で最新ソースコードからビルドしていたようですが、今はaptコマンドですぐにインストールできます。 日々便利になるRaspberry Piはすごいですね!
$ sudo apt install nginx libnginx-mod-rtmp ffmpeg
依存パッケージも含めてまとめてインストールしましょう。
USBカメラ接続
インストールしたら、早速USBカメラをつないでみます。 Raspberry Pi3は認識してくれたかな…?チェックするには、次のコマンドを使います。
$ v4l2-ctl --list-device
bcm2835-codec-decode (platform:bcm2835-codec):
/dev/video10
/dev/video11
/dev/video12
USB_Camera: USB_Camera (usb-3f980000.usb-1.4):
/dev/video0
/dev/video1
bcm2835-codec-decode以外に、USB_Cameraが出きたので、認識されていそうですね。
私のWebカメラにはマイク🎤がついているので、マイクも認識されているかチェックしましょう。 こんなコマンドを使います。
$ arecord -l **** List of CAPTURE Hardware Devices **** card 1: USBCamera [USB_Camera], device 0: USB Audio [USB Audio] Subdevices: 0/1 Subdevice #0: subdevice #0
ここにもUSB Audioが認識されていますね。マイクも大丈夫そうです。
Nginxの設定
/etc/nginx/modules-available/rtmp-live.confというファイルを新規作成します。
$ sudo nano /etc/nginx/modules-available/rtmp-live.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 /tmp/live;
hls_fragment 1s;
hls_type live;
}
}
}
これで、RTMPで動画を待ち受けて、HLSで動画配信するようになります。
配信用のファイルは/tmp/liveというディレクトリに出力します。
次回起動時にこの設定が有効になるように、modules-enabledに追加します。
$ cd /etc/nginx/modules-enabled $ sudo ln -s ../modules-available/rtmp-live.conf ./99-rtmp-live.conf
これで、HLSで配信できるようになりました。
動画置き場をSDカードからRAMディスクに変更
ここまでで、RTMPで受信した動画を/tmp/liveに書き出すようになりました。
しかし、Raspbianのデフォルトでは、/tmpはSDカードに保存されます。
動画を書いたり消したりを繰り返していると、SDカードの寿命が心配です。
RAMに書き出すようにしましょう。
書き出し先を変えるには、/etc/fstabを編集します。
$ sudo nano /etc/fstab
このファイルに次の行を追加しました。
tmpfs /tmp tmpfs defaults,noatime,mode=1777 0 0
これで、/tmpがRAM上に書き出されるようになります。
RAMなので、再起動すると消えるので重要なファイルを置かないよう注意してくださいね。
この動画配信だけに使うならば、まったく問題ありません。
index.htmlの準備
ブラウザから生のHLSを見るのはハードルが高いです。
簡単にみられるよう、Webページを用意しました。
動画プレーヤーを簡単に作れるVideo.jsというJavaScriptライブラリがありますので、利用させていただきました。
Webページは、/var/www/htmlに配置します。
WebページからHLS動画が見られるよう、先ほどの/tmp/liveにシンボリックリンクを貼りました。
$ cd /var/www/html $ sudo ln -s /tmp/live live
そして、トップページになるindex.htmlを作ります。
$ sudo nano /var/www/html/index.html
Video.jsの公式ページのサンプルをベースに、このようなファイルを作ってみました。
<head>
<link href="https://vjs.zencdn.net/7.6.6/video-js.css" rel="stylesheet" />
</head>
<body>
<video-js
id="my-video"
controls
liveui="true"
preload="auto"
width="1280"
height="720"
data-setup="{responsive: true}"
>
<source
src="/live/stream.m3u8"
type="application/x-mpegURL" />
<p class="vjs-no-js">
To view this video please enable JavaScript, and consider upgrading to a
web browser that
<a href="https://videojs.com/html5-video-support/" target="_blank"
>supports HTML5 video</a
>
</p>
</video-js>
<script src="https://vjs.zencdn.net/7.6.6/video.js"></script>
</body>
長かったNginxの準備もこれで終わりです。お疲れさまでした。
Nginxの動作チェック
早速、Nginxを再起動してこれまでの設定を反映しましょう。
$ sudo systemctl restart nginx.service
エラーなく起動しましたか?もしエラーが出たら、/var/log/nginx/error.logにエラー原因が書いてあるかもしれません。タイプミスなどがあれば修正してください。私は何度もやり直しました😅
エラー無く起動できれば、早速ブラウザから見てみましょう。
ブラウザから、http://raspberrypi.localを開いてみてください。

このように、動画プレーヤー画面が出てくればOKです。 まだ動画配信をスタートしていないので、真っ暗です。いよいよ動画配信しましょう。
ffmpegで動画配信
それでは、試しに動画配信します。次のコマンドを実行してください。
$ ffmpeg \
-f v4l2 -thread_queue_size 8192 -input_format yuyv422 \
-video_size 1280x720 -framerate 10 \
-i /dev/video0 \
-c:v h264_omx -b:v 768k \
-bufsize 768k -vsync 1 -g 10 \
-f flv rtmp://localhost/live/stream \
ブラウザでもう一度アクセスしなおしてみましょう。Webカメラの映像が見えました!😄
自動起動化
配信スクリプトを用意しましょう。
$ nano ~/mystreaming.sh
ビデオだけ配信するには、次のようにします。
#!/bin/bash
if pidof -x ffmpeg > /dev/null; then
echo "FFmpeg is already running."
exit 1
fi
ffmpeg \
-f v4l2 -thread_queue_size 8192 -input_format yuyv422 \
-video_size 1280x720 -framerate 10 \
-i /dev/video0 \
-c:v h264_omx -b:v 768k \
-bufsize 768k -vsync 1 -g 10 \
-f flv rtmp://localhost/live/stream \
> /dev/null 2>&1 < /dev/null
音声も一緒に配信するなら、次のようにします。
#!/bin/bash
if pidof -x ffmpeg > /dev/null; then
echo "FFmpeg is already running."
exit 1
fi
ffmpeg \
-f alsa -thread_queue_size 8192 -i hw:1 \
-f v4l2 -thread_queue_size 8192 -input_format yuyv422 \
-video_size 1280x720 -framerate 8 -i /dev/video0 \
-c:v h264_omx -b:v 768k -bufsize 768k -vsync 1 -g 10 \
-c:a aac -b:a 96k -ar 44100 -af "volume=30dB" \
-f flv rtmp://localhost/live/stream \
> /dev/null 2>&1 < /dev/null
Raspbianのサービス化して自動起動するようにしましょう。
$ sudo nano /etc/systemd/system/mystreaming.service
内容は次の通りです。
[Unit] Description=My Streaming Service RequiresMountsFor=/tmp After=nginx.target [Service] Type=simple Restart=always RestartSec=10 User=pi Group=pi WorkingDirectory=/home/pi ExecStart=/bin/bash /home/pi/mystreaming.sh [Install] WantedBy=multi-user.target
起動時に自動起動するようにします。
$ sudo systemctl enable mystreaming.service
再起動してみましょう。 再起動後、ブラウザでWebカメラ映像が見られれば成功です。

モバイルバッテリー付きで道頓堀川へ持って行って撮影しました。なぜかかなり黄みがかった映像になってしまいました…🤔
注意点としては、遅延が結構大きい(最低5秒くらい)のと、たまにストリーミングが異常終了してしまうことがあることです。 もう少し安定化できないか、いろいろ試したいと思います😆
追記 (2020/8/15)
以下の記事で、JanusというオープンソースのWebRTCサーバーを用いて同様にUSBカメラの映像をH.264でストリーミングしています。 こちらのほうが遅延が1秒以下と優秀ですので、ぜひこちらもお試しください。
※こちらのロジクールのカメラはオートフォーカスで画質が良くお気に入りです!