Skip to content

Commit

Permalink
Clean up AdTagLoader and ImaAdsLoader
Browse files Browse the repository at this point in the history
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
  • Loading branch information
andrewlewis committed Nov 6, 2020
1 parent 764e5e8 commit ae4cf9f
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 56 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,8 @@
private long contentDurationMs;
private AdPlaybackState adPlaybackState;

private boolean released;

// Fields tracking IMA's state.

/** Whether IMA has sent an ad event to pause content since the last resume content event. */
Expand Down Expand Up @@ -300,14 +302,12 @@ public void start(Player player, AdViewProvider adViewProvider, EventListener ev
adsId, ImaUtil.getAdGroupTimesUsForCuePoints(adsManager.getAdCuePoints()));
updateAdPlaybackState();
}
if (adDisplayContainer != null) {
for (OverlayInfo overlayInfo : adViewProvider.getAdOverlayInfos()) {
adDisplayContainer.registerFriendlyObstruction(
imaFactory.createFriendlyObstruction(
overlayInfo.view,
ImaUtil.getFriendlyObstructionPurpose(overlayInfo.purpose),
overlayInfo.reasonDetail));
}
for (OverlayInfo overlayInfo : adViewProvider.getAdOverlayInfos()) {
adDisplayContainer.registerFriendlyObstruction(
imaFactory.createFriendlyObstruction(
overlayInfo.view,
ImaUtil.getFriendlyObstructionPurpose(overlayInfo.purpose),
overlayInfo.reasonDetail));
}
}

Expand All @@ -326,26 +326,26 @@ public void stop() {
lastVolumePercent = getPlayerVolumePercent();
lastAdProgress = getAdVideoProgressUpdate();
lastContentProgress = getContentVideoProgressUpdate();
if (adDisplayContainer != null) {
adDisplayContainer.unregisterAllFriendlyObstructions();
}
adDisplayContainer.unregisterAllFriendlyObstructions();
player.removeListener(this);
this.player = null;
eventListener = null;
}

/** Releases all resources used by the ad tag loader. */
public void release() {
if (released) {
return;
}
released = true;
pendingAdRequestContext = null;
destroyAdsManager();
if (adsLoader != null) {
adsLoader.removeAdsLoadedListener(componentListener);
adsLoader.removeAdErrorListener(componentListener);
if (configuration.applicationAdErrorListener != null) {
adsLoader.removeAdErrorListener(configuration.applicationAdErrorListener);
}
adsLoader.release();
adsLoader.removeAdsLoadedListener(componentListener);
adsLoader.removeAdErrorListener(componentListener);
if (configuration.applicationAdErrorListener != null) {
adsLoader.removeAdErrorListener(configuration.applicationAdErrorListener);
}
adsLoader.release();
imaPausedContent = false;
imaAdState = IMA_AD_STATE_NONE;
imaAdMediaInfo = null;
Expand Down Expand Up @@ -394,27 +394,15 @@ public void onTimelineChanged(Timeline timeline, @Player.TimelineChangeReason in
}
checkArgument(timeline.getPeriodCount() == 1);
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);
if (contentDurationUs != C.TIME_UNSET) {
if (contentDurationUs != adPlaybackState.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();
}
long contentPositionMs = getContentPeriodPositionMs(player, timeline, period);
maybeInitializeAdsManager(contentPositionMs, contentDurationMs);
handleTimelineOrPositionChanged();
}

Expand Down Expand Up @@ -515,12 +503,33 @@ private AdsLoader requestAds(
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
* {@code null} if no ads should play.
*/
@Nullable
private AdsRenderingSettings setupAdsRendering() {
private AdsRenderingSettings setupAdsRendering(long contentPositionMs, long contentDurationMs) {
AdsRenderingSettings adsRenderingSettings = imaFactory.createAdsRenderingSettings();
adsRenderingSettings.setEnablePreloading(true);
adsRenderingSettings.setMimeTypes(
Expand All @@ -541,7 +550,6 @@ private AdsRenderingSettings setupAdsRendering() {

// Skip ads based on the start position as required.
long[] adGroupTimesUs = adPlaybackState.adGroupTimesUs;
long contentPositionMs = getContentPeriodPositionMs(checkNotNull(player), timeline, period);
int adGroupForPositionIndex =
adPlaybackState.getAdGroupIndexForPositionUs(
C.msToUs(contentPositionMs), C.msToUs(contentDurationMs));
Expand Down Expand Up @@ -957,7 +965,6 @@ private void stopAdInternal(AdMediaInfo adMediaInfo) {
}
return;
}
checkNotNull(player);
imaAdState = IMA_AD_STATE_NONE;
stopUpdatingAdProgress();
// TODO: Handle the skipped event so the ad can be marked as skipped rather than played.
Expand Down Expand Up @@ -1155,10 +1162,12 @@ private String getAdMediaInfoString(AdMediaInfo adMediaInfo) {
private static long getContentPeriodPositionMs(
Player player, Timeline timeline, Timeline.Period period) {
long contentWindowPositionMs = player.getContentPosition();
return contentWindowPositionMs
- (timeline.isEmpty()
? 0
: timeline.getPeriod(/* periodIndex= */ 0, period).getPositionInWindowMs());
if (timeline.isEmpty()) {
return contentWindowPositionMs;
} else {
return contentWindowPositionMs
- timeline.getPeriod(player.getCurrentPeriodIndex(), period).getPositionInWindowMs();
}
}

private static boolean hasMidrollAdGroups(long[] adGroupTimesUs) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
import com.google.ads.interactivemedia.v3.api.AdDisplayContainer;
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.AdsLoader;
import com.google.ads.interactivemedia.v3.api.AdsManager;
import com.google.ads.interactivemedia.v3.api.AdsRenderingSettings;
import com.google.ads.interactivemedia.v3.api.AdsRequest;
Expand All @@ -46,6 +45,7 @@
import com.google.android.exoplayer2.ExoPlayerLibraryInfo;
import com.google.android.exoplayer2.Player;
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.upstream.DataSpec;
import com.google.android.exoplayer2.util.MimeTypes;
Expand All @@ -61,8 +61,7 @@
import java.util.Set;

/**
* {@link com.google.android.exoplayer2.source.ads.AdsLoader} using the IMA SDK. All methods must be
* called on the main thread.
* {@link AdsLoader} using the IMA SDK. All methods must be called on the main thread.
*
* <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
Expand All @@ -83,8 +82,7 @@
* href="https://developers.google.com/interactive-media-ads/docs/sdks/android/client-side/omsdk">IMA
* SDK Open Measurement documentation</a> for more information.
*/
public final class ImaAdsLoader
implements Player.EventListener, com.google.android.exoplayer2.source.ads.AdsLoader {
public final class ImaAdsLoader implements Player.EventListener, AdsLoader {

static {
ExoPlayerLibraryInfo.registerModule("goog.exo.ima");
Expand Down Expand Up @@ -154,8 +152,8 @@ public Builder setImaSdkSettings(ImaSdkSettings imaSdkSettings) {

/**
* Sets a listener for ad errors that will be passed to {@link
* AdsLoader#addAdErrorListener(AdErrorListener)} and {@link
* AdsManager#addAdErrorListener(AdErrorListener)}.
* com.google.ads.interactivemedia.v3.api.AdsLoader#addAdErrorListener(AdErrorListener)} and
* {@link AdsManager#addAdErrorListener(AdErrorListener)}.
*
* @param adErrorListener The ad error listener.
* @return This builder, for convenience.
Expand Down Expand Up @@ -384,11 +382,11 @@ private ImaAdsLoader(
}

/**
* Returns the underlying {@link AdsLoader} wrapped by this instance, or {@code null} if ads have
* not been requested yet.
* Returns the underlying {@link com.google.ads.interactivemedia.v3.api.AdsLoader} wrapped by this
* instance, or {@code null} if ads have not been requested yet.
*/
@Nullable
public AdsLoader getAdsLoader() {
public com.google.ads.interactivemedia.v3.api.AdsLoader getAdsLoader() {
return adTagLoader != null ? adTagLoader.getAdsLoader() : null;
}

Expand All @@ -400,8 +398,8 @@ public AdsLoader getAdsLoader() {
* AdDisplayContainer#registerFriendlyObstruction(FriendlyObstruction)} will be unregistered
* 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
* the {@link com.google.android.exoplayer2.source.ads.AdsLoader.AdViewProvider} when creating the
* media source to benefit from automatic registration.
* the {@link AdViewProvider} when creating the media source to benefit from automatic
* registration.
*/
@Nullable
public AdDisplayContainer getAdDisplayContainer() {
Expand Down Expand Up @@ -448,7 +446,7 @@ public void skipAd() {
}
}

// com.google.android.exoplayer2.source.ads.AdsLoader implementation.
// AdsLoader implementation.

@Override
public void setPlayer(@Nullable Player player) {
Expand Down Expand Up @@ -576,7 +574,7 @@ public AdsRequest createAdsRequest() {
}

@Override
public AdsLoader createAdsLoader(
public com.google.ads.interactivemedia.v3.api.AdsLoader createAdsLoader(
Context context, ImaSdkSettings imaSdkSettings, AdDisplayContainer adDisplayContainer) {
return ImaSdkFactory.getInstance()
.createAdsLoader(context, imaSdkSettings, adDisplayContainer);
Expand Down

0 comments on commit ae4cf9f

Please sign in to comment.