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 f5d223ebab..69c78eeb6d 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
@@ -809,23 +809,6 @@ public final class Util {
return result;
}
- /**
- * Returns the value of the {@code long} argument; throwing an exception if the value overflows an
- * {@code int}.
- *
- *
Equivalent to API 26 {@link Math#toIntExact}.
- *
- * @param value The long value.
- * @return The argument as an int.
- * @throws ArithmeticException If the {@code argument} overflows an int.
- */
- public static int toIntExact(long value) {
- if ((int) value != value) {
- throw new ArithmeticException(); // integer overflow
- }
- return (int) value;
- }
-
/**
* Returns the index of the first occurrence of {@code value} in {@code array}, or {@link
* C#INDEX_UNSET} if {@code value} is not contained in {@code array}.
diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioSink.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioSink.java
index c63dbab47b..8c45a6347b 100644
--- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioSink.java
+++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioSink.java
@@ -16,7 +16,6 @@
package androidx.media3.exoplayer.audio;
import static androidx.media3.common.util.Assertions.checkNotNull;
-import static androidx.media3.common.util.Util.constrainValue;
import static androidx.media3.exoplayer.audio.AudioCapabilities.DEFAULT_AUDIO_CAPABILITIES;
import static com.google.common.base.MoreObjects.firstNonNull;
import static java.lang.Math.max;
@@ -219,38 +218,6 @@ public final class DefaultAudioSink implements AudioSink {
}
}
- /** Provide the buffer size to use when creating an {@link AudioTrack}. */
- interface AudioTrackBufferSizeProvider {
- /** Default instance. */
- AudioTrackBufferSizeProvider DEFAULT =
- new DefaultAudioTrackBufferSizeProvider.Builder().build();
- /**
- * Returns the buffer size to use when creating an {@link AudioTrack} for a specific format and
- * output mode.
- *
- * @param minBufferSizeInBytes The minimum buffer size in bytes required to play this format.
- * See {@link AudioTrack#getMinBufferSize}.
- * @param encoding The {@link C.Encoding} of the format.
- * @param outputMode How the audio will be played. One of the {@link OutputMode output modes}.
- * @param pcmFrameSize The size of the PCM frames if the {@code encoding} is pcm, ignored
- * otherwise, in bytes.
- * @param sampleRate The sample rate of the format, in Hz.
- * @param maxAudioTrackPlaybackSpeed The maximum speed the content will be played using {@link
- * AudioTrack#setPlaybackParams}. 0.5 is 2x slow motion, 1 is real time, 2 is 2x fast
- * forward... This will be {@code 1} unless {@link
- * Builder#setEnableAudioTrackPlaybackParams} is enabled.
- * @return The computed buffer size in bytes. It should always be {@code >=
- * minBufferSizeInBytes}.
- */
- int getBufferSizeInBytes(
- int minBufferSizeInBytes,
- @C.Encoding int encoding,
- @OutputMode int outputMode,
- int pcmFrameSize,
- int sampleRate,
- double maxAudioTrackPlaybackSpeed);
- }
-
/** A builder to create {@link DefaultAudioSink} instances. */
public static final class Builder {
@@ -259,13 +226,11 @@ public final class DefaultAudioSink implements AudioSink {
private boolean enableFloatOutput;
private boolean enableAudioTrackPlaybackParams;
private int offloadMode;
- AudioTrackBufferSizeProvider audioTrackBufferSizeProvider;
/** Creates a new builder. */
public Builder() {
audioCapabilities = DEFAULT_AUDIO_CAPABILITIES;
offloadMode = OFFLOAD_MODE_DISABLED;
- audioTrackBufferSizeProvider = AudioTrackBufferSizeProvider.DEFAULT;
}
/**
@@ -346,18 +311,6 @@ public final class DefaultAudioSink implements AudioSink {
return this;
}
- /**
- * Sets an {@link AudioTrackBufferSizeProvider} to compute the buffer size when {@link
- * #configure} is called with {@code specifiedBufferSize == 0}.
- *
- *
The default value is {@link AudioTrackBufferSizeProvider#DEFAULT}.
- */
- public Builder setAudioTrackBufferSizeProvider(
- AudioTrackBufferSizeProvider audioTrackBufferSizeProvider) {
- this.audioTrackBufferSizeProvider = audioTrackBufferSizeProvider;
- return this;
- }
-
/** Builds the {@link DefaultAudioSink}. Must only be called once per Builder instance. */
public DefaultAudioSink build() {
if (audioProcessorChain == null) {
@@ -418,18 +371,31 @@ public final class DefaultAudioSink implements AudioSink {
*/
public static final int OFFLOAD_MODE_ENABLED_GAPLESS_DISABLED = 3;
- /** Output mode of the audio sink. */
@Documented
@Retention(RetentionPolicy.SOURCE)
@IntDef({OUTPUT_MODE_PCM, OUTPUT_MODE_OFFLOAD, OUTPUT_MODE_PASSTHROUGH})
- public @interface OutputMode {}
+ private @interface OutputMode {}
- /** The audio sink plays PCM audio. */
- public static final int OUTPUT_MODE_PCM = 0;
- /** The audio sink plays encoded audio in offload. */
- public static final int OUTPUT_MODE_OFFLOAD = 1;
- /** The audio sink plays encoded audio in passthrough. */
- public static final int OUTPUT_MODE_PASSTHROUGH = 2;
+ private static final int OUTPUT_MODE_PCM = 0;
+ private static final int OUTPUT_MODE_OFFLOAD = 1;
+ private static final int OUTPUT_MODE_PASSTHROUGH = 2;
+
+ /** A minimum length for the {@link AudioTrack} buffer, in microseconds. */
+ private static final long MIN_BUFFER_DURATION_US = 250_000;
+ /** A maximum length for the {@link AudioTrack} buffer, in microseconds. */
+ private static final long MAX_BUFFER_DURATION_US = 750_000;
+ /** The length for passthrough {@link AudioTrack} buffers, in microseconds. */
+ private static final long PASSTHROUGH_BUFFER_DURATION_US = 250_000;
+ /** The length for offload {@link AudioTrack} buffers, in microseconds. */
+ private static final long OFFLOAD_BUFFER_DURATION_US = 50_000_000;
+
+ /**
+ * A multiplication factor to apply to the minimum buffer size requested by the underlying {@link
+ * AudioTrack}.
+ */
+ private static final int BUFFER_MULTIPLICATION_FACTOR = 4;
+ /** To avoid underruns on some devices (e.g., Broadcom 7271), scale up the AC3 buffer duration. */
+ private static final int AC3_BUFFER_MULTIPLICATION_FACTOR = 2;
/**
* Native error code equivalent of {@link AudioTrack#ERROR_DEAD_OBJECT} to workaround missing
@@ -476,7 +442,6 @@ public final class DefaultAudioSink implements AudioSink {
private final PendingExceptionHolder
initializationExceptionPendingExceptionHolder;
private final PendingExceptionHolder writeExceptionPendingExceptionHolder;
- private final AudioTrackBufferSizeProvider audioTrackBufferSizeProvider;
@Nullable private PlayerId playerId;
@Nullable private Listener listener;
@@ -597,7 +562,6 @@ public final class DefaultAudioSink implements AudioSink {
enableFloatOutput = Util.SDK_INT >= 21 && builder.enableFloatOutput;
enableAudioTrackPlaybackParams = Util.SDK_INT >= 23 && builder.enableAudioTrackPlaybackParams;
offloadMode = Util.SDK_INT >= 29 ? builder.offloadMode : OFFLOAD_MODE_DISABLED;
- audioTrackBufferSizeProvider = builder.audioTrackBufferSizeProvider;
releasingConditionVariable = new ConditionVariable(true);
audioTrackPositionTracker = new AudioTrackPositionTracker(new PositionTrackerListener());
channelMappingAudioProcessor = new ChannelMappingAudioProcessor();
@@ -760,16 +724,6 @@ public final class DefaultAudioSink implements AudioSink {
outputChannelConfig = encodingAndChannelConfig.second;
}
}
- int bufferSize =
- specifiedBufferSize != 0
- ? specifiedBufferSize
- : audioTrackBufferSizeProvider.getBufferSizeInBytes(
- getAudioTrackMinBufferSize(outputSampleRate, outputChannelConfig, outputEncoding),
- outputEncoding,
- outputMode,
- outputPcmFrameSize,
- outputSampleRate,
- enableAudioTrackPlaybackParams ? MAX_PLAYBACK_SPEED : DEFAULT_PLAYBACK_SPEED);
if (outputEncoding == C.ENCODING_INVALID) {
throw new ConfigurationException(
@@ -791,7 +745,8 @@ public final class DefaultAudioSink implements AudioSink {
outputSampleRate,
outputChannelConfig,
outputEncoding,
- bufferSize,
+ specifiedBufferSize,
+ enableAudioTrackPlaybackParams,
availableAudioProcessors);
if (isAudioTrackInitialized()) {
this.pendingConfiguration = pendingConfiguration;
@@ -1252,8 +1207,8 @@ public final class DefaultAudioSink implements AudioSink {
public void setPlaybackParameters(PlaybackParameters playbackParameters) {
playbackParameters =
new PlaybackParameters(
- constrainValue(playbackParameters.speed, MIN_PLAYBACK_SPEED, MAX_PLAYBACK_SPEED),
- constrainValue(playbackParameters.pitch, MIN_PITCH, MAX_PITCH));
+ Util.constrainValue(playbackParameters.speed, MIN_PLAYBACK_SPEED, MAX_PLAYBACK_SPEED),
+ Util.constrainValue(playbackParameters.pitch, MIN_PITCH, MAX_PITCH));
if (enableAudioTrackPlaybackParams && Util.SDK_INT >= 23) {
setAudioTrackPlaybackParametersV23(playbackParameters);
} else {
@@ -1829,6 +1784,47 @@ public final class DefaultAudioSink implements AudioSink {
return Util.SDK_INT >= 29 && audioTrack.isOffloadedPlayback();
}
+ private static int getMaximumEncodedRateBytesPerSecond(@C.Encoding int encoding) {
+ switch (encoding) {
+ case C.ENCODING_MP3:
+ return MpegAudioUtil.MAX_RATE_BYTES_PER_SECOND;
+ case C.ENCODING_AAC_LC:
+ return AacUtil.AAC_LC_MAX_RATE_BYTES_PER_SECOND;
+ case C.ENCODING_AAC_HE_V1:
+ return AacUtil.AAC_HE_V1_MAX_RATE_BYTES_PER_SECOND;
+ case C.ENCODING_AAC_HE_V2:
+ return AacUtil.AAC_HE_V2_MAX_RATE_BYTES_PER_SECOND;
+ case C.ENCODING_AAC_XHE:
+ return AacUtil.AAC_XHE_MAX_RATE_BYTES_PER_SECOND;
+ case C.ENCODING_AAC_ELD:
+ return AacUtil.AAC_ELD_MAX_RATE_BYTES_PER_SECOND;
+ case C.ENCODING_AC3:
+ return Ac3Util.AC3_MAX_RATE_BYTES_PER_SECOND;
+ case C.ENCODING_E_AC3:
+ case C.ENCODING_E_AC3_JOC:
+ return Ac3Util.E_AC3_MAX_RATE_BYTES_PER_SECOND;
+ case C.ENCODING_AC4:
+ return Ac4Util.MAX_RATE_BYTES_PER_SECOND;
+ case C.ENCODING_DTS:
+ return DtsUtil.DTS_MAX_RATE_BYTES_PER_SECOND;
+ case C.ENCODING_DTS_HD:
+ return DtsUtil.DTS_HD_MAX_RATE_BYTES_PER_SECOND;
+ case C.ENCODING_DOLBY_TRUEHD:
+ return Ac3Util.TRUEHD_MAX_RATE_BYTES_PER_SECOND;
+ case C.ENCODING_PCM_16BIT:
+ case C.ENCODING_PCM_16BIT_BIG_ENDIAN:
+ case C.ENCODING_PCM_24BIT:
+ case C.ENCODING_PCM_32BIT:
+ case C.ENCODING_PCM_8BIT:
+ case C.ENCODING_PCM_FLOAT:
+ case C.ENCODING_AAC_ER_BSAC:
+ case C.ENCODING_INVALID:
+ case Format.NO_VALUE:
+ default:
+ throw new IllegalArgumentException();
+ }
+ }
+
private static int getFramesPerEncodedSample(@C.Encoding int encoding, ByteBuffer buffer) {
switch (encoding) {
case C.ENCODING_MP3:
@@ -2018,13 +2014,6 @@ public final class DefaultAudioSink implements AudioSink {
.build();
}
- private static int getAudioTrackMinBufferSize(
- int sampleRateInHz, int channelConfig, int encoding) {
- int minBufferSize = AudioTrack.getMinBufferSize(sampleRateInHz, channelConfig, encoding);
- Assertions.checkState(minBufferSize != AudioTrack.ERROR_BAD_VALUE);
- return minBufferSize;
- }
-
private final class PositionTrackerListener implements AudioTrackPositionTracker.Listener {
@Override
@@ -2119,7 +2108,8 @@ public final class DefaultAudioSink implements AudioSink {
int outputSampleRate,
int outputChannelConfig,
int outputEncoding,
- int bufferSize,
+ int specifiedBufferSize,
+ boolean enableAudioTrackPlaybackParams,
AudioProcessor[] availableAudioProcessors) {
this.inputFormat = inputFormat;
this.inputPcmFrameSize = inputPcmFrameSize;
@@ -2128,8 +2118,10 @@ public final class DefaultAudioSink implements AudioSink {
this.outputSampleRate = outputSampleRate;
this.outputChannelConfig = outputChannelConfig;
this.outputEncoding = outputEncoding;
- this.bufferSize = bufferSize;
this.availableAudioProcessors = availableAudioProcessors;
+
+ // Call computeBufferSize() last as it depends on the other configuration values.
+ this.bufferSize = computeBufferSize(specifiedBufferSize, enableAudioTrackPlaybackParams);
}
/** Returns if the configurations are sufficiently compatible to reuse the audio track. */
@@ -2149,6 +2141,10 @@ public final class DefaultAudioSink implements AudioSink {
return (frameCount * C.MICROS_PER_SECOND) / outputSampleRate;
}
+ public long durationUsToFrames(long durationUs) {
+ return (durationUs * outputSampleRate) / C.MICROS_PER_SECOND;
+ }
+
public AudioTrack buildAudioTrack(
boolean tunneling, AudioAttributes audioAttributes, int audioSessionId)
throws InitializationException {
@@ -2249,6 +2245,49 @@ public final class DefaultAudioSink implements AudioSink {
}
}
+ private int computeBufferSize(
+ int specifiedBufferSize, boolean enableAudioTrackPlaybackParameters) {
+ if (specifiedBufferSize != 0) {
+ return specifiedBufferSize;
+ }
+ switch (outputMode) {
+ case OUTPUT_MODE_PCM:
+ return getPcmDefaultBufferSize(
+ enableAudioTrackPlaybackParameters ? MAX_PLAYBACK_SPEED : DEFAULT_PLAYBACK_SPEED);
+ case OUTPUT_MODE_OFFLOAD:
+ return getEncodedDefaultBufferSize(OFFLOAD_BUFFER_DURATION_US);
+ case OUTPUT_MODE_PASSTHROUGH:
+ return getEncodedDefaultBufferSize(PASSTHROUGH_BUFFER_DURATION_US);
+ default:
+ throw new IllegalStateException();
+ }
+ }
+
+ private int getEncodedDefaultBufferSize(long bufferDurationUs) {
+ int rate = getMaximumEncodedRateBytesPerSecond(outputEncoding);
+ if (outputEncoding == C.ENCODING_AC3) {
+ rate *= AC3_BUFFER_MULTIPLICATION_FACTOR;
+ }
+ return (int) (bufferDurationUs * rate / C.MICROS_PER_SECOND);
+ }
+
+ private int getPcmDefaultBufferSize(float maxAudioTrackPlaybackSpeed) {
+ int minBufferSize =
+ AudioTrack.getMinBufferSize(outputSampleRate, outputChannelConfig, outputEncoding);
+ Assertions.checkState(minBufferSize != AudioTrack.ERROR_BAD_VALUE);
+ int multipliedBufferSize = minBufferSize * BUFFER_MULTIPLICATION_FACTOR;
+ int minAppBufferSize = (int) durationUsToFrames(MIN_BUFFER_DURATION_US) * outputPcmFrameSize;
+ int maxAppBufferSize =
+ max(minBufferSize, (int) durationUsToFrames(MAX_BUFFER_DURATION_US) * outputPcmFrameSize);
+ int bufferSize =
+ Util.constrainValue(multipliedBufferSize, minAppBufferSize, maxAppBufferSize);
+ if (maxAudioTrackPlaybackSpeed != 1f) {
+ // Maintain the buffer duration by scaling the size accordingly.
+ bufferSize = Math.round(bufferSize * maxAudioTrackPlaybackSpeed);
+ }
+ return bufferSize;
+ }
+
@RequiresApi(21)
private static android.media.AudioAttributes getAudioTrackAttributesV21(
AudioAttributes audioAttributes, boolean tunneling) {
diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioTrackBufferSizeProvider.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioTrackBufferSizeProvider.java
deleted file mode 100644
index f6e0d843ff..0000000000
--- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioTrackBufferSizeProvider.java
+++ /dev/null
@@ -1,262 +0,0 @@
-/*
- * Copyright (C) 2021 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 androidx.media3.exoplayer.audio;
-
-import static androidx.media3.common.util.Util.constrainValue;
-import static androidx.media3.common.util.Util.toIntExact;
-import static androidx.media3.exoplayer.audio.DefaultAudioSink.OUTPUT_MODE_OFFLOAD;
-import static androidx.media3.exoplayer.audio.DefaultAudioSink.OUTPUT_MODE_PASSTHROUGH;
-import static androidx.media3.exoplayer.audio.DefaultAudioSink.OUTPUT_MODE_PCM;
-import static java.lang.Math.max;
-
-import android.media.AudioTrack;
-import androidx.media3.common.C;
-import androidx.media3.common.Format;
-import androidx.media3.common.util.UnstableApi;
-import androidx.media3.exoplayer.audio.DefaultAudioSink.OutputMode;
-import androidx.media3.extractor.AacUtil;
-import androidx.media3.extractor.Ac3Util;
-import androidx.media3.extractor.Ac4Util;
-import androidx.media3.extractor.DtsUtil;
-import androidx.media3.extractor.MpegAudioUtil;
-
-/** Provide the buffer size to use when creating an {@link AudioTrack}. */
-@UnstableApi
-public class DefaultAudioTrackBufferSizeProvider
- implements DefaultAudioSink.AudioTrackBufferSizeProvider {
-
- /** Default minimum length for the {@link AudioTrack} buffer, in microseconds. */
- private static final int MIN_PCM_BUFFER_DURATION_US = 250_000;
- /** Default maximum length for the {@link AudioTrack} buffer, in microseconds. */
- private static final int MAX_PCM_BUFFER_DURATION_US = 750_000;
- /** Default multiplication factor to apply to the minimum buffer size requested. */
- private static final int PCM_BUFFER_MULTIPLICATION_FACTOR = 4;
- /** Default length for passthrough {@link AudioTrack} buffers, in microseconds. */
- private static final int PASSTHROUGH_BUFFER_DURATION_US = 250_000;
- /** Default length for offload {@link AudioTrack} buffers, in microseconds. */
- private static final int OFFLOAD_BUFFER_DURATION_US = 50_000_000;
- /**
- * Default multiplication factor to apply to AC3 passthrough buffer to avoid underruns on some
- * devices (e.g., Broadcom 7271).
- */
- private static final int AC3_BUFFER_MULTIPLICATION_FACTOR = 2;
-
- /** A builder to create {@link DefaultAudioTrackBufferSizeProvider} instances. */
- public static class Builder {
-
- private int minPcmBufferDurationUs;
- private int maxPcmBufferDurationUs;
- private int pcmBufferMultiplicationFactor;
- private int passthroughBufferDurationUs;
- private int offloadBufferDurationUs;
- private int ac3BufferMultiplicationFactor;
-
- /** Creates a new builder. */
- public Builder() {
- minPcmBufferDurationUs = MIN_PCM_BUFFER_DURATION_US;
- maxPcmBufferDurationUs = MAX_PCM_BUFFER_DURATION_US;
- pcmBufferMultiplicationFactor = PCM_BUFFER_MULTIPLICATION_FACTOR;
- passthroughBufferDurationUs = PASSTHROUGH_BUFFER_DURATION_US;
- offloadBufferDurationUs = OFFLOAD_BUFFER_DURATION_US;
- ac3BufferMultiplicationFactor = AC3_BUFFER_MULTIPLICATION_FACTOR;
- }
-
- /**
- * Sets the minimum length for PCM {@link AudioTrack} buffers, in microseconds. Default is
- * {@value #MIN_PCM_BUFFER_DURATION_US}.
- */
- public Builder setMinPcmBufferDurationUs(int minPcmBufferDurationUs) {
- this.minPcmBufferDurationUs = minPcmBufferDurationUs;
- return this;
- }
-
- /**
- * Sets the maximum length for PCM {@link AudioTrack} buffers, in microseconds. Default is
- * {@value #MAX_PCM_BUFFER_DURATION_US}.
- */
- public Builder setMaxPcmBufferDurationUs(int maxPcmBufferDurationUs) {
- this.maxPcmBufferDurationUs = maxPcmBufferDurationUs;
- return this;
- }
-
- /**
- * Sets the multiplication factor to apply to the minimum buffer size requested. Default is
- * {@value #PCM_BUFFER_MULTIPLICATION_FACTOR}.
- */
- public Builder setPcmBufferMultiplicationFactor(int pcmBufferMultiplicationFactor) {
- this.pcmBufferMultiplicationFactor = pcmBufferMultiplicationFactor;
- return this;
- }
-
- /**
- * Sets the length for passthrough {@link AudioTrack} buffers, in microseconds. Default is
- * {@value #PASSTHROUGH_BUFFER_DURATION_US}.
- */
- public Builder setPassthroughBufferDurationUs(int passthroughBufferDurationUs) {
- this.passthroughBufferDurationUs = passthroughBufferDurationUs;
- return this;
- }
-
- /**
- * The length for offload {@link AudioTrack} buffers, in microseconds. Default is {@value
- * #OFFLOAD_BUFFER_DURATION_US}.
- */
- public Builder setOffloadBufferDurationUs(int offloadBufferDurationUs) {
- this.offloadBufferDurationUs = offloadBufferDurationUs;
- return this;
- }
-
- /**
- * Sets the multiplication factor to apply to the passthrough buffer for AC3 to avoid underruns
- * on some devices (e.g., Broadcom 7271). Default is {@value #AC3_BUFFER_MULTIPLICATION_FACTOR}.
- */
- public Builder setAc3BufferMultiplicationFactor(int ac3BufferMultiplicationFactor) {
- this.ac3BufferMultiplicationFactor = ac3BufferMultiplicationFactor;
- return this;
- }
-
- /** Build the {@link DefaultAudioTrackBufferSizeProvider}. */
- public DefaultAudioTrackBufferSizeProvider build() {
- return new DefaultAudioTrackBufferSizeProvider(this);
- }
- }
-
- /** The minimum length for PCM {@link AudioTrack} buffers, in microseconds. */
- protected final int minPcmBufferDurationUs;
- /** The maximum length for PCM {@link AudioTrack} buffers, in microseconds. */
- protected final int maxPcmBufferDurationUs;
- /** The multiplication factor to apply to the minimum buffer size requested. */
- protected final int pcmBufferMultiplicationFactor;
- /** The length for passthrough {@link AudioTrack} buffers, in microseconds. */
- protected final int passthroughBufferDurationUs;
- /** The length for offload {@link AudioTrack} buffers, in microseconds. */
- protected final int offloadBufferDurationUs;
- /**
- * The multiplication factor to apply to AC3 passthrough buffer to avoid underruns on some devices
- * (e.g., Broadcom 7271).
- */
- public final int ac3BufferMultiplicationFactor;
-
- protected DefaultAudioTrackBufferSizeProvider(Builder builder) {
- minPcmBufferDurationUs = builder.minPcmBufferDurationUs;
- maxPcmBufferDurationUs = builder.maxPcmBufferDurationUs;
- pcmBufferMultiplicationFactor = builder.pcmBufferMultiplicationFactor;
- passthroughBufferDurationUs = builder.passthroughBufferDurationUs;
- offloadBufferDurationUs = builder.offloadBufferDurationUs;
- ac3BufferMultiplicationFactor = builder.ac3BufferMultiplicationFactor;
- }
-
- @Override
- public int getBufferSizeInBytes(
- int minBufferSizeInBytes,
- @C.Encoding int encoding,
- @OutputMode int outputMode,
- int pcmFrameSize,
- int sampleRate,
- double maxAudioTrackPlaybackSpeed) {
- int bufferSize =
- get1xBufferSizeInBytes(
- minBufferSizeInBytes, encoding, outputMode, pcmFrameSize, sampleRate);
- // Maintain the buffer duration by scaling the size accordingly.
- bufferSize = (int) (bufferSize * maxAudioTrackPlaybackSpeed);
- // Buffer size must not be lower than the AudioTrack min buffer size for this format.
- return max(minBufferSizeInBytes, bufferSize);
- }
-
- /** Returns the buffer size for playback at 1x speed. */
- protected int get1xBufferSizeInBytes(
- int minBufferSizeInBytes, int encoding, int outputMode, int pcmFrameSize, int sampleRate) {
- switch (outputMode) {
- case OUTPUT_MODE_PCM:
- return getPcmBufferSizeInBytes(minBufferSizeInBytes, sampleRate, pcmFrameSize);
- case OUTPUT_MODE_PASSTHROUGH:
- return getPassthroughBufferSizeInBytes(encoding);
- case OUTPUT_MODE_OFFLOAD:
- return getOffloadBufferSizeInBytes(encoding);
- default:
- throw new IllegalArgumentException();
- }
- }
-
- /** Returns the buffer size for PCM playback. */
- protected int getPcmBufferSizeInBytes(int minBufferSizeInBytes, int samplingRate, int frameSize) {
- int targetBufferSize = minBufferSizeInBytes * pcmBufferMultiplicationFactor;
- int minAppBufferSize = durationUsToBytes(minPcmBufferDurationUs, samplingRate, frameSize);
- int maxAppBufferSize = durationUsToBytes(maxPcmBufferDurationUs, samplingRate, frameSize);
- return constrainValue(targetBufferSize, minAppBufferSize, maxAppBufferSize);
- }
-
- /** Returns the buffer size for passthrough playback. */
- protected int getPassthroughBufferSizeInBytes(@C.Encoding int encoding) {
- int bufferSizeUs = passthroughBufferDurationUs;
- if (encoding == C.ENCODING_AC3) {
- bufferSizeUs *= ac3BufferMultiplicationFactor;
- }
- int maxByteRate = getMaximumEncodedRateBytesPerSecond(encoding);
- return toIntExact((long) bufferSizeUs * maxByteRate / C.MICROS_PER_SECOND);
- }
-
- /** Returns the buffer size for offload playback. */
- protected int getOffloadBufferSizeInBytes(@C.Encoding int encoding) {
- int maxByteRate = getMaximumEncodedRateBytesPerSecond(encoding);
- return toIntExact((long) offloadBufferDurationUs * maxByteRate / C.MICROS_PER_SECOND);
- }
-
- protected static int durationUsToBytes(int durationUs, int samplingRate, int frameSize) {
- return toIntExact((long) durationUs * samplingRate * frameSize / C.MICROS_PER_SECOND);
- }
-
- protected static int getMaximumEncodedRateBytesPerSecond(@C.Encoding int encoding) {
- switch (encoding) {
- case C.ENCODING_MP3:
- return MpegAudioUtil.MAX_RATE_BYTES_PER_SECOND;
- case C.ENCODING_AAC_LC:
- return AacUtil.AAC_LC_MAX_RATE_BYTES_PER_SECOND;
- case C.ENCODING_AAC_HE_V1:
- return AacUtil.AAC_HE_V1_MAX_RATE_BYTES_PER_SECOND;
- case C.ENCODING_AAC_HE_V2:
- return AacUtil.AAC_HE_V2_MAX_RATE_BYTES_PER_SECOND;
- case C.ENCODING_AAC_XHE:
- return AacUtil.AAC_XHE_MAX_RATE_BYTES_PER_SECOND;
- case C.ENCODING_AAC_ELD:
- return AacUtil.AAC_ELD_MAX_RATE_BYTES_PER_SECOND;
- case C.ENCODING_AC3:
- return Ac3Util.AC3_MAX_RATE_BYTES_PER_SECOND;
- case C.ENCODING_E_AC3:
- case C.ENCODING_E_AC3_JOC:
- return Ac3Util.E_AC3_MAX_RATE_BYTES_PER_SECOND;
- case C.ENCODING_AC4:
- return Ac4Util.MAX_RATE_BYTES_PER_SECOND;
- case C.ENCODING_DTS:
- return DtsUtil.DTS_MAX_RATE_BYTES_PER_SECOND;
- case C.ENCODING_DTS_HD:
- return DtsUtil.DTS_HD_MAX_RATE_BYTES_PER_SECOND;
- case C.ENCODING_DOLBY_TRUEHD:
- return Ac3Util.TRUEHD_MAX_RATE_BYTES_PER_SECOND;
- case C.ENCODING_PCM_16BIT:
- case C.ENCODING_PCM_16BIT_BIG_ENDIAN:
- case C.ENCODING_PCM_24BIT:
- case C.ENCODING_PCM_32BIT:
- case C.ENCODING_PCM_8BIT:
- case C.ENCODING_PCM_FLOAT:
- case C.ENCODING_AAC_ER_BSAC:
- case C.ENCODING_INVALID:
- case Format.NO_VALUE:
- default:
- throw new IllegalArgumentException();
- }
- }
-}
diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DefaultAudioTrackBufferSizeProviderTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DefaultAudioTrackBufferSizeProviderTest.java
deleted file mode 100644
index a76a49374a..0000000000
--- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/audio/DefaultAudioTrackBufferSizeProviderTest.java
+++ /dev/null
@@ -1,267 +0,0 @@
-/*
- * Copyright (C) 2021 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 androidx.media3.exoplayer.audio;
-
-import static androidx.media3.exoplayer.audio.DefaultAudioSink.OUTPUT_MODE_PASSTHROUGH;
-import static androidx.media3.exoplayer.audio.DefaultAudioSink.OUTPUT_MODE_PCM;
-import static androidx.media3.exoplayer.audio.DefaultAudioTrackBufferSizeProvider.getMaximumEncodedRateBytesPerSecond;
-import static com.google.common.truth.Truth.assertThat;
-
-import androidx.media3.common.C;
-import androidx.media3.common.util.Util;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Sets;
-import java.util.List;
-import java.util.stream.Collectors;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-import org.junit.runners.Parameterized;
-
-/** Tests for {@link DefaultAudioTrackBufferSizeProvider}. */
-@RunWith(JUnit4.class)
-public class DefaultAudioTrackBufferSizeProviderTest {
-
- private static final DefaultAudioTrackBufferSizeProvider DEFAULT =
- new DefaultAudioTrackBufferSizeProvider.Builder().build();
-
- /** Tests for {@link DefaultAudioTrackBufferSizeProvider} for PCM audio. */
- @RunWith(Parameterized.class)
- public static class PcmTest {
-
- @Parameterized.Parameter(0)
- @C.PcmEncoding
- public int encoding;
-
- @Parameterized.Parameter(1)
- public int channelCount;
-
- @Parameterized.Parameter(2)
- public int sampleRate;
-
- @Parameterized.Parameters(name = "{index}: encoding={0}, channelCount={1}, sampleRate={2}")
- public static List data() {
- return Sets.cartesianProduct(
- ImmutableList.of(
- /* encoding */ ImmutableSet.of(
- C.ENCODING_PCM_8BIT,
- C.ENCODING_PCM_16BIT,
- C.ENCODING_PCM_16BIT_BIG_ENDIAN,
- C.ENCODING_PCM_24BIT,
- C.ENCODING_PCM_32BIT,
- C.ENCODING_PCM_FLOAT),
- /* channelCount */ ImmutableSet.of(1, 2, 3, 4, 6, 8),
- /* sampleRate*/ ImmutableSet.of(8000, 16000, 44100, 48000, 96000)))
- .stream()
- .map(s -> s.toArray(new Integer[0]))
- .collect(Collectors.toList());
- }
-
- private int getPcmFrameSize() {
- return Util.getPcmFrameSize(encoding, channelCount);
- }
-
- private int durationUsToBytes(int durationUs) {
- return (int) (((long) durationUs * getPcmFrameSize() * sampleRate) / C.MICROS_PER_SECOND);
- }
-
- @Test
- public void getBufferSizeInBytes_veryBigMinBufferSize_isMinBufferSize() {
- int bufferSize =
- DEFAULT.getBufferSizeInBytes(
- /* minBufferSizeInBytes= */ 123456789,
- /* encoding= */ encoding,
- /* outputMode= */ OUTPUT_MODE_PCM,
- /* pcmFrameSize= */ getPcmFrameSize(),
- /* sampleRate= */ sampleRate,
- /* maxAudioTrackPlaybackSpeed= */ 1);
-
- assertThat(bufferSize).isEqualTo(123456789);
- }
-
- @Test
- public void getBufferSizeInBytes_noMinBufferSize_isMinBufferDuration() {
- int bufferSize =
- DEFAULT.getBufferSizeInBytes(
- /* minBufferSizeInBytes= */ 0,
- /* encoding= */ encoding,
- /* outputMode= */ OUTPUT_MODE_PCM,
- /* pcmFrameSize= */ getPcmFrameSize(),
- /* sampleRate= */ sampleRate,
- /* maxAudioTrackPlaybackSpeed= */ 1);
-
- assertThat(bufferSize).isEqualTo(durationUsToBytes(DEFAULT.minPcmBufferDurationUs));
- }
-
- @Test
- public void getBufferSizeInBytes_tooSmallMinBufferSize_isMinBufferDuration() {
- int minBufferSizeInBytes =
- durationUsToBytes(DEFAULT.minPcmBufferDurationUs / DEFAULT.pcmBufferMultiplicationFactor)
- - 1;
- int bufferSize =
- DEFAULT.getBufferSizeInBytes(
- /* minBufferSizeInBytes= */ minBufferSizeInBytes,
- /* encoding= */ encoding,
- /* outputMode= */ OUTPUT_MODE_PCM,
- /* pcmFrameSize= */ getPcmFrameSize(),
- /* sampleRate= */ sampleRate,
- /* maxAudioTrackPlaybackSpeed= */ 1);
-
- assertThat(bufferSize).isEqualTo(durationUsToBytes(DEFAULT.minPcmBufferDurationUs));
- }
-
- @Test
- public void getBufferSizeInBytes_lowMinBufferSize_multipliesAudioTrackMinBuffer() {
- int minBufferSizeInBytes =
- durationUsToBytes(DEFAULT.minPcmBufferDurationUs / DEFAULT.pcmBufferMultiplicationFactor)
- + 1;
- int bufferSize =
- DEFAULT.getBufferSizeInBytes(
- /* minBufferSizeInBytes= */ minBufferSizeInBytes,
- /* encoding= */ encoding,
- /* outputMode= */ OUTPUT_MODE_PCM,
- /* pcmFrameSize= */ getPcmFrameSize(),
- /* sampleRate= */ sampleRate,
- /* maxAudioTrackPlaybackSpeed= */ 1);
-
- assertThat(bufferSize)
- .isEqualTo(minBufferSizeInBytes * DEFAULT.pcmBufferMultiplicationFactor);
- }
-
- @Test
- public void getBufferSizeInBytes_highMinBufferSize_multipliesAudioTrackMinBuffer() {
- int minBufferSizeInBytes =
- durationUsToBytes(DEFAULT.maxPcmBufferDurationUs / DEFAULT.pcmBufferMultiplicationFactor)
- - 1;
- int bufferSize =
- DEFAULT.getBufferSizeInBytes(
- /* minBufferSizeInBytes= */ minBufferSizeInBytes,
- /* encoding= */ encoding,
- /* outputMode= */ OUTPUT_MODE_PCM,
- /* pcmFrameSize= */ getPcmFrameSize(),
- /* sampleRate= */ sampleRate,
- /* maxAudioTrackPlaybackSpeed= */ 1);
-
- assertThat(bufferSize)
- .isEqualTo(minBufferSizeInBytes * DEFAULT.pcmBufferMultiplicationFactor);
- }
-
- @Test
- public void getBufferSizeInBytes_tooHighMinBufferSize_isMaxBufferDuration() {
- int minBufferSizeInBytes =
- durationUsToBytes(DEFAULT.maxPcmBufferDurationUs / DEFAULT.pcmBufferMultiplicationFactor)
- + 1;
- int bufferSize =
- DEFAULT.getBufferSizeInBytes(
- /* minBufferSizeInBytes= */ minBufferSizeInBytes,
- /* encoding= */ encoding,
- /* outputMode= */ OUTPUT_MODE_PCM,
- /* pcmFrameSize= */ getPcmFrameSize(),
- /* sampleRate= */ sampleRate,
- /* maxAudioTrackPlaybackSpeed= */ 1);
-
- assertThat(bufferSize).isEqualTo(durationUsToBytes(DEFAULT.maxPcmBufferDurationUs));
- }
-
- @Test
- public void getBufferSizeInBytes_lowPlaybackSpeed_isScaledByPlaybackSpeed() {
- int bufferSize =
- DEFAULT.getBufferSizeInBytes(
- /* minBufferSizeInBytes= */ 0,
- /* encoding= */ encoding,
- /* outputMode= */ OUTPUT_MODE_PCM,
- /* pcmFrameSize= */ getPcmFrameSize(),
- /* sampleRate= */ sampleRate,
- /* maxAudioTrackPlaybackSpeed= */ 1 / 5F);
-
- assertThat(bufferSize).isEqualTo(durationUsToBytes(DEFAULT.minPcmBufferDurationUs / 5));
- }
-
- @Test
- public void getBufferSizeInBytes_highPlaybackSpeed_isScaledByPlaybackSpeed() {
- int bufferSize =
- DEFAULT.getBufferSizeInBytes(
- /* minBufferSizeInBytes= */ 0,
- /* encoding= */ encoding,
- /* outputMode= */ OUTPUT_MODE_PCM,
- /* pcmFrameSize= */ getPcmFrameSize(),
- /* sampleRate= */ sampleRate,
- /* maxAudioTrackPlaybackSpeed= */ 5F);
-
- assertThat(bufferSize).isEqualTo(durationUsToBytes(DEFAULT.minPcmBufferDurationUs * 5));
- }
- }
- /**
- * Tests for {@link DefaultAudioTrackBufferSizeProvider} for encoded audio except {@link
- * C#ENCODING_AC3}.
- */
- @RunWith(Parameterized.class)
- public static class EncodedTest {
-
- @Parameterized.Parameter(0)
- @C.Encoding
- public int encoding;
-
- @Parameterized.Parameters(name = "{index}: encoding={0}")
- public static ImmutableList data() {
- return ImmutableList.of(
- C.ENCODING_MP3,
- C.ENCODING_AAC_LC,
- C.ENCODING_AAC_HE_V1,
- C.ENCODING_AC4,
- C.ENCODING_DTS,
- C.ENCODING_DOLBY_TRUEHD);
- }
-
- @Test
- public void getBufferSizeInBytes_veryBigMinBufferSize_isMinBufferSize() {
- int bufferSize =
- DEFAULT.getBufferSizeInBytes(
- /* minBufferSizeInBytes= */ 123456789,
- /* encoding= */ encoding,
- /* outputMode= */ OUTPUT_MODE_PASSTHROUGH,
- /* pcmFrameSize= */ C.LENGTH_UNSET,
- /* sampleRate= */ 0,
- /* maxAudioTrackPlaybackSpeed= */ 0);
-
- assertThat(bufferSize).isEqualTo(123456789);
- }
- }
-
- @Test
- public void
- getBufferSizeInBytes_passthroughAC3_isPassthroughBufferSizeTimesMultiplicationFactor() {
- int bufferSize =
- DEFAULT.getBufferSizeInBytes(
- /* minBufferSizeInBytes= */ 0,
- /* encoding= */ C.ENCODING_AC3,
- /* outputMode= */ OUTPUT_MODE_PASSTHROUGH,
- /* pcmFrameSize= */ C.LENGTH_UNSET,
- /* sampleRate= */ 0,
- /* maxAudioTrackPlaybackSpeed= */ 1);
-
- assertThat(bufferSize)
- .isEqualTo(
- durationUsToAc3MaxBytes(DEFAULT.passthroughBufferDurationUs)
- * DEFAULT.ac3BufferMultiplicationFactor);
- }
-
- private static int durationUsToAc3MaxBytes(long durationUs) {
- return (int)
- (durationUs * getMaximumEncodedRateBytesPerSecond(C.ENCODING_AC3) / C.MICROS_PER_SECOND);
- }
-}