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 e5ec8e6b47
commit e261b8d0ef
3 changed files with 260 additions and 35 deletions

View File

@ -6,6 +6,7 @@
seeking to an unprepared period within the current window. For example when seeking to an unprepared period within the current window. For example when
seeking over an ad group, or to the next period in a multi-period DASH seeking over an ad group, or to the next period in a multi-period DASH
stream ([#5507](https://github.com/google/ExoPlayer/issues/5507)). stream ([#5507](https://github.com/google/ExoPlayer/issues/5507)).
* IMA extension: Add option to skip ads before the start position.
### 2.11.5 (2020-06-05) ### ### 2.11.5 (2020-06-05) ###

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,14 +998,19 @@ 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();
if (adsRenderingSettings == null) {
// There are no ads to play.
destroyAdsManager();
} else {
adsManager.init(adsRenderingSettings); adsManager.init(adsRenderingSettings);
adsManager.start(); adsManager.start();
updateAdPlaybackState();
if (DEBUG) { if (DEBUG) {
Log.d(TAG, "Initialized with ads rendering settings: " + adsRenderingSettings); Log.d(TAG, "Initialized with ads rendering settings: " + adsRenderingSettings);
} }
} }
updateAdPlaybackState();
}
handleTimelineOrPositionChanged(); handleTimelineOrPositionChanged();
} }
@ -1054,7 +1074,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);
@ -1074,28 +1098,44 @@ 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) {
boolean playAdWhenStartingPlayback =
playAdBeforeStartPosition
|| 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 // 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. // no midrolls, we are playing a preroll and any pending content position wouldn't be
if (hasMidrollAdGroups(adGroupTimesUs)) { // 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; }
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); adsRenderingSettings.setPlayAdsAfterTime(midpointTimeUs / C.MICROS_PER_SECOND);
} }
} }
}
return adsRenderingSettings; return adsRenderingSettings;
} }
@ -1543,6 +1583,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

@ -446,6 +446,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);
@ -459,14 +624,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);
} }