Collect configuration values in IamfDecoder.

Instead of hard-coding values in multiple files, all default values are declared in `IamfDecoder`. Additionally, the max number of frames used for output buffer initialisation is fetched from `libiamf` native functions.

PiperOrigin-RevId: 658772175
This commit is contained in:
ktrajkovski 2024-08-02 06:28:24 -07:00 committed by Copybara-Service
parent 9ace81bf2f
commit 20df8b282a
4 changed files with 59 additions and 31 deletions

View File

@ -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:

View File

@ -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<DecoderInputBuffer, SimpleDecoderOutputBuffer, IamfDecoderException> {
// 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();
}

View File

@ -33,12 +33,6 @@ import java.util.Objects;
/** Decodes and renders audio using the native IAMF decoder. */
public class LibiamfAudioRenderer extends DecoderAudioRenderer<IamfDecoder> {
// 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<IamfDecoder> {
@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

View File

@ -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<void*>(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); }