diff --git a/RELEASENOTES.md b/RELEASENOTES.md index bd2c3752a3..ef3977a527 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -11,6 +11,10 @@ ([#10057](https://github.com/google/ExoPlayer/issues/10057)). * Limit parallel download removals to 1 to avoid excessive thread creation ([#10458](https://github.com/google/ExoPlayer/issues/10458)). +* Audio: + * Adds AudioOffloadListener.onExperimentalOffloadedPlayback for the + AudioTrack offload state. + ([#134](https://github.com/androidx/media/issues/134)). * Metadata: * `MetadataRenderer` can now be configured to render metadata as soon as they are available. Create an instance with diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java index de32120a38..29614aa26a 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java @@ -435,6 +435,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/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioSink.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioSink.java index 9431639668..4d6d7d1f4d 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioSink.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioSink.java @@ -50,6 +50,7 @@ import androidx.media3.common.util.ConditionVariable; import androidx.media3.common.util.Log; import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.Util; +import androidx.media3.exoplayer.ExoPlayer.AudioOffloadListener; import androidx.media3.exoplayer.analytics.PlayerId; import androidx.media3.exoplayer.audio.AudioProcessor.UnhandledAudioFormatException; import androidx.media3.extractor.AacUtil; @@ -274,6 +275,7 @@ public final class DefaultAudioSink implements AudioSink { private boolean enableAudioTrackPlaybackParams; private int offloadMode; AudioTrackBufferSizeProvider audioTrackBufferSizeProvider; + @Nullable AudioOffloadListener audioOffloadListener; /** Creates a new builder. */ public Builder() { @@ -379,6 +381,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) { @@ -509,6 +524,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; @@ -669,6 +685,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. @@ -1096,7 +1113,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);