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
This commit is contained in:
krocard 2022-01-31 11:29:21 +00:00 committed by Ian Baker
parent bdd64ce6a1
commit 82ee82635b
2 changed files with 45 additions and 5 deletions

View File

@ -58,6 +58,8 @@
* Allow customization of the `AudioTrack` buffer size calculation by * Allow customization of the `AudioTrack` buffer size calculation by
injecting an `AudioTrackBufferSizeProvider` to `DefaultAudioSink`. injecting an `AudioTrackBufferSizeProvider` to `DefaultAudioSink`.
([#8891](https://github.com/google/ExoPlayer/issues/8891)). ([#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: * Extractors:
* Fix inconsistency with spec in H.265 SPS nal units parsing * Fix inconsistency with spec in H.265 SPS nal units parsing
([#9719](https://github.com/google/ExoPlayer/issues/9719)). ([#9719](https://github.com/google/ExoPlayer/issues/9719)).

View File

@ -69,6 +69,12 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
*/ */
public final class DefaultAudioSink implements AudioSink { 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 * Thrown when the audio track has provided a spurious timestamp, if {@link
* #failOnSpuriousAudioTimestamp} is set. * #failOnSpuriousAudioTimestamp} is set.
@ -824,7 +830,7 @@ public final class DefaultAudioSink implements AudioSink {
// initialization of the audio track to fail. // initialization of the audio track to fail.
releasingConditionVariable.block(); releasingConditionVariable.block();
audioTrack = buildAudioTrack(); audioTrack = buildAudioTrackWithRetry();
if (isOffloadedPlayback(audioTrack)) { if (isOffloadedPlayback(audioTrack)) {
registerStreamEventCallbackV29(audioTrack); registerStreamEventCallbackV29(audioTrack);
if (offloadMode != OFFLOAD_MODE_ENABLED_GAPLESS_DISABLED) { if (offloadMode != OFFLOAD_MODE_ENABLED_GAPLESS_DISABLED) {
@ -1018,12 +1024,31 @@ public final class DefaultAudioSink implements AudioSink {
return false; return false;
} }
private AudioTrack buildAudioTrack() throws InitializationException { private AudioTrack buildAudioTrackWithRetry() throws InitializationException {
try { try {
return checkNotNull(configuration) return buildAudioTrack(checkNotNull(configuration));
.buildAudioTrack(tunneling, audioAttributes, audioSessionId); } catch (InitializationException initialFailure) {
} catch (InitializationException e) { // 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(); maybeDisableOffload();
throw initialFailure;
}
}
private AudioTrack buildAudioTrack(Configuration configuration) throws InitializationException {
try {
return configuration.buildAudioTrack(tunneling, audioAttributes, audioSessionId);
} catch (InitializationException e) {
if (listener != null) { if (listener != null) {
listener.onAudioSinkError(e); listener.onAudioSinkError(e);
} }
@ -2129,6 +2154,19 @@ public final class DefaultAudioSink implements AudioSink {
this.availableAudioProcessors = availableAudioProcessors; 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. */ /** Returns if the configurations are sufficiently compatible to reuse the audio track. */
public boolean canReuseAudioTrack(Configuration audioTrackConfiguration) { public boolean canReuseAudioTrack(Configuration audioTrackConfiguration) {
return audioTrackConfiguration.outputMode == outputMode return audioTrackConfiguration.outputMode == outputMode