Android
VR

Resonance Audio SDKとExoPlayerで再生可能な3rd order Ambisonicsの動画を作成する方法

GoogleのResonance Audio SDKExoPlayerを使うと動画に埋め込まれたアンビソニック音声を再生できます。Resonance Audio SDKは3rd orderまでのアンビソニックのデコードに対応しているので、これを使えばGear VRで3rd order Ambisonicsの360度動画が再生できるぞ!と思ったのでやってみたらいろいろ罠がありました。

ハマったポイント

  • AACは規格上48chまで対応しているが、エンコーダーが8chまでしか対応していない (参考:AAC encoders)
  • Androidは5.1chまでのAACしかサポートしていない (参考: Supported Media Formats)

上記の理由により、MP4(音声コーデックAAC)を使用することができません。

Androidでサポートされているその他の音声コーデックはFLAC, MP3, Opus, Vorbis, PCM(非圧縮)がありますが、Supported Media FormatsによればFLAC, MP3はステレオまでしかサポートしていません。PCMは非圧縮データでありファイルサイズが大きくなりすぎるのであまり現実的ではありません(1秒間で約1.3MB)。
最終的に残る候補はOpus, Vorbisの二つです。ただしOpusのFFmpegサポートはまだ実験段階らしいので、今回はVorbisを使うことにしました。

Vorbisを音声に使用できるコンテナフォーマットはMKVとWebMがありますが、今回MKVで試してみたらうまくいきました。映像コーデックはH264です。
ちなみにReaperから16ch音声のWebMを書き出そうとしたらうまく行きませんでした。

ただし、なかなか一発でこの形式に書き出せる映像編集ソフトはないと思うので(REAPERではできなかった)、まず映像と音声を別々に書き出した後でFFmpegで結合するという方法をとりました。以下のようにします。

  • 16ch音声をVorbisで書き出す。
  • ↑の音に合う映像をMP4(H264)で書き出す(音声は付いていても付いていなくても良い)。

上記二つのファイルを用意した後、以下のコマンドで映像と音声を結合します。例として映像ファイルはvideo.mp4、音声ファイルはaudio.oggだとします。

ffmpeg -i video.mp4 -i audio.ogg -c:v copy -c:a copy -map 0:v:0 -map 1:a:0 output.mkv

これで映像+音声(3rd order Ambisonics)のMKVファイル output.mkv ができます!

あとはこれをAndroid端末に入れてExoPlayer + GVR Extensionで再生するだけです。細かいことは省きますが、だいたい以下のようなことをします。

ExoPlayerとResonance Audioをプロジェクトに追加

build.gradle
implementation 'com.google.android.exoplayer:exoplayer-core:r2.5.4'
implementation 'com.google.android.exoplayer:extension-gvr:r2.5.4'
implementation 'com.google.vr:sdk-audio:1.101.0'

ExoPlayerのインスタンスを作成

// フィールドに保持
val gvrAudioProcessor = GvrAudioProcessor()

fun createExoPlayer() : SimpleExoPlayer {
  val trackSelector = DefaultTrackSelector()
  val renderersFactory = object : DefaultRenderersFactory(context) {
    override fun buildAudioProcessors(): Array<AudioProcessor> = arrayOf(gvrAudioProcessor)
  }

  return ExoPlayerFactory.newSimpleInstance(renderersFactory, trackSelector)
}

// 毎フレーム呼び出す
fun update() {
  // w,x,y,z にユーザーのヘッドトラッキングのQuaternionの値を入れる
  gvrAudioProcessor.updateOrientation(w, x, y, z)
}