Skip to content

Commit

Permalink
Expose the ads identifier in the Timeline period
Browse files Browse the repository at this point in the history
Issue: #3750
PiperOrigin-RevId: 341021084
  • Loading branch information
andrewlewis committed Nov 6, 2020
1 parent 0c301fe commit 764e5e8
Show file tree
Hide file tree
Showing 12 changed files with 143 additions and 81 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@
private final ImaUtil.ImaFactory imaFactory;
private final List<String> supportedMimeTypes;
private final DataSpec adTagDataSpec;
private final Object adsId;
private final Timeline.Period period;
private final Handler handler;
private final ComponentListener componentListener;
Expand All @@ -146,7 +147,6 @@

@Nullable private AdsManager adsManager;
private boolean isAdsManagerInitialized;
private boolean hasAdPlaybackState;
@Nullable private AdLoadException pendingAdLoadError;
private Timeline timeline;
private long contentDurationMs;
Expand Down Expand Up @@ -214,6 +214,7 @@ public AdTagLoader(
ImaUtil.ImaFactory imaFactory,
List<String> supportedMimeTypes,
DataSpec adTagDataSpec,
Object adsId,
@Nullable ViewGroup adViewGroup) {
this.configuration = configuration;
this.imaFactory = imaFactory;
Expand All @@ -228,6 +229,7 @@ public AdTagLoader(
imaSdkSettings.setPlayerVersion(IMA_SDK_SETTINGS_PLAYER_VERSION);
this.supportedMimeTypes = supportedMimeTypes;
this.adTagDataSpec = adTagDataSpec;
this.adsId = adsId;
period = new Timeline.Period();
handler = Util.createHandler(getImaLooper(), /* callback= */ null);
componentListener = new ComponentListener();
Expand Down Expand Up @@ -286,14 +288,16 @@ public void start(Player player, AdViewProvider adViewProvider, EventListener ev
lastAdProgress = VideoProgressUpdate.VIDEO_TIME_NOT_READY;
lastContentProgress = VideoProgressUpdate.VIDEO_TIME_NOT_READY;
maybeNotifyPendingAdLoadError();
if (hasAdPlaybackState) {
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 = ImaUtil.getInitialAdPlaybackStateForCuePoints(adsManager.getAdCuePoints());
adPlaybackState =
new AdPlaybackState(
adsId, ImaUtil.getAdGroupTimesUsForCuePoints(adsManager.getAdCuePoints()));
updateAdPlaybackState();
}
if (adDisplayContainer != null) {
Expand Down Expand Up @@ -348,8 +352,7 @@ public void release() {
stopUpdatingAdProgress();
imaAdInfo = null;
pendingAdLoadError = null;
adPlaybackState = AdPlaybackState.NONE;
hasAdPlaybackState = true;
adPlaybackState = new AdPlaybackState(adsId);
updateAdPlaybackState();
}

Expand Down Expand Up @@ -496,7 +499,7 @@ private AdsLoader requestAds(
try {
request = ImaUtil.getAdsRequestForAdTagDataSpec(imaFactory, adTagDataSpec);
} catch (IOException e) {
hasAdPlaybackState = true;
adPlaybackState = new AdPlaybackState(adsId);
updateAdPlaybackState();
pendingAdLoadError = AdLoadException.createForAllAds(e);
maybeNotifyPendingAdLoadError();
Expand Down Expand Up @@ -1215,8 +1218,8 @@ public void onAdsManagerLoaded(AdsManagerLoadedEvent adsManagerLoadedEvent) {
// If a player is attached already, start playback immediately.
try {
adPlaybackState =
ImaUtil.getInitialAdPlaybackStateForCuePoints(adsManager.getAdCuePoints());
hasAdPlaybackState = true;
new AdPlaybackState(
adsId, ImaUtil.getAdGroupTimesUsForCuePoints(adsManager.getAdCuePoints()));
updateAdPlaybackState();
} catch (RuntimeException e) {
maybeNotifyInternalError("onAdsManagerLoaded", e);
Expand Down Expand Up @@ -1276,8 +1279,7 @@ public void onAdError(AdErrorEvent adErrorEvent) {
if (adsManager == null) {
// No ads were loaded, so allow playback to start without any ads.
pendingAdRequestContext = null;
adPlaybackState = AdPlaybackState.NONE;
hasAdPlaybackState = true;
adPlaybackState = new AdPlaybackState(adsId);
updateAdPlaybackState();
} else if (ImaUtil.isAdGroupLoadError(error)) {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -417,14 +417,21 @@ public AdDisplayContainer getAdDisplayContainer() {
*
* @param adTagDataSpec The data specification of the ad tag to load. See class javadoc for
* information about compatible ad tag formats.
* @param adsId A opaque identifier for the ad playback state across start/stop calls.
* @param adViewGroup A {@link ViewGroup} on top of the player that will show any ad UI, or {@code
* null} if playing audio-only ads.
*/
public void requestAds(DataSpec adTagDataSpec, @Nullable ViewGroup adViewGroup) {
public void requestAds(DataSpec adTagDataSpec, Object adsId, @Nullable ViewGroup adViewGroup) {
if (adTagLoader == null) {
adTagLoader =
new AdTagLoader(
context, configuration, imaFactory, supportedMimeTypes, adTagDataSpec, adViewGroup);
context,
configuration,
imaFactory,
supportedMimeTypes,
adTagDataSpec,
adsId,
adViewGroup);
}
}

Expand Down Expand Up @@ -488,7 +495,7 @@ public void start(
return;
}
if (adTagLoader == null) {
requestAds(adTagDataSpec, adViewProvider.getAdViewGroup());
requestAds(adTagDataSpec, adsId, adViewProvider.getAdViewGroup());
}
checkNotNull(adTagLoader).start(player, adViewProvider, eventListener);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
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.source.ads.AdPlaybackState;
import com.google.android.exoplayer2.source.ads.AdsLoader.OverlayInfo;
import com.google.android.exoplayer2.upstream.DataSchemeDataSource;
import com.google.android.exoplayer2.upstream.DataSpec;
Expand Down Expand Up @@ -154,15 +153,14 @@ public static FriendlyObstructionPurpose getFriendlyObstructionPurpose(
}

/**
* Returns an initial {@link AdPlaybackState} with ad groups at the provided {@code cuePoints}.
* Returns the microsecond ad group timestamps corresponding to the specified cue points.
*
* @param cuePoints The cue points of the ads in seconds.
* @return The {@link AdPlaybackState}.
* @param cuePoints The cue points of the ads in seconds, provided by the IMA SDK.
* @return The corresponding microsecond ad group timestamps.
*/
public static AdPlaybackState getInitialAdPlaybackStateForCuePoints(List<Float> cuePoints) {
public static long[] getAdGroupTimesUsForCuePoints(List<Float> cuePoints) {
if (cuePoints.isEmpty()) {
// If no cue points are specified, there is a preroll ad.
return new AdPlaybackState(/* adGroupTimesUs...= */ 0);
return new long[] {0L};
}

int count = cuePoints.size();
Expand All @@ -178,7 +176,7 @@ public static AdPlaybackState getInitialAdPlaybackStateForCuePoints(List<Float>
}
// Cue points may be out of order, so sort them.
Arrays.sort(adGroupTimesUs, 0, adGroupIndex);
return new AdPlaybackState(adGroupTimesUs);
return adGroupTimesUs;
}

/** Returns an {@link AdsRequest} based on the specified ad tag {@link DataSpec}. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package com.google.android.exoplayer2.ext.ima;

import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
import static com.google.android.exoplayer2.ext.ima.ImaUtil.getAdGroupTimesUsForCuePoints;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyDouble;
Expand Down Expand Up @@ -226,7 +227,7 @@ public void start_updatesAdPlaybackState() {

assertThat(adsLoaderListener.adPlaybackState)
.isEqualTo(
new AdPlaybackState(/* adGroupTimesUs...= */ 0)
new AdPlaybackState(TEST_ADS_ID, /* adGroupTimesUs...= */ 0)
.withContentDurationUs(CONTENT_PERIOD_DURATION_US));
}

Expand All @@ -242,7 +243,7 @@ public void startAfterRelease() {
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, adViewGroup);
imaAdsLoader.requestAds(TEST_DATA_SPEC, TEST_ADS_ID, adViewGroup);
imaAdsLoader.release();
imaAdsLoader.start(
adsMediaSource, TEST_DATA_SPEC, TEST_ADS_ID, adViewProvider, adsLoaderListener);
Expand All @@ -253,7 +254,7 @@ public void startAndCallbacksAfterRelease() {
// Note: we can't currently call getContentProgress/getAdProgress as a VerifyError is thrown
// when using Robolectric and accessing VideoProgressUpdate.VIDEO_TIME_NOT_READY, due to the IMA
// SDK being proguarded.
imaAdsLoader.requestAds(TEST_DATA_SPEC, adViewGroup);
imaAdsLoader.requestAds(TEST_DATA_SPEC, TEST_ADS_ID, adViewGroup);
adEventListener.onAdEvent(getAdEvent(AdEventType.LOADED, mockPrerollSingleAd));
videoAdPlayer.loadAd(TEST_AD_MEDIA_INFO, mockAdPodInfo);
adEventListener.onAdEvent(getAdEvent(AdEventType.CONTENT_PAUSE_REQUESTED, mockPrerollSingleAd));
Expand Down Expand Up @@ -300,7 +301,7 @@ public void playback_withPrerollAd_marksAdAsPlayed() {
// Verify that the preroll ad has been marked as played.
assertThat(adsLoaderListener.adPlaybackState)
.isEqualTo(
new AdPlaybackState(/* adGroupTimesUs...= */ 0)
new AdPlaybackState(TEST_ADS_ID, /* adGroupTimesUs...= */ 0)
.withContentDurationUs(CONTENT_PERIOD_DURATION_US)
.withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1)
.withAdUri(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0, TEST_URI)
Expand All @@ -324,7 +325,7 @@ public void playback_withMidrollFetchError_marksAdAsInErrorState() {

assertThat(adsLoaderListener.adPlaybackState)
.isEqualTo(
new AdPlaybackState(/* adGroupTimesUs...= */ 20_500_000)
new AdPlaybackState(TEST_ADS_ID, /* adGroupTimesUs...= */ 20_500_000)
.withContentDurationUs(CONTENT_PERIOD_DURATION_US)
.withAdDurationsUs(new long[][] {{TEST_AD_DURATION_US}})
.withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1)
Expand Down Expand Up @@ -372,7 +373,7 @@ public void playback_withPostrollFetchError_marksAdAsInErrorState() {

assertThat(adsLoaderListener.adPlaybackState)
.isEqualTo(
new AdPlaybackState(/* adGroupTimesUs...= */ C.TIME_END_OF_SOURCE)
new AdPlaybackState(TEST_ADS_ID, /* adGroupTimesUs...= */ C.TIME_END_OF_SOURCE)
.withContentDurationUs(CONTENT_PERIOD_DURATION_US)
.withAdDurationsUs(new long[][] {{TEST_AD_DURATION_US}})
.withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1)
Expand Down Expand Up @@ -400,7 +401,7 @@ public void playback_withAdNotPreloadingBeforeTimeout_hasNoError() {

assertThat(adsLoaderListener.adPlaybackState)
.isEqualTo(
ImaUtil.getInitialAdPlaybackStateForCuePoints(cuePoints)
new AdPlaybackState(TEST_ADS_ID, getAdGroupTimesUsForCuePoints(cuePoints))
.withContentDurationUs(CONTENT_PERIOD_DURATION_US));
}

Expand All @@ -425,7 +426,7 @@ public void playback_withAdNotPreloadingAfterTimeout_hasErrorAdGroup() {

assertThat(adsLoaderListener.adPlaybackState)
.isEqualTo(
ImaUtil.getInitialAdPlaybackStateForCuePoints(cuePoints)
new AdPlaybackState(TEST_ADS_ID, getAdGroupTimesUsForCuePoints(cuePoints))
.withContentDurationUs(CONTENT_PERIOD_DURATION_US)
.withAdDurationsUs(new long[][] {{TEST_AD_DURATION_US}})
.withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1)
Expand All @@ -448,7 +449,7 @@ public void resumePlaybackBeforeMidroll_playsPreroll() {
verify(mockAdsRenderingSettings, never()).setPlayAdsAfterTime(anyDouble());
assertThat(adsLoaderListener.adPlaybackState)
.isEqualTo(
ImaUtil.getInitialAdPlaybackStateForCuePoints(cuePoints)
new AdPlaybackState(TEST_ADS_ID, getAdGroupTimesUsForCuePoints(cuePoints))
.withContentDurationUs(CONTENT_PERIOD_DURATION_US));
}

Expand All @@ -473,7 +474,7 @@ public void resumePlaybackAtMidroll_skipsPreroll() {
.of(expectedPlayAdsAfterTimeUs / C.MICROS_PER_SECOND);
assertThat(adsLoaderListener.adPlaybackState)
.isEqualTo(
ImaUtil.getInitialAdPlaybackStateForCuePoints(cuePoints)
new AdPlaybackState(TEST_ADS_ID, getAdGroupTimesUsForCuePoints(cuePoints))
.withContentDurationUs(CONTENT_PERIOD_DURATION_US)
.withSkippedAdGroup(/* adGroupIndex= */ 0));
}
Expand All @@ -499,7 +500,7 @@ public void resumePlaybackAfterMidroll_skipsPreroll() {
.of(expectedPlayAdsAfterTimeUs / C.MICROS_PER_SECOND);
assertThat(adsLoaderListener.adPlaybackState)
.isEqualTo(
ImaUtil.getInitialAdPlaybackStateForCuePoints(cuePoints)
new AdPlaybackState(TEST_ADS_ID, getAdGroupTimesUsForCuePoints(cuePoints))
.withContentDurationUs(CONTENT_PERIOD_DURATION_US)
.withSkippedAdGroup(/* adGroupIndex= */ 0));
}
Expand Down Expand Up @@ -527,7 +528,7 @@ public void resumePlaybackBeforeSecondMidroll_playsFirstMidroll() {
verify(mockAdsRenderingSettings, never()).setPlayAdsAfterTime(anyDouble());
assertThat(adsLoaderListener.adPlaybackState)
.isEqualTo(
ImaUtil.getInitialAdPlaybackStateForCuePoints(cuePoints)
new AdPlaybackState(TEST_ADS_ID, getAdGroupTimesUsForCuePoints(cuePoints))
.withContentDurationUs(CONTENT_PERIOD_DURATION_US));
}

Expand Down Expand Up @@ -559,7 +560,7 @@ public void resumePlaybackAtSecondMidroll_skipsFirstMidroll() {
.of(expectedPlayAdsAfterTimeUs / C.MICROS_PER_SECOND);
assertThat(adsLoaderListener.adPlaybackState)
.isEqualTo(
ImaUtil.getInitialAdPlaybackStateForCuePoints(cuePoints)
new AdPlaybackState(TEST_ADS_ID, getAdGroupTimesUsForCuePoints(cuePoints))
.withContentDurationUs(CONTENT_PERIOD_DURATION_US)
.withSkippedAdGroup(/* adGroupIndex= */ 0));
}
Expand Down Expand Up @@ -594,7 +595,7 @@ public void resumePlaybackBeforeMidroll_withoutPlayAdBeforeStartPosition_skipsPr
.of(expectedPlayAdsAfterTimeUs / C.MICROS_PER_SECOND);
assertThat(adsLoaderListener.adPlaybackState)
.isEqualTo(
ImaUtil.getInitialAdPlaybackStateForCuePoints(cuePoints)
new AdPlaybackState(TEST_ADS_ID, getAdGroupTimesUsForCuePoints(cuePoints))
.withSkippedAdGroup(/* adGroupIndex= */ 0)
.withContentDurationUs(CONTENT_PERIOD_DURATION_US));
}
Expand Down Expand Up @@ -629,7 +630,7 @@ public void resumePlaybackAtMidroll_withoutPlayAdBeforeStartPosition_skipsPrerol
.of(expectedPlayAdsAfterTimeUs / C.MICROS_PER_SECOND);
assertThat(adsLoaderListener.adPlaybackState)
.isEqualTo(
ImaUtil.getInitialAdPlaybackStateForCuePoints(cuePoints)
new AdPlaybackState(TEST_ADS_ID, getAdGroupTimesUsForCuePoints(cuePoints))
.withContentDurationUs(CONTENT_PERIOD_DURATION_US)
.withSkippedAdGroup(/* adGroupIndex= */ 0));
}
Expand Down Expand Up @@ -659,7 +660,7 @@ public void resumePlaybackAfterMidroll_withoutPlayAdBeforeStartPosition_skipsMid
verify(mockAdsManager).destroy();
assertThat(adsLoaderListener.adPlaybackState)
.isEqualTo(
ImaUtil.getInitialAdPlaybackStateForCuePoints(cuePoints)
new AdPlaybackState(TEST_ADS_ID, getAdGroupTimesUsForCuePoints(cuePoints))
.withContentDurationUs(CONTENT_PERIOD_DURATION_US)
.withSkippedAdGroup(/* adGroupIndex= */ 0)
.withSkippedAdGroup(/* adGroupIndex= */ 1));
Expand Down Expand Up @@ -703,7 +704,7 @@ public void resumePlaybackAfterMidroll_withoutPlayAdBeforeStartPosition_skipsMid
.of(expectedPlayAdsAfterTimeUs / C.MICROS_PER_SECOND);
assertThat(adsLoaderListener.adPlaybackState)
.isEqualTo(
ImaUtil.getInitialAdPlaybackStateForCuePoints(cuePoints)
new AdPlaybackState(TEST_ADS_ID, getAdGroupTimesUsForCuePoints(cuePoints))
.withSkippedAdGroup(/* adGroupIndex= */ 0)
.withContentDurationUs(CONTENT_PERIOD_DURATION_US));
}
Expand Down Expand Up @@ -745,7 +746,7 @@ public void resumePlaybackAtSecondMidroll_withoutPlayAdBeforeStartPosition_skips
.of(expectedPlayAdsAfterTimeUs / C.MICROS_PER_SECOND);
assertThat(adsLoaderListener.adPlaybackState)
.isEqualTo(
ImaUtil.getInitialAdPlaybackStateForCuePoints(cuePoints)
new AdPlaybackState(TEST_ADS_ID, getAdGroupTimesUsForCuePoints(cuePoints))
.withContentDurationUs(CONTENT_PERIOD_DURATION_US)
.withSkippedAdGroup(/* adGroupIndex= */ 0));
}
Expand Down Expand Up @@ -835,7 +836,7 @@ 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, adViewGroup);
imaAdsLoader.requestAds(TEST_DATA_SPEC, TEST_ADS_ID, adViewGroup);
imaAdsLoader.stop(adsMediaSource);

InOrder inOrder = inOrder(mockAdDisplayContainer);
Expand Down Expand Up @@ -887,7 +888,7 @@ public double getTimeOffset() {

assertThat(adsLoaderListener.adPlaybackState)
.isEqualTo(
ImaUtil.getInitialAdPlaybackStateForCuePoints(cuePoints)
new AdPlaybackState(TEST_ADS_ID, getAdGroupTimesUsForCuePoints(cuePoints))
.withContentDurationUs(CONTENT_PERIOD_DURATION_US)
.withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1)
.withAdUri(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0, TEST_URI)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -519,9 +519,13 @@ public long getPositionInWindowUs() {
return positionInWindowUs;
}

/**
* Returns the number of ad groups in the period.
*/
/** Returns the opaque identifier for ads played with this period, or {@code null} if unset. */
@Nullable
public Object getAdsId() {
return adPlaybackState.adsId;
}

/** Returns the number of ad groups in the period. */
public int getAdGroupCount() {
return adPlaybackState.adGroupCount;
}
Expand Down
Loading

0 comments on commit 764e5e8

Please sign in to comment.