diff --git a/libraries/common/src/main/java/androidx/media3/common/util/Util.java b/libraries/common/src/main/java/androidx/media3/common/util/Util.java index cc3021c739..5d0ea0b09b 100644 --- a/libraries/common/src/main/java/androidx/media3/common/util/Util.java +++ b/libraries/common/src/main/java/androidx/media3/common/util/Util.java @@ -2315,19 +2315,30 @@ public final class Util { */ @UnstableApi public static int getPcmFrameSize(@C.PcmEncoding int pcmEncoding, int channelCount) { + return getByteDepth(pcmEncoding) * channelCount; + } + + /** + * Returns the byte depth for audio with the specified encoding. + * + * @param pcmEncoding The encoding of the audio data. + * @return The byte depth of the audio. + */ + @UnstableApi + public static int getByteDepth(@C.PcmEncoding int pcmEncoding) { switch (pcmEncoding) { case C.ENCODING_PCM_8BIT: - return channelCount; + return 1; case C.ENCODING_PCM_16BIT: case C.ENCODING_PCM_16BIT_BIG_ENDIAN: - return channelCount * 2; + return 2; case C.ENCODING_PCM_24BIT: case C.ENCODING_PCM_24BIT_BIG_ENDIAN: - return channelCount * 3; + return 3; case C.ENCODING_PCM_32BIT: case C.ENCODING_PCM_32BIT_BIG_ENDIAN: case C.ENCODING_PCM_FLOAT: - return channelCount * 4; + return 4; case C.ENCODING_INVALID: case Format.NO_VALUE: default: diff --git a/libraries/decoder_iamf/src/main/java/androidx/media3/decoder/iamf/IamfDecoder.java b/libraries/decoder_iamf/src/main/java/androidx/media3/decoder/iamf/IamfDecoder.java index 81ad9b9b3a..a74f53b861 100644 --- a/libraries/decoder_iamf/src/main/java/androidx/media3/decoder/iamf/IamfDecoder.java +++ b/libraries/decoder_iamf/src/main/java/androidx/media3/decoder/iamf/IamfDecoder.java @@ -18,6 +18,7 @@ package androidx.media3.decoder.iamf; import static android.support.annotation.VisibleForTesting.PACKAGE_PRIVATE; import androidx.annotation.VisibleForTesting; +import androidx.media3.common.C; import androidx.media3.common.util.Util; import androidx.media3.decoder.DecoderInputBuffer; import androidx.media3.decoder.SimpleDecoder; @@ -30,9 +31,10 @@ import javax.annotation.Nullable; @VisibleForTesting(otherwise = PACKAGE_PRIVATE) public final class IamfDecoder extends SimpleDecoder { - - // TODO(ktrajkovski): Find the maximum acceptable output buffer size. - private static final int DEFAULT_OUTPUT_BUFFER_SIZE = 4096; + // TODO(ktrajkovski): Fetch channel count from the device instead of hardcoding. + /* package */ static final int DEFAULT_CHANNEL_COUNT = 2; + /* package */ static final int DEFAULT_OUTPUT_SAMPLE_RATE = 48000; + /* package */ static final @C.PcmEncoding int DEFAULT_PCM_ENCODING = C.ENCODING_PCM_16BIT; private final byte[] initializationData; @@ -48,7 +50,12 @@ public final class IamfDecoder throw new IamfDecoderException("Initialization data must contain a single element."); } this.initializationData = initializationData.get(0); - int status = iamfConfigDecoder(this.initializationData); + int status = + iamfConfigDecoder( + this.initializationData, + Util.getByteDepth(DEFAULT_PCM_ENCODING) * C.BITS_PER_BYTE, + DEFAULT_OUTPUT_SAMPLE_RATE, + DEFAULT_CHANNEL_COUNT); if (status != 0) { throw new IamfDecoderException("Failed to configure decoder with returned status: " + status); } @@ -90,9 +97,15 @@ public final class IamfDecoder DecoderInputBuffer inputBuffer, SimpleDecoderOutputBuffer outputBuffer, boolean reset) { if (reset) { iamfClose(); - iamfConfigDecoder(this.initializationData); // reconfigure + iamfConfigDecoder( + this.initializationData, + Util.getByteDepth(DEFAULT_PCM_ENCODING) * C.BITS_PER_BYTE, + DEFAULT_OUTPUT_SAMPLE_RATE, + DEFAULT_CHANNEL_COUNT); // reconfigure } - outputBuffer.init(inputBuffer.timeUs, DEFAULT_OUTPUT_BUFFER_SIZE); + int bufferSize = + iamfGetMaxFrameSize() * DEFAULT_CHANNEL_COUNT * Util.getByteDepth(DEFAULT_PCM_ENCODING); + outputBuffer.init(inputBuffer.timeUs, bufferSize); ByteBuffer outputData = Util.castNonNull(outputBuffer.data); ByteBuffer inputData = Util.castNonNull(inputBuffer.data); int ret = iamfDecode(inputData, inputData.limit(), outputData); @@ -100,17 +113,22 @@ public final class IamfDecoder return new IamfDecoderException("Failed to decode error= " + ret); } outputData.position(0); - // TODO(ktrajkovski): Extract the outputData limit from the iamfDecode return value, given - // channel count, and given bit depth. - outputData.limit(ret * 4); // x2 for expected bit depth, x2 for channel count + outputData.limit(ret * DEFAULT_CHANNEL_COUNT * Util.getByteDepth(DEFAULT_PCM_ENCODING)); return null; } private native int iamfLayoutBinauralChannelsCount(); - private native int iamfConfigDecoder(byte[] initializationData); + private native int iamfConfigDecoder( + byte[] initializationData, int bitDepth, int sampleRate, int channelCount); private native void iamfClose(); private native int iamfDecode(ByteBuffer inputBuffer, int inputSize, ByteBuffer outputBuffer); + + /** + * Returns the maximum expected number of PCM samples per channel in a compressed audio frame. + * Used to initialize the output buffer. + */ + private native int iamfGetMaxFrameSize(); } diff --git a/libraries/decoder_iamf/src/main/java/androidx/media3/decoder/iamf/LibiamfAudioRenderer.java b/libraries/decoder_iamf/src/main/java/androidx/media3/decoder/iamf/LibiamfAudioRenderer.java index ab9d639a43..bae8b5baab 100644 --- a/libraries/decoder_iamf/src/main/java/androidx/media3/decoder/iamf/LibiamfAudioRenderer.java +++ b/libraries/decoder_iamf/src/main/java/androidx/media3/decoder/iamf/LibiamfAudioRenderer.java @@ -33,12 +33,6 @@ import java.util.Objects; /** Decodes and renders audio using the native IAMF decoder. */ public class LibiamfAudioRenderer extends DecoderAudioRenderer { - // TODO(ktrajkovski): Values need to be configured and must come from the same source of truth as - // in {@link IamfDecoder}. - private static final int BINAURAL_CHANNEL_COUNT = 2; - private static final int DEFAULT_OUTPUT_SAMPLE_RATE = 48000; - private static final int DEFAULT_PCM_ENCODING = C.ENCODING_PCM_16BIT; - /** * Creates a new instance. * @@ -89,7 +83,9 @@ public class LibiamfAudioRenderer extends DecoderAudioRenderer { @Override protected Format getOutputFormat(IamfDecoder decoder) { return Util.getPcmFormat( - DEFAULT_PCM_ENCODING, BINAURAL_CHANNEL_COUNT, DEFAULT_OUTPUT_SAMPLE_RATE); + IamfDecoder.DEFAULT_PCM_ENCODING, + IamfDecoder.DEFAULT_CHANNEL_COUNT, + IamfDecoder.DEFAULT_OUTPUT_SAMPLE_RATE); } @Override diff --git a/libraries/decoder_iamf/src/main/jni/iamf_jni.cc b/libraries/decoder_iamf/src/main/jni/iamf_jni.cc index dd3938245a..a51aa1f722 100644 --- a/libraries/decoder_iamf/src/main/jni/iamf_jni.cc +++ b/libraries/decoder_iamf/src/main/jni/iamf_jni.cc @@ -56,18 +56,17 @@ DECODER_FUNC(jint, iamfLayoutBinauralChannelsCount) { IAMF_DecoderHandle handle; -DECODER_FUNC(jint, iamfConfigDecoder, jbyteArray initializationDataArray) { +DECODER_FUNC(jint, iamfConfigDecoder, jbyteArray initializationDataArray, + jint bitDepth, jint sampleRate, jint channelCount) { handle = IAMF_decoder_open(); - - // TODO(ktrajkovski): Values need to be aligned with IamfDecoder and - // LibiamfAudioRenderer and/or extracted from ConfigOBUs. 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); + IAMF_decoder_set_bit_depth(handle, bitDepth); + IAMF_decoder_set_sampling_rate(handle, sampleRate); + if (channelCount == 2) { + IAMF_decoder_output_layout_set_binaural(handle); + } else { + IAMF_decoder_output_layout_set_sound_system(handle, SOUND_SYSTEM_INVALID); + } uint32_t* bytes_read = nullptr; jbyte* initializationDataBytes = @@ -92,4 +91,8 @@ DECODER_FUNC(jint, iamfDecode, jobject inputBuffer, jint inputSize, reinterpret_cast(env->GetDirectBufferAddress(outputBuffer))); } +DECODER_FUNC(jint, iamfGetMaxFrameSize) { + return IAMF_decoder_get_stream_info(handle)->max_frame_size; +} + DECODER_FUNC(void, iamfClose) { IAMF_decoder_close(handle); }