UnityでExoPlayerを使ってアンビソニック音声ファイルを再生する

UnityでExoPlayerを使ってアンビソニック音声ファイルを再生したかったので、作ってみました。

## Androidライブラリを作成

まずAndroid StudioでAndroidライブラリのプロジェクトを作り、以下のようなクラスを作成します。このクラスはExoPlayerのインスタンスを作ったりURIの読み込みをしたりするメソッドを提供します。

```java
import android.content.Context;
import android.net.Uri;

import com.google.android.exoplayer2.DefaultRenderersFactory;
import com.google.android.exoplayer2.ExoPlayerFactory;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.audio.AudioProcessor;
import com.google.android.exoplayer2.ext.gvr.GvrAudioProcessor;
import com.google.android.exoplayer2.source.ExtractorMediaSource;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import com.google.android.exoplayer2.util.Util;

public class UnityExoPlayer {

    private final GvrAudioProcessor audioProcessor = new GvrAudioProcessor();
    private final SimpleExoPlayer player;
    private final ExtractorMediaSource.Factory factory;

    public UnityExoPlayer(Context context) {

        final DefaultRenderersFactory renderersFactory = new DefaultRenderersFactory(context) {
            @Override
            protected AudioProcessor[] buildAudioProcessors() {
                return new AudioProcessor[]{audioProcessor};
            }
        };

        player = ExoPlayerFactory.newSimpleInstance(renderersFactory, new DefaultTrackSelector());
        factory = new ExtractorMediaSource.Factory(new DefaultDataSourceFactory(context, Util.getUserAgent(context, "UnityExoPlayer")));
    }

    public void prepare(String uri) {
        ExtractorMediaSource mediaSource = factory.createMediaSource(Uri.parse(uri));
        player.prepare(mediaSource);
    }

    public void play() {
        player.setPlayWhenReady(true);
    }

    public void pause() {
        player.setPlayWhenReady(false);
    }

    public void stop() {
        player.stop();
    }

    public void release() {
        player.release();
    }

    public void updateOrientation(float w, float x, float y, float z) {
        audioProcessor.updateOrientation(w, x, y, z);
    }

}
```

このコードをコンパイルするためには以下のような`build.gradle`のdependenciesが必要です。

```groovy
dependencies {
    implementation 'com.google.android.exoplayer:exoplayer-core:2.8.1'
    implementation 'com.google.vr:sdk-audio:1.150.0'
}
```

そしたらGradleの`assemble`ビルドを動かしてaarファイルを作ります。

![キャプチャ.PNG](https://qiita-image-store.s3.amazonaws.com/0/56668/1fac0966-88bc-229e-d84b-f21d642f1700.png)

## Unityプロジェクトへaarファイルを入れる

↑で作ったaarには、ExoPlayerとGVR Audioのクラスは含まれていないため、別途aarファイルを持ってきてUnityプロジェクトに入れます。aarは以下のリンク先からダウンロードできます。2018/06/15時点の最新版はそれぞれExoPlayerが2.8.1、GVR Audioは1.150.0です。

* [ExoPlayer](https://bintray.com/google/exoplayer/exoplayer-core#files/com/google/android/exoplayer/exoplayer-core/2.8.1)
* [GVR Audio](https://github.com/googlevr/gvr-android-sdk/tree/master/libraries)

## Unity側のクラスを作成

次に、↑のJavaクラスをC#から呼び出すためのラッパーを書きます。

```csharp
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class UnityExoPlayer
{

  private AndroidJavaObject javaInstance;

  public UnityExoPlayer()
  {
    using (AndroidJavaClass javaUnityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
    {
      using (var currentActivity = javaUnityPlayer.GetStatic<AndroidJavaObject>("currentActivity"))
      {
        //                                   ↓ここのクラス名はパッケージ名に合わせて書き換える
        javaInstance = new AndroidJavaObject("com.niusounds.unityexoplayer.UnityExoPlayer", currentActivity);
      }
    }
  }

  public void Prepare(string uri)
  {
    javaInstance.Call("prepare", uri);
  }

  public void Play()
  {
    javaInstance.Call("play");
  }

  public void Pause()
  {
    javaInstance.Call("pause");
  }

  public void Stop()
  {
    javaInstance.Call("stop");
  }

  public void SetTexture(int texPtr)
  {
    javaInstance.Call("setTexture", texPtr);
  }

  public void Release()
  {
    javaInstance.Call("release");
  }

  public void UpdateOrientation(float w, float x, float y, float z)
  {
    javaInstance.Call("updateOrientation", w, x, y, z);
  }

}
```

## 使う

ここまでできたら、`UnityExoPlayer`クラスを使うことでExoPlayerを使った空間音響の再生ができるようになります。

```csharp
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ExoPlayerComponent : MonoBehaviour
{
  private UnityExoPlayer player;

  void Awake()
  {
    player = new UnityExoPlayer();
  }

  void Start()
  {
    player.Prepare("/path/to/audio");
    player.Play();
  }

  void Update()
  {
    var orientation = Quaternion.Inverse(Camera.main.transform.rotation);
    player.UpdateOrientation(orientation.w, orientation.x, orientation.y, orientation.z);
  }
}
```

## 動画対応について

`player.Prepare`に動画のURIを渡せば、アンビソニック音声付き動画の音声だけは再生できます。あとはUnityのテクスチャをどうにかしてExoPlayerに渡してUnityシーン中の球の表面に映像を出せればアンビソニック付き360動画の完全なプレイヤーが作れるのですが…どなたか挑戦した方がいたら教えてください。