mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Migrate to new IMA preloading APIs
issue:#6429 PiperOrigin-RevId: 309906760
This commit is contained in:
parent
a8d1de5198
commit
fa7d26dd9f
@ -172,6 +172,8 @@
|
|||||||
([#7234](https://github.com/google/ExoPlayer/issues/7234)).
|
([#7234](https://github.com/google/ExoPlayer/issues/7234)).
|
||||||
* AV1 extension: Add a heuristic to determine the default number of threads
|
* AV1 extension: Add a heuristic to determine the default number of threads
|
||||||
used for AV1 playback using the extension.
|
used for AV1 playback using the extension.
|
||||||
|
* IMA extension: Upgrade to IMA SDK version 3.18.1, and migrate to new
|
||||||
|
preloading APIs ([#6429](https://github.com/google/ExoPlayer/issues/6429)).
|
||||||
|
|
||||||
### 2.11.4 (2020-04-08)
|
### 2.11.4 (2020-04-08)
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
api 'com.google.ads.interactivemedia.v3:interactivemedia:3.11.3'
|
api 'com.google.ads.interactivemedia.v3:interactivemedia:3.18.1'
|
||||||
implementation project(modulePrefix + 'library-core')
|
implementation project(modulePrefix + 'library-core')
|
||||||
implementation 'androidx.annotation:annotation:' + androidxAnnotationVersion
|
implementation 'androidx.annotation:annotation:' + androidxAnnotationVersion
|
||||||
implementation 'com.google.android.gms:play-services-ads-identifier:17.0.0'
|
implementation 'com.google.android.gms:play-services-ads-identifier:17.0.0'
|
||||||
|
@ -19,6 +19,7 @@ import static com.google.android.exoplayer2.util.Util.castNonNull;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@ -26,7 +27,6 @@ import android.view.ViewGroup;
|
|||||||
import androidx.annotation.IntDef;
|
import androidx.annotation.IntDef;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
import com.google.ads.interactivemedia.v3.api.Ad;
|
|
||||||
import com.google.ads.interactivemedia.v3.api.AdDisplayContainer;
|
import com.google.ads.interactivemedia.v3.api.AdDisplayContainer;
|
||||||
import com.google.ads.interactivemedia.v3.api.AdError;
|
import com.google.ads.interactivemedia.v3.api.AdError;
|
||||||
import com.google.ads.interactivemedia.v3.api.AdError.AdErrorCode;
|
import com.google.ads.interactivemedia.v3.api.AdError.AdErrorCode;
|
||||||
@ -45,6 +45,7 @@ import com.google.ads.interactivemedia.v3.api.CompanionAdSlot;
|
|||||||
import com.google.ads.interactivemedia.v3.api.ImaSdkFactory;
|
import com.google.ads.interactivemedia.v3.api.ImaSdkFactory;
|
||||||
import com.google.ads.interactivemedia.v3.api.ImaSdkSettings;
|
import com.google.ads.interactivemedia.v3.api.ImaSdkSettings;
|
||||||
import com.google.ads.interactivemedia.v3.api.UiElement;
|
import com.google.ads.interactivemedia.v3.api.UiElement;
|
||||||
|
import com.google.ads.interactivemedia.v3.api.player.AdMediaInfo;
|
||||||
import com.google.ads.interactivemedia.v3.api.player.ContentProgressProvider;
|
import com.google.ads.interactivemedia.v3.api.player.ContentProgressProvider;
|
||||||
import com.google.ads.interactivemedia.v3.api.player.VideoAdPlayer;
|
import com.google.ads.interactivemedia.v3.api.player.VideoAdPlayer;
|
||||||
import com.google.ads.interactivemedia.v3.api.player.VideoProgressUpdate;
|
import com.google.ads.interactivemedia.v3.api.player.VideoProgressUpdate;
|
||||||
@ -54,7 +55,6 @@ import com.google.android.exoplayer2.ExoPlayerLibraryInfo;
|
|||||||
import com.google.android.exoplayer2.Player;
|
import com.google.android.exoplayer2.Player;
|
||||||
import com.google.android.exoplayer2.Timeline;
|
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.AdsLoader;
|
import com.google.android.exoplayer2.source.ads.AdsLoader;
|
||||||
import com.google.android.exoplayer2.source.ads.AdsMediaSource.AdLoadException;
|
import com.google.android.exoplayer2.source.ads.AdsMediaSource.AdLoadException;
|
||||||
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
|
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
|
||||||
@ -71,6 +71,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -277,6 +278,14 @@ public final class ImaAdsLoader
|
|||||||
private static final String IMA_SDK_SETTINGS_PLAYER_TYPE = "google/exo.ext.ima";
|
private static final String IMA_SDK_SETTINGS_PLAYER_TYPE = "google/exo.ext.ima";
|
||||||
private static final String IMA_SDK_SETTINGS_PLAYER_VERSION = ExoPlayerLibraryInfo.VERSION;
|
private static final String IMA_SDK_SETTINGS_PLAYER_VERSION = ExoPlayerLibraryInfo.VERSION;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interval at which ad progress updates are provided to the IMA SDK, in milliseconds. 100 ms is
|
||||||
|
* the interval recommended by the IMA documentation.
|
||||||
|
*
|
||||||
|
* @see com.google.ads.interactivemedia.v3.api.player.VideoAdPlayer.VideoAdPlayerCallback
|
||||||
|
*/
|
||||||
|
private static final int AD_PROGRESS_UPDATE_INTERVAL_MS = 100;
|
||||||
|
|
||||||
/** The value used in {@link VideoProgressUpdate}s to indicate an unset duration. */
|
/** The value used in {@link VideoProgressUpdate}s to indicate an unset duration. */
|
||||||
private static final long IMA_DURATION_UNSET = -1L;
|
private static final long IMA_DURATION_UNSET = -1L;
|
||||||
|
|
||||||
@ -286,9 +295,6 @@ public final class ImaAdsLoader
|
|||||||
*/
|
*/
|
||||||
private static final long END_OF_CONTENT_THRESHOLD_MS = 5000;
|
private static final long END_OF_CONTENT_THRESHOLD_MS = 5000;
|
||||||
|
|
||||||
/** The maximum duration before an ad break that IMA may start preloading the next ad. */
|
|
||||||
private static final long MAXIMUM_PRELOAD_DURATION_MS = 8000;
|
|
||||||
|
|
||||||
private static final int TIMEOUT_UNSET = -1;
|
private static final int TIMEOUT_UNSET = -1;
|
||||||
private static final int BITRATE_UNSET = -1;
|
private static final int BITRATE_UNSET = -1;
|
||||||
|
|
||||||
@ -302,11 +308,12 @@ public final class ImaAdsLoader
|
|||||||
*/
|
*/
|
||||||
private static final int IMA_AD_STATE_NONE = 0;
|
private static final int IMA_AD_STATE_NONE = 0;
|
||||||
/**
|
/**
|
||||||
* The ad playback state when IMA has called {@link #playAd()} and not {@link #pauseAd()}.
|
* The ad playback state when IMA has called {@link #playAd(AdMediaInfo)} and not {@link
|
||||||
|
* #pauseAd(AdMediaInfo)}.
|
||||||
*/
|
*/
|
||||||
private static final int IMA_AD_STATE_PLAYING = 1;
|
private static final int IMA_AD_STATE_PLAYING = 1;
|
||||||
/**
|
/**
|
||||||
* The ad playback state when IMA has called {@link #pauseAd()} while playing an ad.
|
* The ad playback state when IMA has called {@link #pauseAd(AdMediaInfo)} while playing an ad.
|
||||||
*/
|
*/
|
||||||
private static final int IMA_AD_STATE_PAUSED = 2;
|
private static final int IMA_AD_STATE_PAUSED = 2;
|
||||||
|
|
||||||
@ -320,9 +327,12 @@ public final class ImaAdsLoader
|
|||||||
@Nullable private final AdEventListener adEventListener;
|
@Nullable private final AdEventListener adEventListener;
|
||||||
private final ImaFactory imaFactory;
|
private final ImaFactory imaFactory;
|
||||||
private final Timeline.Period period;
|
private final Timeline.Period period;
|
||||||
|
private final Handler handler;
|
||||||
private final List<VideoAdPlayerCallback> adCallbacks;
|
private final List<VideoAdPlayerCallback> adCallbacks;
|
||||||
private final AdDisplayContainer adDisplayContainer;
|
private final AdDisplayContainer adDisplayContainer;
|
||||||
private final com.google.ads.interactivemedia.v3.api.AdsLoader adsLoader;
|
private final com.google.ads.interactivemedia.v3.api.AdsLoader adsLoader;
|
||||||
|
private final Runnable updateAdProgressRunnable;
|
||||||
|
private final Map<AdMediaInfo, AdInfo> adInfoByAdMediaInfo;
|
||||||
|
|
||||||
private boolean wasSetPlayerCalled;
|
private boolean wasSetPlayerCalled;
|
||||||
@Nullable private Player nextPlayer;
|
@Nullable private Player nextPlayer;
|
||||||
@ -340,19 +350,18 @@ public final class ImaAdsLoader
|
|||||||
@Nullable private AdLoadException pendingAdLoadError;
|
@Nullable private AdLoadException pendingAdLoadError;
|
||||||
private Timeline timeline;
|
private Timeline timeline;
|
||||||
private long contentDurationMs;
|
private long contentDurationMs;
|
||||||
private int podIndexOffset;
|
|
||||||
private AdPlaybackState adPlaybackState;
|
private AdPlaybackState adPlaybackState;
|
||||||
|
|
||||||
// Fields tracking IMA's state.
|
// Fields tracking IMA's state.
|
||||||
|
|
||||||
/** The expected ad group index that IMA should load next. */
|
|
||||||
private int expectedAdGroupIndex;
|
|
||||||
/** The index of the current ad group that IMA is loading. */
|
|
||||||
private int adGroupIndex;
|
|
||||||
/** Whether IMA has sent an ad event to pause content since the last resume content event. */
|
/** Whether IMA has sent an ad event to pause content since the last resume content event. */
|
||||||
private boolean imaPausedContent;
|
private boolean imaPausedContent;
|
||||||
/** The current ad playback state. */
|
/** The current ad playback state. */
|
||||||
private @ImaAdState int imaAdState;
|
private @ImaAdState int imaAdState;
|
||||||
|
/** The current ad media info, or {@code null} if in state {@link #IMA_AD_STATE_NONE}. */
|
||||||
|
@Nullable private AdMediaInfo imaAdMediaInfo;
|
||||||
|
/** The current ad info, or {@code null} if in state {@link #IMA_AD_STATE_NONE}. */
|
||||||
|
@Nullable private AdInfo imaAdInfo;
|
||||||
/**
|
/**
|
||||||
* Whether {@link com.google.ads.interactivemedia.v3.api.AdsLoader#contentComplete()} has been
|
* Whether {@link com.google.ads.interactivemedia.v3.api.AdsLoader#contentComplete()} has been
|
||||||
* called since starting ad playback.
|
* called since starting ad playback.
|
||||||
@ -363,20 +372,23 @@ public final class ImaAdsLoader
|
|||||||
|
|
||||||
/** Whether the player is playing an ad. */
|
/** Whether the player is playing an ad. */
|
||||||
private boolean playingAd;
|
private boolean playingAd;
|
||||||
|
/** Whether the player is buffering an ad. */
|
||||||
|
private boolean bufferingAd;
|
||||||
/**
|
/**
|
||||||
* If the player is playing an ad, stores the ad index in its ad group. {@link C#INDEX_UNSET}
|
* If the player is playing an ad, stores the ad index in its ad group. {@link C#INDEX_UNSET}
|
||||||
* otherwise.
|
* otherwise.
|
||||||
*/
|
*/
|
||||||
private int playingAdIndexInAdGroup;
|
private int playingAdIndexInAdGroup;
|
||||||
/**
|
/**
|
||||||
* Whether there's a pending ad preparation error which IMA needs to be notified of when it
|
* The ad info for a pending ad for which the media failed preparation, or {@code null} if no
|
||||||
* transitions from playing content to playing the ad.
|
* pending ads have failed to prepare.
|
||||||
*/
|
*/
|
||||||
private boolean shouldNotifyAdPrepareError;
|
@Nullable private AdInfo pendingAdPrepareErrorAdInfo;
|
||||||
/**
|
/**
|
||||||
* If a content period has finished but IMA has not yet called {@link #playAd()}, stores the value
|
* If a content period has finished but IMA has not yet called {@link #playAd(AdMediaInfo)},
|
||||||
* of {@link SystemClock#elapsedRealtime()} when the content stopped playing. This can be used to
|
* stores the value of {@link SystemClock#elapsedRealtime()} when the content stopped playing.
|
||||||
* determine a fake, increasing content position. {@link C#TIME_UNSET} otherwise.
|
* This can be used to determine a fake, increasing content position. {@link C#TIME_UNSET}
|
||||||
|
* otherwise.
|
||||||
*/
|
*/
|
||||||
private long fakeContentProgressElapsedRealtimeMs;
|
private long fakeContentProgressElapsedRealtimeMs;
|
||||||
/**
|
/**
|
||||||
@ -441,7 +453,7 @@ public final class ImaAdsLoader
|
|||||||
/* imaFactory= */ new DefaultImaFactory());
|
/* imaFactory= */ new DefaultImaFactory());
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("nullness:argument.type.incompatible")
|
@SuppressWarnings({"nullness:argument.type.incompatible", "methodref.receiver.bound.invalid"})
|
||||||
private ImaAdsLoader(
|
private ImaAdsLoader(
|
||||||
Context context,
|
Context context,
|
||||||
@Nullable Uri adTagUri,
|
@Nullable Uri adTagUri,
|
||||||
@ -473,6 +485,7 @@ public final class ImaAdsLoader
|
|||||||
imaSdkSettings.setPlayerType(IMA_SDK_SETTINGS_PLAYER_TYPE);
|
imaSdkSettings.setPlayerType(IMA_SDK_SETTINGS_PLAYER_TYPE);
|
||||||
imaSdkSettings.setPlayerVersion(IMA_SDK_SETTINGS_PLAYER_VERSION);
|
imaSdkSettings.setPlayerVersion(IMA_SDK_SETTINGS_PLAYER_VERSION);
|
||||||
period = new Timeline.Period();
|
period = new Timeline.Period();
|
||||||
|
handler = Util.createHandler(getImaLooper(), /* callback= */ null);
|
||||||
adCallbacks = new ArrayList<>(/* initialCapacity= */ 1);
|
adCallbacks = new ArrayList<>(/* initialCapacity= */ 1);
|
||||||
adDisplayContainer = imaFactory.createAdDisplayContainer();
|
adDisplayContainer = imaFactory.createAdDisplayContainer();
|
||||||
adDisplayContainer.setPlayer(/* videoAdPlayer= */ this);
|
adDisplayContainer.setPlayer(/* videoAdPlayer= */ this);
|
||||||
@ -481,13 +494,14 @@ public final class ImaAdsLoader
|
|||||||
context.getApplicationContext(), imaSdkSettings, adDisplayContainer);
|
context.getApplicationContext(), imaSdkSettings, adDisplayContainer);
|
||||||
adsLoader.addAdErrorListener(/* adErrorListener= */ this);
|
adsLoader.addAdErrorListener(/* adErrorListener= */ this);
|
||||||
adsLoader.addAdsLoadedListener(/* adsLoadedListener= */ this);
|
adsLoader.addAdsLoadedListener(/* adsLoadedListener= */ this);
|
||||||
|
updateAdProgressRunnable = this::updateAdProgress;
|
||||||
|
adInfoByAdMediaInfo = new HashMap<>();
|
||||||
supportedMimeTypes = Collections.emptyList();
|
supportedMimeTypes = Collections.emptyList();
|
||||||
lastContentProgress = VideoProgressUpdate.VIDEO_TIME_NOT_READY;
|
lastContentProgress = VideoProgressUpdate.VIDEO_TIME_NOT_READY;
|
||||||
lastAdProgress = VideoProgressUpdate.VIDEO_TIME_NOT_READY;
|
lastAdProgress = VideoProgressUpdate.VIDEO_TIME_NOT_READY;
|
||||||
fakeContentProgressElapsedRealtimeMs = C.TIME_UNSET;
|
fakeContentProgressElapsedRealtimeMs = C.TIME_UNSET;
|
||||||
fakeContentProgressOffsetMs = C.TIME_UNSET;
|
fakeContentProgressOffsetMs = C.TIME_UNSET;
|
||||||
pendingContentPositionMs = C.TIME_UNSET;
|
pendingContentPositionMs = C.TIME_UNSET;
|
||||||
adGroupIndex = C.INDEX_UNSET;
|
|
||||||
contentDurationMs = C.TIME_UNSET;
|
contentDurationMs = C.TIME_UNSET;
|
||||||
timeline = Timeline.EMPTY;
|
timeline = Timeline.EMPTY;
|
||||||
adPlaybackState = AdPlaybackState.NONE;
|
adPlaybackState = AdPlaybackState.NONE;
|
||||||
@ -562,9 +576,8 @@ public final class ImaAdsLoader
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setPlayer(@Nullable Player player) {
|
public void setPlayer(@Nullable Player player) {
|
||||||
Assertions.checkState(Looper.getMainLooper() == Looper.myLooper());
|
Assertions.checkState(Looper.myLooper() == getImaLooper());
|
||||||
Assertions.checkState(
|
Assertions.checkState(player == null || player.getApplicationLooper() == getImaLooper());
|
||||||
player == null || player.getApplicationLooper() == Looper.getMainLooper());
|
|
||||||
nextPlayer = player;
|
nextPlayer = player;
|
||||||
wasSetPlayerCalled = true;
|
wasSetPlayerCalled = true;
|
||||||
}
|
}
|
||||||
@ -640,7 +653,7 @@ public final class ImaAdsLoader
|
|||||||
playingAd ? C.msToUs(player.getCurrentPosition()) : 0);
|
playingAd ? C.msToUs(player.getCurrentPosition()) : 0);
|
||||||
}
|
}
|
||||||
lastVolumePercentage = getVolume();
|
lastVolumePercentage = getVolume();
|
||||||
lastAdProgress = getAdProgress();
|
lastAdProgress = getAdVideoProgressUpdate();
|
||||||
lastContentProgress = getContentProgress();
|
lastContentProgress = getContentProgress();
|
||||||
adDisplayContainer.unregisterAllVideoControlsOverlays();
|
adDisplayContainer.unregisterAllVideoControlsOverlays();
|
||||||
player.removeListener(this);
|
player.removeListener(this);
|
||||||
@ -664,6 +677,8 @@ public final class ImaAdsLoader
|
|||||||
adsLoader.removeAdErrorListener(/* adErrorListener= */ this);
|
adsLoader.removeAdErrorListener(/* adErrorListener= */ this);
|
||||||
imaPausedContent = false;
|
imaPausedContent = false;
|
||||||
imaAdState = IMA_AD_STATE_NONE;
|
imaAdState = IMA_AD_STATE_NONE;
|
||||||
|
imaAdMediaInfo = null;
|
||||||
|
imaAdInfo = null;
|
||||||
pendingAdLoadError = null;
|
pendingAdLoadError = null;
|
||||||
adPlaybackState = AdPlaybackState.NONE;
|
adPlaybackState = AdPlaybackState.NONE;
|
||||||
hasAdPlaybackState = false;
|
hasAdPlaybackState = false;
|
||||||
@ -768,32 +783,11 @@ public final class ImaAdsLoader
|
|||||||
if (pendingContentPositionMs != C.TIME_UNSET) {
|
if (pendingContentPositionMs != C.TIME_UNSET) {
|
||||||
sentPendingContentPositionMs = true;
|
sentPendingContentPositionMs = true;
|
||||||
contentPositionMs = pendingContentPositionMs;
|
contentPositionMs = pendingContentPositionMs;
|
||||||
expectedAdGroupIndex =
|
|
||||||
adPlaybackState.getAdGroupIndexForPositionUs(
|
|
||||||
C.msToUs(contentPositionMs), C.msToUs(contentDurationMs));
|
|
||||||
} else if (fakeContentProgressElapsedRealtimeMs != C.TIME_UNSET) {
|
} else if (fakeContentProgressElapsedRealtimeMs != C.TIME_UNSET) {
|
||||||
long elapsedSinceEndMs = SystemClock.elapsedRealtime() - fakeContentProgressElapsedRealtimeMs;
|
long elapsedSinceEndMs = SystemClock.elapsedRealtime() - fakeContentProgressElapsedRealtimeMs;
|
||||||
contentPositionMs = fakeContentProgressOffsetMs + elapsedSinceEndMs;
|
contentPositionMs = fakeContentProgressOffsetMs + elapsedSinceEndMs;
|
||||||
expectedAdGroupIndex =
|
|
||||||
adPlaybackState.getAdGroupIndexForPositionUs(
|
|
||||||
C.msToUs(contentPositionMs), C.msToUs(contentDurationMs));
|
|
||||||
} else if (imaAdState == IMA_AD_STATE_NONE && !playingAd && hasContentDuration) {
|
} else if (imaAdState == IMA_AD_STATE_NONE && !playingAd && hasContentDuration) {
|
||||||
contentPositionMs = getContentPeriodPositionMs(player, timeline, period);
|
contentPositionMs = getContentPeriodPositionMs(player, timeline, period);
|
||||||
// Update the expected ad group index for the current content position. The update is delayed
|
|
||||||
// until MAXIMUM_PRELOAD_DURATION_MS before the ad so that an ad group load error delivered
|
|
||||||
// just after an ad group isn't incorrectly attributed to the next ad group.
|
|
||||||
int nextAdGroupIndex =
|
|
||||||
adPlaybackState.getAdGroupIndexAfterPositionUs(
|
|
||||||
C.msToUs(contentPositionMs), C.msToUs(contentDurationMs));
|
|
||||||
if (nextAdGroupIndex != expectedAdGroupIndex && nextAdGroupIndex != C.INDEX_UNSET) {
|
|
||||||
long nextAdGroupTimeMs = C.usToMs(adPlaybackState.adGroupTimesUs[nextAdGroupIndex]);
|
|
||||||
if (nextAdGroupTimeMs == C.TIME_END_OF_SOURCE) {
|
|
||||||
nextAdGroupTimeMs = contentDurationMs;
|
|
||||||
}
|
|
||||||
if (nextAdGroupTimeMs - contentPositionMs < MAXIMUM_PRELOAD_DURATION_MS) {
|
|
||||||
expectedAdGroupIndex = nextAdGroupIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return VideoProgressUpdate.VIDEO_TIME_NOT_READY;
|
return VideoProgressUpdate.VIDEO_TIME_NOT_READY;
|
||||||
}
|
}
|
||||||
@ -805,15 +799,7 @@ public final class ImaAdsLoader
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public VideoProgressUpdate getAdProgress() {
|
public VideoProgressUpdate getAdProgress() {
|
||||||
if (player == null) {
|
throw new IllegalStateException("Unexpected call to getAdProgress when using preloading");
|
||||||
return lastAdProgress;
|
|
||||||
} else if (imaAdState != IMA_AD_STATE_NONE && playingAd) {
|
|
||||||
long adDuration = player.getDuration();
|
|
||||||
return adDuration == C.TIME_UNSET ? VideoProgressUpdate.VIDEO_TIME_NOT_READY
|
|
||||||
: new VideoProgressUpdate(player.getCurrentPosition(), adDuration);
|
|
||||||
} else {
|
|
||||||
return VideoProgressUpdate.VIDEO_TIME_NOT_READY;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -839,30 +825,37 @@ public final class ImaAdsLoader
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void loadAd(String adUriString) {
|
public void loadAd(AdMediaInfo adMediaInfo, AdPodInfo adPodInfo) {
|
||||||
try {
|
try {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "loadAd in ad group " + adGroupIndex);
|
Log.d(TAG, "loadAd " + getAdMediaInfoString(adMediaInfo) + ", ad pod " + adPodInfo);
|
||||||
}
|
}
|
||||||
if (adsManager == null) {
|
if (adsManager == null) {
|
||||||
// Drop events after release.
|
// Drop events after release.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (adGroupIndex == C.INDEX_UNSET) {
|
int adGroupIndex = getAdGroupIndex(adPodInfo);
|
||||||
adGroupIndex = expectedAdGroupIndex;
|
int adIndexInAdGroup = adPodInfo.getAdPosition() - 1;
|
||||||
adsManager.start();
|
AdInfo adInfo = new AdInfo(adGroupIndex, adIndexInAdGroup);
|
||||||
Log.w(
|
adInfoByAdMediaInfo.put(adMediaInfo, adInfo);
|
||||||
TAG,
|
AdPlaybackState.AdGroup adGroup = adPlaybackState.adGroups[adInfo.adGroupIndex];
|
||||||
"Unexpected loadAd without LOADED event; assuming ad group index is actually "
|
if (adGroup.count == C.LENGTH_UNSET) {
|
||||||
+ expectedAdGroupIndex);
|
adPlaybackState =
|
||||||
|
adPlaybackState.withAdCount(
|
||||||
|
adInfo.adGroupIndex, Math.max(adPodInfo.getTotalAds(), adGroup.states.length));
|
||||||
|
adGroup = adPlaybackState.adGroups[adInfo.adGroupIndex];
|
||||||
}
|
}
|
||||||
int adIndexInAdGroup = getAdIndexInAdGroupToLoad(adGroupIndex);
|
for (int i = 0; i < adIndexInAdGroup; i++) {
|
||||||
if (adIndexInAdGroup == C.INDEX_UNSET) {
|
// Any preceding ads that haven't loaded are not going to load.
|
||||||
Log.w(TAG, "Unexpected loadAd in an ad group with no remaining unavailable ads");
|
if (adGroup.states[i] == AdPlaybackState.AD_STATE_UNAVAILABLE) {
|
||||||
return;
|
adPlaybackState =
|
||||||
|
adPlaybackState.withAdLoadError(
|
||||||
|
/* adGroupIndex= */ adGroupIndex, /* adIndexInAdGroup= */ i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Uri adUri = Uri.parse(adMediaInfo.getUrl());
|
||||||
adPlaybackState =
|
adPlaybackState =
|
||||||
adPlaybackState.withAdUri(adGroupIndex, adIndexInAdGroup, Uri.parse(adUriString));
|
adPlaybackState.withAdUri(adInfo.adGroupIndex, adInfo.adIndexInAdGroup, adUri);
|
||||||
updateAdPlaybackState();
|
updateAdPlaybackState();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
maybeNotifyInternalError("loadAd", e);
|
maybeNotifyInternalError("loadAd", e);
|
||||||
@ -880,69 +873,62 @@ public final class ImaAdsLoader
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void playAd() {
|
public void playAd(AdMediaInfo adMediaInfo) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "playAd");
|
Log.d(TAG, "playAd " + getAdMediaInfoString(adMediaInfo));
|
||||||
}
|
}
|
||||||
if (adsManager == null) {
|
if (adsManager == null) {
|
||||||
// Drop events after release.
|
// Drop events after release.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
switch (imaAdState) {
|
|
||||||
case IMA_AD_STATE_PLAYING:
|
if (imaAdState == IMA_AD_STATE_PLAYING) {
|
||||||
// IMA does not always call stopAd before resuming content.
|
// IMA does not always call stopAd before resuming content.
|
||||||
// See [Internal: b/38354028, b/63320878].
|
// See [Internal: b/38354028].
|
||||||
Log.w(TAG, "Unexpected playAd without stopAd");
|
Log.w(TAG, "Unexpected playAd without stopAd");
|
||||||
break;
|
|
||||||
case IMA_AD_STATE_NONE:
|
|
||||||
// IMA is requesting to play the ad, so stop faking the content position.
|
|
||||||
fakeContentProgressElapsedRealtimeMs = C.TIME_UNSET;
|
|
||||||
fakeContentProgressOffsetMs = C.TIME_UNSET;
|
|
||||||
imaAdState = IMA_AD_STATE_PLAYING;
|
|
||||||
for (int i = 0; i < adCallbacks.size(); i++) {
|
|
||||||
adCallbacks.get(i).onPlay();
|
|
||||||
}
|
|
||||||
if (shouldNotifyAdPrepareError) {
|
|
||||||
shouldNotifyAdPrepareError = false;
|
|
||||||
for (int i = 0; i < adCallbacks.size(); i++) {
|
|
||||||
adCallbacks.get(i).onError();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case IMA_AD_STATE_PAUSED:
|
|
||||||
imaAdState = IMA_AD_STATE_PLAYING;
|
|
||||||
for (int i = 0; i < adCallbacks.size(); i++) {
|
|
||||||
adCallbacks.get(i).onResume();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new IllegalStateException();
|
|
||||||
}
|
}
|
||||||
if (player == null) {
|
|
||||||
// Sometimes messages from IMA arrive after detaching the player. See [Internal: b/63801642].
|
if (imaAdState == IMA_AD_STATE_NONE) {
|
||||||
Log.w(TAG, "Unexpected playAd while detached");
|
// IMA is requesting to play the ad, so stop faking the content position.
|
||||||
} else if (!player.getPlayWhenReady()) {
|
fakeContentProgressElapsedRealtimeMs = C.TIME_UNSET;
|
||||||
|
fakeContentProgressOffsetMs = C.TIME_UNSET;
|
||||||
|
imaAdState = IMA_AD_STATE_PLAYING;
|
||||||
|
imaAdMediaInfo = adMediaInfo;
|
||||||
|
imaAdInfo = Assertions.checkNotNull(adInfoByAdMediaInfo.get(adMediaInfo));
|
||||||
|
for (int i = 0; i < adCallbacks.size(); i++) {
|
||||||
|
adCallbacks.get(i).onPlay(adMediaInfo);
|
||||||
|
}
|
||||||
|
if (pendingAdPrepareErrorAdInfo != null && pendingAdPrepareErrorAdInfo.equals(imaAdInfo)) {
|
||||||
|
pendingAdPrepareErrorAdInfo = null;
|
||||||
|
for (int i = 0; i < adCallbacks.size(); i++) {
|
||||||
|
adCallbacks.get(i).onError(adMediaInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateAdProgress();
|
||||||
|
} else {
|
||||||
|
imaAdState = IMA_AD_STATE_PLAYING;
|
||||||
|
Assertions.checkState(adMediaInfo.equals(imaAdMediaInfo));
|
||||||
|
for (int i = 0; i < adCallbacks.size(); i++) {
|
||||||
|
adCallbacks.get(i).onResume(adMediaInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!Assertions.checkNotNull(player).getPlayWhenReady()) {
|
||||||
Assertions.checkNotNull(adsManager).pause();
|
Assertions.checkNotNull(adsManager).pause();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stopAd() {
|
public void stopAd(AdMediaInfo adMediaInfo) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "stopAd");
|
Log.d(TAG, "stopAd " + getAdMediaInfoString(adMediaInfo));
|
||||||
}
|
}
|
||||||
if (adsManager == null) {
|
if (adsManager == null) {
|
||||||
// Drop event after release.
|
// Drop event after release.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (player == null) {
|
|
||||||
// Sometimes messages from IMA arrive after detaching the player. See [Internal: b/63801642].
|
Assertions.checkNotNull(player);
|
||||||
Log.w(TAG, "Unexpected stopAd while detached");
|
Assertions.checkState(imaAdState != IMA_AD_STATE_NONE);
|
||||||
}
|
|
||||||
if (imaAdState == IMA_AD_STATE_NONE) {
|
|
||||||
Log.w(TAG, "Unexpected stopAd");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
stopAdInternal();
|
stopAdInternal();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@ -951,26 +937,21 @@ public final class ImaAdsLoader
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void pauseAd() {
|
public void pauseAd(AdMediaInfo adMediaInfo) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "pauseAd");
|
Log.d(TAG, "pauseAd " + getAdMediaInfoString(adMediaInfo));
|
||||||
}
|
}
|
||||||
if (imaAdState == IMA_AD_STATE_NONE) {
|
if (imaAdState == IMA_AD_STATE_NONE) {
|
||||||
// This method is called after content is resumed.
|
// This method is called after content is resumed.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
Assertions.checkState(adMediaInfo.equals(imaAdMediaInfo));
|
||||||
imaAdState = IMA_AD_STATE_PAUSED;
|
imaAdState = IMA_AD_STATE_PAUSED;
|
||||||
for (int i = 0; i < adCallbacks.size(); i++) {
|
for (int i = 0; i < adCallbacks.size(); i++) {
|
||||||
adCallbacks.get(i).onPause();
|
adCallbacks.get(i).onPause(adMediaInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void resumeAd() {
|
|
||||||
// This method is never called. See [Internal: b/18931719].
|
|
||||||
maybeNotifyInternalError("resumeAd", new IllegalStateException("Unexpected call to resumeAd"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Player.EventListener implementation.
|
// Player.EventListener implementation.
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -1028,8 +1009,9 @@ public final class ImaAdsLoader
|
|||||||
@Override
|
@Override
|
||||||
public void onPlayerError(ExoPlaybackException error) {
|
public void onPlayerError(ExoPlaybackException error) {
|
||||||
if (imaAdState != IMA_AD_STATE_NONE) {
|
if (imaAdState != IMA_AD_STATE_NONE) {
|
||||||
|
AdMediaInfo adMediaInfo = Assertions.checkNotNull(imaAdMediaInfo);
|
||||||
for (int i = 0; i < adCallbacks.size(); i++) {
|
for (int i = 0; i < adCallbacks.size(); i++) {
|
||||||
adCallbacks.get(i).onError();
|
adCallbacks.get(i).onError(adMediaInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1071,25 +1053,13 @@ public final class ImaAdsLoader
|
|||||||
adsRenderingSettings.setPlayAdsAfterTime(midpointTimeUs / C.MICROS_PER_SECOND);
|
adsRenderingSettings.setPlayAdsAfterTime(midpointTimeUs / C.MICROS_PER_SECOND);
|
||||||
}
|
}
|
||||||
|
|
||||||
// IMA indexes any remaining midroll ad pods from 1. A preroll (if present) has index 0.
|
|
||||||
// Store an index offset as we want to index all ads (including skipped ones) from 0.
|
|
||||||
if (adGroupIndexForPosition == 0 && adGroupTimesUs[0] == 0) {
|
|
||||||
// We are playing a preroll.
|
|
||||||
podIndexOffset = 0;
|
|
||||||
} else if (adGroupIndexForPosition == C.INDEX_UNSET) {
|
|
||||||
// There's no ad to play which means there's no preroll.
|
|
||||||
podIndexOffset = -1;
|
|
||||||
} else {
|
|
||||||
// We are playing a midroll and any ads before it were skipped.
|
|
||||||
podIndexOffset = adGroupIndexForPosition - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (adGroupIndexForPosition != C.INDEX_UNSET && hasMidrollAdGroups(adGroupTimesUs)) {
|
if (adGroupIndexForPosition != C.INDEX_UNSET && hasMidrollAdGroups(adGroupTimesUs)) {
|
||||||
// Provide the player's initial position to trigger loading and playing the ad.
|
// Provide the player's initial position to trigger loading and playing the ad.
|
||||||
pendingContentPositionMs = contentPositionMs;
|
pendingContentPositionMs = contentPositionMs;
|
||||||
}
|
}
|
||||||
|
|
||||||
adsManager.init(adsRenderingSettings);
|
adsManager.init(adsRenderingSettings);
|
||||||
|
adsManager.start();
|
||||||
updateAdPlaybackState();
|
updateAdPlaybackState();
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "Initialized with ads rendering settings: " + adsRenderingSettings);
|
Log.d(TAG, "Initialized with ads rendering settings: " + adsRenderingSettings);
|
||||||
@ -1097,39 +1067,32 @@ public final class ImaAdsLoader
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void handleAdEvent(AdEvent adEvent) {
|
private void handleAdEvent(AdEvent adEvent) {
|
||||||
Ad ad = adEvent.getAd();
|
|
||||||
switch (adEvent.getType()) {
|
switch (adEvent.getType()) {
|
||||||
case LOADED:
|
case AD_BREAK_FETCH_ERROR:
|
||||||
// The ad position is not always accurate when using preloading. See [Internal: b/62613240].
|
String adGroupTimeSecondsString =
|
||||||
AdPodInfo adPodInfo = ad.getAdPodInfo();
|
Assertions.checkNotNull(adEvent.getAdData().get("adBreakTime"));
|
||||||
int podIndex = adPodInfo.getPodIndex();
|
|
||||||
adGroupIndex =
|
|
||||||
podIndex == -1 ? (adPlaybackState.adGroupCount - 1) : (podIndex + podIndexOffset);
|
|
||||||
int adPosition = adPodInfo.getAdPosition();
|
|
||||||
int adCount = adPodInfo.getTotalAds();
|
|
||||||
Assertions.checkNotNull(adsManager).start();
|
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "Loaded ad " + adPosition + " of " + adCount + " in group " + adGroupIndex);
|
Log.d(TAG, "Fetch error for ad at " + adGroupTimeSecondsString + " seconds");
|
||||||
}
|
}
|
||||||
int oldAdCount = adPlaybackState.adGroups[adGroupIndex].count;
|
int adGroupTimeSeconds = Integer.parseInt(adGroupTimeSecondsString);
|
||||||
if (adCount != oldAdCount) {
|
int adGroupIndex =
|
||||||
if (oldAdCount == C.LENGTH_UNSET) {
|
Arrays.binarySearch(
|
||||||
adPlaybackState = adPlaybackState.withAdCount(adGroupIndex, adCount);
|
adPlaybackState.adGroupTimesUs, C.MICROS_PER_SECOND * adGroupTimeSeconds);
|
||||||
updateAdPlaybackState();
|
AdPlaybackState.AdGroup adGroup = adPlaybackState.adGroups[adGroupIndex];
|
||||||
} else {
|
if (adGroup.count == C.LENGTH_UNSET) {
|
||||||
// IMA sometimes unexpectedly decreases the ad count in an ad group.
|
adPlaybackState =
|
||||||
Log.w(TAG, "Unexpected ad count in LOADED, " + adCount + ", expected " + oldAdCount);
|
adPlaybackState.withAdCount(adGroupIndex, Math.max(1, adGroup.states.length));
|
||||||
|
adGroup = adPlaybackState.adGroups[adGroupIndex];
|
||||||
|
}
|
||||||
|
for (int i = 0; i < adGroup.count; i++) {
|
||||||
|
if (adGroup.states[i] == AdPlaybackState.AD_STATE_UNAVAILABLE) {
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.d(TAG, "Removing ad " + i + " in ad group " + adGroupIndex);
|
||||||
|
}
|
||||||
|
adPlaybackState = adPlaybackState.withAdLoadError(adGroupIndex, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (adGroupIndex != expectedAdGroupIndex) {
|
updateAdPlaybackState();
|
||||||
Log.w(
|
|
||||||
TAG,
|
|
||||||
"Expected ad group index "
|
|
||||||
+ expectedAdGroupIndex
|
|
||||||
+ ", actual ad group index "
|
|
||||||
+ adGroupIndex);
|
|
||||||
expectedAdGroupIndex = adGroupIndex;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case CONTENT_PAUSE_REQUESTED:
|
case CONTENT_PAUSE_REQUESTED:
|
||||||
// After CONTENT_PAUSE_REQUESTED, IMA will playAd/pauseAd/stopAd to show one or more ads
|
// After CONTENT_PAUSE_REQUESTED, IMA will playAd/pauseAd/stopAd to show one or more ads
|
||||||
@ -1155,23 +1118,65 @@ public final class ImaAdsLoader
|
|||||||
Map<String, String> adData = adEvent.getAdData();
|
Map<String, String> adData = adEvent.getAdData();
|
||||||
String message = "AdEvent: " + adData;
|
String message = "AdEvent: " + adData;
|
||||||
Log.i(TAG, message);
|
Log.i(TAG, message);
|
||||||
if ("adLoadError".equals(adData.get("type"))) {
|
|
||||||
handleAdGroupLoadError(new IOException(message));
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private VideoProgressUpdate getAdVideoProgressUpdate() {
|
||||||
|
if (player == null) {
|
||||||
|
return lastAdProgress;
|
||||||
|
} else if (imaAdState != IMA_AD_STATE_NONE && playingAd) {
|
||||||
|
long adDuration = player.getDuration();
|
||||||
|
return adDuration == C.TIME_UNSET
|
||||||
|
? VideoProgressUpdate.VIDEO_TIME_NOT_READY
|
||||||
|
: new VideoProgressUpdate(player.getCurrentPosition(), adDuration);
|
||||||
|
} else {
|
||||||
|
return VideoProgressUpdate.VIDEO_TIME_NOT_READY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateAdProgress() {
|
||||||
|
VideoProgressUpdate videoProgressUpdate = getAdVideoProgressUpdate();
|
||||||
|
AdMediaInfo adMediaInfo = Assertions.checkNotNull(imaAdMediaInfo);
|
||||||
|
for (int i = 0; i < adCallbacks.size(); i++) {
|
||||||
|
adCallbacks.get(i).onAdProgress(adMediaInfo, videoProgressUpdate);
|
||||||
|
}
|
||||||
|
handler.removeCallbacks(updateAdProgressRunnable);
|
||||||
|
handler.postDelayed(updateAdProgressRunnable, AD_PROGRESS_UPDATE_INTERVAL_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stopUpdatingAdProgress() {
|
||||||
|
handler.removeCallbacks(updateAdProgressRunnable);
|
||||||
|
}
|
||||||
|
|
||||||
private void handlePlayerStateChanged(boolean playWhenReady, @Player.State int playbackState) {
|
private void handlePlayerStateChanged(boolean playWhenReady, @Player.State int playbackState) {
|
||||||
|
if (playingAd && imaAdState == IMA_AD_STATE_PLAYING) {
|
||||||
|
if (!bufferingAd && playbackState == Player.STATE_BUFFERING) {
|
||||||
|
AdMediaInfo adMediaInfo = Assertions.checkNotNull(imaAdMediaInfo);
|
||||||
|
for (int i = 0; i < adCallbacks.size(); i++) {
|
||||||
|
adCallbacks.get(i).onBuffering(adMediaInfo);
|
||||||
|
}
|
||||||
|
stopUpdatingAdProgress();
|
||||||
|
} else if (bufferingAd && playbackState == Player.STATE_READY) {
|
||||||
|
bufferingAd = false;
|
||||||
|
updateAdProgress();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (imaAdState == IMA_AD_STATE_NONE
|
if (imaAdState == IMA_AD_STATE_NONE
|
||||||
&& playbackState == Player.STATE_BUFFERING
|
&& playbackState == Player.STATE_BUFFERING
|
||||||
&& playWhenReady) {
|
&& playWhenReady) {
|
||||||
checkForContentComplete();
|
checkForContentComplete();
|
||||||
} else if (imaAdState != IMA_AD_STATE_NONE && playbackState == Player.STATE_ENDED) {
|
} else if (imaAdState != IMA_AD_STATE_NONE && playbackState == Player.STATE_ENDED) {
|
||||||
for (int i = 0; i < adCallbacks.size(); i++) {
|
AdMediaInfo adMediaInfo = Assertions.checkNotNull(imaAdMediaInfo);
|
||||||
adCallbacks.get(i).onEnded();
|
if (adMediaInfo == null) {
|
||||||
|
Log.w(TAG, "onEnded without ad media info");
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i < adCallbacks.size(); i++) {
|
||||||
|
adCallbacks.get(i).onEnded(adMediaInfo);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "VideoAdPlayerCallback.onEnded in onPlaybackStateChanged");
|
Log.d(TAG, "VideoAdPlayerCallback.onEnded in onPlaybackStateChanged");
|
||||||
@ -1200,9 +1205,6 @@ public final class ImaAdsLoader
|
|||||||
if (newAdGroupIndex != C.INDEX_UNSET) {
|
if (newAdGroupIndex != C.INDEX_UNSET) {
|
||||||
sentPendingContentPositionMs = false;
|
sentPendingContentPositionMs = false;
|
||||||
pendingContentPositionMs = positionMs;
|
pendingContentPositionMs = positionMs;
|
||||||
if (newAdGroupIndex != adGroupIndex) {
|
|
||||||
shouldNotifyAdPrepareError = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1215,8 +1217,13 @@ public final class ImaAdsLoader
|
|||||||
if (adFinished) {
|
if (adFinished) {
|
||||||
// IMA is waiting for the ad playback to finish so invoke the callback now.
|
// IMA is waiting for the ad playback to finish so invoke the callback now.
|
||||||
// Either CONTENT_RESUME_REQUESTED will be passed next, or playAd will be called again.
|
// Either CONTENT_RESUME_REQUESTED will be passed next, or playAd will be called again.
|
||||||
for (int i = 0; i < adCallbacks.size(); i++) {
|
@Nullable AdMediaInfo adMediaInfo = imaAdMediaInfo;
|
||||||
adCallbacks.get(i).onEnded();
|
if (adMediaInfo == null) {
|
||||||
|
Log.w(TAG, "onEnded without ad media info");
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i < adCallbacks.size(); i++) {
|
||||||
|
adCallbacks.get(i).onEnded(adMediaInfo);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "VideoAdPlayerCallback.onEnded in onTimelineChanged/onPositionDiscontinuity");
|
Log.d(TAG, "VideoAdPlayerCallback.onEnded in onTimelineChanged/onPositionDiscontinuity");
|
||||||
@ -1234,15 +1241,8 @@ public final class ImaAdsLoader
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void resumeContentInternal() {
|
private void resumeContentInternal() {
|
||||||
if (imaAdState != IMA_AD_STATE_NONE) {
|
if (imaAdInfo != null) {
|
||||||
imaAdState = IMA_AD_STATE_NONE;
|
adPlaybackState = adPlaybackState.withSkippedAdGroup(imaAdInfo.adGroupIndex);
|
||||||
if (DEBUG) {
|
|
||||||
Log.d(TAG, "Unexpected CONTENT_RESUME_REQUESTED without stopAd");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (adGroupIndex != C.INDEX_UNSET) {
|
|
||||||
adPlaybackState = adPlaybackState.withSkippedAdGroup(adGroupIndex);
|
|
||||||
adGroupIndex = C.INDEX_UNSET;
|
|
||||||
updateAdPlaybackState();
|
updateAdPlaybackState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1257,23 +1257,40 @@ public final class ImaAdsLoader
|
|||||||
|
|
||||||
private void stopAdInternal() {
|
private void stopAdInternal() {
|
||||||
imaAdState = IMA_AD_STATE_NONE;
|
imaAdState = IMA_AD_STATE_NONE;
|
||||||
int adIndexInAdGroup = adPlaybackState.adGroups[adGroupIndex].getFirstAdIndexToPlay();
|
stopUpdatingAdProgress();
|
||||||
// TODO: Handle the skipped event so the ad can be marked as skipped rather than played.
|
// TODO: Handle the skipped event so the ad can be marked as skipped rather than played.
|
||||||
|
Assertions.checkNotNull(imaAdInfo);
|
||||||
|
int adGroupIndex = imaAdInfo.adGroupIndex;
|
||||||
|
int adIndexInAdGroup = imaAdInfo.adIndexInAdGroup;
|
||||||
adPlaybackState =
|
adPlaybackState =
|
||||||
adPlaybackState.withPlayedAd(adGroupIndex, adIndexInAdGroup).withAdResumePositionUs(0);
|
adPlaybackState.withPlayedAd(adGroupIndex, adIndexInAdGroup).withAdResumePositionUs(0);
|
||||||
updateAdPlaybackState();
|
updateAdPlaybackState();
|
||||||
if (!playingAd) {
|
if (!playingAd) {
|
||||||
adGroupIndex = C.INDEX_UNSET;
|
imaAdMediaInfo = null;
|
||||||
|
imaAdInfo = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleAdGroupLoadError(Exception error) {
|
private void handleAdGroupLoadError(Exception error) {
|
||||||
int adGroupIndex =
|
if (player == null) {
|
||||||
this.adGroupIndex == C.INDEX_UNSET ? expectedAdGroupIndex : this.adGroupIndex;
|
|
||||||
if (adGroupIndex == C.INDEX_UNSET) {
|
|
||||||
// Drop the error, as we don't know which ad group it relates to.
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Once IMA signals which ad group failed to load, clean up this code.
|
||||||
|
long playerPositionMs = player.getContentPosition();
|
||||||
|
int adGroupIndex =
|
||||||
|
adPlaybackState.getAdGroupIndexForPositionUs(
|
||||||
|
C.msToUs(playerPositionMs), C.msToUs(contentDurationMs));
|
||||||
|
if (adGroupIndex == C.INDEX_UNSET) {
|
||||||
|
adGroupIndex =
|
||||||
|
adPlaybackState.getAdGroupIndexAfterPositionUs(
|
||||||
|
C.msToUs(playerPositionMs), C.msToUs(contentDurationMs));
|
||||||
|
if (adGroupIndex == C.INDEX_UNSET) {
|
||||||
|
// The error doesn't seem to relate to any ad group so give up handling it.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
AdPlaybackState.AdGroup adGroup = adPlaybackState.adGroups[adGroupIndex];
|
AdPlaybackState.AdGroup adGroup = adPlaybackState.adGroups[adGroupIndex];
|
||||||
if (adGroup.count == C.LENGTH_UNSET) {
|
if (adGroup.count == C.LENGTH_UNSET) {
|
||||||
adPlaybackState =
|
adPlaybackState =
|
||||||
@ -1313,19 +1330,20 @@ public final class ImaAdsLoader
|
|||||||
if (fakeContentProgressOffsetMs == C.TIME_END_OF_SOURCE) {
|
if (fakeContentProgressOffsetMs == C.TIME_END_OF_SOURCE) {
|
||||||
fakeContentProgressOffsetMs = contentDurationMs;
|
fakeContentProgressOffsetMs = contentDurationMs;
|
||||||
}
|
}
|
||||||
shouldNotifyAdPrepareError = true;
|
pendingAdPrepareErrorAdInfo = new AdInfo(adGroupIndex, adIndexInAdGroup);
|
||||||
} else {
|
} else {
|
||||||
|
AdMediaInfo adMediaInfo = Assertions.checkNotNull(imaAdMediaInfo);
|
||||||
// We're already playing an ad.
|
// We're already playing an ad.
|
||||||
if (adIndexInAdGroup > playingAdIndexInAdGroup) {
|
if (adIndexInAdGroup > playingAdIndexInAdGroup) {
|
||||||
// Mark the playing ad as ended so we can notify the error on the next ad and remove it,
|
// Mark the playing ad as ended so we can notify the error on the next ad and remove it,
|
||||||
// which means that the ad after will load (if any).
|
// which means that the ad after will load (if any).
|
||||||
for (int i = 0; i < adCallbacks.size(); i++) {
|
for (int i = 0; i < adCallbacks.size(); i++) {
|
||||||
adCallbacks.get(i).onEnded();
|
adCallbacks.get(i).onEnded(adMediaInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
playingAdIndexInAdGroup = adPlaybackState.adGroups[adGroupIndex].getFirstAdIndexToPlay();
|
playingAdIndexInAdGroup = adPlaybackState.adGroups[adGroupIndex].getFirstAdIndexToPlay();
|
||||||
for (int i = 0; i < adCallbacks.size(); i++) {
|
for (int i = 0; i < adCallbacks.size(); i++) {
|
||||||
adCallbacks.get(i).onError();
|
adCallbacks.get(i).onError(Assertions.checkNotNull(adMediaInfo));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
adPlaybackState = adPlaybackState.withAdLoadError(adGroupIndex, adIndexInAdGroup);
|
adPlaybackState = adPlaybackState.withAdLoadError(adGroupIndex, adIndexInAdGroup);
|
||||||
@ -1343,11 +1361,6 @@ public final class ImaAdsLoader
|
|||||||
Log.d(TAG, "adsLoader.contentComplete");
|
Log.d(TAG, "adsLoader.contentComplete");
|
||||||
}
|
}
|
||||||
sentContentComplete = true;
|
sentContentComplete = true;
|
||||||
// After sending content complete IMA will not poll the content position, so set the expected
|
|
||||||
// ad group index.
|
|
||||||
expectedAdGroupIndex =
|
|
||||||
adPlaybackState.getAdGroupIndexForPositionUs(
|
|
||||||
C.msToUs(contentDurationMs), C.msToUs(contentDurationMs));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1358,21 +1371,6 @@ public final class ImaAdsLoader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the next ad index in the specified ad group to load, or {@link C#INDEX_UNSET} if all
|
|
||||||
* ads in the ad group have loaded.
|
|
||||||
*/
|
|
||||||
private int getAdIndexInAdGroupToLoad(int adGroupIndex) {
|
|
||||||
@AdState int[] states = adPlaybackState.adGroups[adGroupIndex].states;
|
|
||||||
int adIndexInAdGroup = 0;
|
|
||||||
// IMA loads ads in order.
|
|
||||||
while (adIndexInAdGroup < states.length
|
|
||||||
&& states[adIndexInAdGroup] != AdPlaybackState.AD_STATE_UNAVAILABLE) {
|
|
||||||
adIndexInAdGroup++;
|
|
||||||
}
|
|
||||||
return adIndexInAdGroup == states.length ? C.INDEX_UNSET : adIndexInAdGroup;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void maybeNotifyPendingAdLoadError() {
|
private void maybeNotifyPendingAdLoadError() {
|
||||||
if (pendingAdLoadError != null && eventListener != null) {
|
if (pendingAdLoadError != null && eventListener != null) {
|
||||||
eventListener.onAdLoadError(pendingAdLoadError, getAdsDataSpec(adTagUri));
|
eventListener.onAdLoadError(pendingAdLoadError, getAdsDataSpec(adTagUri));
|
||||||
@ -1406,6 +1404,22 @@ public final class ImaAdsLoader
|
|||||||
- timeline.getPeriod(/* periodIndex= */ 0, period).getPositionInWindowMs();
|
- timeline.getPeriod(/* periodIndex= */ 0, period).getPositionInWindowMs();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int getAdGroupIndex(AdPodInfo adPodInfo) {
|
||||||
|
if (adPodInfo.getPodIndex() == -1) {
|
||||||
|
// This is a postroll ad.
|
||||||
|
return adPlaybackState.adGroupCount - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// adPodInfo.podIndex may be 0-based or 1-based, so for now look up the cue point instead.
|
||||||
|
long adGroupTimeUs = (long) (((float) adPodInfo.getTimeOffset()) * C.MICROS_PER_SECOND);
|
||||||
|
for (int adGroupIndex = 0; adGroupIndex < adPlaybackState.adGroupCount; adGroupIndex++) {
|
||||||
|
if (adPlaybackState.adGroupTimesUs[adGroupIndex] == adGroupTimeUs) {
|
||||||
|
return adGroupIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalStateException("Failed to find cue point");
|
||||||
|
}
|
||||||
|
|
||||||
private static long[] getAdGroupTimesUs(List<Float> cuePoints) {
|
private static long[] getAdGroupTimesUs(List<Float> cuePoints) {
|
||||||
if (cuePoints.isEmpty()) {
|
if (cuePoints.isEmpty()) {
|
||||||
// If no cue points are specified, there is a preroll ad.
|
// If no cue points are specified, there is a preroll ad.
|
||||||
@ -1435,6 +1449,12 @@ public final class ImaAdsLoader
|
|||||||
|| adError.getErrorCode() == AdErrorCode.UNKNOWN_ERROR;
|
|| adError.getErrorCode() == AdErrorCode.UNKNOWN_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Looper getImaLooper() {
|
||||||
|
// IMA SDK callbacks occur on the main thread. This method can be used to check that the player
|
||||||
|
// is using the same looper, to ensure all interaction with this class is on the main thread.
|
||||||
|
return Looper.getMainLooper();
|
||||||
|
}
|
||||||
|
|
||||||
private static boolean hasMidrollAdGroups(long[] adGroupTimesUs) {
|
private static boolean hasMidrollAdGroups(long[] adGroupTimesUs) {
|
||||||
int count = adGroupTimesUs.length;
|
int count = adGroupTimesUs.length;
|
||||||
if (count == 1) {
|
if (count == 1) {
|
||||||
@ -1463,6 +1483,49 @@ public final class ImaAdsLoader
|
|||||||
Context context, ImaSdkSettings imaSdkSettings, AdDisplayContainer adDisplayContainer);
|
Context context, ImaSdkSettings imaSdkSettings, AdDisplayContainer adDisplayContainer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getAdMediaInfoString(AdMediaInfo adMediaInfo) {
|
||||||
|
@Nullable AdInfo adInfo = adInfoByAdMediaInfo.get(adMediaInfo);
|
||||||
|
return "AdMediaInfo[" + adMediaInfo.getUrl() + (adInfo != null ? ", " + adInfo : "") + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Consider moving this into AdPlaybackState.
|
||||||
|
private static final class AdInfo {
|
||||||
|
public final int adGroupIndex;
|
||||||
|
public final int adIndexInAdGroup;
|
||||||
|
|
||||||
|
public AdInfo(int adGroupIndex, int adIndexInAdGroup) {
|
||||||
|
this.adGroupIndex = adGroupIndex;
|
||||||
|
this.adIndexInAdGroup = adIndexInAdGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(@Nullable Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
AdInfo adInfo = (AdInfo) o;
|
||||||
|
if (adGroupIndex != adInfo.adGroupIndex) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return adIndexInAdGroup == adInfo.adIndexInAdGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int result = adGroupIndex;
|
||||||
|
result = 31 * result + adIndexInAdGroup;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "(" + adGroupIndex + ", " + adIndexInAdGroup + ')';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Default {@link ImaFactory} for non-test usage, which delegates to {@link ImaSdkFactory}. */
|
/** Default {@link ImaFactory} for non-test usage, which delegates to {@link ImaSdkFactory}. */
|
||||||
private static final class DefaultImaFactory implements ImaFactory {
|
private static final class DefaultImaFactory implements ImaFactory {
|
||||||
@Override
|
@Override
|
||||||
|
@ -41,6 +41,7 @@ import com.google.ads.interactivemedia.v3.api.AdsManagerLoadedEvent;
|
|||||||
import com.google.ads.interactivemedia.v3.api.AdsRenderingSettings;
|
import com.google.ads.interactivemedia.v3.api.AdsRenderingSettings;
|
||||||
import com.google.ads.interactivemedia.v3.api.AdsRequest;
|
import com.google.ads.interactivemedia.v3.api.AdsRequest;
|
||||||
import com.google.ads.interactivemedia.v3.api.ImaSdkSettings;
|
import com.google.ads.interactivemedia.v3.api.ImaSdkSettings;
|
||||||
|
import com.google.ads.interactivemedia.v3.api.player.AdMediaInfo;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.ExoPlaybackException;
|
import com.google.android.exoplayer2.ExoPlaybackException;
|
||||||
import com.google.android.exoplayer2.Player;
|
import com.google.android.exoplayer2.Player;
|
||||||
@ -85,6 +86,7 @@ public final class ImaAdsLoaderTest {
|
|||||||
private static final long CONTENT_PERIOD_DURATION_US =
|
private static final long CONTENT_PERIOD_DURATION_US =
|
||||||
CONTENT_TIMELINE.getPeriod(/* periodIndex= */ 0, new Period()).durationUs;
|
CONTENT_TIMELINE.getPeriod(/* periodIndex= */ 0, new Period()).durationUs;
|
||||||
private static final Uri TEST_URI = Uri.EMPTY;
|
private static final Uri TEST_URI = Uri.EMPTY;
|
||||||
|
private static final AdMediaInfo TEST_AD_MEDIA_INFO = new AdMediaInfo(TEST_URI.toString());
|
||||||
private static final long TEST_AD_DURATION_US = 5 * C.MICROS_PER_SECOND;
|
private static final long TEST_AD_DURATION_US = 5 * C.MICROS_PER_SECOND;
|
||||||
private static final long[][] PREROLL_ADS_DURATIONS_US = new long[][] {{TEST_AD_DURATION_US}};
|
private static final long[][] PREROLL_ADS_DURATIONS_US = new long[][] {{TEST_AD_DURATION_US}};
|
||||||
private static final Float[] PREROLL_CUE_POINTS_SECONDS = new Float[] {0f};
|
private static final Float[] PREROLL_CUE_POINTS_SECONDS = new Float[] {0f};
|
||||||
@ -99,7 +101,7 @@ public final class ImaAdsLoaderTest {
|
|||||||
@Mock private AdsManagerLoadedEvent mockAdsManagerLoadedEvent;
|
@Mock private AdsManagerLoadedEvent mockAdsManagerLoadedEvent;
|
||||||
@Mock private com.google.ads.interactivemedia.v3.api.AdsLoader mockAdsLoader;
|
@Mock private com.google.ads.interactivemedia.v3.api.AdsLoader mockAdsLoader;
|
||||||
@Mock private ImaFactory mockImaFactory;
|
@Mock private ImaFactory mockImaFactory;
|
||||||
@Mock private AdPodInfo mockPrerollSingleAdAdPodInfo;
|
@Mock private AdPodInfo mockAdPodInfo;
|
||||||
@Mock private Ad mockPrerollSingleAd;
|
@Mock private Ad mockPrerollSingleAd;
|
||||||
|
|
||||||
private ViewGroup adViewGroup;
|
private ViewGroup adViewGroup;
|
||||||
@ -195,12 +197,12 @@ public final class ImaAdsLoaderTest {
|
|||||||
// SDK being proguarded.
|
// SDK being proguarded.
|
||||||
imaAdsLoader.requestAds(adViewGroup);
|
imaAdsLoader.requestAds(adViewGroup);
|
||||||
imaAdsLoader.onAdEvent(getAdEvent(AdEventType.LOADED, mockPrerollSingleAd));
|
imaAdsLoader.onAdEvent(getAdEvent(AdEventType.LOADED, mockPrerollSingleAd));
|
||||||
imaAdsLoader.loadAd(TEST_URI.toString());
|
imaAdsLoader.loadAd(TEST_AD_MEDIA_INFO, mockAdPodInfo);
|
||||||
imaAdsLoader.onAdEvent(getAdEvent(AdEventType.CONTENT_PAUSE_REQUESTED, mockPrerollSingleAd));
|
imaAdsLoader.onAdEvent(getAdEvent(AdEventType.CONTENT_PAUSE_REQUESTED, mockPrerollSingleAd));
|
||||||
imaAdsLoader.playAd();
|
imaAdsLoader.playAd(TEST_AD_MEDIA_INFO);
|
||||||
imaAdsLoader.onAdEvent(getAdEvent(AdEventType.STARTED, mockPrerollSingleAd));
|
imaAdsLoader.onAdEvent(getAdEvent(AdEventType.STARTED, mockPrerollSingleAd));
|
||||||
imaAdsLoader.pauseAd();
|
imaAdsLoader.pauseAd(TEST_AD_MEDIA_INFO);
|
||||||
imaAdsLoader.stopAd();
|
imaAdsLoader.stopAd(TEST_AD_MEDIA_INFO);
|
||||||
imaAdsLoader.onPlayerError(ExoPlaybackException.createForSource(new IOException()));
|
imaAdsLoader.onPlayerError(ExoPlaybackException.createForSource(new IOException()));
|
||||||
imaAdsLoader.onPositionDiscontinuity(Player.DISCONTINUITY_REASON_SEEK);
|
imaAdsLoader.onPositionDiscontinuity(Player.DISCONTINUITY_REASON_SEEK);
|
||||||
imaAdsLoader.onAdEvent(getAdEvent(AdEventType.CONTENT_RESUME_REQUESTED, /* ad= */ null));
|
imaAdsLoader.onAdEvent(getAdEvent(AdEventType.CONTENT_RESUME_REQUESTED, /* ad= */ null));
|
||||||
@ -215,11 +217,11 @@ public final class ImaAdsLoaderTest {
|
|||||||
// Load the preroll ad.
|
// Load the preroll ad.
|
||||||
imaAdsLoader.start(adsLoaderListener, adViewProvider);
|
imaAdsLoader.start(adsLoaderListener, adViewProvider);
|
||||||
imaAdsLoader.onAdEvent(getAdEvent(AdEventType.LOADED, mockPrerollSingleAd));
|
imaAdsLoader.onAdEvent(getAdEvent(AdEventType.LOADED, mockPrerollSingleAd));
|
||||||
imaAdsLoader.loadAd(TEST_URI.toString());
|
imaAdsLoader.loadAd(TEST_AD_MEDIA_INFO, mockAdPodInfo);
|
||||||
imaAdsLoader.onAdEvent(getAdEvent(AdEventType.CONTENT_PAUSE_REQUESTED, mockPrerollSingleAd));
|
imaAdsLoader.onAdEvent(getAdEvent(AdEventType.CONTENT_PAUSE_REQUESTED, mockPrerollSingleAd));
|
||||||
|
|
||||||
// Play the preroll ad.
|
// Play the preroll ad.
|
||||||
imaAdsLoader.playAd();
|
imaAdsLoader.playAd(TEST_AD_MEDIA_INFO);
|
||||||
fakeExoPlayer.setPlayingAdPosition(
|
fakeExoPlayer.setPlayingAdPosition(
|
||||||
/* adGroupIndex= */ 0,
|
/* adGroupIndex= */ 0,
|
||||||
/* adIndexInAdGroup= */ 0,
|
/* adIndexInAdGroup= */ 0,
|
||||||
@ -233,7 +235,7 @@ public final class ImaAdsLoaderTest {
|
|||||||
|
|
||||||
// Play the content.
|
// Play the content.
|
||||||
fakeExoPlayer.setPlayingContentPosition(0);
|
fakeExoPlayer.setPlayingContentPosition(0);
|
||||||
imaAdsLoader.stopAd();
|
imaAdsLoader.stopAd(TEST_AD_MEDIA_INFO);
|
||||||
imaAdsLoader.onAdEvent(getAdEvent(AdEventType.CONTENT_RESUME_REQUESTED, /* ad= */ null));
|
imaAdsLoader.onAdEvent(getAdEvent(AdEventType.CONTENT_RESUME_REQUESTED, /* ad= */ null));
|
||||||
|
|
||||||
// Verify that the preroll ad has been marked as played.
|
// Verify that the preroll ad has been marked as played.
|
||||||
@ -313,11 +315,11 @@ public final class ImaAdsLoaderTest {
|
|||||||
when(mockImaFactory.createAdsRequest()).thenReturn(mockAdsRequest);
|
when(mockImaFactory.createAdsRequest()).thenReturn(mockAdsRequest);
|
||||||
when(mockImaFactory.createAdsLoader(any(), any(), any())).thenReturn(mockAdsLoader);
|
when(mockImaFactory.createAdsLoader(any(), any(), any())).thenReturn(mockAdsLoader);
|
||||||
|
|
||||||
when(mockPrerollSingleAdAdPodInfo.getPodIndex()).thenReturn(0);
|
when(mockAdPodInfo.getPodIndex()).thenReturn(0);
|
||||||
when(mockPrerollSingleAdAdPodInfo.getTotalAds()).thenReturn(1);
|
when(mockAdPodInfo.getTotalAds()).thenReturn(1);
|
||||||
when(mockPrerollSingleAdAdPodInfo.getAdPosition()).thenReturn(1);
|
when(mockAdPodInfo.getAdPosition()).thenReturn(1);
|
||||||
|
|
||||||
when(mockPrerollSingleAd.getAdPodInfo()).thenReturn(mockPrerollSingleAdAdPodInfo);
|
when(mockPrerollSingleAd.getAdPodInfo()).thenReturn(mockAdPodInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static AdEvent getAdEvent(AdEventType adEventType, @Nullable Ad ad) {
|
private static AdEvent getAdEvent(AdEventType adEventType, @Nullable Ad ad) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user