From a971d09a46a0b3d375b3a4cd10c89ac08268eb9b Mon Sep 17 00:00:00 2001 From: krocard Date: Fri, 26 Jun 2020 09:55:54 +0100 Subject: [PATCH] Add Offload gapless support This it is enabled only on a list of manually tested devices. The list is empty in this CL. *** Reason for rollforward *** Fixed dependent cl was rolled forward. *** Original change description *** Rollback of https://github.com/google/ExoPlayer/commit/962e08d3be3b47166d1628cd1951e115c5cc00be *** Original commit *** Add Offload gapless support Confirmed to work on a Pixel 4 after enabling the feature: `setprop vendor.audio.offload.gapless.enabled true` *** *** PiperOrigin-RevId: 318433123 --- .../exoplayer2/audio/DefaultAudioSink.java | 45 ++++++++++++++++--- 1 file changed, 40 insertions(+), 5 deletions(-) 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 6f0a3a96af..567169f5be 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 @@ -432,7 +432,12 @@ public final class DefaultAudioSink implements AudioSink { } if (enableOffload && isOffloadedPlaybackSupported( - format.channelCount, format.sampleRate, encoding, audioAttributes)) { + format.channelCount, + format.sampleRate, + encoding, + audioAttributes, + format.encoderDelay, + format.encoderPadding)) { return true; } return isPassthroughPlaybackSupported(encoding, format.channelCount); @@ -516,7 +521,13 @@ public final class DefaultAudioSink implements AudioSink { boolean useOffload = enableOffload && !isInputPcm - && isOffloadedPlaybackSupported(channelCount, sampleRate, encoding, audioAttributes); + && isOffloadedPlaybackSupported( + channelCount, + sampleRate, + encoding, + audioAttributes, + trimStartFrames, + trimEndFrames); Configuration pendingConfiguration = new Configuration( @@ -531,6 +542,8 @@ public final class DefaultAudioSink implements AudioSink { processingEnabled, canApplyPlaybackParameters, availableAudioProcessors, + trimStartFrames, + trimEndFrames, useOffload); if (isInitialized()) { this.pendingConfiguration = pendingConfiguration; @@ -576,6 +589,7 @@ public final class DefaultAudioSink implements AudioSink { .buildAudioTrack(tunneling, audioAttributes, audioSessionId); if (isOffloadedPlayback(audioTrack)) { registerStreamEventCallbackV29(audioTrack); + audioTrack.setOffloadDelayPadding(configuration.trimStartFrames, configuration.trimEndFrames); } int audioSessionId = audioTrack.getAudioSessionId(); if (enablePreV21AudioSessionWorkaround) { @@ -653,6 +667,11 @@ public final class DefaultAudioSink implements AudioSink { // The current audio track can be reused for the new configuration. configuration = pendingConfiguration; pendingConfiguration = null; + if (isOffloadedPlayback(audioTrack)) { + audioTrack.setOffloadEndOfStream(); + audioTrack.setOffloadDelayPadding( + configuration.trimStartFrames, configuration.trimEndFrames); + } } // Re-apply playback parameters. applyPlaybackSpeedAndSkipSilence(presentationTimeUs); @@ -1255,14 +1274,24 @@ public final class DefaultAudioSink implements AudioSink { int channelCount, int sampleRateHz, @C.Encoding int encoding, - AudioAttributes audioAttributes) { + AudioAttributes audioAttributes, + int trimStartFrames, + int trimEndFrames) { if (Util.SDK_INT < 29) { return false; } int channelMask = getChannelConfig(channelCount, /* isInputPcm= */ false); AudioFormat audioFormat = getAudioFormat(sampleRateHz, channelMask, encoding); - return AudioManager.isOffloadedPlaybackSupported( - audioFormat, audioAttributes.getAudioAttributesV21()); + if (!AudioManager.isOffloadedPlaybackSupported( + audioFormat, audioAttributes.getAudioAttributesV21())) { + return false; + } + if (trimStartFrames > 0 || trimEndFrames > 0) { + // TODO(internal b/158191844): Gapless offload is not supported by all devices and there is no + // API to query its support. + return false; + } + return true; } private static boolean isOffloadedPlayback(AudioTrack audioTrack) { @@ -1579,6 +1608,8 @@ public final class DefaultAudioSink implements AudioSink { public final boolean processingEnabled; public final boolean canApplyPlaybackParameters; public final AudioProcessor[] availableAudioProcessors; + public int trimStartFrames; + public int trimEndFrames; public final boolean useOffload; public Configuration( @@ -1593,6 +1624,8 @@ public final class DefaultAudioSink implements AudioSink { boolean processingEnabled, boolean canApplyPlaybackParameters, AudioProcessor[] availableAudioProcessors, + int trimStartFrames, + int trimEndFrames, boolean useOffload) { this.isInputPcm = isInputPcm; this.inputPcmFrameSize = inputPcmFrameSize; @@ -1604,6 +1637,8 @@ public final class DefaultAudioSink implements AudioSink { this.processingEnabled = processingEnabled; this.canApplyPlaybackParameters = canApplyPlaybackParameters; this.availableAudioProcessors = availableAudioProcessors; + this.trimStartFrames = trimStartFrames; + this.trimEndFrames = trimEndFrames; this.useOffload = useOffload; // Call computeBufferSize() last as it depends on the other configuration values.