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 5730f551be..0327c4f1a8 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 @@ -804,6 +804,17 @@ public abstract class Timeline implements Bundleable { return adPlaybackState.adResumePositionUs; } + /** + * Returns whether the ad group at index {@code adGroupIndex} is server-side inserted and part + * of the content stream. + * + * @param adGroupIndex The ad group index. + * @return Whether this ad group is server-side inserted and part of the content stream. + */ + public boolean isServerSideInsertedAdGroup(int adGroupIndex) { + return adPlaybackState.adGroups[adGroupIndex].isServerSideInserted; + } + /** * Returns the offset in microseconds which should be added to the content stream when resuming * playback after the specified ad group. 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 c3be231209..760cb32b17 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 @@ -63,6 +63,8 @@ public final class AdPlaybackState implements Bundleable { * after the ad group. */ public final long contentResumeOffsetUs; + /** Whether this ad group is server-side inserted and part of the content stream. */ + public final boolean isServerSideInserted; /** Creates a new ad group with an unspecified number of ads. */ public AdGroup() { @@ -71,7 +73,8 @@ public final class AdPlaybackState implements Bundleable { /* states= */ new int[0], /* uris= */ new Uri[0], /* durationsUs= */ new long[0], - /* contentResumeOffsetUs= */ 0); + /* contentResumeOffsetUs= */ 0, + /* isServerSideInserted= */ false); } private AdGroup( @@ -79,13 +82,15 @@ public final class AdPlaybackState implements Bundleable { @AdState int[] states, @NullableType Uri[] uris, long[] durationsUs, - long contentResumeOffsetUs) { + long contentResumeOffsetUs, + boolean isServerSideInserted) { checkArgument(states.length == uris.length); this.count = count; this.states = states; this.uris = uris; this.durationsUs = durationsUs; this.contentResumeOffsetUs = contentResumeOffsetUs; + this.isServerSideInserted = isServerSideInserted; } /** @@ -130,7 +135,8 @@ public final class AdPlaybackState implements Bundleable { && Arrays.equals(uris, adGroup.uris) && Arrays.equals(states, adGroup.states) && Arrays.equals(durationsUs, adGroup.durationsUs) - && contentResumeOffsetUs == adGroup.contentResumeOffsetUs; + && contentResumeOffsetUs == adGroup.contentResumeOffsetUs + && isServerSideInserted == adGroup.isServerSideInserted; } @Override @@ -140,6 +146,7 @@ public final class AdPlaybackState implements Bundleable { result = 31 * result + Arrays.hashCode(states); result = 31 * result + Arrays.hashCode(durationsUs); result = 31 * result + (int) (contentResumeOffsetUs ^ (contentResumeOffsetUs >>> 32)); + result = 31 * result + (isServerSideInserted ? 1 : 0); return result; } @@ -149,7 +156,8 @@ public final class AdPlaybackState implements Bundleable { @AdState int[] states = copyStatesWithSpaceForAdCount(this.states, count); long[] durationsUs = copyDurationsUsWithSpaceForAdCount(this.durationsUs, count); @NullableType Uri[] uris = Arrays.copyOf(this.uris, count); - return new AdGroup(count, states, uris, durationsUs, contentResumeOffsetUs); + return new AdGroup( + count, states, uris, durationsUs, contentResumeOffsetUs, isServerSideInserted); } /** @@ -166,7 +174,8 @@ public final class AdPlaybackState implements Bundleable { @NullableType Uri[] uris = Arrays.copyOf(this.uris, states.length); uris[index] = uri; states[index] = AD_STATE_AVAILABLE; - return new AdGroup(count, states, uris, durationsUs, contentResumeOffsetUs); + return new AdGroup( + count, states, uris, durationsUs, contentResumeOffsetUs, isServerSideInserted); } /** @@ -193,7 +202,8 @@ public final class AdPlaybackState implements Bundleable { Uri[] uris = this.uris.length == states.length ? this.uris : Arrays.copyOf(this.uris, states.length); states[index] = state; - return new AdGroup(count, states, uris, durationsUs, contentResumeOffsetUs); + return new AdGroup( + count, states, uris, durationsUs, contentResumeOffsetUs, isServerSideInserted); } /** Returns a new instance with the specified ad durations, in microseconds. */ @@ -204,13 +214,22 @@ public final class AdPlaybackState implements Bundleable { } else if (count != C.LENGTH_UNSET && durationsUs.length > uris.length) { durationsUs = Arrays.copyOf(durationsUs, uris.length); } - return new AdGroup(count, states, uris, durationsUs, contentResumeOffsetUs); + return new AdGroup( + count, states, uris, durationsUs, contentResumeOffsetUs, isServerSideInserted); } /** Returns an instance with the specified {@link #contentResumeOffsetUs}. */ @CheckResult public AdGroup withContentResumeOffsetUs(long contentResumeOffsetUs) { - return new AdGroup(count, states, uris, durationsUs, contentResumeOffsetUs); + return new AdGroup( + count, states, uris, durationsUs, contentResumeOffsetUs, isServerSideInserted); + } + + /** Returns an instance with the specified value for {@link #isServerSideInserted}. */ + @CheckResult + public AdGroup withIsServerSideInserted(boolean isServerSideInserted) { + return new AdGroup( + count, states, uris, durationsUs, contentResumeOffsetUs, isServerSideInserted); } /** @@ -225,7 +244,8 @@ public final class AdPlaybackState implements Bundleable { /* states= */ new int[0], /* uris= */ new Uri[0], /* durationsUs= */ new long[0], - contentResumeOffsetUs); + contentResumeOffsetUs, + isServerSideInserted); } int count = this.states.length; @AdState int[] states = Arrays.copyOf(this.states, count); @@ -234,7 +254,8 @@ public final class AdPlaybackState implements Bundleable { states[i] = AD_STATE_SKIPPED; } } - return new AdGroup(count, states, uris, durationsUs, contentResumeOffsetUs); + return new AdGroup( + count, states, uris, durationsUs, contentResumeOffsetUs, isServerSideInserted); } @CheckResult @@ -265,6 +286,7 @@ public final class AdPlaybackState implements Bundleable { FIELD_STATES, FIELD_DURATIONS_US, FIELD_CONTENT_RESUME_OFFSET_US, + FIELD_IS_SERVER_SIDE_INSERTED, }) private @interface FieldNumber {} @@ -273,6 +295,7 @@ public final class AdPlaybackState implements Bundleable { private static final int FIELD_STATES = 2; private static final int FIELD_DURATIONS_US = 3; private static final int FIELD_CONTENT_RESUME_OFFSET_US = 4; + private static final int FIELD_IS_SERVER_SIDE_INSERTED = 5; // putParcelableArrayList actually supports null elements. @SuppressWarnings("nullness:argument.type.incompatible") @@ -285,6 +308,7 @@ public final class AdPlaybackState implements Bundleable { bundle.putIntArray(keyForField(FIELD_STATES), states); bundle.putLongArray(keyForField(FIELD_DURATIONS_US), durationsUs); bundle.putLong(keyForField(FIELD_CONTENT_RESUME_OFFSET_US), contentResumeOffsetUs); + bundle.putBoolean(keyForField(FIELD_IS_SERVER_SIDE_INSERTED), isServerSideInserted); return bundle; } @@ -302,12 +326,14 @@ public final class AdPlaybackState implements Bundleable { int[] states = bundle.getIntArray(keyForField(FIELD_STATES)); @Nullable long[] durationsUs = bundle.getLongArray(keyForField(FIELD_DURATIONS_US)); long contentResumeOffsetUs = bundle.getLong(keyForField(FIELD_CONTENT_RESUME_OFFSET_US)); + boolean isServerSideInserted = bundle.getBoolean(keyForField(FIELD_IS_SERVER_SIDE_INSERTED)); return new AdGroup( count, states == null ? new int[0] : states, uriList == null ? new Uri[0] : uriList.toArray(new Uri[0]), durationsUs == null ? new long[0] : durationsUs, - contentResumeOffsetUs); + contentResumeOffsetUs, + isServerSideInserted); } private static String keyForField(@AdGroup.FieldNumber int field) { @@ -605,6 +631,21 @@ public final class AdPlaybackState implements Bundleable { adsId, adGroupTimesUs, adGroups, adResumePositionUs, contentDurationUs); } + /** + * Returns an instance with the specified value for {@link AdGroup#isServerSideInserted} in the + * specified ad group. + */ + @CheckResult + public AdPlaybackState withIsServerSideInserted(int adGroupIndex, boolean isServerSideInserted) { + if (adGroups[adGroupIndex].isServerSideInserted == isServerSideInserted) { + return this; + } + AdGroup[] adGroups = Util.nullSafeArrayCopy(this.adGroups, this.adGroups.length); + adGroups[adGroupIndex] = adGroups[adGroupIndex].withIsServerSideInserted(isServerSideInserted); + return new AdPlaybackState( + adsId, adGroupTimesUs, adGroups, adResumePositionUs, contentDurationUs); + } + @Override public boolean equals(@Nullable Object o) { if (this == o) { 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 1b8e2e9685..1596b8ccda 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 @@ -195,6 +195,8 @@ public class AdPlaybackStateTest { .withAdUri(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 1, TEST_URI) .withContentResumeOffsetUs(/* adGroupIndex= */ 0, /* contentResumeOffsetUs= */ 4444) .withContentResumeOffsetUs(/* adGroupIndex= */ 1, /* contentResumeOffsetUs= */ 3333) + .withIsServerSideInserted(/* adGroupIndex= */ 0, /* isServerSideInserted= */ true) + .withIsServerSideInserted(/* adGroupIndex= */ 1, /* isServerSideInserted= */ true) .withAdDurationsUs(new long[][] {{12}, {34, 56}}) .withAdResumePositionUs(123) .withContentDurationUs(456); @@ -219,7 +221,8 @@ public class AdPlaybackStateTest { .withAdUri(Uri.parse("https://www.google.com"), /* index= */ 0) .withAdUri(Uri.EMPTY, /* index= */ 1) .withAdDurationsUs(new long[] {1234, 5678}) - .withContentResumeOffsetUs(4444); + .withContentResumeOffsetUs(4444) + .withIsServerSideInserted(true); assertThat(AdPlaybackState.AdGroup.CREATOR.fromBundle(adGroup.toBundle())).isEqualTo(adGroup); }