WebRTCでも360ライブストリーミングがしたい!

#これは何?
今まで360カメラなんて全く興味がなかったのに、Ricoh R developmentkit を5月末にポチってしまい、6月にすぐに発送されたのでWebRTCでライブストリーミングしてみました。

こちらのサイト様で後悔しているコードをmediasoupに組み込んで動いたよーという話です。
https://ics.media/entry/14490/3

#仕組み
Ricoh R development kit と macを接続しmediasoupに流し込みます。
別端末からビューワーを開くと WebRTCのvideoタグをthree.jsで球体に貼り付けます。
それだけです。theta sよりも簡単です。

#theta sとの違い
theta s のサンプルが世の中に出回っていまして、ラズパイと接続してとかできたんですが、theta sは未加工のレンズのママの映像が取得できます。
対してRicoh Rは一般的な?形式にしてくれるので球体にそのままマッピングするだけです。

#ソースコード
ほとんどこちらのソースコードそのままです。
https://ics.media/entry/14490/3

WebRTC側、pc.onaddstream(stream) などでvideoタグにsrcObjectをセットする際に使います。
以下はmediasoup-demoでのファイル名と修正箇所を示しています。

three.jsをnpm installしましょう

```bash
npm install three-js --save
```

app/lib/components/Video.jsx

```js
import * as sphere from './sphere';
...

video.srcObject = stream;
sphere.setVR(video); // これを追加
...
```

app/lib/components/sphere.js (新規作成)

```js
import THREELib from 'three-js';

// windowサイズを画面サイズに合わせる
let width, height;
let element;
let scene, camera, renderer, controls, texture;
let THREE = THREELib(['OrbitControls']);

export function setVR(target) {
    width = 1920; // target.videoWidth;
    height = 1080; // target.videoHeight;

    // シーンの作成
    scene = new THREE.Scene();

    // リサイズイベントを検知してリサイズ処理を実行
    window.addEventListener('resize', handleResize, false);

    // カメラの作成
    camera = new THREE.PerspectiveCamera(75, width / height, 1, 1000);
    camera.position.set(0, 0, 0.1);
    scene.add(camera);

    // 球体の形状を作成
    var geometry = new THREE.SphereGeometry(5, 60, 40);
    geometry.scale(-1, 1, 1);

    // テクスチャーにvideoを設定
    texture = new THREE.VideoTexture(target);
    texture.minFilter = THREE.LinearFilter;
    texture.magFilter = THREE.LinearFilter;
    texture.format = THREE.RGBFormat;

    // マテリアルの作成
    var material = new THREE.MeshBasicMaterial({
      // 画像をテクスチャとして読み込み
      map: texture
    });

    // 球体(形状)にマテリアル(質感)を貼り付けて物体を作成
    let sphere = new THREE.Mesh(geometry, material);

    // シーンに追加
    scene.add(sphere);

    // レンダラーの作成
    renderer = new THREE.WebGLRenderer({antialias: true});
    // レンダラーをwindowサイズに合わせる
    renderer.setSize(window.innerWidth, window.innerHeight);
    renderer.setClearColor({color: 0xffccff});
    element = renderer.domElement;
    target.parentNode.appendChild(element);
    renderer.render(scene, camera);

    // デバイスの判別
    var isAndroid = false;
    var isIOS = false;
    if (navigator.userAgent.indexOf('Android') != -1) {
      // デバイスがAndroidの場合
      isAndroid = true;
    } else if (/(iPad|iPhone|iPod)/g.test(navigator.userAgent)) {
      // デバイスがiOSの場合
      isIOS = true;
    }
    if (isAndroid || isIOS) {
      // デバイスがスマートフォンまたはタブレット端末の場合
      // ジャイロセンサーで視点操作を可能にする
    } else {
      // パソコンの場合
      // マウスドラッグで視点操作を可能にする
      setOrbitControls();
    }

    render();
  }

  // リサイズ処理
  function handleResize() {
    renderer.setSize(window.innerWidth, window.innerHeight);
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
  }

  function setOrbitControls() {
    // パソコン閲覧時マウスドラッグで視点操作する
    controls = new THREE.OrbitControls(camera, element);
    controls.target.set(
      camera.position.x + 0.15,
      camera.position.y,
      camera.position.z
    );
    // 視点操作のイージングをONにする
    controls.enableDamping = true;
    // 視点操作のイージングの値
    controls.dampingFactor = 0.2;
    // 視点変更の速さ
    controls.rotateSpeed = 0.1;
    // ズーム禁止
    controls.noZoom = true;
    // パン操作禁止
    controls.noPan = true;
  }

  function render() {
    requestAnimationFrame(render);
    renderer.render(scene, camera);
    controls.update();
  }
```

あとは gulp prod　すれば動きます。