Raspberry Piで魚眼カメラの映像をVRでライブストリーミングしてみた

Raspberry Pi4を使用して、Picam360の魚眼映像をVR映像としてライブストリーミングしてみました。今回はその手順をご紹介します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2515859/aec86a00-0511-47b1-bf0e-a80102df4866.png)

# 実際の映像
Raspberry Piからスマホにリアルタイムストリーミングして、画面をキャプチャしたものです。（キャリブレーションをちゃんとやっていないので若干歪んでます）

どうでしょうか？ビューワーも映っている状態で撮影したので実際の使用感は伝わるんじゃないかと思います。

今回のストリーミングのビットレート設定は1.5MBpsでした。Raspberry PiはJetson Nanoに比べて性能が劣るので解像度、フレームレートが低めですが（なのでJetson Nanoを使う方がおすすめです）、用途によっては使えると思います。
<iframe width="735" height="500" src="https://www.youtube.com/embed/yEKlLQ_0kso" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

# 準備するもの
- Raspberry Pi
この記事ではRaspberry Pi4を使用しています。Raspberry Pi3でも動くと思いますが未確認です。

- Picam360
PICAM360-4KHDR, PICAM360-CAMPT8MP, PICAM360-CAMTWDRのいずれでもOK。Raspberry Piの処理性能故に入力画素数が落ちるが、実際に見ると画質的にはやはりPICAM360-4KHDRが綺麗です。

# Raspberry PiにPicam360ソフトウェアをインストールする
基本的なセットアップの流れは[「Jetson NanoでPicam360を始めよう」](https://blog.picam360.com/startpicam360withjetsonnano-1_jp/)の手順と同じです。同じ部分はそちらの記事を参照する形で記載します。

ターミナルを使って必要なソフトウェアをインストールしていきます。コマンドを実行したら正常に完了しているか表示されているログを見て確認しましょう。エラーが出ている場合はエラーメッセージをウェブ検索すると多くの場合解決方法を見つけることができます。

まずPicam360サーバーサイドソフトウェア(PServer)に必要なソフトウェアをインストールします。

```
$ sudo apt-get install cmake
$ sudo apt-get install libtbb-dev
$ sudo apt-get install gstreamer1.0-tools
$ curl --location git.io/nodebrew | perl - setup
```

上記の最後にインストールしたnodebrewにパスを通します。nanoエディタで.bashrcを開きます。

```
$ cd ~
$ nano .bashrc
```

ファイルの末尾に下記をペーストします。

```
export PATH=$HOME/.nodebrew/current/bin:$PATH
```

__ctrl+o__, __ctrl+x__でファイルを保存して閉じます。

.bashrcを再読み込み。

```
$ source ~/.bashrc
```

nodeとnpmをインストール。

```
$ nodebrew install v14.17.1
$ nodebrew use v14.17.1
```

PServerをインストール。

```
$ npm install -g node-pserver@0.1.5
```

以上でraspberry pi上でのソフトウェアのインストールは完了です。

# 設定ファイルの設置
ソフトウェアで使用する設定ファイルを配置します。

まず設定ファイルを設置するフォルダを作成します。

```
$ cd ~
$ mkdir pserver
```

以下は設定ファイルの内容です。

```json-doc
{
"wrtc_enabled" : false,
"wrtc_key" : "your_key",
"stream_def" : "default",
"stream_defs" : {
# "default" : "pipe name=capture t=I420 s=1600x1200 ! pgl_remapper name=remapper deg_offset=\"-90,0,0\" w=512 h=512 ! pipe name=encoder t=H264"
# "default" : "pipe name=capture t=I420 s=1280x960 ! pgl_remapper name=remapper deg_offset=\"-90,0,0\" w=512 h=512 ! pipe name=encoder t=H264"
"default" : "pipe name=capture t=I420 s=2048x1536 ! pgl_remapper name=remapper deg_offset=\"-90,0,0\" w=512 h=512 ! pipe name=encoder t=H264"
# "default" : "pipe name=capture t=I420 s=2048x2048 ! pgl_remapper name=remapper deg_offset=\"-90,0,0\" w=512 h=512 ! pipe name=encoder t=H264"
# "default" : "pipe name=capture t=I420 s=640x480 ! pgl_remapper name=remapper deg_offset=\"-90,0,0\" w=512 h=512 ! pgl_renderer name=renderer"
},
"stream_params" : {
"default" : {
"capture.meta" : "<meta maptype=\"FISH\" lens_params=\"file:///home/pi/pserver/lens_params.json\" />",
# "capture.def" : "raspivid -w @OWIDTH@ -h @OHEIGHT@ -fps 30 -t 0 -n -r -",
# "capture.def" : "gst-launch-1.0 -q v4l2src device=/dev/video0 ! image/jpeg,width=@OWIDTH@,height=@OHEIGHT@,framerate=15/1 ! v4l2jpegdec ! fdsink",
# "capture.def" : "ffmpeg -f video4linux2 -s @OWIDTH@x@OHEIGHT@ -r 15 -i \"/dev/video0\" -f rawvideo -pix_fmt yuv420p -",
"capture.def" : "ffmpeg -f video4linux2 -s @OWIDTH@x@OHEIGHT@ -r 15 -i \"/dev/video0\" -pix_fmt yuv420p -f rawvideo -",
# "encoder.def" : "gst-launch-1.0 -q fdsrc ! rawvideoparse width=@IWIDTH@ height=@IHEIGHT@ framerate=1 format=rgba ! videoconvert ! video/x-raw,format=I420 ! v4l2h264enc ! fdsink"
"encoder.def" : "gst-launch-1.0 -q fdsrc ! rawvideoparse width=@IWIDTH@ height=@IHEIGHT@ framerate=1 format=rgba ! videoconvert ! video/x-raw,format=I420 ! omxh264enc target-bitrate=1500000 control-rate=variable ! fdsink"
# "encoder.def" : "ffmpeg -loglevel quiet -r 15 -f rawvideo -s @IWIDTH@x@IHEIGHT@ -pix_fmt rgb0 -i - -pix_fmt yuv420p -f rawvideo -c:v h264_omx -g 1 -"
# "encoder.def" : "ffmpeg -f rawvideo -s @IWIDTH@x@IHEIGHT@ -pix_fmt rgba -i - -pix_fmt yuv420p -f rawvideo -c:v libx264 -"
}
},
"debug" : 0,
"license" : {"app_key" : "none"}
}
```

nanoエディタでファイルを作成し上記内容をコピー＆ペーストします。

```
$ cd ~/pserver
$ nano confing.json
```

__ctrl+o__, __ctrl+x__でファイルを保存して閉じます。

# インターネット経由で接続する場合：
上記で作成したconfig.jsonを開いて、WebRTCを有効にするフラグをONにします。

```
# 入力例
"wrtc_enabled" : true,
"wrtc_key" : "0dcad23c27a5407db4eacfb5dfb4a676"
```

__wrtc_enable__を__true__に変更します。
__wrtc_key__に任意のキーを入力します。WebRTCを有効にした場合、このキーによってカメラに接続できます。キーを知っていれば接続できるため、他人と重複しない十分に長いキーを指定してください。wrtc_enableがtrueでもローカルネットワーク経由での接続は可能ですが、インターネット経由で接続する使い方をしない場合はfalseにしておきましょう。

# キャリブレーション
キャリブレーション機能にバグがあり現在修正中です。暫定的にキャリブレーションファイルを設置すれば利用できるので、その手順を実施します。キャリブレーション機能の修正が完了次第この記事も更新します。

__暫定的にキャリブレーションファイルを設置：__

キャリブレーションファイルの中身。

```json-doc
{"k":[0.050062,0.023326,-0.019822,0.002563],"f":[0.190850,0.255342],"c":[0.485990,0.557938],"maxfov":220}
```

nanoエディタでファイルを作成し上記内容をコピー＆ペーストします。

```
$ cd ~/pserver
$ nano lens_params.json
```

__ctrl+o__, __ctrl+x__でファイルを保存して閉じます。

設定ファイル内にある__lens_params__ファイルのパスをフルパスに書き換える必要があるので、以下をコマンドで現在のディレクトリのパスを調べます。

```
$ pwd
```

ファイルを開いて__stream_params__内の__capture.meta__要素の__lens_params__をフルパスに変更します。

```
nano config.json # 設定ファイルを開く

# 変更前:
lens_params=\”file://lens_params.json\”

# 変更後:
lens_params=\”file:///home/pi/pserver/lens_params.json\”
```

設定ファイルの管理ディレクトの場所やキャリブレーションファイルの名称を変更している場合は、実際の物に合わせてください。

変更したら保存して閉じます。

これで__~/pserver__ディレクトリには__config.json__と__lens_params.json__の2つがあるはずです。

```
$ ls ~/pserver
```

# サーバーソフトウェアの起動
PServerを起動します。

__モニターを接続している場合：__

```
$ pserver -c ~/pserver/config.json
```

__Headless（モニター無し）の場合:__

Xvfbをインストールします。これは1度で大丈夫です。

```
$ sudo apt-get install xvfb
```

ターミナルを2つ開きます。

一方でxvfbを起動します。

```
$ Xvfb :1 -screen 0 1024x768x24
```

もう一方でPServerを起動します。

```
$ export DISPLAY=:1 && pserver -c ~/pserver/config.json
```

以上でサーバーソフトウェアが起動し、ビュワーから接続できる状態になります。

PServerを終了する場合は、ターミナルで__ctrl+c__を押します。

# Let’s リアルタイムストリーミング
ビューワーアプリのインストールと接続は[「Jetson NanoでPicam360を始めよう – 5: ビューワーアプリのインストール」](https://blog.picam360.com/startpicam360withjetsonnano-5_jp/)と[「Jetson NanoでPicam360を始めよう – 6: 接続する」](https://blog.picam360.com/startpicam360withjetsonnano-6_jp/)の手順と同じです。これでリアルタイムストリーミングできます！