diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayer.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayer.java index fb10d741e9..381393d8b3 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayer.java @@ -423,6 +423,16 @@ public interface ExoPlayer extends Player { *

This method is experimental, and will be renamed or removed in a future release. */ default void onExperimentalSleepingForOffloadChanged(boolean sleepingForOffload) {} + + /** + * Called when the value of {@link AudioTrack#isOffloadedPlayback} changes. + * + *

This should not be generally required to be acted upon. But when offload is critical for + * efficiency, or audio features (gapless, playback speed), this will let the app know. + * + *

This method is experimental, and will be renamed or removed in a future release. + */ + default void onExperimentalOffloadedPlayback(boolean offloadedPlayback) {} } /** 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 47fe55eafc..b098fa4d4d 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 @@ -39,6 +39,7 @@ import androidx.annotation.IntDef; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.ExoPlayer.AudioOffloadListener; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.analytics.PlayerId; @@ -265,6 +266,7 @@ public final class DefaultAudioSink implements AudioSink { private boolean enableAudioTrackPlaybackParams; private int offloadMode; AudioTrackBufferSizeProvider audioTrackBufferSizeProvider; + @Nullable AudioOffloadListener audioOffloadListener; /** Creates a new builder. */ public Builder() { @@ -370,6 +372,19 @@ public final class DefaultAudioSink implements AudioSink { return this; } + /** + * Sets an optional {@link AudioOffloadListener} to receive events relevant to offloaded + * playback. + * + *

The default value is null. + */ + @CanIgnoreReturnValue + public Builder setExperimentalAudioOffloadListener( + @Nullable AudioOffloadListener audioOffloadListener) { + this.audioOffloadListener = audioOffloadListener; + return this; + } + /** Builds the {@link DefaultAudioSink}. Must only be called once per Builder instance. */ public DefaultAudioSink build() { if (audioProcessorChain == null) { @@ -500,6 +515,7 @@ public final class DefaultAudioSink implements AudioSink { initializationExceptionPendingExceptionHolder; private final PendingExceptionHolder writeExceptionPendingExceptionHolder; private final AudioTrackBufferSizeProvider audioTrackBufferSizeProvider; + @Nullable private final AudioOffloadListener audioOffloadListener; @Nullable private PlayerId playerId; @Nullable private Listener listener; @@ -660,6 +676,7 @@ public final class DefaultAudioSink implements AudioSink { new PendingExceptionHolder<>(AUDIO_TRACK_RETRY_DURATION_MS); writeExceptionPendingExceptionHolder = new PendingExceptionHolder<>(AUDIO_TRACK_RETRY_DURATION_MS); + audioOffloadListener = builder.audioOffloadListener; } // AudioSink implementation. @@ -1087,7 +1104,12 @@ public final class DefaultAudioSink implements AudioSink { private AudioTrack buildAudioTrack(Configuration configuration) throws InitializationException { try { - return configuration.buildAudioTrack(tunneling, audioAttributes, audioSessionId); + AudioTrack audioTrack = + configuration.buildAudioTrack(tunneling, audioAttributes, audioSessionId); + if (audioOffloadListener != null) { + audioOffloadListener.onExperimentalOffloadedPlayback(isOffloadedPlayback(audioTrack)); + } + return audioTrack; } catch (InitializationException e) { if (listener != null) { listener.onAudioSinkError(e);