Support ad tag with default media source
This is the missing attribute to support all features of the Sample with MediaItem. Hence PlayerActivity can use the setMediaItems() method directly without creating actual media sources in the app code. PiperOrigin-RevId: 307102036
This commit is contained in:
parent
9937744f0e
commit
8ea33e2315
@ -45,10 +45,8 @@ import com.google.android.exoplayer2.mediacodec.MediaCodecUtil.DecoderQueryExcep
|
||||
import com.google.android.exoplayer2.offline.DownloadRequest;
|
||||
import com.google.android.exoplayer2.source.BehindLiveWindowException;
|
||||
import com.google.android.exoplayer2.source.DefaultMediaSourceFactory;
|
||||
import com.google.android.exoplayer2.source.MediaSource;
|
||||
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||
import com.google.android.exoplayer2.source.ads.AdsLoader;
|
||||
import com.google.android.exoplayer2.source.ads.AdsMediaSource;
|
||||
import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection;
|
||||
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
|
||||
import com.google.android.exoplayer2.trackselection.MappingTrackSelector.MappedTrackInfo;
|
||||
@ -60,7 +58,6 @@ import com.google.android.exoplayer2.ui.PlayerControlView;
|
||||
import com.google.android.exoplayer2.ui.PlayerView;
|
||||
import com.google.android.exoplayer2.ui.spherical.SphericalGLSurfaceView;
|
||||
import com.google.android.exoplayer2.upstream.DataSource;
|
||||
import com.google.android.exoplayer2.upstream.HttpDataSource;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
import com.google.android.exoplayer2.util.ErrorMessageProvider;
|
||||
import com.google.android.exoplayer2.util.EventLogger;
|
||||
@ -138,12 +135,11 @@ public class PlayerActivity extends AppCompatActivity
|
||||
|
||||
private DataSource.Factory dataSourceFactory;
|
||||
private SimpleExoPlayer player;
|
||||
private List<MediaSource> mediaSources;
|
||||
private List<MediaItem> mediaItems;
|
||||
private DefaultTrackSelector trackSelector;
|
||||
private DefaultTrackSelector.Parameters trackSelectorParameters;
|
||||
private DebugTextViewHelper debugViewHelper;
|
||||
private TrackGroupArray lastSeenTrackGroupArray;
|
||||
private DefaultMediaSourceFactory mediaSourceFactory;
|
||||
private boolean startAutoPlay;
|
||||
private int startWindow;
|
||||
private long startPosition;
|
||||
@ -164,8 +160,6 @@ public class PlayerActivity extends AppCompatActivity
|
||||
}
|
||||
super.onCreate(savedInstanceState);
|
||||
dataSourceFactory = buildDataSourceFactory();
|
||||
mediaSourceFactory =
|
||||
DefaultMediaSourceFactory.newInstance(/* context= */ this, dataSourceFactory);
|
||||
if (CookieHandler.getDefault() != DEFAULT_COOKIE_MANAGER) {
|
||||
CookieHandler.setDefault(DEFAULT_COOKIE_MANAGER);
|
||||
}
|
||||
@ -328,7 +322,7 @@ public class PlayerActivity extends AppCompatActivity
|
||||
|
||||
@Override
|
||||
public void preparePlayback() {
|
||||
player.retry();
|
||||
player.prepare();
|
||||
}
|
||||
|
||||
// PlaybackControlView.VisibilityListener implementation
|
||||
@ -343,8 +337,8 @@ public class PlayerActivity extends AppCompatActivity
|
||||
private void initializePlayer() {
|
||||
if (player == null) {
|
||||
Intent intent = getIntent();
|
||||
mediaSources = createTopLevelMediaSources(intent);
|
||||
if (mediaSources.isEmpty()) {
|
||||
mediaItems = createMediaItems(intent);
|
||||
if (mediaItems.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
TrackSelection.Factory trackSelectionFactory;
|
||||
@ -370,6 +364,9 @@ public class PlayerActivity extends AppCompatActivity
|
||||
|
||||
player =
|
||||
new SimpleExoPlayer.Builder(/* context= */ this, renderersFactory)
|
||||
.setMediaSourceFactory(
|
||||
new DefaultMediaSourceFactory(
|
||||
/* context= */ this, dataSourceFactory, new AdSupportProvider()))
|
||||
.setTrackSelector(trackSelector)
|
||||
.build();
|
||||
player.addListener(new PlayerEventListener());
|
||||
@ -380,20 +377,17 @@ public class PlayerActivity extends AppCompatActivity
|
||||
playerView.setPlaybackPreparer(this);
|
||||
debugViewHelper = new DebugTextViewHelper(player, debugTextView);
|
||||
debugViewHelper.start();
|
||||
if (adsLoader != null) {
|
||||
adsLoader.setPlayer(player);
|
||||
}
|
||||
}
|
||||
boolean haveStartPosition = startWindow != C.INDEX_UNSET;
|
||||
if (haveStartPosition) {
|
||||
player.seekTo(startWindow, startPosition);
|
||||
}
|
||||
player.setMediaSources(mediaSources, /* resetPosition= */ !haveStartPosition);
|
||||
player.setMediaItems(mediaItems, /* resetPosition= */ !haveStartPosition);
|
||||
player.prepare();
|
||||
updateButtonVisibility();
|
||||
}
|
||||
|
||||
private List<MediaSource> createTopLevelMediaSources(Intent intent) {
|
||||
private List<MediaItem> createMediaItems(Intent intent) {
|
||||
String action = intent.getAction();
|
||||
boolean actionIsListView = ACTION_VIEW_LIST.equals(action);
|
||||
if (!actionIsListView && !ACTION_VIEW.equals(action)) {
|
||||
@ -408,69 +402,14 @@ public class PlayerActivity extends AppCompatActivity
|
||||
? ((Sample.PlaylistSample) intentAsSample).children
|
||||
: new UriSample[] {(UriSample) intentAsSample};
|
||||
|
||||
List<MediaSource> mediaSources = new ArrayList<>();
|
||||
Uri adTagUri = null;
|
||||
List<MediaItem> mediaItems = new ArrayList<>();
|
||||
boolean hasAds = false;
|
||||
for (UriSample sample : samples) {
|
||||
MediaItem mediaItem = sample.toMediaItem();
|
||||
Assertions.checkNotNull(mediaItem.playbackProperties);
|
||||
if (!Util.checkCleartextTrafficPermitted(mediaItem)) {
|
||||
showToast(R.string.error_cleartext_not_permitted);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
if (Util.maybeRequestReadExternalStoragePermission(/* activity= */ this, mediaItem)) {
|
||||
// The player will be reinitialized if the permission is granted.
|
||||
return Collections.emptyList();
|
||||
}
|
||||
MediaSource mediaSource = createLeafMediaSource(mediaItem);
|
||||
if (mediaSource != null) {
|
||||
adTagUri = sample.adTagUri;
|
||||
mediaSources.add(mediaSource);
|
||||
}
|
||||
}
|
||||
|
||||
if (adTagUri == null) {
|
||||
releaseAdsLoader();
|
||||
} else if (mediaSources.size() == 1) {
|
||||
if (!adTagUri.equals(loadedAdTagUri)) {
|
||||
releaseAdsLoader();
|
||||
loadedAdTagUri = adTagUri;
|
||||
}
|
||||
MediaSource adsMediaSource = createAdsMediaSource(mediaSources.get(0), adTagUri);
|
||||
if (adsMediaSource != null) {
|
||||
mediaSources.set(0, adsMediaSource);
|
||||
} else {
|
||||
showToast(R.string.ima_not_loaded);
|
||||
}
|
||||
} else if (mediaSources.size() > 1) {
|
||||
showToast(R.string.unsupported_ads_in_concatenation);
|
||||
releaseAdsLoader();
|
||||
}
|
||||
|
||||
return mediaSources;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private MediaSource createLeafMediaSource(MediaItem mediaItem) {
|
||||
Assertions.checkNotNull(mediaItem.playbackProperties);
|
||||
HttpDataSource.Factory drmDataSourceFactory = null;
|
||||
if (mediaItem.playbackProperties.drmConfiguration != null) {
|
||||
if (Util.SDK_INT < 18) {
|
||||
showToast(R.string.error_drm_unsupported_before_api_18);
|
||||
finish();
|
||||
return null;
|
||||
} else if (!MediaDrm.isCryptoSchemeSupported(
|
||||
mediaItem.playbackProperties.drmConfiguration.uuid)) {
|
||||
showToast(R.string.error_drm_unsupported_scheme);
|
||||
finish();
|
||||
return null;
|
||||
}
|
||||
drmDataSourceFactory = ((DemoApplication) getApplication()).buildHttpDataSourceFactory();
|
||||
}
|
||||
|
||||
DownloadRequest downloadRequest =
|
||||
((DemoApplication) getApplication())
|
||||
.getDownloadTracker()
|
||||
.getDownloadRequest(mediaItem.playbackProperties.sourceUri);
|
||||
.getDownloadRequest(Assertions.checkNotNull(mediaItem.playbackProperties).sourceUri);
|
||||
if (downloadRequest != null) {
|
||||
mediaItem =
|
||||
mediaItem
|
||||
@ -479,9 +418,36 @@ public class PlayerActivity extends AppCompatActivity
|
||||
.setCustomCacheKey(downloadRequest.customCacheKey)
|
||||
.build();
|
||||
}
|
||||
return mediaSourceFactory
|
||||
.setDrmHttpDataSourceFactory(drmDataSourceFactory)
|
||||
.createMediaSource(mediaItem);
|
||||
|
||||
if (!Util.checkCleartextTrafficPermitted(mediaItem)) {
|
||||
showToast(R.string.error_cleartext_not_permitted);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
if (Util.maybeRequestReadExternalStoragePermission(/* activity= */ this, mediaItem)) {
|
||||
// The player will be reinitialized if the permission is granted.
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
MediaItem.DrmConfiguration drmConfiguration =
|
||||
Assertions.checkNotNull(mediaItem.playbackProperties).drmConfiguration;
|
||||
if (drmConfiguration != null) {
|
||||
if (Util.SDK_INT < 18) {
|
||||
showToast(R.string.error_drm_unsupported_before_api_18);
|
||||
finish();
|
||||
return Collections.emptyList();
|
||||
} else if (!MediaDrm.isCryptoSchemeSupported(drmConfiguration.uuid)) {
|
||||
showToast(R.string.error_drm_unsupported_scheme);
|
||||
finish();
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
hasAds |= mediaItem.playbackProperties.adTagUri != null;
|
||||
mediaItems.add(mediaItem);
|
||||
}
|
||||
if (!hasAds) {
|
||||
releaseAdsLoader();
|
||||
}
|
||||
return mediaItems;
|
||||
}
|
||||
|
||||
private void releasePlayer() {
|
||||
@ -492,7 +458,7 @@ public class PlayerActivity extends AppCompatActivity
|
||||
debugViewHelper = null;
|
||||
player.release();
|
||||
player = null;
|
||||
mediaSources = Collections.emptyList();
|
||||
mediaItems = Collections.emptyList();
|
||||
trackSelector = null;
|
||||
}
|
||||
if (adsLoader != null) {
|
||||
@ -534,14 +500,15 @@ public class PlayerActivity extends AppCompatActivity
|
||||
return ((DemoApplication) getApplication()).buildDataSourceFactory();
|
||||
}
|
||||
|
||||
/** Returns an ads media source, reusing the ads loader if one exists. */
|
||||
/**
|
||||
* Returns an ads loader for the Interactive Media Ads SDK if found in the classpath, or null
|
||||
* otherwise.
|
||||
*/
|
||||
@Nullable
|
||||
private MediaSource createAdsMediaSource(MediaSource mediaSource, Uri adTagUri) {
|
||||
private AdsLoader createAdsLoader(Uri adTagUri) {
|
||||
// Load the extension source using reflection so the demo app doesn't have to depend on it.
|
||||
// The ads loader is reused for multiple playbacks, so that ad playback can resume.
|
||||
try {
|
||||
Class<?> loaderClass = Class.forName("com.google.android.exoplayer2.ext.ima.ImaAdsLoader");
|
||||
if (adsLoader == null) {
|
||||
// Full class names used so the lint rule triggers should any of the classes move.
|
||||
// LINT.IfChange
|
||||
Constructor<? extends AdsLoader> loaderConstructor =
|
||||
@ -549,9 +516,7 @@ public class PlayerActivity extends AppCompatActivity
|
||||
.asSubclass(AdsLoader.class)
|
||||
.getConstructor(android.content.Context.class, android.net.Uri.class);
|
||||
// LINT.ThenChange(../../../../../../../../proguard-rules.txt)
|
||||
adsLoader = loaderConstructor.newInstance(this, adTagUri);
|
||||
}
|
||||
return new AdsMediaSource(mediaSource, mediaSourceFactory, adsLoader, playerView);
|
||||
return loaderConstructor.newInstance(this, adTagUri);
|
||||
} catch (ClassNotFoundException e) {
|
||||
// IMA extension not loaded.
|
||||
return null;
|
||||
@ -670,4 +635,36 @@ public class PlayerActivity extends AppCompatActivity
|
||||
return Pair.create(0, errorString);
|
||||
}
|
||||
}
|
||||
|
||||
private class AdSupportProvider implements DefaultMediaSourceFactory.AdSupportProvider {
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public AdsLoader getAdsLoader(Uri adTagUri) {
|
||||
if (mediaItems.size() > 1) {
|
||||
showToast(R.string.unsupported_ads_in_concatenation);
|
||||
releaseAdsLoader();
|
||||
return null;
|
||||
}
|
||||
if (!adTagUri.equals(loadedAdTagUri)) {
|
||||
releaseAdsLoader();
|
||||
loadedAdTagUri = adTagUri;
|
||||
}
|
||||
if (adsLoader == null) {
|
||||
// The ads loader is reused for multiple playbacks, so that ad playback can resume.
|
||||
adsLoader = createAdsLoader(adTagUri);
|
||||
if (adsLoader != null) {
|
||||
adsLoader.setPlayer(player);
|
||||
} else {
|
||||
showToast(R.string.ima_not_loaded);
|
||||
}
|
||||
}
|
||||
return adsLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdsLoader.AdViewProvider getAdViewProvider() {
|
||||
return Assertions.checkNotNull(playerView);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -146,8 +146,11 @@ import java.util.UUID;
|
||||
}
|
||||
|
||||
public MediaItem toMediaItem() {
|
||||
MediaItem.Builder builder = new MediaItem.Builder().setSourceUri(uri);
|
||||
builder.setMimeType(inferAdaptiveStreamMimeType(uri, extension));
|
||||
MediaItem.Builder builder =
|
||||
new MediaItem.Builder()
|
||||
.setSourceUri(uri)
|
||||
.setMimeType(inferAdaptiveStreamMimeType(uri, extension))
|
||||
.setAdTagUri(adTagUri);
|
||||
if (drmInfo != null) {
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
if (drmInfo.drmKeyRequestProperties != null) {
|
||||
|
@ -71,6 +71,7 @@ public final class MediaItem {
|
||||
private List<StreamKey> streamKeys;
|
||||
@Nullable private String customCacheKey;
|
||||
private List<Subtitle> subtitles;
|
||||
@Nullable private Uri adTagUri;
|
||||
@Nullable private Object tag;
|
||||
@Nullable private MediaMetadata mediaMetadata;
|
||||
|
||||
@ -88,12 +89,13 @@ public final class MediaItem {
|
||||
clipEndPositionMs = mediaItem.clippingProperties.endPositionMs;
|
||||
clipRelativeToLiveWindow = mediaItem.clippingProperties.relativeToLiveWindow;
|
||||
clipRelativeToDefaultPosition = mediaItem.clippingProperties.relativeToDefaultPosition;
|
||||
clipStartsAtKeyFrame = mediaItem.clippingProperties.startsAtKeyFrame;
|
||||
clipStartPositionMs = mediaItem.clippingProperties.startPositionMs;
|
||||
clipStartsAtKeyFrame = mediaItem.clippingProperties.startsAtKeyFrame;
|
||||
mediaId = mediaItem.mediaId;
|
||||
mediaMetadata = mediaItem.mediaMetadata;
|
||||
@Nullable PlaybackProperties playbackProperties = mediaItem.playbackProperties;
|
||||
if (playbackProperties != null) {
|
||||
adTagUri = playbackProperties.adTagUri;
|
||||
customCacheKey = playbackProperties.customCacheKey;
|
||||
mimeType = playbackProperties.mimeType;
|
||||
sourceUri = playbackProperties.sourceUri;
|
||||
@ -353,6 +355,28 @@ public final class MediaItem {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the optional ad tag URI.
|
||||
*
|
||||
* <p>If a {@link PlaybackProperties#sourceUri} is set, the ad tag URI is used to create a
|
||||
* {@link PlaybackProperties} object. Otherwise it will be ignored.
|
||||
*/
|
||||
public Builder setAdTagUri(@Nullable String adTagUri) {
|
||||
this.adTagUri = adTagUri != null ? Uri.parse(adTagUri) : null;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the optional ad tag {@link Uri}.
|
||||
*
|
||||
* <p>If a {@link PlaybackProperties#sourceUri} is set, the ad tag URI is used to create a
|
||||
* {@link PlaybackProperties} object. Otherwise it will be ignored.
|
||||
*/
|
||||
public Builder setAdTagUri(@Nullable Uri adTagUri) {
|
||||
this.adTagUri = adTagUri;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the optional tag for custom attributes. The tag for the media source which will be
|
||||
* published in the {@code com.google.android.exoplayer2.Timeline} of the source as {@code
|
||||
@ -395,6 +419,7 @@ public final class MediaItem {
|
||||
streamKeys,
|
||||
customCacheKey,
|
||||
subtitles,
|
||||
adTagUri,
|
||||
tag);
|
||||
mediaId = mediaId != null ? mediaId : sourceUri.toString();
|
||||
}
|
||||
@ -509,6 +534,9 @@ public final class MediaItem {
|
||||
/** Optional subtitles to be sideloaded. */
|
||||
public final List<Subtitle> subtitles;
|
||||
|
||||
/** Optional ad tag {@link Uri}. */
|
||||
@Nullable public final Uri adTagUri;
|
||||
|
||||
/**
|
||||
* Optional tag for custom attributes. The tag for the media source which will be published in
|
||||
* the {@code com.google.android.exoplayer2.Timeline} of the source as {@code
|
||||
@ -523,6 +551,7 @@ public final class MediaItem {
|
||||
List<StreamKey> streamKeys,
|
||||
@Nullable String customCacheKey,
|
||||
List<Subtitle> subtitles,
|
||||
@Nullable Uri adTagUri,
|
||||
@Nullable Object tag) {
|
||||
this.sourceUri = sourceUri;
|
||||
this.mimeType = mimeType;
|
||||
@ -530,6 +559,7 @@ public final class MediaItem {
|
||||
this.streamKeys = streamKeys;
|
||||
this.customCacheKey = customCacheKey;
|
||||
this.subtitles = subtitles;
|
||||
this.adTagUri = adTagUri;
|
||||
this.tag = tag;
|
||||
}
|
||||
|
||||
@ -549,6 +579,7 @@ public final class MediaItem {
|
||||
&& streamKeys.equals(other.streamKeys)
|
||||
&& Util.areEqual(customCacheKey, other.customCacheKey)
|
||||
&& subtitles.equals(other.subtitles)
|
||||
&& Util.areEqual(adTagUri, other.adTagUri)
|
||||
&& Util.areEqual(tag, other.tag);
|
||||
}
|
||||
|
||||
@ -560,6 +591,7 @@ public final class MediaItem {
|
||||
result = 31 * result + streamKeys.hashCode();
|
||||
result = 31 * result + (customCacheKey == null ? 0 : customCacheKey.hashCode());
|
||||
result = 31 * result + subtitles.hashCode();
|
||||
result = 31 * result + (adTagUri == null ? 0 : adTagUri.hashCode());
|
||||
result = 31 * result + (tag == null ? 0 : tag.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
@ -266,6 +266,16 @@ public class MediaItemTest {
|
||||
assertThat(mediaItem.clippingProperties.startsAtKeyFrame).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void builderSetAdTagUri_setsAdTagUri() {
|
||||
Uri adTagUri = Uri.parse(URI_STRING + "/ad");
|
||||
|
||||
MediaItem mediaItem =
|
||||
new MediaItem.Builder().setSourceUri(URI_STRING).setAdTagUri(adTagUri).build();
|
||||
|
||||
assertThat(mediaItem.playbackProperties.adTagUri).isEqualTo(adTagUri);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void builderSetMediaMetadata_setsMetadata() {
|
||||
MediaMetadata mediaMetadata = new MediaMetadata.Builder().setTitle("title").build();
|
||||
@ -280,6 +290,7 @@ public class MediaItemTest {
|
||||
public void buildUpon_equalsToOriginal() {
|
||||
MediaItem mediaItem =
|
||||
new MediaItem.Builder()
|
||||
.setAdTagUri(URI_STRING)
|
||||
.setClipEndPositionMs(1000)
|
||||
.setClipRelativeToDefaultPosition(true)
|
||||
.setClipRelativeToLiveWindow(true)
|
||||
|
@ -15,6 +15,8 @@
|
||||
*/
|
||||
package com.google.android.exoplayer2;
|
||||
|
||||
import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
@ -28,6 +30,7 @@ import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
|
||||
import com.google.android.exoplayer2.source.MediaSourceFactory;
|
||||
import com.google.android.exoplayer2.source.ShuffleOrder;
|
||||
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||
import com.google.android.exoplayer2.source.ads.AdsMediaSource;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelection;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelector;
|
||||
@ -84,6 +87,7 @@ import java.util.concurrent.TimeoutException;
|
||||
private SeekParameters seekParameters;
|
||||
private ShuffleOrder shuffleOrder;
|
||||
private boolean pauseAtEndOfMediaItems;
|
||||
private boolean hasAdsMediaSource;
|
||||
|
||||
// Playback information when there is no pending seek/set source operation.
|
||||
private PlaybackInfo playbackInfo;
|
||||
@ -123,8 +127,8 @@ import java.util.concurrent.TimeoutException;
|
||||
Log.i(TAG, "Init " + Integer.toHexString(System.identityHashCode(this)) + " ["
|
||||
+ ExoPlayerLibraryInfo.VERSION_SLASHY + "] [" + Util.DEVICE_DEBUG_INFO + "]");
|
||||
Assertions.checkState(renderers.length > 0);
|
||||
this.renderers = Assertions.checkNotNull(renderers);
|
||||
this.trackSelector = Assertions.checkNotNull(trackSelector);
|
||||
this.renderers = checkNotNull(renderers);
|
||||
this.trackSelector = checkNotNull(trackSelector);
|
||||
this.mediaSourceFactory = mediaSourceFactory;
|
||||
this.useLazyPreparation = useLazyPreparation;
|
||||
repeatMode = Player.REPEAT_MODE_OFF;
|
||||
@ -397,9 +401,7 @@ import java.util.concurrent.TimeoutException;
|
||||
@Override
|
||||
public void addMediaSources(int index, List<MediaSource> mediaSources) {
|
||||
Assertions.checkArgument(index >= 0);
|
||||
for (int i = 0; i < mediaSources.size(); i++) {
|
||||
Assertions.checkArgument(mediaSources.get(i) != null);
|
||||
}
|
||||
validateMediaSources(mediaSources, /* mediaSourceReplacement= */ false);
|
||||
int currentWindowIndex = getCurrentWindowIndex();
|
||||
long currentPositionMs = getCurrentPosition();
|
||||
Timeline oldTimeline = getCurrentTimeline();
|
||||
@ -973,9 +975,7 @@ import java.util.concurrent.TimeoutException;
|
||||
int startWindowIndex,
|
||||
long startPositionMs,
|
||||
boolean resetToDefaultPosition) {
|
||||
for (int i = 0; i < mediaSources.size(); i++) {
|
||||
Assertions.checkArgument(mediaSources.get(i) != null);
|
||||
}
|
||||
validateMediaSources(mediaSources, /* mediaSourceReplacement= */ true);
|
||||
int currentWindowIndex = getCurrentWindowIndexInternal();
|
||||
long currentPositionMs = getCurrentPosition();
|
||||
pendingOperationAcks++;
|
||||
@ -1076,9 +1076,42 @@ import java.util.concurrent.TimeoutException;
|
||||
removed.add(mediaSourceHolders.remove(i));
|
||||
}
|
||||
shuffleOrder = shuffleOrder.cloneAndRemove(fromIndex, toIndexExclusive);
|
||||
if (mediaSourceHolders.isEmpty()) {
|
||||
hasAdsMediaSource = false;
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates media sources before any modification of the existing list of media sources is made.
|
||||
* This way we can throw an exception before changing the state of the player in case of a
|
||||
* validation failure.
|
||||
*
|
||||
* @param mediaSources The media sources to set or add.
|
||||
* @param mediaSourceReplacement Whether the given media sources will replace existing ones.
|
||||
*/
|
||||
private void validateMediaSources(
|
||||
List<MediaSource> mediaSources, boolean mediaSourceReplacement) {
|
||||
if (hasAdsMediaSource && !mediaSourceReplacement && !mediaSources.isEmpty()) {
|
||||
// Adding media sources to an ads media source is not allowed
|
||||
// (see https://github.com/google/ExoPlayer/issues/3750).
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
int sizeAfterModification =
|
||||
mediaSources.size() + (mediaSourceReplacement ? 0 : mediaSourceHolders.size());
|
||||
for (int i = 0; i < mediaSources.size(); i++) {
|
||||
MediaSource mediaSource = checkNotNull(mediaSources.get(i));
|
||||
if (mediaSource instanceof AdsMediaSource) {
|
||||
if (sizeAfterModification > 1) {
|
||||
// Ads media sources only allowed with a single source
|
||||
// (see https://github.com/google/ExoPlayer/issues/3750).
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
hasAdsMediaSource = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private PlaybackInfo maskTimeline() {
|
||||
return playbackInfo.copyWithTimeline(
|
||||
mediaSourceHolders.isEmpty()
|
||||
|
@ -29,6 +29,8 @@ import com.google.android.exoplayer2.drm.FrameworkMediaDrm;
|
||||
import com.google.android.exoplayer2.drm.HttpMediaDrmCallback;
|
||||
import com.google.android.exoplayer2.drm.MediaDrmCallback;
|
||||
import com.google.android.exoplayer2.offline.StreamKey;
|
||||
import com.google.android.exoplayer2.source.ads.AdsLoader;
|
||||
import com.google.android.exoplayer2.source.ads.AdsMediaSource;
|
||||
import com.google.android.exoplayer2.upstream.DataSource;
|
||||
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
|
||||
import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory;
|
||||
@ -36,6 +38,7 @@ import com.google.android.exoplayer2.upstream.DefaultLoadErrorHandlingPolicy;
|
||||
import com.google.android.exoplayer2.upstream.HttpDataSource;
|
||||
import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
import com.google.android.exoplayer2.util.Log;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
import java.util.Arrays;
|
||||
@ -89,9 +92,36 @@ import java.util.Map;
|
||||
* alternative dummy, apps can pass a drm session manager to {@link
|
||||
* #setDrmSessionManager(DrmSessionManager)} which will be used for all items without a drm
|
||||
* configuration.
|
||||
*
|
||||
* <h3>Ad support for media items with ad tag uri</h3>
|
||||
*
|
||||
* <p>For a media item with an ad tag uri an {@link AdSupportProvider} needs to be passed to the
|
||||
* constructor {@link #DefaultMediaSourceFactory(Context, DataSource.Factory, AdSupportProvider)}.
|
||||
*/
|
||||
public final class DefaultMediaSourceFactory implements MediaSourceFactory {
|
||||
|
||||
/**
|
||||
* Provides {@link AdsLoader ads loaders} and an {@link AdsLoader.AdViewProvider} to created
|
||||
* {@link AdsMediaSource AdsMediaSources}.
|
||||
*/
|
||||
public interface AdSupportProvider {
|
||||
|
||||
/**
|
||||
* Returns an {@link AdsLoader} for the given {@link Uri ad tag uri} or null if no ads loader is
|
||||
* available for the given ad tag uri.
|
||||
*
|
||||
* <p>This method is called for each media item for which a media source is created.
|
||||
*/
|
||||
@Nullable
|
||||
AdsLoader getAdsLoader(Uri adTagUri);
|
||||
|
||||
/**
|
||||
* Returns an {@link AdsLoader.AdViewProvider} which is used to create {@link AdsMediaSource
|
||||
* AdsMediaSources}.
|
||||
*/
|
||||
AdsLoader.AdViewProvider getAdViewProvider();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance with the given {@link Context}.
|
||||
*
|
||||
@ -115,10 +145,13 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory {
|
||||
*/
|
||||
public static DefaultMediaSourceFactory newInstance(
|
||||
Context context, DataSource.Factory dataSourceFactory) {
|
||||
return new DefaultMediaSourceFactory(context, dataSourceFactory);
|
||||
return new DefaultMediaSourceFactory(context, dataSourceFactory, /* adSupportProvider= */ null);
|
||||
}
|
||||
|
||||
private static final String TAG = "DefaultMediaSourceFactory";
|
||||
|
||||
private final DataSource.Factory dataSourceFactory;
|
||||
@Nullable private final AdSupportProvider adSupportProvider;
|
||||
private final SparseArray<MediaSourceFactory> mediaSourceFactories;
|
||||
@C.ContentType private final int[] supportedTypes;
|
||||
private final String userAgent;
|
||||
@ -127,8 +160,20 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory {
|
||||
private HttpDataSource.Factory drmHttpDataSourceFactory;
|
||||
@Nullable private List<StreamKey> streamKeys;
|
||||
|
||||
private DefaultMediaSourceFactory(Context context, DataSource.Factory dataSourceFactory) {
|
||||
/**
|
||||
* Creates a new instance with the given {@link Context} and {@link DataSource.Factory}.
|
||||
*
|
||||
* @param context The {@link Context}.
|
||||
* @param dataSourceFactory A {@link DataSource.Factory} to be used to create media sources.
|
||||
* @param adSupportProvider An {@link AdSupportProvider} to get ads loaders and ad view providers
|
||||
* to be used to create {@link AdsMediaSource AdsMediaSources}.
|
||||
*/
|
||||
public DefaultMediaSourceFactory(
|
||||
Context context,
|
||||
DataSource.Factory dataSourceFactory,
|
||||
@Nullable AdSupportProvider adSupportProvider) {
|
||||
this.dataSourceFactory = dataSourceFactory;
|
||||
this.adSupportProvider = adSupportProvider;
|
||||
drmSessionManager = DrmSessionManager.getDummyDrmSessionManager();
|
||||
userAgent = Util.getUserAgent(context, ExoPlayerLibraryInfo.VERSION_SLASHY);
|
||||
drmHttpDataSourceFactory = new DefaultHttpDataSourceFactory(userAgent);
|
||||
@ -214,15 +259,12 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory {
|
||||
? mediaItem.playbackProperties.streamKeys
|
||||
: streamKeys);
|
||||
|
||||
MediaSource leafMediaSource = mediaSourceFactory.createMediaSource(mediaItem);
|
||||
MediaSource mediaSource = mediaSourceFactory.createMediaSource(mediaItem);
|
||||
|
||||
List<MediaItem.Subtitle> subtitles = mediaItem.playbackProperties.subtitles;
|
||||
if (subtitles.isEmpty()) {
|
||||
return maybeClipMediaSource(mediaItem, leafMediaSource);
|
||||
}
|
||||
|
||||
if (!subtitles.isEmpty()) {
|
||||
MediaSource[] mediaSources = new MediaSource[subtitles.size() + 1];
|
||||
mediaSources[0] = leafMediaSource;
|
||||
mediaSources[0] = mediaSource;
|
||||
SingleSampleMediaSource.Factory singleSampleSourceFactory =
|
||||
new SingleSampleMediaSource.Factory(dataSourceFactory);
|
||||
for (int i = 0; i < subtitles.size(); i++) {
|
||||
@ -237,7 +279,9 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory {
|
||||
singleSampleSourceFactory.createMediaSource(
|
||||
subtitle.uri, subtitleFormat, /* durationUs= */ C.TIME_UNSET);
|
||||
}
|
||||
return maybeClipMediaSource(mediaItem, new MergingMediaSource(mediaSources));
|
||||
mediaSource = new MergingMediaSource(mediaSources);
|
||||
}
|
||||
return maybeWrapWithAdsMediaSource(mediaItem, maybeClipMediaSource(mediaItem, mediaSource));
|
||||
}
|
||||
|
||||
// internal methods
|
||||
@ -285,6 +329,34 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory {
|
||||
mediaItem.clippingProperties.relativeToDefaultPosition);
|
||||
}
|
||||
|
||||
private MediaSource maybeWrapWithAdsMediaSource(MediaItem mediaItem, MediaSource mediaSource) {
|
||||
Assertions.checkNotNull(mediaItem.playbackProperties);
|
||||
if (mediaItem.playbackProperties.adTagUri == null) {
|
||||
return mediaSource;
|
||||
}
|
||||
if (adSupportProvider == null) {
|
||||
Log.w(
|
||||
TAG,
|
||||
"Playing media without ads. Pass an AdsSupportProvider to the constructor for supporting"
|
||||
+ " media items with an ad tag uri.");
|
||||
return mediaSource;
|
||||
}
|
||||
AdsLoader adsLoader = adSupportProvider.getAdsLoader(mediaItem.playbackProperties.adTagUri);
|
||||
if (adsLoader == null) {
|
||||
Log.w(
|
||||
TAG,
|
||||
String.format(
|
||||
"Playing media without ads. No AdsLoader for media item with mediaId '%s'.",
|
||||
mediaItem.mediaId));
|
||||
return mediaSource;
|
||||
}
|
||||
return new AdsMediaSource(
|
||||
mediaSource,
|
||||
/* adMediaSourceFactory= */ this,
|
||||
adsLoader,
|
||||
adSupportProvider.getAdViewProvider());
|
||||
}
|
||||
|
||||
private static SparseArray<MediaSourceFactory> loadDelegates(
|
||||
DataSource.Factory dataSourceFactory) {
|
||||
SparseArray<MediaSourceFactory> factories = new SparseArray<>();
|
||||
|
@ -27,6 +27,8 @@ import android.graphics.SurfaceTexture;
|
||||
import android.media.AudioManager;
|
||||
import android.os.Looper;
|
||||
import android.view.Surface;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
@ -48,6 +50,8 @@ import com.google.android.exoplayer2.source.SampleStream;
|
||||
import com.google.android.exoplayer2.source.TrackGroup;
|
||||
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||
import com.google.android.exoplayer2.source.ads.AdPlaybackState;
|
||||
import com.google.android.exoplayer2.source.ads.AdsLoader;
|
||||
import com.google.android.exoplayer2.source.ads.AdsMediaSource;
|
||||
import com.google.android.exoplayer2.testutil.ActionSchedule;
|
||||
import com.google.android.exoplayer2.testutil.ActionSchedule.PlayerRunnable;
|
||||
import com.google.android.exoplayer2.testutil.ActionSchedule.PlayerTarget;
|
||||
@ -72,10 +76,12 @@ import com.google.android.exoplayer2.trackselection.TrackSelection;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
|
||||
import com.google.android.exoplayer2.upstream.Allocation;
|
||||
import com.google.android.exoplayer2.upstream.Allocator;
|
||||
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
|
||||
import com.google.android.exoplayer2.upstream.Loader;
|
||||
import com.google.android.exoplayer2.upstream.TransferListener;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
import com.google.android.exoplayer2.util.Clock;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@ -4378,6 +4384,121 @@ public final class ExoPlayerTest {
|
||||
assertThat(positionAfterSetShuffleOrder.get()).isAtLeast(5000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setMediaSources_secondAdMediaSource_throws() throws Exception {
|
||||
AdsMediaSource adsMediaSource =
|
||||
new AdsMediaSource(
|
||||
new FakeMediaSource(new FakeTimeline(/* windowCount= */ 1)),
|
||||
new DefaultDataSourceFactory(
|
||||
context, Util.getUserAgent(context, ExoPlayerLibraryInfo.VERSION_SLASHY)),
|
||||
new DummyAdsLoader(),
|
||||
new DummyAdViewProvider());
|
||||
Exception[] exception = {null};
|
||||
ActionSchedule actionSchedule =
|
||||
new ActionSchedule.Builder(TAG)
|
||||
.executeRunnable(
|
||||
new PlayerRunnable() {
|
||||
@Override
|
||||
public void run(SimpleExoPlayer player) {
|
||||
try {
|
||||
player.setMediaSource(adsMediaSource);
|
||||
player.addMediaSource(adsMediaSource);
|
||||
} catch (Exception e) {
|
||||
exception[0] = e;
|
||||
}
|
||||
player.prepare();
|
||||
}
|
||||
})
|
||||
.build();
|
||||
|
||||
new ExoPlayerTestRunner.Builder(context)
|
||||
.setActionSchedule(actionSchedule)
|
||||
.build()
|
||||
.start(/* doPrepare= */ false)
|
||||
.blockUntilActionScheduleFinished(TIMEOUT_MS)
|
||||
.blockUntilEnded(TIMEOUT_MS);
|
||||
|
||||
assertThat(exception[0]).isInstanceOf(IllegalStateException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setMediaSources_multipleMediaSourcesWithAd_throws() throws Exception {
|
||||
MediaSource mediaSource = new FakeMediaSource(new FakeTimeline(/* windowCount= */ 1));
|
||||
AdsMediaSource adsMediaSource =
|
||||
new AdsMediaSource(
|
||||
mediaSource,
|
||||
new DefaultDataSourceFactory(
|
||||
context, Util.getUserAgent(context, ExoPlayerLibraryInfo.VERSION_SLASHY)),
|
||||
new DummyAdsLoader(),
|
||||
new DummyAdViewProvider());
|
||||
final Exception[] exception = {null};
|
||||
ActionSchedule actionSchedule =
|
||||
new ActionSchedule.Builder(TAG)
|
||||
.executeRunnable(
|
||||
new PlayerRunnable() {
|
||||
@Override
|
||||
public void run(SimpleExoPlayer player) {
|
||||
try {
|
||||
List<MediaSource> sources = new ArrayList<>();
|
||||
sources.add(mediaSource);
|
||||
sources.add(adsMediaSource);
|
||||
player.setMediaSources(sources);
|
||||
} catch (Exception e) {
|
||||
exception[0] = e;
|
||||
}
|
||||
player.prepare();
|
||||
}
|
||||
})
|
||||
.build();
|
||||
|
||||
new ExoPlayerTestRunner.Builder(context)
|
||||
.setActionSchedule(actionSchedule)
|
||||
.build()
|
||||
.start(/* doPrepare= */ false)
|
||||
.blockUntilActionScheduleFinished(TIMEOUT_MS)
|
||||
.blockUntilEnded(TIMEOUT_MS);
|
||||
|
||||
assertThat(exception[0]).isInstanceOf(IllegalArgumentException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setMediaSources_addingMediaSourcesWithAdToNonEmptyPlaylist_throws() throws Exception {
|
||||
MediaSource mediaSource = new FakeMediaSource(new FakeTimeline(/* windowCount= */ 1));
|
||||
AdsMediaSource adsMediaSource =
|
||||
new AdsMediaSource(
|
||||
mediaSource,
|
||||
new DefaultDataSourceFactory(
|
||||
context, Util.getUserAgent(context, ExoPlayerLibraryInfo.VERSION_SLASHY)),
|
||||
new DummyAdsLoader(),
|
||||
new DummyAdViewProvider());
|
||||
final Exception[] exception = {null};
|
||||
ActionSchedule actionSchedule =
|
||||
new ActionSchedule.Builder(TAG)
|
||||
.waitForPlaybackState(Player.STATE_READY)
|
||||
.executeRunnable(
|
||||
new PlayerRunnable() {
|
||||
@Override
|
||||
public void run(SimpleExoPlayer player) {
|
||||
try {
|
||||
player.addMediaSource(adsMediaSource);
|
||||
} catch (Exception e) {
|
||||
exception[0] = e;
|
||||
}
|
||||
}
|
||||
})
|
||||
.build();
|
||||
|
||||
new ExoPlayerTestRunner.Builder(context)
|
||||
.setMediaSources(mediaSource)
|
||||
.setActionSchedule(actionSchedule)
|
||||
.build()
|
||||
.start()
|
||||
.blockUntilActionScheduleFinished(TIMEOUT_MS)
|
||||
.blockUntilEnded(TIMEOUT_MS);
|
||||
|
||||
assertThat(exception[0]).isInstanceOf(IllegalArgumentException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setMediaSources_empty_whenEmpty_correctMaskingWindowIndex() throws Exception {
|
||||
Timeline secondTimeline = new FakeTimeline(/* windowCount= */ 1);
|
||||
@ -6493,4 +6614,38 @@ public final class ExoPlayerTest {
|
||||
return Loader.RETRY;
|
||||
}
|
||||
}
|
||||
|
||||
private static class DummyAdsLoader implements AdsLoader {
|
||||
|
||||
@Override
|
||||
public void setPlayer(@Nullable Player player) {}
|
||||
|
||||
@Override
|
||||
public void release() {}
|
||||
|
||||
@Override
|
||||
public void setSupportedContentTypes(int... contentTypes) {}
|
||||
|
||||
@Override
|
||||
public void start(AdsLoader.EventListener eventListener, AdViewProvider adViewProvider) {}
|
||||
|
||||
@Override
|
||||
public void stop() {}
|
||||
|
||||
@Override
|
||||
public void handlePrepareError(int adGroupIndex, int adIndexInAdGroup, IOException exception) {}
|
||||
}
|
||||
|
||||
private static class DummyAdViewProvider implements AdsLoader.AdViewProvider {
|
||||
|
||||
@Override
|
||||
public ViewGroup getAdViewGroup() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View[] getAdOverlayViews() {
|
||||
return new View[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,12 +16,18 @@
|
||||
package com.google.android.exoplayer2.source;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.MediaItem;
|
||||
import com.google.android.exoplayer2.source.ads.AdsLoader;
|
||||
import com.google.android.exoplayer2.source.ads.AdsMediaSource;
|
||||
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
@ -184,4 +190,65 @@ public final class DefaultMediaSourceFactoryTest {
|
||||
|
||||
assertThat(supportedTypes).asList().containsExactly(C.TYPE_OTHER);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createMediaSource_withAdTagUri_callsAdsLoader() {
|
||||
Context applicationContext = ApplicationProvider.getApplicationContext();
|
||||
Uri adTagUri = Uri.parse(URI_MEDIA);
|
||||
MediaItem mediaItem =
|
||||
new MediaItem.Builder().setSourceUri(URI_MEDIA).setAdTagUri(adTagUri).build();
|
||||
DefaultMediaSourceFactory defaultMediaSourceFactory =
|
||||
new DefaultMediaSourceFactory(
|
||||
applicationContext,
|
||||
new DefaultDataSourceFactory(applicationContext, "userAgent"),
|
||||
createAdSupportProvider(mock(AdsLoader.class), mock(AdsLoader.AdViewProvider.class)));
|
||||
|
||||
MediaSource mediaSource = defaultMediaSourceFactory.createMediaSource(mediaItem);
|
||||
|
||||
assertThat(mediaSource).isInstanceOf(AdsMediaSource.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createMediaSource_withAdTagUriAdsLoaderNull_playsWithoutAdNoException() {
|
||||
Context applicationContext = ApplicationProvider.getApplicationContext();
|
||||
MediaItem mediaItem =
|
||||
new MediaItem.Builder().setSourceUri(URI_MEDIA).setAdTagUri(Uri.parse(URI_MEDIA)).build();
|
||||
DefaultMediaSourceFactory defaultMediaSourceFactory =
|
||||
new DefaultMediaSourceFactory(
|
||||
applicationContext,
|
||||
new DefaultDataSourceFactory(applicationContext, "userAgent"),
|
||||
createAdSupportProvider(/* adsLoader= */ null, mock(AdsLoader.AdViewProvider.class)));
|
||||
|
||||
MediaSource mediaSource = defaultMediaSourceFactory.createMediaSource(mediaItem);
|
||||
|
||||
assertThat(mediaSource).isNotInstanceOf(AdsMediaSource.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createMediaSource_withAdTagUriProvidersNull_playsWithoutAdNoException() {
|
||||
Context applicationContext = ApplicationProvider.getApplicationContext();
|
||||
MediaItem mediaItem =
|
||||
new MediaItem.Builder().setSourceUri(URI_MEDIA).setAdTagUri(Uri.parse(URI_MEDIA)).build();
|
||||
|
||||
MediaSource mediaSource =
|
||||
DefaultMediaSourceFactory.newInstance(applicationContext).createMediaSource(mediaItem);
|
||||
|
||||
assertThat(mediaSource).isNotInstanceOf(AdsMediaSource.class);
|
||||
}
|
||||
|
||||
private static DefaultMediaSourceFactory.AdSupportProvider createAdSupportProvider(
|
||||
@Nullable AdsLoader adsLoader, AdsLoader.AdViewProvider adViewProvider) {
|
||||
return new DefaultMediaSourceFactory.AdSupportProvider() {
|
||||
@Nullable
|
||||
@Override
|
||||
public AdsLoader getAdsLoader(Uri adTagUri) {
|
||||
return adsLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdsLoader.AdViewProvider getAdViewProvider() {
|
||||
return adViewProvider;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user