From 2938d40bf4abd9a6a8d33512200e416071bd91f4 Mon Sep 17 00:00:00 2001 From: tonihei Date: Thu, 27 May 2021 16:07:32 +0100 Subject: [PATCH] Allow to specify the number of removed ad groups in AdPlaybackState. This helps to remove old ad groups (e.g. those that fell out of the live window) to keep the data size of AdPlaybackState small. Also added this case to some existing unit tests to ensure it's covered. PiperOrigin-RevId: 376170653 --- .../google/android/exoplayer2/Timeline.java | 8 + .../source/ads/AdPlaybackState.java | 188 ++++++++--- .../source/ads/AdPlaybackStateTest.java | 231 +++++++------ .../ads/ServerSideInsertedAdsMediaSource.java | 2 +- .../source/ads/ServerSideInsertedAdsUtil.java | 10 +- .../ads/ServerSideInsertedAdsUtilTest.java | 305 +++++++++--------- .../exoplayer2/ui/PlayerControlView.java | 5 +- .../ui/StyledPlayerControlView.java | 5 +- 8 files changed, 438 insertions(+), 316 deletions(-) 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) {