Reset prewarming renderers when seek resets prewarming media period

Seeking will reset media periods following the current playing period. This includes resetting any current pre-warming media periods. Therefore while seeking, any current pre-warming should be disabled and reset.

#cherrypick

PiperOrigin-RevId: 723439145
This commit is contained in:
michaelkatz 2025-02-05 03:12:13 -08:00 committed by Copybara-Service
parent f8f66bdfaa
commit 97a1d31b5d
3 changed files with 61 additions and 0 deletions

View File

@ -6,6 +6,8 @@
* ExoPlayer: * ExoPlayer:
* Add option to `ClippingMediaSource` to allow clipping in unseekable * Add option to `ClippingMediaSource` to allow clipping in unseekable
media. media.
* Fix bug where seeking with pre-warming could block following media item
transition.
* Transformer: * Transformer:
* Track Selection: * Track Selection:
* Extractors: * Extractors:

View File

@ -1544,6 +1544,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
} }
} }
// Disable pre-warming as following logic will reset any pre-warming media periods.
disableAndResetPrewarmingRenderers();
// Do the actual seeking. // Do the actual seeking.
if (newPlayingPeriodHolder != null) { if (newPlayingPeriodHolder != null) {
queue.removeAfter(newPlayingPeriodHolder); queue.removeAfter(newPlayingPeriodHolder);

View File

@ -620,6 +620,57 @@ public class ExoPlayerWithPrewarmingRenderersTest {
assertThat(selectedAudioTrack.get()).isNull(); assertThat(selectedAudioTrack.get()).isNull();
} }
@Test
public void
seek_intoCurrentPeriodWithPrimaryBeforeReadingPeriodAdvanced_resetsPrewarmingRenderer()
throws Exception {
Clock fakeClock = new FakeClock(/* isAutoAdvancing= */ true);
ExoPlayer player =
new TestExoPlayerBuilder(context)
.setClock(fakeClock)
.setRenderersFactory(
new FakeRenderersFactorySupportingSecondaryVideoRenderer(fakeClock))
.build();
Renderer videoRenderer = player.getRenderer(/* index= */ 0);
Renderer secondaryVideoRenderer = player.getSecondaryRenderer(/* index= */ 0);
// Set a playlist that allows a new renderer to be enabled early.
player.setMediaSources(
ImmutableList.of(
// Use FakeBlockingMediaSource so that reading period is not advanced when pre-warming.
new FakeBlockingMediaSource(new FakeTimeline(), ExoPlayerTestRunner.VIDEO_FORMAT),
new FakeMediaSource(new FakeTimeline(), ExoPlayerTestRunner.VIDEO_FORMAT),
new FakeMediaSource(
new FakeTimeline(),
ExoPlayerTestRunner.VIDEO_FORMAT,
ExoPlayerTestRunner.AUDIO_FORMAT)));
player.prepare();
// Play a bit until the second renderer is pre-warming.
player.play();
run(player)
.untilBackgroundThreadCondition(
() -> secondaryVideoRenderer.getState() == Renderer.STATE_ENABLED);
@Renderer.State int videoState1 = videoRenderer.getState();
@Renderer.State int secondaryVideoState1 = secondaryVideoRenderer.getState();
SampleStream secondaryVideoStream1 = secondaryVideoRenderer.getStream();
// Seek to position in current period.
player.seekTo(/* mediaItemIndex= */ 0, /* positionMs= */ 3000);
run(player).untilPendingCommandsAreFullyHandled();
run(player)
.untilBackgroundThreadCondition(
() -> secondaryVideoRenderer.getState() == Renderer.STATE_ENABLED);
@Renderer.State int videoState2 = videoRenderer.getState();
@Renderer.State int secondaryVideoState2 = secondaryVideoRenderer.getState();
SampleStream secondaryVideoStream2 = secondaryVideoRenderer.getStream();
player.release();
assertThat(videoState1).isEqualTo(Renderer.STATE_STARTED);
assertThat(secondaryVideoState1).isEqualTo(Renderer.STATE_ENABLED);
assertThat(videoState2).isEqualTo(Renderer.STATE_STARTED);
assertThat(secondaryVideoState2).isEqualTo(Renderer.STATE_ENABLED);
assertThat(secondaryVideoStream1).isNotEqualTo(secondaryVideoStream2);
}
@Test @Test
public void public void
seek_intoCurrentPeriodWithSecondaryBeforeReadingPeriodAdvanced_doesNotSwapToPrimaryRenderer() seek_intoCurrentPeriodWithSecondaryBeforeReadingPeriodAdvanced_doesNotSwapToPrimaryRenderer()
@ -650,9 +701,13 @@ public class ExoPlayerWithPrewarmingRenderersTest {
run(player).untilPendingCommandsAreFullyHandled(); run(player).untilPendingCommandsAreFullyHandled();
@Renderer.State int videoState1 = videoRenderer.getState(); @Renderer.State int videoState1 = videoRenderer.getState();
@Renderer.State int secondaryVideoState1 = secondaryVideoRenderer.getState(); @Renderer.State int secondaryVideoState1 = secondaryVideoRenderer.getState();
SampleStream videoStream1 = videoRenderer.getStream();
// Seek to position in current period. // Seek to position in current period.
player.seekTo(/* mediaItemIndex= */ 1, /* positionMs= */ 3000); player.seekTo(/* mediaItemIndex= */ 1, /* positionMs= */ 3000);
run(player).untilPendingCommandsAreFullyHandled(); run(player).untilPendingCommandsAreFullyHandled();
run(player)
.untilBackgroundThreadCondition(() -> videoRenderer.getState() == Renderer.STATE_ENABLED);
SampleStream videoStream2 = videoRenderer.getStream();
@Renderer.State int videoState2 = videoRenderer.getState(); @Renderer.State int videoState2 = videoRenderer.getState();
@Renderer.State int secondaryVideoState2 = secondaryVideoRenderer.getState(); @Renderer.State int secondaryVideoState2 = secondaryVideoRenderer.getState();
player.release(); player.release();
@ -661,6 +716,7 @@ public class ExoPlayerWithPrewarmingRenderersTest {
assertThat(secondaryVideoState1).isEqualTo(Renderer.STATE_STARTED); assertThat(secondaryVideoState1).isEqualTo(Renderer.STATE_STARTED);
assertThat(videoState2).isEqualTo(Renderer.STATE_ENABLED); assertThat(videoState2).isEqualTo(Renderer.STATE_ENABLED);
assertThat(secondaryVideoState2).isEqualTo(Renderer.STATE_STARTED); assertThat(secondaryVideoState2).isEqualTo(Renderer.STATE_STARTED);
assertThat(videoStream1).isNotEqualTo(videoStream2);
} }
@Test @Test