はじめに
最近、Raspberry Pi 3 と CSI 接続の標準カメラモジュールを入手しました。
せっかくカメラも入手したので、カメラで撮影した動画をリアルタイムで、できれば遅延を減らして高fpsでブラウザから閲覧したいなと思って色々方法を試してみたので、まとめてみます。
なお Raspberry Pi 3 の OS は Raspbian Jessie が前提となります。
$ uname -a
Linux raspberrypi 4.4.45-v7+ #954 SMP Fri Jan 27 19:06:40 GMT 2017 armv7l GNU/Linux
配信方法1 - mjpg-streamer
この方法は motion jpeg で配信します。いろんなところで紹介されている標準的な方法だと思います。
インストール
$ sudo apt-get install -y cmake libv4l-dev libjpeg-dev imagemagick
$ git clone https://github.com/jacksonliam/mjpg-streamer.git
$ cd mjpg-streamer/mjpg-streamer-experimental
$ make
動画の配信
$ ./mjpg_streamer -o "./output_http.so -w ./www" -i "./input_raspicam.so -x 640 -y 480 -fps 30 -q 10"
-q は jpeg の画質指定で、1が最小100が最大です。こいつを小さくすると高レートで配信できます。↑の例だと 10 なので画質は無茶苦茶粗いですが、fps はけっこう出ます。遅延は回線によりますが、LAN内なら WiFi とかでもほぼリアルタイムで見られました。
動画の受信
ブラウザで http://[ラズパイのIP]:8080/ を開くだけです。
配信方法2 - uv4l-raspicam
mjpg-streamer と同じく、 motion jpeg での配信となります。デーモンとして動作するので、ちゃんと使うおうとするとちょっと設定が手間かも。
インストール
linux-projects.org のリポジトリを参照しますので、まずは apt-key で鍵を登録します。
$ curl http://www.linux-projects.org/listing/uv4l_repo/lrkey.asc | sudo apt-key add -
登録できたら、/etc/apt/sources.list
に以下を追加します。
deb http://www.linux-projects.org/listing/uv4l_repo/raspbian/ jessie main
その後、
$ sudo apt-get update
$ sudo apt-get install uv4l-webrtc uv4l-raspicam-extras
でインストール完了です。インストール完了後 uv4l_raspicam サービスが勝手に起動します。
動画の配信
本来は /etc/uv4l_raspicam/ 以下の設定を書き換えてサービスを起動すべきなのですが、説明が長くなるので端折ります。まずは、uv4l_raspicam サービスを止めます。
$ sudo service uv4l_raspicam stop
次にコマンドラインから uv4l を直接起動します。
$ uv4l --auto-video_nr --driver raspicam --encoding mjpeg --width 640 --height 480 --quality 15 --server-option '--port=9000'
動画の受信
ブラウザで http://[ラズパイのIP]:9000/stream/video.mjpeg を開くとストリームが見られます。
配信方法3 - gstreamer で h264動画 を [HLS][] (MPEG-2 TS) 形式にして配信
gstreamer の tcpserversink 経由で動画ストリームをそのままブラウザに渡す(HTML5 タグのソースとして使う)という豪快な方法を紹介している記事がいくつか見つかります。HTTPプロトコルまるっきり無視な気がするけど、ブラウザから GET 来た時だけ HTTP 喋ったりするんですかね...?よく分かりません。
しかし、ラズパイが生成できる h264 ストリームをそのままvideoタグに突っ込んで再生できるブラウザはないようです。
しかたないので以下では h264 ストリームを gstreamer を使って [HLS] 化して配信します。この方法は遅延が数十秒になるのと最近のブラウザだと Android の Chrome あたりでしか再生できないのですが、いちおう参考ということで。
[HLS]: http://k-tai.watch.impress.co.jp/docs/column/keyword/515059.html
インストール
まずは gstreamer 本体をインストールします。
$ sudo apt-get install -y gstreamer1.0 gstreamer1.0-tools
gstreamer は raspivid コマンドの出力をそのままパイプで入力することも出来ますが、せっかくなので Raspberry Pi の標準カメラを直接入力ソースとして使える gst-rpicamsrc もインストールします。
$ git clone https://github.com/thaytan/gst-rpicamsrc.git
$ cd gst-rpicamsrc
$ ./autogen.sh --prefix=/usr --libdir=/usr/lib/arm-linux-gnueabihf/
$ make
$ sudo make install
生成したHLSファイル一式を配信するために Web サーバーがいるので、 nginx を入れます。
$ sudo apt-get install -y nginx
HLSファイルを /var/www/html/stream/ に生成するので mkdir します。
$ sudo mkdir /var/www/html/stream/
以下の内容でindex.html ファイルを /var/www/html/stream/index.html に置きます。
<!DOCTYPE html>
<html><head></head><body>
<video src="./stream/output.m3u8" width="640" height="480"/>
</body></html>
動画の配信
次のコマンドで、カメラ画像のストリーミングを開始します。
$ gst-launch-1.0 -v -e rpicamsrc ! video/x-h264,width=640,height=480,bitrate=800000 ! h264parse ! mpegtsmux ! hlssink max-files=8 target-duration=5 location=/var/www/html/stream/segment%05d.ts playlist-location=/var/www/html/stream/output.m3u8 playlist-root=./
動画の受信
ブラウザで http://[ラズパイのIP]/stream/ を開きます。
補足
gstreamer + Janus WebRTC Gateway という配信方法もあるようです。こちらは VP8 になるので Chrome 専用(だと思う)のと、 Raspberry Pi で VP8 エンコードはちょっと重いかな...と思って今回は試しておりません。
配信方法4 - h264-live-player
Javascript で h264 デコードして canvas タグ上で再生するというぶっちぎりアプローチです。最初ネタだと思ったんですが、PC はおろか iPhone 7の Safari でも普通に動いてしまいました。ラズパイ側では raspivid の出力を WebSocket 経由でブラウザに垂れ流すというシンプルな構成。ちなみに h264-live-player が使っている h264 デコーダは Broadway.js は Android のデコーダを emscripten でビルドしたものらしいです。
インストール
まず、nodejs をインストールします。普通に apt-get するとむちゃくちゃ古い nodejs が入ってしまうので、本家の記事に従って6系をインストールします。
$ curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash -
$ sudo apt-get install -y nodejs
次に h264-live-player をインストール
$ git clone https://github.com/131/h264-live-player
$ cd h264-live-player
$ npm install
動画の配信
以下を実行します。内部的には raspivid とウェブサーバーが起動してブラウザからの接続を待つ状態になります。
$ node server-rpi.js
動画の再生
ブラウザで http://${ラズパイのIP}:8080/ を開きます。
補足
ブラウザが WebSocket を切断したタイミングで server-rpi.js がクラッシュします。クラッシュした場合は raspivid のプロセスが残っているので、それを kill してから node server-rpi.js を再度実行する必要があります。
ちなみに切断時にクラッシュする現象は h264-live-player の lib/_server.js の 79行目 self.readstream.end()
をコメントアウトすると抑えられます。
78: socket.on('close', function() {
79: self.readStream.end();
80: console.log('stopping client interval');
81: });
おわりに
低解像度なら mjpeg 形式の配信であまり困らないと思います。h264 はビットレートが安定するので突然のラグは少ない反面、ブロックノイズは目立つことがあります。
再生遅延については画質と解像度によって mjpeg と h264 どちらが良いかは一概に言えない印象なので、色々設定を変えながら両方とも試すのが良いかなと思います。