diff --git a/RELEASENOTES.md b/RELEASENOTES.md index d170f13c35..57573c24fe 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -33,6 +33,9 @@ * Fix issue that OPUS and VORBIS channel layouts are wrong for 3, 5, 6, 7 and 8 channels ([#8396](https://github.com/google/ExoPlayer/issues/8396)). + * Fix issue where track selections after seek to zero in a live stream + incorrectly let the stream start at its default position + ([#9347](https://github.com/google/ExoPlayer/issues/9347)). * Transformer: * Add support for flattening H.265/HEVC SEF slow motion videos. * Increase transmuxing speed, especially for 'remove video' edits. diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MaskingMediaPeriod.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MaskingMediaPeriod.java index 8be27948a7..f2ba4bb1d3 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MaskingMediaPeriod.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MaskingMediaPeriod.java @@ -183,8 +183,8 @@ public final class MaskingMediaPeriod implements MediaPeriod, MediaPeriod.Callba long positionUs) { if (preparePositionOverrideUs != C.TIME_UNSET && positionUs == preparePositionUs) { positionUs = preparePositionOverrideUs; - preparePositionOverrideUs = C.TIME_UNSET; } + preparePositionOverrideUs = C.TIME_UNSET; return castNonNull(mediaPeriod) .selectTracks(selections, mayRetainStreamFlags, streams, streamResetFlags, positionUs); } 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 e75a0d6ad9..9a7e38dfd8 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java @@ -122,6 +122,7 @@ import androidx.media3.common.Player.PositionInfo; import androidx.media3.common.Timeline; import androidx.media3.common.Timeline.Window; import androidx.media3.common.TrackGroup; +import androidx.media3.common.TrackSelectionParameters; import androidx.media3.common.TrackSelectionParameters.AudioOffloadPreferences; import androidx.media3.common.Tracks; import androidx.media3.common.VideoSize; @@ -14179,6 +14180,53 @@ public final class ExoPlayerTest { player.release(); } + @Test + public void seekToZeroAndTrackSelection_withNonZeroDefaultPosition_startsPlaybackAtZero() + throws Exception { + // Create a timeline with a non-zero default position. It's important to use a + // windowOffsetInFirstPeriodUs of zero to ensure that our later manual seek to zero could be + // mistaken for the initial placeholder start position of zero + // (see https://github.com/google/ExoPlayer/issues/9347). + Timeline timeline = + new FakeTimeline( + new TimelineWindowDefinition( + /* periodCount= */ 1, + /* id= */ new Object(), + /* isSeekable= */ true, + /* isDynamic= */ true, + /* isLive= */ true, + /* isPlaceholder= */ false, + /* durationUs= */ 10_000_000, + /* defaultPositionUs= */ 9_000_000, + /* windowOffsetInFirstPeriodUs= */ 0, + /* adPlaybackState= */ AdPlaybackState.NONE)); + FakeMediaSource mediaSource = + new FakeMediaSource( + timeline, ExoPlayerTestRunner.VIDEO_FORMAT, ExoPlayerTestRunner.AUDIO_FORMAT); + // Make sure the player has to use its placeholder values initially. + mediaSource.setAllowPreparation(false); + ExoPlayer player = new TestExoPlayerBuilder(context).build(); + player.setMediaSource(mediaSource); + player.prepare(); + runUntilPendingCommandsAreFullyHandled(player); + mediaSource.setAllowPreparation(true); + runUntilPlaybackState(player, Player.STATE_READY); + long positionAfterPrepare = player.getCurrentPosition(); + + // Manually seek back to zero and force to reselect tracks. + player.seekTo(0); + player.setTrackSelectionParameters( + new TrackSelectionParameters.Builder(context) + .setTrackTypeDisabled(C.TRACK_TYPE_AUDIO, /* disabled= */ true) + .build()); + runUntilPendingCommandsAreFullyHandled(player); + long positionAfterSeek = player.getContentPosition(); + player.release(); + + assertThat(positionAfterPrepare).isEqualTo(9000); + assertThat(positionAfterSeek).isEqualTo(0); + } + // Internal methods. private void addWatchAsSystemFeature() {