Jetson Nanoでストリーミング配信を行う

# はじめに

本記事はJetson Nanoに接続したカメラ映像をストリーミング配信する試みである。自分がJetson Nanoを使うとき、多くの場合リモートで操作を行っている。したがってカメラ映像もリモートで見られると楽だなぁと考えていたので重い腰を上げて実施することにした。

# 前提条件

デバイス`/dev/video0`で映像が取得できることを前提とする。


# GstreamerとRTSP

一番最初に思いついたのは`gstreamer`と`RTSP`を使うことだ。RTSPはReal Time Streaming Protocolのことで、`gst-launch-1.0`コマンドから直接使うことはできない。そのため

https://github.com/GStreamer/gst-rtsp-server/blob/master/examples/test-launch.c

にある`test-launch.c`をダウンロードしてコンパイルする。

```bash:JetsonNanoで実行
sudo apt install libgstrtspserver-1.0-dev
gcc -o test-launch test-launch.c `pkg-config --cflags --libs gstreamer-rtsp-server-1.0`
```

そうすると`test-launch`が作成されるので、下記のコマンドで起動する[^1]。

```bash:JetsonNanoで実行
./test-launch "v4l2src device=/dev/video0 ! video/x-raw,framerate=30/1,width=640,height=480 | x264enc ! queue ! rtph264pay name=pay0 pt=96"
```

[^1]: `v4l2src`を使っているのは`nvarguscamerasrc`や`nvcamerasrc`がUSB接続のC920ではハングアップするため。

こうして立ち上げた後、別の端末からVLCを使って`rtsp://jetson-nano:8554/test`へアクセスするとカメラ映像を見ることができる。なお`jetons-nano`の部分は私のサーバ名なので、適宜ホスト名やIPアドレスに変更する必要がある。

```bash:Macで実行
open -a vlc rtsp://jetson-nano:8554/test
```

![vlc_rtsp_access.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252224/5c6d2c40-c155-5202-6a3d-9d6dbb122eb6.png)

面白い点は`VLC`でアクセスすると初めてカメラが有効になる点。切断するとカメラもOFFになり、再接続するとまたONになる。

# mjpg-streamer

次に試したのが`mjpg-stramer`。これはオープンソースのMotion JPEGをHTTPでストリーミングするタイプ。

[mjpeg-streamer+ipcam-viewでRaspberry Piのカメラの映像をAndroidへ配信する][1]
を参考にセットアップする。

[1]:https://qiita.com/xeno1991/items/e32e52c688d969d182e2

webサーバの起動のところだけ変更するため、下記のようなスクリプトを作成した。

```shell:run_mjpg-streamer.sh
#!/bin/sh

input=/usr/local/lib/mjpg-streamer/input_uvc.so
output=/usr/local/lib/mjpg-streamer/output_http.so
port=8080
device=/dev/video0
webroot=/usr/local/share/mjpg-streamer/www

mjpg_streamer -i "$input -d $device -r 640x480 -f 1" -o "$output -p $port -w $webroot"
```

これを実行したのち、`http://jetson-nano:8080/`でアクセスすると次のようなページが表示される。

![mjpg_streamer_access.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252224/d4d34c5c-2e0e-9c9b-61bf-5c6291b48f1e.png)

なるほど、これはこれで便利そうだ。ブラウザがあればアクセスできるというのは素晴らしい。

# 自前で作る

Jetson Nanoを使ってGPUで処理した映像はどうやって`mjpg-streamer`に渡せば良いのだろう?
`/dev/video0`でデバイス指定されているので、処理した映像を仮想的に作ったデバイスに紐づけるのが良さそうだ。

しかし今更CやC++で書くのがとても面倒に思えた。慣れというのは恐ろしい。できればPythonとOpenCVを使って処理したいのでPythonとのインターフェイスが欲しい。

いろいろ考えた結果、Motion JPEGをHTTPでストリーミングする部分をPythonで書けば良いんじゃなかろうかという結論に達した。作ったものは

https://github.com/yamamo-to/jetson_nano/tree/master/mjpeg_http

に置いた。なんだかんだで3時間以上掛かった。こんなことなら初めからCやC++で/dev/video周辺を作るべきだったと後悔しつつ、せっかく作ったので公開することにした。


```bash:JetsonNanoで実行
git clone https://github.com/yamamo-to/jetson_nano.git
cd jetson_nano/mjpeg_http
```

ディレクトリ`html/`にindex.htmlがあるのでこれを編集する。自分の場合は`jetson-nano`というホスト名があるので

```html
<img src="http://127.0.0.1:8080/camera.mjpg" />
```
を

```html
<img src="http://jetson-nano:8080/camera.mjpg" />
```
に変更した。最後に起動する。安全を考慮してデフォルトでは127.0.0.1でバインドするので、リモートから接続するためには`bind`オプションをつけて起動する。

```bash:JetsonNanoで実行
python3 -m mjpeg_http.server --bind 0.0.0.0
```

Google Chromeであれば`http://jetson-nano:8080/camera.mjpg`に直接アクセスできる。
![chrome_access.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252224/5c6f127c-633d-2885-a006-b9c124678d7e.png)

Safariの場合は一度index.htmlを通す必要があるため`http://jetson-nano:8080/`にアクセスする。
![safari.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252224/0a4ac4eb-b70d-3988-279b-d69a188b8e0a.png)

これでPythonとも連携可能なMJPEG Streaming環境ができた。ただし使っているモジュールは`http.server`で製品には適さないらしい。

```
Warning http.server is not recommended for production. It only implements basic security checks.
```


# まとめ

リモートでカメラ映像を映す方法として次の3種類を試した。

- GstreamerとRTSPによる配信
- mjpg-streamerによる配信
- 自作のMotion JPEG serverによる配信

今後のことを考えると/dev/video以下に仮装デバイスを実装した方が良さそうだ。