Commit dcbdbe53 by andrewlewis Committed by Oliver Woodman

Fix handling of ad timelines without prerolls

The player's initial PlaylistTimeline has no ad cue points, so its first
MediaPeriodId has no nextAdGroupIndex. When the AdsMediaSource provides its
first timeline, the cue points become known. For the case where a preroll cue
point appeared, the preroll was detected due to isAd changing on the
MediaPeriodId.  For the case where a midroll/postroll cue point appeared, the
MediaPeriodId was actually treated as the same, which leads to keeping the
unclipped original MediaPeriod.

Fix this behavior by checking for nextAdGroupIndex becoming set or decreasing.

PiperOrigin-RevId: 308974490
parent b22783f8
...@@ -2241,13 +2241,18 @@ import java.util.concurrent.atomic.AtomicBoolean; ...@@ -2241,13 +2241,18 @@ import java.util.concurrent.atomic.AtomicBoolean;
// Ensure ad insertion metadata is up to date. // Ensure ad insertion metadata is up to date.
MediaPeriodId periodIdWithAds = MediaPeriodId periodIdWithAds =
queue.resolveMediaPeriodIdForAds(timeline, newPeriodUid, contentPositionForAdResolutionUs); 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 = boolean oldAndNewPeriodIdAreSame =
oldPeriodId.periodUid.equals(newPeriodUid) oldPeriodId.periodUid.equals(newPeriodUid)
&& !oldPeriodId.isAd() && !oldPeriodId.isAd()
&& !periodIdWithAds.isAd(); && !periodIdWithAds.isAd()
// Drop update if we keep playing the same content (MediaPeriod.periodUid are identical) and && earliestCuePointIsUnchangedOrLater;
// only MediaPeriodId.nextAdGroupIndex may have changed. This postpones a potential
// discontinuity until we reach the former next ad group position.
MediaPeriodId newPeriodId = oldAndNewPeriodIdAreSame ? oldPeriodId : periodIdWithAds; MediaPeriodId newPeriodId = oldAndNewPeriodIdAreSame ? oldPeriodId : periodIdWithAds;
long periodPositionUs = contentPositionForAdResolutionUs; long periodPositionUs = contentPositionForAdResolutionUs;
......
...@@ -2401,6 +2401,56 @@ public final class ExoPlayerTest { ...@@ -2401,6 +2401,56 @@ public final class ExoPlayerTest {
} }
@Test @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() public void repeatedSeeksToUnpreparedPeriodInSameWindowKeepsWindowSequenceNumber()
throws Exception { throws Exception {
Timeline timeline = Timeline timeline =
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment