diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 767eb49048..8699d4be69 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -39,6 +39,9 @@ * DataSource: * Audio: * Allow constant power upmixing/downmixing in DefaultAudioMixer. + * Fix offload issue where position might get stuck when playing a playlist + of short content + ([#1920](https://github.com/androidx/media/issues/1920)). * Video: * Add experimental `ExoPlayer` API to include the `MediaCodec.BUFFER_FLAG_DECODE_ONLY` flag when queuing decode-only input diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java index 3f29654723..135d14d2f7 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java @@ -2783,6 +2783,10 @@ import java.util.concurrent.atomic.AtomicBoolean; private void maybeUpdateOffloadScheduling() { // If playing period is audio-only with offload mode preference to enable, then offload // scheduling should be enabled. + if (queue.getPlayingPeriod() != queue.getReadingPeriod()) { + // Do not enable offload scheduling when starting to process the next media item. + return; + } @Nullable MediaPeriodHolder playingPeriodHolder = queue.getPlayingPeriod(); if (playingPeriodHolder != null) { TrackSelectorResult trackSelectorResult = playingPeriodHolder.getTrackSelectorResult(); diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java index 944af0a284..d56c1c23ce 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java @@ -11786,6 +11786,54 @@ public class ExoPlayerTest { player.release(); } + @Test + public void enablingOffload_withFastReadingPeriodAdvancement_playerDoesNotSleep() + throws Exception { + FakeSleepRenderer sleepRenderer = new FakeSleepRenderer(C.TRACK_TYPE_AUDIO); + AtomicInteger sleepingForOffloadCounter = new AtomicInteger(); + ExoPlayer player = + parameterizeTestExoPlayerBuilder( + new TestExoPlayerBuilder(context).setRenderers(sleepRenderer)) + .build(); + ExoPlayer.AudioOffloadListener listener = + new ExoPlayer.AudioOffloadListener() { + @Override + public void onSleepingForOffloadChanged(boolean sleepingForOffload) { + if (sleepingForOffload) { + sleepingForOffloadCounter.getAndIncrement(); + } + } + }; + player.addAudioOffloadListener(listener); + // Set a playlist of multiple, short audio-only items such that the reading period quickly + // advances past the playing period. + Timeline timeline = new FakeTimeline(); + player.setMediaSources( + ImmutableList.of( + new FakeMediaSource(timeline, ExoPlayerTestRunner.AUDIO_FORMAT), + new FakeMediaSource(timeline, ExoPlayerTestRunner.AUDIO_FORMAT), + new FakeMediaSource(timeline, ExoPlayerTestRunner.AUDIO_FORMAT))); + player.setTrackSelectionParameters( + player + .getTrackSelectionParameters() + .buildUpon() + .setAudioOffloadPreferences( + new AudioOffloadPreferences.Builder() + .setAudioOffloadMode(AudioOffloadPreferences.AUDIO_OFFLOAD_MODE_REQUIRED) + .build()) + .build()); + player.prepare(); + player.play(); + advance(player).untilStartOfMediaItem(/* mediaItemIndex= */ 1); + + sleepRenderer.sleepOnNextRender(); + runUntilPlaybackState(player, Player.STATE_ENDED); + + assertThat(sleepingForOffloadCounter.get()).isEqualTo(0); + + player.release(); + } + @Test public void wakeupListenerWhileSleepingForOffload_isWokenUp_renderingResumes() throws Exception { FakeSleepRenderer sleepRenderer = new FakeSleepRenderer(C.TRACK_TYPE_AUDIO).sleepOnNextRender();