RICOH THETA V の動画ファイルサイズを極限まで削ってみる

# この記事について
リコーが販売している全天球カメラ [THETA](https://theta360.com/ja/) の Plugin のサンプルをいじり、録画した動画のファイルサイズが削減できないか試してみます。

# THETA
## THETA とは？
リコーが販売している全天球カメラです。
上位モデル(V/Z1)は、内部のOSとして Android が試用されており、Androidアプリ(THETA用語では"Plugin")を開発すれば THETA を自由に操作できるという、素敵なものです。
なお、下位モデル(S/SC/SC2)でも、Pluginほどの自由度はないですが、内部で動作している REST API を使用すればそれなりの操作はできるようです（試していない）。

詳細は以下をご参照ください。

- THETA の製品情報
 - https://theta360.com/ja/
- THETA の開発の情報
 - https://api.ricoh/products/theta-jp/
 - https://qiita.com/organizations/theta-plugin

## THETA の困りごと
THETAを使ってみて悪い意味で驚いたことは、録画した動画ファイルのサイズが巨大であることでした。
撮影する内容にもよると思いますが、デフォルトの4K・高ビットレートの設定で5分の動画を撮影したところ、ファイルサイズは約1.5GBというサイズになりました。
画質はかなり良いですし、全天球(左右360度・上下360度)分の情報量がありますので、妥当といえば妥当なのかもしれません。
が、画質を絞ってもう少しファイルサイズを下げたいなー、と考え、どこまでファイルサイズを下げられるか試すことにしました。

# THETA の動画のファイルサイズを下げてみる
## やってみること
今回は [THETA V](https://theta360.com/ja/about/theta/v.html) を使用し、小さいファイルサイズで録画できる Plugin を作ってみます。
カメラ撮影の plugin のサンプルである https://github.com/ricohapi/theta-plugin-camera-api-sample をベースに、ちょっと内部をいじってみます。

## デフォルト状態でのファイルサイズを調べる
ソースコード `CameraFragment.java` を見てみると、このサンプルでは、4K画質・ビットレート56Mbps・フレームレート30fpsで、最大25分撮影していることがわかります。

```java:CameraFragment.java(変更前)
            // Sample:Set up 4K Equi videos
            mParameters.set("RIC_PROC_STITCHING", "RicStaticStitching");
            mParameters.set("RIC_SHOOTING_MODE", "RicMovieRecording4kEqui");
            CamcorderProfile camcorderProfile = CamcorderProfile.get(mCameraId, 10013);
            mParameters.set("video-size", "3840x1920");
            //（中略)
            mMediaRecorder.setVideoEncodingBitRate(56000000); // 56 Mbps
            mMediaRecorder.setVideoFrameRate(30); // 30 fps
            mMediaRecorder.setMaxDuration(1500000); // max: 25 min
```

とりあえず、1分間録画したときのファイルサイズを見るため、ちょっといじります。

```java:CameraFragment.java(変更後)
            //mMediaRecorder.setMaxDuration(1500000); // max: 25 min
            mMediaRecorder.setMaxDuration(60000);     // max:  1 min
```

これで録画したところ、1分間の動画のファイルサイズは **約450MB** ほどでした。

## ファイルサイズを削るためにあれこれやってみる
「見るに耐えうる画質」かつ「モバイル回線で送信できるレベルのファイルサイズ」を目標に、画質を調整してみます。

### 画質の調整

画質は、4K(3840x1920) から 2K(1920x960) に落とし、ビットレートは 1Mbps, フレームレートは 9fps まで落としてみます。
ビットレート・フレームレートはいくつか試してみたのですが、個人的にはこれくらいが「最下限」の値だと思います。

#### 変更後

```java:CameraFragment.java(変更後)
            mParameters.set("RIC_SHOOTING_MODE", "RicMovieRecording2kEqui");
            CamcorderProfile camcorderProfile = CamcorderProfile.get(mCameraId, 10014);
            mParameters.set("video-size", "1920x960");
            //（中略)
            mMediaRecorder.setVideoEncodingBitRate(1000000); // 1 Mbps
            mMediaRecorder.setVideoFrameRate(9);             // 9 fps
            mMediaRecorder.setMaxDuration(60000);            // max: 1 min
```

### 音声について

さらに、デフォルトでは音声が `.wav` 形式で保存され、それが動画とマージされるような挙動となっているのですが、`.wav`の音声が使われるとそれだけでかなりファイルサイズが増加するようなので、`.wav`の音声は使わないようにしてみます。なお、`.wav`を使わないでも動画に音声は保存されているようです。

#### 変更前

```java:CameraFragment.java(変更前)
            mMp4filePath = String.format("%s/plugin_%s.MP4", DCIM, dateTime);
            mWavfilePath = String.format("%s/plugin_%s.WAV", DCIM, dateTime);
            String videoWavFile = String.format("%s,%s", mMp4filePath, mWavfilePath);
            mMediaRecorder.setOutputFile(videoWavFile);
//（中略）
                mInstanceRecordMP4 = new File(mMp4filePath);
                mInstanceRecordWAV = new File(mWavfilePath);
//（中略）
                Box box = new Box();
                box.formBox(mMp4filePath, mWavfilePath, mBoxCallback);
//（中略）
                // cancel recording
                mInstanceRecordMP4.delete();
                mInstanceRecordWAV.delete();
//（中略）
    public File[] getRecordFiles() {
        File[] recordFiles = {mInstanceRecordMP4, mInstanceRecordWAV};
        return recordFiles;
    }
```

#### 変更後
変数 `mWavfilePath` `mInstanceRecordWAV` を使っている部分を片っ端から削除します。

```java:CameraFragment.java(変更後)
            mMp4filePath = String.format("%s/plugin_%s.MP4", DCIM, dateTime);
            //mWavfilePath = String.format("%s/plugin_%s.WAV", DCIM, dateTime);
            //String videoWavFile = String.format("%s,%s", mMp4filePath, mWavfilePath);
            //mMediaRecorder.setOutputFile(videoWavFile);
            mMediaRecorder.setOutputFile(mMp4filePath);
//（中略）
                mInstanceRecordMP4 = new File(mMp4filePath);
                //mInstanceRecordWAV = new File(mWavfilePath);
//（中略）
                Box box = new Box();
                //box.formBox(mMp4filePath, mWavfilePath, mBoxCallback);
                box.formBox(mMp4filePath, "", mBoxCallback);
//（中略）
                // cancel recording
                mInstanceRecordMP4.delete();
                //mInstanceRecordWAV.delete();
//（中略）
    public File[] getRecordFiles() {
        //File[] recordFiles = {mInstanceRecordMP4, mInstanceRecordWAV};
        File[] recordFiles = {mInstanceRecordMP4};
        return recordFiles;
    }
```

## 変更後の Plugin を動かす
この修正を行った Plugin で動画を録画してみたところ、1分間の動画のファイルサイズを **約8MB** まで落とすことができました。 450MB→8MBですから、だいぶファイルサイズを削減できたのではないでしょうか。
当初の動画と比べると、画質はあからさまに落ちましたし、ブロックノイズも増えました。一言でいうと、光回線の画質がLTE回線の画質に落ちた、といった感じでしょうか。が、何が映っているか判断できるレベルは保っていると思います。

### 修正後のソースコード
githubに置いておきました。
https://github.com/nara256/theta-plugin-camera-api-sample/tree/low_resolution

# まとめ（ではないけど...）
THETA は楽しいねー。
plugin でアレコレ操作してみると多くの可能性を感じます。
これからもっと遊んでみる予定です。

# 最後に
theta SDK を細部まで確認しないままテキトーに実装していますので、なにかお気づきな点や、変なことをやっているようなことがあればご指摘いただけると幸いです。