mirror of
https://github.com/androidx/media.git
synced 2025-05-04 06:00:37 +08:00
Add support for ad playlists with ImaAdsLoader
Issue: #3750 PiperOrigin-RevId: 343878310
This commit is contained in:
parent
689e89e5f3
commit
05f6d24821
@ -17,8 +17,8 @@ package com.google.android.exoplayer2.ext.ima;
|
||||
|
||||
import static com.google.android.exoplayer2.ext.ima.ImaUtil.BITRATE_UNSET;
|
||||
import static com.google.android.exoplayer2.ext.ima.ImaUtil.TIMEOUT_UNSET;
|
||||
import static com.google.android.exoplayer2.ext.ima.ImaUtil.getAdGroupTimesUsForCuePoints;
|
||||
import static com.google.android.exoplayer2.ext.ima.ImaUtil.getImaLooper;
|
||||
import static com.google.android.exoplayer2.util.Assertions.checkArgument;
|
||||
import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
|
||||
import static com.google.android.exoplayer2.util.Assertions.checkState;
|
||||
import static java.lang.Math.max;
|
||||
@ -280,11 +280,11 @@ import java.util.Map;
|
||||
}
|
||||
}
|
||||
|
||||
/** Starts using the ads loader for playback. */
|
||||
public void start(Player player, AdViewProvider adViewProvider, EventListener eventListener) {
|
||||
this.player = player;
|
||||
player.addListener(this);
|
||||
boolean playWhenReady = player.getPlayWhenReady();
|
||||
/**
|
||||
* Starts passing events from this instance (including any pending ad playback state) and
|
||||
* registers obstructions.
|
||||
*/
|
||||
public void start(AdViewProvider adViewProvider, EventListener eventListener) {
|
||||
this.eventListener = eventListener;
|
||||
lastVolumePercent = 0;
|
||||
lastAdProgress = VideoProgressUpdate.VIDEO_TIME_NOT_READY;
|
||||
@ -293,13 +293,9 @@ import java.util.Map;
|
||||
if (!AdPlaybackState.NONE.equals(adPlaybackState)) {
|
||||
// Pass the ad playback state to the player, and resume ads if necessary.
|
||||
eventListener.onAdPlaybackState(adPlaybackState);
|
||||
if (adsManager != null && imaPausedContent && playWhenReady) {
|
||||
adsManager.resume();
|
||||
}
|
||||
} else if (adsManager != null) {
|
||||
adPlaybackState =
|
||||
new AdPlaybackState(
|
||||
adsId, ImaUtil.getAdGroupTimesUsForCuePoints(adsManager.getAdCuePoints()));
|
||||
new AdPlaybackState(adsId, getAdGroupTimesUsForCuePoints(adsManager.getAdCuePoints()));
|
||||
updateAdPlaybackState();
|
||||
}
|
||||
for (OverlayInfo overlayInfo : adViewProvider.getAdOverlayInfos()) {
|
||||
@ -311,14 +307,36 @@ import java.util.Map;
|
||||
}
|
||||
}
|
||||
|
||||
/** Stops using the ads loader for playback. */
|
||||
public void stop() {
|
||||
@Nullable Player player = this.player;
|
||||
if (player == null) {
|
||||
return;
|
||||
/**
|
||||
* Populates the ad playback state with loaded cue points, if available. Any preroll will be
|
||||
* paused immediately while waiting for this instance to be {@link #activate(Player) activated}.
|
||||
*/
|
||||
public void maybePreloadAds(long contentPositionMs, long contentDurationMs) {
|
||||
maybeInitializeAdsManager(contentPositionMs, contentDurationMs);
|
||||
}
|
||||
|
||||
/** Activates playback. */
|
||||
public void activate(Player player) {
|
||||
this.player = player;
|
||||
player.addListener(this);
|
||||
|
||||
boolean playWhenReady = player.getPlayWhenReady();
|
||||
onTimelineChanged(player.getCurrentTimeline(), Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE);
|
||||
if (!AdPlaybackState.NONE.equals(adPlaybackState)
|
||||
&& adsManager != null
|
||||
&& imaPausedContent
|
||||
&& playWhenReady) {
|
||||
adsManager.resume();
|
||||
}
|
||||
if (adsManager != null && imaPausedContent) {
|
||||
adsManager.pause();
|
||||
}
|
||||
|
||||
/** Deactivates playback. */
|
||||
public void deactivate() {
|
||||
Player player = checkNotNull(this.player);
|
||||
if (!AdPlaybackState.NONE.equals(adPlaybackState) && imaPausedContent) {
|
||||
if (adsManager != null) {
|
||||
adsManager.pause();
|
||||
}
|
||||
adPlaybackState =
|
||||
adPlaybackState.withAdResumePositionUs(
|
||||
playingAd ? C.msToUs(player.getCurrentPosition()) : 0);
|
||||
@ -326,10 +344,15 @@ import java.util.Map;
|
||||
lastVolumePercent = getPlayerVolumePercent();
|
||||
lastAdProgress = getAdVideoProgressUpdate();
|
||||
lastContentProgress = getContentVideoProgressUpdate();
|
||||
adDisplayContainer.unregisterAllFriendlyObstructions();
|
||||
|
||||
player.removeListener(this);
|
||||
this.player = null;
|
||||
}
|
||||
|
||||
/** Stops passing of events from this instance and unregisters obstructions. */
|
||||
public void stop() {
|
||||
eventListener = null;
|
||||
adDisplayContainer.unregisterAllFriendlyObstructions();
|
||||
}
|
||||
|
||||
/** Releases all resources used by the ad tag loader. */
|
||||
@ -392,7 +415,6 @@ import java.util.Map;
|
||||
// The player is being reset or contains no media.
|
||||
return;
|
||||
}
|
||||
checkArgument(timeline.getPeriodCount() == 1);
|
||||
this.timeline = timeline;
|
||||
Player player = checkNotNull(this.player);
|
||||
long contentDurationUs = timeline.getPeriod(player.getCurrentPeriodIndex(), period).durationUs;
|
||||
@ -592,14 +614,13 @@ import java.util.Map;
|
||||
}
|
||||
|
||||
private VideoProgressUpdate getContentVideoProgressUpdate() {
|
||||
if (player == null) {
|
||||
return lastContentProgress;
|
||||
}
|
||||
boolean hasContentDuration = contentDurationMs != C.TIME_UNSET;
|
||||
long contentPositionMs;
|
||||
if (pendingContentPositionMs != C.TIME_UNSET) {
|
||||
sentPendingContentPositionMs = true;
|
||||
contentPositionMs = pendingContentPositionMs;
|
||||
} else if (player == null) {
|
||||
return lastContentProgress;
|
||||
} else if (fakeContentProgressElapsedRealtimeMs != C.TIME_UNSET) {
|
||||
long elapsedSinceEndMs = SystemClock.elapsedRealtime() - fakeContentProgressElapsedRealtimeMs;
|
||||
contentPositionMs = fakeContentProgressOffsetMs + elapsedSinceEndMs;
|
||||
@ -923,7 +944,8 @@ import java.util.Map;
|
||||
adCallbacks.get(i).onResume(adMediaInfo);
|
||||
}
|
||||
}
|
||||
if (!checkNotNull(player).getPlayWhenReady()) {
|
||||
if (player == null || !player.getPlayWhenReady()) {
|
||||
// Either this loader hasn't been activated yet, or the player is paused now.
|
||||
checkNotNull(adsManager).pause();
|
||||
}
|
||||
}
|
||||
@ -941,7 +963,14 @@ import java.util.Map;
|
||||
// to a different position, so drop the event. See also [Internal: b/159111848].
|
||||
return;
|
||||
}
|
||||
checkState(adMediaInfo.equals(imaAdMediaInfo));
|
||||
if (configuration.debugModeEnabled && !adMediaInfo.equals(imaAdMediaInfo)) {
|
||||
Log.w(
|
||||
TAG,
|
||||
"Unexpected pauseAd for "
|
||||
+ getAdMediaInfoString(adMediaInfo)
|
||||
+ ", expected "
|
||||
+ getAdMediaInfoString(imaAdMediaInfo));
|
||||
}
|
||||
imaAdState = IMA_AD_STATE_PAUSED;
|
||||
for (int i = 0; i < adCallbacks.size(); i++) {
|
||||
adCallbacks.get(i).onPause(adMediaInfo);
|
||||
@ -1157,9 +1186,13 @@ import java.util.Map;
|
||||
throw new IllegalStateException("Failed to find cue point");
|
||||
}
|
||||
|
||||
private String getAdMediaInfoString(AdMediaInfo adMediaInfo) {
|
||||
private String getAdMediaInfoString(@Nullable AdMediaInfo adMediaInfo) {
|
||||
@Nullable AdInfo adInfo = adInfoByAdMediaInfo.get(adMediaInfo);
|
||||
return "AdMediaInfo[" + adMediaInfo.getUrl() + (adInfo != null ? ", " + adInfo : "") + "]";
|
||||
return "AdMediaInfo["
|
||||
+ (adMediaInfo == null ? "null" : adMediaInfo.getUrl())
|
||||
+ ", "
|
||||
+ adInfo
|
||||
+ "]";
|
||||
}
|
||||
|
||||
private static long getContentPeriodPositionMs(
|
||||
@ -1226,16 +1259,12 @@ import java.util.Map;
|
||||
if (configuration.applicationAdEventListener != null) {
|
||||
adsManager.addAdEventListener(configuration.applicationAdEventListener);
|
||||
}
|
||||
if (player != null) {
|
||||
// If a player is attached already, start playback immediately.
|
||||
try {
|
||||
adPlaybackState =
|
||||
new AdPlaybackState(
|
||||
adsId, ImaUtil.getAdGroupTimesUsForCuePoints(adsManager.getAdCuePoints()));
|
||||
updateAdPlaybackState();
|
||||
} catch (RuntimeException e) {
|
||||
maybeNotifyInternalError("onAdsManagerLoaded", e);
|
||||
}
|
||||
try {
|
||||
adPlaybackState =
|
||||
new AdPlaybackState(adsId, getAdGroupTimesUsForCuePoints(adsManager.getAdCuePoints()));
|
||||
updateAdPlaybackState();
|
||||
} catch (RuntimeException e) {
|
||||
maybeNotifyInternalError("onAdsManagerLoaded", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,6 +44,7 @@ import com.google.ads.interactivemedia.v3.api.player.VideoAdPlayer;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.ExoPlayerLibraryInfo;
|
||||
import com.google.android.exoplayer2.Player;
|
||||
import com.google.android.exoplayer2.Timeline;
|
||||
import com.google.android.exoplayer2.source.MediaSourceFactory;
|
||||
import com.google.android.exoplayer2.source.ads.AdsLoader;
|
||||
import com.google.android.exoplayer2.source.ads.AdsMediaSource;
|
||||
@ -57,6 +58,7 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
@ -371,12 +373,16 @@ public final class ImaAdsLoader implements Player.EventListener, AdsLoader {
|
||||
private final ImaUtil.Configuration configuration;
|
||||
private final Context context;
|
||||
private final ImaUtil.ImaFactory imaFactory;
|
||||
private final HashMap<Object, AdTagLoader> adTagLoaderByAdsId;
|
||||
private final HashMap<AdsMediaSource, AdTagLoader> adTagLoaderByAdsMediaSource;
|
||||
private final Timeline.Period period;
|
||||
private final Timeline.Window window;
|
||||
|
||||
private boolean wasSetPlayerCalled;
|
||||
@Nullable private Player nextPlayer;
|
||||
@Nullable private AdTagLoader adTagLoader;
|
||||
private List<String> supportedMimeTypes;
|
||||
@Nullable private Player player;
|
||||
@Nullable private AdTagLoader currentAdTagLoader;
|
||||
|
||||
private ImaAdsLoader(
|
||||
Context context, ImaUtil.Configuration configuration, ImaUtil.ImaFactory imaFactory) {
|
||||
@ -384,6 +390,10 @@ public final class ImaAdsLoader implements Player.EventListener, AdsLoader {
|
||||
this.configuration = configuration;
|
||||
this.imaFactory = imaFactory;
|
||||
supportedMimeTypes = ImmutableList.of();
|
||||
adTagLoaderByAdsId = new HashMap<>();
|
||||
adTagLoaderByAdsMediaSource = new HashMap<>();
|
||||
period = new Timeline.Period();
|
||||
window = new Timeline.Window();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -394,7 +404,7 @@ public final class ImaAdsLoader implements Player.EventListener, AdsLoader {
|
||||
@SuppressWarnings("nullness:nullness.on.outer")
|
||||
@Nullable
|
||||
public com.google.ads.interactivemedia.v3.api.AdsLoader getAdsLoader() {
|
||||
return adTagLoader != null ? adTagLoader.getAdsLoader() : null;
|
||||
return currentAdTagLoader != null ? currentAdTagLoader.getAdsLoader() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -410,7 +420,7 @@ public final class ImaAdsLoader implements Player.EventListener, AdsLoader {
|
||||
*/
|
||||
@Nullable
|
||||
public AdDisplayContainer getAdDisplayContainer() {
|
||||
return adTagLoader != null ? adTagLoader.getAdDisplayContainer() : null;
|
||||
return currentAdTagLoader != null ? currentAdTagLoader.getAdDisplayContainer() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -427,8 +437,8 @@ public final class ImaAdsLoader implements Player.EventListener, AdsLoader {
|
||||
* null} if playing audio-only ads.
|
||||
*/
|
||||
public void requestAds(DataSpec adTagDataSpec, Object adsId, @Nullable ViewGroup adViewGroup) {
|
||||
if (adTagLoader == null) {
|
||||
adTagLoader =
|
||||
if (!adTagLoaderByAdsId.containsKey(adsId)) {
|
||||
AdTagLoader adTagLoader =
|
||||
new AdTagLoader(
|
||||
context,
|
||||
configuration,
|
||||
@ -437,6 +447,7 @@ public final class ImaAdsLoader implements Player.EventListener, AdsLoader {
|
||||
adTagDataSpec,
|
||||
adsId,
|
||||
adViewGroup);
|
||||
adTagLoaderByAdsId.put(adsId, adTagLoader);
|
||||
}
|
||||
}
|
||||
|
||||
@ -448,8 +459,8 @@ public final class ImaAdsLoader implements Player.EventListener, AdsLoader {
|
||||
* IMA SDK provides the UI to skip ads in the ad view group passed via {@link AdViewProvider}.
|
||||
*/
|
||||
public void skipAd() {
|
||||
if (adTagLoader != null) {
|
||||
adTagLoader.skipAd();
|
||||
if (currentAdTagLoader != null) {
|
||||
currentAdTagLoader.skipAd();
|
||||
}
|
||||
}
|
||||
|
||||
@ -494,37 +505,67 @@ public final class ImaAdsLoader implements Player.EventListener, AdsLoader {
|
||||
EventListener eventListener) {
|
||||
checkState(
|
||||
wasSetPlayerCalled, "Set player using adsLoader.setPlayer before preparing the player.");
|
||||
player = nextPlayer;
|
||||
@Nullable Player player = this.player;
|
||||
if (player == null) {
|
||||
return;
|
||||
if (adTagLoaderByAdsMediaSource.isEmpty()) {
|
||||
player = nextPlayer;
|
||||
@Nullable Player player = this.player;
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
player.addListener(this);
|
||||
}
|
||||
|
||||
@Nullable AdTagLoader adTagLoader = adTagLoaderByAdsId.get(adsId);
|
||||
if (adTagLoader == null) {
|
||||
requestAds(adTagDataSpec, adsId, adViewProvider.getAdViewGroup());
|
||||
adTagLoader = adTagLoaderByAdsId.get(adsId);
|
||||
}
|
||||
checkNotNull(adTagLoader).start(player, adViewProvider, eventListener);
|
||||
adTagLoaderByAdsMediaSource.put(adsMediaSource, checkNotNull(adTagLoader));
|
||||
checkNotNull(adTagLoader).start(adViewProvider, eventListener);
|
||||
maybeUpdateCurrentAdTagLoader();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop(AdsMediaSource adsMediaSource) {
|
||||
if (player != null && adTagLoader != null) {
|
||||
adTagLoader.stop();
|
||||
@Nullable AdTagLoader removedAdTagLoader = adTagLoaderByAdsMediaSource.remove(adsMediaSource);
|
||||
maybeUpdateCurrentAdTagLoader();
|
||||
if (removedAdTagLoader != null) {
|
||||
removedAdTagLoader.stop();
|
||||
}
|
||||
|
||||
if (player != null && adTagLoaderByAdsMediaSource.isEmpty()) {
|
||||
player.removeListener(this);
|
||||
player = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() {
|
||||
if (adTagLoader != null) {
|
||||
if (player != null) {
|
||||
player.removeListener(this);
|
||||
player = null;
|
||||
maybeUpdateCurrentAdTagLoader();
|
||||
}
|
||||
nextPlayer = null;
|
||||
|
||||
for (AdTagLoader adTagLoader : adTagLoaderByAdsMediaSource.values()) {
|
||||
adTagLoader.release();
|
||||
}
|
||||
adTagLoaderByAdsMediaSource.clear();
|
||||
|
||||
for (AdTagLoader adTagLoader : adTagLoaderByAdsId.values()) {
|
||||
adTagLoader.release();
|
||||
}
|
||||
adTagLoaderByAdsId.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlePrepareComplete(
|
||||
AdsMediaSource adsMediaSource, int adGroupIndex, int adIndexInAdGroup) {
|
||||
if (adTagLoader != null) {
|
||||
adTagLoader.handlePrepareComplete(adGroupIndex, adIndexInAdGroup);
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
checkNotNull(adTagLoaderByAdsMediaSource.get(adsMediaSource))
|
||||
.handlePrepareComplete(adGroupIndex, adIndexInAdGroup);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -533,9 +574,112 @@ public final class ImaAdsLoader implements Player.EventListener, AdsLoader {
|
||||
int adGroupIndex,
|
||||
int adIndexInAdGroup,
|
||||
IOException exception) {
|
||||
if (adTagLoader != null) {
|
||||
adTagLoader.handlePrepareError(adGroupIndex, adIndexInAdGroup, exception);
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
checkNotNull(adTagLoaderByAdsMediaSource.get(adsMediaSource))
|
||||
.handlePrepareError(adGroupIndex, adIndexInAdGroup, exception);
|
||||
}
|
||||
|
||||
// Player.EventListener implementation.
|
||||
|
||||
@Override
|
||||
public void onTimelineChanged(Timeline timeline, @Player.TimelineChangeReason int reason) {
|
||||
if (timeline.isEmpty()) {
|
||||
// The player is being reset or contains no media.
|
||||
return;
|
||||
}
|
||||
maybeUpdateCurrentAdTagLoader();
|
||||
maybePreloadNextPeriodAds();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPositionDiscontinuity(@Player.DiscontinuityReason int reason) {
|
||||
maybeUpdateCurrentAdTagLoader();
|
||||
maybePreloadNextPeriodAds();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {
|
||||
maybePreloadNextPeriodAds();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRepeatModeChanged(@Player.RepeatMode int repeatMode) {
|
||||
maybePreloadNextPeriodAds();
|
||||
}
|
||||
|
||||
// Internal methods.
|
||||
|
||||
private void maybeUpdateCurrentAdTagLoader() {
|
||||
@Nullable AdTagLoader oldAdTagLoader = currentAdTagLoader;
|
||||
@Nullable AdTagLoader newAdTagLoader = getCurrentAdTagLoader();
|
||||
if (!Util.areEqual(oldAdTagLoader, newAdTagLoader)) {
|
||||
if (oldAdTagLoader != null) {
|
||||
oldAdTagLoader.deactivate();
|
||||
}
|
||||
currentAdTagLoader = newAdTagLoader;
|
||||
if (newAdTagLoader != null) {
|
||||
newAdTagLoader.activate(checkNotNull(player));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private AdTagLoader getCurrentAdTagLoader() {
|
||||
@Nullable Player player = this.player;
|
||||
if (player == null) {
|
||||
return null;
|
||||
}
|
||||
Timeline timeline = player.getCurrentTimeline();
|
||||
if (timeline.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
int periodIndex = player.getCurrentPeriodIndex();
|
||||
@Nullable Object adsId = timeline.getPeriod(periodIndex, period).getAdsId();
|
||||
if (adsId == null) {
|
||||
return null;
|
||||
}
|
||||
@Nullable AdTagLoader adTagLoader = adTagLoaderByAdsId.get(adsId);
|
||||
if (adTagLoader == null || !adTagLoaderByAdsMediaSource.containsValue(adTagLoader)) {
|
||||
return null;
|
||||
}
|
||||
return adTagLoader;
|
||||
}
|
||||
|
||||
private void maybePreloadNextPeriodAds() {
|
||||
@Nullable Player player = this.player;
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
Timeline timeline = player.getCurrentTimeline();
|
||||
if (timeline.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
int nextPeriodIndex =
|
||||
timeline.getNextPeriodIndex(
|
||||
player.getCurrentPeriodIndex(),
|
||||
period,
|
||||
window,
|
||||
player.getRepeatMode(),
|
||||
player.getShuffleModeEnabled());
|
||||
if (nextPeriodIndex == C.INDEX_UNSET) {
|
||||
return;
|
||||
}
|
||||
timeline.getPeriod(nextPeriodIndex, period);
|
||||
@Nullable Object nextAdsId = period.getAdsId();
|
||||
if (nextAdsId == null) {
|
||||
return;
|
||||
}
|
||||
@Nullable AdTagLoader nextAdTagLoader = adTagLoaderByAdsId.get(nextAdsId);
|
||||
if (nextAdTagLoader == null || nextAdTagLoader == currentAdTagLoader) {
|
||||
return;
|
||||
}
|
||||
long periodPositionUs =
|
||||
timeline.getPeriodPosition(
|
||||
window, period, period.windowIndex, /* windowPositionUs= */ C.TIME_UNSET)
|
||||
.second;
|
||||
nextAdTagLoader.maybePreloadAds(C.usToMs(periodPositionUs), C.usToMs(period.durationUs));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -21,25 +21,30 @@ import com.google.android.exoplayer2.Player;
|
||||
import com.google.android.exoplayer2.Timeline;
|
||||
import com.google.android.exoplayer2.testutil.StubExoPlayer;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
|
||||
import java.util.ArrayList;
|
||||
import com.google.android.exoplayer2.util.ListenerSet;
|
||||
|
||||
/** A fake player for testing content/ad playback. */
|
||||
/* package */ final class FakePlayer extends StubExoPlayer {
|
||||
|
||||
private final ArrayList<Player.EventListener> listeners;
|
||||
private final ListenerSet<EventListener, Events> listeners;
|
||||
private final Timeline.Period period;
|
||||
private final Timeline timeline;
|
||||
|
||||
private Timeline timeline;
|
||||
@Player.State private int state;
|
||||
private boolean playWhenReady;
|
||||
private long position;
|
||||
private long contentPosition;
|
||||
private int periodIndex;
|
||||
private long positionMs;
|
||||
private long contentPositionMs;
|
||||
private boolean isPlayingAd;
|
||||
private int adGroupIndex;
|
||||
private int adIndexInAdGroup;
|
||||
|
||||
public FakePlayer() {
|
||||
listeners = new ArrayList<>();
|
||||
listeners =
|
||||
new ListenerSet<>(
|
||||
Looper.getMainLooper(),
|
||||
Player.Events::new,
|
||||
(listener, eventFlags) -> listener.onEvents(/* player= */ this, eventFlags));
|
||||
period = new Timeline.Period();
|
||||
state = Player.STATE_IDLE;
|
||||
playWhenReady = true;
|
||||
@ -48,26 +53,27 @@ import java.util.ArrayList;
|
||||
|
||||
/** Sets the timeline on this fake player, which notifies listeners with the changed timeline. */
|
||||
public void updateTimeline(Timeline timeline, @TimelineChangeReason int reason) {
|
||||
for (Player.EventListener listener : listeners) {
|
||||
listener.onTimelineChanged(timeline, reason);
|
||||
}
|
||||
this.timeline = timeline;
|
||||
listeners.sendEvent(
|
||||
Player.EVENT_TIMELINE_CHANGED, listener -> listener.onTimelineChanged(timeline, reason));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the state of this player as if it were playing content at the given {@code position}. If
|
||||
* an ad is currently playing, this will trigger a position discontinuity.
|
||||
*/
|
||||
public void setPlayingContentPosition(long position) {
|
||||
public void setPlayingContentPosition(int periodIndex, long positionMs) {
|
||||
boolean notify = isPlayingAd;
|
||||
isPlayingAd = false;
|
||||
adGroupIndex = C.INDEX_UNSET;
|
||||
adIndexInAdGroup = C.INDEX_UNSET;
|
||||
this.position = position;
|
||||
contentPosition = position;
|
||||
this.periodIndex = periodIndex;
|
||||
this.positionMs = positionMs;
|
||||
contentPositionMs = positionMs;
|
||||
if (notify) {
|
||||
for (Player.EventListener listener : listeners) {
|
||||
listener.onPositionDiscontinuity(DISCONTINUITY_REASON_AD_INSERTION);
|
||||
}
|
||||
listeners.sendEvent(
|
||||
Player.EVENT_POSITION_DISCONTINUITY,
|
||||
listener -> listener.onPositionDiscontinuity(DISCONTINUITY_REASON_AD_INSERTION));
|
||||
}
|
||||
}
|
||||
|
||||
@ -77,17 +83,22 @@ import java.util.ArrayList;
|
||||
* position discontinuity.
|
||||
*/
|
||||
public void setPlayingAdPosition(
|
||||
int adGroupIndex, int adIndexInAdGroup, long position, long contentPosition) {
|
||||
int periodIndex,
|
||||
int adGroupIndex,
|
||||
int adIndexInAdGroup,
|
||||
long positionMs,
|
||||
long contentPositionMs) {
|
||||
boolean notify = !isPlayingAd || this.adIndexInAdGroup != adIndexInAdGroup;
|
||||
isPlayingAd = true;
|
||||
this.periodIndex = periodIndex;
|
||||
this.adGroupIndex = adGroupIndex;
|
||||
this.adIndexInAdGroup = adIndexInAdGroup;
|
||||
this.position = position;
|
||||
this.contentPosition = contentPosition;
|
||||
this.positionMs = positionMs;
|
||||
this.contentPositionMs = contentPositionMs;
|
||||
if (notify) {
|
||||
for (Player.EventListener listener : listeners) {
|
||||
listener.onPositionDiscontinuity(DISCONTINUITY_REASON_AD_INSERTION);
|
||||
}
|
||||
listeners.sendEvent(
|
||||
EVENT_POSITION_DISCONTINUITY,
|
||||
listener -> listener.onPositionDiscontinuity(DISCONTINUITY_REASON_AD_INSERTION));
|
||||
}
|
||||
}
|
||||
|
||||
@ -99,16 +110,18 @@ import java.util.ArrayList;
|
||||
this.state = state;
|
||||
this.playWhenReady = playWhenReady;
|
||||
if (playbackStateChanged || playWhenReadyChanged) {
|
||||
for (Player.EventListener listener : listeners) {
|
||||
listener.onPlayerStateChanged(playWhenReady, state);
|
||||
if (playbackStateChanged) {
|
||||
listener.onPlaybackStateChanged(state);
|
||||
}
|
||||
if (playWhenReadyChanged) {
|
||||
listener.onPlayWhenReadyChanged(
|
||||
playWhenReady, PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST);
|
||||
}
|
||||
}
|
||||
listeners.sendEvent(
|
||||
Player.EVENT_PLAYBACK_STATE_CHANGED,
|
||||
listener -> {
|
||||
listener.onPlayerStateChanged(playWhenReady, state);
|
||||
if (playbackStateChanged) {
|
||||
listener.onPlaybackStateChanged(state);
|
||||
}
|
||||
if (playWhenReadyChanged) {
|
||||
listener.onPlayWhenReadyChanged(
|
||||
playWhenReady, PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -145,6 +158,17 @@ import java.util.ArrayList;
|
||||
return playWhenReady;
|
||||
}
|
||||
|
||||
@Override
|
||||
@RepeatMode
|
||||
public int getRepeatMode() {
|
||||
return REPEAT_MODE_OFF;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getShuffleModeEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRendererCount() {
|
||||
return 0;
|
||||
@ -162,7 +186,7 @@ import java.util.ArrayList;
|
||||
|
||||
@Override
|
||||
public int getCurrentPeriodIndex() {
|
||||
return 0;
|
||||
return periodIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -186,7 +210,7 @@ import java.util.ArrayList;
|
||||
|
||||
@Override
|
||||
public long getCurrentPosition() {
|
||||
return position;
|
||||
return positionMs;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -206,6 +230,6 @@ import java.util.ArrayList;
|
||||
|
||||
@Override
|
||||
public long getContentPosition() {
|
||||
return contentPosition;
|
||||
return contentPositionMs;
|
||||
}
|
||||
}
|
||||
|
@ -53,18 +53,15 @@ import com.google.ads.interactivemedia.v3.api.player.VideoAdPlayer;
|
||||
import com.google.ads.interactivemedia.v3.api.player.VideoProgressUpdate;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.ExoPlaybackException;
|
||||
import com.google.android.exoplayer2.MediaItem;
|
||||
import com.google.android.exoplayer2.Player;
|
||||
import com.google.android.exoplayer2.Timeline;
|
||||
import com.google.android.exoplayer2.Timeline.Period;
|
||||
import com.google.android.exoplayer2.ext.ima.ImaUtil.ImaFactory;
|
||||
import com.google.android.exoplayer2.source.DefaultMediaSourceFactory;
|
||||
import com.google.android.exoplayer2.source.MaskingMediaSource.PlaceholderTimeline;
|
||||
import com.google.android.exoplayer2.source.ads.AdPlaybackState;
|
||||
import com.google.android.exoplayer2.source.ads.AdsLoader;
|
||||
import com.google.android.exoplayer2.source.ads.AdsMediaSource;
|
||||
import com.google.android.exoplayer2.source.ads.AdsMediaSource.AdLoadException;
|
||||
import com.google.android.exoplayer2.source.ads.SinglePeriodAdTimeline;
|
||||
import com.google.android.exoplayer2.testutil.FakeMediaSource;
|
||||
import com.google.android.exoplayer2.testutil.FakeTimeline;
|
||||
import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition;
|
||||
@ -133,12 +130,13 @@ public final class ImaAdsLoaderTest {
|
||||
private ContentProgressProvider contentProgressProvider;
|
||||
private VideoAdPlayer videoAdPlayer;
|
||||
private TestAdsLoaderListener adsLoaderListener;
|
||||
private FakePlayer fakeExoPlayer;
|
||||
private FakePlayer fakePlayer;
|
||||
private ImaAdsLoader imaAdsLoader;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
setupMocks();
|
||||
fakePlayer = new FakePlayer();
|
||||
adViewGroup = new FrameLayout(getApplicationContext());
|
||||
View adOverlayView = new View(getApplicationContext());
|
||||
adViewProvider =
|
||||
@ -166,19 +164,32 @@ public final class ImaAdsLoaderTest {
|
||||
return ImmutableList.of();
|
||||
}
|
||||
};
|
||||
imaAdsLoader =
|
||||
new ImaAdsLoader.Builder(getApplicationContext())
|
||||
.setImaFactory(mockImaFactory)
|
||||
.setImaSdkSettings(mockImaSdkSettings)
|
||||
.build();
|
||||
imaAdsLoader.setPlayer(fakePlayer);
|
||||
adsMediaSource =
|
||||
new AdsMediaSource(
|
||||
new FakeMediaSource(CONTENT_TIMELINE),
|
||||
TEST_DATA_SPEC,
|
||||
TEST_ADS_ID,
|
||||
new DefaultMediaSourceFactory((Context) getApplicationContext()),
|
||||
imaAdsLoader,
|
||||
adViewProvider);
|
||||
adsLoaderListener = new TestAdsLoaderListener(getInitialTimelineWindowDefinition(TEST_ADS_ID));
|
||||
when(mockAdsManager.getAdCuePoints()).thenReturn(PREROLL_CUE_POINTS_SECONDS);
|
||||
}
|
||||
|
||||
@After
|
||||
public void teardown() {
|
||||
if (imaAdsLoader != null) {
|
||||
imaAdsLoader.release();
|
||||
}
|
||||
imaAdsLoader.release();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void builder_overridesPlayerType() {
|
||||
public void loader_overridesCustomPlayerType() {
|
||||
when(mockImaSdkSettings.getPlayerType()).thenReturn("test player type");
|
||||
setupPlayback(CONTENT_TIMELINE, PREROLL_CUE_POINTS_SECONDS);
|
||||
imaAdsLoader.start(
|
||||
adsMediaSource, TEST_DATA_SPEC, TEST_ADS_ID, adViewProvider, adsLoaderListener);
|
||||
|
||||
@ -187,7 +198,6 @@ public final class ImaAdsLoaderTest {
|
||||
|
||||
@Test
|
||||
public void start_setsAdUiViewGroup() {
|
||||
setupPlayback(CONTENT_TIMELINE, PREROLL_CUE_POINTS_SECONDS);
|
||||
imaAdsLoader.start(
|
||||
adsMediaSource, TEST_DATA_SPEC, TEST_ADS_ID, adViewProvider, adsLoaderListener);
|
||||
|
||||
@ -198,7 +208,6 @@ public final class ImaAdsLoaderTest {
|
||||
|
||||
@Test
|
||||
public void startForAudioOnlyAds_createsAudioOnlyAdDisplayContainer() {
|
||||
setupPlayback(CONTENT_TIMELINE, PREROLL_CUE_POINTS_SECONDS);
|
||||
imaAdsLoader.start(
|
||||
adsMediaSource, TEST_DATA_SPEC, TEST_ADS_ID, audioAdsAdViewProvider, adsLoaderListener);
|
||||
|
||||
@ -210,8 +219,11 @@ public final class ImaAdsLoaderTest {
|
||||
|
||||
@Test
|
||||
public void start_withPlaceholderContent_initializedAdsLoader() {
|
||||
Timeline placeholderTimeline = new PlaceholderTimeline(MediaItem.fromUri(Uri.EMPTY));
|
||||
setupPlayback(placeholderTimeline, PREROLL_CUE_POINTS_SECONDS);
|
||||
adsLoaderListener =
|
||||
new TestAdsLoaderListener(
|
||||
getInitialTimelineWindowDefinition(TEST_ADS_ID, /* isPlaceholder= */ true));
|
||||
|
||||
when(mockAdsManager.getAdCuePoints()).thenReturn(PREROLL_CUE_POINTS_SECONDS);
|
||||
imaAdsLoader.start(
|
||||
adsMediaSource, TEST_DATA_SPEC, TEST_ADS_ID, adViewProvider, adsLoaderListener);
|
||||
|
||||
@ -221,11 +233,10 @@ public final class ImaAdsLoaderTest {
|
||||
|
||||
@Test
|
||||
public void start_updatesAdPlaybackState() {
|
||||
setupPlayback(CONTENT_TIMELINE, PREROLL_CUE_POINTS_SECONDS);
|
||||
imaAdsLoader.start(
|
||||
adsMediaSource, TEST_DATA_SPEC, TEST_ADS_ID, adViewProvider, adsLoaderListener);
|
||||
|
||||
assertThat(adsLoaderListener.adPlaybackState)
|
||||
assertThat(adsLoaderListener.getAdPlaybackState(/* periodIndex= */ 0))
|
||||
.isEqualTo(
|
||||
new AdPlaybackState(TEST_ADS_ID, /* adGroupTimesUs...= */ 0)
|
||||
.withContentDurationUs(CONTENT_PERIOD_DURATION_US));
|
||||
@ -233,7 +244,6 @@ public final class ImaAdsLoaderTest {
|
||||
|
||||
@Test
|
||||
public void startAfterRelease() {
|
||||
setupPlayback(CONTENT_TIMELINE, PREROLL_CUE_POINTS_SECONDS);
|
||||
imaAdsLoader.release();
|
||||
imaAdsLoader.start(
|
||||
adsMediaSource, TEST_DATA_SPEC, TEST_ADS_ID, adViewProvider, adsLoaderListener);
|
||||
@ -241,14 +251,13 @@ public final class ImaAdsLoaderTest {
|
||||
|
||||
@Test
|
||||
public void startAndCallbacksAfterRelease() {
|
||||
setupPlayback(CONTENT_TIMELINE, PREROLL_CUE_POINTS_SECONDS);
|
||||
// Request ads in order to get a reference to the ad event listener.
|
||||
imaAdsLoader.requestAds(TEST_DATA_SPEC, TEST_ADS_ID, adViewGroup);
|
||||
imaAdsLoader.release();
|
||||
imaAdsLoader.start(
|
||||
adsMediaSource, TEST_DATA_SPEC, TEST_ADS_ID, adViewProvider, adsLoaderListener);
|
||||
fakeExoPlayer.setPlayingContentPosition(/* position= */ 0);
|
||||
fakeExoPlayer.setState(Player.STATE_READY, true);
|
||||
fakePlayer.setPlayingContentPosition(/* periodIndex= */ 0, /* positionMs= */ 0);
|
||||
fakePlayer.setState(Player.STATE_READY, true);
|
||||
|
||||
// If callbacks are invoked there is no crash.
|
||||
// Note: we can't currently call getContentProgress/getAdProgress as a VerifyError is thrown
|
||||
@ -271,8 +280,6 @@ public final class ImaAdsLoaderTest {
|
||||
|
||||
@Test
|
||||
public void playback_withPrerollAd_marksAdAsPlayed() {
|
||||
setupPlayback(CONTENT_TIMELINE, PREROLL_CUE_POINTS_SECONDS);
|
||||
|
||||
// Load the preroll ad.
|
||||
imaAdsLoader.start(
|
||||
adsMediaSource, TEST_DATA_SPEC, TEST_ADS_ID, adViewProvider, adsLoaderListener);
|
||||
@ -282,24 +289,25 @@ public final class ImaAdsLoaderTest {
|
||||
|
||||
// Play the preroll ad.
|
||||
videoAdPlayer.playAd(TEST_AD_MEDIA_INFO);
|
||||
fakeExoPlayer.setPlayingAdPosition(
|
||||
fakePlayer.setPlayingAdPosition(
|
||||
/* periodIndex= */ 0,
|
||||
/* adGroupIndex= */ 0,
|
||||
/* adIndexInAdGroup= */ 0,
|
||||
/* position= */ 0,
|
||||
/* contentPosition= */ 0);
|
||||
fakeExoPlayer.setState(Player.STATE_READY, true);
|
||||
fakePlayer.setState(Player.STATE_READY, true);
|
||||
adEventListener.onAdEvent(getAdEvent(AdEventType.STARTED, mockPrerollSingleAd));
|
||||
adEventListener.onAdEvent(getAdEvent(AdEventType.FIRST_QUARTILE, mockPrerollSingleAd));
|
||||
adEventListener.onAdEvent(getAdEvent(AdEventType.MIDPOINT, mockPrerollSingleAd));
|
||||
adEventListener.onAdEvent(getAdEvent(AdEventType.THIRD_QUARTILE, mockPrerollSingleAd));
|
||||
|
||||
// Play the content.
|
||||
fakeExoPlayer.setPlayingContentPosition(0);
|
||||
fakePlayer.setPlayingContentPosition(/* periodIndex= */ 0, /* positionMs= */ 0);
|
||||
videoAdPlayer.stopAd(TEST_AD_MEDIA_INFO);
|
||||
adEventListener.onAdEvent(getAdEvent(AdEventType.CONTENT_RESUME_REQUESTED, /* ad= */ null));
|
||||
|
||||
// Verify that the preroll ad has been marked as played.
|
||||
assertThat(adsLoaderListener.adPlaybackState)
|
||||
assertThat(adsLoaderListener.getAdPlaybackState(/* periodIndex= */ 0))
|
||||
.isEqualTo(
|
||||
new AdPlaybackState(TEST_ADS_ID, /* adGroupTimesUs...= */ 0)
|
||||
.withContentDurationUs(CONTENT_PERIOD_DURATION_US)
|
||||
@ -316,14 +324,14 @@ public final class ImaAdsLoaderTest {
|
||||
when(mockMidrollFetchErrorAdEvent.getType()).thenReturn(AdEventType.AD_BREAK_FETCH_ERROR);
|
||||
when(mockMidrollFetchErrorAdEvent.getAdData())
|
||||
.thenReturn(ImmutableMap.of("adBreakTime", "20.5"));
|
||||
setupPlayback(CONTENT_TIMELINE, ImmutableList.of(20.5f));
|
||||
when(mockAdsManager.getAdCuePoints()).thenReturn(ImmutableList.of(20.5f));
|
||||
|
||||
// Simulate loading an empty midroll ad.
|
||||
imaAdsLoader.start(
|
||||
adsMediaSource, TEST_DATA_SPEC, TEST_ADS_ID, adViewProvider, adsLoaderListener);
|
||||
adEventListener.onAdEvent(mockMidrollFetchErrorAdEvent);
|
||||
|
||||
assertThat(adsLoaderListener.adPlaybackState)
|
||||
assertThat(adsLoaderListener.getAdPlaybackState(/* periodIndex= */ 0))
|
||||
.isEqualTo(
|
||||
new AdPlaybackState(TEST_ADS_ID, /* adGroupTimesUs...= */ 20_500_000)
|
||||
.withContentDurationUs(CONTENT_PERIOD_DURATION_US)
|
||||
@ -338,7 +346,7 @@ public final class ImaAdsLoaderTest {
|
||||
when(mockMidrollFetchErrorAdEvent.getType()).thenReturn(AdEventType.AD_BREAK_FETCH_ERROR);
|
||||
when(mockMidrollFetchErrorAdEvent.getAdData())
|
||||
.thenReturn(ImmutableMap.of("adBreakTime", "5.5"));
|
||||
setupPlayback(CONTENT_TIMELINE, ImmutableList.of(5.5f));
|
||||
when(mockAdsManager.getAdCuePoints()).thenReturn(ImmutableList.of(5.5f));
|
||||
|
||||
// Simulate loading an empty midroll ad and advancing the player position.
|
||||
imaAdsLoader.start(
|
||||
@ -349,7 +357,7 @@ public final class ImaAdsLoaderTest {
|
||||
playerPositionUs + TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US;
|
||||
long periodDurationUs =
|
||||
CONTENT_TIMELINE.getPeriod(/* periodIndex= */ 0, new Period()).durationUs;
|
||||
fakeExoPlayer.setPlayingContentPosition(C.usToMs(playerPositionUs));
|
||||
fakePlayer.setPlayingContentPosition(/* periodIndex= */ 0, C.usToMs(playerPositionUs));
|
||||
|
||||
// Verify the content progress is updated to reflect the new player position.
|
||||
assertThat(contentProgressProvider.getContentProgress())
|
||||
@ -364,14 +372,14 @@ public final class ImaAdsLoaderTest {
|
||||
when(mockPostrollFetchErrorAdEvent.getType()).thenReturn(AdEventType.AD_BREAK_FETCH_ERROR);
|
||||
when(mockPostrollFetchErrorAdEvent.getAdData())
|
||||
.thenReturn(ImmutableMap.of("adBreakTime", "-1"));
|
||||
setupPlayback(CONTENT_TIMELINE, ImmutableList.of(-1f));
|
||||
when(mockAdsManager.getAdCuePoints()).thenReturn(ImmutableList.of(-1f));
|
||||
|
||||
// Simulate loading an empty postroll ad.
|
||||
imaAdsLoader.start(
|
||||
adsMediaSource, TEST_DATA_SPEC, TEST_ADS_ID, adViewProvider, adsLoaderListener);
|
||||
adEventListener.onAdEvent(mockPostrollFetchErrorAdEvent);
|
||||
|
||||
assertThat(adsLoaderListener.adPlaybackState)
|
||||
assertThat(adsLoaderListener.getAdPlaybackState(/* periodIndex= */ 0))
|
||||
.isEqualTo(
|
||||
new AdPlaybackState(TEST_ADS_ID, /* adGroupTimesUs...= */ C.TIME_END_OF_SOURCE)
|
||||
.withContentDurationUs(CONTENT_PERIOD_DURATION_US)
|
||||
@ -388,18 +396,18 @@ public final class ImaAdsLoaderTest {
|
||||
adGroupPositionInWindowUs
|
||||
+ TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US;
|
||||
ImmutableList<Float> cuePoints = ImmutableList.of((float) adGroupTimeUs / C.MICROS_PER_SECOND);
|
||||
setupPlayback(CONTENT_TIMELINE, cuePoints);
|
||||
when(mockAdsManager.getAdCuePoints()).thenReturn(cuePoints);
|
||||
|
||||
// Advance playback to just before the midroll and simulate buffering.
|
||||
imaAdsLoader.start(
|
||||
adsMediaSource, TEST_DATA_SPEC, TEST_ADS_ID, adViewProvider, adsLoaderListener);
|
||||
fakeExoPlayer.setPlayingContentPosition(C.usToMs(adGroupPositionInWindowUs));
|
||||
fakeExoPlayer.setState(Player.STATE_BUFFERING, /* playWhenReady= */ true);
|
||||
fakePlayer.setPlayingContentPosition(/* periodIndex= */ 0, C.usToMs(adGroupPositionInWindowUs));
|
||||
fakePlayer.setState(Player.STATE_BUFFERING, /* playWhenReady= */ true);
|
||||
// Advance before the timeout and simulating polling content progress.
|
||||
ShadowSystemClock.advanceBy(Duration.ofSeconds(1));
|
||||
contentProgressProvider.getContentProgress();
|
||||
|
||||
assertThat(adsLoaderListener.adPlaybackState)
|
||||
assertThat(adsLoaderListener.getAdPlaybackState(/* periodIndex= */ 0))
|
||||
.isEqualTo(
|
||||
new AdPlaybackState(TEST_ADS_ID, getAdGroupTimesUsForCuePoints(cuePoints))
|
||||
.withContentDurationUs(CONTENT_PERIOD_DURATION_US));
|
||||
@ -413,18 +421,18 @@ public final class ImaAdsLoaderTest {
|
||||
adGroupPositionInWindowUs
|
||||
+ TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US;
|
||||
ImmutableList<Float> cuePoints = ImmutableList.of((float) adGroupTimeUs / C.MICROS_PER_SECOND);
|
||||
setupPlayback(CONTENT_TIMELINE, cuePoints);
|
||||
when(mockAdsManager.getAdCuePoints()).thenReturn(cuePoints);
|
||||
|
||||
// Advance playback to just before the midroll and simulate buffering.
|
||||
imaAdsLoader.start(
|
||||
adsMediaSource, TEST_DATA_SPEC, TEST_ADS_ID, adViewProvider, adsLoaderListener);
|
||||
fakeExoPlayer.setPlayingContentPosition(C.usToMs(adGroupPositionInWindowUs));
|
||||
fakeExoPlayer.setState(Player.STATE_BUFFERING, /* playWhenReady= */ true);
|
||||
fakePlayer.setPlayingContentPosition(/* periodIndex= */ 0, C.usToMs(adGroupPositionInWindowUs));
|
||||
fakePlayer.setState(Player.STATE_BUFFERING, /* playWhenReady= */ true);
|
||||
// Advance past the timeout and simulate polling content progress.
|
||||
ShadowSystemClock.advanceBy(Duration.ofSeconds(5));
|
||||
contentProgressProvider.getContentProgress();
|
||||
|
||||
assertThat(adsLoaderListener.adPlaybackState)
|
||||
assertThat(adsLoaderListener.getAdPlaybackState(/* periodIndex= */ 0))
|
||||
.isEqualTo(
|
||||
new AdPlaybackState(TEST_ADS_ID, getAdGroupTimesUsForCuePoints(cuePoints))
|
||||
.withContentDurationUs(CONTENT_PERIOD_DURATION_US)
|
||||
@ -440,14 +448,15 @@ public final class ImaAdsLoaderTest {
|
||||
midrollWindowTimeUs + TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US;
|
||||
ImmutableList<Float> cuePoints =
|
||||
ImmutableList.of(0f, (float) midrollPeriodTimeUs / C.MICROS_PER_SECOND);
|
||||
setupPlayback(CONTENT_TIMELINE, cuePoints);
|
||||
when(mockAdsManager.getAdCuePoints()).thenReturn(cuePoints);
|
||||
|
||||
fakeExoPlayer.setPlayingContentPosition(C.usToMs(midrollWindowTimeUs) - 1_000);
|
||||
fakePlayer.setPlayingContentPosition(
|
||||
/* periodIndex= */ 0, C.usToMs(midrollWindowTimeUs) - 1_000);
|
||||
imaAdsLoader.start(
|
||||
adsMediaSource, TEST_DATA_SPEC, TEST_ADS_ID, adViewProvider, adsLoaderListener);
|
||||
|
||||
verify(mockAdsRenderingSettings, never()).setPlayAdsAfterTime(anyDouble());
|
||||
assertThat(adsLoaderListener.adPlaybackState)
|
||||
assertThat(adsLoaderListener.getAdPlaybackState(/* periodIndex= */ 0))
|
||||
.isEqualTo(
|
||||
new AdPlaybackState(TEST_ADS_ID, getAdGroupTimesUsForCuePoints(cuePoints))
|
||||
.withContentDurationUs(CONTENT_PERIOD_DURATION_US));
|
||||
@ -460,9 +469,9 @@ public final class ImaAdsLoaderTest {
|
||||
midrollWindowTimeUs + TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US;
|
||||
ImmutableList<Float> cuePoints =
|
||||
ImmutableList.of(0f, (float) midrollPeriodTimeUs / C.MICROS_PER_SECOND);
|
||||
setupPlayback(CONTENT_TIMELINE, cuePoints);
|
||||
when(mockAdsManager.getAdCuePoints()).thenReturn(cuePoints);
|
||||
|
||||
fakeExoPlayer.setPlayingContentPosition(C.usToMs(midrollWindowTimeUs));
|
||||
fakePlayer.setPlayingContentPosition(/* periodIndex= */ 0, C.usToMs(midrollWindowTimeUs));
|
||||
imaAdsLoader.start(
|
||||
adsMediaSource, TEST_DATA_SPEC, TEST_ADS_ID, adViewProvider, adsLoaderListener);
|
||||
|
||||
@ -472,7 +481,7 @@ public final class ImaAdsLoaderTest {
|
||||
assertThat(playAdsAfterTimeCaptor.getValue())
|
||||
.isWithin(0.1)
|
||||
.of(expectedPlayAdsAfterTimeUs / C.MICROS_PER_SECOND);
|
||||
assertThat(adsLoaderListener.adPlaybackState)
|
||||
assertThat(adsLoaderListener.getAdPlaybackState(/* periodIndex= */ 0))
|
||||
.isEqualTo(
|
||||
new AdPlaybackState(TEST_ADS_ID, getAdGroupTimesUsForCuePoints(cuePoints))
|
||||
.withContentDurationUs(CONTENT_PERIOD_DURATION_US)
|
||||
@ -486,9 +495,10 @@ public final class ImaAdsLoaderTest {
|
||||
midrollWindowTimeUs + TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US;
|
||||
ImmutableList<Float> cuePoints =
|
||||
ImmutableList.of(0f, (float) midrollPeriodTimeUs / C.MICROS_PER_SECOND);
|
||||
setupPlayback(CONTENT_TIMELINE, cuePoints);
|
||||
when(mockAdsManager.getAdCuePoints()).thenReturn(cuePoints);
|
||||
|
||||
fakeExoPlayer.setPlayingContentPosition(C.usToMs(midrollWindowTimeUs) + 1_000);
|
||||
fakePlayer.setPlayingContentPosition(
|
||||
/* periodIndex= */ 0, C.usToMs(midrollWindowTimeUs) + 1_000);
|
||||
imaAdsLoader.start(
|
||||
adsMediaSource, TEST_DATA_SPEC, TEST_ADS_ID, adViewProvider, adsLoaderListener);
|
||||
|
||||
@ -498,7 +508,7 @@ public final class ImaAdsLoaderTest {
|
||||
assertThat(playAdsAfterTimeCaptor.getValue())
|
||||
.isWithin(0.1)
|
||||
.of(expectedPlayAdsAfterTimeUs / C.MICROS_PER_SECOND);
|
||||
assertThat(adsLoaderListener.adPlaybackState)
|
||||
assertThat(adsLoaderListener.getAdPlaybackState(/* periodIndex= */ 0))
|
||||
.isEqualTo(
|
||||
new AdPlaybackState(TEST_ADS_ID, getAdGroupTimesUsForCuePoints(cuePoints))
|
||||
.withContentDurationUs(CONTENT_PERIOD_DURATION_US)
|
||||
@ -519,14 +529,15 @@ public final class ImaAdsLoaderTest {
|
||||
ImmutableList.of(
|
||||
(float) firstMidrollPeriodTimeUs / C.MICROS_PER_SECOND,
|
||||
(float) secondMidrollPeriodTimeUs / C.MICROS_PER_SECOND);
|
||||
setupPlayback(CONTENT_TIMELINE, cuePoints);
|
||||
when(mockAdsManager.getAdCuePoints()).thenReturn(cuePoints);
|
||||
|
||||
fakeExoPlayer.setPlayingContentPosition(C.usToMs(secondMidrollWindowTimeUs) - 1_000);
|
||||
fakePlayer.setPlayingContentPosition(
|
||||
/* periodIndex= */ 0, C.usToMs(secondMidrollWindowTimeUs) - 1_000);
|
||||
imaAdsLoader.start(
|
||||
adsMediaSource, TEST_DATA_SPEC, TEST_ADS_ID, adViewProvider, adsLoaderListener);
|
||||
|
||||
verify(mockAdsRenderingSettings, never()).setPlayAdsAfterTime(anyDouble());
|
||||
assertThat(adsLoaderListener.adPlaybackState)
|
||||
assertThat(adsLoaderListener.getAdPlaybackState(/* periodIndex= */ 0))
|
||||
.isEqualTo(
|
||||
new AdPlaybackState(TEST_ADS_ID, getAdGroupTimesUsForCuePoints(cuePoints))
|
||||
.withContentDurationUs(CONTENT_PERIOD_DURATION_US));
|
||||
@ -546,9 +557,9 @@ public final class ImaAdsLoaderTest {
|
||||
ImmutableList.of(
|
||||
(float) firstMidrollPeriodTimeUs / C.MICROS_PER_SECOND,
|
||||
(float) secondMidrollPeriodTimeUs / C.MICROS_PER_SECOND);
|
||||
setupPlayback(CONTENT_TIMELINE, cuePoints);
|
||||
when(mockAdsManager.getAdCuePoints()).thenReturn(cuePoints);
|
||||
|
||||
fakeExoPlayer.setPlayingContentPosition(C.usToMs(secondMidrollWindowTimeUs));
|
||||
fakePlayer.setPlayingContentPosition(/* periodIndex= */ 0, C.usToMs(secondMidrollWindowTimeUs));
|
||||
imaAdsLoader.start(
|
||||
adsMediaSource, TEST_DATA_SPEC, TEST_ADS_ID, adViewProvider, adsLoaderListener);
|
||||
|
||||
@ -558,7 +569,7 @@ public final class ImaAdsLoaderTest {
|
||||
assertThat(playAdsAfterTimeCaptor.getValue())
|
||||
.isWithin(0.1)
|
||||
.of(expectedPlayAdsAfterTimeUs / C.MICROS_PER_SECOND);
|
||||
assertThat(adsLoaderListener.adPlaybackState)
|
||||
assertThat(adsLoaderListener.getAdPlaybackState(/* periodIndex= */ 0))
|
||||
.isEqualTo(
|
||||
new AdPlaybackState(TEST_ADS_ID, getAdGroupTimesUsForCuePoints(cuePoints))
|
||||
.withContentDurationUs(CONTENT_PERIOD_DURATION_US)
|
||||
@ -567,23 +578,30 @@ public final class ImaAdsLoaderTest {
|
||||
|
||||
@Test
|
||||
public void resumePlaybackBeforeMidroll_withoutPlayAdBeforeStartPosition_skipsPreroll() {
|
||||
imaAdsLoader =
|
||||
new ImaAdsLoader.Builder(getApplicationContext())
|
||||
.setPlayAdBeforeStartPosition(false)
|
||||
.setImaFactory(mockImaFactory)
|
||||
.setImaSdkSettings(mockImaSdkSettings)
|
||||
.build();
|
||||
imaAdsLoader.setPlayer(fakePlayer);
|
||||
adsMediaSource =
|
||||
new AdsMediaSource(
|
||||
new FakeMediaSource(CONTENT_TIMELINE),
|
||||
TEST_DATA_SPEC,
|
||||
TEST_ADS_ID,
|
||||
new DefaultMediaSourceFactory((Context) getApplicationContext()),
|
||||
imaAdsLoader,
|
||||
adViewProvider);
|
||||
long midrollWindowTimeUs = 2 * C.MICROS_PER_SECOND;
|
||||
long midrollPeriodTimeUs =
|
||||
midrollWindowTimeUs + TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US;
|
||||
ImmutableList<Float> cuePoints =
|
||||
ImmutableList.of(0f, (float) midrollPeriodTimeUs / C.MICROS_PER_SECOND);
|
||||
setupPlayback(
|
||||
CONTENT_TIMELINE,
|
||||
cuePoints,
|
||||
new ImaAdsLoader.Builder(getApplicationContext())
|
||||
.setPlayAdBeforeStartPosition(false)
|
||||
.setImaFactory(mockImaFactory)
|
||||
.setImaSdkSettings(mockImaSdkSettings)
|
||||
.build(),
|
||||
TEST_DATA_SPEC,
|
||||
TEST_ADS_ID);
|
||||
when(mockAdsManager.getAdCuePoints()).thenReturn(cuePoints);
|
||||
|
||||
fakeExoPlayer.setPlayingContentPosition(C.usToMs(midrollWindowTimeUs) - 1_000);
|
||||
fakePlayer.setPlayingContentPosition(
|
||||
/* periodIndex= */ 0, C.usToMs(midrollWindowTimeUs) - 1_000);
|
||||
imaAdsLoader.start(
|
||||
adsMediaSource, TEST_DATA_SPEC, TEST_ADS_ID, adViewProvider, adsLoaderListener);
|
||||
|
||||
@ -593,7 +611,7 @@ public final class ImaAdsLoaderTest {
|
||||
assertThat(playAdsAfterTimeCaptor.getValue())
|
||||
.isWithin(0.1d)
|
||||
.of(expectedPlayAdsAfterTimeUs / C.MICROS_PER_SECOND);
|
||||
assertThat(adsLoaderListener.adPlaybackState)
|
||||
assertThat(adsLoaderListener.getAdPlaybackState(/* periodIndex= */ 0))
|
||||
.isEqualTo(
|
||||
new AdPlaybackState(TEST_ADS_ID, getAdGroupTimesUsForCuePoints(cuePoints))
|
||||
.withSkippedAdGroup(/* adGroupIndex= */ 0)
|
||||
@ -602,23 +620,29 @@ public final class ImaAdsLoaderTest {
|
||||
|
||||
@Test
|
||||
public void resumePlaybackAtMidroll_withoutPlayAdBeforeStartPosition_skipsPreroll() {
|
||||
imaAdsLoader =
|
||||
new ImaAdsLoader.Builder(getApplicationContext())
|
||||
.setPlayAdBeforeStartPosition(false)
|
||||
.setImaFactory(mockImaFactory)
|
||||
.setImaSdkSettings(mockImaSdkSettings)
|
||||
.build();
|
||||
imaAdsLoader.setPlayer(fakePlayer);
|
||||
adsMediaSource =
|
||||
new AdsMediaSource(
|
||||
new FakeMediaSource(CONTENT_TIMELINE),
|
||||
TEST_DATA_SPEC,
|
||||
TEST_ADS_ID,
|
||||
new DefaultMediaSourceFactory((Context) getApplicationContext()),
|
||||
imaAdsLoader,
|
||||
adViewProvider);
|
||||
long midrollWindowTimeUs = 2 * C.MICROS_PER_SECOND;
|
||||
long midrollPeriodTimeUs =
|
||||
midrollWindowTimeUs + TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US;
|
||||
ImmutableList<Float> cuePoints =
|
||||
ImmutableList.of(0f, (float) midrollPeriodTimeUs / C.MICROS_PER_SECOND);
|
||||
setupPlayback(
|
||||
CONTENT_TIMELINE,
|
||||
cuePoints,
|
||||
new ImaAdsLoader.Builder(getApplicationContext())
|
||||
.setPlayAdBeforeStartPosition(false)
|
||||
.setImaFactory(mockImaFactory)
|
||||
.setImaSdkSettings(mockImaSdkSettings)
|
||||
.build(),
|
||||
TEST_DATA_SPEC,
|
||||
TEST_ADS_ID);
|
||||
when(mockAdsManager.getAdCuePoints()).thenReturn(cuePoints);
|
||||
|
||||
fakeExoPlayer.setPlayingContentPosition(C.usToMs(midrollWindowTimeUs));
|
||||
fakePlayer.setPlayingContentPosition(/* periodIndex= */ 0, C.usToMs(midrollWindowTimeUs));
|
||||
imaAdsLoader.start(
|
||||
adsMediaSource, TEST_DATA_SPEC, TEST_ADS_ID, adViewProvider, adsLoaderListener);
|
||||
|
||||
@ -628,7 +652,7 @@ public final class ImaAdsLoaderTest {
|
||||
assertThat(playAdsAfterTimeCaptor.getValue())
|
||||
.isWithin(0.1d)
|
||||
.of(expectedPlayAdsAfterTimeUs / C.MICROS_PER_SECOND);
|
||||
assertThat(adsLoaderListener.adPlaybackState)
|
||||
assertThat(adsLoaderListener.getAdPlaybackState(/* periodIndex= */ 0))
|
||||
.isEqualTo(
|
||||
new AdPlaybackState(TEST_ADS_ID, getAdGroupTimesUsForCuePoints(cuePoints))
|
||||
.withContentDurationUs(CONTENT_PERIOD_DURATION_US)
|
||||
@ -637,28 +661,35 @@ public final class ImaAdsLoaderTest {
|
||||
|
||||
@Test
|
||||
public void resumePlaybackAfterMidroll_withoutPlayAdBeforeStartPosition_skipsMidroll() {
|
||||
imaAdsLoader =
|
||||
new ImaAdsLoader.Builder(getApplicationContext())
|
||||
.setPlayAdBeforeStartPosition(false)
|
||||
.setImaFactory(mockImaFactory)
|
||||
.setImaSdkSettings(mockImaSdkSettings)
|
||||
.build();
|
||||
imaAdsLoader.setPlayer(fakePlayer);
|
||||
adsMediaSource =
|
||||
new AdsMediaSource(
|
||||
new FakeMediaSource(CONTENT_TIMELINE),
|
||||
TEST_DATA_SPEC,
|
||||
TEST_ADS_ID,
|
||||
new DefaultMediaSourceFactory((Context) getApplicationContext()),
|
||||
imaAdsLoader,
|
||||
adViewProvider);
|
||||
long midrollWindowTimeUs = 2 * C.MICROS_PER_SECOND;
|
||||
long midrollPeriodTimeUs =
|
||||
midrollWindowTimeUs + TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US;
|
||||
ImmutableList<Float> cuePoints =
|
||||
ImmutableList.of(0f, (float) midrollPeriodTimeUs / C.MICROS_PER_SECOND);
|
||||
setupPlayback(
|
||||
CONTENT_TIMELINE,
|
||||
cuePoints,
|
||||
new ImaAdsLoader.Builder(getApplicationContext())
|
||||
.setPlayAdBeforeStartPosition(false)
|
||||
.setImaFactory(mockImaFactory)
|
||||
.setImaSdkSettings(mockImaSdkSettings)
|
||||
.build(),
|
||||
TEST_DATA_SPEC,
|
||||
TEST_ADS_ID);
|
||||
when(mockAdsManager.getAdCuePoints()).thenReturn(cuePoints);
|
||||
|
||||
fakeExoPlayer.setPlayingContentPosition(C.usToMs(midrollWindowTimeUs) + 1_000);
|
||||
fakePlayer.setPlayingContentPosition(
|
||||
/* periodIndex= */ 0, C.usToMs(midrollWindowTimeUs) + 1_000);
|
||||
imaAdsLoader.start(
|
||||
adsMediaSource, TEST_DATA_SPEC, TEST_ADS_ID, adViewProvider, adsLoaderListener);
|
||||
|
||||
verify(mockAdsManager).destroy();
|
||||
assertThat(adsLoaderListener.adPlaybackState)
|
||||
assertThat(adsLoaderListener.getAdPlaybackState(/* periodIndex= */ 0))
|
||||
.isEqualTo(
|
||||
new AdPlaybackState(TEST_ADS_ID, getAdGroupTimesUsForCuePoints(cuePoints))
|
||||
.withContentDurationUs(CONTENT_PERIOD_DURATION_US)
|
||||
@ -669,6 +700,21 @@ public final class ImaAdsLoaderTest {
|
||||
@Test
|
||||
public void
|
||||
resumePlaybackBeforeSecondMidroll_withoutPlayAdBeforeStartPosition_skipsFirstMidroll() {
|
||||
imaAdsLoader =
|
||||
new ImaAdsLoader.Builder(getApplicationContext())
|
||||
.setPlayAdBeforeStartPosition(false)
|
||||
.setImaFactory(mockImaFactory)
|
||||
.setImaSdkSettings(mockImaSdkSettings)
|
||||
.build();
|
||||
imaAdsLoader.setPlayer(fakePlayer);
|
||||
adsMediaSource =
|
||||
new AdsMediaSource(
|
||||
new FakeMediaSource(CONTENT_TIMELINE),
|
||||
TEST_DATA_SPEC,
|
||||
TEST_ADS_ID,
|
||||
new DefaultMediaSourceFactory((Context) getApplicationContext()),
|
||||
imaAdsLoader,
|
||||
adViewProvider);
|
||||
long firstMidrollWindowTimeUs = 2 * C.MICROS_PER_SECOND;
|
||||
long firstMidrollPeriodTimeUs =
|
||||
firstMidrollWindowTimeUs
|
||||
@ -681,18 +727,10 @@ public final class ImaAdsLoaderTest {
|
||||
ImmutableList.of(
|
||||
(float) firstMidrollPeriodTimeUs / C.MICROS_PER_SECOND,
|
||||
(float) secondMidrollPeriodTimeUs / C.MICROS_PER_SECOND);
|
||||
setupPlayback(
|
||||
CONTENT_TIMELINE,
|
||||
cuePoints,
|
||||
new ImaAdsLoader.Builder(getApplicationContext())
|
||||
.setPlayAdBeforeStartPosition(false)
|
||||
.setImaFactory(mockImaFactory)
|
||||
.setImaSdkSettings(mockImaSdkSettings)
|
||||
.build(),
|
||||
TEST_DATA_SPEC,
|
||||
TEST_ADS_ID);
|
||||
when(mockAdsManager.getAdCuePoints()).thenReturn(cuePoints);
|
||||
|
||||
fakeExoPlayer.setPlayingContentPosition(C.usToMs(secondMidrollWindowTimeUs) - 1_000);
|
||||
fakePlayer.setPlayingContentPosition(
|
||||
/* periodIndex= */ 0, C.usToMs(secondMidrollWindowTimeUs) - 1_000);
|
||||
imaAdsLoader.start(
|
||||
adsMediaSource, TEST_DATA_SPEC, TEST_ADS_ID, adViewProvider, adsLoaderListener);
|
||||
|
||||
@ -702,7 +740,7 @@ public final class ImaAdsLoaderTest {
|
||||
assertThat(playAdsAfterTimeCaptor.getValue())
|
||||
.isWithin(0.1d)
|
||||
.of(expectedPlayAdsAfterTimeUs / C.MICROS_PER_SECOND);
|
||||
assertThat(adsLoaderListener.adPlaybackState)
|
||||
assertThat(adsLoaderListener.getAdPlaybackState(/* periodIndex= */ 0))
|
||||
.isEqualTo(
|
||||
new AdPlaybackState(TEST_ADS_ID, getAdGroupTimesUsForCuePoints(cuePoints))
|
||||
.withSkippedAdGroup(/* adGroupIndex= */ 0)
|
||||
@ -711,6 +749,21 @@ public final class ImaAdsLoaderTest {
|
||||
|
||||
@Test
|
||||
public void resumePlaybackAtSecondMidroll_withoutPlayAdBeforeStartPosition_skipsFirstMidroll() {
|
||||
imaAdsLoader =
|
||||
new ImaAdsLoader.Builder(getApplicationContext())
|
||||
.setPlayAdBeforeStartPosition(false)
|
||||
.setImaFactory(mockImaFactory)
|
||||
.setImaSdkSettings(mockImaSdkSettings)
|
||||
.build();
|
||||
imaAdsLoader.setPlayer(fakePlayer);
|
||||
adsMediaSource =
|
||||
new AdsMediaSource(
|
||||
new FakeMediaSource(CONTENT_TIMELINE),
|
||||
TEST_DATA_SPEC,
|
||||
TEST_ADS_ID,
|
||||
new DefaultMediaSourceFactory((Context) getApplicationContext()),
|
||||
imaAdsLoader,
|
||||
adViewProvider);
|
||||
long firstMidrollWindowTimeUs = 2 * C.MICROS_PER_SECOND;
|
||||
long firstMidrollPeriodTimeUs =
|
||||
firstMidrollWindowTimeUs
|
||||
@ -723,18 +776,9 @@ public final class ImaAdsLoaderTest {
|
||||
ImmutableList.of(
|
||||
(float) firstMidrollPeriodTimeUs / C.MICROS_PER_SECOND,
|
||||
(float) secondMidrollPeriodTimeUs / C.MICROS_PER_SECOND);
|
||||
setupPlayback(
|
||||
CONTENT_TIMELINE,
|
||||
cuePoints,
|
||||
new ImaAdsLoader.Builder(getApplicationContext())
|
||||
.setPlayAdBeforeStartPosition(false)
|
||||
.setImaFactory(mockImaFactory)
|
||||
.setImaSdkSettings(mockImaSdkSettings)
|
||||
.build(),
|
||||
TEST_DATA_SPEC,
|
||||
TEST_ADS_ID);
|
||||
when(mockAdsManager.getAdCuePoints()).thenReturn(cuePoints);
|
||||
|
||||
fakeExoPlayer.setPlayingContentPosition(C.usToMs(secondMidrollWindowTimeUs));
|
||||
fakePlayer.setPlayingContentPosition(/* periodIndex= */ 0, C.usToMs(secondMidrollWindowTimeUs));
|
||||
imaAdsLoader.start(
|
||||
adsMediaSource, TEST_DATA_SPEC, TEST_ADS_ID, adViewProvider, adsLoaderListener);
|
||||
|
||||
@ -744,7 +788,7 @@ public final class ImaAdsLoaderTest {
|
||||
assertThat(playAdsAfterTimeCaptor.getValue())
|
||||
.isWithin(0.1d)
|
||||
.of(expectedPlayAdsAfterTimeUs / C.MICROS_PER_SECOND);
|
||||
assertThat(adsLoaderListener.adPlaybackState)
|
||||
assertThat(adsLoaderListener.getAdPlaybackState(/* periodIndex= */ 0))
|
||||
.isEqualTo(
|
||||
new AdPlaybackState(TEST_ADS_ID, getAdGroupTimesUsForCuePoints(cuePoints))
|
||||
.withContentDurationUs(CONTENT_PERIOD_DURATION_US)
|
||||
@ -762,16 +806,6 @@ public final class ImaAdsLoaderTest {
|
||||
+ " </Ad>\n"
|
||||
+ "</VAST>";
|
||||
DataSpec adDataSpec = new DataSpec(Util.getDataUriForString("text/xml", adsResponse));
|
||||
|
||||
setupPlayback(
|
||||
CONTENT_TIMELINE,
|
||||
ImmutableList.of(0f),
|
||||
new ImaAdsLoader.Builder(getApplicationContext())
|
||||
.setImaFactory(mockImaFactory)
|
||||
.setImaSdkSettings(mockImaSdkSettings)
|
||||
.build(),
|
||||
adDataSpec,
|
||||
TEST_ADS_ID);
|
||||
imaAdsLoader.start(adsMediaSource, adDataSpec, TEST_ADS_ID, adViewProvider, adsLoaderListener);
|
||||
|
||||
verify(mockAdsRequest).setAdsResponse(adsResponse);
|
||||
@ -779,15 +813,6 @@ public final class ImaAdsLoaderTest {
|
||||
|
||||
@Test
|
||||
public void requestAdTagWithUri_requestsWithAdTagUrl() throws Exception {
|
||||
setupPlayback(
|
||||
CONTENT_TIMELINE,
|
||||
ImmutableList.of(0f),
|
||||
new ImaAdsLoader.Builder(getApplicationContext())
|
||||
.setImaFactory(mockImaFactory)
|
||||
.setImaSdkSettings(mockImaSdkSettings)
|
||||
.build(),
|
||||
TEST_DATA_SPEC,
|
||||
TEST_ADS_ID);
|
||||
imaAdsLoader.start(
|
||||
adsMediaSource, TEST_DATA_SPEC, TEST_ADS_ID, adViewProvider, adsLoaderListener);
|
||||
|
||||
@ -796,7 +821,6 @@ public final class ImaAdsLoaderTest {
|
||||
|
||||
@Test
|
||||
public void setsDefaultMimeTypes() throws Exception {
|
||||
setupPlayback(CONTENT_TIMELINE, ImmutableList.of(0f));
|
||||
imaAdsLoader.setSupportedContentTypes(C.TYPE_DASH, C.TYPE_OTHER);
|
||||
imaAdsLoader.start(
|
||||
adsMediaSource, TEST_DATA_SPEC, TEST_ADS_ID, adViewProvider, adsLoaderListener);
|
||||
@ -814,16 +838,23 @@ public final class ImaAdsLoaderTest {
|
||||
|
||||
@Test
|
||||
public void buildWithAdMediaMimeTypes_setsMimeTypes() throws Exception {
|
||||
setupPlayback(
|
||||
CONTENT_TIMELINE,
|
||||
ImmutableList.of(0f),
|
||||
imaAdsLoader =
|
||||
new ImaAdsLoader.Builder(getApplicationContext())
|
||||
.setAdMediaMimeTypes(ImmutableList.of(MimeTypes.AUDIO_MPEG))
|
||||
.setImaFactory(mockImaFactory)
|
||||
.setImaSdkSettings(mockImaSdkSettings)
|
||||
.setAdMediaMimeTypes(ImmutableList.of(MimeTypes.AUDIO_MPEG))
|
||||
.build(),
|
||||
TEST_DATA_SPEC,
|
||||
TEST_ADS_ID);
|
||||
.build();
|
||||
imaAdsLoader.setPlayer(fakePlayer);
|
||||
adsMediaSource =
|
||||
new AdsMediaSource(
|
||||
new FakeMediaSource(CONTENT_TIMELINE),
|
||||
TEST_DATA_SPEC,
|
||||
TEST_ADS_ID,
|
||||
new DefaultMediaSourceFactory((Context) getApplicationContext()),
|
||||
imaAdsLoader,
|
||||
adViewProvider);
|
||||
when(mockAdsManager.getAdCuePoints()).thenReturn(PREROLL_CUE_POINTS_SECONDS);
|
||||
|
||||
imaAdsLoader.setSupportedContentTypes(C.TYPE_OTHER);
|
||||
imaAdsLoader.start(
|
||||
adsMediaSource, TEST_DATA_SPEC, TEST_ADS_ID, adViewProvider, adsLoaderListener);
|
||||
@ -833,7 +864,6 @@ public final class ImaAdsLoaderTest {
|
||||
|
||||
@Test
|
||||
public void stop_unregistersAllVideoControlOverlays() {
|
||||
setupPlayback(CONTENT_TIMELINE, PREROLL_CUE_POINTS_SECONDS);
|
||||
imaAdsLoader.start(
|
||||
adsMediaSource, TEST_DATA_SPEC, TEST_ADS_ID, adViewProvider, adsLoaderListener);
|
||||
imaAdsLoader.requestAds(TEST_DATA_SPEC, TEST_ADS_ID, adViewGroup);
|
||||
@ -848,8 +878,9 @@ public final class ImaAdsLoaderTest {
|
||||
public void loadAd_withLargeAdCuePoint_updatesAdPlaybackStateWithLoadedAd() {
|
||||
// Use a large enough value to test correct truncating of large cue points.
|
||||
float midrollTimeSecs = Float.MAX_VALUE;
|
||||
ImmutableList<Float> cuePoints = ImmutableList.of(midrollTimeSecs);
|
||||
setupPlayback(CONTENT_TIMELINE, cuePoints);
|
||||
List<Float> cuePoints = ImmutableList.of(midrollTimeSecs);
|
||||
when(mockAdsManager.getAdCuePoints()).thenReturn(cuePoints);
|
||||
|
||||
imaAdsLoader.start(
|
||||
adsMediaSource, TEST_DATA_SPEC, TEST_ADS_ID, adViewProvider, adsLoaderListener);
|
||||
videoAdPlayer.loadAd(
|
||||
@ -886,7 +917,7 @@ public final class ImaAdsLoaderTest {
|
||||
}
|
||||
});
|
||||
|
||||
assertThat(adsLoaderListener.adPlaybackState)
|
||||
assertThat(adsLoaderListener.getAdPlaybackState(/* periodIndex= */ 0))
|
||||
.isEqualTo(
|
||||
new AdPlaybackState(TEST_ADS_ID, getAdGroupTimesUsForCuePoints(cuePoints))
|
||||
.withContentDurationUs(CONTENT_PERIOD_DURATION_US)
|
||||
@ -895,37 +926,126 @@ public final class ImaAdsLoaderTest {
|
||||
.withAdDurationsUs(new long[][] {{TEST_AD_DURATION_US}}));
|
||||
}
|
||||
|
||||
private void setupPlayback(Timeline contentTimeline, List<Float> cuePoints) {
|
||||
setupPlayback(
|
||||
contentTimeline,
|
||||
cuePoints,
|
||||
new ImaAdsLoader.Builder(getApplicationContext())
|
||||
.setImaFactory(mockImaFactory)
|
||||
.setImaSdkSettings(mockImaSdkSettings)
|
||||
.build(),
|
||||
TEST_DATA_SPEC,
|
||||
TEST_ADS_ID);
|
||||
}
|
||||
|
||||
private void setupPlayback(
|
||||
Timeline contentTimeline,
|
||||
List<Float> cuePoints,
|
||||
ImaAdsLoader imaAdsLoader,
|
||||
DataSpec adTagDataSpec,
|
||||
Object adsId) {
|
||||
fakeExoPlayer = new FakePlayer();
|
||||
adsLoaderListener = new TestAdsLoaderListener(fakeExoPlayer, contentTimeline);
|
||||
adsMediaSource =
|
||||
@Test
|
||||
public void playbackWithTwoAdsMediaSources_preloadsSecondAdTag() {
|
||||
Object secondAdsId = new Object();
|
||||
AdsMediaSource secondAdsMediaSource =
|
||||
new AdsMediaSource(
|
||||
new FakeMediaSource(new FakeTimeline(/* windowCount= */ 1)),
|
||||
adTagDataSpec,
|
||||
adsId,
|
||||
new FakeMediaSource(CONTENT_TIMELINE),
|
||||
TEST_DATA_SPEC,
|
||||
secondAdsId,
|
||||
new DefaultMediaSourceFactory((Context) getApplicationContext()),
|
||||
imaAdsLoader,
|
||||
adViewProvider);
|
||||
when(mockAdsManager.getAdCuePoints()).thenReturn(cuePoints);
|
||||
this.imaAdsLoader = imaAdsLoader;
|
||||
imaAdsLoader.setPlayer(fakeExoPlayer);
|
||||
adsLoaderListener =
|
||||
new TestAdsLoaderListener(
|
||||
getInitialTimelineWindowDefinition(TEST_ADS_ID),
|
||||
getInitialTimelineWindowDefinition(secondAdsId));
|
||||
|
||||
// Load and play the preroll ad then content.
|
||||
imaAdsLoader.start(
|
||||
adsMediaSource, TEST_DATA_SPEC, TEST_ADS_ID, adViewProvider, adsLoaderListener);
|
||||
adEventListener.onAdEvent(getAdEvent(AdEventType.LOADED, mockPrerollSingleAd));
|
||||
videoAdPlayer.loadAd(TEST_AD_MEDIA_INFO, mockAdPodInfo);
|
||||
adEventListener.onAdEvent(getAdEvent(AdEventType.CONTENT_PAUSE_REQUESTED, mockPrerollSingleAd));
|
||||
videoAdPlayer.playAd(TEST_AD_MEDIA_INFO);
|
||||
fakePlayer.setPlayingAdPosition(
|
||||
/* periodIndex= */ 0,
|
||||
/* adGroupIndex= */ 0,
|
||||
/* adIndexInAdGroup= */ 0,
|
||||
/* position= */ 0,
|
||||
/* contentPosition= */ 0);
|
||||
fakePlayer.setState(Player.STATE_READY, true);
|
||||
adEventListener.onAdEvent(getAdEvent(AdEventType.STARTED, mockPrerollSingleAd));
|
||||
adEventListener.onAdEvent(getAdEvent(AdEventType.FIRST_QUARTILE, mockPrerollSingleAd));
|
||||
adEventListener.onAdEvent(getAdEvent(AdEventType.MIDPOINT, mockPrerollSingleAd));
|
||||
adEventListener.onAdEvent(getAdEvent(AdEventType.THIRD_QUARTILE, mockPrerollSingleAd));
|
||||
fakePlayer.setPlayingContentPosition(/* periodIndex= */ 0, /* positionMs= */ 0);
|
||||
videoAdPlayer.stopAd(TEST_AD_MEDIA_INFO);
|
||||
adEventListener.onAdEvent(getAdEvent(AdEventType.CONTENT_RESUME_REQUESTED, /* ad= */ null));
|
||||
|
||||
// Simulate starting to buffer the second ads media source.
|
||||
imaAdsLoader.start(
|
||||
secondAdsMediaSource, TEST_DATA_SPEC, secondAdsId, adViewProvider, adsLoaderListener);
|
||||
|
||||
// Verify that the preroll ad has been marked as played.
|
||||
assertThat(adsLoaderListener.getAdPlaybackState(/* periodIndex= */ 0))
|
||||
.isEqualTo(
|
||||
new AdPlaybackState(TEST_ADS_ID, /* adGroupTimesUs...= */ 0)
|
||||
.withContentDurationUs(CONTENT_PERIOD_DURATION_US)
|
||||
.withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1)
|
||||
.withAdUri(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0, TEST_URI)
|
||||
.withAdDurationsUs(new long[][] {{TEST_AD_DURATION_US}})
|
||||
.withPlayedAd(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0)
|
||||
.withAdResumePositionUs(/* adResumePositionUs= */ 0));
|
||||
// Verify that the second source's ad cue points have preloaded.
|
||||
assertThat(adsLoaderListener.getAdPlaybackState(/* periodIndex= */ 1))
|
||||
.isEqualTo(new AdPlaybackState(secondAdsId, /* adGroupTimesUs...= */ 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void playbackWithTwoAdsMediaSources_preloadsSecondAdTagWithBackgroundResume() {
|
||||
Object secondAdsId = new Object();
|
||||
AdsMediaSource secondAdsMediaSource =
|
||||
new AdsMediaSource(
|
||||
new FakeMediaSource(CONTENT_TIMELINE),
|
||||
TEST_DATA_SPEC,
|
||||
secondAdsId,
|
||||
new DefaultMediaSourceFactory((Context) getApplicationContext()),
|
||||
imaAdsLoader,
|
||||
adViewProvider);
|
||||
adsLoaderListener =
|
||||
new TestAdsLoaderListener(
|
||||
getInitialTimelineWindowDefinition(TEST_ADS_ID),
|
||||
getInitialTimelineWindowDefinition(secondAdsId));
|
||||
|
||||
// Load and play the preroll ad then content.
|
||||
imaAdsLoader.start(
|
||||
adsMediaSource, TEST_DATA_SPEC, TEST_ADS_ID, adViewProvider, adsLoaderListener);
|
||||
adEventListener.onAdEvent(getAdEvent(AdEventType.LOADED, mockPrerollSingleAd));
|
||||
videoAdPlayer.loadAd(TEST_AD_MEDIA_INFO, mockAdPodInfo);
|
||||
adEventListener.onAdEvent(getAdEvent(AdEventType.CONTENT_PAUSE_REQUESTED, mockPrerollSingleAd));
|
||||
videoAdPlayer.playAd(TEST_AD_MEDIA_INFO);
|
||||
fakePlayer.setPlayingAdPosition(
|
||||
/* periodIndex= */ 0,
|
||||
/* adGroupIndex= */ 0,
|
||||
/* adIndexInAdGroup= */ 0,
|
||||
/* position= */ 0,
|
||||
/* contentPosition= */ 0);
|
||||
fakePlayer.setState(Player.STATE_READY, true);
|
||||
adEventListener.onAdEvent(getAdEvent(AdEventType.STARTED, mockPrerollSingleAd));
|
||||
adEventListener.onAdEvent(getAdEvent(AdEventType.FIRST_QUARTILE, mockPrerollSingleAd));
|
||||
adEventListener.onAdEvent(getAdEvent(AdEventType.MIDPOINT, mockPrerollSingleAd));
|
||||
adEventListener.onAdEvent(getAdEvent(AdEventType.THIRD_QUARTILE, mockPrerollSingleAd));
|
||||
fakePlayer.setPlayingContentPosition(/* periodIndex= */ 0, /* positionMs= */ 0);
|
||||
videoAdPlayer.stopAd(TEST_AD_MEDIA_INFO);
|
||||
adEventListener.onAdEvent(getAdEvent(AdEventType.CONTENT_RESUME_REQUESTED, /* ad= */ null));
|
||||
|
||||
// Simulate starting to buffer the second ads media source.
|
||||
imaAdsLoader.start(
|
||||
secondAdsMediaSource, TEST_DATA_SPEC, secondAdsId, adViewProvider, adsLoaderListener);
|
||||
|
||||
// Simulate backgrounding/resuming.
|
||||
imaAdsLoader.stop(adsMediaSource);
|
||||
imaAdsLoader.stop(secondAdsMediaSource);
|
||||
imaAdsLoader.start(
|
||||
adsMediaSource, TEST_DATA_SPEC, TEST_ADS_ID, adViewProvider, adsLoaderListener);
|
||||
imaAdsLoader.start(
|
||||
secondAdsMediaSource, TEST_DATA_SPEC, secondAdsId, adViewProvider, adsLoaderListener);
|
||||
|
||||
// Verify that the preroll ad has been marked as played.
|
||||
assertThat(adsLoaderListener.getAdPlaybackState(/* periodIndex= */ 0))
|
||||
.isEqualTo(
|
||||
new AdPlaybackState(TEST_ADS_ID, /* adGroupTimesUs...= */ 0)
|
||||
.withContentDurationUs(CONTENT_PERIOD_DURATION_US)
|
||||
.withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1)
|
||||
.withAdUri(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0, TEST_URI)
|
||||
.withAdDurationsUs(new long[][] {{TEST_AD_DURATION_US}})
|
||||
.withPlayedAd(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0)
|
||||
.withAdResumePositionUs(/* adResumePositionUs= */ 0));
|
||||
// Verify that the second source's ad cue points have preloaded.
|
||||
assertThat(adsLoaderListener.getAdPlaybackState(/* periodIndex= */ 1))
|
||||
.isEqualTo(new AdPlaybackState(secondAdsId, /* adGroupTimesUs...= */ 0));
|
||||
}
|
||||
|
||||
private void setupMocks() {
|
||||
@ -1023,16 +1143,16 @@ public final class ImaAdsLoaderTest {
|
||||
}
|
||||
|
||||
/** Ad loader event listener that forwards ad playback state to a fake player. */
|
||||
private static final class TestAdsLoaderListener implements AdsLoader.EventListener {
|
||||
private final class TestAdsLoaderListener implements AdsLoader.EventListener {
|
||||
|
||||
private final FakePlayer fakeExoPlayer;
|
||||
private final Timeline contentTimeline;
|
||||
private final TimelineWindowDefinition[] timelineWindowDefinitions;
|
||||
|
||||
public AdPlaybackState adPlaybackState;
|
||||
public TestAdsLoaderListener(TimelineWindowDefinition... timelineWindowDefinitions) {
|
||||
this.timelineWindowDefinitions = timelineWindowDefinitions;
|
||||
}
|
||||
|
||||
public TestAdsLoaderListener(FakePlayer fakeExoPlayer, Timeline contentTimeline) {
|
||||
this.fakeExoPlayer = fakeExoPlayer;
|
||||
this.contentTimeline = contentTimeline;
|
||||
public AdPlaybackState getAdPlaybackState(int periodIndex) {
|
||||
return timelineWindowDefinitions[periodIndex].adPlaybackState;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1043,10 +1163,28 @@ public final class ImaAdsLoaderTest {
|
||||
Arrays.fill(adDurationsUs[adGroupIndex], TEST_AD_DURATION_US);
|
||||
}
|
||||
adPlaybackState = adPlaybackState.withAdDurationsUs(adDurationsUs);
|
||||
this.adPlaybackState = adPlaybackState;
|
||||
fakeExoPlayer.updateTimeline(
|
||||
new SinglePeriodAdTimeline(contentTimeline, adPlaybackState),
|
||||
Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE);
|
||||
|
||||
// Update the timeline window definition(s) to reflect the new ad playback state.
|
||||
for (int i = 0; i < timelineWindowDefinitions.length; i++) {
|
||||
TimelineWindowDefinition timelineWindowDefinition = timelineWindowDefinitions[i];
|
||||
if (!Util.areEqual(timelineWindowDefinition.adPlaybackState.adsId, adPlaybackState.adsId)) {
|
||||
continue;
|
||||
}
|
||||
timelineWindowDefinitions[i] =
|
||||
new TimelineWindowDefinition(
|
||||
timelineWindowDefinition.periodCount,
|
||||
timelineWindowDefinition.id,
|
||||
timelineWindowDefinition.isSeekable,
|
||||
timelineWindowDefinition.isDynamic,
|
||||
timelineWindowDefinition.isLive,
|
||||
timelineWindowDefinition.isPlaceholder,
|
||||
timelineWindowDefinition.durationUs,
|
||||
timelineWindowDefinition.defaultPositionUs,
|
||||
timelineWindowDefinition.windowOffsetInFirstPeriodUs,
|
||||
adPlaybackState);
|
||||
}
|
||||
fakePlayer.updateTimeline(
|
||||
new FakeTimeline(timelineWindowDefinitions), Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1064,4 +1202,24 @@ public final class ImaAdsLoaderTest {
|
||||
// Do nothing.
|
||||
}
|
||||
}
|
||||
|
||||
private static TimelineWindowDefinition getInitialTimelineWindowDefinition(Object adsId) {
|
||||
return getInitialTimelineWindowDefinition(adsId, /* isPlaceholder= */ false);
|
||||
}
|
||||
|
||||
private static TimelineWindowDefinition getInitialTimelineWindowDefinition(
|
||||
Object adsId, boolean isPlaceholder) {
|
||||
return new TimelineWindowDefinition(
|
||||
/* periodCount= */ 1,
|
||||
/* id= */ new Object(),
|
||||
/* isSeekable= */ true,
|
||||
/* isDynamic= */ false,
|
||||
/* isLive= */ false,
|
||||
/* isPlaceholder= */ isPlaceholder,
|
||||
/* durationUs= */ CONTENT_DURATION_US,
|
||||
/* defaultPositionUs= */ 0,
|
||||
/* windowOffsetInFirstPeriodUs= */ TimelineWindowDefinition
|
||||
.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US,
|
||||
new AdPlaybackState(adsId));
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user