Raspberry Pi 3 の標準カメラで撮影した動画をブラウザに配信する方法まとめ

# はじめに
最近、Raspberry Pi 3 と CSI 接続の標準カメラモジュールを入手しました。

せっかくカメラも入手したので、カメラで撮影した動画をリアルタイムで、できれば遅延を減らして高fpsでブラウザから閲覧したいなと思って色々方法を試してみたので、まとめてみます。

なお Raspberry Pi 3 の OS は Raspbian Jessie が前提となります。

```shell-session
$ uname -a
Linux raspberrypi 4.4.45-v7+ #954 SMP Fri Jan 27 19:06:40 GMT 2017 armv7l GNU/Linux
```

[mjpg-streamer]:http://sourceforge.net/projects/mjpg-streamer/
[h264-live-player]:https://github.com/131/h264-live-player
[uv4l-raspicam]:http://www.linux-projects.org/documentation/uv4l-raspicam/
[gstreamer]: https://gstreamer.freedesktop.org/

# 配信方法1 - mjpg-streamer
この方法は motion jpeg で配信します。いろんなところで紹介されている標準的な方法だと思います。
### インストール

```shell-session
$ 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
```

### 動画の配信

```shell-session
$ ./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 で鍵を登録します。

```shell-session
$ 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
```

その後、

```shell-session
$ sudo apt-get update
$ sudo apt-get install uv4l-webrtc uv4l-raspicam-extras
```

でインストール完了です。インストール完了後 uv4l_raspicam サービスが勝手に起動します。

### 動画の配信
本来は /etc/uv4l_raspicam/ 以下の設定を書き換えてサービスを起動すべきなのですが、説明が長くなるので端折ります。まずは、uv4l_raspicam サービスを止めます。

```shell-session
$ sudo service uv4l_raspicam stop
```

次にコマンドラインから uv4l を直接起動します。

```shell-session
$ 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 <video>タグのソースとして使う）という[豪快な方法](https://gist.github.com/shocrunch/0c038321d05586841897)を紹介している記事がいくつか見つかります。HTTPプロトコルまるっきり無視な気がするけど、ブラウザから GET 来た時だけ HTTP 喋ったりするんですかね...？よく分かりません。

しかし、ラズパイが生成できる h264 ストリームをそのままvideoタグに突っ込んで再生できるブラウザはないようです。

しかたないので以下では h264 ストリームを gstreamer を使って [HLS] 化して配信します。この方法は遅延が数十秒になるのと最近のブラウザだと Android の Chrome あたりでしか再生できないのですが、いちおう参考ということで。
[HLS]: http://k-tai.watch.impress.co.jp/docs/column/keyword/515059.html
### インストール
まずは gstreamer 本体をインストールします。

```shell-session
$ sudo apt-get install -y gstreamer1.0 gstreamer1.0-tools
```

gstreamer は raspivid コマンドの出力をそのままパイプで入力することも出来ますが、せっかくなので Raspberry Pi の標準カメラを直接入力ソースとして使える gst-rpicamsrc もインストールします。

```shell-session
$ 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 を入れます。

```shell-session
$ sudo apt-get install -y nginx
```

HLSファイルを /var/www/html/stream/ に生成するので mkdir します。

```shell-session
$ sudo mkdir /var/www/html/stream/
```

以下の内容でindex.html ファイルを /var/www/html/stream/index.html に置きます。

```html:/var/www/html/stream/index.html
<!DOCTYPE html>
<html><head></head><body>
<video src="./stream/output.m3u8" width="640" height="480"/>
</body></html>
```

### 動画の配信
次のコマンドで、カメラ画像のストリーミングを開始します。

```shell-session
$ 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](http://qiita.com/mt08/items/7cdc0bb0e08c4efb6a1f) という配信方法もあるようです。こちらは VP8 になるので Chrome 専用（だと思う）のと、 Raspberry Pi で VP8 エンコードはちょっと重いかな...と思って今回は試しておりません。

# 配信方法4 - h264-live-player
Javascript で h264 デコードして canvas タグ上で再生するというぶっちぎりアプローチです。最初ネタだと思ったんですが、PC はおろか iPhone 7の Safari でも普通に動いてしまいました。ラズパイ側では raspivid の出力を WebSocket 経由でブラウザに垂れ流すというシンプルな構成。ちなみに h264-live-player が使っている h264 デコーダは [Broadway.js](https://github.com/mbebenita/Broadway) は Android のデコーダを emscripten でビルドしたものらしいです。

### インストール
まず、nodejs をインストールします。普通に apt-get するとむちゃくちゃ古い nodejs が入ってしまうので、[本家の記事](https://nodejs.org/en/download/package-manager/#debian-and-ubuntu-based-linux-distributions)に従って6系をインストールします。

```shell-session
$ curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash -
$ sudo apt-get install -y nodejs
```

次に h264-live-player をインストール

```shell-session
$ git clone https://github.com/131/h264-live-player
$ cd h264-live-player
$ npm install
```

### 動画の配信
以下を実行します。内部的には raspivid とウェブサーバーが起動してブラウザからの接続を待つ状態になります。

```shell-session
$ 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()` をコメントアウトすると抑えられます。

```javascript
78:    socket.on('close', function() {
79:      self.readStream.end();
80:      console.log('stopping client interval');
81:    });
```

# おわりに
低解像度なら mjpeg 形式の配信であまり困らないと思います。h264 はビットレートが安定するので突然のラグは少ない反面、ブロックノイズは目立つことがあります。

再生遅延については画質と解像度によって mjpeg と h264 どちらが良いかは一概に言えない印象なので、色々設定を変えながら両方とも試すのが良いかなと思います。