diff --git a/library/common/src/main/java/com/google/android/exoplayer2/Timeline.java b/library/common/src/main/java/com/google/android/exoplayer2/Timeline.java index 80c1de5190..c087cecd84 100644 --- a/library/common/src/main/java/com/google/android/exoplayer2/Timeline.java +++ b/library/common/src/main/java/com/google/android/exoplayer2/Timeline.java @@ -699,6 +699,14 @@ public abstract class Timeline implements Bundleable { return adPlaybackState.adGroupCount; } + /** + * Returns the number of removed ad groups in the period. Ad groups with indices between {@code + * 0} (inclusive) and {@code removedAdGroupCount} (exclusive) will be empty. + */ + public int getRemovedAdGroupCount() { + return adPlaybackState.adGroupCount; + } + /** * Returns the time of the ad group at index {@code adGroupIndex} in the period, in * microseconds. diff --git a/library/common/src/main/java/com/google/android/exoplayer2/source/ads/AdPlaybackState.java b/library/common/src/main/java/com/google/android/exoplayer2/source/ads/AdPlaybackState.java index f6cb40075a..779f99eaa7 100644 --- a/library/common/src/main/java/com/google/android/exoplayer2/source/ads/AdPlaybackState.java +++ b/library/common/src/main/java/com/google/android/exoplayer2/source/ads/AdPlaybackState.java @@ -16,6 +16,7 @@ package com.google.android.exoplayer2.source.ads; import static com.google.android.exoplayer2.util.Assertions.checkArgument; +import static com.google.android.exoplayer2.util.Assertions.checkState; import static java.lang.Math.max; import android.net.Uri; @@ -417,7 +418,10 @@ public final class AdPlaybackState implements Bundleable { /* adsId= */ null, /* adGroups= */ new AdGroup[0], /* adResumePositionUs= */ 0L, - /* contentDurationUs= */ C.TIME_UNSET); + /* contentDurationUs= */ C.TIME_UNSET, + /* removedAdGroupCount= */ 0); + + private static final AdGroup REMOVED_AD_GROUP = new AdGroup(/* timeUs= */ 0).withAdCount(0); /** * The opaque identifier for ads with which this instance is associated, or {@code null} if unset. @@ -432,6 +436,12 @@ public final class AdPlaybackState implements Bundleable { * The duration of the content period in microseconds, if known. {@link C#TIME_UNSET} otherwise. */ public final long contentDurationUs; + /** + * The number of ad groups the have been removed. Ad groups with indices between {@code 0} + * (inclusive) and {@code removedAdGroupCount} (exclusive) will be empty and must not be modified + * by any of the {@code with*} methods. + */ + public final int removedAdGroupCount; private final AdGroup[] adGroups; @@ -448,21 +458,29 @@ public final class AdPlaybackState implements Bundleable { adsId, createEmptyAdGroups(adGroupTimesUs), /* adResumePositionUs= */ 0, - /* contentDurationUs= */ C.TIME_UNSET); + /* contentDurationUs= */ C.TIME_UNSET, + /* removedAdGroupCount= */ 0); } private AdPlaybackState( - @Nullable Object adsId, AdGroup[] adGroups, long adResumePositionUs, long contentDurationUs) { + @Nullable Object adsId, + AdGroup[] adGroups, + long adResumePositionUs, + long contentDurationUs, + int removedAdGroupCount) { this.adsId = adsId; this.adResumePositionUs = adResumePositionUs; this.contentDurationUs = contentDurationUs; - adGroupCount = adGroups.length; + adGroupCount = adGroups.length + removedAdGroupCount; this.adGroups = adGroups; + this.removedAdGroupCount = removedAdGroupCount; } /** Returns the specified {@link AdGroup}. */ public AdGroup getAdGroup(int adGroupIndex) { - return adGroups[adGroupIndex]; + return adGroupIndex < removedAdGroupCount + ? REMOVED_AD_GROUP + : adGroups[adGroupIndex - removedAdGroupCount]; } /** @@ -480,11 +498,11 @@ public final class AdPlaybackState implements Bundleable { public int getAdGroupIndexForPositionUs(long positionUs, long periodDurationUs) { // Use a linear search as the array elements may not be increasing due to TIME_END_OF_SOURCE. // In practice we expect there to be few ad groups so the search shouldn't be expensive. - int index = adGroups.length - 1; + int index = adGroupCount - 1; while (index >= 0 && isPositionBeforeAdGroup(positionUs, periodDurationUs, index)) { index--; } - return index >= 0 && adGroups[index].hasUnplayedAds() ? index : C.INDEX_UNSET; + return index >= 0 && getAdGroup(index).hasUnplayedAds() ? index : C.INDEX_UNSET; } /** @@ -505,21 +523,22 @@ public final class AdPlaybackState implements Bundleable { } // Use a linear search as the array elements may not be increasing due to TIME_END_OF_SOURCE. // In practice we expect there to be few ad groups so the search shouldn't be expensive. - int index = 0; - while (index < adGroups.length - && ((adGroups[index].timeUs != C.TIME_END_OF_SOURCE && adGroups[index].timeUs <= positionUs) - || !adGroups[index].shouldPlayAdGroup())) { + int index = removedAdGroupCount; + while (index < adGroupCount + && ((getAdGroup(index).timeUs != C.TIME_END_OF_SOURCE + && getAdGroup(index).timeUs <= positionUs) + || !getAdGroup(index).shouldPlayAdGroup())) { index++; } - return index < adGroups.length ? index : C.INDEX_UNSET; + return index < adGroupCount ? index : C.INDEX_UNSET; } /** Returns whether the specified ad has been marked as in {@link #AD_STATE_ERROR}. */ public boolean isAdInErrorState(int adGroupIndex, int adIndexInAdGroup) { - if (adGroupIndex >= adGroups.length) { + if (adGroupIndex >= adGroupCount) { return false; } - AdGroup adGroup = adGroups[adGroupIndex]; + AdGroup adGroup = getAdGroup(adGroupIndex); if (adGroup.count == C.LENGTH_UNSET || adIndexInAdGroup >= adGroup.count) { return false; } @@ -536,9 +555,11 @@ public final class AdPlaybackState implements Bundleable { */ @CheckResult public AdPlaybackState withAdGroupTimeUs(int adGroupIndex, long adGroupTimeUs) { + int adjustedIndex = adGroupIndex - removedAdGroupCount; AdGroup[] adGroups = Util.nullSafeArrayCopy(this.adGroups, this.adGroups.length); - adGroups[adGroupIndex] = this.adGroups[adGroupIndex].withTimeUs(adGroupTimeUs); - return new AdPlaybackState(adsId, adGroups, adResumePositionUs, contentDurationUs); + adGroups[adjustedIndex] = this.adGroups[adjustedIndex].withTimeUs(adGroupTimeUs); + return new AdPlaybackState( + adsId, adGroups, adResumePositionUs, contentDurationUs, removedAdGroupCount); } /** @@ -551,16 +572,18 @@ public final class AdPlaybackState implements Bundleable { */ @CheckResult public AdPlaybackState withNewAdGroup(int adGroupIndex, long adGroupTimeUs) { + int adjustedIndex = adGroupIndex - removedAdGroupCount; AdGroup newAdGroup = new AdGroup(adGroupTimeUs); AdGroup[] adGroups = Util.nullSafeArrayAppend(this.adGroups, newAdGroup); System.arraycopy( /* src= */ adGroups, - /* srcPos= */ adGroupIndex, + /* srcPos= */ adjustedIndex, /* dest= */ adGroups, - /* destPos= */ adGroupIndex + 1, - /* length= */ adGroupCount - adGroupIndex); - adGroups[adGroupIndex] = newAdGroup; - return new AdPlaybackState(adsId, adGroups, adResumePositionUs, contentDurationUs); + /* destPos= */ adjustedIndex + 1, + /* length= */ this.adGroups.length - adjustedIndex); + adGroups[adjustedIndex] = newAdGroup; + return new AdPlaybackState( + adsId, adGroups, adResumePositionUs, contentDurationUs, removedAdGroupCount); } /** @@ -570,44 +593,56 @@ public final class AdPlaybackState implements Bundleable { @CheckResult public AdPlaybackState withAdCount(int adGroupIndex, int adCount) { checkArgument(adCount > 0); - if (adGroups[adGroupIndex].count == adCount) { + int adjustedIndex = adGroupIndex - removedAdGroupCount; + if (adGroups[adjustedIndex].count == adCount) { return this; } AdGroup[] adGroups = Util.nullSafeArrayCopy(this.adGroups, this.adGroups.length); - adGroups[adGroupIndex] = this.adGroups[adGroupIndex].withAdCount(adCount); - return new AdPlaybackState(adsId, adGroups, adResumePositionUs, contentDurationUs); + adGroups[adjustedIndex] = this.adGroups[adjustedIndex].withAdCount(adCount); + return new AdPlaybackState( + adsId, adGroups, adResumePositionUs, contentDurationUs, removedAdGroupCount); } /** Returns an instance with the specified ad URI. */ @CheckResult public AdPlaybackState withAdUri(int adGroupIndex, int adIndexInAdGroup, Uri uri) { + int adjustedIndex = adGroupIndex - removedAdGroupCount; AdGroup[] adGroups = Util.nullSafeArrayCopy(this.adGroups, this.adGroups.length); - adGroups[adGroupIndex] = adGroups[adGroupIndex].withAdUri(uri, adIndexInAdGroup); - return new AdPlaybackState(adsId, adGroups, adResumePositionUs, contentDurationUs); + adGroups[adjustedIndex] = adGroups[adjustedIndex].withAdUri(uri, adIndexInAdGroup); + return new AdPlaybackState( + adsId, adGroups, adResumePositionUs, contentDurationUs, removedAdGroupCount); } /** Returns an instance with the specified ad marked as played. */ @CheckResult public AdPlaybackState withPlayedAd(int adGroupIndex, int adIndexInAdGroup) { + int adjustedIndex = adGroupIndex - removedAdGroupCount; AdGroup[] adGroups = Util.nullSafeArrayCopy(this.adGroups, this.adGroups.length); - adGroups[adGroupIndex] = adGroups[adGroupIndex].withAdState(AD_STATE_PLAYED, adIndexInAdGroup); - return new AdPlaybackState(adsId, adGroups, adResumePositionUs, contentDurationUs); + adGroups[adjustedIndex] = + adGroups[adjustedIndex].withAdState(AD_STATE_PLAYED, adIndexInAdGroup); + return new AdPlaybackState( + adsId, adGroups, adResumePositionUs, contentDurationUs, removedAdGroupCount); } /** Returns an instance with the specified ad marked as skipped. */ @CheckResult public AdPlaybackState withSkippedAd(int adGroupIndex, int adIndexInAdGroup) { + int adjustedIndex = adGroupIndex - removedAdGroupCount; AdGroup[] adGroups = Util.nullSafeArrayCopy(this.adGroups, this.adGroups.length); - adGroups[adGroupIndex] = adGroups[adGroupIndex].withAdState(AD_STATE_SKIPPED, adIndexInAdGroup); - return new AdPlaybackState(adsId, adGroups, adResumePositionUs, contentDurationUs); + adGroups[adjustedIndex] = + adGroups[adjustedIndex].withAdState(AD_STATE_SKIPPED, adIndexInAdGroup); + return new AdPlaybackState( + adsId, adGroups, adResumePositionUs, contentDurationUs, removedAdGroupCount); } /** Returns an instance with the specified ad marked as having a load error. */ @CheckResult public AdPlaybackState withAdLoadError(int adGroupIndex, int adIndexInAdGroup) { + int adjustedIndex = adGroupIndex - removedAdGroupCount; AdGroup[] adGroups = Util.nullSafeArrayCopy(this.adGroups, this.adGroups.length); - adGroups[adGroupIndex] = adGroups[adGroupIndex].withAdState(AD_STATE_ERROR, adIndexInAdGroup); - return new AdPlaybackState(adsId, adGroups, adResumePositionUs, contentDurationUs); + adGroups[adjustedIndex] = adGroups[adjustedIndex].withAdState(AD_STATE_ERROR, adIndexInAdGroup); + return new AdPlaybackState( + adsId, adGroups, adResumePositionUs, contentDurationUs, removedAdGroupCount); } /** @@ -616,19 +651,27 @@ public final class AdPlaybackState implements Bundleable { */ @CheckResult public AdPlaybackState withSkippedAdGroup(int adGroupIndex) { + int adjustedIndex = adGroupIndex - removedAdGroupCount; AdGroup[] adGroups = Util.nullSafeArrayCopy(this.adGroups, this.adGroups.length); - adGroups[adGroupIndex] = adGroups[adGroupIndex].withAllAdsSkipped(); - return new AdPlaybackState(adsId, adGroups, adResumePositionUs, contentDurationUs); + adGroups[adjustedIndex] = adGroups[adjustedIndex].withAllAdsSkipped(); + return new AdPlaybackState( + adsId, adGroups, adResumePositionUs, contentDurationUs, removedAdGroupCount); } - /** Returns an instance with the specified ad durations, in microseconds. */ + /** + * Returns an instance with the specified ad durations, in microseconds. + * + *
Must only be used if {@link #removedAdGroupCount} is 0. + */ @CheckResult public AdPlaybackState withAdDurationsUs(long[][] adDurationUs) { + checkState(removedAdGroupCount == 0); AdGroup[] adGroups = Util.nullSafeArrayCopy(this.adGroups, this.adGroups.length); for (int adGroupIndex = 0; adGroupIndex < adGroupCount; adGroupIndex++) { adGroups[adGroupIndex] = adGroups[adGroupIndex].withAdDurationsUs(adDurationUs[adGroupIndex]); } - return new AdPlaybackState(adsId, adGroups, adResumePositionUs, contentDurationUs); + return new AdPlaybackState( + adsId, adGroups, adResumePositionUs, contentDurationUs, removedAdGroupCount); } /** @@ -637,9 +680,11 @@ public final class AdPlaybackState implements Bundleable { */ @CheckResult public AdPlaybackState withAdDurationsUs(int adGroupIndex, long... adDurationsUs) { + int adjustedIndex = adGroupIndex - removedAdGroupCount; AdGroup[] adGroups = Util.nullSafeArrayCopy(this.adGroups, this.adGroups.length); - adGroups[adGroupIndex] = adGroups[adGroupIndex].withAdDurationsUs(adDurationsUs); - return new AdPlaybackState(adsId, adGroups, adResumePositionUs, contentDurationUs); + adGroups[adjustedIndex] = adGroups[adjustedIndex].withAdDurationsUs(adDurationsUs); + return new AdPlaybackState( + adsId, adGroups, adResumePositionUs, contentDurationUs, removedAdGroupCount); } /** @@ -651,7 +696,8 @@ public final class AdPlaybackState implements Bundleable { if (this.adResumePositionUs == adResumePositionUs) { return this; } else { - return new AdPlaybackState(adsId, adGroups, adResumePositionUs, contentDurationUs); + return new AdPlaybackState( + adsId, adGroups, adResumePositionUs, contentDurationUs, removedAdGroupCount); } } @@ -661,7 +707,33 @@ public final class AdPlaybackState implements Bundleable { if (this.contentDurationUs == contentDurationUs) { return this; } else { - return new AdPlaybackState(adsId, adGroups, adResumePositionUs, contentDurationUs); + return new AdPlaybackState( + adsId, adGroups, adResumePositionUs, contentDurationUs, removedAdGroupCount); + } + } + + /** + * Returns an instance with the specified number of {@link #removedAdGroupCount removed ad + * groups}. + * + *
Ad groups with indices between {@code 0} (inclusive) and {@code removedAdGroupCount} + * (exclusive) will be empty and must not be modified by any of the {@code with*} methods. + */ + @CheckResult + public AdPlaybackState withRemovedAdGroupCount(int removedAdGroupCount) { + if (this.removedAdGroupCount == removedAdGroupCount) { + return this; + } else { + checkArgument(removedAdGroupCount > this.removedAdGroupCount); + AdGroup[] adGroups = new AdGroup[adGroupCount - removedAdGroupCount]; + System.arraycopy( + /* src= */ this.adGroups, + /* srcPos= */ removedAdGroupCount - this.removedAdGroupCount, + /* dest= */ adGroups, + /* destPos= */ 0, + /* length= */ adGroups.length); + return new AdPlaybackState( + adsId, adGroups, adResumePositionUs, contentDurationUs, removedAdGroupCount); } } @@ -671,13 +743,15 @@ public final class AdPlaybackState implements Bundleable { */ @CheckResult public AdPlaybackState withContentResumeOffsetUs(int adGroupIndex, long contentResumeOffsetUs) { - if (adGroups[adGroupIndex].contentResumeOffsetUs == contentResumeOffsetUs) { + int adjustedIndex = adGroupIndex - removedAdGroupCount; + if (adGroups[adjustedIndex].contentResumeOffsetUs == contentResumeOffsetUs) { return this; } AdGroup[] adGroups = Util.nullSafeArrayCopy(this.adGroups, this.adGroups.length); - adGroups[adGroupIndex] = - adGroups[adGroupIndex].withContentResumeOffsetUs(contentResumeOffsetUs); - return new AdPlaybackState(adsId, adGroups, adResumePositionUs, contentDurationUs); + adGroups[adjustedIndex] = + adGroups[adjustedIndex].withContentResumeOffsetUs(contentResumeOffsetUs); + return new AdPlaybackState( + adsId, adGroups, adResumePositionUs, contentDurationUs, removedAdGroupCount); } /** @@ -686,12 +760,15 @@ public final class AdPlaybackState implements Bundleable { */ @CheckResult public AdPlaybackState withIsServerSideInserted(int adGroupIndex, boolean isServerSideInserted) { - if (adGroups[adGroupIndex].isServerSideInserted == isServerSideInserted) { + int adjustedIndex = adGroupIndex - removedAdGroupCount; + if (adGroups[adjustedIndex].isServerSideInserted == isServerSideInserted) { return this; } AdGroup[] adGroups = Util.nullSafeArrayCopy(this.adGroups, this.adGroups.length); - adGroups[adGroupIndex] = adGroups[adGroupIndex].withIsServerSideInserted(isServerSideInserted); - return new AdPlaybackState(adsId, adGroups, adResumePositionUs, contentDurationUs); + adGroups[adjustedIndex] = + adGroups[adjustedIndex].withIsServerSideInserted(isServerSideInserted); + return new AdPlaybackState( + adsId, adGroups, adResumePositionUs, contentDurationUs, removedAdGroupCount); } @Override @@ -707,6 +784,7 @@ public final class AdPlaybackState implements Bundleable { && adGroupCount == that.adGroupCount && adResumePositionUs == that.adResumePositionUs && contentDurationUs == that.contentDurationUs + && removedAdGroupCount == that.removedAdGroupCount && Arrays.equals(adGroups, that.adGroups); } @@ -716,6 +794,7 @@ public final class AdPlaybackState implements Bundleable { result = 31 * result + (adsId == null ? 0 : adsId.hashCode()); result = 31 * result + (int) adResumePositionUs; result = 31 * result + (int) contentDurationUs; + result = 31 * result + removedAdGroupCount; result = 31 * result + Arrays.hashCode(adGroups); return result; } @@ -776,7 +855,7 @@ public final class AdPlaybackState implements Bundleable { // The end of the content is at (but not before) any postroll ad, and after any other ads. return false; } - long adGroupPositionUs = adGroups[adGroupIndex].timeUs; + long adGroupPositionUs = getAdGroup(adGroupIndex).timeUs; if (adGroupPositionUs == C.TIME_END_OF_SOURCE) { return periodDurationUs == C.TIME_UNSET || positionUs < periodDurationUs; } else { @@ -788,12 +867,18 @@ public final class AdPlaybackState implements Bundleable { @Documented @Retention(RetentionPolicy.SOURCE) - @IntDef({FIELD_AD_GROUPS, FIELD_AD_RESUME_POSITION_US, FIELD_CONTENT_DURATION_US}) + @IntDef({ + FIELD_AD_GROUPS, + FIELD_AD_RESUME_POSITION_US, + FIELD_CONTENT_DURATION_US, + FIELD_REMOVED_AD_GROUP_COUNT + }) private @interface FieldNumber {} private static final int FIELD_AD_GROUPS = 1; private static final int FIELD_AD_RESUME_POSITION_US = 2; private static final int FIELD_CONTENT_DURATION_US = 3; + private static final int FIELD_REMOVED_AD_GROUP_COUNT = 4; /** * {@inheritDoc} @@ -812,6 +897,7 @@ public final class AdPlaybackState implements Bundleable { bundle.putParcelableArrayList(keyForField(FIELD_AD_GROUPS), adGroupBundleList); bundle.putLong(keyForField(FIELD_AD_RESUME_POSITION_US), adResumePositionUs); bundle.putLong(keyForField(FIELD_CONTENT_DURATION_US), contentDurationUs); + bundle.putInt(keyForField(FIELD_REMOVED_AD_GROUP_COUNT), removedAdGroupCount); return bundle; } @@ -839,7 +925,9 @@ public final class AdPlaybackState implements Bundleable { bundle.getLong(keyForField(FIELD_AD_RESUME_POSITION_US), /* defaultValue= */ 0); long contentDurationUs = bundle.getLong(keyForField(FIELD_CONTENT_DURATION_US), /* defaultValue= */ C.TIME_UNSET); - return new AdPlaybackState(/* adsId= */ null, adGroups, adResumePositionUs, contentDurationUs); + int removedAdGroupCount = bundle.getInt(keyForField(FIELD_REMOVED_AD_GROUP_COUNT)); + return new AdPlaybackState( + /* adsId= */ null, adGroups, adResumePositionUs, contentDurationUs, removedAdGroupCount); } private static String keyForField(@FieldNumber int field) { diff --git a/library/common/src/test/java/com/google/android/exoplayer2/source/ads/AdPlaybackStateTest.java b/library/common/src/test/java/com/google/android/exoplayer2/source/ads/AdPlaybackStateTest.java index d42a0f91c4..90e28934b0 100644 --- a/library/common/src/test/java/com/google/android/exoplayer2/source/ads/AdPlaybackStateTest.java +++ b/library/common/src/test/java/com/google/android/exoplayer2/source/ads/AdPlaybackStateTest.java @@ -23,7 +23,6 @@ import static org.junit.Assert.fail; import android.net.Uri; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.google.android.exoplayer2.C; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -31,84 +30,90 @@ import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) public class AdPlaybackStateTest { - private static final long[] TEST_AD_GROUP_TMES_US = new long[] {0, C.msToUs(10_000)}; + private static final long[] TEST_AD_GROUP_TIMES_US = new long[] {0, 5_000_000, 10_000_000}; private static final Uri TEST_URI = Uri.EMPTY; private static final Object TEST_ADS_ID = new Object(); - private AdPlaybackState state; - - @Before - public void setUp() { - state = new AdPlaybackState(TEST_ADS_ID, TEST_AD_GROUP_TMES_US); - } - @Test public void setAdCount() { - assertThat(state.getAdGroup(0).count).isEqualTo(C.LENGTH_UNSET); - state = state.withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1); - assertThat(state.getAdGroup(0).count).isEqualTo(1); + AdPlaybackState state = + new AdPlaybackState(TEST_ADS_ID, TEST_AD_GROUP_TIMES_US).withRemovedAdGroupCount(1); + + assertThat(state.getAdGroup(1).count).isEqualTo(C.LENGTH_UNSET); + state = state.withAdCount(/* adGroupIndex= */ 1, /* adCount= */ 1); + + assertThat(state.getAdGroup(1).count).isEqualTo(1); } @Test public void setAdUriBeforeAdCount() { - state = state.withAdUri(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 1, TEST_URI); - state = state.withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 2); + AdPlaybackState state = + new AdPlaybackState(TEST_ADS_ID, TEST_AD_GROUP_TIMES_US).withRemovedAdGroupCount(1); - assertThat(state.getAdGroup(0).uris[0]).isNull(); - assertThat(state.getAdGroup(0).states[0]).isEqualTo(AdPlaybackState.AD_STATE_UNAVAILABLE); - assertThat(state.getAdGroup(0).uris[1]).isSameInstanceAs(TEST_URI); - assertThat(state.getAdGroup(0).states[1]).isEqualTo(AdPlaybackState.AD_STATE_AVAILABLE); + state = state.withAdUri(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 1, TEST_URI); + state = state.withAdCount(/* adGroupIndex= */ 1, /* adCount= */ 2); + + assertThat(state.getAdGroup(1).uris[0]).isNull(); + assertThat(state.getAdGroup(1).states[0]).isEqualTo(AdPlaybackState.AD_STATE_UNAVAILABLE); + assertThat(state.getAdGroup(1).uris[1]).isSameInstanceAs(TEST_URI); + assertThat(state.getAdGroup(1).states[1]).isEqualTo(AdPlaybackState.AD_STATE_AVAILABLE); } @Test public void setAdErrorBeforeAdCount() { - state = state.withAdLoadError(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0); - state = state.withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 2); + AdPlaybackState state = + new AdPlaybackState(TEST_ADS_ID, TEST_AD_GROUP_TIMES_US).withRemovedAdGroupCount(1); - assertThat(state.getAdGroup(0).uris[0]).isNull(); - assertThat(state.getAdGroup(0).states[0]).isEqualTo(AdPlaybackState.AD_STATE_ERROR); - assertThat(state.isAdInErrorState(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0)).isTrue(); - assertThat(state.getAdGroup(0).states[1]).isEqualTo(AdPlaybackState.AD_STATE_UNAVAILABLE); - assertThat(state.isAdInErrorState(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 1)).isFalse(); + state = state.withAdLoadError(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0); + state = state.withAdCount(/* adGroupIndex= */ 1, /* adCount= */ 2); + + assertThat(state.getAdGroup(1).uris[0]).isNull(); + assertThat(state.getAdGroup(1).states[0]).isEqualTo(AdPlaybackState.AD_STATE_ERROR); + assertThat(state.isAdInErrorState(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0)).isTrue(); + assertThat(state.getAdGroup(1).states[1]).isEqualTo(AdPlaybackState.AD_STATE_UNAVAILABLE); + assertThat(state.isAdInErrorState(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 1)).isFalse(); } @Test public void withAdGroupTimeUs_updatesAdGroupTimeUs() { - AdPlaybackState state = new AdPlaybackState(TEST_ADS_ID, /* adGroupTimesUs...= */ 0, 10_000); + AdPlaybackState state = + new AdPlaybackState(TEST_ADS_ID, /* adGroupTimesUs...= */ 0, 5_000, 10_000) + .withRemovedAdGroupCount(1); state = state - .withAdGroupTimeUs(/* adGroupIndex= */ 0, 3_000) - .withAdGroupTimeUs(/* adGroupIndex= */ 1, 6_000); + .withAdGroupTimeUs(/* adGroupIndex= */ 1, 3_000) + .withAdGroupTimeUs(/* adGroupIndex= */ 2, 6_000); - assertThat(state.adGroupCount).isEqualTo(2); - assertThat(state.getAdGroup(0).timeUs).isEqualTo(3_000); - assertThat(state.getAdGroup(1).timeUs).isEqualTo(6_000); + assertThat(state.adGroupCount).isEqualTo(3); + assertThat(state.getAdGroup(1).timeUs).isEqualTo(3_000); + assertThat(state.getAdGroup(2).timeUs).isEqualTo(6_000); } @Test public void withNewAdGroup_addsGroupAndKeepsExistingGroups() { AdPlaybackState state = - new AdPlaybackState(TEST_ADS_ID, /* adGroupTimesUs...= */ 3_000, 6_000) - .withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 2) - .withAdCount(/* adGroupIndex= */ 1, /* adCount= */ 1) - .withAdUri(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 1, TEST_URI) - .withSkippedAd(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0); + new AdPlaybackState(TEST_ADS_ID, /* adGroupTimesUs...= */ 0, 3_000, 6_000) + .withRemovedAdGroupCount(1) + .withAdCount(/* adGroupIndex= */ 1, /* adCount= */ 2) + .withAdCount(/* adGroupIndex= */ 2, /* adCount= */ 1) + .withAdUri(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 1, TEST_URI) + .withSkippedAd(/* adGroupIndex= */ 2, /* adIndexInAdGroup= */ 0); state = state - .withNewAdGroup(/* adGroupIndex= */ 0, /* adGroupTimeUs= */ 1_000) - .withNewAdGroup(/* adGroupIndex= */ 2, /* adGroupTimeUs= */ 5_000) - .withNewAdGroup(/* adGroupIndex= */ 4, /* adGroupTimeUs= */ 8_000); + .withNewAdGroup(/* adGroupIndex= */ 1, /* adGroupTimeUs= */ 1_000) + .withNewAdGroup(/* adGroupIndex= */ 3, /* adGroupTimeUs= */ 5_000) + .withNewAdGroup(/* adGroupIndex= */ 5, /* adGroupTimeUs= */ 8_000); - assertThat(state.adGroupCount).isEqualTo(5); - assertThat(state.getAdGroup(0).count).isEqualTo(C.INDEX_UNSET); - assertThat(state.getAdGroup(1).count).isEqualTo(2); - assertThat(state.getAdGroup(1).uris[1]).isSameInstanceAs(TEST_URI); - assertThat(state.getAdGroup(2).count).isEqualTo(C.INDEX_UNSET); - assertThat(state.getAdGroup(3).count).isEqualTo(1); - assertThat(state.getAdGroup(3).states[0]).isEqualTo(AdPlaybackState.AD_STATE_SKIPPED); - assertThat(state.getAdGroup(4).count).isEqualTo(C.INDEX_UNSET); + assertThat(state.adGroupCount).isEqualTo(6); + assertThat(state.getAdGroup(1).count).isEqualTo(C.INDEX_UNSET); + assertThat(state.getAdGroup(2).count).isEqualTo(2); + assertThat(state.getAdGroup(2).uris[1]).isSameInstanceAs(TEST_URI); + assertThat(state.getAdGroup(3).count).isEqualTo(C.INDEX_UNSET); + assertThat(state.getAdGroup(4).count).isEqualTo(1); + assertThat(state.getAdGroup(4).states[0]).isEqualTo(AdPlaybackState.AD_STATE_SKIPPED); + assertThat(state.getAdGroup(5).count).isEqualTo(C.INDEX_UNSET); } @Test @@ -129,92 +134,107 @@ public class AdPlaybackStateTest { @Test public void getFirstAdIndexToPlayIsZero() { - state = state.withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 3); - state = state.withAdUri(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0, TEST_URI); - state = state.withAdUri(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 2, TEST_URI); + AdPlaybackState state = + new AdPlaybackState(TEST_ADS_ID, TEST_AD_GROUP_TIMES_US).withRemovedAdGroupCount(1); + state = state.withAdCount(/* adGroupIndex= */ 1, /* adCount= */ 3); + state = state.withAdUri(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0, TEST_URI); + state = state.withAdUri(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 2, TEST_URI); - assertThat(state.getAdGroup(0).getFirstAdIndexToPlay()).isEqualTo(0); + assertThat(state.getAdGroup(1).getFirstAdIndexToPlay()).isEqualTo(0); } @Test public void getFirstAdIndexToPlaySkipsPlayedAd() { - state = state.withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 3); - state = state.withAdUri(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0, TEST_URI); - state = state.withAdUri(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 2, TEST_URI); + AdPlaybackState state = + new AdPlaybackState(TEST_ADS_ID, TEST_AD_GROUP_TIMES_US).withRemovedAdGroupCount(1); + state = state.withAdCount(/* adGroupIndex= */ 1, /* adCount= */ 3); + state = state.withAdUri(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0, TEST_URI); + state = state.withAdUri(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 2, TEST_URI); - state = state.withPlayedAd(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0); + state = state.withPlayedAd(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0); - assertThat(state.getAdGroup(0).getFirstAdIndexToPlay()).isEqualTo(1); - assertThat(state.getAdGroup(0).states[1]).isEqualTo(AdPlaybackState.AD_STATE_UNAVAILABLE); - assertThat(state.getAdGroup(0).states[2]).isEqualTo(AdPlaybackState.AD_STATE_AVAILABLE); + assertThat(state.getAdGroup(1).getFirstAdIndexToPlay()).isEqualTo(1); + assertThat(state.getAdGroup(1).states[1]).isEqualTo(AdPlaybackState.AD_STATE_UNAVAILABLE); + assertThat(state.getAdGroup(1).states[2]).isEqualTo(AdPlaybackState.AD_STATE_AVAILABLE); } @Test public void getFirstAdIndexToPlaySkipsSkippedAd() { - state = state.withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 3); - state = state.withAdUri(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0, TEST_URI); - state = state.withAdUri(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 2, TEST_URI); + AdPlaybackState state = + new AdPlaybackState(TEST_ADS_ID, TEST_AD_GROUP_TIMES_US).withRemovedAdGroupCount(1); + state = state.withAdCount(/* adGroupIndex= */ 1, /* adCount= */ 3); + state = state.withAdUri(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0, TEST_URI); + state = state.withAdUri(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 2, TEST_URI); - state = state.withSkippedAd(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0); + state = state.withSkippedAd(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0); - assertThat(state.getAdGroup(0).getFirstAdIndexToPlay()).isEqualTo(1); - assertThat(state.getAdGroup(0).states[1]).isEqualTo(AdPlaybackState.AD_STATE_UNAVAILABLE); - assertThat(state.getAdGroup(0).states[2]).isEqualTo(AdPlaybackState.AD_STATE_AVAILABLE); + assertThat(state.getAdGroup(1).getFirstAdIndexToPlay()).isEqualTo(1); + assertThat(state.getAdGroup(1).states[1]).isEqualTo(AdPlaybackState.AD_STATE_UNAVAILABLE); + assertThat(state.getAdGroup(1).states[2]).isEqualTo(AdPlaybackState.AD_STATE_AVAILABLE); } @Test public void getFirstAdIndexToPlaySkipsErrorAds() { - state = state.withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 3); - state = state.withAdUri(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0, TEST_URI); - state = state.withAdUri(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 2, TEST_URI); + AdPlaybackState state = + new AdPlaybackState(TEST_ADS_ID, TEST_AD_GROUP_TIMES_US).withRemovedAdGroupCount(1); + state = state.withAdCount(/* adGroupIndex= */ 1, /* adCount= */ 3); + state = state.withAdUri(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0, TEST_URI); + state = state.withAdUri(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 2, TEST_URI); - state = state.withPlayedAd(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0); - state = state.withAdLoadError(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 1); + state = state.withPlayedAd(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0); + state = state.withAdLoadError(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 1); - assertThat(state.getAdGroup(0).getFirstAdIndexToPlay()).isEqualTo(2); + assertThat(state.getAdGroup(1).getFirstAdIndexToPlay()).isEqualTo(2); } @Test public void getNextAdIndexToPlaySkipsErrorAds() { - state = state.withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 3); - state = state.withAdUri(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 1, TEST_URI); + AdPlaybackState state = + new AdPlaybackState(TEST_ADS_ID, TEST_AD_GROUP_TIMES_US).withRemovedAdGroupCount(1); + state = state.withAdCount(/* adGroupIndex= */ 1, /* adCount= */ 3); + state = state.withAdUri(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 1, TEST_URI); - state = state.withAdLoadError(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 1); + state = state.withAdLoadError(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 1); - assertThat(state.getAdGroup(0).getNextAdIndexToPlay(0)).isEqualTo(2); + assertThat(state.getAdGroup(1).getNextAdIndexToPlay(0)).isEqualTo(2); } @Test public void getFirstAdIndexToPlay_withPlayedServerSideInsertedAds_returnsFirstIndex() { - state = state.withIsServerSideInserted(/* adGroupIndex= */ 0, /* isServerSideInserted= */ true); - state = state.withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 3); - state = state.withAdUri(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0, TEST_URI); - state = state.withAdUri(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 1, TEST_URI); - state = state.withAdUri(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 2, TEST_URI); + AdPlaybackState state = + new AdPlaybackState(TEST_ADS_ID, TEST_AD_GROUP_TIMES_US).withRemovedAdGroupCount(1); + state = state.withIsServerSideInserted(/* adGroupIndex= */ 1, /* isServerSideInserted= */ true); + state = state.withAdCount(/* adGroupIndex= */ 1, /* adCount= */ 3); + state = state.withAdUri(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0, TEST_URI); + state = state.withAdUri(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 1, TEST_URI); + state = state.withAdUri(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 2, TEST_URI); - state = state.withPlayedAd(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0); + state = state.withPlayedAd(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0); - assertThat(state.getAdGroup(0).getFirstAdIndexToPlay()).isEqualTo(0); + assertThat(state.getAdGroup(1).getFirstAdIndexToPlay()).isEqualTo(0); } @Test public void getNextAdIndexToPlay_withPlayedServerSideInsertedAds_returnsNextIndex() { - state = state.withIsServerSideInserted(/* adGroupIndex= */ 0, /* isServerSideInserted= */ true); - state = state.withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 3); - state = state.withAdUri(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0, TEST_URI); - state = state.withAdUri(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 1, TEST_URI); - state = state.withAdUri(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 2, TEST_URI); + AdPlaybackState state = + new AdPlaybackState(TEST_ADS_ID, TEST_AD_GROUP_TIMES_US).withRemovedAdGroupCount(1); + state = state.withIsServerSideInserted(/* adGroupIndex= */ 1, /* isServerSideInserted= */ true); + state = state.withAdCount(/* adGroupIndex= */ 1, /* adCount= */ 3); + state = state.withAdUri(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0, TEST_URI); + state = state.withAdUri(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 1, TEST_URI); + state = state.withAdUri(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 2, TEST_URI); - state = state.withPlayedAd(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0); - state = state.withPlayedAd(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 1); - state = state.withPlayedAd(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 2); + state = state.withPlayedAd(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0); + state = state.withPlayedAd(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 1); + state = state.withPlayedAd(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 2); - assertThat(state.getAdGroup(0).getNextAdIndexToPlay(/* lastPlayedAdIndex= */ 0)).isEqualTo(1); - assertThat(state.getAdGroup(0).getNextAdIndexToPlay(/* lastPlayedAdIndex= */ 1)).isEqualTo(2); + assertThat(state.getAdGroup(1).getNextAdIndexToPlay(/* lastPlayedAdIndex= */ 0)).isEqualTo(1); + assertThat(state.getAdGroup(1).getNextAdIndexToPlay(/* lastPlayedAdIndex= */ 1)).isEqualTo(2); } @Test public void setAdStateTwiceThrows() { + AdPlaybackState state = new AdPlaybackState(TEST_ADS_ID, TEST_AD_GROUP_TIMES_US); state = state.withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1); state = state.withPlayedAd(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0); try { @@ -227,6 +247,7 @@ public class AdPlaybackStateTest { @Test public void skipAllWithoutAdCount() { + AdPlaybackState state = new AdPlaybackState(TEST_ADS_ID, TEST_AD_GROUP_TIMES_US); state = state.withSkippedAdGroup(0); state = state.withSkippedAdGroup(1); assertThat(state.getAdGroup(0).count).isEqualTo(0); @@ -236,20 +257,22 @@ public class AdPlaybackStateTest { @Test public void roundTripViaBundle_yieldsEqualFieldsExceptAdsId() { AdPlaybackState originalState = - state - .withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1) - .withPlayedAd(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0) - .withAdUri(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0, TEST_URI) - .withAdCount(/* adGroupIndex= */ 1, /* adCount= */ 2) - .withSkippedAd(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0) - .withPlayedAd(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 1) + new AdPlaybackState(TEST_ADS_ID, TEST_AD_GROUP_TIMES_US) + .withRemovedAdGroupCount(1) + .withAdCount(/* adGroupIndex= */ 1, /* adCount= */ 1) + .withPlayedAd(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0) .withAdUri(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0, TEST_URI) - .withAdUri(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 1, TEST_URI) - .withContentResumeOffsetUs(/* adGroupIndex= */ 0, /* contentResumeOffsetUs= */ 4444) - .withContentResumeOffsetUs(/* adGroupIndex= */ 1, /* contentResumeOffsetUs= */ 3333) - .withIsServerSideInserted(/* adGroupIndex= */ 0, /* isServerSideInserted= */ true) + .withAdCount(/* adGroupIndex= */ 2, /* adCount= */ 2) + .withSkippedAd(/* adGroupIndex= */ 2, /* adIndexInAdGroup= */ 0) + .withPlayedAd(/* adGroupIndex= */ 2, /* adIndexInAdGroup= */ 1) + .withAdUri(/* adGroupIndex= */ 2, /* adIndexInAdGroup= */ 0, TEST_URI) + .withAdUri(/* adGroupIndex= */ 2, /* adIndexInAdGroup= */ 1, TEST_URI) + .withContentResumeOffsetUs(/* adGroupIndex= */ 1, /* contentResumeOffsetUs= */ 4444) + .withContentResumeOffsetUs(/* adGroupIndex= */ 2, /* contentResumeOffsetUs= */ 3333) .withIsServerSideInserted(/* adGroupIndex= */ 1, /* isServerSideInserted= */ true) - .withAdDurationsUs(new long[][] {{12}, {34, 56}}) + .withIsServerSideInserted(/* adGroupIndex= */ 2, /* isServerSideInserted= */ true) + .withAdDurationsUs(/* adGroupIndex= */ 1, /* adDurationsUs...= */ 12) + .withAdDurationsUs(/* adGroupIndex= */ 2, /* adDurationsUs...= */ 34, 56) .withAdResumePositionUs(123) .withContentDurationUs(456); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/ads/ServerSideInsertedAdsMediaSource.java b/library/core/src/main/java/com/google/android/exoplayer2/source/ads/ServerSideInsertedAdsMediaSource.java index 1eaa369f72..19239f807e 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/ads/ServerSideInsertedAdsMediaSource.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/ads/ServerSideInsertedAdsMediaSource.java @@ -121,7 +121,7 @@ public final class ServerSideInsertedAdsMediaSource extends BaseMediaSource */ public void setAdPlaybackState(AdPlaybackState adPlaybackState) { checkArgument(adPlaybackState.adGroupCount >= this.adPlaybackState.adGroupCount); - for (int i = 0; i < adPlaybackState.adGroupCount; i++) { + for (int i = adPlaybackState.removedAdGroupCount; i < adPlaybackState.adGroupCount; i++) { AdPlaybackState.AdGroup adGroup = adPlaybackState.getAdGroup(i); checkArgument(adGroup.isServerSideInserted); if (i < this.adPlaybackState.adGroupCount) { diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/ads/ServerSideInsertedAdsUtil.java b/library/core/src/main/java/com/google/android/exoplayer2/source/ads/ServerSideInsertedAdsUtil.java index aace219597..65567d005b 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/ads/ServerSideInsertedAdsUtil.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/ads/ServerSideInsertedAdsUtil.java @@ -53,7 +53,7 @@ public final class ServerSideInsertedAdsUtil { long adGroupInsertionPositionUs = getMediaPeriodPositionUsForContent( fromPositionUs, /* nextAdGroupIndex= */ C.INDEX_UNSET, adPlaybackState); - int insertionIndex = 0; + int insertionIndex = adPlaybackState.removedAdGroupCount; while (insertionIndex < adPlaybackState.adGroupCount && adPlaybackState.getAdGroup(insertionIndex).timeUs != C.TIME_END_OF_SOURCE && adPlaybackState.getAdGroup(insertionIndex).timeUs <= adGroupInsertionPositionUs) { @@ -184,7 +184,7 @@ public final class ServerSideInsertedAdsUtil { long positionUs, int adGroupIndex, int adIndexInAdGroup, AdPlaybackState adPlaybackState) { AdPlaybackState.AdGroup currentAdGroup = adPlaybackState.getAdGroup(adGroupIndex); positionUs += currentAdGroup.timeUs; - for (int i = 0; i < adGroupIndex; i++) { + for (int i = adPlaybackState.removedAdGroupCount; i < adGroupIndex; i++) { AdPlaybackState.AdGroup adGroup = adPlaybackState.getAdGroup(i); for (int j = 0; j < getAdCountInGroup(adPlaybackState, /* adGroupIndex= */ i); j++) { positionUs += adGroup.durationsUs[j]; @@ -214,7 +214,7 @@ public final class ServerSideInsertedAdsUtil { long positionUs, int adGroupIndex, int adIndexInAdGroup, AdPlaybackState adPlaybackState) { AdPlaybackState.AdGroup currentAdGroup = adPlaybackState.getAdGroup(adGroupIndex); positionUs -= currentAdGroup.timeUs; - for (int i = 0; i < adGroupIndex; i++) { + for (int i = adPlaybackState.removedAdGroupCount; i < adGroupIndex; i++) { AdPlaybackState.AdGroup adGroup = adPlaybackState.getAdGroup(i); for (int j = 0; j < getAdCountInGroup(adPlaybackState, /* adGroupIndex= */ i); j++) { positionUs -= adGroup.durationsUs[j]; @@ -246,7 +246,7 @@ public final class ServerSideInsertedAdsUtil { if (nextAdGroupIndex == C.INDEX_UNSET) { nextAdGroupIndex = adPlaybackState.adGroupCount; } - for (int i = 0; i < nextAdGroupIndex; i++) { + for (int i = adPlaybackState.removedAdGroupCount; i < nextAdGroupIndex; i++) { AdPlaybackState.AdGroup adGroup = adPlaybackState.getAdGroup(i); if (adGroup.timeUs == C.TIME_END_OF_SOURCE || adGroup.timeUs > positionUs) { break; @@ -283,7 +283,7 @@ public final class ServerSideInsertedAdsUtil { if (nextAdGroupIndex == C.INDEX_UNSET) { nextAdGroupIndex = adPlaybackState.adGroupCount; } - for (int i = 0; i < nextAdGroupIndex; i++) { + for (int i = adPlaybackState.removedAdGroupCount; i < nextAdGroupIndex; i++) { AdPlaybackState.AdGroup adGroup = adPlaybackState.getAdGroup(i); if (adGroup.timeUs == C.TIME_END_OF_SOURCE || adGroup.timeUs > positionUs - totalAdDurationBeforePositionUs) { diff --git a/library/core/src/test/java/com/google/android/exoplayer2/source/ads/ServerSideInsertedAdsUtilTest.java b/library/core/src/test/java/com/google/android/exoplayer2/source/ads/ServerSideInsertedAdsUtilTest.java index 15a217bf84..25379a816b 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/source/ads/ServerSideInsertedAdsUtilTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/source/ads/ServerSideInsertedAdsUtilTest.java @@ -37,7 +37,8 @@ public final class ServerSideInsertedAdsUtilTest { @Test public void addAdGroupToAdPlaybackState_insertsCorrectAdGroupData() { AdPlaybackState state = - new AdPlaybackState(ADS_ID, /* adGroupTimesUs...= */ C.TIME_END_OF_SOURCE); + new AdPlaybackState(ADS_ID, /* adGroupTimesUs...= */ 0, 1, C.TIME_END_OF_SOURCE) + .withRemovedAdGroupCount(2); // stream: 0-- content --4300-- ad1 --4500-- content // content timeline: 0-4300 - [ad1] - 4700-end @@ -50,11 +51,12 @@ public final class ServerSideInsertedAdsUtilTest { assertThat(state) .isEqualTo( - new AdPlaybackState(ADS_ID, /* adGroupTimesUs...= */ 4300, C.TIME_END_OF_SOURCE) - .withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1) - .withIsServerSideInserted(/* adGroupIndex= */ 0, /* isServerSideInserted= */ true) - .withContentResumeOffsetUs(/* adGroupIndex= */ 0, /* contentResumeOffsetUs= */ 400) - .withAdDurationsUs(new long[][] {new long[] {200}, new long[0]})); + new AdPlaybackState(ADS_ID, /* adGroupTimesUs...= */ 0, 0, 4300, C.TIME_END_OF_SOURCE) + .withRemovedAdGroupCount(2) + .withAdCount(/* adGroupIndex= */ 2, /* adCount= */ 1) + .withIsServerSideInserted(/* adGroupIndex= */ 2, /* isServerSideInserted= */ true) + .withContentResumeOffsetUs(/* adGroupIndex= */ 2, /* contentResumeOffsetUs= */ 400) + .withAdDurationsUs(/* adGroupIndex= */ 2, /* adDurationsUs...= */ 200)); // stream: 0-- content --2100-- ad1 --2400-- content --4300-- ad2 --4500-- content // content timeline: 0-2100 - [ad1] - 2100-4000 - [ad2] - 4400-end @@ -67,13 +69,16 @@ public final class ServerSideInsertedAdsUtilTest { assertThat(state) .isEqualTo( - new AdPlaybackState(ADS_ID, /* adGroupTimesUs...= */ 2100, 4000, C.TIME_END_OF_SOURCE) - .withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1) - .withAdCount(/* adGroupIndex= */ 1, /* adCount= */ 1) - .withIsServerSideInserted(/* adGroupIndex= */ 0, /* isServerSideInserted= */ true) - .withIsServerSideInserted(/* adGroupIndex= */ 1, /* isServerSideInserted= */ true) - .withContentResumeOffsetUs(/* adGroupIndex= */ 1, /* contentResumeOffsetUs= */ 400) - .withAdDurationsUs(new long[][] {new long[] {300}, new long[] {200}, new long[0]})); + new AdPlaybackState( + ADS_ID, /* adGroupTimesUs...= */ 0, 0, 2100, 4000, C.TIME_END_OF_SOURCE) + .withRemovedAdGroupCount(2) + .withAdCount(/* adGroupIndex= */ 2, /* adCount= */ 1) + .withAdCount(/* adGroupIndex= */ 3, /* adCount= */ 1) + .withIsServerSideInserted(/* adGroupIndex= */ 2, /* isServerSideInserted= */ true) + .withIsServerSideInserted(/* adGroupIndex= */ 3, /* isServerSideInserted= */ true) + .withContentResumeOffsetUs(/* adGroupIndex= */ 3, /* contentResumeOffsetUs= */ 400) + .withAdDurationsUs(/* adGroupIndex= */ 2, /* adDurationsUs...= */ 300) + .withAdDurationsUs(/* adGroupIndex= */ 3, /* adDurationsUs...= */ 200)); // stream: 0-- ad1 --100-- content --2100-- ad2 --2400-- content --4300-- ad3 --4500-- content // content timeline: 0 - [ad1] - 50-2050 -[ad2] - 2050-3950 - [ad3] - 4350-end @@ -87,19 +92,19 @@ public final class ServerSideInsertedAdsUtilTest { assertThat(state) .isEqualTo( new AdPlaybackState( - ADS_ID, /* adGroupTimesUs...= */ 0, 2050, 3950, C.TIME_END_OF_SOURCE) - .withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1) - .withAdCount(/* adGroupIndex= */ 1, /* adCount= */ 1) + ADS_ID, /* adGroupTimesUs...= */ 0, 0, 0, 2050, 3950, C.TIME_END_OF_SOURCE) + .withRemovedAdGroupCount(2) .withAdCount(/* adGroupIndex= */ 2, /* adCount= */ 1) - .withIsServerSideInserted(/* adGroupIndex= */ 0, /* isServerSideInserted= */ true) - .withIsServerSideInserted(/* adGroupIndex= */ 1, /* isServerSideInserted= */ true) + .withAdCount(/* adGroupIndex= */ 3, /* adCount= */ 1) + .withAdCount(/* adGroupIndex= */ 4, /* adCount= */ 1) .withIsServerSideInserted(/* adGroupIndex= */ 2, /* isServerSideInserted= */ true) - .withContentResumeOffsetUs(/* adGroupIndex= */ 0, /* contentResumeOffsetUs= */ 50) - .withContentResumeOffsetUs(/* adGroupIndex= */ 2, /* contentResumeOffsetUs= */ 400) - .withAdDurationsUs( - new long[][] { - new long[] {100}, new long[] {300}, new long[] {200}, new long[0] - })); + .withIsServerSideInserted(/* adGroupIndex= */ 3, /* isServerSideInserted= */ true) + .withIsServerSideInserted(/* adGroupIndex= */ 4, /* isServerSideInserted= */ true) + .withContentResumeOffsetUs(/* adGroupIndex= */ 2, /* contentResumeOffsetUs= */ 50) + .withContentResumeOffsetUs(/* adGroupIndex= */ 4, /* contentResumeOffsetUs= */ 400) + .withAdDurationsUs(/* adGroupIndex= */ 2, /* adDurationsUs...= */ 100) + .withAdDurationsUs(/* adGroupIndex= */ 3, /* adDurationsUs...= */ 300) + .withAdDurationsUs(/* adGroupIndex= */ 4, /* adDurationsUs...= */ 200)); // stream: 0-- ad1 --100-- c --2100-- ad2 --2400-- c --4300-- ad3 --4500-- c --5000-- ad4 --6000 // content timeline: 0 - [ad1] - 50-2050 -[ad2] - 2050-3950 - [ad3] - 4350-4850 - [ad4] - 4850 @@ -113,25 +118,29 @@ public final class ServerSideInsertedAdsUtilTest { assertThat(state) .isEqualTo( new AdPlaybackState( - ADS_ID, /* adGroupTimesUs...= */ 0, 2050, 3950, 4850, C.TIME_END_OF_SOURCE) - .withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1) - .withAdCount(/* adGroupIndex= */ 1, /* adCount= */ 1) + ADS_ID, /* adGroupTimesUs...= */ + 0, + 0, + 0, + 2050, + 3950, + 4850, + C.TIME_END_OF_SOURCE) + .withRemovedAdGroupCount(2) .withAdCount(/* adGroupIndex= */ 2, /* adCount= */ 1) .withAdCount(/* adGroupIndex= */ 3, /* adCount= */ 1) - .withIsServerSideInserted(/* adGroupIndex= */ 0, /* isServerSideInserted= */ true) - .withIsServerSideInserted(/* adGroupIndex= */ 1, /* isServerSideInserted= */ true) + .withAdCount(/* adGroupIndex= */ 4, /* adCount= */ 1) + .withAdCount(/* adGroupIndex= */ 5, /* adCount= */ 1) .withIsServerSideInserted(/* adGroupIndex= */ 2, /* isServerSideInserted= */ true) .withIsServerSideInserted(/* adGroupIndex= */ 3, /* isServerSideInserted= */ true) - .withContentResumeOffsetUs(/* adGroupIndex= */ 0, /* contentResumeOffsetUs= */ 50) - .withContentResumeOffsetUs(/* adGroupIndex= */ 2, /* contentResumeOffsetUs= */ 400) - .withAdDurationsUs( - new long[][] { - new long[] {100}, - new long[] {300}, - new long[] {200}, - new long[] {1000}, - new long[0] - })); + .withIsServerSideInserted(/* adGroupIndex= */ 4, /* isServerSideInserted= */ true) + .withIsServerSideInserted(/* adGroupIndex= */ 5, /* isServerSideInserted= */ true) + .withContentResumeOffsetUs(/* adGroupIndex= */ 2, /* contentResumeOffsetUs= */ 50) + .withContentResumeOffsetUs(/* adGroupIndex= */ 4, /* contentResumeOffsetUs= */ 400) + .withAdDurationsUs(/* adGroupIndex= */ 2, /* adDurationsUs...= */ 100) + .withAdDurationsUs(/* adGroupIndex= */ 3, /* adDurationsUs...= */ 300) + .withAdDurationsUs(/* adGroupIndex= */ 4, /* adDurationsUs...= */ 200) + .withAdDurationsUs(/* adGroupIndex= */ 5, /* adDurationsUs...= */ 1000)); } @Test @@ -139,100 +148,98 @@ public final class ServerSideInsertedAdsUtilTest { // stream: 0-- ad1 --200-- content --2100-- ad2 --2300-- content --4300-- ad3 --4500-- content // content timeline: 0 - [ad1] - 100-2000 -[ad2] - 2000-4000 - [ad3] - 4400-end AdPlaybackState state = - new AdPlaybackState(ADS_ID, /* adGroupTimesUs...= */ 0, 2000, 4000) - .withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 2) - .withAdCount(/* adGroupIndex= */ 1, /* adCount= */ 1) - .withAdCount(/* adGroupIndex= */ 2, /* adCount= */ 3) - .withContentResumeOffsetUs(/* adGroupIndex= */ 0, /* contentResumeOffsetUs= */ 100) - .withContentResumeOffsetUs(/* adGroupIndex= */ 1, /* contentResumeOffsetUs= */ 0) - .withContentResumeOffsetUs(/* adGroupIndex= */ 2, /* contentResumeOffsetUs= */ 400) - .withAdDurationsUs( - new long[][] { - new long[] {150, 50}, - new long[] {200}, - new long[] {50, 50, 100} - }); + new AdPlaybackState(ADS_ID, /* adGroupTimesUs...= */ 0, 0, 0, 2000, 4000) + .withRemovedAdGroupCount(2) + .withAdCount(/* adGroupIndex= */ 2, /* adCount= */ 2) + .withAdCount(/* adGroupIndex= */ 3, /* adCount= */ 1) + .withAdCount(/* adGroupIndex= */ 4, /* adCount= */ 3) + .withContentResumeOffsetUs(/* adGroupIndex= */ 2, /* contentResumeOffsetUs= */ 100) + .withContentResumeOffsetUs(/* adGroupIndex= */ 3, /* contentResumeOffsetUs= */ 0) + .withContentResumeOffsetUs(/* adGroupIndex= */ 4, /* contentResumeOffsetUs= */ 400) + .withAdDurationsUs(/* adGroupIndex= */ 2, /* adDurationsUs...= */ 150, 50) + .withAdDurationsUs(/* adGroupIndex= */ 3, /* adDurationsUs...= */ 200) + .withAdDurationsUs(/* adGroupIndex= */ 4, /* adDurationsUs...= */ 50, 50, 100); assertThat( getStreamPositionUsForAd( - /* positionUs= */ 0, /* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0, state)) + /* positionUs= */ 0, /* adGroupIndex= */ 2, /* adIndexInAdGroup= */ 0, state)) .isEqualTo(0); assertThat( getStreamPositionUsForAd( - /* positionUs= */ 100, /* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0, state)) + /* positionUs= */ 100, /* adGroupIndex= */ 2, /* adIndexInAdGroup= */ 0, state)) .isEqualTo(100); assertThat( getStreamPositionUsForAd( - /* positionUs= */ 200, /* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0, state)) + /* positionUs= */ 200, /* adGroupIndex= */ 2, /* adIndexInAdGroup= */ 0, state)) .isEqualTo(200); assertThat( getStreamPositionUsForAd( - /* positionUs= */ -50, /* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 1, state)) + /* positionUs= */ -50, /* adGroupIndex= */ 2, /* adIndexInAdGroup= */ 1, state)) .isEqualTo(100); assertThat( getStreamPositionUsForAd( - /* positionUs= */ 0, /* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 1, state)) + /* positionUs= */ 0, /* adGroupIndex= */ 2, /* adIndexInAdGroup= */ 1, state)) .isEqualTo(150); assertThat( getStreamPositionUsForAd( - /* positionUs= */ 100, /* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 1, state)) + /* positionUs= */ 100, /* adGroupIndex= */ 2, /* adIndexInAdGroup= */ 1, state)) .isEqualTo(250); assertThat( getStreamPositionUsForAd( - /* positionUs= */ -50, /* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0, state)) + /* positionUs= */ -50, /* adGroupIndex= */ 3, /* adIndexInAdGroup= */ 0, state)) .isEqualTo(2050); assertThat( getStreamPositionUsForAd( - /* positionUs= */ 0, /* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0, state)) + /* positionUs= */ 0, /* adGroupIndex= */ 3, /* adIndexInAdGroup= */ 0, state)) .isEqualTo(2100); assertThat( getStreamPositionUsForAd( - /* positionUs= */ 200, /* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0, state)) + /* positionUs= */ 200, /* adGroupIndex= */ 3, /* adIndexInAdGroup= */ 0, state)) .isEqualTo(2300); assertThat( getStreamPositionUsForAd( - /* positionUs= */ 300, /* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0, state)) + /* positionUs= */ 300, /* adGroupIndex= */ 3, /* adIndexInAdGroup= */ 0, state)) .isEqualTo(2400); assertThat( getStreamPositionUsForAd( - /* positionUs= */ -50, /* adGroupIndex= */ 2, /* adIndexInAdGroup= */ 0, state)) + /* positionUs= */ -50, /* adGroupIndex= */ 4, /* adIndexInAdGroup= */ 0, state)) .isEqualTo(4250); assertThat( getStreamPositionUsForAd( - /* positionUs= */ 0, /* adGroupIndex= */ 2, /* adIndexInAdGroup= */ 0, state)) + /* positionUs= */ 0, /* adGroupIndex= */ 4, /* adIndexInAdGroup= */ 0, state)) .isEqualTo(4300); assertThat( getStreamPositionUsForAd( - /* positionUs= */ 100, /* adGroupIndex= */ 2, /* adIndexInAdGroup= */ 0, state)) + /* positionUs= */ 100, /* adGroupIndex= */ 4, /* adIndexInAdGroup= */ 0, state)) .isEqualTo(4400); assertThat( getStreamPositionUsForAd( - /* positionUs= */ -50, /* adGroupIndex= */ 2, /* adIndexInAdGroup= */ 1, state)) + /* positionUs= */ -50, /* adGroupIndex= */ 4, /* adIndexInAdGroup= */ 1, state)) .isEqualTo(4300); assertThat( getStreamPositionUsForAd( - /* positionUs= */ 0, /* adGroupIndex= */ 2, /* adIndexInAdGroup= */ 1, state)) + /* positionUs= */ 0, /* adGroupIndex= */ 4, /* adIndexInAdGroup= */ 1, state)) .isEqualTo(4350); assertThat( getStreamPositionUsForAd( - /* positionUs= */ 100, /* adGroupIndex= */ 2, /* adIndexInAdGroup= */ 1, state)) + /* positionUs= */ 100, /* adGroupIndex= */ 4, /* adIndexInAdGroup= */ 1, state)) .isEqualTo(4450); assertThat( getStreamPositionUsForAd( - /* positionUs= */ -50, /* adGroupIndex= */ 2, /* adIndexInAdGroup= */ 2, state)) + /* positionUs= */ -50, /* adGroupIndex= */ 4, /* adIndexInAdGroup= */ 2, state)) .isEqualTo(4350); assertThat( getStreamPositionUsForAd( - /* positionUs= */ 50, /* adGroupIndex= */ 2, /* adIndexInAdGroup= */ 2, state)) + /* positionUs= */ 50, /* adGroupIndex= */ 4, /* adIndexInAdGroup= */ 2, state)) .isEqualTo(4450); assertThat( getStreamPositionUsForAd( - /* positionUs= */ 150, /* adGroupIndex= */ 2, /* adIndexInAdGroup= */ 2, state)) + /* positionUs= */ 150, /* adGroupIndex= */ 4, /* adIndexInAdGroup= */ 2, state)) .isEqualTo(4550); } @@ -241,105 +248,103 @@ public final class ServerSideInsertedAdsUtilTest { // stream: 0-- ad1 --200-- content --2100-- ad2 --2300-- content --4300-- ad3 --4500-- content // content timeline: 0 - [ad1] - 100-2000 -[ad2] - 2000-4000 - [ad3] - 4400-end AdPlaybackState state = - new AdPlaybackState(ADS_ID, /* adGroupTimesUs...= */ 0, 2000, 4000) - .withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 2) - .withAdCount(/* adGroupIndex= */ 1, /* adCount= */ 1) - .withAdCount(/* adGroupIndex= */ 2, /* adCount= */ 3) - .withContentResumeOffsetUs(/* adGroupIndex= */ 0, /* contentResumeOffsetUs= */ 100) - .withContentResumeOffsetUs(/* adGroupIndex= */ 1, /* contentResumeOffsetUs= */ 0) - .withContentResumeOffsetUs(/* adGroupIndex= */ 2, /* contentResumeOffsetUs= */ 400) - .withAdDurationsUs( - new long[][] { - new long[] {150, 50}, - new long[] {200}, - new long[] {50, 50, 100} - }); + new AdPlaybackState(ADS_ID, /* adGroupTimesUs...= */ 0, 0, 0, 2000, 4000) + .withRemovedAdGroupCount(2) + .withAdCount(/* adGroupIndex= */ 2, /* adCount= */ 2) + .withAdCount(/* adGroupIndex= */ 3, /* adCount= */ 1) + .withAdCount(/* adGroupIndex= */ 4, /* adCount= */ 3) + .withContentResumeOffsetUs(/* adGroupIndex= */ 2, /* contentResumeOffsetUs= */ 100) + .withContentResumeOffsetUs(/* adGroupIndex= */ 3, /* contentResumeOffsetUs= */ 0) + .withContentResumeOffsetUs(/* adGroupIndex= */ 4, /* contentResumeOffsetUs= */ 400) + .withAdDurationsUs(/* adGroupIndex= */ 2, /* adDurationsUs...= */ 150, 50) + .withAdDurationsUs(/* adGroupIndex= */ 3, /* adDurationsUs...= */ 200) + .withAdDurationsUs(/* adGroupIndex= */ 4, /* adDurationsUs...= */ 50, 50, 100); assertThat( getMediaPeriodPositionUsForAd( - /* positionUs= */ 0, /* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0, state)) + /* positionUs= */ 0, /* adGroupIndex= */ 2, /* adIndexInAdGroup= */ 0, state)) .isEqualTo(0); assertThat( getMediaPeriodPositionUsForAd( - /* positionUs= */ 100, /* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0, state)) + /* positionUs= */ 100, /* adGroupIndex= */ 2, /* adIndexInAdGroup= */ 0, state)) .isEqualTo(100); assertThat( getMediaPeriodPositionUsForAd( - /* positionUs= */ 100, /* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 1, state)) + /* positionUs= */ 100, /* adGroupIndex= */ 2, /* adIndexInAdGroup= */ 1, state)) .isEqualTo(-50); assertThat( getMediaPeriodPositionUsForAd( - /* positionUs= */ 200, /* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0, state)) + /* positionUs= */ 200, /* adGroupIndex= */ 2, /* adIndexInAdGroup= */ 0, state)) .isEqualTo(200); assertThat( getMediaPeriodPositionUsForAd( - /* positionUs= */ 200, /* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 1, state)) + /* positionUs= */ 200, /* adGroupIndex= */ 2, /* adIndexInAdGroup= */ 1, state)) .isEqualTo(50); assertThat( getMediaPeriodPositionUsForAd( - /* positionUs= */ 300, /* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 1, state)) + /* positionUs= */ 300, /* adGroupIndex= */ 2, /* adIndexInAdGroup= */ 1, state)) .isEqualTo(150); assertThat( getMediaPeriodPositionUsForAd( - /* positionUs= */ 2000, /* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0, state)) + /* positionUs= */ 2000, /* adGroupIndex= */ 3, /* adIndexInAdGroup= */ 0, state)) .isEqualTo(-100); assertThat( getMediaPeriodPositionUsForAd( - /* positionUs= */ 2100, /* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0, state)) + /* positionUs= */ 2100, /* adGroupIndex= */ 3, /* adIndexInAdGroup= */ 0, state)) .isEqualTo(0); assertThat( getMediaPeriodPositionUsForAd( - /* positionUs= */ 2300, /* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0, state)) + /* positionUs= */ 2300, /* adGroupIndex= */ 3, /* adIndexInAdGroup= */ 0, state)) .isEqualTo(200); assertThat( getMediaPeriodPositionUsForAd( - /* positionUs= */ 4300, /* adGroupIndex= */ 2, /* adIndexInAdGroup= */ 0, state)) + /* positionUs= */ 4300, /* adGroupIndex= */ 4, /* adIndexInAdGroup= */ 0, state)) .isEqualTo(0); assertThat( getMediaPeriodPositionUsForAd( - /* positionUs= */ 4300, /* adGroupIndex= */ 2, /* adIndexInAdGroup= */ 1, state)) + /* positionUs= */ 4300, /* adGroupIndex= */ 4, /* adIndexInAdGroup= */ 1, state)) .isEqualTo(-50); assertThat( getMediaPeriodPositionUsForAd( - /* positionUs= */ 4300, /* adGroupIndex= */ 2, /* adIndexInAdGroup= */ 2, state)) + /* positionUs= */ 4300, /* adGroupIndex= */ 4, /* adIndexInAdGroup= */ 2, state)) .isEqualTo(-100); assertThat( getMediaPeriodPositionUsForAd( - /* positionUs= */ 4400, /* adGroupIndex= */ 2, /* adIndexInAdGroup= */ 0, state)) + /* positionUs= */ 4400, /* adGroupIndex= */ 4, /* adIndexInAdGroup= */ 0, state)) .isEqualTo(100); assertThat( getMediaPeriodPositionUsForAd( - /* positionUs= */ 4400, /* adGroupIndex= */ 2, /* adIndexInAdGroup= */ 1, state)) + /* positionUs= */ 4400, /* adGroupIndex= */ 4, /* adIndexInAdGroup= */ 1, state)) .isEqualTo(50); assertThat( getMediaPeriodPositionUsForAd( - /* positionUs= */ 4400, /* adGroupIndex= */ 2, /* adIndexInAdGroup= */ 2, state)) + /* positionUs= */ 4400, /* adGroupIndex= */ 4, /* adIndexInAdGroup= */ 2, state)) .isEqualTo(0); assertThat( getMediaPeriodPositionUsForAd( - /* positionUs= */ 4500, /* adGroupIndex= */ 2, /* adIndexInAdGroup= */ 1, state)) + /* positionUs= */ 4500, /* adGroupIndex= */ 4, /* adIndexInAdGroup= */ 1, state)) .isEqualTo(150); assertThat( getMediaPeriodPositionUsForAd( - /* positionUs= */ 4500, /* adGroupIndex= */ 2, /* adIndexInAdGroup= */ 2, state)) + /* positionUs= */ 4500, /* adGroupIndex= */ 4, /* adIndexInAdGroup= */ 2, state)) .isEqualTo(100); assertThat( getMediaPeriodPositionUsForAd( - /* positionUs= */ 4700, /* adGroupIndex= */ 2, /* adIndexInAdGroup= */ 1, state)) + /* positionUs= */ 4700, /* adGroupIndex= */ 4, /* adIndexInAdGroup= */ 1, state)) .isEqualTo(350); assertThat( getMediaPeriodPositionUsForAd( - /* positionUs= */ 4700, /* adGroupIndex= */ 2, /* adIndexInAdGroup= */ 2, state)) + /* positionUs= */ 4700, /* adGroupIndex= */ 4, /* adIndexInAdGroup= */ 2, state)) .isEqualTo(300); } @@ -348,23 +353,21 @@ public final class ServerSideInsertedAdsUtilTest { // stream: 0-- ad1 --200-- content --2100-- ad2 --2300-- content --4300-- ad3 --4500-- content // content timeline: 0 - [ad1] - 100-2000 -[ad2] - 2000-4000 - [ad3] - 4400-end AdPlaybackState state = - new AdPlaybackState(ADS_ID, /* adGroupTimesUs...= */ 0, 2000, 4000) - .withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 2) - .withAdCount(/* adGroupIndex= */ 1, /* adCount= */ 1) - .withAdCount(/* adGroupIndex= */ 2, /* adCount= */ 3) - .withContentResumeOffsetUs(/* adGroupIndex= */ 0, /* contentResumeOffsetUs= */ 100) - .withContentResumeOffsetUs(/* adGroupIndex= */ 1, /* contentResumeOffsetUs= */ 0) - .withContentResumeOffsetUs(/* adGroupIndex= */ 2, /* contentResumeOffsetUs= */ 400) - .withAdDurationsUs( - new long[][] { - new long[] {150, 50}, - new long[] {200}, - new long[] {50, 50, 100} - }); + new AdPlaybackState(ADS_ID, /* adGroupTimesUs...= */ 0, 0, 0, 2000, 4000) + .withRemovedAdGroupCount(2) + .withAdCount(/* adGroupIndex= */ 2, /* adCount= */ 2) + .withAdCount(/* adGroupIndex= */ 3, /* adCount= */ 1) + .withAdCount(/* adGroupIndex= */ 4, /* adCount= */ 3) + .withContentResumeOffsetUs(/* adGroupIndex= */ 2, /* contentResumeOffsetUs= */ 100) + .withContentResumeOffsetUs(/* adGroupIndex= */ 3, /* contentResumeOffsetUs= */ 0) + .withContentResumeOffsetUs(/* adGroupIndex= */ 4, /* contentResumeOffsetUs= */ 400) + .withAdDurationsUs(/* adGroupIndex= */ 2, /* adDurationsUs...= */ 150, 50) + .withAdDurationsUs(/* adGroupIndex= */ 3, /* adDurationsUs...= */ 200) + .withAdDurationsUs(/* adGroupIndex= */ 4, /* adDurationsUs...= */ 50, 50, 100); - assertThat(getStreamPositionUsForContent(/* positionUs= */ 0, /* nextAdGroupIndex= */ 0, state)) + assertThat(getStreamPositionUsForContent(/* positionUs= */ 0, /* nextAdGroupIndex= */ 2, state)) .isEqualTo(0); - assertThat(getStreamPositionUsForContent(/* positionUs= */ 0, /* nextAdGroupIndex= */ 1, state)) + assertThat(getStreamPositionUsForContent(/* positionUs= */ 0, /* nextAdGroupIndex= */ 3, state)) .isEqualTo(100); assertThat( getStreamPositionUsForContent( @@ -372,10 +375,10 @@ public final class ServerSideInsertedAdsUtilTest { .isEqualTo(100); assertThat( - getStreamPositionUsForContent(/* positionUs= */ 50, /* nextAdGroupIndex= */ 0, state)) + getStreamPositionUsForContent(/* positionUs= */ 50, /* nextAdGroupIndex= */ 2, state)) .isEqualTo(50); assertThat( - getStreamPositionUsForContent(/* positionUs= */ 50, /* nextAdGroupIndex= */ 1, state)) + getStreamPositionUsForContent(/* positionUs= */ 50, /* nextAdGroupIndex= */ 3, state)) .isEqualTo(150); assertThat( getStreamPositionUsForContent( @@ -383,7 +386,7 @@ public final class ServerSideInsertedAdsUtilTest { .isEqualTo(150); assertThat( - getStreamPositionUsForContent(/* positionUs= */ 100, /* nextAdGroupIndex= */ 1, state)) + getStreamPositionUsForContent(/* positionUs= */ 100, /* nextAdGroupIndex= */ 3, state)) .isEqualTo(200); assertThat( getStreamPositionUsForContent( @@ -391,7 +394,7 @@ public final class ServerSideInsertedAdsUtilTest { .isEqualTo(200); assertThat( - getStreamPositionUsForContent(/* positionUs= */ 1999, /* nextAdGroupIndex= */ 1, state)) + getStreamPositionUsForContent(/* positionUs= */ 1999, /* nextAdGroupIndex= */ 3, state)) .isEqualTo(2099); assertThat( getStreamPositionUsForContent( @@ -399,10 +402,10 @@ public final class ServerSideInsertedAdsUtilTest { .isEqualTo(2099); assertThat( - getStreamPositionUsForContent(/* positionUs= */ 2000, /* nextAdGroupIndex= */ 1, state)) + getStreamPositionUsForContent(/* positionUs= */ 2000, /* nextAdGroupIndex= */ 3, state)) .isEqualTo(2100); assertThat( - getStreamPositionUsForContent(/* positionUs= */ 2000, /* nextAdGroupIndex= */ 2, state)) + getStreamPositionUsForContent(/* positionUs= */ 2000, /* nextAdGroupIndex= */ 4, state)) .isEqualTo(2300); assertThat( getStreamPositionUsForContent( @@ -410,7 +413,7 @@ public final class ServerSideInsertedAdsUtilTest { .isEqualTo(2300); assertThat( - getStreamPositionUsForContent(/* positionUs= */ 3999, /* nextAdGroupIndex= */ 2, state)) + getStreamPositionUsForContent(/* positionUs= */ 3999, /* nextAdGroupIndex= */ 4, state)) .isEqualTo(4299); assertThat( getStreamPositionUsForContent( @@ -418,7 +421,7 @@ public final class ServerSideInsertedAdsUtilTest { .isEqualTo(4299); assertThat( - getStreamPositionUsForContent(/* positionUs= */ 4000, /* nextAdGroupIndex= */ 2, state)) + getStreamPositionUsForContent(/* positionUs= */ 4000, /* nextAdGroupIndex= */ 4, state)) .isEqualTo(4300); assertThat( getStreamPositionUsForContent( @@ -446,27 +449,25 @@ public final class ServerSideInsertedAdsUtilTest { // stream: 0-- ad1 --200-- content --2100-- ad2 --2300-- content --4300-- ad3 --4500-- content // content timeline: 0 - [ad1] - 100-2000 -[ad2] - 2000-4000 - [ad3] - 4400-end AdPlaybackState state = - new AdPlaybackState(ADS_ID, /* adGroupTimesUs...= */ 0, 2000, 4000) - .withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 2) - .withAdCount(/* adGroupIndex= */ 1, /* adCount= */ 1) - .withAdCount(/* adGroupIndex= */ 2, /* adCount= */ 3) - .withContentResumeOffsetUs(/* adGroupIndex= */ 0, /* contentResumeOffsetUs= */ 100) - .withContentResumeOffsetUs(/* adGroupIndex= */ 1, /* contentResumeOffsetUs= */ 0) - .withContentResumeOffsetUs(/* adGroupIndex= */ 2, /* contentResumeOffsetUs= */ 400) - .withAdDurationsUs( - new long[][] { - new long[] {150, 50}, - new long[] {200}, - new long[] {50, 50, 100} - }); + new AdPlaybackState(ADS_ID, /* adGroupTimesUs...= */ 0, 0, 0, 2000, 4000) + .withRemovedAdGroupCount(2) + .withAdCount(/* adGroupIndex= */ 2, /* adCount= */ 2) + .withAdCount(/* adGroupIndex= */ 3, /* adCount= */ 1) + .withAdCount(/* adGroupIndex= */ 4, /* adCount= */ 3) + .withContentResumeOffsetUs(/* adGroupIndex= */ 2, /* contentResumeOffsetUs= */ 100) + .withContentResumeOffsetUs(/* adGroupIndex= */ 3, /* contentResumeOffsetUs= */ 0) + .withContentResumeOffsetUs(/* adGroupIndex= */ 4, /* contentResumeOffsetUs= */ 400) + .withAdDurationsUs(/* adGroupIndex= */ 2, /* adDurationsUs...= */ 150, 50) + .withAdDurationsUs(/* adGroupIndex= */ 3, /* adDurationsUs...= */ 200) + .withAdDurationsUs(/* adGroupIndex= */ 4, /* adDurationsUs...= */ 50, 50, 100); assertThat( getMediaPeriodPositionUsForContent( - /* positionUs= */ 0, /* nextAdGroupIndex= */ 0, state)) + /* positionUs= */ 0, /* nextAdGroupIndex= */ 2, state)) .isEqualTo(0); assertThat( getMediaPeriodPositionUsForContent( - /* positionUs= */ 0, /* nextAdGroupIndex= */ 1, state)) + /* positionUs= */ 0, /* nextAdGroupIndex= */ 3, state)) .isEqualTo(0); assertThat( getMediaPeriodPositionUsForContent( @@ -475,11 +476,11 @@ public final class ServerSideInsertedAdsUtilTest { assertThat( getMediaPeriodPositionUsForContent( - /* positionUs= */ 100, /* nextAdGroupIndex= */ 0, state)) + /* positionUs= */ 100, /* nextAdGroupIndex= */ 2, state)) .isEqualTo(100); assertThat( getMediaPeriodPositionUsForContent( - /* positionUs= */ 100, /* nextAdGroupIndex= */ 1, state)) + /* positionUs= */ 100, /* nextAdGroupIndex= */ 3, state)) .isEqualTo(0); assertThat( getMediaPeriodPositionUsForContent( @@ -488,7 +489,7 @@ public final class ServerSideInsertedAdsUtilTest { assertThat( getMediaPeriodPositionUsForContent( - /* positionUs= */ 200, /* nextAdGroupIndex= */ 1, state)) + /* positionUs= */ 200, /* nextAdGroupIndex= */ 3, state)) .isEqualTo(100); assertThat( getMediaPeriodPositionUsForContent( @@ -497,7 +498,7 @@ public final class ServerSideInsertedAdsUtilTest { assertThat( getMediaPeriodPositionUsForContent( - /* positionUs= */ 2099, /* nextAdGroupIndex= */ 1, state)) + /* positionUs= */ 2099, /* nextAdGroupIndex= */ 3, state)) .isEqualTo(1999); assertThat( getMediaPeriodPositionUsForContent( @@ -506,11 +507,11 @@ public final class ServerSideInsertedAdsUtilTest { assertThat( getMediaPeriodPositionUsForContent( - /* positionUs= */ 2100, /* nextAdGroupIndex= */ 1, state)) + /* positionUs= */ 2100, /* nextAdGroupIndex= */ 3, state)) .isEqualTo(2000); assertThat( getMediaPeriodPositionUsForContent( - /* positionUs= */ 2100, /* nextAdGroupIndex= */ 2, state)) + /* positionUs= */ 2100, /* nextAdGroupIndex= */ 4, state)) .isEqualTo(2000); assertThat( getMediaPeriodPositionUsForContent( @@ -519,11 +520,11 @@ public final class ServerSideInsertedAdsUtilTest { assertThat( getMediaPeriodPositionUsForContent( - /* positionUs= */ 2300, /* nextAdGroupIndex= */ 1, state)) + /* positionUs= */ 2300, /* nextAdGroupIndex= */ 3, state)) .isEqualTo(2200); assertThat( getMediaPeriodPositionUsForContent( - /* positionUs= */ 2300, /* nextAdGroupIndex= */ 2, state)) + /* positionUs= */ 2300, /* nextAdGroupIndex= */ 4, state)) .isEqualTo(2000); assertThat( getMediaPeriodPositionUsForContent( @@ -532,7 +533,7 @@ public final class ServerSideInsertedAdsUtilTest { assertThat( getMediaPeriodPositionUsForContent( - /* positionUs= */ 4299, /* nextAdGroupIndex= */ 2, state)) + /* positionUs= */ 4299, /* nextAdGroupIndex= */ 4, state)) .isEqualTo(3999); assertThat( getMediaPeriodPositionUsForContent( @@ -541,7 +542,7 @@ public final class ServerSideInsertedAdsUtilTest { assertThat( getMediaPeriodPositionUsForContent( - /* positionUs= */ 4300, /* nextAdGroupIndex= */ 2, state)) + /* positionUs= */ 4300, /* nextAdGroupIndex= */ 4, state)) .isEqualTo(4000); assertThat( getMediaPeriodPositionUsForContent( @@ -550,7 +551,7 @@ public final class ServerSideInsertedAdsUtilTest { assertThat( getMediaPeriodPositionUsForContent( - /* positionUs= */ 4500, /* nextAdGroupIndex= */ 2, state)) + /* positionUs= */ 4500, /* nextAdGroupIndex= */ 4, state)) .isEqualTo(4200); assertThat( getMediaPeriodPositionUsForContent( diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerControlView.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerControlView.java index 11d584e466..9aae362196 100644 --- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerControlView.java +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerControlView.java @@ -982,8 +982,9 @@ public class PlayerControlView extends FrameLayout { } for (int j = window.firstPeriodIndex; j <= window.lastPeriodIndex; j++) { timeline.getPeriod(j, period); - int periodAdGroupCount = period.getAdGroupCount(); - for (int adGroupIndex = 0; adGroupIndex < periodAdGroupCount; adGroupIndex++) { + int removedGroups = period.getRemovedAdGroupCount(); + int totalGroups = period.getAdGroupCount(); + for (int adGroupIndex = removedGroups; adGroupIndex < totalGroups; adGroupIndex++) { long adGroupTimeInPeriodUs = period.getAdGroupTimeUs(adGroupIndex); if (adGroupTimeInPeriodUs == C.TIME_END_OF_SOURCE) { if (period.durationUs == C.TIME_UNSET) { diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/StyledPlayerControlView.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/StyledPlayerControlView.java index 62aeb2bfe4..7a24d1244e 100644 --- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/StyledPlayerControlView.java +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/StyledPlayerControlView.java @@ -1345,8 +1345,9 @@ public class StyledPlayerControlView extends FrameLayout { } for (int j = window.firstPeriodIndex; j <= window.lastPeriodIndex; j++) { timeline.getPeriod(j, period); - int periodAdGroupCount = period.getAdGroupCount(); - for (int adGroupIndex = 0; adGroupIndex < periodAdGroupCount; adGroupIndex++) { + int removedGroups = period.getRemovedAdGroupCount(); + int totalGroups = period.getAdGroupCount(); + for (int adGroupIndex = removedGroups; adGroupIndex < totalGroups; adGroupIndex++) { long adGroupTimeInPeriodUs = period.getAdGroupTimeUs(adGroupIndex); if (adGroupTimeInPeriodUs == C.TIME_END_OF_SOURCE) { if (period.durationUs == C.TIME_UNSET) {