use of com.google.android.exoplayer2.source.ads.AdPlaybackState in project ExoPlayer by google.
the class AdTagLoader method loadAdInternal.
private void loadAdInternal(AdMediaInfo adMediaInfo, AdPodInfo adPodInfo) {
if (adsManager == null) {
// Drop events after release.
if (configuration.debugModeEnabled) {
Log.d(TAG, "loadAd after release " + getAdMediaInfoString(adMediaInfo) + ", ad pod " + adPodInfo);
}
return;
}
int adGroupIndex = getAdGroupIndexForAdPod(adPodInfo);
int adIndexInAdGroup = adPodInfo.getAdPosition() - 1;
AdInfo adInfo = new AdInfo(adGroupIndex, adIndexInAdGroup);
// The ad URI may already be known, so force put to update it if needed.
adInfoByAdMediaInfo.forcePut(adMediaInfo, adInfo);
if (configuration.debugModeEnabled) {
Log.d(TAG, "loadAd " + getAdMediaInfoString(adMediaInfo));
}
if (adPlaybackState.isAdInErrorState(adGroupIndex, adIndexInAdGroup)) {
// timeout after its media load timeout.
return;
}
// The ad count may increase on successive loads of ads in the same ad pod, for example, due to
// separate requests for ad tags with multiple ads within the ad pod completing after an earlier
// ad has loaded. See also https://github.com/google/ExoPlayer/issues/7477.
AdPlaybackState.AdGroup adGroup = adPlaybackState.getAdGroup(adInfo.adGroupIndex);
adPlaybackState = adPlaybackState.withAdCount(adInfo.adGroupIndex, max(adPodInfo.getTotalAds(), adGroup.states.length));
adGroup = adPlaybackState.getAdGroup(adInfo.adGroupIndex);
for (int i = 0; i < adIndexInAdGroup; i++) {
// Any preceding ads that haven't loaded are not going to load.
if (adGroup.states[i] == AdPlaybackState.AD_STATE_UNAVAILABLE) {
adPlaybackState = adPlaybackState.withAdLoadError(adGroupIndex, /* adIndexInAdGroup= */
i);
}
}
Uri adUri = Uri.parse(adMediaInfo.getUrl());
adPlaybackState = adPlaybackState.withAdUri(adInfo.adGroupIndex, adInfo.adIndexInAdGroup, adUri);
updateAdPlaybackState();
}
use of com.google.android.exoplayer2.source.ads.AdPlaybackState in project ExoPlayer by google.
the class ImaServerSideAdInsertionMediaSource method setContentUri.
// Internal methods (called on the playback thread).
private void setContentUri(Uri contentUri) {
if (serverSideAdInsertionMediaSource != null) {
return;
}
MediaItem contentMediaItem = new MediaItem.Builder().setUri(contentUri).setDrmConfiguration(checkNotNull(mediaItem.localConfiguration).drmConfiguration).setLiveConfiguration(mediaItem.liveConfiguration).setCustomCacheKey(mediaItem.localConfiguration.customCacheKey).setStreamKeys(mediaItem.localConfiguration.streamKeys).build();
ServerSideAdInsertionMediaSource serverSideAdInsertionMediaSource = new ServerSideAdInsertionMediaSource(contentMediaSourceFactory.createMediaSource(contentMediaItem), componentListener);
this.serverSideAdInsertionMediaSource = serverSideAdInsertionMediaSource;
if (isLiveStream) {
AdPlaybackState liveAdPlaybackState = new AdPlaybackState(adsId).withNewAdGroup(/* adGroupIndex= */
0, /* adGroupTimeUs= */
C.TIME_END_OF_SOURCE).withIsServerSideInserted(/* adGroupIndex= */
0, true);
mainHandler.post(() -> setAdPlaybackState(liveAdPlaybackState));
}
prepareChildSource(/* id= */
null, serverSideAdInsertionMediaSource);
}
use of com.google.android.exoplayer2.source.ads.AdPlaybackState in project ExoPlayer by google.
the class ImaUtil method splitAdPlaybackStateForPeriods.
/**
* Splits an {@link AdPlaybackState} into a separate {@link AdPlaybackState} for each period of a
* content timeline.
*
* <p>If a period is enclosed by an ad group, the period is considered an ad period. Splitting
* results in a separate {@link AdPlaybackState ad playback state} for each period that has either
* no ads or a single ad. In the latter case, the duration of the single ad is set to the duration
* of the period consuming the entire duration of the period. Accordingly an ad period does not
* contribute to the duration of the containing window.
*
* @param adPlaybackState The ad playback state to be split.
* @param contentTimeline The content timeline for each period of which to create an {@link
* AdPlaybackState}.
* @return A map of ad playback states for each period UID in the content timeline.
*/
public static ImmutableMap<Object, AdPlaybackState> splitAdPlaybackStateForPeriods(AdPlaybackState adPlaybackState, Timeline contentTimeline) {
Timeline.Period period = new Timeline.Period();
if (contentTimeline.getPeriodCount() == 1) {
// A single period gets the entire ad playback state that may contain multiple ad groups.
return ImmutableMap.of(checkNotNull(contentTimeline.getPeriod(/* periodIndex= */
0, period, /* setIds= */
true).uid), adPlaybackState);
}
int periodIndex = 0;
long totalElapsedContentDurationUs = 0;
Object adsId = checkNotNull(adPlaybackState.adsId);
AdPlaybackState contentOnlyAdPlaybackState = new AdPlaybackState(adsId);
Map<Object, AdPlaybackState> adPlaybackStates = new HashMap<>();
for (int i = adPlaybackState.removedAdGroupCount; i < adPlaybackState.adGroupCount; i++) {
AdPlaybackState.AdGroup adGroup = adPlaybackState.getAdGroup(/* adGroupIndex= */
i);
if (adGroup.timeUs == C.TIME_END_OF_SOURCE) {
checkState(i == adPlaybackState.adGroupCount - 1);
// The last ad group is a placeholder for a potential post roll. We can just stop here.
break;
}
// The ad group start timeUs is in content position. We need to add the ad
// duration before the ad group to translate the start time to the position in the period.
long adGroupDurationUs = sum(adGroup.durationsUs);
long elapsedAdGroupAdDurationUs = 0;
for (int j = periodIndex; j < contentTimeline.getPeriodCount(); j++) {
contentTimeline.getPeriod(j, period, /* setIds= */
true);
// Subtract one microsecond to work around rounding errors with adGroup.timeUs.
if (totalElapsedContentDurationUs < adGroup.timeUs - 1) {
// Period starts before the ad group, so it is a content period.
adPlaybackStates.put(checkNotNull(period.uid), contentOnlyAdPlaybackState);
totalElapsedContentDurationUs += period.durationUs;
} else {
long periodStartUs = totalElapsedContentDurationUs + elapsedAdGroupAdDurationUs;
// Add one microsecond to work around rounding errors with adGroup.timeUs.
if (periodStartUs + period.durationUs <= adGroup.timeUs + adGroupDurationUs + 1) {
// The period ends before the end of the ad group, so it is an ad period (Note: A VOD ad
// reported by the IMA SDK spans multiple periods before the LOADED event arrives).
adPlaybackStates.put(checkNotNull(period.uid), splitAdGroupForPeriod(adsId, adGroup, periodStartUs, period.durationUs));
elapsedAdGroupAdDurationUs += period.durationUs;
} else {
// Period is after the current ad group. Continue with next ad group.
break;
}
}
// Increment the period index to the next unclassified period.
periodIndex++;
}
}
// The remaining periods end after the last ad group, so these are content periods.
for (int i = periodIndex; i < contentTimeline.getPeriodCount(); i++) {
contentTimeline.getPeriod(i, period, /* setIds= */
true);
adPlaybackStates.put(checkNotNull(period.uid), contentOnlyAdPlaybackState);
}
return ImmutableMap.copyOf(adPlaybackStates);
}
use of com.google.android.exoplayer2.source.ads.AdPlaybackState in project ExoPlayer by google.
the class ImaAdsLoaderTest method playback_withMidrollFetchError_marksAdAsInErrorState.
@Test
public void playback_withMidrollFetchError_marksAdAsInErrorState() {
AdEvent mockMidrollFetchErrorAdEvent = mock(AdEvent.class);
when(mockMidrollFetchErrorAdEvent.getType()).thenReturn(AdEventType.AD_BREAK_FETCH_ERROR);
when(mockMidrollFetchErrorAdEvent.getAdData()).thenReturn(ImmutableMap.of("adBreakTime", "20.5"));
when(mockAdsManager.getAdCuePoints()).thenReturn(ImmutableList.of(20.5f));
// Simulate loading an empty midroll ad.
imaAdsLoader.start(adsMediaSource, TEST_DATA_SPEC, TEST_ADS_ID, adViewProvider, adsLoaderListener);
adEventListener.onAdEvent(mockMidrollFetchErrorAdEvent);
assertThat(getAdPlaybackState(/* periodIndex= */
0)).isEqualTo(new AdPlaybackState(TEST_ADS_ID, /* adGroupTimesUs...= */
20_500_000).withContentDurationUs(CONTENT_PERIOD_DURATION_US).withAdDurationsUs(new long[][] { { TEST_AD_DURATION_US } }).withAdCount(/* adGroupIndex= */
0, /* adCount= */
1).withAdLoadError(/* adGroupIndex= */
0, /* adIndexInAdGroup= */
0));
}
use of com.google.android.exoplayer2.source.ads.AdPlaybackState in project ExoPlayer by google.
the class ImaAdsLoaderTest method resumePlaybackAtMidroll_withoutPlayAdBeforeStartPosition_skipsPreroll.
@Test
public void resumePlaybackAtMidroll_withoutPlayAdBeforeStartPosition_skipsPreroll() {
imaAdsLoader = new ImaAdsLoader.Builder(getApplicationContext()).setPlayAdBeforeStartPosition(false).setImaFactory(mockImaFactory).setImaSdkSettings(mockImaSdkSettings).build();
imaAdsLoader.setPlayer(fakePlayer);
adsMediaSource = new AdsMediaSource(new FakeMediaSource(CONTENT_TIMELINE), TEST_DATA_SPEC, TEST_ADS_ID, new DefaultMediaSourceFactory((Context) getApplicationContext()), imaAdsLoader, adViewProvider);
long midrollWindowTimeUs = 2 * C.MICROS_PER_SECOND;
long midrollPeriodTimeUs = midrollWindowTimeUs + TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US;
ImmutableList<Float> cuePoints = ImmutableList.of(0f, (float) midrollPeriodTimeUs / C.MICROS_PER_SECOND);
when(mockAdsManager.getAdCuePoints()).thenReturn(cuePoints);
fakePlayer.setPlayingContentPosition(/* periodIndex= */
0, Util.usToMs(midrollWindowTimeUs));
imaAdsLoader.start(adsMediaSource, TEST_DATA_SPEC, TEST_ADS_ID, adViewProvider, adsLoaderListener);
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(getAdPlaybackState(/* periodIndex= */
0)).isEqualTo(new AdPlaybackState(TEST_ADS_ID, getAdGroupTimesUsForCuePoints(cuePoints)).withContentDurationUs(CONTENT_PERIOD_DURATION_US).withSkippedAdGroup(/* adGroupIndex= */
0));
}
Aggregations