mirror of
https://github.com/androidx/media.git
synced 2025-05-07 23:50:44 +08:00
Use audio bitrate to calculate AudioTrack min buffer in passthrough
Use the bitrate of the audio format (when available) in DefaultAudioSink.AudioTrackBufferSizeProvider.getBufferSizeInBytes() to calculate accurate buffer sizes for direct (passthrough) playbacks. #minor-release PiperOrigin-RevId: 491628530 (cherry picked from commit e219ac21ae604f182769d69f6f590191a92100d0)
This commit is contained in:
parent
b6477ddddd
commit
d7923785a7
@ -194,6 +194,8 @@ public final class DefaultAudioSink implements AudioSink {
|
||||
* @param pcmFrameSize The size of the PCM frames if the {@code encoding} is PCM, 1 otherwise,
|
||||
* in bytes.
|
||||
* @param sampleRate The sample rate of the format, in Hz.
|
||||
* @param bitrate The bitrate of the audio stream if the stream is compressed, or {@link
|
||||
* Format#NO_VALUE} if {@code encoding} is PCM or the bitrate is not known.
|
||||
* @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, etc. This will be {@code 1} unless {@link
|
||||
@ -208,6 +210,7 @@ public final class DefaultAudioSink implements AudioSink {
|
||||
@OutputMode int outputMode,
|
||||
int pcmFrameSize,
|
||||
int sampleRate,
|
||||
int bitrate,
|
||||
double maxAudioTrackPlaybackSpeed);
|
||||
}
|
||||
|
||||
@ -781,6 +784,7 @@ public final class DefaultAudioSink implements AudioSink {
|
||||
outputMode,
|
||||
outputPcmFrameSize != C.LENGTH_UNSET ? outputPcmFrameSize : 1,
|
||||
outputSampleRate,
|
||||
inputFormat.bitrate,
|
||||
enableAudioTrackPlaybackParams ? MAX_PLAYBACK_SPEED : DEFAULT_PLAYBACK_SPEED);
|
||||
|
||||
offloadDisabledUntilNextConfiguration = false;
|
||||
|
@ -19,6 +19,7 @@ import static com.google.android.exoplayer2.audio.DefaultAudioSink.OUTPUT_MODE_O
|
||||
import static com.google.android.exoplayer2.audio.DefaultAudioSink.OUTPUT_MODE_PASSTHROUGH;
|
||||
import static com.google.android.exoplayer2.audio.DefaultAudioSink.OUTPUT_MODE_PCM;
|
||||
import static com.google.android.exoplayer2.util.Util.constrainValue;
|
||||
import static com.google.common.math.IntMath.divide;
|
||||
import static com.google.common.primitives.Ints.checkedCast;
|
||||
import static java.lang.Math.max;
|
||||
|
||||
@ -27,6 +28,7 @@ import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.audio.DefaultAudioSink.OutputMode;
|
||||
import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
||||
import java.math.RoundingMode;
|
||||
|
||||
/** Provide the buffer size to use when creating an {@link AudioTrack}. */
|
||||
public class DefaultAudioTrackBufferSizeProvider
|
||||
@ -166,10 +168,11 @@ public class DefaultAudioTrackBufferSizeProvider
|
||||
@OutputMode int outputMode,
|
||||
int pcmFrameSize,
|
||||
int sampleRate,
|
||||
int bitrate,
|
||||
double maxAudioTrackPlaybackSpeed) {
|
||||
int bufferSize =
|
||||
get1xBufferSizeInBytes(
|
||||
minBufferSizeInBytes, encoding, outputMode, pcmFrameSize, sampleRate);
|
||||
minBufferSizeInBytes, encoding, outputMode, pcmFrameSize, sampleRate, bitrate);
|
||||
// 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.
|
||||
@ -180,12 +183,17 @@ public class DefaultAudioTrackBufferSizeProvider
|
||||
|
||||
/** Returns the buffer size for playback at 1x speed. */
|
||||
protected int get1xBufferSizeInBytes(
|
||||
int minBufferSizeInBytes, int encoding, int outputMode, int pcmFrameSize, int sampleRate) {
|
||||
int minBufferSizeInBytes,
|
||||
int encoding,
|
||||
int outputMode,
|
||||
int pcmFrameSize,
|
||||
int sampleRate,
|
||||
int bitrate) {
|
||||
switch (outputMode) {
|
||||
case OUTPUT_MODE_PCM:
|
||||
return getPcmBufferSizeInBytes(minBufferSizeInBytes, sampleRate, pcmFrameSize);
|
||||
case OUTPUT_MODE_PASSTHROUGH:
|
||||
return getPassthroughBufferSizeInBytes(encoding);
|
||||
return getPassthroughBufferSizeInBytes(encoding, bitrate);
|
||||
case OUTPUT_MODE_OFFLOAD:
|
||||
return getOffloadBufferSizeInBytes(encoding);
|
||||
default:
|
||||
@ -202,13 +210,16 @@ public class DefaultAudioTrackBufferSizeProvider
|
||||
}
|
||||
|
||||
/** Returns the buffer size for passthrough playback. */
|
||||
protected int getPassthroughBufferSizeInBytes(@C.Encoding int encoding) {
|
||||
protected int getPassthroughBufferSizeInBytes(@C.Encoding int encoding, int bitrate) {
|
||||
int bufferSizeUs = passthroughBufferDurationUs;
|
||||
if (encoding == C.ENCODING_AC3) {
|
||||
bufferSizeUs *= ac3BufferMultiplicationFactor;
|
||||
}
|
||||
int maxByteRate = getMaximumEncodedRateBytesPerSecond(encoding);
|
||||
return checkedCast((long) bufferSizeUs * maxByteRate / C.MICROS_PER_SECOND);
|
||||
int byteRate =
|
||||
bitrate != Format.NO_VALUE
|
||||
? divide(bitrate, 8, RoundingMode.CEILING)
|
||||
: getMaximumEncodedRateBytesPerSecond(encoding);
|
||||
return checkedCast((long) bufferSizeUs * byteRate / C.MICROS_PER_SECOND);
|
||||
}
|
||||
|
||||
/** Returns the buffer size for offload playback. */
|
||||
|
@ -22,6 +22,7 @@ import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@ -34,7 +35,7 @@ public class DefaultAudioTrackBufferSizeProviderAC3Test {
|
||||
|
||||
@Test
|
||||
public void
|
||||
getBufferSizeInBytes_passthroughAC3_isPassthroughBufferSizeTimesMultiplicationFactor() {
|
||||
getBufferSizeInBytes_passthroughAc3AndNoBitrate_assumesMaxByteRateTimesMultiplicationFactor() {
|
||||
int bufferSize =
|
||||
DEFAULT.getBufferSizeInBytes(
|
||||
/* minBufferSizeInBytes= */ 0,
|
||||
@ -42,6 +43,7 @@ public class DefaultAudioTrackBufferSizeProviderAC3Test {
|
||||
/* outputMode= */ OUTPUT_MODE_PASSTHROUGH,
|
||||
/* pcmFrameSize= */ 1,
|
||||
/* sampleRate= */ 0,
|
||||
/* bitrate= */ Format.NO_VALUE,
|
||||
/* maxAudioTrackPlaybackSpeed= */ 1);
|
||||
|
||||
assertThat(bufferSize)
|
||||
@ -50,6 +52,23 @@ public class DefaultAudioTrackBufferSizeProviderAC3Test {
|
||||
* DEFAULT.ac3BufferMultiplicationFactor);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
getBufferSizeInBytes_passthroughAC3At256Kbits_isPassthroughBufferSizeTimesMultiplicationFactor() {
|
||||
int bufferSize =
|
||||
DEFAULT.getBufferSizeInBytes(
|
||||
/* minBufferSizeInBytes= */ 0,
|
||||
/* encoding= */ C.ENCODING_AC3,
|
||||
/* outputMode= */ OUTPUT_MODE_PASSTHROUGH,
|
||||
/* pcmFrameSize= */ 1,
|
||||
/* sampleRate= */ 0,
|
||||
/* bitrate= */ 256_000,
|
||||
/* maxAudioTrackPlaybackSpeed= */ 1);
|
||||
|
||||
// Default buffer duration 0.25s => 0.25 * 256000 / 8 = 8000
|
||||
assertThat(bufferSize).isEqualTo(8000 * DEFAULT.ac3BufferMultiplicationFactor);
|
||||
}
|
||||
|
||||
private static int durationUsToAc3MaxBytes(long durationUs) {
|
||||
return (int)
|
||||
(durationUs * getMaximumEncodedRateBytesPerSecond(C.ENCODING_AC3) / MICROS_PER_SECOND);
|
||||
|
@ -15,10 +15,13 @@
|
||||
*/
|
||||
package com.google.android.exoplayer2.audio;
|
||||
|
||||
import static com.google.android.exoplayer2.C.MICROS_PER_SECOND;
|
||||
import static com.google.android.exoplayer2.audio.DefaultAudioSink.OUTPUT_MODE_PASSTHROUGH;
|
||||
import static com.google.android.exoplayer2.audio.DefaultAudioTrackBufferSizeProvider.getMaximumEncodedRateBytesPerSecond;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@ -43,6 +46,8 @@ public class DefaultAudioTrackBufferSizeProviderEncodedTest {
|
||||
C.ENCODING_MP3,
|
||||
C.ENCODING_AAC_LC,
|
||||
C.ENCODING_AAC_HE_V1,
|
||||
C.ENCODING_E_AC3,
|
||||
C.ENCODING_E_AC3_JOC,
|
||||
C.ENCODING_AC4,
|
||||
C.ENCODING_DTS,
|
||||
C.ENCODING_DOLBY_TRUEHD);
|
||||
@ -57,8 +62,46 @@ public class DefaultAudioTrackBufferSizeProviderEncodedTest {
|
||||
/* outputMode= */ OUTPUT_MODE_PASSTHROUGH,
|
||||
/* pcmFrameSize= */ 1,
|
||||
/* sampleRate= */ 0,
|
||||
/* bitrate= */ Format.NO_VALUE,
|
||||
/* maxAudioTrackPlaybackSpeed= */ 0);
|
||||
|
||||
assertThat(bufferSize).isEqualTo(123456789);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
getBufferSizeInBytes_passThroughAndBitrateNotSet_returnsBufferSizeWithAssumedBitrate() {
|
||||
int bufferSize =
|
||||
DEFAULT.getBufferSizeInBytes(
|
||||
/* minBufferSizeInBytes= */ 0,
|
||||
/* encoding= */ encoding,
|
||||
/* outputMode= */ OUTPUT_MODE_PASSTHROUGH,
|
||||
/* pcmFrameSize= */ 1,
|
||||
/* sampleRate= */ 0,
|
||||
/* bitrate= */ Format.NO_VALUE,
|
||||
/* maxAudioTrackPlaybackSpeed= */ 1);
|
||||
|
||||
assertThat(bufferSize)
|
||||
.isEqualTo(durationUsToMaxBytes(encoding, DEFAULT.passthroughBufferDurationUs));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getBufferSizeInBytes_passthroughAndBitrateDefined() {
|
||||
int bufferSize =
|
||||
DEFAULT.getBufferSizeInBytes(
|
||||
/* minBufferSizeInBytes= */ 0,
|
||||
/* encoding= */ encoding,
|
||||
/* outputMode= */ OUTPUT_MODE_PASSTHROUGH,
|
||||
/* pcmFrameSize= */ 1,
|
||||
/* sampleRate= */ 0,
|
||||
/* bitrate= */ 256_000,
|
||||
/* maxAudioTrackPlaybackSpeed= */ 1);
|
||||
|
||||
// Default buffer duration is 250ms => 0.25 * 256000 / 8 = 8000
|
||||
assertThat(bufferSize).isEqualTo(8000);
|
||||
}
|
||||
|
||||
private static int durationUsToMaxBytes(@C.Encoding int encoding, long durationUs) {
|
||||
return (int) (durationUs * getMaximumEncodedRateBytesPerSecond(encoding) / MICROS_PER_SECOND);
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ import static com.google.common.truth.Truth.assertThat;
|
||||
import static java.lang.Math.ceil;
|
||||
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
@ -89,6 +90,7 @@ public class DefaultAudioTrackBufferSizeProviderPcmTest {
|
||||
/* outputMode= */ OUTPUT_MODE_PCM,
|
||||
/* pcmFrameSize= */ getPcmFrameSize(),
|
||||
/* sampleRate= */ sampleRate,
|
||||
/* bitrate= */ Format.NO_VALUE,
|
||||
/* maxAudioTrackPlaybackSpeed= */ 1);
|
||||
|
||||
assertThat(bufferSize).isEqualTo(roundUpToFrame(1234567890));
|
||||
@ -103,6 +105,7 @@ public class DefaultAudioTrackBufferSizeProviderPcmTest {
|
||||
/* outputMode= */ OUTPUT_MODE_PCM,
|
||||
/* pcmFrameSize= */ getPcmFrameSize(),
|
||||
/* sampleRate= */ sampleRate,
|
||||
/* bitrate= */ Format.NO_VALUE,
|
||||
/* maxAudioTrackPlaybackSpeed= */ 1);
|
||||
|
||||
assertThat(bufferSize)
|
||||
@ -121,6 +124,7 @@ public class DefaultAudioTrackBufferSizeProviderPcmTest {
|
||||
/* outputMode= */ OUTPUT_MODE_PCM,
|
||||
/* pcmFrameSize= */ getPcmFrameSize(),
|
||||
/* sampleRate= */ sampleRate,
|
||||
/* bitrate= */ Format.NO_VALUE,
|
||||
/* maxAudioTrackPlaybackSpeed= */ 1);
|
||||
|
||||
assertThat(bufferSize)
|
||||
@ -139,6 +143,7 @@ public class DefaultAudioTrackBufferSizeProviderPcmTest {
|
||||
/* outputMode= */ OUTPUT_MODE_PCM,
|
||||
/* pcmFrameSize= */ getPcmFrameSize(),
|
||||
/* sampleRate= */ sampleRate,
|
||||
/* bitrate= */ Format.NO_VALUE,
|
||||
/* maxAudioTrackPlaybackSpeed= */ 1);
|
||||
|
||||
assertThat(bufferSize)
|
||||
@ -157,6 +162,7 @@ public class DefaultAudioTrackBufferSizeProviderPcmTest {
|
||||
/* outputMode= */ OUTPUT_MODE_PCM,
|
||||
/* pcmFrameSize= */ getPcmFrameSize(),
|
||||
/* sampleRate= */ sampleRate,
|
||||
/* bitrate= */ Format.NO_VALUE,
|
||||
/* maxAudioTrackPlaybackSpeed= */ 1);
|
||||
|
||||
assertThat(bufferSize)
|
||||
@ -175,6 +181,7 @@ public class DefaultAudioTrackBufferSizeProviderPcmTest {
|
||||
/* outputMode= */ OUTPUT_MODE_PCM,
|
||||
/* pcmFrameSize= */ getPcmFrameSize(),
|
||||
/* sampleRate= */ sampleRate,
|
||||
/* bitrate= */ Format.NO_VALUE,
|
||||
/* maxAudioTrackPlaybackSpeed= */ 1);
|
||||
|
||||
assertThat(bufferSize)
|
||||
@ -190,6 +197,7 @@ public class DefaultAudioTrackBufferSizeProviderPcmTest {
|
||||
/* outputMode= */ OUTPUT_MODE_PCM,
|
||||
/* pcmFrameSize= */ getPcmFrameSize(),
|
||||
/* sampleRate= */ sampleRate,
|
||||
/* bitrate= */ Format.NO_VALUE,
|
||||
/* maxAudioTrackPlaybackSpeed= */ 1 / 5F);
|
||||
|
||||
assertThat(bufferSize)
|
||||
@ -205,6 +213,7 @@ public class DefaultAudioTrackBufferSizeProviderPcmTest {
|
||||
/* outputMode= */ OUTPUT_MODE_PCM,
|
||||
/* pcmFrameSize= */ getPcmFrameSize(),
|
||||
/* sampleRate= */ sampleRate,
|
||||
/* bitrate= */ Format.NO_VALUE,
|
||||
/* maxAudioTrackPlaybackSpeed= */ 8F);
|
||||
|
||||
int expected = roundUpToFrame(durationUsToBytes(DEFAULT.minPcmBufferDurationUs) * 8);
|
||||
|
Loading…
x
Reference in New Issue
Block a user