Implement IamfDecoder and LibiamfAudioRenderer configuration.
PiperOrigin-RevId: 655212678
This commit is contained in:
parent
b77f1d0f99
commit
2793ba845e
@ -18,6 +18,7 @@ package androidx.media3.decoder.iamf;
|
|||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
@ -25,6 +26,17 @@ import org.junit.runner.RunWith;
|
|||||||
/** Test IAMF native functions. */
|
/** Test IAMF native functions. */
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public final class IamfDecoderTest {
|
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
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
@ -32,8 +44,10 @@ public final class IamfDecoderTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void iamfLayoutBinauralChannelsCountTest() {
|
public void iamfBinauralLayoutChannelsCount_equalsTwo() throws Exception {
|
||||||
IamfDecoder iamf = new IamfDecoder();
|
IamfDecoder iamf = new IamfDecoder(ImmutableList.of(IACB_OBUS));
|
||||||
assertThat(iamf.getBinauralLayoutChannelCount()).isEqualTo(2);
|
|
||||||
|
assertThat(iamf.getBinauralLayoutChannelCount())
|
||||||
|
.isEqualTo(DEFAULT_BINAURAL_LAYOUT_CHANNEL_COUNT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,14 +21,31 @@ import androidx.annotation.VisibleForTesting;
|
|||||||
import androidx.media3.decoder.DecoderInputBuffer;
|
import androidx.media3.decoder.DecoderInputBuffer;
|
||||||
import androidx.media3.decoder.SimpleDecoder;
|
import androidx.media3.decoder.SimpleDecoder;
|
||||||
import androidx.media3.decoder.SimpleDecoderOutputBuffer;
|
import androidx.media3.decoder.SimpleDecoderOutputBuffer;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/** IAMF decoder. */
|
/** IAMF decoder. */
|
||||||
@VisibleForTesting(otherwise = PACKAGE_PRIVATE)
|
@VisibleForTesting(otherwise = PACKAGE_PRIVATE)
|
||||||
public final class IamfDecoder
|
public final class IamfDecoder
|
||||||
extends SimpleDecoder<DecoderInputBuffer, SimpleDecoderOutputBuffer, IamfDecoderException> {
|
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]);
|
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() {
|
public int getBinauralLayoutChannelCount() {
|
||||||
@ -42,17 +59,17 @@ public final class IamfDecoder
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected DecoderInputBuffer createInputBuffer() {
|
protected DecoderInputBuffer createInputBuffer() {
|
||||||
throw new UnsupportedOperationException();
|
return new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_DIRECT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected SimpleDecoderOutputBuffer createOutputBuffer() {
|
protected SimpleDecoderOutputBuffer createOutputBuffer() {
|
||||||
throw new UnsupportedOperationException();
|
return new SimpleDecoderOutputBuffer(this::releaseOutputBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected IamfDecoderException createUnexpectedDecodeException(Throwable error) {
|
protected IamfDecoderException createUnexpectedDecodeException(Throwable error) {
|
||||||
throw new UnsupportedOperationException();
|
return new IamfDecoderException("Unexpected decode error", error);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -62,4 +79,8 @@ public final class IamfDecoder
|
|||||||
}
|
}
|
||||||
|
|
||||||
private native int iamfLayoutBinauralChannelsCount();
|
private native int iamfLayoutBinauralChannelsCount();
|
||||||
|
|
||||||
|
private native int iamfConfigDecoder(byte[] initializationData);
|
||||||
|
|
||||||
|
private native void iamfClose();
|
||||||
}
|
}
|
||||||
|
@ -15,25 +15,67 @@
|
|||||||
*/
|
*/
|
||||||
package androidx.media3.decoder.iamf;
|
package androidx.media3.decoder.iamf;
|
||||||
|
|
||||||
|
import android.os.Handler;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.Format;
|
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.CryptoConfig;
|
||||||
import androidx.media3.decoder.DecoderException;
|
import androidx.media3.decoder.DecoderException;
|
||||||
|
import androidx.media3.exoplayer.audio.AudioRendererEventListener;
|
||||||
|
import androidx.media3.exoplayer.audio.AudioSink;
|
||||||
import androidx.media3.exoplayer.audio.DecoderAudioRenderer;
|
import androidx.media3.exoplayer.audio.DecoderAudioRenderer;
|
||||||
|
|
||||||
/** Decodes and renders audio using the native IAMF decoder. */
|
/** Decodes and renders audio using the native IAMF decoder. */
|
||||||
public class LibiamfAudioRenderer extends DecoderAudioRenderer<IamfDecoder> {
|
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
|
@Override
|
||||||
protected int supportsFormatInternal(Format format) {
|
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
|
@Override
|
||||||
protected IamfDecoder createDecoder(Format format, @Nullable CryptoConfig cryptoConfig)
|
protected IamfDecoder createDecoder(Format format, @Nullable CryptoConfig cryptoConfig)
|
||||||
throws DecoderException {
|
throws DecoderException {
|
||||||
throw new UnsupportedOperationException();
|
TraceUtil.beginSection("createIamfDecoder");
|
||||||
|
IamfDecoder decoder = new IamfDecoder(format.initializationData);
|
||||||
|
TraceUtil.endSection();
|
||||||
|
return decoder;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -43,6 +85,6 @@ public class LibiamfAudioRenderer extends DecoderAudioRenderer<IamfDecoder> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
throw new UnsupportedOperationException();
|
return "LibiamfAudioRenderer";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,3 +53,29 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
|||||||
DECODER_FUNC(jint, iamfLayoutBinauralChannelsCount) {
|
DECODER_FUNC(jint, iamfLayoutBinauralChannelsCount) {
|
||||||
return IAMF_layout_binaural_channels_count();
|
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); }
|
||||||
|
Loading…
x
Reference in New Issue
Block a user