Commit 4fd6d670 by tonihei

Add method to reset ad group from final states to be playable again.

The player will not play ads in final states (played, skipped, error)
again. To allow ads loader customizations to play ads again, we can
add a method that resets the state back to available or unavailable
(depending on whether we have the URI for the ad).

Issue: google/ExoPlayer#9615
PiperOrigin-RevId: 411042842
parent e64faf53
...@@ -7,6 +7,10 @@ ...@@ -7,6 +7,10 @@
When a `DrmSessionManager` is used by an app in a custom `MediaSource`, When a `DrmSessionManager` is used by an app in a custom `MediaSource`,
the `playbackLooper` needs to be passed to `DrmSessionManager.setPlayer` the `playbackLooper` needs to be passed to `DrmSessionManager.setPlayer`
instead. instead.
* IMA:
* Add a method to `AdPlaybackState` to allow resetting an ad group so that
it can be played again
([#9615](https://github.com/google/ExoPlayer/issues/9615)).
### 2.16.1 (2021-11-18) ### 2.16.1 (2021-11-18)
......
...@@ -300,6 +300,28 @@ public final class AdPlaybackState implements Bundleable { ...@@ -300,6 +300,28 @@ public final class AdPlaybackState implements Bundleable {
timeUs, count, states, uris, durationsUs, contentResumeOffsetUs, isServerSideInserted); timeUs, count, states, uris, durationsUs, contentResumeOffsetUs, isServerSideInserted);
} }
/**
* Returns an instance with all ads in final states (played, skipped, error) reset to either
* available or unavailable, which allows to play them again.
*/
@CheckResult
public AdGroup withAllAdsReset() {
if (count == C.LENGTH_UNSET) {
return this;
}
int count = this.states.length;
@AdState int[] states = Arrays.copyOf(this.states, count);
for (int i = 0; i < count; i++) {
if (states[i] == AD_STATE_PLAYED
|| states[i] == AD_STATE_SKIPPED
|| states[i] == AD_STATE_ERROR) {
states[i] = uris[i] == null ? AD_STATE_UNAVAILABLE : AD_STATE_AVAILABLE;
}
}
return new AdGroup(
timeUs, count, states, uris, durationsUs, contentResumeOffsetUs, isServerSideInserted);
}
@CheckResult @CheckResult
private static @AdState int[] copyStatesWithSpaceForAdCount(@AdState int[] states, int count) { private static @AdState int[] copyStatesWithSpaceForAdCount(@AdState int[] states, int count) {
int oldStateCount = states.length; int oldStateCount = states.length;
...@@ -783,6 +805,19 @@ public final class AdPlaybackState implements Bundleable { ...@@ -783,6 +805,19 @@ public final class AdPlaybackState implements Bundleable {
adsId, adGroups, adResumePositionUs, contentDurationUs, removedAdGroupCount); adsId, adGroups, adResumePositionUs, contentDurationUs, removedAdGroupCount);
} }
/**
* Returns an instance with all ads in the specified ad group reset from final states (played,
* skipped, error) to either available or unavailable, which allows to play them again.
*/
@CheckResult
public AdPlaybackState withResetAdGroup(@IntRange(from = 0) int adGroupIndex) {
int adjustedIndex = adGroupIndex - removedAdGroupCount;
AdGroup[] adGroups = Util.nullSafeArrayCopy(this.adGroups, this.adGroups.length);
adGroups[adjustedIndex] = adGroups[adjustedIndex].withAllAdsReset();
return new AdPlaybackState(
adsId, adGroups, adResumePositionUs, contentDurationUs, removedAdGroupCount);
}
@Override @Override
public boolean equals(@Nullable Object o) { public boolean equals(@Nullable Object o) {
if (this == o) { if (this == o) {
......
...@@ -16,7 +16,10 @@ ...@@ -16,7 +16,10 @@
package com.google.android.exoplayer2.source.ads; package com.google.android.exoplayer2.source.ads;
import static com.google.android.exoplayer2.source.ads.AdPlaybackState.AD_STATE_AVAILABLE; import static com.google.android.exoplayer2.source.ads.AdPlaybackState.AD_STATE_AVAILABLE;
import static com.google.android.exoplayer2.source.ads.AdPlaybackState.AD_STATE_ERROR;
import static com.google.android.exoplayer2.source.ads.AdPlaybackState.AD_STATE_PLAYED; import static com.google.android.exoplayer2.source.ads.AdPlaybackState.AD_STATE_PLAYED;
import static com.google.android.exoplayer2.source.ads.AdPlaybackState.AD_STATE_SKIPPED;
import static com.google.android.exoplayer2.source.ads.AdPlaybackState.AD_STATE_UNAVAILABLE;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
...@@ -255,6 +258,60 @@ public class AdPlaybackStateTest { ...@@ -255,6 +258,60 @@ public class AdPlaybackStateTest {
} }
@Test @Test
public void withResetAdGroup_beforeSetAdCount_doesNothing() {
AdPlaybackState state = new AdPlaybackState(TEST_ADS_ID, TEST_AD_GROUP_TIMES_US);
state = state.withResetAdGroup(/* adGroupIndex= */ 1);
assertThat(state.getAdGroup(1).count).isEqualTo(C.LENGTH_UNSET);
}
@Test
public void withResetAdGroup_resetsAdsInFinalStates() {
AdPlaybackState state = new AdPlaybackState(TEST_ADS_ID, TEST_AD_GROUP_TIMES_US);
state = state.withAdCount(/* adGroupIndex= */ 1, /* adCount= */ 5);
state =
state.withAdDurationsUs(
/* adGroupIndex= */ 1, /* adDurationsUs...= */ 1_000L, 2_000L, 3_000L, 4_000L, 5_000L);
state = state.withAdUri(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 1, Uri.EMPTY);
state = state.withAdUri(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 2, Uri.EMPTY);
state = state.withAdUri(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 3, Uri.EMPTY);
state = state.withAdUri(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 4, Uri.EMPTY);
state = state.withPlayedAd(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 2);
state = state.withSkippedAd(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 3);
state = state.withAdLoadError(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 4);
// Verify setup.
assertThat(state.getAdGroup(/* adGroupIndex= */ 1).states)
.asList()
.containsExactly(
AD_STATE_UNAVAILABLE,
AD_STATE_AVAILABLE,
AD_STATE_PLAYED,
AD_STATE_SKIPPED,
AD_STATE_ERROR)
.inOrder();
state = state.withResetAdGroup(/* adGroupIndex= */ 1);
assertThat(state.getAdGroup(/* adGroupIndex= */ 1).states)
.asList()
.containsExactly(
AD_STATE_UNAVAILABLE,
AD_STATE_AVAILABLE,
AD_STATE_AVAILABLE,
AD_STATE_AVAILABLE,
AD_STATE_AVAILABLE)
.inOrder();
assertThat(state.getAdGroup(/* adGroupIndex= */ 1).uris)
.asList()
.containsExactly(null, Uri.EMPTY, Uri.EMPTY, Uri.EMPTY, Uri.EMPTY)
.inOrder();
assertThat(state.getAdGroup(/* adGroupIndex= */ 1).durationsUs)
.asList()
.containsExactly(1_000L, 2_000L, 3_000L, 4_000L, 5_000L);
}
@Test
public void roundTripViaBundle_yieldsEqualFieldsExceptAdsId() { public void roundTripViaBundle_yieldsEqualFieldsExceptAdsId() {
AdPlaybackState originalState = AdPlaybackState originalState =
new AdPlaybackState(TEST_ADS_ID, TEST_AD_GROUP_TIMES_US) new AdPlaybackState(TEST_ADS_ID, TEST_AD_GROUP_TIMES_US)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment