Skip played live ad media periods
This change makes sure that live ad periods that are played are skip when attempted to be added to the queue. To make this work the existing filter logic had to be take into account the content resume offset that live periods use. PiperOrigin-RevId: 518068138
This commit is contained in:
parent
fca9197e1c
commit
0a2a20d4fb
@ -441,7 +441,8 @@ public class TimelineTest {
|
||||
/* nowUs= */ 60_000_000,
|
||||
/* adSequencePattern= */ new boolean[] {false, true, true},
|
||||
/* isContentTimeline= */ false,
|
||||
/* populateAds= */ true);
|
||||
/* populateAds= */ true,
|
||||
/* playedAds= */ false);
|
||||
|
||||
assertThat(timeline.getPeriodCount()).isEqualTo(4);
|
||||
assertThat(
|
||||
|
@ -474,13 +474,10 @@ import com.google.common.collect.ImmutableList;
|
||||
Timeline.Period period) {
|
||||
timeline.getPeriodByUid(periodUid, period);
|
||||
timeline.getWindow(period.windowIndex, window);
|
||||
int periodIndex = timeline.getIndexOfPeriod(periodUid);
|
||||
// Skip ignorable server side inserted ad periods.
|
||||
while ((period.durationUs == 0
|
||||
&& period.getAdGroupCount() > 0
|
||||
&& period.isServerSideInsertedAdGroup(period.getRemovedAdGroupCount())
|
||||
&& period.getAdGroupIndexForPositionUs(0) == C.INDEX_UNSET)
|
||||
&& periodIndex++ < window.lastPeriodIndex) {
|
||||
for (int periodIndex = timeline.getIndexOfPeriod(periodUid);
|
||||
isSkippableAdPeriod(period) && periodIndex <= window.lastPeriodIndex;
|
||||
periodIndex++) {
|
||||
timeline.getPeriod(periodIndex, period, /* setIds= */ true);
|
||||
periodUid = checkNotNull(period.uid);
|
||||
}
|
||||
@ -495,6 +492,26 @@ import com.google.common.collect.ImmutableList;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isSkippableAdPeriod(Timeline.Period period) {
|
||||
int adGroupCount = period.getAdGroupCount();
|
||||
if (adGroupCount == 0
|
||||
|| (adGroupCount == 1 && period.isLivePostrollPlaceholder(/* adGroupIndex= */ 0))
|
||||
|| !period.isServerSideInsertedAdGroup(period.getRemovedAdGroupCount())
|
||||
|| period.getAdGroupIndexForPositionUs(0L) != C.INDEX_UNSET) {
|
||||
return false;
|
||||
}
|
||||
if (period.durationUs == 0) {
|
||||
return true;
|
||||
}
|
||||
long contentResumeOffsetUs = 0;
|
||||
int lastIndexInclusive =
|
||||
adGroupCount - (period.isLivePostrollPlaceholder(adGroupCount - 1) ? 2 : 1);
|
||||
for (int i = 0; i <= lastIndexInclusive; i++) {
|
||||
contentResumeOffsetUs += period.getContentResumeOffsetUs(/* adGroupIndex= */ i);
|
||||
}
|
||||
return period.durationUs <= contentResumeOffsetUs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the specified timeline period and position to a {@link MediaPeriodId} that should be
|
||||
* played after a period position change, returning an identifier for an ad group if one needs to
|
||||
|
@ -438,7 +438,8 @@ public final class MediaPeriodQueueTest {
|
||||
/* nowUs= */ 110_000_000,
|
||||
new boolean[] {false, true, true},
|
||||
/* isContentTimeline= */ true,
|
||||
/* populateAds= */ false);
|
||||
/* populateAds= */ false,
|
||||
/* playedAds= */ false);
|
||||
setupTimeline(multiPeriodLiveTimeline);
|
||||
assertGetNextMediaPeriodInfoReturnsContentMediaPeriod(
|
||||
/* periodUid= */ firstPeriodUid,
|
||||
@ -504,7 +505,8 @@ public final class MediaPeriodQueueTest {
|
||||
/* nowUs= */ 110_000_000,
|
||||
new boolean[] {false, true, true},
|
||||
/* isContentTimeline= */ false,
|
||||
/* populateAds= */ false);
|
||||
/* populateAds= */ false,
|
||||
/* playedAds= */ false);
|
||||
setupTimeline(multiPeriodLiveTimeline);
|
||||
assertGetNextMediaPeriodInfoReturnsContentMediaPeriod(
|
||||
/* periodUid= */ firstPeriodUid,
|
||||
@ -569,7 +571,8 @@ public final class MediaPeriodQueueTest {
|
||||
/* nowUs= */ 110_000_000,
|
||||
new boolean[] {false, true, true},
|
||||
/* isContentTimeline= */ false,
|
||||
/* populateAds= */ true);
|
||||
/* populateAds= */ true,
|
||||
/* playedAds= */ false);
|
||||
setupTimeline(multiPeriodLiveTimeline);
|
||||
assertGetNextMediaPeriodInfoReturnsContentMediaPeriod(
|
||||
/* periodUid= */ firstPeriodUid,
|
||||
@ -638,6 +641,47 @@ public final class MediaPeriodQueueTest {
|
||||
assertThat(getNextMediaPeriodInfo()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void getNextMediaPeriodInfo_multiPeriodTimelineWithPlayedAdsAndWithPostRollPlaceHolder() {
|
||||
long contentPeriodDurationUs = FakeMultiPeriodLiveTimeline.PERIOD_DURATION_US;
|
||||
FakeMultiPeriodLiveTimeline multiPeriodLiveTimeline =
|
||||
new FakeMultiPeriodLiveTimeline(
|
||||
/* availabilityStartTimeUs= */ 0,
|
||||
/* liveWindowDurationUs= */ 60_000_000,
|
||||
/* nowUs= */ 110_000_000,
|
||||
new boolean[] {false, true, true},
|
||||
/* isContentTimeline= */ false,
|
||||
/* populateAds= */ true,
|
||||
/* playedAds= */ true);
|
||||
setupTimeline(multiPeriodLiveTimeline);
|
||||
assertGetNextMediaPeriodInfoReturnsContentMediaPeriod(
|
||||
/* periodUid= */ firstPeriodUid,
|
||||
/* startPositionUs= */ 0,
|
||||
/* requestedContentPositionUs= */ C.TIME_UNSET,
|
||||
/* endPositionUs= */ C.TIME_UNSET,
|
||||
/* durationUs= */ contentPeriodDurationUs,
|
||||
/* isFollowedByTransitionToSameStream= */ false,
|
||||
/* isLastInPeriod= */ false,
|
||||
/* isLastInWindow= */ false,
|
||||
/* isFinal= */ false,
|
||||
/* nextAdGroupIndex= */ 0);
|
||||
advance();
|
||||
assertGetNextMediaPeriodInfoReturnsContentMediaPeriod(
|
||||
new Pair<Object, Object>(((Pair<Object, Object>) firstPeriodUid).first, "uid-6[c]"),
|
||||
/* startPositionUs= */ 0,
|
||||
/* requestedContentPositionUs= */ 0,
|
||||
/* endPositionUs= */ C.TIME_UNSET,
|
||||
/* durationUs= */ C.TIME_UNSET, // Last period in stream.
|
||||
/* isFollowedByTransitionToSameStream= */ false,
|
||||
/* isLastInPeriod= */ false,
|
||||
/* isLastInWindow= */ false,
|
||||
/* isFinal= */ false,
|
||||
/* nextAdGroupIndex= */ 0);
|
||||
advance();
|
||||
assertThat(getNextMediaPeriodInfo()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getNextMediaPeriodInfo_withPostrollLoadError_returnsEmptyFinalMediaPeriodInfo() {
|
||||
setupAdTimeline(/* adGroupTimesUs...= */ C.TIME_END_OF_SOURCE);
|
||||
|
@ -566,7 +566,8 @@ public class ImaUtilTest {
|
||||
/* nowUs= */ 150_000_000,
|
||||
/* adSequencePattern= */ new boolean[] {false, true, true},
|
||||
/* isContentTimeline= */ true,
|
||||
/* populateAds= */ false);
|
||||
/* populateAds= */ false,
|
||||
/* playedAds= */ false);
|
||||
// Ad event received from SDK around 130s.
|
||||
adPlaybackState =
|
||||
addLiveAdBreak(
|
||||
@ -855,7 +856,8 @@ public class ImaUtilTest {
|
||||
/* nowUs= */ 59_999_999,
|
||||
/* adSequencePattern= */ new boolean[] {false, true, true},
|
||||
/* isContentTimeline= */ true,
|
||||
/* populateAds= */ false);
|
||||
/* populateAds= */ false,
|
||||
/* playedAds= */ false);
|
||||
// Ad event received from SDK around 30s.
|
||||
adPlaybackState =
|
||||
addLiveAdBreak(
|
||||
@ -1296,7 +1298,8 @@ public class ImaUtilTest {
|
||||
/* nowUs= */ 150_000_000,
|
||||
/* adSequencePattern= */ new boolean[] {false, true, true},
|
||||
/* isContentTimeline= */ true,
|
||||
/* populateAds= */ false);
|
||||
/* populateAds= */ false,
|
||||
/* playedAds= */ false);
|
||||
AdPlaybackState adPlaybackState =
|
||||
new AdPlaybackState("adsId").withLivePostrollPlaceholderAppended();
|
||||
adPlaybackState =
|
||||
|
@ -57,6 +57,7 @@ public class FakeMultiPeriodLiveTimeline extends Timeline {
|
||||
private final long liveWindowDurationUs;
|
||||
private final boolean isContentTimeline;
|
||||
private final boolean populateAds;
|
||||
private final boolean playedAds;
|
||||
|
||||
private long nowUs;
|
||||
private ImmutableList<PeriodData> periods;
|
||||
@ -72,7 +73,7 @@ public class FakeMultiPeriodLiveTimeline extends Timeline {
|
||||
* @param isContentTimeline Whether the timeline is a content timeline without {@link
|
||||
* AdPlaybackState}s.
|
||||
* @param populateAds Whether to populate ads like after the ad event has been received. This
|
||||
* parameter is ignored if the timeline is a content timeline.
|
||||
* @param playedAds Whether ads should be marked as played if populated.
|
||||
*/
|
||||
public FakeMultiPeriodLiveTimeline(
|
||||
long availabilityStartTimeUs,
|
||||
@ -80,7 +81,8 @@ public class FakeMultiPeriodLiveTimeline extends Timeline {
|
||||
long nowUs,
|
||||
boolean[] adSequencePattern,
|
||||
boolean isContentTimeline,
|
||||
boolean populateAds) {
|
||||
boolean populateAds,
|
||||
boolean playedAds) {
|
||||
checkArgument(nowUs - liveWindowDurationUs >= availabilityStartTimeUs);
|
||||
this.availabilityStartTimeUs = availabilityStartTimeUs;
|
||||
this.liveWindowDurationUs = liveWindowDurationUs;
|
||||
@ -88,6 +90,7 @@ public class FakeMultiPeriodLiveTimeline extends Timeline {
|
||||
this.adSequencePattern = Arrays.copyOf(adSequencePattern, adSequencePattern.length);
|
||||
this.isContentTimeline = isContentTimeline;
|
||||
this.populateAds = populateAds;
|
||||
this.playedAds = playedAds;
|
||||
mediaItem = new MediaItem.Builder().build();
|
||||
periods =
|
||||
invalidate(
|
||||
@ -96,7 +99,8 @@ public class FakeMultiPeriodLiveTimeline extends Timeline {
|
||||
nowUs,
|
||||
adSequencePattern,
|
||||
isContentTimeline,
|
||||
populateAds);
|
||||
populateAds,
|
||||
playedAds);
|
||||
}
|
||||
|
||||
/** Calculates the total duration of the given ad period sequence. */
|
||||
@ -118,7 +122,8 @@ public class FakeMultiPeriodLiveTimeline extends Timeline {
|
||||
nowUs,
|
||||
adSequencePattern,
|
||||
isContentTimeline,
|
||||
populateAds);
|
||||
populateAds,
|
||||
playedAds);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -188,7 +193,8 @@ public class FakeMultiPeriodLiveTimeline extends Timeline {
|
||||
long now,
|
||||
boolean[] adSequencePattern,
|
||||
boolean isContentTimeline,
|
||||
boolean populateAds) {
|
||||
boolean populateAds,
|
||||
boolean playedAds) {
|
||||
long windowStartTimeUs = now - liveWindowDurationUs;
|
||||
int sequencePeriodCount = adSequencePattern.length;
|
||||
long sequenceDurationUs = calculateAdSequencePatternDurationUs(adSequencePattern);
|
||||
@ -226,6 +232,10 @@ public class FakeMultiPeriodLiveTimeline extends Timeline {
|
||||
/* adGroupIndex= */ 0, /* adDurationsUs...= */ periodDurationUs)
|
||||
.withContentResumeOffsetUs(
|
||||
/* adGroupIndex= */ 0, /* contentResumeOffsetUs= */ periodDurationUs);
|
||||
if (playedAds) {
|
||||
adPlaybackState =
|
||||
adPlaybackState.withPlayedAd(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
liveWindow.add(
|
||||
|
@ -43,7 +43,8 @@ public class FakeMultiPeriodLiveTimelineTest {
|
||||
/* nowUs= */ 60_000_000L,
|
||||
adSequencePattern,
|
||||
/* isContentTimeline= */ true,
|
||||
/* populateAds= */ false);
|
||||
/* populateAds= */ false,
|
||||
/* playedAds= */ false);
|
||||
Timeline.Period period = new Timeline.Period();
|
||||
Timeline.Window window = new Timeline.Window();
|
||||
|
||||
@ -79,7 +80,8 @@ public class FakeMultiPeriodLiveTimelineTest {
|
||||
/* nowUs= */ 100_000_000L,
|
||||
adSequencePattern,
|
||||
/* isContentTimeline= */ false,
|
||||
/* populateAds= */ true);
|
||||
/* populateAds= */ true,
|
||||
/* playedAds= */ false);
|
||||
Timeline.Period period = new Timeline.Period();
|
||||
Timeline.Window window = new Timeline.Window();
|
||||
|
||||
@ -138,7 +140,8 @@ public class FakeMultiPeriodLiveTimelineTest {
|
||||
/* nowUs= */ 100_000_000L,
|
||||
adSequencePattern,
|
||||
/* isContentTimeline= */ false,
|
||||
/* populateAds= */ false);
|
||||
/* populateAds= */ false,
|
||||
/* playedAds= */ false);
|
||||
Timeline.Period period = new Timeline.Period();
|
||||
Timeline.Window window = new Timeline.Window();
|
||||
|
||||
@ -189,7 +192,8 @@ public class FakeMultiPeriodLiveTimelineTest {
|
||||
/* nowUs= */ 60_000_123L,
|
||||
adSequencePattern,
|
||||
/* isContentTimeline= */ true,
|
||||
/* populateAds= */ false);
|
||||
/* populateAds= */ false,
|
||||
/* playedAds= */ false);
|
||||
Timeline.Period period = new Timeline.Period();
|
||||
Timeline.Window window = new Timeline.Window();
|
||||
|
||||
@ -273,7 +277,8 @@ public class FakeMultiPeriodLiveTimelineTest {
|
||||
nowUs,
|
||||
adSequencePattern,
|
||||
/* isContentTimeline= */ true,
|
||||
/* populateAds= */ false);
|
||||
/* populateAds= */ false,
|
||||
/* playedAds= */ true);
|
||||
|
||||
assertThat(timeline.getWindow(0, new Timeline.Window()).windowStartTimeMs)
|
||||
.isEqualTo(Util.usToMs(nowUs - liveWindowDurationUs));
|
||||
@ -333,7 +338,8 @@ public class FakeMultiPeriodLiveTimelineTest {
|
||||
nowUs,
|
||||
adSequencePattern,
|
||||
/* isContentTimeline= */ true,
|
||||
/* populateAds= */ false);
|
||||
/* populateAds= */ false,
|
||||
/* playedAds= */ false);
|
||||
|
||||
assertThat(timeline.getWindow(0, new Timeline.Window()).windowStartTimeMs)
|
||||
.isEqualTo(Util.usToMs(nowUs - liveWindowDurationUs));
|
||||
@ -353,7 +359,8 @@ public class FakeMultiPeriodLiveTimelineTest {
|
||||
/* nowUs= */ 120_000_000L,
|
||||
new boolean[] {false, true, true, true},
|
||||
/* isContentTimeline= */ true,
|
||||
/* populateAds= */ false);
|
||||
/* populateAds= */ false,
|
||||
/* playedAds= */ false);
|
||||
Timeline.Period period = new Timeline.Period();
|
||||
Timeline.Window window = new Timeline.Window();
|
||||
|
||||
@ -390,7 +397,8 @@ public class FakeMultiPeriodLiveTimelineTest {
|
||||
/* nowUs= */ 250_000_000L,
|
||||
new boolean[] {false, true, false, true, false},
|
||||
/* isContentTimeline= */ true,
|
||||
/* populateAds= */ false);
|
||||
/* populateAds= */ false,
|
||||
/* playedAds= */ false);
|
||||
|
||||
assertThat(timeline.getPeriodCount()).isEqualTo(10);
|
||||
assertThat(timeline.getWindow(0, window).windowStartTimeMs).isEqualTo(30_000L);
|
||||
|
Loading…
x
Reference in New Issue
Block a user