DeviceConnectでTHETAのリアルタイムビューワーを作る

DeviceConnectのTHETAプラグインには、リアルタイムプレビューの機能が備わっています。THETAの映像をリアルタイムに読み込んで、画像として表示できます（via [メディアストリーミング録画API - DeviceConnect Users](http://deviceconnectusers.github.io/webapi/mediastream_recording/)のカメラの映像をプレビューする）。データは懐かし（？）のmultipart/x-mixed-replaceで送られてくる形です。

すごく簡単に言うと、

1. プレビューAPIで画像データを取り込む
1. 全天球の画像データを見やすい形に加工（Canvasに貼り付け）
1. スマートフォンの加速度センサーを使って上下、左右の動きに追従させる

でできあがりです。

## 利用したライブラリ

- [Device Connect JavaScript SDK](https://raw.githubusercontent.com/DeviceConnect/DeviceConnect-JS/master/dConnectSDKForJavascript/dconnectsdk-2.0.0.js)
- [ThView.js - JavaScript Spherical Image Viewer](http://aikelab.net/thview/)

## DeviceConnectManagerとTHETAプラグインのインストール

今回はAndroid向けに作ってみたので、[Device Web API Manager - Android Apps on Google Play](https://play.google.com/store/apps/details?id=org.deviceconnect.android.manager) と [THETAプラグイン](https://github.com/DeviceConnectUsers/deviceconnectusers.github.io/releases/download/0.1/dConnectDeviceTheta.apk)をインストールします。THETAプラグインは野良なので、Androidのセキュリティ設定を変更するか、自分でビルドしてください。

## DeviceConnectの接続まで

基本的なコードは[DeviceConnectアプリケーションの作り方 - DeviceConnect Users](http://deviceconnectusers.github.io/javascript/basic/)でできます。

## URLを取得した後の処理を書く

今回の処理は全体として次のようになります。DeviceConnectのセットアップが完了後、thetaImageUrlを実行します。その結果、プレビューのURLが送られてきますので、それをThViewに適用して表示します。

```
$(function() {
  // DeviceConnectの生存確認
  var dc = new DeviceConnect({
    applicationName: "thetaViewer"
  });
  dc.setup()
    .then(function() {
      return dc.thetaImageUrl();
    })
    .then(function(url) {
      var img1 = new ThView({
        id:'image1',
        file: url,
        rendererType: 1,
        width: 980,
        height: 560
      });
    }, function(error) {
      alert(error)
      alert("DeviceConnectが起動していません");
    })
});
```

## THETAのプレビュー画像を取得する

dc.thetaImageUrl()の内容です。ここはjQueryを使いました。全体の処理は次のようになります。 `me.getMediaRecorder` が成功するとカメラのIDが返ってきますので、 `me.getPreviewUrl` を実行して、プレビューできるURLを受け取ります。

```
p.thetaImageUrl = function() {
  me = this;
  var p = new Promise(function(resolve, reject) {
    var id = me.findService("theta");
    if (typeof id == 'undefined') {
      return reject({
        errorCode: 99,
        errorMessage: 'THETA is not found.'
      });
    }
    me.getMediaRecorder(id)
    .then(function(result) {
      var recorder_id = result.recorders[0].id
      return me.getPreviewUrl(id, recorder_id);
    })
    .then(function(result) {
      return resolve(result.uri);
    })
    .fail(function(error) {
      return reject(error);
    })
  });
  return p;
}
```

`me.findService` は次の通りです。指定した文字で始まっているサービスのIDを取得するだけです。開発中はthetaではなくhostにしておく方が良いです。

```
p.findService = function(name) {
  for (i in this.services) {
    if (this.services[i].id.toLowerCase().indexOf(name.toLowerCase()) == 0) {
      return this.services[i].id;
    }
  }
}
```

### me.getMediaRecorderについて

では `me.getMediaRecorder` についてです。カメラの一覧を取得するWeb APIを叩きます。

```
p.getMediaRecorder = function(id) {
  me = this;
  return $.ajax({
    url: me.host + "/gotapi/mediastream_recording/mediarecorder",
    type: "GET",
    data: {
      serviceId: id,
      accessToken: me.accessToken
    }
  });
};
```

### me.getPreviewUrl について

サービスIDとカメラIDでプレビューURLを取得するWeb APIを叩きます。

```
p.getPreviewUrl = function(id, recorder_id) {
  me = this;
  return $.ajax({
    url: me.host + "/gotapi/mediastream_recording/preview",
    type: "PUT",
    data: {
      serviceId: id,
      accessToken: me.accessToken,
      target: recorder_id
    }
  });
};
```

これで、例えば `http://localhost:9001/1` といったURLが返ってきます。

### ThView.jsの修正点

デフォルトのThView.jsでは幾つかの問題があったので、修正しました。

- multipart/x-mixed-replaceに対応していないので画像が更新されない
- なぜか上下逆さまに…
- 画像を上下逆にすると、左右上下の動きまで逆に…

これはThView.js側ではなく、適用する画像に起因するものだと思われます。

修正点は以下の通りです。

-90回転した際に、

```
// 元
this.mesh.rotation.y = this.degree[1] + alpha - Math.PI / 2;
this.camera.rotation.z = -beta + Math.PI;
// 修正後
this.mesh.rotation.y = this.degree[1] + alpha + Math.PI / 2;
this.camera.rotation.z = -beta;
```

レンダリング実行前に、

```
self.texture[0].needsUpdate = true;  // これを追加
renderer.render(scene, self.camera);
```

としました。

### ファイルの転送

できあがったアプリをデバイスに送る際にはadbを使います。

```
$ adb push path_to_dir/theta_viewer /sdcard/data/data
```

後はAndroidのChromeブラウザでアップしたファイルのindex.htmlを開きます。

![Screenshot_ 2016-08-29 12.22.20.png](https://qiita-image-store.s3.amazonaws.com/0/135927/87e88b8a-96ee-077d-8b0c-f1e7367e1388.png)

実行した動画は以下です。結構画質は粗いです。

https://www.youtube.com/watch?v=7oPKP1bZHSU

----

ソースコードは[DeviceConnectUsers/theta_viewer](https://github.com/DeviceConnectUsers/theta_viewer)にアップしてあります。AndroidとTHETA m15またはTHETA Sがあれば、JavaScriptだけでリアルタイムビューワーが作れますのでぜひやってみてください。