Clean up AdTagLoader and ImaAdsLoader

In preparation for adding support for ads in playlists:
- Make releasing a no-op if the instance was already released
- Remove null checks on non-null `adDisplayContainer` and `adsLoader`
- Move initializing the ads manager into a private method as it will need to be
  called from two places soon.
- Misc other cleanup.

Issue: #3750
PiperOrigin-RevId: 341021493
This commit is contained in:
andrewlewis 2020-11-06 11:42:19 +00:00 committed by Andrew Lewis
parent 764e5e8141
commit ae4cf9f1da
2 changed files with 63 additions and 56 deletions

View File

@ -152,6 +152,8 @@ import java.util.Map;
private long contentDurationMs; private long contentDurationMs;
private AdPlaybackState adPlaybackState; private AdPlaybackState adPlaybackState;
private boolean released;
// Fields tracking IMA's state. // Fields tracking IMA's state.
/** 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. */
@ -300,14 +302,12 @@ import java.util.Map;
adsId, ImaUtil.getAdGroupTimesUsForCuePoints(adsManager.getAdCuePoints())); adsId, ImaUtil.getAdGroupTimesUsForCuePoints(adsManager.getAdCuePoints()));
updateAdPlaybackState(); updateAdPlaybackState();
} }
if (adDisplayContainer != null) { for (OverlayInfo overlayInfo : adViewProvider.getAdOverlayInfos()) {
for (OverlayInfo overlayInfo : adViewProvider.getAdOverlayInfos()) { adDisplayContainer.registerFriendlyObstruction(
adDisplayContainer.registerFriendlyObstruction( imaFactory.createFriendlyObstruction(
imaFactory.createFriendlyObstruction( overlayInfo.view,
overlayInfo.view, ImaUtil.getFriendlyObstructionPurpose(overlayInfo.purpose),
ImaUtil.getFriendlyObstructionPurpose(overlayInfo.purpose), overlayInfo.reasonDetail));
overlayInfo.reasonDetail));
}
} }
} }
@ -326,9 +326,7 @@ import java.util.Map;
lastVolumePercent = getPlayerVolumePercent(); lastVolumePercent = getPlayerVolumePercent();
lastAdProgress = getAdVideoProgressUpdate(); lastAdProgress = getAdVideoProgressUpdate();
lastContentProgress = getContentVideoProgressUpdate(); lastContentProgress = getContentVideoProgressUpdate();
if (adDisplayContainer != null) { adDisplayContainer.unregisterAllFriendlyObstructions();
adDisplayContainer.unregisterAllFriendlyObstructions();
}
player.removeListener(this); player.removeListener(this);
this.player = null; this.player = null;
eventListener = null; eventListener = null;
@ -336,16 +334,18 @@ import java.util.Map;
/** Releases all resources used by the ad tag loader. */ /** Releases all resources used by the ad tag loader. */
public void release() { public void release() {
if (released) {
return;
}
released = true;
pendingAdRequestContext = null; pendingAdRequestContext = null;
destroyAdsManager(); destroyAdsManager();
if (adsLoader != null) { adsLoader.removeAdsLoadedListener(componentListener);
adsLoader.removeAdsLoadedListener(componentListener); adsLoader.removeAdErrorListener(componentListener);
adsLoader.removeAdErrorListener(componentListener); if (configuration.applicationAdErrorListener != null) {
if (configuration.applicationAdErrorListener != null) { adsLoader.removeAdErrorListener(configuration.applicationAdErrorListener);
adsLoader.removeAdErrorListener(configuration.applicationAdErrorListener);
}
adsLoader.release();
} }
adsLoader.release();
imaPausedContent = false; imaPausedContent = false;
imaAdState = IMA_AD_STATE_NONE; imaAdState = IMA_AD_STATE_NONE;
imaAdMediaInfo = null; imaAdMediaInfo = null;
@ -394,27 +394,15 @@ import java.util.Map;
} }
checkArgument(timeline.getPeriodCount() == 1); checkArgument(timeline.getPeriodCount() == 1);
this.timeline = timeline; this.timeline = timeline;
long contentDurationUs = timeline.getPeriod(/* periodIndex= */ 0, period).durationUs; Player player = checkNotNull(this.player);
long contentDurationUs = timeline.getPeriod(player.getCurrentPeriodIndex(), period).durationUs;
contentDurationMs = C.usToMs(contentDurationUs); contentDurationMs = C.usToMs(contentDurationUs);
if (contentDurationUs != C.TIME_UNSET) { if (contentDurationUs != adPlaybackState.contentDurationUs) {
adPlaybackState = adPlaybackState.withContentDurationUs(contentDurationUs); adPlaybackState = adPlaybackState.withContentDurationUs(contentDurationUs);
}
@Nullable AdsManager adsManager = this.adsManager;
if (!isAdsManagerInitialized && adsManager != null) {
isAdsManagerInitialized = true;
@Nullable AdsRenderingSettings adsRenderingSettings = setupAdsRendering();
if (adsRenderingSettings == null) {
// There are no ads to play.
destroyAdsManager();
} else {
adsManager.init(adsRenderingSettings);
adsManager.start();
if (configuration.debugModeEnabled) {
Log.d(TAG, "Initialized with ads rendering settings: " + adsRenderingSettings);
}
}
updateAdPlaybackState(); updateAdPlaybackState();
} }
long contentPositionMs = getContentPeriodPositionMs(player, timeline, period);
maybeInitializeAdsManager(contentPositionMs, contentDurationMs);
handleTimelineOrPositionChanged(); handleTimelineOrPositionChanged();
} }
@ -515,12 +503,33 @@ import java.util.Map;
return adsLoader; return adsLoader;
} }
private void maybeInitializeAdsManager(long contentPositionMs, long contentDurationMs) {
@Nullable AdsManager adsManager = this.adsManager;
if (!isAdsManagerInitialized && adsManager != null) {
isAdsManagerInitialized = true;
@Nullable
AdsRenderingSettings adsRenderingSettings =
setupAdsRendering(contentPositionMs, contentDurationMs);
if (adsRenderingSettings == null) {
// There are no ads to play.
destroyAdsManager();
} else {
adsManager.init(adsRenderingSettings);
adsManager.start();
if (configuration.debugModeEnabled) {
Log.d(TAG, "Initialized with ads rendering settings: " + adsRenderingSettings);
}
}
updateAdPlaybackState();
}
}
/** /**
* Configures ads rendering for starting playback, returning the settings for the IMA SDK or * Configures ads rendering for starting playback, returning the settings for the IMA SDK or
* {@code null} if no ads should play. * {@code null} if no ads should play.
*/ */
@Nullable @Nullable
private AdsRenderingSettings setupAdsRendering() { private AdsRenderingSettings setupAdsRendering(long contentPositionMs, long contentDurationMs) {
AdsRenderingSettings adsRenderingSettings = imaFactory.createAdsRenderingSettings(); AdsRenderingSettings adsRenderingSettings = imaFactory.createAdsRenderingSettings();
adsRenderingSettings.setEnablePreloading(true); adsRenderingSettings.setEnablePreloading(true);
adsRenderingSettings.setMimeTypes( adsRenderingSettings.setMimeTypes(
@ -541,7 +550,6 @@ import java.util.Map;
// Skip ads based on the start position as required. // Skip ads based on the start position as required.
long[] adGroupTimesUs = adPlaybackState.adGroupTimesUs; long[] adGroupTimesUs = adPlaybackState.adGroupTimesUs;
long contentPositionMs = getContentPeriodPositionMs(checkNotNull(player), timeline, period);
int adGroupForPositionIndex = int adGroupForPositionIndex =
adPlaybackState.getAdGroupIndexForPositionUs( adPlaybackState.getAdGroupIndexForPositionUs(
C.msToUs(contentPositionMs), C.msToUs(contentDurationMs)); C.msToUs(contentPositionMs), C.msToUs(contentDurationMs));
@ -957,7 +965,6 @@ import java.util.Map;
} }
return; return;
} }
checkNotNull(player);
imaAdState = IMA_AD_STATE_NONE; imaAdState = IMA_AD_STATE_NONE;
stopUpdatingAdProgress(); 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.
@ -1155,10 +1162,12 @@ import java.util.Map;
private static long getContentPeriodPositionMs( private static long getContentPeriodPositionMs(
Player player, Timeline timeline, Timeline.Period period) { Player player, Timeline timeline, Timeline.Period period) {
long contentWindowPositionMs = player.getContentPosition(); long contentWindowPositionMs = player.getContentPosition();
return contentWindowPositionMs if (timeline.isEmpty()) {
- (timeline.isEmpty() return contentWindowPositionMs;
? 0 } else {
: timeline.getPeriod(/* periodIndex= */ 0, period).getPositionInWindowMs()); return contentWindowPositionMs
- timeline.getPeriod(player.getCurrentPeriodIndex(), period).getPositionInWindowMs();
}
} }
private static boolean hasMidrollAdGroups(long[] adGroupTimesUs) { private static boolean hasMidrollAdGroups(long[] adGroupTimesUs) {

View File

@ -31,7 +31,6 @@ import androidx.annotation.VisibleForTesting;
import com.google.ads.interactivemedia.v3.api.AdDisplayContainer; import com.google.ads.interactivemedia.v3.api.AdDisplayContainer;
import com.google.ads.interactivemedia.v3.api.AdErrorEvent.AdErrorListener; import com.google.ads.interactivemedia.v3.api.AdErrorEvent.AdErrorListener;
import com.google.ads.interactivemedia.v3.api.AdEvent.AdEventListener; import com.google.ads.interactivemedia.v3.api.AdEvent.AdEventListener;
import com.google.ads.interactivemedia.v3.api.AdsLoader;
import com.google.ads.interactivemedia.v3.api.AdsManager; import com.google.ads.interactivemedia.v3.api.AdsManager;
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;
@ -46,6 +45,7 @@ import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlayerLibraryInfo; import com.google.android.exoplayer2.ExoPlayerLibraryInfo;
import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.source.MediaSourceFactory; import com.google.android.exoplayer2.source.MediaSourceFactory;
import com.google.android.exoplayer2.source.ads.AdsLoader;
import com.google.android.exoplayer2.source.ads.AdsMediaSource; import com.google.android.exoplayer2.source.ads.AdsMediaSource;
import com.google.android.exoplayer2.upstream.DataSpec; import com.google.android.exoplayer2.upstream.DataSpec;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
@ -61,8 +61,7 @@ import java.util.List;
import java.util.Set; import java.util.Set;
/** /**
* {@link com.google.android.exoplayer2.source.ads.AdsLoader} using the IMA SDK. All methods must be * {@link AdsLoader} using the IMA SDK. All methods must be called on the main thread.
* called on the main thread.
* *
* <p>The player instance that will play the loaded ads must be set before playback using {@link * <p>The player instance that will play the loaded ads must be set before playback using {@link
* #setPlayer(Player)}. If the ads loader is no longer required, it must be released by calling * #setPlayer(Player)}. If the ads loader is no longer required, it must be released by calling
@ -83,8 +82,7 @@ import java.util.Set;
* href="https://developers.google.com/interactive-media-ads/docs/sdks/android/client-side/omsdk">IMA * href="https://developers.google.com/interactive-media-ads/docs/sdks/android/client-side/omsdk">IMA
* SDK Open Measurement documentation</a> for more information. * SDK Open Measurement documentation</a> for more information.
*/ */
public final class ImaAdsLoader public final class ImaAdsLoader implements Player.EventListener, AdsLoader {
implements Player.EventListener, com.google.android.exoplayer2.source.ads.AdsLoader {
static { static {
ExoPlayerLibraryInfo.registerModule("goog.exo.ima"); ExoPlayerLibraryInfo.registerModule("goog.exo.ima");
@ -154,8 +152,8 @@ public final class ImaAdsLoader
/** /**
* Sets a listener for ad errors that will be passed to {@link * Sets a listener for ad errors that will be passed to {@link
* AdsLoader#addAdErrorListener(AdErrorListener)} and {@link * com.google.ads.interactivemedia.v3.api.AdsLoader#addAdErrorListener(AdErrorListener)} and
* AdsManager#addAdErrorListener(AdErrorListener)}. * {@link AdsManager#addAdErrorListener(AdErrorListener)}.
* *
* @param adErrorListener The ad error listener. * @param adErrorListener The ad error listener.
* @return This builder, for convenience. * @return This builder, for convenience.
@ -384,11 +382,11 @@ public final class ImaAdsLoader
} }
/** /**
* Returns the underlying {@link AdsLoader} wrapped by this instance, or {@code null} if ads have * Returns the underlying {@link com.google.ads.interactivemedia.v3.api.AdsLoader} wrapped by this
* not been requested yet. * instance, or {@code null} if ads have not been requested yet.
*/ */
@Nullable @Nullable
public AdsLoader getAdsLoader() { public com.google.ads.interactivemedia.v3.api.AdsLoader getAdsLoader() {
return adTagLoader != null ? adTagLoader.getAdsLoader() : null; return adTagLoader != null ? adTagLoader.getAdsLoader() : null;
} }
@ -400,8 +398,8 @@ public final class ImaAdsLoader
* AdDisplayContainer#registerFriendlyObstruction(FriendlyObstruction)} will be unregistered * AdDisplayContainer#registerFriendlyObstruction(FriendlyObstruction)} will be unregistered
* automatically when the media source detaches from this instance. It is therefore necessary to * automatically when the media source detaches from this instance. It is therefore necessary to
* re-register views each time the ads loader is reused. Alternatively, provide overlay views via * re-register views each time the ads loader is reused. Alternatively, provide overlay views via
* the {@link com.google.android.exoplayer2.source.ads.AdsLoader.AdViewProvider} when creating the * the {@link AdViewProvider} when creating the media source to benefit from automatic
* media source to benefit from automatic registration. * registration.
*/ */
@Nullable @Nullable
public AdDisplayContainer getAdDisplayContainer() { public AdDisplayContainer getAdDisplayContainer() {
@ -448,7 +446,7 @@ public final class ImaAdsLoader
} }
} }
// com.google.android.exoplayer2.source.ads.AdsLoader implementation. // AdsLoader implementation.
@Override @Override
public void setPlayer(@Nullable Player player) { public void setPlayer(@Nullable Player player) {
@ -576,7 +574,7 @@ public final class ImaAdsLoader
} }
@Override @Override
public AdsLoader createAdsLoader( public com.google.ads.interactivemedia.v3.api.AdsLoader createAdsLoader(
Context context, ImaSdkSettings imaSdkSettings, AdDisplayContainer adDisplayContainer) { Context context, ImaSdkSettings imaSdkSettings, AdDisplayContainer adDisplayContainer) {
return ImaSdkFactory.getInstance() return ImaSdkFactory.getInstance()
.createAdsLoader(context, imaSdkSettings, adDisplayContainer); .createAdsLoader(context, imaSdkSettings, adDisplayContainer);