みかんのゆるふわ技術ブログ

Raspberry PiやIoT関係のことを書き残していきます

Raspberry Pi + USBカメラ のH.264映像をブラウザで見られるようにする

ちょっと離れたところの様子がみたいことってありますよね。 たとえば、風が強い日の洗濯物👕の様子とか、もうすこしで花🌺が咲きそうなつぼみ、など。 たびたび見に行くのは面倒ですよね。

そんな時にご家庭にあるRaspberry Pi3USBカメラで映像配信して、スマホで見られたらいいなと思って、 生活に役立つ工作をしてみることにしました。

やりたいこと

f:id:kimura_khs:20200119122649p:plain

イメージはこんな感じです😎

どうやって作るの?

f:id:kimura_khs:20200119125225p:plain

安い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カメラでほぼ大丈夫でしょう。 私は、家に転がっていたこちらを使いました。

www.buffalo.jp

Raspberry Pi3はRaspbian Buster Liteをインストールしました。 もしやり方がわからなければこちらをどうぞ。

www.mikan-tech.net

以下、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ライブラリがありますので、利用させていただきました。

videojs.com

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を開いてみてください。

f:id:kimura_khs:20200119134537p:plain

このように、動画プレーヤー画面が出てくれば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
# Wait for system boot up
sleep 30

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カメラ映像が見られれば成功です。

f:id:kimura_khs:20200202220444p:plain

モバイルバッテリー付きで道頓堀川へ持って行って撮影しました。なぜかかなり黄みがかった映像になってしまいました…🤔

注意点としては、遅延が結構大きい(最低5秒くらい)のと、たまにストリーミングが異常終了してしまうことがあることです。 もう少し安定化できないか、いろいろ試したいと思います😆

追記 (2020/8/15)

以下の記事で、JanusというWebRTCサーバーを用いて同様にUSBカメラの映像をH.264でストリーミングしています。 こちらのほうが遅延が1秒以下と優秀ですので、ぜひこちらもお試しください。

www.mikan-tech.net