Don't update the ad group count when releasing ImaAdsLoader

`ImaAdsLoader` clears its `AdPlaybackState` when it's released but this could
cause `AdsMediaSource` to look up information in the ad playback state that is
no longer in bounds.

Issue: #8693

#minor-release

PiperOrigin-RevId: 362556286
This commit is contained in:
andrewlewis 2021-03-12 18:53:46 +00:00 committed by Oliver Woodman
parent 7c8ab133e9
commit 93c9c7a072
4 changed files with 18 additions and 8 deletions

View File

@ -81,6 +81,9 @@
`DashMediaSource.Factory`. `DashMediaSource.Factory`.
* We don't currently support using platform extractors with * We don't currently support using platform extractors with
SmoothStreaming. SmoothStreaming.
* IMA extension: fix error caused by `AdPlaybackState` ad group times being
cleared, which can occur if the `ImaAdsLoader` is released while an ad is
pending loading ([#8693](https://github.com/google/ExoPlayer/issues/8693)).
### 2.13.2 (2021-02-25) ### 2.13.2 (2021-02-25)

View File

@ -411,7 +411,10 @@ import java.util.Map;
stopUpdatingAdProgress(); stopUpdatingAdProgress();
imaAdInfo = null; imaAdInfo = null;
pendingAdLoadError = null; pendingAdLoadError = null;
adPlaybackState = new AdPlaybackState(adsId); // No more ads will play once the loader is released, so mark all ad groups as skipped.
for (int i = 0; i < adPlaybackState.adGroupCount; i++) {
adPlaybackState = adPlaybackState.withSkippedAdGroup(i);
}
updateAdPlaybackState(); updateAdPlaybackState();
} }

View File

@ -56,7 +56,8 @@ public interface AdsLoader {
interface EventListener { interface EventListener {
/** /**
* Called when the ad playback state has been updated. * Called when the ad playback state has been updated. The number of {@link
* AdPlaybackState#adGroups ad groups} may not change after the first call.
* *
* @param adPlaybackState The new ad playback state. * @param adPlaybackState The new ad playback state.
*/ */

View File

@ -16,6 +16,7 @@
package com.google.android.exoplayer2.source.ads; package com.google.android.exoplayer2.source.ads;
import static com.google.android.exoplayer2.util.Assertions.checkNotNull; import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
import static com.google.android.exoplayer2.util.Assertions.checkState;
import android.net.Uri; import android.net.Uri;
import android.os.Handler; import android.os.Handler;
@ -290,6 +291,8 @@ public final class AdsMediaSource extends CompositeMediaSource<MediaPeriodId> {
if (this.adPlaybackState == null) { if (this.adPlaybackState == null) {
adMediaSourceHolders = new AdMediaSourceHolder[adPlaybackState.adGroupCount][]; adMediaSourceHolders = new AdMediaSourceHolder[adPlaybackState.adGroupCount][];
Arrays.fill(adMediaSourceHolders, new AdMediaSourceHolder[0]); Arrays.fill(adMediaSourceHolders, new AdMediaSourceHolder[0]);
} else {
checkState(adPlaybackState.adGroupCount == this.adPlaybackState.adGroupCount);
} }
this.adPlaybackState = adPlaybackState; this.adPlaybackState = adPlaybackState;
maybeUpdateAdMediaSources(); maybeUpdateAdMediaSources();
@ -350,12 +353,12 @@ public final class AdsMediaSource extends CompositeMediaSource<MediaPeriodId> {
private void maybeUpdateSourceInfo() { private void maybeUpdateSourceInfo() {
@Nullable Timeline contentTimeline = this.contentTimeline; @Nullable Timeline contentTimeline = this.contentTimeline;
if (adPlaybackState != null && contentTimeline != null) { if (adPlaybackState != null && contentTimeline != null) {
adPlaybackState = adPlaybackState.withAdDurationsUs(getAdDurationsUs()); if (adPlaybackState.adGroupCount == 0) {
Timeline timeline = refreshSourceInfo(contentTimeline);
adPlaybackState.adGroupCount == 0 } else {
? contentTimeline adPlaybackState = adPlaybackState.withAdDurationsUs(getAdDurationsUs());
: new SinglePeriodAdTimeline(contentTimeline, adPlaybackState); refreshSourceInfo(new SinglePeriodAdTimeline(contentTimeline, adPlaybackState));
refreshSourceInfo(timeline); }
} }
} }