From 82ee82635be189743eac82c8e2269f0cfd40858e Mon Sep 17 00:00:00 2001 From: krocard Date: Mon, 31 Jan 2022 11:29:21 +0000 Subject: [PATCH] Retry AudioTrack with smaller buffer if > 1M Some phone with limited memory can't allocate bigger shared memory buffers. This might or might not be related to Binder's 1M transaction limit. Tested on Pixel 4 by setting the minimum buffer size to 1h. https://github.com/google/ExoPlayer/issues/9712 #minor-release PiperOrigin-RevId: 425324536 --- RELEASENOTES.md | 2 + .../exoplayer2/audio/DefaultAudioSink.java | 48 +++++++++++++++++-- 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 6ac9d5a210..c6f478c67c 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -58,6 +58,8 @@ * Allow customization of the `AudioTrack` buffer size calculation by injecting an `AudioTrackBufferSizeProvider` to `DefaultAudioSink`. ([#8891](https://github.com/google/ExoPlayer/issues/8891)). + * Retry `AudioTrack` creation if the requested buffer size was > 1MB. + ([#9712](https://github.com/google/ExoPlayer/issues/9712)). * Extractors: * Fix inconsistency with spec in H.265 SPS nal units parsing ([#9719](https://github.com/google/ExoPlayer/issues/9719)). 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 c799a5d341..74d5b5c0da 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 @@ -69,6 +69,12 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; */ public final class DefaultAudioSink implements AudioSink { + /** + * If an attempt to instantiate an AudioTrack with a buffer size larger than this value fails, a + * second attempt is made using this buffer size. + */ + private static final int AUDIO_TRACK_SMALLER_BUFFER_RETRY_SIZE = 1_000_000; + /** * Thrown when the audio track has provided a spurious timestamp, if {@link * #failOnSpuriousAudioTimestamp} is set. @@ -824,7 +830,7 @@ public final class DefaultAudioSink implements AudioSink { // initialization of the audio track to fail. releasingConditionVariable.block(); - audioTrack = buildAudioTrack(); + audioTrack = buildAudioTrackWithRetry(); if (isOffloadedPlayback(audioTrack)) { registerStreamEventCallbackV29(audioTrack); if (offloadMode != OFFLOAD_MODE_ENABLED_GAPLESS_DISABLED) { @@ -1018,12 +1024,31 @@ public final class DefaultAudioSink implements AudioSink { return false; } - private AudioTrack buildAudioTrack() throws InitializationException { + private AudioTrack buildAudioTrackWithRetry() throws InitializationException { try { - return checkNotNull(configuration) - .buildAudioTrack(tunneling, audioAttributes, audioSessionId); - } catch (InitializationException e) { + return buildAudioTrack(checkNotNull(configuration)); + } catch (InitializationException initialFailure) { + // Retry with a smaller buffer size. + if (configuration.bufferSize > AUDIO_TRACK_SMALLER_BUFFER_RETRY_SIZE) { + Configuration retryConfiguration = + configuration.copyWithBufferSize(AUDIO_TRACK_SMALLER_BUFFER_RETRY_SIZE); + try { + AudioTrack audioTrack = buildAudioTrack(retryConfiguration); + configuration = retryConfiguration; + return audioTrack; + } catch (InitializationException retryFailure) { + initialFailure.addSuppressed(retryFailure); + } + } maybeDisableOffload(); + throw initialFailure; + } + } + + private AudioTrack buildAudioTrack(Configuration configuration) throws InitializationException { + try { + return configuration.buildAudioTrack(tunneling, audioAttributes, audioSessionId); + } catch (InitializationException e) { if (listener != null) { listener.onAudioSinkError(e); } @@ -2129,6 +2154,19 @@ public final class DefaultAudioSink implements AudioSink { this.availableAudioProcessors = availableAudioProcessors; } + public Configuration copyWithBufferSize(int bufferSize) { + return new Configuration( + inputFormat, + inputPcmFrameSize, + outputMode, + outputPcmFrameSize, + outputSampleRate, + outputChannelConfig, + outputEncoding, + bufferSize, + availableAudioProcessors); + } + /** Returns if the configurations are sufficiently compatible to reuse the audio track. */ public boolean canReuseAudioTrack(Configuration audioTrackConfiguration) { return audioTrackConfiguration.outputMode == outputMode