Commit cd3bef24 by tonihei Committed by Ian Baker

Simplify StreamRequest.Builder to an Uri Builder and make it public

Right now, the option to build an IMA DAI URI programmatically is still
package-private. To simplify the process, we can remove the StreamRequest
wrapper and directly provide an URI builder.

The same class can provide some package-private helper methods to parse the
created URI.

#minor-release

PiperOrigin-RevId: 427445326
parent eb6e25b6
...@@ -323,7 +323,10 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou ...@@ -323,7 +323,10 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
private final com.google.ads.interactivemedia.v3.api.AdsLoader adsLoader; private final com.google.ads.interactivemedia.v3.api.AdsLoader adsLoader;
@Nullable private final AdEventListener applicationAdEventListener; @Nullable private final AdEventListener applicationAdEventListener;
@Nullable private final AdErrorListener applicationAdErrorListener; @Nullable private final AdErrorListener applicationAdErrorListener;
private final ServerSideAdInsertionStreamRequest streamRequest; private final boolean isLiveStream;
private final String adsId;
private final StreamRequest streamRequest;
private final int loadVideoTimeoutMs;
private final StreamPlayer streamPlayer; private final StreamPlayer streamPlayer;
private final Handler mainHandler; private final Handler mainHandler;
private final ComponentListener componentListener; private final ComponentListener componentListener;
...@@ -354,7 +357,10 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou ...@@ -354,7 +357,10 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
adPlaybackState = AdPlaybackState.NONE; adPlaybackState = AdPlaybackState.NONE;
mainHandler = Util.createHandlerForCurrentLooper(); mainHandler = Util.createHandlerForCurrentLooper();
Uri streamRequestUri = checkNotNull(mediaItem.localConfiguration).uri; Uri streamRequestUri = checkNotNull(mediaItem.localConfiguration).uri;
streamRequest = ServerSideAdInsertionStreamRequest.fromUri(streamRequestUri); isLiveStream = ImaServerSideAdInsertionUriBuilder.isLiveStream(streamRequestUri);
adsId = ImaServerSideAdInsertionUriBuilder.getAdsId(streamRequestUri);
loadVideoTimeoutMs = ImaServerSideAdInsertionUriBuilder.getLoadVideoTimeoutMs(streamRequestUri);
streamRequest = ImaServerSideAdInsertionUriBuilder.createStreamRequest(streamRequestUri);
} }
@Override @Override
...@@ -371,10 +377,10 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou ...@@ -371,10 +377,10 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
StreamManagerLoadable streamManagerLoadable = StreamManagerLoadable streamManagerLoadable =
new StreamManagerLoadable( new StreamManagerLoadable(
adsLoader, adsLoader,
streamRequest.getStreamRequest(), streamRequest,
streamPlayer, streamPlayer,
applicationAdErrorListener, applicationAdErrorListener,
streamRequest.loadVideoTimeoutMs); loadVideoTimeoutMs);
loader.startLoading( loader.startLoading(
streamManagerLoadable, streamManagerLoadable,
new StreamManagerLoadableCallback(), new StreamManagerLoadableCallback(),
...@@ -483,7 +489,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou ...@@ -483,7 +489,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
if (!adPlaybackState.equals(AdPlaybackState.NONE) && contentTimeline != null) { if (!adPlaybackState.equals(AdPlaybackState.NONE) && contentTimeline != null) {
ImmutableMap<Object, AdPlaybackState> splitAdPlaybackStates = ImmutableMap<Object, AdPlaybackState> splitAdPlaybackStates =
splitAdPlaybackStateForPeriods(adPlaybackState, contentTimeline); splitAdPlaybackStateForPeriods(adPlaybackState, contentTimeline);
streamPlayer.setAdPlaybackStates(streamRequest.adsId, splitAdPlaybackStates, contentTimeline); streamPlayer.setAdPlaybackStates(adsId, splitAdPlaybackStates, contentTimeline);
checkNotNull(serverSideAdInsertionMediaSource).setAdPlaybackStates(splitAdPlaybackStates); checkNotNull(serverSideAdInsertionMediaSource).setAdPlaybackStates(splitAdPlaybackStates);
} }
} }
...@@ -499,9 +505,9 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou ...@@ -499,9 +505,9 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
contentMediaSourceFactory.createMediaSource(MediaItem.fromUri(contentUri)), contentMediaSourceFactory.createMediaSource(MediaItem.fromUri(contentUri)),
componentListener); componentListener);
this.serverSideAdInsertionMediaSource = serverSideAdInsertionMediaSource; this.serverSideAdInsertionMediaSource = serverSideAdInsertionMediaSource;
if (streamRequest.isLiveStream()) { if (isLiveStream) {
AdPlaybackState liveAdPlaybackState = AdPlaybackState liveAdPlaybackState =
new AdPlaybackState(streamRequest.adsId) new AdPlaybackState(adsId)
.withNewAdGroup(/* adGroupIndex= */ 0, /* adGroupTimeUs= */ C.TIME_END_OF_SOURCE) .withNewAdGroup(/* adGroupIndex= */ 0, /* adGroupTimeUs= */ C.TIME_END_OF_SOURCE)
.withIsServerSideInserted(/* adGroupIndex= */ 0, true); .withIsServerSideInserted(/* adGroupIndex= */ 0, true);
mainHandler.post(() -> setAdPlaybackState(liveAdPlaybackState)); mainHandler.post(() -> setAdPlaybackState(liveAdPlaybackState));
...@@ -614,7 +620,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou ...@@ -614,7 +620,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
if (!mediaItem.equals(oldPosition.mediaItem) if (!mediaItem.equals(oldPosition.mediaItem)
|| !mediaItem.equals(newPosition.mediaItem) || !mediaItem.equals(newPosition.mediaItem)
|| !streamRequest.adsId.equals( || !adsId.equals(
player player
.getCurrentTimeline() .getCurrentTimeline()
.getPeriodByUid(checkNotNull(newPosition.periodUid), new Timeline.Period()) .getPeriodByUid(checkNotNull(newPosition.periodUid), new Timeline.Period())
...@@ -648,7 +654,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou ...@@ -648,7 +654,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
@Override @Override
public void onMetadata(Metadata metadata) { public void onMetadata(Metadata metadata) {
if (!isCurrentAdPlaying(player, mediaItem, streamRequest.adsId)) { if (!isCurrentAdPlaying(player, mediaItem, adsId)) {
return; return;
} }
for (int i = 0; i < metadata.length(); i++) { for (int i = 0; i < metadata.length(); i++) {
...@@ -668,15 +674,14 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou ...@@ -668,15 +674,14 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
@Override @Override
public void onPlaybackStateChanged(@Player.State int state) { public void onPlaybackStateChanged(@Player.State int state) {
if (state == Player.STATE_ENDED if (state == Player.STATE_ENDED && isCurrentAdPlaying(player, mediaItem, adsId)) {
&& isCurrentAdPlaying(player, mediaItem, streamRequest.adsId)) {
streamPlayer.onContentCompleted(); streamPlayer.onContentCompleted();
} }
} }
@Override @Override
public void onVolumeChanged(float volume) { public void onVolumeChanged(float volume) {
if (!isCurrentAdPlaying(player, mediaItem, streamRequest.adsId)) { if (!isCurrentAdPlaying(player, mediaItem, adsId)) {
return; return;
} }
int volumePct = (int) Math.floor(volume * 100); int volumePct = (int) Math.floor(volume * 100);
...@@ -692,15 +697,14 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou ...@@ -692,15 +697,14 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
switch (event.getType()) { switch (event.getType()) {
case CUEPOINTS_CHANGED: case CUEPOINTS_CHANGED:
// CUEPOINTS_CHANGED event is firing multiple times with the same queue points. // CUEPOINTS_CHANGED event is firing multiple times with the same queue points.
if (!streamRequest.isLiveStream() && newAdPlaybackState.equals(AdPlaybackState.NONE)) { if (!isLiveStream && newAdPlaybackState.equals(AdPlaybackState.NONE)) {
newAdPlaybackState = newAdPlaybackState =
setVodAdGroupPlaceholders( setVodAdGroupPlaceholders(
checkNotNull(streamManager).getCuePoints(), checkNotNull(streamManager).getCuePoints(), new AdPlaybackState(adsId));
new AdPlaybackState(streamRequest.adsId));
} }
break; break;
case LOADED: case LOADED:
if (streamRequest.isLiveStream()) { if (isLiveStream) {
Timeline timeline = player.getCurrentTimeline(); Timeline timeline = player.getCurrentTimeline();
Timeline.Window window = Timeline.Window window =
timeline.getWindow(player.getCurrentMediaItemIndex(), new Timeline.Window()); timeline.getWindow(player.getCurrentMediaItemIndex(), new Timeline.Window());
...@@ -717,14 +721,14 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou ...@@ -717,14 +721,14 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
event.getAd(), event.getAd(),
currentPeriodPosition, currentPeriodPosition,
newAdPlaybackState.equals(AdPlaybackState.NONE) newAdPlaybackState.equals(AdPlaybackState.NONE)
? new AdPlaybackState(streamRequest.adsId) ? new AdPlaybackState(adsId)
: newAdPlaybackState); : newAdPlaybackState);
} else { } else {
newAdPlaybackState = setVodAdInPlaceholder(event.getAd(), newAdPlaybackState); newAdPlaybackState = setVodAdInPlaceholder(event.getAd(), newAdPlaybackState);
} }
break; break;
case SKIPPED: case SKIPPED:
if (!streamRequest.isLiveStream()) { if (!isLiveStream) {
newAdPlaybackState = skipAd(event.getAd(), newAdPlaybackState); newAdPlaybackState = skipAd(event.getAd(), newAdPlaybackState);
} }
break; break;
...@@ -742,7 +746,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou ...@@ -742,7 +746,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
mainHandler.post(() -> setContentTimeline(contentTimeline)); mainHandler.post(() -> setContentTimeline(contentTimeline));
// Defer source refresh to ad playback state update for VOD. Refresh immediately when live // Defer source refresh to ad playback state update for VOD. Refresh immediately when live
// with single period. // with single period.
return !streamRequest.isLiveStream() || contentTimeline.getPeriodCount() > 1; return !isLiveStream || contentTimeline.getPeriodCount() > 1;
} }
} }
......
...@@ -24,228 +24,25 @@ import android.text.TextUtils; ...@@ -24,228 +24,25 @@ import android.text.TextUtils;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.media3.common.C; import androidx.media3.common.C;
import androidx.media3.common.C.ContentType; import androidx.media3.common.C.ContentType;
import androidx.media3.common.util.UnstableApi;
import com.google.ads.interactivemedia.v3.api.ImaSdkFactory; import com.google.ads.interactivemedia.v3.api.ImaSdkFactory;
import com.google.ads.interactivemedia.v3.api.StreamRequest; import com.google.ads.interactivemedia.v3.api.StreamRequest;
import com.google.ads.interactivemedia.v3.api.StreamRequest.StreamFormat; import com.google.ads.interactivemedia.v3.api.StreamRequest.StreamFormat;
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
/** Stream request data for an IMA DAI stream. */ /**
/* package */ final class ServerSideAdInsertionStreamRequest { * Builder for URI for IMA DAI streams. The resulting URI can be used to build a {@link
* androidx.media3.common.MediaItem#fromUri(Uri) media item} that can be played by the {@link
* ImaServerSideAdInsertionMediaSource}.
*/
@UnstableApi
public final class ImaServerSideAdInsertionUriBuilder {
/** The default timeout for loading the video URI, in milliseconds. */ /** The default timeout for loading the video URI, in milliseconds. */
public static final int DEFAULT_LOAD_VIDEO_TIMEOUT_MS = 10_000; public static final int DEFAULT_LOAD_VIDEO_TIMEOUT_MS = 10_000;
/** Builds a {@link ServerSideAdInsertionStreamRequest}. */
public static final class Builder {
@Nullable private String adsId;
@Nullable private String assetKey;
@Nullable private String apiKey;
@Nullable private String contentSourceId;
@Nullable private String videoId;
@Nullable private String manifestSuffix;
@Nullable private String contentUrl;
@Nullable private String authToken;
@Nullable private String streamActivityMonitorId;
private ImmutableMap<String, String> adTagParameters;
public @ContentType int format = C.TYPE_HLS;
private int loadVideoTimeoutMs;
/** Creates a new instance. */
public Builder() {
adTagParameters = ImmutableMap.of();
loadVideoTimeoutMs = DEFAULT_LOAD_VIDEO_TIMEOUT_MS;
}
/**
* An opaque identifier for associated ad playback state, or {@code null} if the {@link
* #setAssetKey(String) asset key} (for live) or {@link #setVideoId(String) video id} (for VOD)
* should be used as the ads identifier.
*
* @param adsId The ads identifier.
* @return This instance, for convenience.
*/
public Builder setAdsId(String adsId) {
this.adsId = adsId;
return this;
}
/**
* The stream request asset key used for live streams.
*
* @param assetKey Live stream asset key.
* @return This instance, for convenience.
*/
public Builder setAssetKey(@Nullable String assetKey) {
this.assetKey = assetKey;
return this;
}
/**
* Sets the stream request authorization token. Used in place of {@link #setApiKey(String) the
* API key} for stricter content authorization. The publisher can control individual content
* streams authorizations based on this token.
*
* @param authToken Live stream authorization token.
* @return This instance, for convenience.
*/
public Builder setAuthToken(@Nullable String authToken) {
this.authToken = authToken;
return this;
}
/**
* The stream request content source ID used for on-demand streams.
*
* @param contentSourceId VOD stream content source id.
* @return This instance, for convenience.
*/
public Builder setContentSourceId(@Nullable String contentSourceId) {
this.contentSourceId = contentSourceId;
return this;
}
/**
* The stream request video ID used for on-demand streams.
*
* @param videoId VOD stream video id.
* @return This instance, for convenience.
*/
public Builder setVideoId(@Nullable String videoId) {
this.videoId = videoId;
return this;
}
/**
* Sets the format of the stream request.
*
* @param format VOD or live stream type.
* @return This instance, for convenience.
*/
public Builder setFormat(@ContentType int format) {
checkArgument(format == C.TYPE_DASH || format == C.TYPE_HLS);
this.format = format;
return this;
}
/**
* The stream request API key. This is used for content authentication. The API key is provided
* to the publisher to unlock their content. It's a security measure used to verify the
* applications that are attempting to access the content.
*
* @param apiKey Stream api key.
* @return This instance, for convenience.
*/
public Builder setApiKey(@Nullable String apiKey) {
this.apiKey = apiKey;
return this;
}
/**
* Sets the ID to be used to debug the stream with the stream activity monitor. This is used to
* provide a convenient way to allow publishers to find a stream log in the stream activity
* monitor tool.
*
* @param streamActivityMonitorId ID for debugging the stream with the stream activity monitor.
* @return This instance, for convenience.
*/
public Builder setStreamActivityMonitorId(@Nullable String streamActivityMonitorId) {
this.streamActivityMonitorId = streamActivityMonitorId;
return this;
}
/**
* Sets the overridable ad tag parameters on the stream request. <a
* href="//support.google.com/dfp_premium/answer/7320899">Supply targeting parameters to your
* stream</a> provides more information.
*
* <p>You can use the dai-ot and dai-ov parameters for stream variant preference. See <a
* href="//support.google.com/dfp_premium/answer/7320898">Override Stream Variant Parameters</a>
* for more information.
*
* @param adTagParameters A map of extra parameters to pass to the ad server.
* @return This instance, for convenience.
*/
public Builder setAdTagParameters(Map<String, String> adTagParameters) {
this.adTagParameters = ImmutableMap.copyOf(adTagParameters);
return this;
}
/**
* Sets the optional stream manifest's suffix, which will be appended to the stream manifest's
* URL. The provided string must be URL-encoded and must not include a leading question mark.
*
* @param manifestSuffix Stream manifest's suffix.
* @return This instance, for convenience.
*/
public Builder setManifestSuffix(@Nullable String manifestSuffix) {
this.manifestSuffix = manifestSuffix;
return this;
}
/**
* Specifies the deep link to the content's screen. If provided, this parameter is passed to the
* OM SDK. See <a href="//developer.android.com/training/app-links/deep-linking">Android
* documentation</a> for more information.
*
* @param contentUrl Deep link to the content's screen.
* @return This instance, for convenience.
*/
public Builder setContentUrl(@Nullable String contentUrl) {
this.contentUrl = contentUrl;
return this;
}
/**
* Sets the duration after which resolving the video URI should time out, in milliseconds.
*
* <p>The default is {@link #DEFAULT_LOAD_VIDEO_TIMEOUT_MS} milliseconds.
*
* @param loadVideoTimeoutMs The timeout after which to give up resolving the video URI.
* @return This instance, for convenience.
*/
public Builder setLoadVideoTimeoutMs(int loadVideoTimeoutMs) {
this.loadVideoTimeoutMs = loadVideoTimeoutMs;
return this;
}
/**
* Builds a {@link ServerSideAdInsertionStreamRequest} with the builder's current values.
*
* @return The build {@link ServerSideAdInsertionStreamRequest}.
* @throws IllegalStateException If request has missing or invalid inputs.
*/
public ServerSideAdInsertionStreamRequest build() {
checkState(
(TextUtils.isEmpty(assetKey)
&& !TextUtils.isEmpty(contentSourceId)
&& !TextUtils.isEmpty(videoId))
|| (!TextUtils.isEmpty(assetKey)
&& TextUtils.isEmpty(contentSourceId)
&& TextUtils.isEmpty(videoId)));
@Nullable String adsId = this.adsId;
if (adsId == null) {
adsId = assetKey != null ? assetKey : checkNotNull(videoId);
}
return new ServerSideAdInsertionStreamRequest(
adsId,
assetKey,
apiKey,
contentSourceId,
videoId,
adTagParameters,
manifestSuffix,
contentUrl,
authToken,
streamActivityMonitorId,
format,
loadVideoTimeoutMs);
}
}
private static final String IMA_AUTHORITY = "dai.google.com"; private static final String IMA_AUTHORITY = "dai.google.com";
private static final String ADS_ID = "adsId"; private static final String ADS_ID = "adsId";
private static final String ASSET_KEY = "assetKey"; private static final String ASSET_KEY = "assetKey";
...@@ -260,86 +57,199 @@ import java.util.Map; ...@@ -260,86 +57,199 @@ import java.util.Map;
private static final String FORMAT = "format"; private static final String FORMAT = "format";
private static final String LOAD_VIDEO_TIMEOUT_MS = "loadVideoTimeoutMs"; private static final String LOAD_VIDEO_TIMEOUT_MS = "loadVideoTimeoutMs";
public final String adsId; @Nullable private String adsId;
@Nullable public final String assetKey; @Nullable private String assetKey;
@Nullable public final String apiKey; @Nullable private String apiKey;
@Nullable public final String contentSourceId; @Nullable private String contentSourceId;
@Nullable public final String videoId; @Nullable private String videoId;
public final ImmutableMap<String, String> adTagParameters; @Nullable private String manifestSuffix;
@Nullable public final String manifestSuffix; @Nullable private String contentUrl;
@Nullable public final String contentUrl; @Nullable private String authToken;
@Nullable public final String authToken; @Nullable private String streamActivityMonitorId;
@Nullable public final String streamActivityMonitorId; private ImmutableMap<String, String> adTagParameters;
public @ContentType int format = C.TYPE_HLS; public @ContentType int format;
public final int loadVideoTimeoutMs; private int loadVideoTimeoutMs;
private ServerSideAdInsertionStreamRequest( /** Creates a new instance. */
String adsId, public ImaServerSideAdInsertionUriBuilder() {
@Nullable String assetKey, adTagParameters = ImmutableMap.of();
@Nullable String apiKey, loadVideoTimeoutMs = DEFAULT_LOAD_VIDEO_TIMEOUT_MS;
@Nullable String contentSourceId, format = C.TYPE_OTHER;
@Nullable String videoId, }
ImmutableMap<String, String> adTagParameters,
@Nullable String manifestSuffix, /**
@Nullable String contentUrl, * An opaque identifier for associated ad playback state, or {@code null} if the {@link
@Nullable String authToken, * #setAssetKey(String) asset key} (for live) or {@link #setVideoId(String) video id} (for VOD)
@Nullable String streamActivityMonitorId, * should be used as the ads identifier.
@ContentType int format, *
int loadVideoTimeoutMs) { * @param adsId The ads identifier.
* @return This instance, for convenience.
*/
public ImaServerSideAdInsertionUriBuilder setAdsId(String adsId) {
this.adsId = adsId; this.adsId = adsId;
return this;
}
/**
* The stream request asset key used for live streams.
*
* @param assetKey Live stream asset key.
* @return This instance, for convenience.
*/
public ImaServerSideAdInsertionUriBuilder setAssetKey(@Nullable String assetKey) {
this.assetKey = assetKey; this.assetKey = assetKey;
this.apiKey = apiKey; return this;
}
/**
* Sets the stream request authorization token. Used in place of {@link #setApiKey(String) the API
* key} for stricter content authorization. The publisher can control individual content streams
* authorizations based on this token.
*
* @param authToken Live stream authorization token.
* @return This instance, for convenience.
*/
public ImaServerSideAdInsertionUriBuilder setAuthToken(@Nullable String authToken) {
this.authToken = authToken;
return this;
}
/**
* The stream request content source ID used for on-demand streams.
*
* @param contentSourceId VOD stream content source id.
* @return This instance, for convenience.
*/
public ImaServerSideAdInsertionUriBuilder setContentSourceId(@Nullable String contentSourceId) {
this.contentSourceId = contentSourceId; this.contentSourceId = contentSourceId;
return this;
}
/**
* The stream request video ID used for on-demand streams.
*
* @param videoId VOD stream video id.
* @return This instance, for convenience.
*/
public ImaServerSideAdInsertionUriBuilder setVideoId(@Nullable String videoId) {
this.videoId = videoId; this.videoId = videoId;
this.adTagParameters = adTagParameters; return this;
this.manifestSuffix = manifestSuffix; }
this.contentUrl = contentUrl;
this.authToken = authToken; /**
this.streamActivityMonitorId = streamActivityMonitorId; * Sets the format of the stream request.
*
* @param format VOD or live stream type.
* @return This instance, for convenience.
*/
public ImaServerSideAdInsertionUriBuilder setFormat(@ContentType int format) {
checkArgument(format == C.TYPE_DASH || format == C.TYPE_HLS);
this.format = format; this.format = format;
this.loadVideoTimeoutMs = loadVideoTimeoutMs; return this;
} }
/** Returns whether this request is for a live stream or false if it is a VOD stream. */ /**
public boolean isLiveStream() { * The stream request API key. This is used for content authentication. The API key is provided to
return !TextUtils.isEmpty(assetKey); * the publisher to unlock their content. It's a security measure used to verify the applications
* that are attempting to access the content.
*
* @param apiKey Stream api key.
* @return This instance, for convenience.
*/
public ImaServerSideAdInsertionUriBuilder setApiKey(@Nullable String apiKey) {
this.apiKey = apiKey;
return this;
} }
/** Returns the corresponding {@link StreamRequest}. */ /**
@SuppressWarnings("nullness") // Required for making nullness test pass for library_with_ima_sdk. * Sets the ID to be used to debug the stream with the stream activity monitor. This is used to
public StreamRequest getStreamRequest() { * provide a convenient way to allow publishers to find a stream log in the stream activity
StreamRequest streamRequest; * monitor tool.
if (!TextUtils.isEmpty(assetKey)) { *
streamRequest = ImaSdkFactory.getInstance().createLiveStreamRequest(assetKey, apiKey); * @param streamActivityMonitorId ID for debugging the stream with the stream activity monitor.
} else { * @return This instance, for convenience.
streamRequest = */
ImaSdkFactory.getInstance() public ImaServerSideAdInsertionUriBuilder setStreamActivityMonitorId(
.createVodStreamRequest(checkNotNull(contentSourceId), checkNotNull(videoId), apiKey); @Nullable String streamActivityMonitorId) {
} this.streamActivityMonitorId = streamActivityMonitorId;
if (format == C.TYPE_DASH) { return this;
streamRequest.setFormat(StreamFormat.DASH); }
} else if (format == C.TYPE_HLS) {
streamRequest.setFormat(StreamFormat.HLS); /**
} * Sets the overridable ad tag parameters on the stream request. <a
// Optional params. * href="//support.google.com/dfp_premium/answer/7320899">Supply targeting parameters to your
streamRequest.setAdTagParameters(adTagParameters); * stream</a> provides more information.
if (manifestSuffix != null) { *
streamRequest.setManifestSuffix(manifestSuffix); * <p>You can use the dai-ot and dai-ov parameters for stream variant preference. See <a
} * href="//support.google.com/dfp_premium/answer/7320898">Override Stream Variant Parameters</a>
if (contentUrl != null) { * for more information.
streamRequest.setContentUrl(contentUrl); *
} * @param adTagParameters A map of extra parameters to pass to the ad server.
if (authToken != null) { * @return This instance, for convenience.
streamRequest.setAuthToken(authToken); */
} public ImaServerSideAdInsertionUriBuilder setAdTagParameters(
if (streamActivityMonitorId != null) { Map<String, String> adTagParameters) {
streamRequest.setStreamActivityMonitorId(streamActivityMonitorId); this.adTagParameters = ImmutableMap.copyOf(adTagParameters);
} return this;
return streamRequest; }
/**
* Sets the optional stream manifest's suffix, which will be appended to the stream manifest's
* URL. The provided string must be URL-encoded and must not include a leading question mark.
*
* @param manifestSuffix Stream manifest's suffix.
* @return This instance, for convenience.
*/
public ImaServerSideAdInsertionUriBuilder setManifestSuffix(@Nullable String manifestSuffix) {
this.manifestSuffix = manifestSuffix;
return this;
}
/**
* Specifies the deep link to the content's screen. If provided, this parameter is passed to the
* OM SDK. See <a href="//developer.android.com/training/app-links/deep-linking">Android
* documentation</a> for more information.
*
* @param contentUrl Deep link to the content's screen.
* @return This instance, for convenience.
*/
public ImaServerSideAdInsertionUriBuilder setContentUrl(@Nullable String contentUrl) {
this.contentUrl = contentUrl;
return this;
}
/**
* Sets the duration after which resolving the video URI should time out, in milliseconds.
*
* <p>The default is {@link #DEFAULT_LOAD_VIDEO_TIMEOUT_MS} milliseconds.
*
* @param loadVideoTimeoutMs The timeout after which to give up resolving the video URI.
* @return This instance, for convenience.
*/
public ImaServerSideAdInsertionUriBuilder setLoadVideoTimeoutMs(int loadVideoTimeoutMs) {
this.loadVideoTimeoutMs = loadVideoTimeoutMs;
return this;
} }
/** Returns a corresponding {@link Uri}. */ /**
public Uri toUri() { * Builds a URI with the builder's current values.
*
* @return The build {@link Uri}.
* @throws IllegalStateException If the builder has missing or invalid inputs.
*/
public Uri build() {
checkState(
(TextUtils.isEmpty(assetKey)
&& !TextUtils.isEmpty(contentSourceId)
&& !TextUtils.isEmpty(videoId))
|| (!TextUtils.isEmpty(assetKey)
&& TextUtils.isEmpty(contentSourceId)
&& TextUtils.isEmpty(videoId)));
checkState(format != C.TYPE_OTHER);
@Nullable String adsId = this.adsId;
if (adsId == null) {
adsId = assetKey != null ? assetKey : checkNotNull(videoId);
}
Uri.Builder dataUriBuilder = new Uri.Builder(); Uri.Builder dataUriBuilder = new Uri.Builder();
dataUriBuilder.scheme(C.SSAI_SCHEME); dataUriBuilder.scheme(C.SSAI_SCHEME);
dataUriBuilder.authority(IMA_AUTHORITY); dataUriBuilder.authority(IMA_AUTHORITY);
...@@ -384,94 +294,80 @@ import java.util.Map; ...@@ -384,94 +294,80 @@ import java.util.Map;
return dataUriBuilder.build(); return dataUriBuilder.build();
} }
@Override /** Returns whether the provided request is for a live stream or false if it is a VOD stream. */
public boolean equals(@Nullable Object o) { /* package */ static boolean isLiveStream(Uri uri) {
if (this == o) { return !TextUtils.isEmpty(uri.getQueryParameter(ASSET_KEY));
return true;
}
if (!(o instanceof ServerSideAdInsertionStreamRequest)) {
return false;
}
ServerSideAdInsertionStreamRequest that = (ServerSideAdInsertionStreamRequest) o;
return format == that.format
&& loadVideoTimeoutMs == that.loadVideoTimeoutMs
&& Objects.equal(adsId, that.adsId)
&& Objects.equal(assetKey, that.assetKey)
&& Objects.equal(apiKey, that.apiKey)
&& Objects.equal(contentSourceId, that.contentSourceId)
&& Objects.equal(videoId, that.videoId)
&& Objects.equal(adTagParameters, that.adTagParameters)
&& Objects.equal(manifestSuffix, that.manifestSuffix)
&& Objects.equal(contentUrl, that.contentUrl)
&& Objects.equal(authToken, that.authToken)
&& Objects.equal(streamActivityMonitorId, that.streamActivityMonitorId);
} }
@Override /** Returns the opaque adsId for this stream. */
public int hashCode() { /* package */ static String getAdsId(Uri uri) {
return Objects.hashCode( return checkNotNull(uri.getQueryParameter(ADS_ID));
adsId,
assetKey,
apiKey,
contentSourceId,
videoId,
adTagParameters,
manifestSuffix,
contentUrl,
authToken,
streamActivityMonitorId,
loadVideoTimeoutMs,
format);
} }
/** /** Returns the video load timeout in milliseconds. */
* Creates a {@link ServerSideAdInsertionStreamRequest} for the given URI. /* package */ static int getLoadVideoTimeoutMs(Uri uri) {
* @Nullable String adsLoaderTimeoutUs = uri.getQueryParameter(LOAD_VIDEO_TIMEOUT_MS);
* @param uri The URI. return TextUtils.isEmpty(adsLoaderTimeoutUs)
* @return An {@link ServerSideAdInsertionStreamRequest} for the given URI. ? DEFAULT_LOAD_VIDEO_TIMEOUT_MS
* @throws IllegalStateException If uri has missing or invalid inputs. : Integer.parseInt(adsLoaderTimeoutUs);
*/ }
public static ServerSideAdInsertionStreamRequest fromUri(Uri uri) {
ServerSideAdInsertionStreamRequest.Builder request = /** Returns the corresponding {@link StreamRequest}. */
new ServerSideAdInsertionStreamRequest.Builder(); @SuppressWarnings("nullness") // Required for making nullness test pass for library_with_ima_sdk.
/* package */ static StreamRequest createStreamRequest(Uri uri) {
if (!C.SSAI_SCHEME.equals(uri.getScheme()) || !IMA_AUTHORITY.equals(uri.getAuthority())) { if (!C.SSAI_SCHEME.equals(uri.getScheme()) || !IMA_AUTHORITY.equals(uri.getAuthority())) {
throw new IllegalArgumentException("Invalid URI scheme or authority."); throw new IllegalArgumentException("Invalid URI scheme or authority.");
} }
request.setAdsId(checkNotNull(uri.getQueryParameter(ADS_ID))); StreamRequest streamRequest;
request.setAssetKey(uri.getQueryParameter(ASSET_KEY)); // Required params.
request.setApiKey(uri.getQueryParameter(API_KEY)); @Nullable String assetKey = uri.getQueryParameter(ASSET_KEY);
request.setContentSourceId(uri.getQueryParameter(CONTENT_SOURCE_ID)); @Nullable String apiKey = uri.getQueryParameter(API_KEY);
request.setVideoId(uri.getQueryParameter(VIDEO_ID)); @Nullable String contentSourceId = uri.getQueryParameter(CONTENT_SOURCE_ID);
request.setManifestSuffix(uri.getQueryParameter(MANIFEST_SUFFIX)); @Nullable String videoId = uri.getQueryParameter(VIDEO_ID);
request.setContentUrl(uri.getQueryParameter(CONTENT_URL)); if (!TextUtils.isEmpty(assetKey)) {
request.setAuthToken(uri.getQueryParameter(AUTH_TOKEN)); streamRequest = ImaSdkFactory.getInstance().createLiveStreamRequest(assetKey, apiKey);
request.setStreamActivityMonitorId(uri.getQueryParameter(STREAM_ACTIVITY_MONITOR_ID)); } else {
String adsLoaderTimeoutUs = uri.getQueryParameter(LOAD_VIDEO_TIMEOUT_MS); streamRequest =
request.setLoadVideoTimeoutMs( ImaSdkFactory.getInstance()
TextUtils.isEmpty(adsLoaderTimeoutUs) .createVodStreamRequest(checkNotNull(contentSourceId), checkNotNull(videoId), apiKey);
? DEFAULT_LOAD_VIDEO_TIMEOUT_MS
: Integer.parseInt(adsLoaderTimeoutUs));
String formatValue = uri.getQueryParameter(FORMAT);
if (!TextUtils.isEmpty(formatValue)) {
request.setFormat(Integer.parseInt(formatValue));
} }
Map<String, String> adTagParameters; int format = Integer.parseInt(uri.getQueryParameter(FORMAT));
String adTagParametersValue; if (format == C.TYPE_DASH) {
String singleAdTagParameterValue; streamRequest.setFormat(StreamFormat.DASH);
if (uri.getQueryParameter(AD_TAG_PARAMETERS) != null) { } else if (format == C.TYPE_HLS) {
adTagParameters = new HashMap<>(); streamRequest.setFormat(StreamFormat.HLS);
adTagParametersValue = uri.getQueryParameter(AD_TAG_PARAMETERS); } else {
if (!TextUtils.isEmpty(adTagParametersValue)) { throw new IllegalArgumentException("Unsupported stream format:" + format);
Uri adTagParametersUri = Uri.parse(adTagParametersValue); }
for (String paramName : adTagParametersUri.getQueryParameterNames()) { // Optional params.
singleAdTagParameterValue = adTagParametersUri.getQueryParameter(paramName); @Nullable String adTagParametersValue = uri.getQueryParameter(AD_TAG_PARAMETERS);
if (!TextUtils.isEmpty(singleAdTagParameterValue)) { if (!TextUtils.isEmpty(adTagParametersValue)) {
adTagParameters.put(paramName, singleAdTagParameterValue); Map<String, String> adTagParameters = new HashMap<>();
} Uri adTagParametersUri = Uri.parse(adTagParametersValue);
for (String paramName : adTagParametersUri.getQueryParameterNames()) {
String singleAdTagParameterValue = adTagParametersUri.getQueryParameter(paramName);
if (!TextUtils.isEmpty(singleAdTagParameterValue)) {
adTagParameters.put(paramName, singleAdTagParameterValue);
} }
} }
request.setAdTagParameters(adTagParameters); streamRequest.setAdTagParameters(adTagParameters);
}
@Nullable String manifestSuffix = uri.getQueryParameter(MANIFEST_SUFFIX);
if (manifestSuffix != null) {
streamRequest.setManifestSuffix(manifestSuffix);
} }
return request.build(); @Nullable String contentUrl = uri.getQueryParameter(CONTENT_URL);
if (contentUrl != null) {
streamRequest.setContentUrl(contentUrl);
}
@Nullable String authToken = uri.getQueryParameter(AUTH_TOKEN);
if (authToken != null) {
streamRequest.setAuthToken(authToken);
}
@Nullable String streamActivityMonitorId = uri.getQueryParameter(STREAM_ACTIVITY_MONITOR_ID);
if (streamActivityMonitorId != null) {
streamRequest.setStreamActivityMonitorId(streamActivityMonitorId);
}
return streamRequest;
} }
} }
...@@ -17,16 +17,20 @@ package androidx.media3.exoplayer.ima; ...@@ -17,16 +17,20 @@ package androidx.media3.exoplayer.ima;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import android.net.Uri;
import androidx.media3.common.C;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.ads.interactivemedia.v3.api.StreamRequest;
import com.google.ads.interactivemedia.v3.api.StreamRequest.StreamFormat;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
/** Unit tests for {@link ServerSideAdInsertionStreamRequest}. */ /** Unit tests for {@link ImaServerSideAdInsertionUriBuilder}. */
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
public final class ServerSideAdInsertionStreamRequestTest { public final class ImaServerSideAdInsertionUriBuilderTest {
private static final String ADS_ID = "testAdsId"; private static final String ADS_ID = "testAdsId";
private static final String ASSET_KEY = "testAssetKey"; private static final String ASSET_KEY = "testAssetKey";
...@@ -39,8 +43,6 @@ public final class ServerSideAdInsertionStreamRequestTest { ...@@ -39,8 +43,6 @@ public final class ServerSideAdInsertionStreamRequestTest {
private static final String AUTH_TOKEN = "testAuthToken"; private static final String AUTH_TOKEN = "testAuthToken";
private static final String STREAM_ACTIVITY_MONITOR_ID = "testStreamActivityMonitorId"; private static final String STREAM_ACTIVITY_MONITOR_ID = "testStreamActivityMonitorId";
private static final int ADS_LOADER_TIMEOUT_MS = 2; private static final int ADS_LOADER_TIMEOUT_MS = 2;
private static final int FORMAT_DASH = 0;
private static final int FORMAT_HLS = 2;
private static final Map<String, String> adTagParameters = new HashMap<>(); private static final Map<String, String> adTagParameters = new HashMap<>();
static { static {
...@@ -49,9 +51,8 @@ public final class ServerSideAdInsertionStreamRequestTest { ...@@ -49,9 +51,8 @@ public final class ServerSideAdInsertionStreamRequestTest {
} }
@Test @Test
public void build_live_correctUriAndParsing() { public void build_live_correctUriParsing() {
ServerSideAdInsertionStreamRequest.Builder builder = ImaServerSideAdInsertionUriBuilder builder = new ImaServerSideAdInsertionUriBuilder();
new ServerSideAdInsertionStreamRequest.Builder();
builder.setAdsId(ADS_ID); builder.setAdsId(ADS_ID);
builder.setAssetKey(ASSET_KEY); builder.setAssetKey(ASSET_KEY);
builder.setApiKey(API_KEY); builder.setApiKey(API_KEY);
...@@ -59,21 +60,34 @@ public final class ServerSideAdInsertionStreamRequestTest { ...@@ -59,21 +60,34 @@ public final class ServerSideAdInsertionStreamRequestTest {
builder.setContentUrl(CONTENT_URL); builder.setContentUrl(CONTENT_URL);
builder.setAuthToken(AUTH_TOKEN); builder.setAuthToken(AUTH_TOKEN);
builder.setStreamActivityMonitorId(STREAM_ACTIVITY_MONITOR_ID); builder.setStreamActivityMonitorId(STREAM_ACTIVITY_MONITOR_ID);
builder.setFormat(FORMAT_HLS); builder.setFormat(C.TYPE_HLS);
builder.setAdTagParameters(adTagParameters); builder.setAdTagParameters(adTagParameters);
builder.setLoadVideoTimeoutMs(ADS_LOADER_TIMEOUT_MS); builder.setLoadVideoTimeoutMs(ADS_LOADER_TIMEOUT_MS);
ServerSideAdInsertionStreamRequest streamRequest = builder.build(); Uri uri = builder.build();
ServerSideAdInsertionStreamRequest requestAfterConversions = StreamRequest streamRequest = ImaServerSideAdInsertionUriBuilder.createStreamRequest(uri);
ServerSideAdInsertionStreamRequest.fromUri(streamRequest.toUri()); assertThat(streamRequest.getAssetKey()).isEqualTo(ASSET_KEY);
assertThat(streamRequest.getApiKey()).isEqualTo(API_KEY);
assertThat(streamRequest).isEqualTo(requestAfterConversions); assertThat(streamRequest.getManifestSuffix()).isEqualTo(MANIFEST_SUFFIX);
assertThat(streamRequest.getContentUrl()).isEqualTo(CONTENT_URL);
assertThat(streamRequest.getAuthToken()).isEqualTo(AUTH_TOKEN);
assertThat(streamRequest.getStreamActivityMonitorId()).isEqualTo(STREAM_ACTIVITY_MONITOR_ID);
assertThat(streamRequest.getFormat()).isEqualTo(StreamFormat.HLS);
assertThat(streamRequest.getAdTagParameters()).isEqualTo(adTagParameters);
boolean isLive = ImaServerSideAdInsertionUriBuilder.isLiveStream(uri);
assertThat(isLive).isTrue();
String adsId = ImaServerSideAdInsertionUriBuilder.getAdsId(uri);
assertThat(adsId).isEqualTo(ADS_ID);
int loadVideoTimeoutMs = ImaServerSideAdInsertionUriBuilder.getLoadVideoTimeoutMs(uri);
assertThat(loadVideoTimeoutMs).isEqualTo(ADS_LOADER_TIMEOUT_MS);
} }
@Test @Test
public void build_vod_correctUriAndParsing() { public void build_vod_correctUriParsing() {
ServerSideAdInsertionStreamRequest.Builder builder = ImaServerSideAdInsertionUriBuilder builder = new ImaServerSideAdInsertionUriBuilder();
new ServerSideAdInsertionStreamRequest.Builder();
builder.setAdsId(ADS_ID); builder.setAdsId(ADS_ID);
builder.setApiKey(API_KEY); builder.setApiKey(API_KEY);
builder.setContentSourceId(CONTENT_SOURCE_ID); builder.setContentSourceId(CONTENT_SOURCE_ID);
...@@ -82,46 +96,60 @@ public final class ServerSideAdInsertionStreamRequestTest { ...@@ -82,46 +96,60 @@ public final class ServerSideAdInsertionStreamRequestTest {
builder.setContentUrl(CONTENT_URL); builder.setContentUrl(CONTENT_URL);
builder.setAuthToken(AUTH_TOKEN); builder.setAuthToken(AUTH_TOKEN);
builder.setStreamActivityMonitorId(STREAM_ACTIVITY_MONITOR_ID); builder.setStreamActivityMonitorId(STREAM_ACTIVITY_MONITOR_ID);
builder.setFormat(FORMAT_DASH); builder.setFormat(C.TYPE_DASH);
builder.setAdTagParameters(adTagParameters); builder.setAdTagParameters(adTagParameters);
builder.setLoadVideoTimeoutMs(ADS_LOADER_TIMEOUT_MS); builder.setLoadVideoTimeoutMs(ADS_LOADER_TIMEOUT_MS);
ServerSideAdInsertionStreamRequest streamRequest = builder.build(); Uri uri = builder.build();
ServerSideAdInsertionStreamRequest requestAfterConversions = StreamRequest streamRequest = ImaServerSideAdInsertionUriBuilder.createStreamRequest(uri);
ServerSideAdInsertionStreamRequest.fromUri(streamRequest.toUri()); assertThat(streamRequest.getApiKey()).isEqualTo(API_KEY);
assertThat(streamRequest.getContentSourceId()).isEqualTo(CONTENT_SOURCE_ID);
assertThat(requestAfterConversions).isEqualTo(streamRequest); assertThat(streamRequest.getVideoId()).isEqualTo(VIDEO_ID);
assertThat(streamRequest.getManifestSuffix()).isEqualTo(MANIFEST_SUFFIX);
assertThat(streamRequest.getContentUrl()).isEqualTo(CONTENT_URL);
assertThat(streamRequest.getAuthToken()).isEqualTo(AUTH_TOKEN);
assertThat(streamRequest.getStreamActivityMonitorId()).isEqualTo(STREAM_ACTIVITY_MONITOR_ID);
assertThat(streamRequest.getFormat()).isEqualTo(StreamFormat.DASH);
assertThat(streamRequest.getAdTagParameters()).isEqualTo(adTagParameters);
boolean isLive = ImaServerSideAdInsertionUriBuilder.isLiveStream(uri);
assertThat(isLive).isFalse();
String adsId = ImaServerSideAdInsertionUriBuilder.getAdsId(uri);
assertThat(adsId).isEqualTo(ADS_ID);
int loadVideoTimeoutMs = ImaServerSideAdInsertionUriBuilder.getLoadVideoTimeoutMs(uri);
assertThat(loadVideoTimeoutMs).isEqualTo(ADS_LOADER_TIMEOUT_MS);
} }
@Test @Test
public void build_vodWithNoAdsId_usesVideoIdAsDefault() { public void build_vodWithNoAdsId_usesVideoIdAsDefault() {
ServerSideAdInsertionStreamRequest.Builder builder = ImaServerSideAdInsertionUriBuilder builder = new ImaServerSideAdInsertionUriBuilder();
new ServerSideAdInsertionStreamRequest.Builder();
builder.setContentSourceId(CONTENT_SOURCE_ID); builder.setContentSourceId(CONTENT_SOURCE_ID);
builder.setVideoId(VIDEO_ID); builder.setVideoId(VIDEO_ID);
builder.setFormat(C.TYPE_DASH);
ServerSideAdInsertionStreamRequest streamRequest = builder.build(); Uri streamRequest = builder.build();
assertThat(streamRequest.adsId).isEqualTo(VIDEO_ID); assertThat(ImaServerSideAdInsertionUriBuilder.getAdsId(streamRequest)).isEqualTo(VIDEO_ID);
assertThat(streamRequest.toUri().getQueryParameter("adsId")).isEqualTo(VIDEO_ID); assertThat(streamRequest.getQueryParameter("adsId")).isEqualTo(VIDEO_ID);
} }
@Test @Test
public void build_liveWithNoAdsId_usesAssetKeyAsDefault() { public void build_liveWithNoAdsId_usesAssetKeyAsDefault() {
ServerSideAdInsertionStreamRequest.Builder builder = ImaServerSideAdInsertionUriBuilder builder = new ImaServerSideAdInsertionUriBuilder();
new ServerSideAdInsertionStreamRequest.Builder();
builder.setAssetKey(ASSET_KEY); builder.setAssetKey(ASSET_KEY);
builder.setFormat(C.TYPE_DASH);
ServerSideAdInsertionStreamRequest streamRequest = builder.build(); Uri streamRequest = builder.build();
assertThat(streamRequest.adsId).isEqualTo(ASSET_KEY); assertThat(ImaServerSideAdInsertionUriBuilder.getAdsId(streamRequest)).isEqualTo(ASSET_KEY);
assertThat(streamRequest.toUri().getQueryParameter("adsId")).isEqualTo(ASSET_KEY); assertThat(streamRequest.getQueryParameter("adsId")).isEqualTo(ASSET_KEY);
} }
@Test @Test
public void build_assetKeyWithVideoId_throwsIllegalStateException() { public void build_assetKeyWithVideoId_throwsIllegalStateException() {
ServerSideAdInsertionStreamRequest.Builder requestBuilder = ImaServerSideAdInsertionUriBuilder requestBuilder = new ImaServerSideAdInsertionUriBuilder();
new ServerSideAdInsertionStreamRequest.Builder();
requestBuilder.setAssetKey(ASSET_KEY); requestBuilder.setAssetKey(ASSET_KEY);
requestBuilder.setVideoId(VIDEO_ID); requestBuilder.setVideoId(VIDEO_ID);
...@@ -130,8 +158,7 @@ public final class ServerSideAdInsertionStreamRequestTest { ...@@ -130,8 +158,7 @@ public final class ServerSideAdInsertionStreamRequestTest {
@Test @Test
public void build_assetKeyWithContentSource_throwsIllegalStateException() { public void build_assetKeyWithContentSource_throwsIllegalStateException() {
ServerSideAdInsertionStreamRequest.Builder requestBuilder = ImaServerSideAdInsertionUriBuilder requestBuilder = new ImaServerSideAdInsertionUriBuilder();
new ServerSideAdInsertionStreamRequest.Builder();
requestBuilder.setAssetKey(ASSET_KEY); requestBuilder.setAssetKey(ASSET_KEY);
requestBuilder.setContentSourceId(CONTENT_SOURCE_ID); requestBuilder.setContentSourceId(CONTENT_SOURCE_ID);
...@@ -140,9 +167,21 @@ public final class ServerSideAdInsertionStreamRequestTest { ...@@ -140,9 +167,21 @@ public final class ServerSideAdInsertionStreamRequestTest {
@Test @Test
public void build_withoutContentSourceAndVideoIdOrAssetKey_throwsIllegalStateException() { public void build_withoutContentSourceAndVideoIdOrAssetKey_throwsIllegalStateException() {
ServerSideAdInsertionStreamRequest.Builder requestBuilder = ImaServerSideAdInsertionUriBuilder requestBuilder = new ImaServerSideAdInsertionUriBuilder();
new ServerSideAdInsertionStreamRequest.Builder();
Assert.assertThrows(IllegalStateException.class, requestBuilder::build); Assert.assertThrows(IllegalStateException.class, requestBuilder::build);
} }
@Test
public void build_withoutLoadVideoTimeoutMs_usesDefaultTimeout() {
Uri uri =
new ImaServerSideAdInsertionUriBuilder()
.setAssetKey(ASSET_KEY)
.setFormat(C.TYPE_DASH)
.build();
int loadVideoTimeoutMs = ImaServerSideAdInsertionUriBuilder.getLoadVideoTimeoutMs(uri);
assertThat(loadVideoTimeoutMs)
.isEqualTo(ImaServerSideAdInsertionUriBuilder.DEFAULT_LOAD_VIDEO_TIMEOUT_MS);
}
} }
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