Commit 2a06e002 by andrewlewis Committed by Oliver Woodman

Expose ad load errors via MediaSourceEventListener

The old event listener on AdsMediaSource is deprecated, in favor of
reporting in the normal way (via MediaSourceEventListener).

Add AdLoadException with information on what ad/ads failed to load.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=195426144
parent 0edc832d
...@@ -3,6 +3,9 @@ ...@@ -3,6 +3,9 @@
### dev-v2 (not yet released) ### ### dev-v2 (not yet released) ###
* Added dependency on checkerframework annotations for static code analysis. * Added dependency on checkerframework annotations for static code analysis.
* IMA: Expose ad load errors via `MediaSourceEventListener` on `AdsMediaSource`,
and allow setting an ad event listener on `ImaAdsLoader`. Deprecate the
`AdsMediaSource.EventListener`.
### 2.8.0 ### ### 2.8.0 ###
......
...@@ -52,6 +52,8 @@ import com.google.android.exoplayer2.Timeline; ...@@ -52,6 +52,8 @@ import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.source.ads.AdPlaybackState; import com.google.android.exoplayer2.source.ads.AdPlaybackState;
import com.google.android.exoplayer2.source.ads.AdPlaybackState.AdState; import com.google.android.exoplayer2.source.ads.AdPlaybackState.AdState;
import com.google.android.exoplayer2.source.ads.AdsLoader; import com.google.android.exoplayer2.source.ads.AdsLoader;
import com.google.android.exoplayer2.source.ads.AdsMediaSource.AdLoadException;
import com.google.android.exoplayer2.upstream.DataSpec;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
...@@ -80,6 +82,7 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A ...@@ -80,6 +82,7 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A
private final Context context; private final Context context;
private @Nullable ImaSdkSettings imaSdkSettings; private @Nullable ImaSdkSettings imaSdkSettings;
private @Nullable AdEventListener adEventListener;
private int vastLoadTimeoutMs; private int vastLoadTimeoutMs;
private int mediaLoadTimeoutMs; private int mediaLoadTimeoutMs;
...@@ -109,6 +112,18 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A ...@@ -109,6 +112,18 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A
} }
/** /**
* Sets a listener for ad events that will be passed to {@link
* AdsManager#addAdEventListener(AdEventListener)}.
*
* @param adEventListener The ad event listener.
* @return This builder, for convenience.
*/
public Builder setAdEventListener(AdEventListener adEventListener) {
this.adEventListener = Assertions.checkNotNull(adEventListener);
return this;
}
/**
* Sets the VAST load timeout, in milliseconds. * Sets the VAST load timeout, in milliseconds.
* *
* @param vastLoadTimeoutMs The VAST load timeout, in milliseconds. * @param vastLoadTimeoutMs The VAST load timeout, in milliseconds.
...@@ -144,7 +159,13 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A ...@@ -144,7 +159,13 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A
*/ */
public ImaAdsLoader buildForAdTag(Uri adTagUri) { public ImaAdsLoader buildForAdTag(Uri adTagUri) {
return new ImaAdsLoader( return new ImaAdsLoader(
context, adTagUri, imaSdkSettings, null, vastLoadTimeoutMs, mediaLoadTimeoutMs); context,
adTagUri,
imaSdkSettings,
null,
vastLoadTimeoutMs,
mediaLoadTimeoutMs,
adEventListener);
} }
/** /**
...@@ -156,7 +177,13 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A ...@@ -156,7 +177,13 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A
*/ */
public ImaAdsLoader buildForAdsResponse(String adsResponse) { public ImaAdsLoader buildForAdsResponse(String adsResponse) {
return new ImaAdsLoader( return new ImaAdsLoader(
context, null, imaSdkSettings, adsResponse, vastLoadTimeoutMs, mediaLoadTimeoutMs); context,
null,
imaSdkSettings,
adsResponse,
vastLoadTimeoutMs,
mediaLoadTimeoutMs,
adEventListener);
} }
} }
...@@ -214,6 +241,7 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A ...@@ -214,6 +241,7 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A
private final @Nullable String adsResponse; private final @Nullable String adsResponse;
private final int vastLoadTimeoutMs; private final int vastLoadTimeoutMs;
private final int mediaLoadTimeoutMs; private final int mediaLoadTimeoutMs;
private final @Nullable AdEventListener adEventListener;
private final Timeline.Period period; private final Timeline.Period period;
private final List<VideoAdPlayerCallback> adCallbacks; private final List<VideoAdPlayerCallback> adCallbacks;
private final ImaSdkFactory imaSdkFactory; private final ImaSdkFactory imaSdkFactory;
...@@ -229,7 +257,7 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A ...@@ -229,7 +257,7 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A
private VideoProgressUpdate lastAdProgress; private VideoProgressUpdate lastAdProgress;
private AdsManager adsManager; private AdsManager adsManager;
private AdErrorEvent pendingAdErrorEvent; private AdLoadException pendingAdLoadError;
private Timeline timeline; private Timeline timeline;
private long contentDurationMs; private long contentDurationMs;
private int podIndexOffset; private int podIndexOffset;
...@@ -308,7 +336,8 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A ...@@ -308,7 +336,8 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A
/* imaSdkSettings= */ null, /* imaSdkSettings= */ null,
/* adsResponse= */ null, /* adsResponse= */ null,
/* vastLoadTimeoutMs= */ TIMEOUT_UNSET, /* vastLoadTimeoutMs= */ TIMEOUT_UNSET,
/* mediaLoadTimeoutMs= */ TIMEOUT_UNSET); /* mediaLoadTimeoutMs= */ TIMEOUT_UNSET,
/* adEventListener= */ null);
} }
/** /**
...@@ -330,7 +359,8 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A ...@@ -330,7 +359,8 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A
imaSdkSettings, imaSdkSettings,
/* adsResponse= */ null, /* adsResponse= */ null,
/* vastLoadTimeoutMs= */ TIMEOUT_UNSET, /* vastLoadTimeoutMs= */ TIMEOUT_UNSET,
/* mediaLoadTimeoutMs= */ TIMEOUT_UNSET); /* mediaLoadTimeoutMs= */ TIMEOUT_UNSET,
/* adEventListener= */ null);
} }
private ImaAdsLoader( private ImaAdsLoader(
...@@ -339,12 +369,14 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A ...@@ -339,12 +369,14 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A
@Nullable ImaSdkSettings imaSdkSettings, @Nullable ImaSdkSettings imaSdkSettings,
@Nullable String adsResponse, @Nullable String adsResponse,
int vastLoadTimeoutMs, int vastLoadTimeoutMs,
int mediaLoadTimeoutMs) { int mediaLoadTimeoutMs,
@Nullable AdEventListener adEventListener) {
Assertions.checkArgument(adTagUri != null || adsResponse != null); Assertions.checkArgument(adTagUri != null || adsResponse != null);
this.adTagUri = adTagUri; this.adTagUri = adTagUri;
this.adsResponse = adsResponse; this.adsResponse = adsResponse;
this.vastLoadTimeoutMs = vastLoadTimeoutMs; this.vastLoadTimeoutMs = vastLoadTimeoutMs;
this.mediaLoadTimeoutMs = mediaLoadTimeoutMs; this.mediaLoadTimeoutMs = mediaLoadTimeoutMs;
this.adEventListener = adEventListener;
period = new Timeline.Period(); period = new Timeline.Period();
adCallbacks = new ArrayList<>(1); adCallbacks = new ArrayList<>(1);
imaSdkFactory = ImaSdkFactory.getInstance(); imaSdkFactory = ImaSdkFactory.getInstance();
...@@ -500,6 +532,9 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A ...@@ -500,6 +532,9 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A
this.adsManager = adsManager; this.adsManager = adsManager;
adsManager.addAdErrorListener(this); adsManager.addAdErrorListener(this);
adsManager.addAdEventListener(this); adsManager.addAdEventListener(this);
if (adEventListener != null) {
adsManager.addAdEventListener(adEventListener);
}
if (player != null) { if (player != null) {
// If a player is attached already, start playback immediately. // If a player is attached already, start playback immediately.
try { try {
...@@ -544,13 +579,13 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A ...@@ -544,13 +579,13 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A
updateAdPlaybackState(); updateAdPlaybackState();
} else if (isAdGroupLoadError(error)) { } else if (isAdGroupLoadError(error)) {
try { try {
handleAdGroupLoadError(); handleAdGroupLoadError(error);
} catch (Exception e) { } catch (Exception e) {
maybeNotifyInternalError("onAdError", e); maybeNotifyInternalError("onAdError", e);
} }
} }
if (pendingAdErrorEvent == null) { if (pendingAdLoadError == null) {
pendingAdErrorEvent = adErrorEvent; pendingAdLoadError = AdLoadException.createForAllAds(error);
} }
maybeNotifyPendingAdLoadError(); maybeNotifyPendingAdLoadError();
} }
...@@ -937,9 +972,10 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A ...@@ -937,9 +972,10 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A
break; break;
case LOG: case LOG:
Map<String, String> adData = adEvent.getAdData(); Map<String, String> adData = adEvent.getAdData();
Log.i(TAG, "Log AdEvent: " + adData); String message = "AdEvent: " + adData;
Log.i(TAG, message);
if ("adLoadError".equals(adData.get("type"))) { if ("adLoadError".equals(adData.get("type"))) {
handleAdGroupLoadError(); handleAdGroupLoadError(new IOException(message));
} }
break; break;
case ALL_ADS_COMPLETED: case ALL_ADS_COMPLETED:
...@@ -1011,7 +1047,7 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A ...@@ -1011,7 +1047,7 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A
} }
} }
private void handleAdGroupLoadError() { private void handleAdGroupLoadError(Exception error) {
int adGroupIndex = int adGroupIndex =
this.adGroupIndex == C.INDEX_UNSET ? expectedAdGroupIndex : this.adGroupIndex; this.adGroupIndex == C.INDEX_UNSET ? expectedAdGroupIndex : this.adGroupIndex;
if (adGroupIndex == C.INDEX_UNSET) { if (adGroupIndex == C.INDEX_UNSET) {
...@@ -1033,6 +1069,9 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A ...@@ -1033,6 +1069,9 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A
} }
} }
updateAdPlaybackState(); updateAdPlaybackState();
if (pendingAdLoadError == null) {
pendingAdLoadError = AdLoadException.createForAdGroup(error, adGroupIndex);
}
} }
private void handleAdPrepareError(int adGroupIndex, int adIndexInAdGroup, Exception exception) { private void handleAdPrepareError(int adGroupIndex, int adIndexInAdGroup, Exception exception) {
...@@ -1111,21 +1150,15 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A ...@@ -1111,21 +1150,15 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A
} }
private void maybeNotifyPendingAdLoadError() { private void maybeNotifyPendingAdLoadError() {
if (pendingAdErrorEvent != null) { if (pendingAdLoadError != null && eventListener != null) {
if (eventListener != null) { eventListener.onAdLoadError(pendingAdLoadError, new DataSpec(adTagUri));
eventListener.onAdLoadError( pendingAdLoadError = null;
new IOException("Ad error: " + pendingAdErrorEvent, pendingAdErrorEvent.getError()));
}
pendingAdErrorEvent = null;
} }
} }
private void maybeNotifyInternalError(String name, Exception cause) { private void maybeNotifyInternalError(String name, Exception cause) {
String message = "Internal error in " + name; String message = "Internal error in " + name;
Log.e(TAG, message, cause); Log.e(TAG, message, cause);
if (eventListener != null) {
eventListener.onInternalAdLoadError(new RuntimeException(message, cause));
}
// We can't recover from an unexpected error in general, so skip all remaining ads. // We can't recover from an unexpected error in general, so skip all remaining ads.
if (adPlaybackState == null) { if (adPlaybackState == null) {
adPlaybackState = new AdPlaybackState(); adPlaybackState = new AdPlaybackState();
...@@ -1135,6 +1168,11 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A ...@@ -1135,6 +1168,11 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A
} }
} }
updateAdPlaybackState(); updateAdPlaybackState();
if (eventListener != null) {
eventListener.onAdLoadError(
AdLoadException.createForUnexpected(new RuntimeException(message, cause)),
new DataSpec(adTagUri));
}
} }
private static long[] getAdGroupTimesUs(List<Float> cuePoints) { private static long[] getAdGroupTimesUs(List<Float> cuePoints) {
......
...@@ -493,6 +493,8 @@ public final class C { ...@@ -493,6 +493,8 @@ public final class C {
* A data type constant for time synchronization data. * A data type constant for time synchronization data.
*/ */
public static final int DATA_TYPE_TIME_SYNCHRONIZATION = 5; public static final int DATA_TYPE_TIME_SYNCHRONIZATION = 5;
/** A data type constant for ads loader data. */
public static final int DATA_TYPE_AD = 6;
/** /**
* Applications or extensions may define custom {@code DATA_TYPE_*} constants greater than or * Applications or extensions may define custom {@code DATA_TYPE_*} constants greater than or
* equal to this value. * equal to this value.
......
...@@ -37,7 +37,7 @@ public final class DeferredMediaPeriod implements MediaPeriod, MediaPeriod.Callb ...@@ -37,7 +37,7 @@ public final class DeferredMediaPeriod implements MediaPeriod, MediaPeriod.Callb
/** /**
* Called the first time an error occurs while refreshing source info or preparing the period. * Called the first time an error occurs while refreshing source info or preparing the period.
*/ */
void onPrepareError(IOException exception); void onPrepareError(MediaPeriodId mediaPeriodId, IOException exception);
} }
public final MediaSource mediaSource; public final MediaSource mediaSource;
...@@ -140,7 +140,7 @@ public final class DeferredMediaPeriod implements MediaPeriod, MediaPeriod.Callb ...@@ -140,7 +140,7 @@ public final class DeferredMediaPeriod implements MediaPeriod, MediaPeriod.Callb
} }
if (!notifiedPrepareError) { if (!notifiedPrepareError) {
notifiedPrepareError = true; notifiedPrepareError = true;
listener.onPrepareError(e); listener.onPrepareError(id, e);
} }
} }
} }
......
...@@ -18,6 +18,8 @@ package com.google.android.exoplayer2.source.ads; ...@@ -18,6 +18,8 @@ package com.google.android.exoplayer2.source.ads;
import android.view.ViewGroup; import android.view.ViewGroup;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.source.ads.AdsMediaSource.AdLoadException;
import com.google.android.exoplayer2.upstream.DataSpec;
import java.io.IOException; import java.io.IOException;
/** /**
...@@ -54,19 +56,12 @@ public interface AdsLoader { ...@@ -54,19 +56,12 @@ public interface AdsLoader {
void onAdPlaybackState(AdPlaybackState adPlaybackState); void onAdPlaybackState(AdPlaybackState adPlaybackState);
/** /**
* Called when there was an error loading ads. The loader will skip the problematic ad(s). * Called when there was an error loading ads.
* *
* @param error The error. * @param error The error.
* @param dataSpec The data spec associated with the load error.
*/ */
void onAdLoadError(IOException error); void onAdLoadError(AdLoadException error, DataSpec dataSpec);
/**
* Called when an unexpected internal error is encountered while loading ads. The loader will
* skip all remaining ads, as the error is not recoverable.
*
* @param error The error.
*/
void onInternalAdLoadError(RuntimeException error);
/** /**
* Called when the user clicks through an ad (for example, following a 'learn more' link). * Called when the user clicks through an ad (for example, following a 'learn more' link).
......
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