diff --git a/libraries/common/src/main/java/androidx/media3/common/util/Util.java b/libraries/common/src/main/java/androidx/media3/common/util/Util.java index 69c78eeb6d..84a9a570be 100644 --- a/libraries/common/src/main/java/androidx/media3/common/util/Util.java +++ b/libraries/common/src/main/java/androidx/media3/common/util/Util.java @@ -2539,6 +2539,20 @@ public final class Util { .build(); } + /** + * Returns the sum of all summands of the given array. + * + * @param summands The summands to calculate the sum from. + * @return The sum of all summands. + */ + public static long sum(long... summands) { + long sum = 0; + for (long summand : summands) { + sum += summand; + } + return sum; + } + @Nullable private static String getSystemProperty(String name) { try { diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ads/ServerSideAdInsertionUtil.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ads/ServerSideAdInsertionUtil.java index 052181ec33..53e3f814ba 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ads/ServerSideAdInsertionUtil.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ads/ServerSideAdInsertionUtil.java @@ -15,6 +15,7 @@ */ package androidx.media3.exoplayer.source.ads; +import static androidx.media3.common.util.Util.sum; import static java.lang.Math.max; import androidx.annotation.CheckResult; @@ -36,23 +37,25 @@ public final class ServerSideAdInsertionUtil { /** * Adds a new server-side inserted ad group to an {@link AdPlaybackState}. * + *
If the first ad with a non-zero duration is not the first ad in the group, all ads before
+ * that ad are marked as skipped.
+ *
* @param adPlaybackState The existing {@link AdPlaybackState}.
* @param fromPositionUs The position in the underlying server-side inserted ads stream at which
* the ad group starts, in microseconds.
- * @param toPositionUs The position in the underlying server-side inserted ads stream at which the
- * ad group ends, in microseconds.
* @param contentResumeOffsetUs The timestamp offset which should be added to the content stream
* when resuming playback after the ad group. An offset of 0 collapses the ad group to a
* single insertion point, an offset of {@code toPositionUs-fromPositionUs} keeps the original
* stream timestamps after the ad group.
+ * @param adDurationsUs The durations of the ads to be added to the group, in microseconds.
* @return The updated {@link AdPlaybackState}.
*/
@CheckResult
public static AdPlaybackState addAdGroupToAdPlaybackState(
AdPlaybackState adPlaybackState,
long fromPositionUs,
- long toPositionUs,
- long contentResumeOffsetUs) {
+ long contentResumeOffsetUs,
+ long... adDurationsUs) {
long adGroupInsertionPositionUs =
getMediaPeriodPositionUsForContent(
fromPositionUs, /* nextAdGroupIndex= */ C.INDEX_UNSET, adPlaybackState);
@@ -62,16 +65,21 @@ public final class ServerSideAdInsertionUtil {
&& adPlaybackState.getAdGroup(insertionIndex).timeUs <= adGroupInsertionPositionUs) {
insertionIndex++;
}
- long adDurationUs = toPositionUs - fromPositionUs;
adPlaybackState =
adPlaybackState
.withNewAdGroup(insertionIndex, adGroupInsertionPositionUs)
.withIsServerSideInserted(insertionIndex, /* isServerSideInserted= */ true)
- .withAdCount(insertionIndex, /* adCount= */ 1)
- .withAdDurationsUs(insertionIndex, adDurationUs)
+ .withAdCount(insertionIndex, /* adCount= */ adDurationsUs.length)
+ .withAdDurationsUs(insertionIndex, adDurationsUs)
.withContentResumeOffsetUs(insertionIndex, contentResumeOffsetUs);
+ // Mark all ads as skipped that are before the first ad with a non-zero duration.
+ int adIndex = 0;
+ while (adIndex < adDurationsUs.length && adDurationsUs[adIndex] == 0) {
+ adPlaybackState =
+ adPlaybackState.withSkippedAd(insertionIndex, /* adIndexInAdGroup= */ adIndex++);
+ }
return correctFollowingAdGroupTimes(
- adPlaybackState, insertionIndex, adDurationUs, contentResumeOffsetUs);
+ adPlaybackState, insertionIndex, sum(adDurationsUs), contentResumeOffsetUs);
}
/**
diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/ads/ServerSideAdInsertionMediaSourceTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/ads/ServerSideAdInsertionMediaSourceTest.java
index 4db0210883..ef8d30a56c 100644
--- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/ads/ServerSideAdInsertionMediaSourceTest.java
+++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/ads/ServerSideAdInsertionMediaSourceTest.java
@@ -183,20 +183,20 @@ public final class ServerSideAdInsertionMediaSourceTest {
addAdGroupToAdPlaybackState(
adPlaybackState,
/* fromPositionUs= */ 0,
- /* toPositionUs= */ 200_000,
- /* contentResumeOffsetUs= */ 0);
+ /* contentResumeOffsetUs= */ 0,
+ /* adDurationsUs...= */ 200_000);
adPlaybackState =
addAdGroupToAdPlaybackState(
adPlaybackState,
/* fromPositionUs= */ 400_000,
- /* toPositionUs= */ 700_000,
- /* contentResumeOffsetUs= */ 1_000_000);
+ /* contentResumeOffsetUs= */ 1_000_000,
+ /* adDurationsUs...= */ 300_000);
AdPlaybackState firstAdPlaybackState =
addAdGroupToAdPlaybackState(
adPlaybackState,
/* fromPositionUs= */ 900_000,
- /* toPositionUs= */ 1_000_000,
- /* contentResumeOffsetUs= */ 0);
+ /* contentResumeOffsetUs= */ 0,
+ /* adDurationsUs...= */ 100_000);
AtomicReference