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
This commit is contained in:
parent
eb6e25b6fc
commit
cd3bef24b5
@ -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
|
|||||||
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
|
|||||||
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
|
|||||||
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
|
|||||||
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
|
|||||||
|
|
||||||
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
|
|||||||
|
|
||||||
@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
|
|||||||
|
|
||||||
@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
|
|||||||
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
|
|||||||
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
|
|||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,373 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package androidx.media3.exoplayer.ima;
|
||||||
|
|
||||||
|
import static androidx.media3.common.util.Assertions.checkArgument;
|
||||||
|
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||||
|
import static androidx.media3.common.util.Assertions.checkState;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.media3.common.C;
|
||||||
|
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.StreamRequest;
|
||||||
|
import com.google.ads.interactivemedia.v3.api.StreamRequest.StreamFormat;
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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. */
|
||||||
|
public static final int DEFAULT_LOAD_VIDEO_TIMEOUT_MS = 10_000;
|
||||||
|
|
||||||
|
private static final String IMA_AUTHORITY = "dai.google.com";
|
||||||
|
private static final String ADS_ID = "adsId";
|
||||||
|
private static final String ASSET_KEY = "assetKey";
|
||||||
|
private static final String API_KEY = "apiKey";
|
||||||
|
private static final String CONTENT_SOURCE_ID = "contentSourceId";
|
||||||
|
private static final String VIDEO_ID = "videoId";
|
||||||
|
private static final String AD_TAG_PARAMETERS = "adTagParameters";
|
||||||
|
private static final String MANIFEST_SUFFIX = "manifestSuffix";
|
||||||
|
private static final String CONTENT_URL = "contentUrl";
|
||||||
|
private static final String AUTH_TOKEN = "authToken";
|
||||||
|
private static final String STREAM_ACTIVITY_MONITOR_ID = "streamActivityMonitorId";
|
||||||
|
private static final String FORMAT = "format";
|
||||||
|
private static final String LOAD_VIDEO_TIMEOUT_MS = "loadVideoTimeoutMs";
|
||||||
|
|
||||||
|
@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;
|
||||||
|
private int loadVideoTimeoutMs;
|
||||||
|
|
||||||
|
/** Creates a new instance. */
|
||||||
|
public ImaServerSideAdInsertionUriBuilder() {
|
||||||
|
adTagParameters = ImmutableMap.of();
|
||||||
|
loadVideoTimeoutMs = DEFAULT_LOAD_VIDEO_TIMEOUT_MS;
|
||||||
|
format = C.TYPE_OTHER;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 ImaServerSideAdInsertionUriBuilder 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 ImaServerSideAdInsertionUriBuilder 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 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;
|
||||||
|
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;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
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 ImaServerSideAdInsertionUriBuilder 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 ImaServerSideAdInsertionUriBuilder 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 ImaServerSideAdInsertionUriBuilder 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 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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();
|
||||||
|
dataUriBuilder.scheme(C.SSAI_SCHEME);
|
||||||
|
dataUriBuilder.authority(IMA_AUTHORITY);
|
||||||
|
dataUriBuilder.appendQueryParameter(ADS_ID, adsId);
|
||||||
|
if (loadVideoTimeoutMs != DEFAULT_LOAD_VIDEO_TIMEOUT_MS) {
|
||||||
|
dataUriBuilder.appendQueryParameter(
|
||||||
|
LOAD_VIDEO_TIMEOUT_MS, String.valueOf(loadVideoTimeoutMs));
|
||||||
|
}
|
||||||
|
if (assetKey != null) {
|
||||||
|
dataUriBuilder.appendQueryParameter(ASSET_KEY, assetKey);
|
||||||
|
}
|
||||||
|
if (apiKey != null) {
|
||||||
|
dataUriBuilder.appendQueryParameter(API_KEY, apiKey);
|
||||||
|
}
|
||||||
|
if (contentSourceId != null) {
|
||||||
|
dataUriBuilder.appendQueryParameter(CONTENT_SOURCE_ID, contentSourceId);
|
||||||
|
}
|
||||||
|
if (videoId != null) {
|
||||||
|
dataUriBuilder.appendQueryParameter(VIDEO_ID, videoId);
|
||||||
|
}
|
||||||
|
if (manifestSuffix != null) {
|
||||||
|
dataUriBuilder.appendQueryParameter(MANIFEST_SUFFIX, manifestSuffix);
|
||||||
|
}
|
||||||
|
if (contentUrl != null) {
|
||||||
|
dataUriBuilder.appendQueryParameter(CONTENT_URL, contentUrl);
|
||||||
|
}
|
||||||
|
if (authToken != null) {
|
||||||
|
dataUriBuilder.appendQueryParameter(AUTH_TOKEN, authToken);
|
||||||
|
}
|
||||||
|
if (streamActivityMonitorId != null) {
|
||||||
|
dataUriBuilder.appendQueryParameter(STREAM_ACTIVITY_MONITOR_ID, streamActivityMonitorId);
|
||||||
|
}
|
||||||
|
if (!adTagParameters.isEmpty()) {
|
||||||
|
Uri.Builder adTagParametersUriBuilder = new Uri.Builder();
|
||||||
|
for (Map.Entry<String, String> entry : adTagParameters.entrySet()) {
|
||||||
|
adTagParametersUriBuilder.appendQueryParameter(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
dataUriBuilder.appendQueryParameter(
|
||||||
|
AD_TAG_PARAMETERS, adTagParametersUriBuilder.build().toString());
|
||||||
|
}
|
||||||
|
dataUriBuilder.appendQueryParameter(FORMAT, String.valueOf(format));
|
||||||
|
return dataUriBuilder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns whether the provided request is for a live stream or false if it is a VOD stream. */
|
||||||
|
/* package */ static boolean isLiveStream(Uri uri) {
|
||||||
|
return !TextUtils.isEmpty(uri.getQueryParameter(ASSET_KEY));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the opaque adsId for this stream. */
|
||||||
|
/* package */ static String getAdsId(Uri uri) {
|
||||||
|
return checkNotNull(uri.getQueryParameter(ADS_ID));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the video load timeout in milliseconds. */
|
||||||
|
/* package */ static int getLoadVideoTimeoutMs(Uri uri) {
|
||||||
|
@Nullable String adsLoaderTimeoutUs = uri.getQueryParameter(LOAD_VIDEO_TIMEOUT_MS);
|
||||||
|
return TextUtils.isEmpty(adsLoaderTimeoutUs)
|
||||||
|
? DEFAULT_LOAD_VIDEO_TIMEOUT_MS
|
||||||
|
: Integer.parseInt(adsLoaderTimeoutUs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the corresponding {@link StreamRequest}. */
|
||||||
|
@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())) {
|
||||||
|
throw new IllegalArgumentException("Invalid URI scheme or authority.");
|
||||||
|
}
|
||||||
|
StreamRequest streamRequest;
|
||||||
|
// Required params.
|
||||||
|
@Nullable String assetKey = uri.getQueryParameter(ASSET_KEY);
|
||||||
|
@Nullable String apiKey = uri.getQueryParameter(API_KEY);
|
||||||
|
@Nullable String contentSourceId = uri.getQueryParameter(CONTENT_SOURCE_ID);
|
||||||
|
@Nullable String videoId = uri.getQueryParameter(VIDEO_ID);
|
||||||
|
if (!TextUtils.isEmpty(assetKey)) {
|
||||||
|
streamRequest = ImaSdkFactory.getInstance().createLiveStreamRequest(assetKey, apiKey);
|
||||||
|
} else {
|
||||||
|
streamRequest =
|
||||||
|
ImaSdkFactory.getInstance()
|
||||||
|
.createVodStreamRequest(checkNotNull(contentSourceId), checkNotNull(videoId), apiKey);
|
||||||
|
}
|
||||||
|
int format = Integer.parseInt(uri.getQueryParameter(FORMAT));
|
||||||
|
if (format == C.TYPE_DASH) {
|
||||||
|
streamRequest.setFormat(StreamFormat.DASH);
|
||||||
|
} else if (format == C.TYPE_HLS) {
|
||||||
|
streamRequest.setFormat(StreamFormat.HLS);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Unsupported stream format:" + format);
|
||||||
|
}
|
||||||
|
// Optional params.
|
||||||
|
@Nullable String adTagParametersValue = uri.getQueryParameter(AD_TAG_PARAMETERS);
|
||||||
|
if (!TextUtils.isEmpty(adTagParametersValue)) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
streamRequest.setAdTagParameters(adTagParameters);
|
||||||
|
}
|
||||||
|
@Nullable String manifestSuffix = uri.getQueryParameter(MANIFEST_SUFFIX);
|
||||||
|
if (manifestSuffix != null) {
|
||||||
|
streamRequest.setManifestSuffix(manifestSuffix);
|
||||||
|
}
|
||||||
|
@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;
|
||||||
|
}
|
||||||
|
}
|
@ -1,477 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2021 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package androidx.media3.exoplayer.ima;
|
|
||||||
|
|
||||||
import static androidx.media3.common.util.Assertions.checkArgument;
|
|
||||||
import static androidx.media3.common.util.Assertions.checkNotNull;
|
|
||||||
import static androidx.media3.common.util.Assertions.checkState;
|
|
||||||
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.media3.common.C;
|
|
||||||
import androidx.media3.common.C.ContentType;
|
|
||||||
import com.google.ads.interactivemedia.v3.api.ImaSdkFactory;
|
|
||||||
import com.google.ads.interactivemedia.v3.api.StreamRequest;
|
|
||||||
import com.google.ads.interactivemedia.v3.api.StreamRequest.StreamFormat;
|
|
||||||
import com.google.common.base.Objects;
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/** Stream request data for an IMA DAI stream. */
|
|
||||||
/* package */ final class ServerSideAdInsertionStreamRequest {
|
|
||||||
|
|
||||||
/** The default timeout for loading the video URI, in milliseconds. */
|
|
||||||
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 ADS_ID = "adsId";
|
|
||||||
private static final String ASSET_KEY = "assetKey";
|
|
||||||
private static final String API_KEY = "apiKey";
|
|
||||||
private static final String CONTENT_SOURCE_ID = "contentSourceId";
|
|
||||||
private static final String VIDEO_ID = "videoId";
|
|
||||||
private static final String AD_TAG_PARAMETERS = "adTagParameters";
|
|
||||||
private static final String MANIFEST_SUFFIX = "manifestSuffix";
|
|
||||||
private static final String CONTENT_URL = "contentUrl";
|
|
||||||
private static final String AUTH_TOKEN = "authToken";
|
|
||||||
private static final String STREAM_ACTIVITY_MONITOR_ID = "streamActivityMonitorId";
|
|
||||||
private static final String FORMAT = "format";
|
|
||||||
private static final String LOAD_VIDEO_TIMEOUT_MS = "loadVideoTimeoutMs";
|
|
||||||
|
|
||||||
public final String adsId;
|
|
||||||
@Nullable public final String assetKey;
|
|
||||||
@Nullable public final String apiKey;
|
|
||||||
@Nullable public final String contentSourceId;
|
|
||||||
@Nullable public final String videoId;
|
|
||||||
public final ImmutableMap<String, String> adTagParameters;
|
|
||||||
@Nullable public final String manifestSuffix;
|
|
||||||
@Nullable public final String contentUrl;
|
|
||||||
@Nullable public final String authToken;
|
|
||||||
@Nullable public final String streamActivityMonitorId;
|
|
||||||
public @ContentType int format = C.TYPE_HLS;
|
|
||||||
public final int loadVideoTimeoutMs;
|
|
||||||
|
|
||||||
private ServerSideAdInsertionStreamRequest(
|
|
||||||
String adsId,
|
|
||||||
@Nullable String assetKey,
|
|
||||||
@Nullable String apiKey,
|
|
||||||
@Nullable String contentSourceId,
|
|
||||||
@Nullable String videoId,
|
|
||||||
ImmutableMap<String, String> adTagParameters,
|
|
||||||
@Nullable String manifestSuffix,
|
|
||||||
@Nullable String contentUrl,
|
|
||||||
@Nullable String authToken,
|
|
||||||
@Nullable String streamActivityMonitorId,
|
|
||||||
@ContentType int format,
|
|
||||||
int loadVideoTimeoutMs) {
|
|
||||||
this.adsId = adsId;
|
|
||||||
this.assetKey = assetKey;
|
|
||||||
this.apiKey = apiKey;
|
|
||||||
this.contentSourceId = contentSourceId;
|
|
||||||
this.videoId = videoId;
|
|
||||||
this.adTagParameters = adTagParameters;
|
|
||||||
this.manifestSuffix = manifestSuffix;
|
|
||||||
this.contentUrl = contentUrl;
|
|
||||||
this.authToken = authToken;
|
|
||||||
this.streamActivityMonitorId = streamActivityMonitorId;
|
|
||||||
this.format = format;
|
|
||||||
this.loadVideoTimeoutMs = loadVideoTimeoutMs;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns whether this request is for a live stream or false if it is a VOD stream. */
|
|
||||||
public boolean isLiveStream() {
|
|
||||||
return !TextUtils.isEmpty(assetKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns the corresponding {@link StreamRequest}. */
|
|
||||||
@SuppressWarnings("nullness") // Required for making nullness test pass for library_with_ima_sdk.
|
|
||||||
public StreamRequest getStreamRequest() {
|
|
||||||
StreamRequest streamRequest;
|
|
||||||
if (!TextUtils.isEmpty(assetKey)) {
|
|
||||||
streamRequest = ImaSdkFactory.getInstance().createLiveStreamRequest(assetKey, apiKey);
|
|
||||||
} else {
|
|
||||||
streamRequest =
|
|
||||||
ImaSdkFactory.getInstance()
|
|
||||||
.createVodStreamRequest(checkNotNull(contentSourceId), checkNotNull(videoId), apiKey);
|
|
||||||
}
|
|
||||||
if (format == C.TYPE_DASH) {
|
|
||||||
streamRequest.setFormat(StreamFormat.DASH);
|
|
||||||
} else if (format == C.TYPE_HLS) {
|
|
||||||
streamRequest.setFormat(StreamFormat.HLS);
|
|
||||||
}
|
|
||||||
// Optional params.
|
|
||||||
streamRequest.setAdTagParameters(adTagParameters);
|
|
||||||
if (manifestSuffix != null) {
|
|
||||||
streamRequest.setManifestSuffix(manifestSuffix);
|
|
||||||
}
|
|
||||||
if (contentUrl != null) {
|
|
||||||
streamRequest.setContentUrl(contentUrl);
|
|
||||||
}
|
|
||||||
if (authToken != null) {
|
|
||||||
streamRequest.setAuthToken(authToken);
|
|
||||||
}
|
|
||||||
if (streamActivityMonitorId != null) {
|
|
||||||
streamRequest.setStreamActivityMonitorId(streamActivityMonitorId);
|
|
||||||
}
|
|
||||||
return streamRequest;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns a corresponding {@link Uri}. */
|
|
||||||
public Uri toUri() {
|
|
||||||
Uri.Builder dataUriBuilder = new Uri.Builder();
|
|
||||||
dataUriBuilder.scheme(C.SSAI_SCHEME);
|
|
||||||
dataUriBuilder.authority(IMA_AUTHORITY);
|
|
||||||
dataUriBuilder.appendQueryParameter(ADS_ID, adsId);
|
|
||||||
if (loadVideoTimeoutMs != DEFAULT_LOAD_VIDEO_TIMEOUT_MS) {
|
|
||||||
dataUriBuilder.appendQueryParameter(
|
|
||||||
LOAD_VIDEO_TIMEOUT_MS, String.valueOf(loadVideoTimeoutMs));
|
|
||||||
}
|
|
||||||
if (assetKey != null) {
|
|
||||||
dataUriBuilder.appendQueryParameter(ASSET_KEY, assetKey);
|
|
||||||
}
|
|
||||||
if (apiKey != null) {
|
|
||||||
dataUriBuilder.appendQueryParameter(API_KEY, apiKey);
|
|
||||||
}
|
|
||||||
if (contentSourceId != null) {
|
|
||||||
dataUriBuilder.appendQueryParameter(CONTENT_SOURCE_ID, contentSourceId);
|
|
||||||
}
|
|
||||||
if (videoId != null) {
|
|
||||||
dataUriBuilder.appendQueryParameter(VIDEO_ID, videoId);
|
|
||||||
}
|
|
||||||
if (manifestSuffix != null) {
|
|
||||||
dataUriBuilder.appendQueryParameter(MANIFEST_SUFFIX, manifestSuffix);
|
|
||||||
}
|
|
||||||
if (contentUrl != null) {
|
|
||||||
dataUriBuilder.appendQueryParameter(CONTENT_URL, contentUrl);
|
|
||||||
}
|
|
||||||
if (authToken != null) {
|
|
||||||
dataUriBuilder.appendQueryParameter(AUTH_TOKEN, authToken);
|
|
||||||
}
|
|
||||||
if (streamActivityMonitorId != null) {
|
|
||||||
dataUriBuilder.appendQueryParameter(STREAM_ACTIVITY_MONITOR_ID, streamActivityMonitorId);
|
|
||||||
}
|
|
||||||
if (!adTagParameters.isEmpty()) {
|
|
||||||
Uri.Builder adTagParametersUriBuilder = new Uri.Builder();
|
|
||||||
for (Map.Entry<String, String> entry : adTagParameters.entrySet()) {
|
|
||||||
adTagParametersUriBuilder.appendQueryParameter(entry.getKey(), entry.getValue());
|
|
||||||
}
|
|
||||||
dataUriBuilder.appendQueryParameter(
|
|
||||||
AD_TAG_PARAMETERS, adTagParametersUriBuilder.build().toString());
|
|
||||||
}
|
|
||||||
dataUriBuilder.appendQueryParameter(FORMAT, String.valueOf(format));
|
|
||||||
return dataUriBuilder.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(@Nullable Object o) {
|
|
||||||
if (this == o) {
|
|
||||||
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
|
|
||||||
public int hashCode() {
|
|
||||||
return Objects.hashCode(
|
|
||||||
adsId,
|
|
||||||
assetKey,
|
|
||||||
apiKey,
|
|
||||||
contentSourceId,
|
|
||||||
videoId,
|
|
||||||
adTagParameters,
|
|
||||||
manifestSuffix,
|
|
||||||
contentUrl,
|
|
||||||
authToken,
|
|
||||||
streamActivityMonitorId,
|
|
||||||
loadVideoTimeoutMs,
|
|
||||||
format);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a {@link ServerSideAdInsertionStreamRequest} for the given URI.
|
|
||||||
*
|
|
||||||
* @param uri The URI.
|
|
||||||
* @return An {@link ServerSideAdInsertionStreamRequest} for the given URI.
|
|
||||||
* @throws IllegalStateException If uri has missing or invalid inputs.
|
|
||||||
*/
|
|
||||||
public static ServerSideAdInsertionStreamRequest fromUri(Uri uri) {
|
|
||||||
ServerSideAdInsertionStreamRequest.Builder request =
|
|
||||||
new ServerSideAdInsertionStreamRequest.Builder();
|
|
||||||
if (!C.SSAI_SCHEME.equals(uri.getScheme()) || !IMA_AUTHORITY.equals(uri.getAuthority())) {
|
|
||||||
throw new IllegalArgumentException("Invalid URI scheme or authority.");
|
|
||||||
}
|
|
||||||
request.setAdsId(checkNotNull(uri.getQueryParameter(ADS_ID)));
|
|
||||||
request.setAssetKey(uri.getQueryParameter(ASSET_KEY));
|
|
||||||
request.setApiKey(uri.getQueryParameter(API_KEY));
|
|
||||||
request.setContentSourceId(uri.getQueryParameter(CONTENT_SOURCE_ID));
|
|
||||||
request.setVideoId(uri.getQueryParameter(VIDEO_ID));
|
|
||||||
request.setManifestSuffix(uri.getQueryParameter(MANIFEST_SUFFIX));
|
|
||||||
request.setContentUrl(uri.getQueryParameter(CONTENT_URL));
|
|
||||||
request.setAuthToken(uri.getQueryParameter(AUTH_TOKEN));
|
|
||||||
request.setStreamActivityMonitorId(uri.getQueryParameter(STREAM_ACTIVITY_MONITOR_ID));
|
|
||||||
String adsLoaderTimeoutUs = uri.getQueryParameter(LOAD_VIDEO_TIMEOUT_MS);
|
|
||||||
request.setLoadVideoTimeoutMs(
|
|
||||||
TextUtils.isEmpty(adsLoaderTimeoutUs)
|
|
||||||
? 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;
|
|
||||||
String adTagParametersValue;
|
|
||||||
String singleAdTagParameterValue;
|
|
||||||
if (uri.getQueryParameter(AD_TAG_PARAMETERS) != null) {
|
|
||||||
adTagParameters = new HashMap<>();
|
|
||||||
adTagParametersValue = uri.getQueryParameter(AD_TAG_PARAMETERS);
|
|
||||||
if (!TextUtils.isEmpty(adTagParametersValue)) {
|
|
||||||
Uri adTagParametersUri = Uri.parse(adTagParametersValue);
|
|
||||||
for (String paramName : adTagParametersUri.getQueryParameterNames()) {
|
|
||||||
singleAdTagParameterValue = adTagParametersUri.getQueryParameter(paramName);
|
|
||||||
if (!TextUtils.isEmpty(singleAdTagParameterValue)) {
|
|
||||||
adTagParameters.put(paramName, singleAdTagParameterValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
request.setAdTagParameters(adTagParameters);
|
|
||||||
}
|
|
||||||
return request.build();
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,187 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package androidx.media3.exoplayer.ima;
|
||||||
|
|
||||||
|
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 com.google.ads.interactivemedia.v3.api.StreamRequest;
|
||||||
|
import com.google.ads.interactivemedia.v3.api.StreamRequest.StreamFormat;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
/** Unit tests for {@link ImaServerSideAdInsertionUriBuilder}. */
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public final class ImaServerSideAdInsertionUriBuilderTest {
|
||||||
|
|
||||||
|
private static final String ADS_ID = "testAdsId";
|
||||||
|
private static final String ASSET_KEY = "testAssetKey";
|
||||||
|
private static final String API_KEY = "testApiKey";
|
||||||
|
private static final String CONTENT_SOURCE_ID = "testContentSourceId";
|
||||||
|
private static final String VIDEO_ID = "testVideoId";
|
||||||
|
private static final String MANIFEST_SUFFIX = "testManifestSuffix";
|
||||||
|
private static final String CONTENT_URL =
|
||||||
|
"http://google.com/contentUrl?queryParamName=queryParamValue";
|
||||||
|
private static final String AUTH_TOKEN = "testAuthToken";
|
||||||
|
private static final String STREAM_ACTIVITY_MONITOR_ID = "testStreamActivityMonitorId";
|
||||||
|
private static final int ADS_LOADER_TIMEOUT_MS = 2;
|
||||||
|
private static final Map<String, String> adTagParameters = new HashMap<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
adTagParameters.put("param1", "value1");
|
||||||
|
adTagParameters.put("param2", "value2");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void build_live_correctUriParsing() {
|
||||||
|
ImaServerSideAdInsertionUriBuilder builder = new ImaServerSideAdInsertionUriBuilder();
|
||||||
|
builder.setAdsId(ADS_ID);
|
||||||
|
builder.setAssetKey(ASSET_KEY);
|
||||||
|
builder.setApiKey(API_KEY);
|
||||||
|
builder.setManifestSuffix(MANIFEST_SUFFIX);
|
||||||
|
builder.setContentUrl(CONTENT_URL);
|
||||||
|
builder.setAuthToken(AUTH_TOKEN);
|
||||||
|
builder.setStreamActivityMonitorId(STREAM_ACTIVITY_MONITOR_ID);
|
||||||
|
builder.setFormat(C.TYPE_HLS);
|
||||||
|
builder.setAdTagParameters(adTagParameters);
|
||||||
|
builder.setLoadVideoTimeoutMs(ADS_LOADER_TIMEOUT_MS);
|
||||||
|
Uri uri = builder.build();
|
||||||
|
|
||||||
|
StreamRequest streamRequest = ImaServerSideAdInsertionUriBuilder.createStreamRequest(uri);
|
||||||
|
assertThat(streamRequest.getAssetKey()).isEqualTo(ASSET_KEY);
|
||||||
|
assertThat(streamRequest.getApiKey()).isEqualTo(API_KEY);
|
||||||
|
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
|
||||||
|
public void build_vod_correctUriParsing() {
|
||||||
|
ImaServerSideAdInsertionUriBuilder builder = new ImaServerSideAdInsertionUriBuilder();
|
||||||
|
builder.setAdsId(ADS_ID);
|
||||||
|
builder.setApiKey(API_KEY);
|
||||||
|
builder.setContentSourceId(CONTENT_SOURCE_ID);
|
||||||
|
builder.setVideoId(VIDEO_ID);
|
||||||
|
builder.setManifestSuffix(MANIFEST_SUFFIX);
|
||||||
|
builder.setContentUrl(CONTENT_URL);
|
||||||
|
builder.setAuthToken(AUTH_TOKEN);
|
||||||
|
builder.setStreamActivityMonitorId(STREAM_ACTIVITY_MONITOR_ID);
|
||||||
|
builder.setFormat(C.TYPE_DASH);
|
||||||
|
builder.setAdTagParameters(adTagParameters);
|
||||||
|
builder.setLoadVideoTimeoutMs(ADS_LOADER_TIMEOUT_MS);
|
||||||
|
Uri uri = builder.build();
|
||||||
|
|
||||||
|
StreamRequest streamRequest = ImaServerSideAdInsertionUriBuilder.createStreamRequest(uri);
|
||||||
|
assertThat(streamRequest.getApiKey()).isEqualTo(API_KEY);
|
||||||
|
assertThat(streamRequest.getContentSourceId()).isEqualTo(CONTENT_SOURCE_ID);
|
||||||
|
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
|
||||||
|
public void build_vodWithNoAdsId_usesVideoIdAsDefault() {
|
||||||
|
ImaServerSideAdInsertionUriBuilder builder = new ImaServerSideAdInsertionUriBuilder();
|
||||||
|
builder.setContentSourceId(CONTENT_SOURCE_ID);
|
||||||
|
builder.setVideoId(VIDEO_ID);
|
||||||
|
builder.setFormat(C.TYPE_DASH);
|
||||||
|
|
||||||
|
Uri streamRequest = builder.build();
|
||||||
|
|
||||||
|
assertThat(ImaServerSideAdInsertionUriBuilder.getAdsId(streamRequest)).isEqualTo(VIDEO_ID);
|
||||||
|
assertThat(streamRequest.getQueryParameter("adsId")).isEqualTo(VIDEO_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void build_liveWithNoAdsId_usesAssetKeyAsDefault() {
|
||||||
|
ImaServerSideAdInsertionUriBuilder builder = new ImaServerSideAdInsertionUriBuilder();
|
||||||
|
builder.setAssetKey(ASSET_KEY);
|
||||||
|
builder.setFormat(C.TYPE_DASH);
|
||||||
|
|
||||||
|
Uri streamRequest = builder.build();
|
||||||
|
|
||||||
|
assertThat(ImaServerSideAdInsertionUriBuilder.getAdsId(streamRequest)).isEqualTo(ASSET_KEY);
|
||||||
|
assertThat(streamRequest.getQueryParameter("adsId")).isEqualTo(ASSET_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void build_assetKeyWithVideoId_throwsIllegalStateException() {
|
||||||
|
ImaServerSideAdInsertionUriBuilder requestBuilder = new ImaServerSideAdInsertionUriBuilder();
|
||||||
|
requestBuilder.setAssetKey(ASSET_KEY);
|
||||||
|
requestBuilder.setVideoId(VIDEO_ID);
|
||||||
|
|
||||||
|
Assert.assertThrows(IllegalStateException.class, requestBuilder::build);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void build_assetKeyWithContentSource_throwsIllegalStateException() {
|
||||||
|
ImaServerSideAdInsertionUriBuilder requestBuilder = new ImaServerSideAdInsertionUriBuilder();
|
||||||
|
requestBuilder.setAssetKey(ASSET_KEY);
|
||||||
|
requestBuilder.setContentSourceId(CONTENT_SOURCE_ID);
|
||||||
|
|
||||||
|
Assert.assertThrows(IllegalStateException.class, requestBuilder::build);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void build_withoutContentSourceAndVideoIdOrAssetKey_throwsIllegalStateException() {
|
||||||
|
ImaServerSideAdInsertionUriBuilder requestBuilder = new ImaServerSideAdInsertionUriBuilder();
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
@ -1,148 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2021 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package androidx.media3.exoplayer.ima;
|
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
|
||||||
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import org.junit.Assert;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
|
|
||||||
/** Unit tests for {@link ServerSideAdInsertionStreamRequest}. */
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
|
||||||
public final class ServerSideAdInsertionStreamRequestTest {
|
|
||||||
|
|
||||||
private static final String ADS_ID = "testAdsId";
|
|
||||||
private static final String ASSET_KEY = "testAssetKey";
|
|
||||||
private static final String API_KEY = "testApiKey";
|
|
||||||
private static final String CONTENT_SOURCE_ID = "testContentSourceId";
|
|
||||||
private static final String VIDEO_ID = "testVideoId";
|
|
||||||
private static final String MANIFEST_SUFFIX = "testManifestSuffix";
|
|
||||||
private static final String CONTENT_URL =
|
|
||||||
"http://google.com/contentUrl?queryParamName=queryParamValue";
|
|
||||||
private static final String AUTH_TOKEN = "testAuthToken";
|
|
||||||
private static final String STREAM_ACTIVITY_MONITOR_ID = "testStreamActivityMonitorId";
|
|
||||||
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<>();
|
|
||||||
|
|
||||||
static {
|
|
||||||
adTagParameters.put("param1", "value1");
|
|
||||||
adTagParameters.put("param2", "value2");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void build_live_correctUriAndParsing() {
|
|
||||||
ServerSideAdInsertionStreamRequest.Builder builder =
|
|
||||||
new ServerSideAdInsertionStreamRequest.Builder();
|
|
||||||
builder.setAdsId(ADS_ID);
|
|
||||||
builder.setAssetKey(ASSET_KEY);
|
|
||||||
builder.setApiKey(API_KEY);
|
|
||||||
builder.setManifestSuffix(MANIFEST_SUFFIX);
|
|
||||||
builder.setContentUrl(CONTENT_URL);
|
|
||||||
builder.setAuthToken(AUTH_TOKEN);
|
|
||||||
builder.setStreamActivityMonitorId(STREAM_ACTIVITY_MONITOR_ID);
|
|
||||||
builder.setFormat(FORMAT_HLS);
|
|
||||||
builder.setAdTagParameters(adTagParameters);
|
|
||||||
builder.setLoadVideoTimeoutMs(ADS_LOADER_TIMEOUT_MS);
|
|
||||||
ServerSideAdInsertionStreamRequest streamRequest = builder.build();
|
|
||||||
|
|
||||||
ServerSideAdInsertionStreamRequest requestAfterConversions =
|
|
||||||
ServerSideAdInsertionStreamRequest.fromUri(streamRequest.toUri());
|
|
||||||
|
|
||||||
assertThat(streamRequest).isEqualTo(requestAfterConversions);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void build_vod_correctUriAndParsing() {
|
|
||||||
ServerSideAdInsertionStreamRequest.Builder builder =
|
|
||||||
new ServerSideAdInsertionStreamRequest.Builder();
|
|
||||||
builder.setAdsId(ADS_ID);
|
|
||||||
builder.setApiKey(API_KEY);
|
|
||||||
builder.setContentSourceId(CONTENT_SOURCE_ID);
|
|
||||||
builder.setVideoId(VIDEO_ID);
|
|
||||||
builder.setManifestSuffix(MANIFEST_SUFFIX);
|
|
||||||
builder.setContentUrl(CONTENT_URL);
|
|
||||||
builder.setAuthToken(AUTH_TOKEN);
|
|
||||||
builder.setStreamActivityMonitorId(STREAM_ACTIVITY_MONITOR_ID);
|
|
||||||
builder.setFormat(FORMAT_DASH);
|
|
||||||
builder.setAdTagParameters(adTagParameters);
|
|
||||||
builder.setLoadVideoTimeoutMs(ADS_LOADER_TIMEOUT_MS);
|
|
||||||
ServerSideAdInsertionStreamRequest streamRequest = builder.build();
|
|
||||||
|
|
||||||
ServerSideAdInsertionStreamRequest requestAfterConversions =
|
|
||||||
ServerSideAdInsertionStreamRequest.fromUri(streamRequest.toUri());
|
|
||||||
|
|
||||||
assertThat(requestAfterConversions).isEqualTo(streamRequest);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void build_vodWithNoAdsId_usesVideoIdAsDefault() {
|
|
||||||
ServerSideAdInsertionStreamRequest.Builder builder =
|
|
||||||
new ServerSideAdInsertionStreamRequest.Builder();
|
|
||||||
builder.setContentSourceId(CONTENT_SOURCE_ID);
|
|
||||||
builder.setVideoId(VIDEO_ID);
|
|
||||||
|
|
||||||
ServerSideAdInsertionStreamRequest streamRequest = builder.build();
|
|
||||||
|
|
||||||
assertThat(streamRequest.adsId).isEqualTo(VIDEO_ID);
|
|
||||||
assertThat(streamRequest.toUri().getQueryParameter("adsId")).isEqualTo(VIDEO_ID);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void build_liveWithNoAdsId_usesAssetKeyAsDefault() {
|
|
||||||
ServerSideAdInsertionStreamRequest.Builder builder =
|
|
||||||
new ServerSideAdInsertionStreamRequest.Builder();
|
|
||||||
builder.setAssetKey(ASSET_KEY);
|
|
||||||
|
|
||||||
ServerSideAdInsertionStreamRequest streamRequest = builder.build();
|
|
||||||
|
|
||||||
assertThat(streamRequest.adsId).isEqualTo(ASSET_KEY);
|
|
||||||
assertThat(streamRequest.toUri().getQueryParameter("adsId")).isEqualTo(ASSET_KEY);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void build_assetKeyWithVideoId_throwsIllegalStateException() {
|
|
||||||
ServerSideAdInsertionStreamRequest.Builder requestBuilder =
|
|
||||||
new ServerSideAdInsertionStreamRequest.Builder();
|
|
||||||
requestBuilder.setAssetKey(ASSET_KEY);
|
|
||||||
requestBuilder.setVideoId(VIDEO_ID);
|
|
||||||
|
|
||||||
Assert.assertThrows(IllegalStateException.class, requestBuilder::build);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void build_assetKeyWithContentSource_throwsIllegalStateException() {
|
|
||||||
ServerSideAdInsertionStreamRequest.Builder requestBuilder =
|
|
||||||
new ServerSideAdInsertionStreamRequest.Builder();
|
|
||||||
requestBuilder.setAssetKey(ASSET_KEY);
|
|
||||||
requestBuilder.setContentSourceId(CONTENT_SOURCE_ID);
|
|
||||||
|
|
||||||
Assert.assertThrows(IllegalStateException.class, requestBuilder::build);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void build_withoutContentSourceAndVideoIdOrAssetKey_throwsIllegalStateException() {
|
|
||||||
ServerSideAdInsertionStreamRequest.Builder requestBuilder =
|
|
||||||
new ServerSideAdInsertionStreamRequest.Builder();
|
|
||||||
|
|
||||||
Assert.assertThrows(IllegalStateException.class, requestBuilder::build);
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user