From 7ead31021976eb9544826b5179cb3d9a8a955e87 Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Tue, 31 Jul 2018 06:10:35 -0700 Subject: [PATCH] Use maximum bitrates for encoded output buffer sizes There is some risk associated with this change, as audio track buffers come from shared memory and limits may be device-specific. I've tested these sizes on Nvidia Shield TV and Nexus Player on various builds. The maximum size allocated is about 800 KB. We could implement support for retrying creating the audio track if it fails to initialize, but it seems preferable to avoid the extra complexity required to do that unless we know it's necessary to work around device-specific limitations. Issue: #3803 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=206749222 --- RELEASENOTES.md | 3 + .../exoplayer2/audio/DefaultAudioSink.java | 55 +++++++++++++------ 2 files changed, 41 insertions(+), 17 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index a92688bc24..f5393b532b 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -18,6 +18,9 @@ map. * Add support for mu-law and A-law PCM with the ffmpeg extension ([#4360](https://github.com/google/ExoPlayer/issues/4360)). + * Increase `AudioTrack` buffer sizes to the theoretical maximum required for + each encoding for passthrough playbacks + ([#3803](https://github.com/google/ExoPlayer/issues/3803)). * Allow apps to pass a `CacheKeyFactory` for setting custom cache keys when creating a `CacheDataSource`. * Turned on Java 8 compiler support for the ExoPlayer library. Apps that depend diff --git a/library/core/src/main/java/com/google/android/exoplayer2/audio/DefaultAudioSink.java b/library/core/src/main/java/com/google/android/exoplayer2/audio/DefaultAudioSink.java index 2ec3f2864c..ceb3080c9b 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/audio/DefaultAudioSink.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/audio/DefaultAudioSink.java @@ -26,6 +26,7 @@ import android.support.annotation.IntDef; import android.support.annotation.Nullable; import android.util.Log; import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Util; @@ -465,29 +466,22 @@ public final class DefaultAudioSink implements AudioSink { outputEncoding = encoding; outputPcmFrameSize = isInputPcm ? Util.getPcmFrameSize(outputEncoding, channelCount) : C.LENGTH_UNSET; - if (specifiedBufferSize != 0) { - bufferSize = specifiedBufferSize; - } else if (isInputPcm) { - int minBufferSize = AudioTrack.getMinBufferSize(sampleRate, channelConfig, outputEncoding); + bufferSize = specifiedBufferSize != 0 ? specifiedBufferSize : getDefaultBufferSize(); + } + + private int getDefaultBufferSize() { + if (isInputPcm) { + int minBufferSize = + AudioTrack.getMinBufferSize(outputSampleRate, outputChannelConfig, outputEncoding); Assertions.checkState(minBufferSize != ERROR_BAD_VALUE); int multipliedBufferSize = minBufferSize * BUFFER_MULTIPLICATION_FACTOR; int minAppBufferSize = (int) durationUsToFrames(MIN_BUFFER_DURATION_US) * outputPcmFrameSize; int maxAppBufferSize = (int) Math.max(minBufferSize, durationUsToFrames(MAX_BUFFER_DURATION_US) * outputPcmFrameSize); - bufferSize = Util.constrainValue(multipliedBufferSize, minAppBufferSize, maxAppBufferSize); + return Util.constrainValue(multipliedBufferSize, minAppBufferSize, maxAppBufferSize); } else { - // TODO: Set the minimum buffer size using getMinBufferSize when it takes the encoding into - // account. [Internal: b/25181305] - if (outputEncoding == C.ENCODING_AC3 || outputEncoding == C.ENCODING_E_AC3) { - // AC-3 allows bitrates up to 640 kbit/s. - bufferSize = (int) (PASSTHROUGH_BUFFER_DURATION_US * 80 * 1024 / C.MICROS_PER_SECOND); - } else if (outputEncoding == C.ENCODING_DTS) { - // DTS allows an 'open' bitrate, but we assume the maximum listed value: 1536 kbit/s. - bufferSize = (int) (PASSTHROUGH_BUFFER_DURATION_US * 192 * 1024 / C.MICROS_PER_SECOND); - } else /* outputEncoding == C.ENCODING_DTS_HD || outputEncoding == C.ENCODING_DOLBY_TRUEHD*/ { - // HD passthrough requires a larger buffer to avoid underrun. - bufferSize = (int) (PASSTHROUGH_BUFFER_DURATION_US * 192 * 6 * 1024 / C.MICROS_PER_SECOND); - } + int rate = getMaximumEncodedRateBytesPerSecond(outputEncoding); + return (int) (PASSTHROUGH_BUFFER_DURATION_US * rate / C.MICROS_PER_SECOND); } } @@ -1153,6 +1147,33 @@ public final class DefaultAudioSink implements AudioSink { return Util.getAudioTrackChannelConfig(channelCount); } + private static int getMaximumEncodedRateBytesPerSecond(@C.Encoding int encoding) { + switch (encoding) { + case C.ENCODING_AC3: + return 640 * 1000 / 8; + case C.ENCODING_E_AC3: + return 6144 * 1000 / 8; + case C.ENCODING_DTS: + // DTS allows an 'open' bitrate, but we assume the maximum listed value: 1536 kbit/s. + return 1536 * 1000 / 8; + case C.ENCODING_DTS_HD: + return 18000 * 1000 / 8; + case C.ENCODING_DOLBY_TRUEHD: + return 24500 * 1000 / 8; + case C.ENCODING_INVALID: + case C.ENCODING_PCM_16BIT: + case C.ENCODING_PCM_24BIT: + case C.ENCODING_PCM_32BIT: + case C.ENCODING_PCM_8BIT: + case C.ENCODING_PCM_A_LAW: + case C.ENCODING_PCM_FLOAT: + case C.ENCODING_PCM_MU_LAW: + case Format.NO_VALUE: + default: + throw new IllegalArgumentException(); + } + } + private static int getFramesPerEncodedSample(@C.Encoding int encoding, ByteBuffer buffer) { if (encoding == C.ENCODING_DTS || encoding == C.ENCODING_DTS_HD) { return DtsUtil.parseDtsAudioSampleCount(buffer);