Implement IamfDecoder and LibiamfAudioRenderer configuration.

PiperOrigin-RevId: 655212678
This commit is contained in:
ktrajkovski 2024-07-23 10:20:43 -07:00 committed by Copybara-Service
parent b77f1d0f99
commit 2793ba845e
4 changed files with 114 additions and 11 deletions

View File

@ -18,6 +18,7 @@ package androidx.media3.decoder.iamf;
import static com.google.common.truth.Truth.assertThat;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.common.collect.ImmutableList;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -25,6 +26,17 @@ import org.junit.runner.RunWith;
/** Test IAMF native functions. */
@RunWith(AndroidJUnit4.class)
public final class IamfDecoderTest {
private static final int DEFAULT_BINAURAL_LAYOUT_CHANNEL_COUNT = 2;
// Sample configOBUs data from sample_iamf.mp4 file.
private static final byte[] IACB_OBUS = {
-8, 6, 105, 97, 109, 102, 0, 0, 0, 15, -56, 1, 105, 112, 99, 109, 64, 0, 0, 1, 16, 0, 0, 62,
-128, 8, 12, -84, 2, 0, -56, 1, 1, 0, 0, 32, 16, 1, 1, 16, 78, 42, 1, 101, 110, 45, 117, 115, 0,
116, 101, 115, 116, 95, 109, 105, 120, 95, 112, 114, 101, 115, 0, 1, 1, -84, 2, 116, 101, 115,
116, 95, 115, 117, 98, 95, 109, 105, 120, 95, 48, 95, 97, 117, 100, 105, 111, 95, 101, 108, 101,
109, 101, 110, 116, 95, 48, 0, 0, 0, 100, -128, 125, -128, 0, 0, 100, -128, 125, -128, 0, 0, 1,
-128, 0, -54, 81, -51, -79
};
@Before
public void setUp() {
@ -32,8 +44,10 @@ public final class IamfDecoderTest {
}
@Test
public void iamfLayoutBinauralChannelsCountTest() {
IamfDecoder iamf = new IamfDecoder();
assertThat(iamf.getBinauralLayoutChannelCount()).isEqualTo(2);
public void iamfBinauralLayoutChannelsCount_equalsTwo() throws Exception {
IamfDecoder iamf = new IamfDecoder(ImmutableList.of(IACB_OBUS));
assertThat(iamf.getBinauralLayoutChannelCount())
.isEqualTo(DEFAULT_BINAURAL_LAYOUT_CHANNEL_COUNT);
}
}

View File

@ -21,14 +21,31 @@ import androidx.annotation.VisibleForTesting;
import androidx.media3.decoder.DecoderInputBuffer;
import androidx.media3.decoder.SimpleDecoder;
import androidx.media3.decoder.SimpleDecoderOutputBuffer;
import java.util.List;
/** IAMF decoder. */
@VisibleForTesting(otherwise = PACKAGE_PRIVATE)
public final class IamfDecoder
extends SimpleDecoder<DecoderInputBuffer, SimpleDecoderOutputBuffer, IamfDecoderException> {
public IamfDecoder() {
/**
* Creates an IAMF decoder.
*
* @param initializationData ConfigOBUs data for the decoder.
* @throws IamfDecoderException Thrown if an exception occurs when initializing the decoder.
*/
public IamfDecoder(List<byte[]> initializationData) throws IamfDecoderException {
super(new DecoderInputBuffer[0], new SimpleDecoderOutputBuffer[0]);
int status = iamfConfigDecoder(initializationData.get(0));
if (status != 0) {
throw new IamfDecoderException("Failed to configure decoder with returned status: " + status);
}
}
@Override
public void release() {
super.release();
iamfClose();
}
public int getBinauralLayoutChannelCount() {
@ -42,17 +59,17 @@ public final class IamfDecoder
@Override
protected DecoderInputBuffer createInputBuffer() {
throw new UnsupportedOperationException();
return new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_DIRECT);
}
@Override
protected SimpleDecoderOutputBuffer createOutputBuffer() {
throw new UnsupportedOperationException();
return new SimpleDecoderOutputBuffer(this::releaseOutputBuffer);
}
@Override
protected IamfDecoderException createUnexpectedDecodeException(Throwable error) {
throw new UnsupportedOperationException();
return new IamfDecoderException("Unexpected decode error", error);
}
@Override
@ -62,4 +79,8 @@ public final class IamfDecoder
}
private native int iamfLayoutBinauralChannelsCount();
private native int iamfConfigDecoder(byte[] initializationData);
private native void iamfClose();
}

View File

@ -15,25 +15,67 @@
*/
package androidx.media3.decoder.iamf;
import android.os.Handler;
import androidx.annotation.Nullable;
import androidx.media3.common.C;
import androidx.media3.common.Format;
import androidx.media3.common.MimeTypes;
import androidx.media3.common.audio.AudioProcessor;
import androidx.media3.common.util.TraceUtil;
import androidx.media3.decoder.CryptoConfig;
import androidx.media3.decoder.DecoderException;
import androidx.media3.exoplayer.audio.AudioRendererEventListener;
import androidx.media3.exoplayer.audio.AudioSink;
import androidx.media3.exoplayer.audio.DecoderAudioRenderer;
/** Decodes and renders audio using the native IAMF decoder. */
public class LibiamfAudioRenderer extends DecoderAudioRenderer<IamfDecoder> {
public LibiamfAudioRenderer() {}
/**
* Creates a new instance.
*
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
* null if delivery of events is not required.
* @param eventListener A listener of events. May be null if delivery of events is not required.
* @param audioProcessors Optional {@link AudioProcessor}s that will process audio before output.
*/
public LibiamfAudioRenderer(
@Nullable Handler eventHandler,
@Nullable AudioRendererEventListener eventListener,
AudioProcessor... audioProcessors) {
super(eventHandler, eventListener, audioProcessors);
}
/**
* Creates a new instance.
*
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
* null if delivery of events is not required.
* @param eventListener A listener of events. May be null if delivery of events is not required.
* @param audioSink The sink to which audio will be output.
*/
public LibiamfAudioRenderer(
@Nullable Handler eventHandler,
@Nullable AudioRendererEventListener eventListener,
AudioSink audioSink) {
super(eventHandler, eventListener, audioSink);
}
@Override
protected int supportsFormatInternal(Format format) {
throw new UnsupportedOperationException();
return !IamfLibrary.isAvailable()
|| !java.util.Objects.equals(format.sampleMimeType, MimeTypes.AUDIO_IAMF)
? C.FORMAT_UNSUPPORTED_TYPE
: C.FORMAT_HANDLED;
}
@Override
protected IamfDecoder createDecoder(Format format, @Nullable CryptoConfig cryptoConfig)
throws DecoderException {
throw new UnsupportedOperationException();
TraceUtil.beginSection("createIamfDecoder");
IamfDecoder decoder = new IamfDecoder(format.initializationData);
TraceUtil.endSection();
return decoder;
}
@Override
@ -43,6 +85,6 @@ public class LibiamfAudioRenderer extends DecoderAudioRenderer<IamfDecoder> {
@Override
public String getName() {
throw new UnsupportedOperationException();
return "LibiamfAudioRenderer";
}
}

View File

@ -53,3 +53,29 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
DECODER_FUNC(jint, iamfLayoutBinauralChannelsCount) {
return IAMF_layout_binaural_channels_count();
}
IAMF_DecoderHandle handle;
DECODER_FUNC(jint, iamfConfigDecoder, jbyteArray initializationDataArray) {
handle = IAMF_decoder_open();
IAMF_decoder_peak_limiter_enable(handle, 0);
IAMF_decoder_peak_limiter_set_threshold(handle, -1.0f);
IAMF_decoder_set_normalization_loudness(handle, 0.0f);
IAMF_decoder_set_bit_depth(handle, 16);
IAMF_decoder_set_sampling_rate(handle, 48000);
IAMF_decoder_output_layout_set_binaural(handle);
IAMF_decoder_set_pts(handle, 0, 90000);
uint32_t* bytes_read = nullptr;
jbyte* initializationDataBytes =
env->GetByteArrayElements(initializationDataArray, 0);
int status = IAMF_decoder_configure(
handle, reinterpret_cast<uint8_t*>(initializationDataBytes),
env->GetArrayLength(initializationDataArray), bytes_read);
env->ReleaseByteArrayElements(initializationDataArray,
initializationDataBytes, 0);
return status;
}
DECODER_FUNC(void, iamfClose) { IAMF_decoder_close(handle); }