From 2f8779baff9cf139d3993903301eb6c288f2b969 Mon Sep 17 00:00:00 2001 From: Steve Mayhew Date: Thu, 2 Sep 2021 11:30:31 -0700 Subject: [PATCH 1/2] TrackSelection does not jump to live with HlsMediaPeriod This is a fix for issue #9347 Once the MaskingMediaPeriod completes `prepare()` (when the masked HlsMediaPeriod reports the first real Timeline with a duration and window start position) and the `preparePositionUs` is used by the player in the `onPrepared()` callback to set the render position it should not use it again. The bug is a track selection causes a jump to live if you are at position 0 in the timeline, even after playback was started from the live point (the prepare position override). --- .../google/android/exoplayer2/source/MaskingMediaPeriod.java | 4 ---- .../google/android/exoplayer2/source/MaskingMediaSource.java | 5 ----- 2 files changed, 9 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/MaskingMediaPeriod.java b/library/core/src/main/java/com/google/android/exoplayer2/source/MaskingMediaPeriod.java index 7c60a379c7f..40bbb786f69 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/MaskingMediaPeriod.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/MaskingMediaPeriod.java @@ -178,10 +178,6 @@ public long selectTracks( @NullableType SampleStream[] streams, boolean[] streamResetFlags, long positionUs) { - if (preparePositionOverrideUs != C.TIME_UNSET && positionUs == preparePositionUs) { - positionUs = preparePositionOverrideUs; - preparePositionOverrideUs = C.TIME_UNSET; - } return castNonNull(mediaPeriod) .selectTracks(selections, mayRetainStreamFlags, streams, streamResetFlags, positionUs); } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/MaskingMediaSource.java b/library/core/src/main/java/com/google/android/exoplayer2/source/MaskingMediaSource.java index c1744ed57a6..d787af15438 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/MaskingMediaSource.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/MaskingMediaSource.java @@ -140,11 +140,6 @@ protected void onChildSourceInfoRefreshed( @Nullable MediaPeriodId idForMaskingPeriodPreparation = null; if (isPrepared) { timeline = timeline.cloneWithUpdatedTimeline(newTimeline); - if (unpreparedMaskingMediaPeriod != null) { - // Reset override in case the duration changed and we need to update our override. - setPreparePositionOverrideToUnpreparedMaskingPeriod( - unpreparedMaskingMediaPeriod.getPreparePositionOverrideUs()); - } } else if (newTimeline.isEmpty()) { timeline = hasRealTimeline From 9f9621d99ab98d9ae800db4d934bc67c62f5f355 Mon Sep 17 00:00:00 2001 From: Steve Mayhew Date: Wed, 8 Sep 2021 10:53:40 -0700 Subject: [PATCH 2/2] Tests trackselection does not seek to default period start Test case fails without the change to `MaskingMediaPeriod` so it's `selectTracks` method does not force a seek to the override start position. --- .../android/exoplayer2/ExoPlayerTest.java | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java b/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java index 8ec85152fd5..b464f85ae29 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java @@ -3249,6 +3249,77 @@ public void run(SimpleExoPlayer player) { assertThat(positionWhenReady.get()).isEqualTo(10); } + @Test + public void trackSelectionDoesNotResetToDefaultPeriodPosition() + throws Exception { + FakeMediaSource mediaSource = + new FakeMediaSource(new FakeTimeline(), ExoPlayerTestRunner.VIDEO_FORMAT, ExoPlayerTestRunner.AUDIO_FORMAT); + FakeTrackSelector trackSelector = new FakeTrackSelector(); + FakeRenderer videoRenderer = new FakeRenderer(C.TRACK_TYPE_VIDEO); + FakeRenderer audioRenderer = new FakeRenderer(C.TRACK_TYPE_AUDIO); + MediaItem mediaItem = + new MediaItem.Builder().setUri(Uri.EMPTY).build(); + final long windowDefaultStartPositionUs = 5 * C.MICROS_PER_SECOND; + final AtomicLong expectedPosition = new AtomicLong(C.TIME_UNSET); + final int lastSeekPosition = 0; + + Timeline liveTimeline = + new SinglePeriodTimeline( + /* presentationStartTimeMs= */ C.TIME_UNSET, + /* windowStartTimeMs= */ C.TIME_UNSET, + /* elapsedRealtimeEpochOffsetMs= */ C.TIME_UNSET, + /* periodDurationUs= */ 10 * C.MICROS_PER_SECOND, + /* windowDurationUs= */ 10 * C.MICROS_PER_SECOND, + /* windowPositionInPeriodUs= */ 0, + /* windowDefaultStartPositionUs= */ windowDefaultStartPositionUs, + /* isSeekable= */ true, + /* isDynamic= */ true, + /* manifest= */ null, + mediaItem, + mediaItem.liveConfiguration); + + ActionSchedule actionSchedule = + new ActionSchedule.Builder(TAG) + .pause() + .waitForPlaybackState(Player.STATE_BUFFERING) + // Finish preparation. + .executeRunnable(() -> mediaSource.setNewSourceInfo(liveTimeline)) + .waitForTimelineChanged() + .waitForPlaybackState(Player.STATE_READY) + .play() + .playUntilPosition(0, windowDefaultStartPositionUs) + .pause() + .seekAndWait(lastSeekPosition) // paused at this position, should not change from here + .executeRunnable(() -> { + DefaultTrackSelector.Parameters parameters = trackSelector.getParameters(); + trackSelector.setParameters(parameters.buildUpon() + .setMaxAudioBitrate(0) // Cause track selection change + .build() + ); + }) + .waitForPendingPlayerCommands() + .executeRunnable(new PlayerRunnable() { + @Override + public void run(SimpleExoPlayer player) { + expectedPosition.set(player.getCurrentPosition()); + } + }) + .stop() // End live playback + .build(); + + new ExoPlayerTestRunner.Builder(context) + .setMediaSources(mediaSource) + .setTrackSelector(trackSelector) + .setRenderers(audioRenderer, videoRenderer) + .setActionSchedule(actionSchedule) + .setUseLazyPreparation(true) // defer prepare until first source update (this is default) + .build() + .start() + .blockUntilEnded(TIMEOUT_MS); + + assertThat(expectedPosition.get()).isEqualTo(lastSeekPosition); + } + @Test public void seekToUnpreparedWindowWithMultiplePeriodsInConcatenationStartsAtCorrectPeriod() throws Exception {