Allow skipping the ad before the start position

PiperOrigin-RevId: 315867160
This commit is contained in:
andrewlewis 2020-06-11 11:17:43 +01:00 committed by Oliver Woodman
parent e1beb1d194
commit e111f850d0
3 changed files with 260 additions and 35 deletions

View File

@ -205,6 +205,7 @@
([#6922](https://github.com/google/ExoPlayer/pull/6922)). ([#6922](https://github.com/google/ExoPlayer/pull/6922)).
* Cast extension: Implement playlist API and deprecate the old queue * Cast extension: Implement playlist API and deprecate the old queue
manipulation API. manipulation API.
* IMA extension: Add option to skip ads before the start position.
* Demo app: Retain previous position in list of samples. * Demo app: Retain previous position in list of samples.
* Add Guava dependency. * Add Guava dependency.

View File

@ -123,6 +123,7 @@ public final class ImaAdsLoader
private int mediaLoadTimeoutMs; private int mediaLoadTimeoutMs;
private int mediaBitrate; private int mediaBitrate;
private boolean focusSkipButtonWhenAvailable; private boolean focusSkipButtonWhenAvailable;
private boolean playAdBeforeStartPosition;
private ImaFactory imaFactory; private ImaFactory imaFactory;
/** /**
@ -137,6 +138,7 @@ public final class ImaAdsLoader
mediaLoadTimeoutMs = TIMEOUT_UNSET; mediaLoadTimeoutMs = TIMEOUT_UNSET;
mediaBitrate = BITRATE_UNSET; mediaBitrate = BITRATE_UNSET;
focusSkipButtonWhenAvailable = true; focusSkipButtonWhenAvailable = true;
playAdBeforeStartPosition = true;
imaFactory = new DefaultImaFactory(); imaFactory = new DefaultImaFactory();
} }
@ -250,6 +252,21 @@ public final class ImaAdsLoader
return this; return this;
} }
/**
* Sets whether to play an ad before the start position when beginning playback. If {@code
* true}, an ad will be played if there is one at or before the start position. If {@code
* false}, an ad will be played only if there is one exactly at the start position. The default
* setting is {@code true}.
*
* @param playAdBeforeStartPosition Whether to play an ad before the start position when
* beginning playback.
* @return This builder, for convenience.
*/
public Builder setPlayAdBeforeStartPosition(boolean playAdBeforeStartPosition) {
this.playAdBeforeStartPosition = playAdBeforeStartPosition;
return this;
}
@VisibleForTesting @VisibleForTesting
/* package */ Builder setImaFactory(ImaFactory imaFactory) { /* package */ Builder setImaFactory(ImaFactory imaFactory) {
this.imaFactory = Assertions.checkNotNull(imaFactory); this.imaFactory = Assertions.checkNotNull(imaFactory);
@ -275,6 +292,7 @@ public final class ImaAdsLoader
mediaLoadTimeoutMs, mediaLoadTimeoutMs,
mediaBitrate, mediaBitrate,
focusSkipButtonWhenAvailable, focusSkipButtonWhenAvailable,
playAdBeforeStartPosition,
adUiElements, adUiElements,
adEventListener, adEventListener,
imaFactory); imaFactory);
@ -298,6 +316,7 @@ public final class ImaAdsLoader
mediaLoadTimeoutMs, mediaLoadTimeoutMs,
mediaBitrate, mediaBitrate,
focusSkipButtonWhenAvailable, focusSkipButtonWhenAvailable,
playAdBeforeStartPosition,
adUiElements, adUiElements,
adEventListener, adEventListener,
imaFactory); imaFactory);
@ -360,6 +379,7 @@ public final class ImaAdsLoader
private final int vastLoadTimeoutMs; private final int vastLoadTimeoutMs;
private final int mediaLoadTimeoutMs; private final int mediaLoadTimeoutMs;
private final boolean focusSkipButtonWhenAvailable; private final boolean focusSkipButtonWhenAvailable;
private final boolean playAdBeforeStartPosition;
private final int mediaBitrate; private final int mediaBitrate;
@Nullable private final Set<UiElement> adUiElements; @Nullable private final Set<UiElement> adUiElements;
@Nullable private final AdEventListener adEventListener; @Nullable private final AdEventListener adEventListener;
@ -465,6 +485,7 @@ public final class ImaAdsLoader
/* mediaLoadTimeoutMs= */ TIMEOUT_UNSET, /* mediaLoadTimeoutMs= */ TIMEOUT_UNSET,
/* mediaBitrate= */ BITRATE_UNSET, /* mediaBitrate= */ BITRATE_UNSET,
/* focusSkipButtonWhenAvailable= */ true, /* focusSkipButtonWhenAvailable= */ true,
/* playAdBeforeStartPosition= */ true,
/* adUiElements= */ null, /* adUiElements= */ null,
/* adEventListener= */ null, /* adEventListener= */ null,
/* imaFactory= */ new DefaultImaFactory()); /* imaFactory= */ new DefaultImaFactory());
@ -481,6 +502,7 @@ public final class ImaAdsLoader
int mediaLoadTimeoutMs, int mediaLoadTimeoutMs,
int mediaBitrate, int mediaBitrate,
boolean focusSkipButtonWhenAvailable, boolean focusSkipButtonWhenAvailable,
boolean playAdBeforeStartPosition,
@Nullable Set<UiElement> adUiElements, @Nullable Set<UiElement> adUiElements,
@Nullable AdEventListener adEventListener, @Nullable AdEventListener adEventListener,
ImaFactory imaFactory) { ImaFactory imaFactory) {
@ -492,6 +514,7 @@ public final class ImaAdsLoader
this.mediaLoadTimeoutMs = mediaLoadTimeoutMs; this.mediaLoadTimeoutMs = mediaLoadTimeoutMs;
this.mediaBitrate = mediaBitrate; this.mediaBitrate = mediaBitrate;
this.focusSkipButtonWhenAvailable = focusSkipButtonWhenAvailable; this.focusSkipButtonWhenAvailable = focusSkipButtonWhenAvailable;
this.playAdBeforeStartPosition = playAdBeforeStartPosition;
this.adUiElements = adUiElements; this.adUiElements = adUiElements;
this.adEventListener = adEventListener; this.adEventListener = adEventListener;
this.imaFactory = imaFactory; this.imaFactory = imaFactory;
@ -671,15 +694,7 @@ public final class ImaAdsLoader
@Override @Override
public void release() { public void release() {
pendingAdRequestContext = null; pendingAdRequestContext = null;
if (adsManager != null) { destroyAdsManager();
adsManager.removeAdErrorListener(this);
adsManager.removeAdEventListener(this);
if (adEventListener != null) {
adsManager.removeAdEventListener(adEventListener);
}
adsManager.destroy();
adsManager = null;
}
adsLoader.removeAdsLoadedListener(/* adsLoadedListener= */ this); adsLoader.removeAdsLoadedListener(/* adsLoadedListener= */ this);
adsLoader.removeAdErrorListener(/* adErrorListener= */ this); adsLoader.removeAdErrorListener(/* adErrorListener= */ this);
imaPausedContent = false; imaPausedContent = false;
@ -983,13 +998,18 @@ public final class ImaAdsLoader
@Nullable AdsManager adsManager = this.adsManager; @Nullable AdsManager adsManager = this.adsManager;
if (!isAdsManagerInitialized && adsManager != null) { if (!isAdsManagerInitialized && adsManager != null) {
isAdsManagerInitialized = true; isAdsManagerInitialized = true;
AdsRenderingSettings adsRenderingSettings = setupAdsRendering(); @Nullable AdsRenderingSettings adsRenderingSettings = setupAdsRendering();
adsManager.init(adsRenderingSettings); if (adsRenderingSettings == null) {
adsManager.start(); // There are no ads to play.
updateAdPlaybackState(); destroyAdsManager();
if (DEBUG) { } else {
Log.d(TAG, "Initialized with ads rendering settings: " + adsRenderingSettings); adsManager.init(adsRenderingSettings);
adsManager.start();
if (DEBUG) {
Log.d(TAG, "Initialized with ads rendering settings: " + adsRenderingSettings);
}
} }
updateAdPlaybackState();
} }
handleTimelineOrPositionChanged(); handleTimelineOrPositionChanged();
} }
@ -1063,7 +1083,11 @@ public final class ImaAdsLoader
// Internal methods. // Internal methods.
/** Configures ads rendering for starting playback, returning the settings for the IMA SDK. */ /**
* Configures ads rendering for starting playback, returning the settings for the IMA SDK or
* {@code null} if no ads should play.
*/
@Nullable
private AdsRenderingSettings setupAdsRendering() { private AdsRenderingSettings setupAdsRendering() {
AdsRenderingSettings adsRenderingSettings = imaFactory.createAdsRenderingSettings(); AdsRenderingSettings adsRenderingSettings = imaFactory.createAdsRenderingSettings();
adsRenderingSettings.setEnablePreloading(true); adsRenderingSettings.setEnablePreloading(true);
@ -1083,26 +1107,42 @@ public final class ImaAdsLoader
long[] adGroupTimesUs = adPlaybackState.adGroupTimesUs; long[] adGroupTimesUs = adPlaybackState.adGroupTimesUs;
long contentPositionMs = long contentPositionMs =
getContentPeriodPositionMs(Assertions.checkNotNull(player), timeline, period); getContentPeriodPositionMs(Assertions.checkNotNull(player), timeline, period);
int adGroupIndexForPosition = int adGroupForPositionIndex =
adPlaybackState.getAdGroupIndexForPositionUs( adPlaybackState.getAdGroupIndexForPositionUs(
C.msToUs(contentPositionMs), C.msToUs(contentDurationMs)); C.msToUs(contentPositionMs), C.msToUs(contentDurationMs));
if (adGroupIndexForPosition != C.INDEX_UNSET) { if (adGroupForPositionIndex != C.INDEX_UNSET) {
// Provide the player's initial position to trigger loading and playing the ad. If there are boolean playAdWhenStartingPlayback =
// no midrolls, we are playing a preroll and any pending content position wouldn't be cleared. playAdBeforeStartPosition
if (hasMidrollAdGroups(adGroupTimesUs)) { || adGroupTimesUs[adGroupForPositionIndex] == C.msToUs(contentPositionMs);
if (!playAdWhenStartingPlayback) {
adGroupForPositionIndex++;
} else if (hasMidrollAdGroups(adGroupTimesUs)) {
// Provide the player's initial position to trigger loading and playing the ad. If there are
// no midrolls, we are playing a preroll and any pending content position wouldn't be
// cleared.
pendingContentPositionMs = contentPositionMs; pendingContentPositionMs = contentPositionMs;
} }
if (adGroupIndexForPosition > 0) { if (adGroupForPositionIndex > 0) {
// Skip any ad groups before the one at or immediately before the playback position. for (int i = 0; i < adGroupForPositionIndex; i++) {
for (int i = 0; i < adGroupIndexForPosition; i++) {
adPlaybackState = adPlaybackState.withSkippedAdGroup(i); adPlaybackState = adPlaybackState.withSkippedAdGroup(i);
} }
// Play ads after the midpoint between the ad to play and the one before it, to avoid issues if (adGroupForPositionIndex == adGroupTimesUs.length) {
// with rounding one of the two ad times. // We don't need to play any ads. Because setPlayAdsAfterTime does not discard non-VMAP
long adGroupForPositionTimeUs = adGroupTimesUs[adGroupIndexForPosition]; // ads, we signal that no ads will render so the caller can destroy the ads manager.
long adGroupBeforeTimeUs = adGroupTimesUs[adGroupIndexForPosition - 1]; return null;
double midpointTimeUs = (adGroupForPositionTimeUs + adGroupBeforeTimeUs) / 2d; }
adsRenderingSettings.setPlayAdsAfterTime(midpointTimeUs / C.MICROS_PER_SECOND); long adGroupForPositionTimeUs = adGroupTimesUs[adGroupForPositionIndex];
long adGroupBeforePositionTimeUs = adGroupTimesUs[adGroupForPositionIndex - 1];
if (adGroupForPositionTimeUs == C.TIME_END_OF_SOURCE) {
// Play the postroll by offsetting the start position just past the last non-postroll ad.
adsRenderingSettings.setPlayAdsAfterTime(
(double) adGroupBeforePositionTimeUs / C.MICROS_PER_SECOND + 1d);
} else {
// Play ads after the midpoint between the ad to play and the one before it, to avoid
// issues with rounding one of the two ad times.
double midpointTimeUs = (adGroupForPositionTimeUs + adGroupBeforePositionTimeUs) / 2d;
adsRenderingSettings.setPlayAdsAfterTime(midpointTimeUs / C.MICROS_PER_SECOND);
}
} }
} }
return adsRenderingSettings; return adsRenderingSettings;
@ -1552,6 +1592,18 @@ public final class ImaAdsLoader
} }
} }
private void destroyAdsManager() {
if (adsManager != null) {
adsManager.removeAdErrorListener(this);
adsManager.removeAdEventListener(this);
if (adEventListener != null) {
adsManager.removeAdEventListener(adEventListener);
}
adsManager.destroy();
adsManager = null;
}
}
/** Factory for objects provided by the IMA SDK. */ /** Factory for objects provided by the IMA SDK. */
@VisibleForTesting @VisibleForTesting
/* package */ interface ImaFactory { /* package */ interface ImaFactory {

View File

@ -453,6 +453,171 @@ public final class ImaAdsLoaderTest {
.withSkippedAdGroup(/* adGroupIndex= */ 0)); .withSkippedAdGroup(/* adGroupIndex= */ 0));
} }
@Test
public void resumePlaybackBeforeMidroll_withoutPlayAdBeforeStartPosition_skipsPreroll() {
long midrollWindowTimeUs = 2 * C.MICROS_PER_SECOND;
long midrollPeriodTimeUs =
midrollWindowTimeUs + TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US;
setupPlayback(
CONTENT_TIMELINE,
new Float[] {0f, (float) midrollPeriodTimeUs / C.MICROS_PER_SECOND},
new ImaAdsLoader.Builder(ApplicationProvider.getApplicationContext())
.setPlayAdBeforeStartPosition(false)
.setImaFactory(mockImaFactory)
.setImaSdkSettings(mockImaSdkSettings)
.buildForAdTag(TEST_URI));
fakeExoPlayer.setPlayingContentPosition(C.usToMs(midrollWindowTimeUs) - 1_000);
imaAdsLoader.start(adsLoaderListener, adViewProvider);
ArgumentCaptor<Double> playAdsAfterTimeCaptor = ArgumentCaptor.forClass(Double.class);
verify(mockAdsRenderingSettings).setPlayAdsAfterTime(playAdsAfterTimeCaptor.capture());
double expectedPlayAdsAfterTimeUs = midrollPeriodTimeUs / 2d;
assertThat(playAdsAfterTimeCaptor.getValue())
.isWithin(0.1d)
.of(expectedPlayAdsAfterTimeUs / C.MICROS_PER_SECOND);
assertThat(adsLoaderListener.adPlaybackState)
.isEqualTo(
new AdPlaybackState(/* adGroupTimesUs...= */ 0, midrollPeriodTimeUs)
.withSkippedAdGroup(/* adGroupIndex= */ 0)
.withContentDurationUs(CONTENT_PERIOD_DURATION_US));
}
@Test
public void resumePlaybackAtMidroll_withoutPlayAdBeforeStartPosition_skipsPreroll() {
long midrollWindowTimeUs = 2 * C.MICROS_PER_SECOND;
long midrollPeriodTimeUs =
midrollWindowTimeUs + TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US;
setupPlayback(
CONTENT_TIMELINE,
new Float[] {0f, (float) midrollPeriodTimeUs / C.MICROS_PER_SECOND},
new ImaAdsLoader.Builder(ApplicationProvider.getApplicationContext())
.setPlayAdBeforeStartPosition(false)
.setImaFactory(mockImaFactory)
.setImaSdkSettings(mockImaSdkSettings)
.buildForAdTag(TEST_URI));
fakeExoPlayer.setPlayingContentPosition(C.usToMs(midrollWindowTimeUs));
imaAdsLoader.start(adsLoaderListener, adViewProvider);
ArgumentCaptor<Double> playAdsAfterTimeCaptor = ArgumentCaptor.forClass(Double.class);
verify(mockAdsRenderingSettings).setPlayAdsAfterTime(playAdsAfterTimeCaptor.capture());
double expectedPlayAdsAfterTimeUs = midrollPeriodTimeUs / 2d;
assertThat(playAdsAfterTimeCaptor.getValue())
.isWithin(0.1d)
.of(expectedPlayAdsAfterTimeUs / C.MICROS_PER_SECOND);
assertThat(adsLoaderListener.adPlaybackState)
.isEqualTo(
new AdPlaybackState(/* adGroupTimesUs...= */ 0, midrollPeriodTimeUs)
.withContentDurationUs(CONTENT_PERIOD_DURATION_US)
.withSkippedAdGroup(/* adGroupIndex= */ 0));
}
@Test
public void resumePlaybackAfterMidroll_withoutPlayAdBeforeStartPosition_skipsMidroll() {
long midrollWindowTimeUs = 2 * C.MICROS_PER_SECOND;
long midrollPeriodTimeUs =
midrollWindowTimeUs + TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US;
setupPlayback(
CONTENT_TIMELINE,
new Float[] {0f, (float) midrollPeriodTimeUs / C.MICROS_PER_SECOND},
new ImaAdsLoader.Builder(ApplicationProvider.getApplicationContext())
.setPlayAdBeforeStartPosition(false)
.setImaFactory(mockImaFactory)
.setImaSdkSettings(mockImaSdkSettings)
.buildForAdTag(TEST_URI));
fakeExoPlayer.setPlayingContentPosition(C.usToMs(midrollWindowTimeUs) + 1_000);
imaAdsLoader.start(adsLoaderListener, adViewProvider);
verify(mockAdsManager).destroy();
assertThat(adsLoaderListener.adPlaybackState)
.isEqualTo(
new AdPlaybackState(/* adGroupTimesUs...= */ 0, midrollPeriodTimeUs)
.withContentDurationUs(CONTENT_PERIOD_DURATION_US)
.withSkippedAdGroup(/* adGroupIndex= */ 0)
.withSkippedAdGroup(/* adGroupIndex= */ 1));
}
@Test
public void
resumePlaybackBeforeSecondMidroll_withoutPlayAdBeforeStartPosition_skipsFirstMidroll() {
long firstMidrollWindowTimeUs = 2 * C.MICROS_PER_SECOND;
long firstMidrollPeriodTimeUs =
firstMidrollWindowTimeUs
+ TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US;
long secondMidrollWindowTimeUs = 4 * C.MICROS_PER_SECOND;
long secondMidrollPeriodTimeUs =
secondMidrollWindowTimeUs
+ TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US;
setupPlayback(
CONTENT_TIMELINE,
new Float[] {
(float) firstMidrollPeriodTimeUs / C.MICROS_PER_SECOND,
(float) secondMidrollPeriodTimeUs / C.MICROS_PER_SECOND
},
new ImaAdsLoader.Builder(ApplicationProvider.getApplicationContext())
.setPlayAdBeforeStartPosition(false)
.setImaFactory(mockImaFactory)
.setImaSdkSettings(mockImaSdkSettings)
.buildForAdTag(TEST_URI));
fakeExoPlayer.setPlayingContentPosition(C.usToMs(secondMidrollWindowTimeUs) - 1_000);
imaAdsLoader.start(adsLoaderListener, adViewProvider);
ArgumentCaptor<Double> playAdsAfterTimeCaptor = ArgumentCaptor.forClass(Double.class);
verify(mockAdsRenderingSettings).setPlayAdsAfterTime(playAdsAfterTimeCaptor.capture());
double expectedPlayAdsAfterTimeUs = (firstMidrollPeriodTimeUs + secondMidrollPeriodTimeUs) / 2d;
assertThat(playAdsAfterTimeCaptor.getValue())
.isWithin(0.1d)
.of(expectedPlayAdsAfterTimeUs / C.MICROS_PER_SECOND);
assertThat(adsLoaderListener.adPlaybackState)
.isEqualTo(
new AdPlaybackState(
/* adGroupTimesUs...= */ firstMidrollPeriodTimeUs, secondMidrollPeriodTimeUs)
.withSkippedAdGroup(/* adGroupIndex= */ 0)
.withContentDurationUs(CONTENT_PERIOD_DURATION_US));
}
@Test
public void resumePlaybackAtSecondMidroll_withoutPlayAdBeforeStartPosition_skipsFirstMidroll() {
long firstMidrollWindowTimeUs = 2 * C.MICROS_PER_SECOND;
long firstMidrollPeriodTimeUs =
firstMidrollWindowTimeUs
+ TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US;
long secondMidrollWindowTimeUs = 4 * C.MICROS_PER_SECOND;
long secondMidrollPeriodTimeUs =
secondMidrollWindowTimeUs
+ TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US;
setupPlayback(
CONTENT_TIMELINE,
new Float[] {
(float) firstMidrollPeriodTimeUs / C.MICROS_PER_SECOND,
(float) secondMidrollPeriodTimeUs / C.MICROS_PER_SECOND
},
new ImaAdsLoader.Builder(ApplicationProvider.getApplicationContext())
.setPlayAdBeforeStartPosition(false)
.setImaFactory(mockImaFactory)
.setImaSdkSettings(mockImaSdkSettings)
.buildForAdTag(TEST_URI));
fakeExoPlayer.setPlayingContentPosition(C.usToMs(secondMidrollWindowTimeUs));
imaAdsLoader.start(adsLoaderListener, adViewProvider);
ArgumentCaptor<Double> playAdsAfterTimeCaptor = ArgumentCaptor.forClass(Double.class);
verify(mockAdsRenderingSettings).setPlayAdsAfterTime(playAdsAfterTimeCaptor.capture());
double expectedPlayAdsAfterTimeUs = (firstMidrollPeriodTimeUs + secondMidrollPeriodTimeUs) / 2d;
assertThat(playAdsAfterTimeCaptor.getValue())
.isWithin(0.1d)
.of(expectedPlayAdsAfterTimeUs / C.MICROS_PER_SECOND);
assertThat(adsLoaderListener.adPlaybackState)
.isEqualTo(
new AdPlaybackState(
/* adGroupTimesUs...= */ firstMidrollPeriodTimeUs, secondMidrollPeriodTimeUs)
.withContentDurationUs(CONTENT_PERIOD_DURATION_US)
.withSkippedAdGroup(/* adGroupIndex= */ 0));
}
@Test @Test
public void stop_unregistersAllVideoControlOverlays() { public void stop_unregistersAllVideoControlOverlays() {
setupPlayback(CONTENT_TIMELINE, PREROLL_CUE_POINTS_SECONDS); setupPlayback(CONTENT_TIMELINE, PREROLL_CUE_POINTS_SECONDS);
@ -466,14 +631,21 @@ public final class ImaAdsLoaderTest {
} }
private void setupPlayback(Timeline contentTimeline, Float[] cuePoints) { private void setupPlayback(Timeline contentTimeline, Float[] cuePoints) {
fakeExoPlayer = new FakePlayer(); setupPlayback(
adsLoaderListener = new TestAdsLoaderListener(fakeExoPlayer, contentTimeline); contentTimeline,
when(mockAdsManager.getAdCuePoints()).thenReturn(Arrays.asList(cuePoints)); cuePoints,
imaAdsLoader =
new ImaAdsLoader.Builder(ApplicationProvider.getApplicationContext()) new ImaAdsLoader.Builder(ApplicationProvider.getApplicationContext())
.setImaFactory(mockImaFactory) .setImaFactory(mockImaFactory)
.setImaSdkSettings(mockImaSdkSettings) .setImaSdkSettings(mockImaSdkSettings)
.buildForAdTag(TEST_URI); .buildForAdTag(TEST_URI));
}
private void setupPlayback(
Timeline contentTimeline, Float[] cuePoints, ImaAdsLoader imaAdsLoader) {
fakeExoPlayer = new FakePlayer();
adsLoaderListener = new TestAdsLoaderListener(fakeExoPlayer, contentTimeline);
when(mockAdsManager.getAdCuePoints()).thenReturn(Arrays.asList(cuePoints));
this.imaAdsLoader = imaAdsLoader;
imaAdsLoader.setPlayer(fakeExoPlayer); imaAdsLoader.setPlayer(fakeExoPlayer);
} }