diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java index 02e271e7a4..53690d8a98 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java @@ -2241,13 +2241,18 @@ import java.util.concurrent.atomic.AtomicBoolean; // Ensure ad insertion metadata is up to date. MediaPeriodId periodIdWithAds = queue.resolveMediaPeriodIdForAds(timeline, newPeriodUid, contentPositionForAdResolutionUs); + boolean earliestCuePointIsUnchangedOrLater = + periodIdWithAds.nextAdGroupIndex == C.INDEX_UNSET + || (oldPeriodId.nextAdGroupIndex != C.INDEX_UNSET + && periodIdWithAds.adGroupIndex >= oldPeriodId.nextAdGroupIndex); + // Drop update if we keep playing the same content (MediaPeriod.periodUid are identical) and + // the only change is that MediaPeriodId.nextAdGroupIndex increased. This postpones a potential + // discontinuity until we reach the former next ad group position. boolean oldAndNewPeriodIdAreSame = oldPeriodId.periodUid.equals(newPeriodUid) && !oldPeriodId.isAd() - && !periodIdWithAds.isAd(); - // Drop update if we keep playing the same content (MediaPeriod.periodUid are identical) and - // only MediaPeriodId.nextAdGroupIndex may have changed. This postpones a potential - // discontinuity until we reach the former next ad group position. + && !periodIdWithAds.isAd() + && earliestCuePointIsUnchangedOrLater; MediaPeriodId newPeriodId = oldAndNewPeriodIdAreSame ? oldPeriodId : periodIdWithAds; long periodPositionUs = contentPositionForAdResolutionUs; 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 d40052a7ca..d8934c0d4e 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 @@ -2400,6 +2400,56 @@ public final class ExoPlayerTest { .isGreaterThan(mediaSource.getCreatedMediaPeriods().get(1).windowSequenceNumber); } + @Test + public void timelineUpdateWithNewMidrollAdCuePoint_dropsPrebufferedPeriod() throws Exception { + Timeline timeline1 = + new FakeTimeline(new TimelineWindowDefinition(/* periodCount= */ 1, /* id= */ 0)); + AdPlaybackState adPlaybackStateWithMidroll = + FakeTimeline.createAdPlaybackState( + /* adsPerAdGroup= */ 1, + /* adGroupTimesUs...= */ TimelineWindowDefinition + .DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US + + 5 * C.MICROS_PER_SECOND); + Timeline timeline2 = + new FakeTimeline( + new TimelineWindowDefinition( + /* periodCount= */ 1, + /* id= */ 0, + /* isSeekable= */ true, + /* isDynamic= */ false, + /* durationUs= */ 10_000_000, + adPlaybackStateWithMidroll)); + FakeMediaSource mediaSource = new FakeMediaSource(timeline1, ExoPlayerTestRunner.VIDEO_FORMAT); + ActionSchedule actionSchedule = + new ActionSchedule.Builder(TAG) + .pause() + .waitForPlaybackState(Player.STATE_READY) + .executeRunnable(() -> mediaSource.setNewSourceInfo(timeline2)) + .waitForTimelineChanged( + timeline2, /* expectedReason= */ Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE) + .play() + .build(); + ExoPlayerTestRunner testRunner = + new ExoPlayerTestRunner.Builder(context) + .setMediaSources(mediaSource) + .setActionSchedule(actionSchedule) + .build() + .start() + .blockUntilEnded(TIMEOUT_MS); + + testRunner.assertTimelineChangeReasonsEqual( + Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, + Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE, + Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE); + testRunner.assertPlayedPeriodIndices(0); + assertThat(mediaSource.getCreatedMediaPeriods()).hasSize(4); + assertThat(mediaSource.getCreatedMediaPeriods().get(0).nextAdGroupIndex) + .isEqualTo(C.INDEX_UNSET); + assertThat(mediaSource.getCreatedMediaPeriods().get(1).nextAdGroupIndex).isEqualTo(0); + assertThat(mediaSource.getCreatedMediaPeriods().get(2).adGroupIndex).isEqualTo(0); + assertThat(mediaSource.getCreatedMediaPeriods().get(3).adGroupIndex).isEqualTo(C.INDEX_UNSET); + } + @Test public void repeatedSeeksToUnpreparedPeriodInSameWindowKeepsWindowSequenceNumber() throws Exception {