Commit c35787a0 by andrewlewis Committed by Andrew Lewis

Add ImaUtil for IMA extension utilities

PiperOrigin-RevId: 334567234
parent 8728706c
......@@ -33,7 +33,6 @@ import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.google.ads.interactivemedia.v3.api.AdDisplayContainer;
import com.google.ads.interactivemedia.v3.api.AdError;
import com.google.ads.interactivemedia.v3.api.AdError.AdErrorCode;
import com.google.ads.interactivemedia.v3.api.AdErrorEvent;
import com.google.ads.interactivemedia.v3.api.AdErrorEvent.AdErrorListener;
import com.google.ads.interactivemedia.v3.api.AdEvent;
......@@ -134,7 +133,7 @@ public final class ImaAdsLoader
private int mediaBitrate;
private boolean focusSkipButtonWhenAvailable;
private boolean playAdBeforeStartPosition;
private ImaFactory imaFactory;
private ImaUtil.ImaFactory imaFactory;
/**
* Creates a new builder for {@link ImaAdsLoader}.
......@@ -303,7 +302,7 @@ public final class ImaAdsLoader
}
@VisibleForTesting
/* package */ Builder setImaFactory(ImaFactory imaFactory) {
/* package */ Builder setImaFactory(ImaUtil.ImaFactory imaFactory) {
this.imaFactory = checkNotNull(imaFactory);
return this;
}
......@@ -397,7 +396,7 @@ public final class ImaAdsLoader
@Nullable private final Collection<CompanionAdSlot> companionAdSlots;
@Nullable private final AdErrorListener adErrorListener;
@Nullable private final AdEventListener adEventListener;
private final ImaFactory imaFactory;
private final ImaUtil.ImaFactory imaFactory;
private final ImaSdkSettings imaSdkSettings;
private final Timeline.Period period;
private final Handler handler;
......@@ -677,7 +676,7 @@ public final class ImaAdsLoader
adsManager.resume();
}
} else if (adsManager != null) {
adPlaybackState = AdPlaybackStateFactory.fromCuePoints(adsManager.getAdCuePoints());
adPlaybackState = ImaUtil.getInitialAdPlaybackStateForCuePoints(adsManager.getAdCuePoints());
updateAdPlaybackState();
} else {
// Ads haven't loaded yet, so request them.
......@@ -688,7 +687,7 @@ public final class ImaAdsLoader
adDisplayContainer.registerFriendlyObstruction(
imaFactory.createFriendlyObstruction(
overlayInfo.view,
getFriendlyObstructionPurpose(overlayInfo.purpose),
ImaUtil.getFriendlyObstructionPurpose(overlayInfo.purpose),
overlayInfo.reasonDetail));
}
}
......@@ -1481,21 +1480,6 @@ public final class ImaAdsLoader
return "AdMediaInfo[" + adMediaInfo.getUrl() + (adInfo != null ? ", " + adInfo : "") + "]";
}
private static FriendlyObstructionPurpose getFriendlyObstructionPurpose(
@OverlayInfo.Purpose int purpose) {
switch (purpose) {
case OverlayInfo.PURPOSE_CONTROLS:
return FriendlyObstructionPurpose.VIDEO_CONTROLS;
case OverlayInfo.PURPOSE_CLOSE_AD:
return FriendlyObstructionPurpose.CLOSE_AD;
case OverlayInfo.PURPOSE_NOT_VISIBLE:
return FriendlyObstructionPurpose.NOT_VISIBLE;
case OverlayInfo.PURPOSE_OTHER:
default:
return FriendlyObstructionPurpose.OTHER;
}
}
private static DataSpec getAdsDataSpec(@Nullable Uri adTagUri) {
return new DataSpec(adTagUri != null ? adTagUri : Uri.EMPTY);
}
......@@ -1509,13 +1493,6 @@ public final class ImaAdsLoader
: timeline.getPeriod(/* periodIndex= */ 0, period).getPositionInWindowMs());
}
private static boolean isAdGroupLoadError(AdError adError) {
// TODO: Find out what other errors need to be handled (if any), and whether each one relates to
// a single ad, ad group or the whole timeline.
return adError.getErrorCode() == AdErrorCode.VAST_LINEAR_ASSET_MISMATCH
|| adError.getErrorCode() == AdErrorCode.UNKNOWN_ERROR;
}
private static Looper getImaLooper() {
// IMA SDK callbacks occur on the main thread. This method can be used to check that the player
// is using the same looper, to ensure all interaction with this class is on the main thread.
......@@ -1549,38 +1526,6 @@ public final class ImaAdsLoader
}
}
/** Factory for objects provided by the IMA SDK. */
@VisibleForTesting
/* package */ interface ImaFactory {
/** Creates {@link ImaSdkSettings} for configuring the IMA SDK. */
ImaSdkSettings createImaSdkSettings();
/**
* Creates {@link AdsRenderingSettings} for giving the {@link AdsManager} parameters that
* control rendering of ads.
*/
AdsRenderingSettings createAdsRenderingSettings();
/**
* Creates an {@link AdDisplayContainer} to hold the player for video ads, a container for
* non-linear ads, and slots for companion ads.
*/
AdDisplayContainer createAdDisplayContainer(ViewGroup container, VideoAdPlayer player);
/** Creates an {@link AdDisplayContainer} to hold the player for audio ads. */
AdDisplayContainer createAudioAdDisplayContainer(Context context, VideoAdPlayer player);
/**
* Creates a {@link FriendlyObstruction} to describe an obstruction considered "friendly" for
* viewability measurement purposes.
*/
FriendlyObstruction createFriendlyObstruction(
View view,
FriendlyObstructionPurpose friendlyObstructionPurpose,
@Nullable String reasonDetail);
/** Creates an {@link AdsRequest} to contain the data used to request ads. */
AdsRequest createAdsRequest();
/** Creates an {@link AdsLoader} for requesting ads using the specified settings. */
AdsLoader createAdsLoader(
Context context, ImaSdkSettings imaSdkSettings, AdDisplayContainer adDisplayContainer);
}
private final class ComponentListener
implements AdsLoadedListener,
ContentProgressProvider,
......@@ -1610,7 +1555,8 @@ public final class ImaAdsLoader
if (player != null) {
// If a player is attached already, start playback immediately.
try {
adPlaybackState = AdPlaybackStateFactory.fromCuePoints(adsManager.getAdCuePoints());
adPlaybackState =
ImaUtil.getInitialAdPlaybackStateForCuePoints(adsManager.getAdCuePoints());
hasAdPlaybackState = true;
updateAdPlaybackState();
} catch (RuntimeException e) {
......@@ -1680,7 +1626,7 @@ public final class ImaAdsLoader
adPlaybackState = AdPlaybackState.NONE;
hasAdPlaybackState = true;
updateAdPlaybackState();
} else if (isAdGroupLoadError(error)) {
} else if (ImaUtil.isAdGroupLoadError(error)) {
try {
handleAdGroupLoadError(error);
} catch (RuntimeException e) {
......@@ -1795,8 +1741,11 @@ public final class ImaAdsLoader
}
}
/** Default {@link ImaFactory} for non-test usage, which delegates to {@link ImaSdkFactory}. */
private static final class DefaultImaFactory implements ImaFactory {
/**
* Default {@link ImaUtil.ImaFactory} for non-test usage, which delegates to {@link
* ImaSdkFactory}.
*/
private static final class DefaultImaFactory implements ImaUtil.ImaFactory {
@Override
public ImaSdkSettings createImaSdkSettings() {
return ImaSdkFactory.getInstance().createImaSdkSettings();
......
......@@ -15,24 +15,86 @@
*/
package com.google.android.exoplayer2.ext.ima;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.Nullable;
import com.google.ads.interactivemedia.v3.api.AdDisplayContainer;
import com.google.ads.interactivemedia.v3.api.AdError;
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;
import com.google.ads.interactivemedia.v3.api.FriendlyObstruction;
import com.google.ads.interactivemedia.v3.api.FriendlyObstructionPurpose;
import com.google.ads.interactivemedia.v3.api.ImaSdkSettings;
import com.google.ads.interactivemedia.v3.api.player.VideoAdPlayer;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.source.ads.AdPlaybackState;
import com.google.android.exoplayer2.source.ads.AdsLoader.OverlayInfo;
import java.util.Arrays;
import java.util.List;
/**
* Static utility class for constructing {@link AdPlaybackState} instances from IMA-specific data.
*/
/* package */ final class AdPlaybackStateFactory {
private AdPlaybackStateFactory() {}
/** Utilities for working with IMA SDK and IMA extension data types. */
/* package */ final class ImaUtil {
/** Factory for objects provided by the IMA SDK. */
public interface ImaFactory {
/** Creates {@link ImaSdkSettings} for configuring the IMA SDK. */
ImaSdkSettings createImaSdkSettings();
/**
* Creates {@link AdsRenderingSettings} for giving the {@link AdsManager} parameters that
* control rendering of ads.
*/
AdsRenderingSettings createAdsRenderingSettings();
/**
* Creates an {@link AdDisplayContainer} to hold the player for video ads, a container for
* non-linear ads, and slots for companion ads.
*/
AdDisplayContainer createAdDisplayContainer(ViewGroup container, VideoAdPlayer player);
/** Creates an {@link AdDisplayContainer} to hold the player for audio ads. */
AdDisplayContainer createAudioAdDisplayContainer(Context context, VideoAdPlayer player);
/**
* Creates a {@link FriendlyObstruction} to describe an obstruction considered "friendly" for
* viewability measurement purposes.
*/
FriendlyObstruction createFriendlyObstruction(
View view,
FriendlyObstructionPurpose friendlyObstructionPurpose,
@Nullable String reasonDetail);
/** Creates an {@link AdsRequest} to contain the data used to request ads. */
AdsRequest createAdsRequest();
/** Creates an {@link AdsLoader} for requesting ads using the specified settings. */
AdsLoader createAdsLoader(
Context context, ImaSdkSettings imaSdkSettings, AdDisplayContainer adDisplayContainer);
}
/**
* Construct an {@link AdPlaybackState} from the provided {@code cuePoints}.
* Returns the IMA {@link FriendlyObstructionPurpose} corresponding to the given {@link
* OverlayInfo#purpose}.
*/
public static FriendlyObstructionPurpose getFriendlyObstructionPurpose(
@OverlayInfo.Purpose int purpose) {
switch (purpose) {
case OverlayInfo.PURPOSE_CONTROLS:
return FriendlyObstructionPurpose.VIDEO_CONTROLS;
case OverlayInfo.PURPOSE_CLOSE_AD:
return FriendlyObstructionPurpose.CLOSE_AD;
case OverlayInfo.PURPOSE_NOT_VISIBLE:
return FriendlyObstructionPurpose.NOT_VISIBLE;
case OverlayInfo.PURPOSE_OTHER:
default:
return FriendlyObstructionPurpose.OTHER;
}
}
/**
* Returns an initial {@link AdPlaybackState} with ad groups at the provided {@code cuePoints}.
*
* @param cuePoints The cue points of the ads in seconds.
* @return The {@link AdPlaybackState}.
*/
public static AdPlaybackState fromCuePoints(List<Float> cuePoints) {
public static AdPlaybackState getInitialAdPlaybackStateForCuePoints(List<Float> cuePoints) {
if (cuePoints.isEmpty()) {
// If no cue points are specified, there is a preroll ad.
return new AdPlaybackState(/* adGroupTimesUs...= */ 0);
......@@ -53,4 +115,14 @@ import java.util.List;
Arrays.sort(adGroupTimesUs, 0, adGroupIndex);
return new AdPlaybackState(adGroupTimesUs);
}
/** Returns whether the ad error indicates that an entire ad group failed to load. */
public static boolean isAdGroupLoadError(AdError adError) {
// TODO: Find out what other errors need to be handled (if any), and whether each one relates to
// a single ad, ad group or the whole timeline.
return adError.getErrorCode() == AdError.AdErrorCode.VAST_LINEAR_ASSET_MISMATCH
|| adError.getErrorCode() == AdError.AdErrorCode.UNKNOWN_ERROR;
}
private ImaUtil() {}
}
......@@ -55,7 +55,7 @@ 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.ImaAdsLoader.ImaFactory;
import com.google.android.exoplayer2.ext.ima.ImaUtil.ImaFactory;
import com.google.android.exoplayer2.source.MaskingMediaSource.PlaceholderTimeline;
import com.google.android.exoplayer2.source.ads.AdPlaybackState;
import com.google.android.exoplayer2.source.ads.AdsLoader;
......@@ -378,7 +378,7 @@ public final class ImaAdsLoaderTest {
assertThat(adsLoaderListener.adPlaybackState)
.isEqualTo(
AdPlaybackStateFactory.fromCuePoints(cuePoints)
ImaUtil.getInitialAdPlaybackStateForCuePoints(cuePoints)
.withContentDurationUs(CONTENT_PERIOD_DURATION_US));
}
......@@ -402,7 +402,7 @@ public final class ImaAdsLoaderTest {
assertThat(adsLoaderListener.adPlaybackState)
.isEqualTo(
AdPlaybackStateFactory.fromCuePoints(cuePoints)
ImaUtil.getInitialAdPlaybackStateForCuePoints(cuePoints)
.withContentDurationUs(CONTENT_PERIOD_DURATION_US)
.withAdDurationsUs(new long[][] {{TEST_AD_DURATION_US}})
.withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1)
......@@ -424,7 +424,7 @@ public final class ImaAdsLoaderTest {
verify(mockAdsRenderingSettings, never()).setPlayAdsAfterTime(anyDouble());
assertThat(adsLoaderListener.adPlaybackState)
.isEqualTo(
AdPlaybackStateFactory.fromCuePoints(cuePoints)
ImaUtil.getInitialAdPlaybackStateForCuePoints(cuePoints)
.withContentDurationUs(CONTENT_PERIOD_DURATION_US));
}
......@@ -448,7 +448,7 @@ public final class ImaAdsLoaderTest {
.of(expectedPlayAdsAfterTimeUs / C.MICROS_PER_SECOND);
assertThat(adsLoaderListener.adPlaybackState)
.isEqualTo(
AdPlaybackStateFactory.fromCuePoints(cuePoints)
ImaUtil.getInitialAdPlaybackStateForCuePoints(cuePoints)
.withContentDurationUs(CONTENT_PERIOD_DURATION_US)
.withSkippedAdGroup(/* adGroupIndex= */ 0));
}
......@@ -473,7 +473,7 @@ public final class ImaAdsLoaderTest {
.of(expectedPlayAdsAfterTimeUs / C.MICROS_PER_SECOND);
assertThat(adsLoaderListener.adPlaybackState)
.isEqualTo(
AdPlaybackStateFactory.fromCuePoints(cuePoints)
ImaUtil.getInitialAdPlaybackStateForCuePoints(cuePoints)
.withContentDurationUs(CONTENT_PERIOD_DURATION_US)
.withSkippedAdGroup(/* adGroupIndex= */ 0));
}
......@@ -500,7 +500,7 @@ public final class ImaAdsLoaderTest {
verify(mockAdsRenderingSettings, never()).setPlayAdsAfterTime(anyDouble());
assertThat(adsLoaderListener.adPlaybackState)
.isEqualTo(
AdPlaybackStateFactory.fromCuePoints(cuePoints)
ImaUtil.getInitialAdPlaybackStateForCuePoints(cuePoints)
.withContentDurationUs(CONTENT_PERIOD_DURATION_US));
}
......@@ -531,7 +531,7 @@ public final class ImaAdsLoaderTest {
.of(expectedPlayAdsAfterTimeUs / C.MICROS_PER_SECOND);
assertThat(adsLoaderListener.adPlaybackState)
.isEqualTo(
AdPlaybackStateFactory.fromCuePoints(cuePoints)
ImaUtil.getInitialAdPlaybackStateForCuePoints(cuePoints)
.withContentDurationUs(CONTENT_PERIOD_DURATION_US)
.withSkippedAdGroup(/* adGroupIndex= */ 0));
}
......@@ -563,7 +563,7 @@ public final class ImaAdsLoaderTest {
.of(expectedPlayAdsAfterTimeUs / C.MICROS_PER_SECOND);
assertThat(adsLoaderListener.adPlaybackState)
.isEqualTo(
AdPlaybackStateFactory.fromCuePoints(cuePoints)
ImaUtil.getInitialAdPlaybackStateForCuePoints(cuePoints)
.withSkippedAdGroup(/* adGroupIndex= */ 0)
.withContentDurationUs(CONTENT_PERIOD_DURATION_US));
}
......@@ -595,7 +595,7 @@ public final class ImaAdsLoaderTest {
.of(expectedPlayAdsAfterTimeUs / C.MICROS_PER_SECOND);
assertThat(adsLoaderListener.adPlaybackState)
.isEqualTo(
AdPlaybackStateFactory.fromCuePoints(cuePoints)
ImaUtil.getInitialAdPlaybackStateForCuePoints(cuePoints)
.withContentDurationUs(CONTENT_PERIOD_DURATION_US)
.withSkippedAdGroup(/* adGroupIndex= */ 0));
}
......@@ -622,7 +622,7 @@ public final class ImaAdsLoaderTest {
verify(mockAdsManager).destroy();
assertThat(adsLoaderListener.adPlaybackState)
.isEqualTo(
AdPlaybackStateFactory.fromCuePoints(cuePoints)
ImaUtil.getInitialAdPlaybackStateForCuePoints(cuePoints)
.withContentDurationUs(CONTENT_PERIOD_DURATION_US)
.withSkippedAdGroup(/* adGroupIndex= */ 0)
.withSkippedAdGroup(/* adGroupIndex= */ 1));
......@@ -663,7 +663,7 @@ public final class ImaAdsLoaderTest {
.of(expectedPlayAdsAfterTimeUs / C.MICROS_PER_SECOND);
assertThat(adsLoaderListener.adPlaybackState)
.isEqualTo(
AdPlaybackStateFactory.fromCuePoints(cuePoints)
ImaUtil.getInitialAdPlaybackStateForCuePoints(cuePoints)
.withSkippedAdGroup(/* adGroupIndex= */ 0)
.withContentDurationUs(CONTENT_PERIOD_DURATION_US));
}
......@@ -702,7 +702,7 @@ public final class ImaAdsLoaderTest {
.of(expectedPlayAdsAfterTimeUs / C.MICROS_PER_SECOND);
assertThat(adsLoaderListener.adPlaybackState)
.isEqualTo(
AdPlaybackStateFactory.fromCuePoints(cuePoints)
ImaUtil.getInitialAdPlaybackStateForCuePoints(cuePoints)
.withContentDurationUs(CONTENT_PERIOD_DURATION_US)
.withSkippedAdGroup(/* adGroupIndex= */ 0));
}
......@@ -761,7 +761,7 @@ public final class ImaAdsLoaderTest {
assertThat(adsLoaderListener.adPlaybackState)
.isEqualTo(
AdPlaybackStateFactory.fromCuePoints(cuePoints)
ImaUtil.getInitialAdPlaybackStateForCuePoints(cuePoints)
.withContentDurationUs(CONTENT_PERIOD_DURATION_US)
.withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1)
.withAdUri(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0, TEST_URI)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment