Make AdPlaybackState.withAdDurationsUs work with removed ad groups

The assertion is changed to check that the number of passed
in arrays of durations is always matching `adGroupCount`
according to the behavior of `withRemoveAdGroupCount(int)`.

Issue: androidx/media#2267
PiperOrigin-RevId: 743185176
This commit is contained in:
bachinger 2025-04-02 10:13:23 -07:00 committed by Copybara-Service
parent a9c0349214
commit d133300627
3 changed files with 123 additions and 4 deletions

View File

@ -15,6 +15,10 @@
* Add `ExoPlayer.setScrubbingModeEnabled(boolean)` method. This optimizes
the player for many frequent seeks (for example, from a user dragging a
scrubber bar around).
* `AdPlaybackState.withAdDurationsUs(long[][])` can be used after ad
groups have been removed. The user still needs to pass in an array of
durations for removed ad groups which can be empty or null
([#2267](https://github.com/androidx/media/issues/2267)).
* Transformer:
* Filling an initial gap (added via `addGap()`) with silent audio now
requires explicitly setting `setForceAudioTrack(true)` in

View File

@ -1072,14 +1072,23 @@ public final class AdPlaybackState {
/**
* Returns an instance with the specified ad durations, in microseconds.
*
* <p>Must only be used if {@link #removedAdGroupCount} is 0.
* <p>The number of arrays of durations ({@code adDurations.length}) must always be equal to
* {@link #adGroupCount}. This is required even on an instance created with {@link
* #withRemovedAdGroupCount(int)}. The array of durations at the index of a removed ad group can
* be null or empty.
*
* @throws IllegalArgumentException if {@code adDurations.length != adGroupCount}.
*/
@CheckResult
public AdPlaybackState withAdDurationsUs(long[][] adDurationUs) {
checkState(removedAdGroupCount == 0);
checkArgument(adDurationUs.length == adGroupCount);
AdGroup[] adGroups = Util.nullSafeArrayCopy(this.adGroups, this.adGroups.length);
for (int adGroupIndex = 0; adGroupIndex < adGroupCount; adGroupIndex++) {
adGroups[adGroupIndex] = adGroups[adGroupIndex].withAdDurationsUs(adDurationUs[adGroupIndex]);
for (int correctedAdGroupIndex = 0;
correctedAdGroupIndex < adGroupCount - removedAdGroupCount;
correctedAdGroupIndex++) {
adGroups[correctedAdGroupIndex] =
adGroups[correctedAdGroupIndex].withAdDurationsUs(
adDurationUs[removedAdGroupCount + correctedAdGroupIndex]);
}
return new AdPlaybackState(
adsId, adGroups, adResumePositionUs, contentDurationUs, removedAdGroupCount);

View File

@ -1091,4 +1091,110 @@ public class AdPlaybackStateTest {
assertThat(AdPlaybackState.AdGroup.fromBundle(adGroup.toBundle()).ids[1]).isNull();
assertThat(AdPlaybackState.AdGroup.fromBundle(adGroup.toBundle())).isEqualTo(adGroup);
}
@Test
public void setDurationsUs_withRemovedAdGroups_updatedCorrectlyAndSafely() {
AdPlaybackState adPlaybackState =
new AdPlaybackState("adsId")
.withLivePostrollPlaceholderAppended(false)
.withNewAdGroup(/* adGroupIndex= */ 0, 10_000)
.withAdCount(/* adGroupIndex= */ 0, 1)
.withAvailableAdMediaItem(
/* adGroupIndex= */ 0,
/* adIndexInAdGroup= */ 0,
MediaItem.fromUri("http://example.com/0-0"))
.withNewAdGroup(/* adGroupIndex= */ 1, 11_000)
.withAdCount(/* adGroupIndex= */ 1, 2)
.withAvailableAdMediaItem(
/* adGroupIndex= */ 1,
/* adIndexInAdGroup= */ 0,
MediaItem.fromUri("http://example.com/1-0"))
.withAvailableAdMediaItem(
/* adGroupIndex= */ 1,
/* adIndexInAdGroup= */ 1,
MediaItem.fromUri("http://example.com/1-1"))
.withNewAdGroup(/* adGroupIndex= */ 2, 12_000)
.withAdCount(/* adGroupIndex= */ 2, 1)
.withAvailableAdMediaItem(
/* adGroupIndex= */ 2,
/* adIndexInAdGroup= */ 0,
MediaItem.fromUri("http://example.com/2-0"));
long[][] adDurationsUs = {
new long[] {10L}, new long[] {20L, 21L}, new long[] {30L}, new long[] {C.TIME_END_OF_SOURCE}
};
adPlaybackState =
adPlaybackState
.withAdDurationsUs(adDurationsUs)
.withRemovedAdGroupCount(/* removedAdGroupCount= */ 1);
assertThat(adPlaybackState.adGroupCount).isEqualTo(4);
assertThat(adPlaybackState.getAdGroup(/* adGroupIndex= */ 0).durationsUs).hasLength(0);
assertThat(adPlaybackState.getAdGroup(/* adGroupIndex= */ 0).count).isEqualTo(0);
assertThat(adPlaybackState.getAdGroup(/* adGroupIndex= */ 0).states).hasLength(0);
assertThat(adPlaybackState.getAdGroup(/* adGroupIndex= */ 0).isPlaceholder).isFalse();
assertThat(adPlaybackState.getAdGroup(/* adGroupIndex= */ 0).mediaItems).hasLength(0);
assertThat(adPlaybackState.getAdGroup(/* adGroupIndex= */ 0).ids).hasLength(0);
assertThat(adPlaybackState.getAdGroup(/* adGroupIndex= */ 1).durationsUs)
.asList()
.containsExactly(20L, 21L)
.inOrder();
assertThat(adPlaybackState.getAdGroup(/* adGroupIndex= */ 2).durationsUs)
.asList()
.containsExactly(30L);
assertThat(adPlaybackState.getAdGroup(/* adGroupIndex= */ 3).durationsUs)
.asList()
.containsExactly(C.TIME_END_OF_SOURCE);
adDurationsUs[1][0] = 120L;
adDurationsUs[1][1] = 121L;
adPlaybackState = adPlaybackState.withAdDurationsUs(adDurationsUs);
assertThat(adPlaybackState.getAdGroup(/* adGroupIndex= */ 1).durationsUs)
.asList()
.containsExactly(120L, 121L)
.inOrder();
assertThat(adPlaybackState.getAdGroup(/* adGroupIndex= */ 2).durationsUs)
.asList()
.containsExactly(30L);
assertThat(adPlaybackState.getAdGroup(/* adGroupIndex= */ 3).durationsUs)
.asList()
.containsExactly(C.TIME_END_OF_SOURCE);
adDurationsUs[0] = null;
adDurationsUs[1] = null;
adDurationsUs[2][0] = C.TIME_UNSET;
adPlaybackState =
adPlaybackState
.withRemovedAdGroupCount(/* removedAdGroupCount= */ 2)
.withAdDurationsUs(adDurationsUs);
assertThat(adPlaybackState.getAdGroup(/* adGroupIndex= */ 1).durationsUs).hasLength(0);
assertThat(adPlaybackState.getAdGroup(/* adGroupIndex= */ 2).durationsUs)
.asList()
.containsExactly(C.TIME_UNSET);
assertThat(adPlaybackState.getAdGroup(/* adGroupIndex= */ 3).durationsUs)
.asList()
.containsExactly(C.TIME_END_OF_SOURCE);
adDurationsUs[2] = null;
adDurationsUs[3][0] = 0L;
adPlaybackState =
adPlaybackState
.withRemovedAdGroupCount(/* removedAdGroupCount= */ 3)
.withAdDurationsUs(adDurationsUs);
assertThat(adPlaybackState.getAdGroup(/* adGroupIndex= */ 2).durationsUs).hasLength(0);
assertThat(adPlaybackState.getAdGroup(/* adGroupIndex= */ 3).durationsUs)
.asList()
.containsExactly(0L);
adDurationsUs[3] = null;
adPlaybackState =
adPlaybackState
.withRemovedAdGroupCount(/* removedAdGroupCount= */ 4)
.withAdDurationsUs(adDurationsUs);
assertThat(adPlaybackState.getAdGroup(/* adGroupIndex= */ 3).durationsUs).hasLength(0);
}
}