Commit 6041b0fe by andrewlewis Committed by Oliver Woodman

Handle VAST loading errors by playing content

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=162204327
parent 706d4e51
...@@ -249,8 +249,8 @@ public final class ImaAdsLoader implements ExoPlayer.EventListener, VideoAdPlaye ...@@ -249,8 +249,8 @@ public final class ImaAdsLoader implements ExoPlayer.EventListener, VideoAdPlaye
lastContentProgress = getContentProgress(); lastContentProgress = getContentProgress();
player.removeListener(this); player.removeListener(this);
player = null; player = null;
eventListener = null;
} }
eventListener = null;
} }
/** /**
...@@ -345,11 +345,14 @@ public final class ImaAdsLoader implements ExoPlayer.EventListener, VideoAdPlaye ...@@ -345,11 +345,14 @@ public final class ImaAdsLoader implements ExoPlayer.EventListener, VideoAdPlaye
if (DEBUG) { if (DEBUG) {
Log.d(TAG, "onAdError " + adErrorEvent); Log.d(TAG, "onAdError " + adErrorEvent);
} }
if (adsManager == null) {
adPlaybackState = new AdPlaybackState(new long[0]);
updateAdPlaybackState();
}
if (eventListener != null) { if (eventListener != null) {
IOException exception = new IOException("Ad error: " + adErrorEvent, adErrorEvent.getError()); IOException exception = new IOException("Ad error: " + adErrorEvent, adErrorEvent.getError());
eventListener.onLoadError(exception); eventListener.onLoadError(exception);
} }
// TODO: Provide a timeline to the player if it doesn't have one yet, so the content can play.
} }
// ContentProgressProvider implementation. // ContentProgressProvider implementation.
...@@ -498,6 +501,10 @@ public final class ImaAdsLoader implements ExoPlayer.EventListener, VideoAdPlaye ...@@ -498,6 +501,10 @@ public final class ImaAdsLoader implements ExoPlayer.EventListener, VideoAdPlaye
@Override @Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) { public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
if (adsManager == null) {
return;
}
if (!imaPlayingAd && playbackState == ExoPlayer.STATE_BUFFERING && playWhenReady) { if (!imaPlayingAd && playbackState == ExoPlayer.STATE_BUFFERING && playWhenReady) {
checkForContentComplete(); checkForContentComplete();
} else if (imaPlayingAd && playbackState == ExoPlayer.STATE_ENDED) { } else if (imaPlayingAd && playbackState == ExoPlayer.STATE_ENDED) {
...@@ -525,6 +532,10 @@ public final class ImaAdsLoader implements ExoPlayer.EventListener, VideoAdPlaye ...@@ -525,6 +532,10 @@ public final class ImaAdsLoader implements ExoPlayer.EventListener, VideoAdPlaye
@Override @Override
public void onPositionDiscontinuity() { public void onPositionDiscontinuity() {
if (adsManager == null) {
return;
}
boolean wasPlayingAd = playingAd; boolean wasPlayingAd = playingAd;
playingAd = player.isPlayingAd(); playingAd = player.isPlayingAd();
if (!playingAd && !wasPlayingAd) { if (!playingAd && !wasPlayingAd) {
......
...@@ -17,6 +17,8 @@ package com.google.android.exoplayer2.ext.ima; ...@@ -17,6 +17,8 @@ package com.google.android.exoplayer2.ext.ima;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.support.annotation.Nullable;
import android.util.Log;
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;
...@@ -39,6 +41,22 @@ import java.util.Map; ...@@ -39,6 +41,22 @@ import java.util.Map;
*/ */
public final class ImaAdsMediaSource implements MediaSource { public final class ImaAdsMediaSource implements MediaSource {
/**
* Listener for events relating to ad loading.
*/
public interface AdsListener {
/**
* Called if there was an error loading ads. The media source will load the content without ads
* if ads can't be loaded, so listen for this event if you need to implement additional handling
* (for example, stopping the player).
*/
void onAdLoadError(IOException error);
}
private static final String TAG = "ImaAdsMediaSource";
private final MediaSource contentMediaSource; private final MediaSource contentMediaSource;
private final DataSource.Factory dataSourceFactory; private final DataSource.Factory dataSourceFactory;
private final ImaAdsLoader imaAdsLoader; private final ImaAdsLoader imaAdsLoader;
...@@ -47,6 +65,10 @@ public final class ImaAdsMediaSource implements MediaSource { ...@@ -47,6 +65,10 @@ public final class ImaAdsMediaSource implements MediaSource {
private final AdsLoaderListener adsLoaderListener; private final AdsLoaderListener adsLoaderListener;
private final Map<MediaPeriod, MediaSource> adMediaSourceByMediaPeriod; private final Map<MediaPeriod, MediaSource> adMediaSourceByMediaPeriod;
private final Timeline.Period period; private final Timeline.Period period;
@Nullable
private final Handler eventHandler;
@Nullable
private final AdsListener eventListener;
private Handler playerHandler; private Handler playerHandler;
private ExoPlayer player; private ExoPlayer player;
...@@ -59,7 +81,6 @@ public final class ImaAdsMediaSource implements MediaSource { ...@@ -59,7 +81,6 @@ public final class ImaAdsMediaSource implements MediaSource {
private MediaSource[][] adGroupMediaSources; private MediaSource[][] adGroupMediaSources;
private long[][] adDurationsUs; private long[][] adDurationsUs;
private MediaSource.Listener listener; private MediaSource.Listener listener;
private IOException adLoadError;
/** /**
* Constructs a new source that inserts ads linearly with the content specified by * Constructs a new source that inserts ads linearly with the content specified by
...@@ -68,13 +89,33 @@ public final class ImaAdsMediaSource implements MediaSource { ...@@ -68,13 +89,33 @@ public final class ImaAdsMediaSource implements MediaSource {
* @param contentMediaSource The {@link MediaSource} providing the content to play. * @param contentMediaSource The {@link MediaSource} providing the content to play.
* @param dataSourceFactory Factory for data sources used to load ad media. * @param dataSourceFactory Factory for data sources used to load ad media.
* @param imaAdsLoader The loader for ads. * @param imaAdsLoader The loader for ads.
* @param adUiViewGroup A {@link ViewGroup} on top of the player that will show any ad UI.
*/ */
public ImaAdsMediaSource(MediaSource contentMediaSource, DataSource.Factory dataSourceFactory, public ImaAdsMediaSource(MediaSource contentMediaSource, DataSource.Factory dataSourceFactory,
ImaAdsLoader imaAdsLoader, ViewGroup adUiViewGroup) { ImaAdsLoader imaAdsLoader, ViewGroup adUiViewGroup) {
this(contentMediaSource, dataSourceFactory, imaAdsLoader, adUiViewGroup, null, null);
}
/**
* Constructs a new source that inserts ads linearly with the content specified by
* {@code contentMediaSource}.
*
* @param contentMediaSource The {@link MediaSource} providing the content to play.
* @param dataSourceFactory Factory for data sources used to load ad media.
* @param imaAdsLoader The loader for ads.
* @param adUiViewGroup A {@link ViewGroup} on top of the player that will show any ad UI.
* @param eventHandler A handler for events. May be null if delivery of events is not required.
* @param eventListener A listener of events. May be null if delivery of events is not required.
*/
public ImaAdsMediaSource(MediaSource contentMediaSource, DataSource.Factory dataSourceFactory,
ImaAdsLoader imaAdsLoader, ViewGroup adUiViewGroup, @Nullable Handler eventHandler,
@Nullable AdsListener eventListener) {
this.contentMediaSource = contentMediaSource; this.contentMediaSource = contentMediaSource;
this.dataSourceFactory = dataSourceFactory; this.dataSourceFactory = dataSourceFactory;
this.imaAdsLoader = imaAdsLoader; this.imaAdsLoader = imaAdsLoader;
this.adUiViewGroup = adUiViewGroup; this.adUiViewGroup = adUiViewGroup;
this.eventHandler = eventHandler;
this.eventListener = eventListener;
mainHandler = new Handler(Looper.getMainLooper()); mainHandler = new Handler(Looper.getMainLooper());
adsLoaderListener = new AdsLoaderListener(); adsLoaderListener = new AdsLoaderListener();
adMediaSourceByMediaPeriod = new HashMap<>(); adMediaSourceByMediaPeriod = new HashMap<>();
...@@ -105,9 +146,6 @@ public final class ImaAdsMediaSource implements MediaSource { ...@@ -105,9 +146,6 @@ public final class ImaAdsMediaSource implements MediaSource {
@Override @Override
public void maybeThrowSourceInfoRefreshError() throws IOException { public void maybeThrowSourceInfoRefreshError() throws IOException {
if (adLoadError != null) {
throw adLoadError;
}
contentMediaSource.maybeThrowSourceInfoRefreshError(); contentMediaSource.maybeThrowSourceInfoRefreshError();
for (MediaSource[] mediaSources : adGroupMediaSources) { for (MediaSource[] mediaSources : adGroupMediaSources) {
for (MediaSource mediaSource : mediaSources) { for (MediaSource mediaSource : mediaSources) {
...@@ -120,7 +158,7 @@ public final class ImaAdsMediaSource implements MediaSource { ...@@ -120,7 +158,7 @@ public final class ImaAdsMediaSource implements MediaSource {
@Override @Override
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) { public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) {
if (id.isAd()) { if (adPlaybackState.adGroupCount > 0 && id.isAd()) {
final int adGroupIndex = id.adGroupIndex; final int adGroupIndex = id.adGroupIndex;
final int adIndexInAdGroup = id.adIndexInAdGroup; final int adIndexInAdGroup = id.adIndexInAdGroup;
if (adGroupMediaSources[adGroupIndex].length <= adIndexInAdGroup) { if (adGroupMediaSources[adGroupIndex].length <= adIndexInAdGroup) {
...@@ -164,7 +202,6 @@ public final class ImaAdsMediaSource implements MediaSource { ...@@ -164,7 +202,6 @@ public final class ImaAdsMediaSource implements MediaSource {
@Override @Override
public void releaseSource() { public void releaseSource() {
released = true; released = true;
adLoadError = null;
contentMediaSource.releaseSource(); contentMediaSource.releaseSource();
for (MediaSource[] mediaSources : adGroupMediaSources) { for (MediaSource[] mediaSources : adGroupMediaSources) {
for (MediaSource mediaSource : mediaSources) { for (MediaSource mediaSource : mediaSources) {
...@@ -194,6 +231,20 @@ public final class ImaAdsMediaSource implements MediaSource { ...@@ -194,6 +231,20 @@ public final class ImaAdsMediaSource implements MediaSource {
maybeUpdateSourceInfo(); maybeUpdateSourceInfo();
} }
private void onLoadError(final IOException error) {
Log.w(TAG, "Ad load error", error);
if (eventHandler != null && eventListener != null) {
eventHandler.post(new Runnable() {
@Override
public void run() {
if (!released) {
eventListener.onAdLoadError(error);
}
}
});
}
}
private void onContentSourceInfoRefreshed(Timeline timeline, Object manifest) { private void onContentSourceInfoRefreshed(Timeline timeline, Object manifest) {
contentTimeline = timeline; contentTimeline = timeline;
contentManifest = manifest; contentManifest = manifest;
...@@ -208,9 +259,10 @@ public final class ImaAdsMediaSource implements MediaSource { ...@@ -208,9 +259,10 @@ public final class ImaAdsMediaSource implements MediaSource {
private void maybeUpdateSourceInfo() { private void maybeUpdateSourceInfo() {
if (adPlaybackState != null && contentTimeline != null) { if (adPlaybackState != null && contentTimeline != null) {
SinglePeriodAdTimeline timeline = new SinglePeriodAdTimeline(contentTimeline, Timeline timeline = adPlaybackState.adGroupCount == 0 ? contentTimeline
adPlaybackState.adGroupTimesUs, adPlaybackState.adCounts, adPlaybackState.adsLoadedCounts, : new SinglePeriodAdTimeline(contentTimeline, adPlaybackState.adGroupTimesUs,
adPlaybackState.adsPlayedCounts, adDurationsUs, adPlaybackState.adResumePositionUs); adPlaybackState.adCounts, adPlaybackState.adsLoadedCounts,
adPlaybackState.adsPlayedCounts, adDurationsUs, adPlaybackState.adResumePositionUs);
listener.onSourceInfoRefreshed(timeline, contentManifest); listener.onSourceInfoRefreshed(timeline, contentManifest);
} }
} }
...@@ -248,7 +300,7 @@ public final class ImaAdsMediaSource implements MediaSource { ...@@ -248,7 +300,7 @@ public final class ImaAdsMediaSource implements MediaSource {
if (released) { if (released) {
return; return;
} }
adLoadError = error; ImaAdsMediaSource.this.onLoadError(error);
} }
}); });
} }
......
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