Add GVR spatial audio rendering extension.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=148658631
This commit is contained in:
andrewlewis 2017-02-27 09:42:03 -08:00 committed by Oliver Woodman
parent b3cfeaa17b
commit 1120e10273
3 changed files with 188 additions and 0 deletions

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2017 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<manifest package="com.google.android.exoplayer2.ext.gvr"/>

View File

@ -0,0 +1,170 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2.ext.gvr;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.audio.BufferProcessor;
import com.google.vr.sdk.audio.GvrAudioSurround;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
/**
* Buffer processor that uses {@code GvrAudioSurround} to provide binaural rendering of surround
* sound and ambisonic soundfields.
*/
public final class GvrBufferProcessor implements BufferProcessor {
private static final int FRAMES_PER_OUTPUT_BUFFER = 1024;
private static final int OUTPUT_CHANNEL_COUNT = 2;
private static final int OUTPUT_FRAME_SIZE = OUTPUT_CHANNEL_COUNT * 2; // 16-bit stereo output.
private int sampleRateHz;
private int channelCount;
private GvrAudioSurround gvrAudioSurround;
private ByteBuffer buffer;
private boolean inputEnded;
private float w;
private float x;
private float y;
private float z;
public GvrBufferProcessor() {
// Use the identity for the initial orientation.
w = 1f;
sampleRateHz = Format.NO_VALUE;
channelCount = Format.NO_VALUE;
}
/**
* Updates the listener head orientation. May be called on any thread. See
* {@code GvrAudioSurround.updateNativeOrientation}.
*/
public synchronized void updateOrientation(float w, float x, float y, float z) {
this.w = w;
this.x = x;
this.y = y;
this.z = z;
if (gvrAudioSurround != null) {
gvrAudioSurround.updateNativeOrientation(w, x, y, z);
}
}
@Override
public synchronized boolean configure(int sampleRateHz, int channelCount,
@C.Encoding int encoding) throws UnhandledFormatException {
if (encoding != C.ENCODING_PCM_16BIT) {
maybeReleaseGvrAudioSurround();
throw new UnhandledFormatException(sampleRateHz, channelCount, encoding);
}
if (this.sampleRateHz == sampleRateHz && this.channelCount == channelCount) {
return false;
}
this.sampleRateHz = sampleRateHz;
this.channelCount = channelCount;
maybeReleaseGvrAudioSurround();
int surroundFormat;
switch (channelCount) {
case 2:
surroundFormat = GvrAudioSurround.SurroundFormat.SURROUND_STEREO;
break;
case 4:
surroundFormat = GvrAudioSurround.SurroundFormat.FIRST_ORDER_AMBISONICS;
break;
case 6:
surroundFormat = GvrAudioSurround.SurroundFormat.SURROUND_FIVE_DOT_ONE;
break;
case 9:
surroundFormat = GvrAudioSurround.SurroundFormat.SECOND_ORDER_AMBISONICS;
break;
case 16:
surroundFormat = GvrAudioSurround.SurroundFormat.THIRD_ORDER_AMBISONICS;
break;
default:
throw new UnhandledFormatException(sampleRateHz, channelCount, encoding);
}
gvrAudioSurround = new GvrAudioSurround(surroundFormat, sampleRateHz, channelCount,
FRAMES_PER_OUTPUT_BUFFER);
gvrAudioSurround.updateNativeOrientation(w, x, y, z);
if (buffer == null) {
buffer = ByteBuffer.allocateDirect(FRAMES_PER_OUTPUT_BUFFER * OUTPUT_FRAME_SIZE)
.order(ByteOrder.nativeOrder());
}
return true;
}
@Override
public boolean isActive() {
return gvrAudioSurround != null;
}
@Override
public int getOutputChannelCount() {
return OUTPUT_CHANNEL_COUNT;
}
@Override
public int getOutputEncoding() {
return C.ENCODING_PCM_16BIT;
}
@Override
public void queueInput(ByteBuffer input) {
int position = input.position();
int readBytes = gvrAudioSurround.addInput(input, position, input.limit() - position);
input.position(position + readBytes);
}
@Override
public void queueEndOfStream() {
inputEnded = true;
gvrAudioSurround.triggerProcessing();
}
@Override
public ByteBuffer getOutput() {
int writtenBytes = gvrAudioSurround.getOutput(buffer, 0, buffer.capacity());
buffer.position(0).limit(writtenBytes);
return buffer;
}
@Override
public boolean isEnded() {
return inputEnded && gvrAudioSurround.getAvailableOutputSize() == 0;
}
@Override
public void flush() {
gvrAudioSurround.flush();
inputEnded = false;
}
@Override
public synchronized void release() {
buffer = null;
maybeReleaseGvrAudioSurround();
}
private void maybeReleaseGvrAudioSurround() {
if (this.gvrAudioSurround != null) {
GvrAudioSurround gvrAudioSurround = this.gvrAudioSurround;
this.gvrAudioSurround = null;
gvrAudioSurround.release();
}
}
}

View File

@ -17,6 +17,7 @@ include ':demo'
include ':playbacktests'
include ':extension-ffmpeg'
include ':extension-flac'
include ':extension-gvr'
include ':extension-okhttp'
include ':extension-opus'
include ':extension-vp9'
@ -25,6 +26,7 @@ include ':extension-vp9'
project(':extension-ffmpeg').projectDir = new File(settingsDir, 'extensions/ffmpeg')
project(':extension-flac').projectDir = new File(settingsDir, 'extensions/flac')
project(':extension-gvr').projectDir = new File(settingsDir, 'extensions/gvr')
project(':extension-okhttp').projectDir = new File(settingsDir, 'extensions/okhttp')
project(':extension-opus').projectDir = new File(settingsDir, 'extensions/opus')
project(':extension-vp9').projectDir = new File(settingsDir, 'extensions/vp9')