FBCAPTURE SDKでUnityの360度キャプチャをライブストリーミングする

このエントリーは[CyberAgent 19新卒 エンジニア Advent Calendar 2018](https://adventar.org/calendars/3342)の19日目の記事です。

## はじめに

**みなさん、VR体験したことありますか？**
私は数か月前にOculus Riftを購入して、いくつかVRのゲームなどで遊んだりしています。
左右の視力が違うせいで少しぼやけたり、HDMIのケーブルを延長すると少し映像が不安定になるのに困っていたりしますが、非常に充実したVRライフを送っています。

VR元年と呼ばれた2016年から二年経ち、2018年も終わろうとしていますが、それでも**技術に関心があまりない人**のHMD所有者はあまり増えていないように感じます。

その原因の最たるものとして、**コンテンツ不足**が挙げられると私は考えています。
今回は「**VRなどの技術に興味を持ってもらう**ための切り口として、**360度映像のライブストリーミング**」をFacebookから公開されているライブラリを使って試してみたいと思います。

普段はWeb回りの技術に触れていて初めてUnityを触ったので、至らない点などありましたらコメントしていただけると幸いです。

## 360度映像とは

いわゆる**全天球映像**というものです。PCであればドラッグなどで、スマートフォンであればジャイロセンサーやタッチ操作などでユーザーが見る方向を変えることが出来る映像です。
VRではないですが、スマートフォン単体で利用できるということもあり、こういったことからVR等の技術に興味を持ってもらえるかもしれません。

360度映像の形式はいくつかありますが、今回利用するライブラリではエクイレクタングラー形式とキューブマップ形式が選択できます。

### エクイレクタングラー形式

エクイレクタングラー形式は360度映像で最も広く使われている形式で、水平方向の中心線から上下に行くほど歪んでいきます。
この映像をプレイヤー側で球体に加工して、全天球映像となります。
YouTube LiveやFacebook Live、Periscopeなど、360度配信を対応しているプラットフォームのほとんどはこの形式で実装されています。

![equirectangular.png](https://qiita-image-store.s3.amazonaws.com/0/262177/f749abd5-2756-fe5b-fdf5-48887e4345ab.png)
© Unity Technologies Japan/UCL

### キューブマップ形式

キューブマップ方式は、空間を上下前後左右の6面の立方体に見立てそれぞれの面を組み合わせた形式です。
それぞれの映像に歪みがないため、エクイレクタングラー形式に比べて素早くエンコードすることができます。
Facebook Liveがキューブマップ形式でのライブストリーミングに対応しています。
![cubemap.png](https://qiita-image-store.s3.amazonaws.com/0/262177/159b2601-d5a1-89d0-1f92-a8d4df5c7b6e.png)
© Unity Technologies Japan/UCL

## 360度映像のライブストリーミング

Facebookの[FBCAPTURE SDK](https://github.com/facebook/360-Capture-SDK)がRTMPをサポートしているので、これを使用して360度のライブストリーミングを試してみようと思います。

## 環境

* Windows 10 Pro 64bit
  * i7-6700K @4.00GHz
  * GeForce GTX1060 6GB
* Unity 2018.2.11f1(64bit)

## とりあえず動かす

サンプルが用意されているのでひとまずそれで動作を確認してみます。

[Releases · facebook/360-Capture-SDK](https://github.com/facebook/360-Capture-SDK/releases)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/262177/d1712ec5-277d-06b3-2d63-2086c08791d9.png)

`FB Capture SDK 2.25 Release` の `UnitySample.zip` をダウンロードし、任意のディレクトリに解凍してUnityで開きます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/262177/82b24b3f-6700-0811-7f5c-2f2cfbce11c0.png)
HierarchyからFBCaptureインスタンスを選択して、Inspectorを表示します。

始めにカメラ位置を調節します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/262177/025212e7-027e-94f4-a0ee-30ee4ef402bd.png)
そのままでも問題はありませんが、キャプチャされる動画の正面とカメラの正面が90度ずれている(カメラの正面が映像の右端になるようにキャプチャされる)ようなので、キューブを正面に映すために右を向かせます。
※映像の正面を右端から1/4の部分とするかそれとも映像の中央にするかはプレイヤー側(プラットフォーム側)の仕様により変わります。今回はYouTube Liveが映像の中央を正面としていたので、それに合わせました。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/262177/6cfaed08-05e5-3ba8-5f39-34b1e36e63d7.png)

次にキャプチャの設定を行います。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/262177/8f5dfc14-f1da-eee2-f5aa-7fcd5afe0381.png)
**Capture Mode**を**360_CAPTURE**に、
**Capture Texture Format**を**RGB_CAPTURE**に、
**Projection Type**を**EQUIRECT**に、
**Video Capture Type**を**LIVE**にします。

Capture Hotkeysはお好みで変えます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/262177/741512b0-843b-17e8-c179-5934bf36a572.png)
Start Encodingのキー押下で、Video Capture Typeで選択された動作が行われます。
LIVEであれば指定URLへのRTMP配信、VODであれば指定ファイルとしての保存です。

Live Streamingの設定をします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/262177/de1d9a1a-c3b8-79ba-1922-1d966cf8a4ef.png)
YouTube Liveでの配信を試してみますので、解像度は2160p、fpsは30fps、ビットレートは15000kbpsとしました。クリエイターツールからストリーミングURLとストリームキーを取得して、連結したものを**Live Stream URL**にしています。

Playボタンから実行して、指定したホットキーをゲームウィンドウ上で押してキャプチャができます
**Start Screen Shot(F1)**でスクリーンショットのキャプチャ、
**Start Encoding(F2)**でRTMP配信または動画のキャプチャ、
**Stop Encoding(F3)**でRTMP配信または動画のキャプチャの終了が行われます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/262177/77a858b7-4d9d-ee38-a216-22fa75aba15c.png)

YouTube側から配信を開始すれば、360度映像のライブストリーミングができます。

テスト配信のアーカイブ([YouTube](https://www.youtube.com/watch?v=Dy6yw-I2dls))
[![image.gif](https://qiita-image-store.s3.amazonaws.com/0/262177/6b4237d8-ba05-95db-258e-4927eb0f8049.gif)
](https://www.youtube.com/watch?v=Dy6yw-I2dls)

凄いですね、簡単に360度のライブストリーミングが出来ました。
FBCAPTURE SDKは**エクイレクタングラー形式**だけでなく、**キューブマップ形式**にも対応しています。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/262177/f4192b99-cfff-4a65-a1df-9d42497f51e2.png)
Facebook Liveがキューブマップ形式に対応しているので、Live Stream URLを変えれば試せます。

## 他のプロジェクトに組み込む

サンプルだけでおなか一杯ですが、Unityの勉強も兼ねて何か作ってみます。
[Beat Saber](http://beatsaber.com/)や[Airtone](http://airtone-vr.com/)といった、プレイヤーの位置が基本的に変わらないようなゲームに近い形態のものを作成しました(見た目のみ)。

![image.gif](https://qiita-image-store.s3.amazonaws.com/0/262177/dd4b5c8e-7896-ec9f-3ed3-f5e029982166.gif)

既存のプロジェクトでFBCAPTURE SDKを利用するには、unitypackageをインポートします。


[Releases · facebook/360-Capture-SDK](https://github.com/facebook/360-Capture-SDK/releases)から**Source code(zip)**をダウンロードして解凍し、**FBCaptureSDK.unitypackage**をインポートします。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/262177/7fd573b7-5c2e-7f29-038e-0bf9e1aaa403.png)

インポート後、**Assets/FBCAPTURE**内にある**FBCapture.prefab**をHierarchyにドラッグして配信用カメラを配置します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/262177/11b0b94c-1b04-11b7-a73b-09d878f19963.png)

今回のように、基本的にプレイヤーが動かないような場合はカメラを固定すると、3D酔いも回避でき、視聴する側が視点を変えて見やすくなると思います。
また、例えばバーチャル空間内に複数の対象がいるような状態(VTuberのコラボ配信で円卓に座る等)で中央に固定カメラを設置して、視聴する側が好きな対象の方を向く、といった形も面白いかもしれません。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/262177/6e43d6fd-0ae8-b9bf-0de4-1604c0b2a3fc.png)

テスト配信のアーカイブ([YouTube](https://www.youtube.com/watch?v=UPxVNP7aJH0))
[![image.gif](https://qiita-image-store.s3.amazonaws.com/0/262177/8cd98526-5e66-053f-2604-4250b99424b4.gif)
](https://www.youtube.com/watch?v=UPxVNP7aJH0)

反対に、正面が決まっておらずプレイヤーが縦横無尽に動けるような場合は、カメラをヘッドトラッキングに追従させないと視聴する側が都度視点を移動させる必要が出てくるため、**CenterEyeAnchor**下等に配置すると良いと思います。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/262177/975e0b58-9228-cc82-553a-dde2c5e094f8.png)
しかし、画面の揺れから3D酔いも発生しやすくなりますし、視聴側の意図していないタイミングで視点が動くため、**正面の方向だけ合わせる**等の工夫をしないと、まだまだ快適に視聴できるというレベルではないかもしれません。

## おまけ

[nginx-rtmp-module](https://github.com/arut/nginx-rtmp-module)と[videojs-vr](https://github.com/videojs/videojs-vr)でストリーミング配信部分をリポジトリに上げました。

[toro-ponz/360-streaming - GitHub](https://github.com/toro-ponz/360-streaming)

```powershell
PS > docker-compose build
PS > docker-compose up
```
でコンテナを立ち上げて、配信先を`rtmp://localhost/live/test`に設定して配信開始します。

http://localhost/live/test.html にアクセスして再生ボタンを押せば360度配信が確認できます。

videojs-vrはキューブマップ方式も対応しているので、下記のように書き換えれば試せます。

```diff:public/live/test.html
  <script>
    var player = videojs('player');
-   player.vr({projection: '360'});
+   player.vr({projection: 'CUBE'});
  </script>
```

## まとめ

* FBCAPTURE SDK、インポートするだけでライブ配信出来て便利
* 360度ライブ配信は正面が決まっているようなVRコンテンツと親和性が高い
* 360度映像はVR体験の共有という点で、視聴する側からするとライトに行える

## 参考

* [Facebook、キューブマッピングを使い360コンテンツ作成をより簡単にできる「360 Capture SDK」を発表！ | VR Inside](https://vrinside.jp/news/facebook-360-capture-sdk/)
* [360度映像制作ガイド | ORIHALCON Technologies, Inc.](https://www.orihalcon.co.jp/technologies/documents/how_to_make_immersive_movies.html)
* [1. Unityの画面構成 - Unity＋Oculus＋MMD　とりあえず書き留めておく場所](http://bvillage.hatenablog.com/entry/2017/11/16/171704)
* [Unity＋Oculus Rift開発メモ - フレームシンセシス](https://framesynthesis.jp/tech/unity/oculusrift/)
* [OculusRiftのヘッドセット画面とPC画面を別のカメラ視点で表示をする方法](https://materializer.co/lab/blog/122)
* [フロントエンドエンジニアのための生放送と RTMP 通信基礎 | ygoto3.com](https://ygoto3.com/posts/live-streaming-and-rtmp-for-frontend-engineers/)