Create an AdEventListener for each supported stream type

This is a refactoring to separate and simplify the logic
of VOD and live streams when handling IMA ad events. An
additional listener will be required for DASH live stream
in a follow-up CL.

PiperOrigin-RevId: 507435741
This commit is contained in:
bachinger 2023-02-06 11:44:31 +00:00 committed by microkatz
parent 0eb11e269c
commit afa3c628c1

View File

@ -528,7 +528,6 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
this.contentMediaSourceFactory = contentMediaSourceFactory;
this.applicationAdEventListener = applicationAdEventListener;
this.applicationAdErrorListener = applicationAdErrorListener;
componentListener = new ComponentListener();
Assertions.checkArgument(player.getApplicationLooper() == Looper.getMainLooper());
mainHandler = new Handler(Looper.getMainLooper());
Uri streamRequestUri = checkNotNull(mediaItem.localConfiguration).uri;
@ -536,6 +535,12 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
adsId = ImaServerSideAdInsertionUriBuilder.getAdsId(streamRequestUri);
loadVideoTimeoutMs = ImaServerSideAdInsertionUriBuilder.getLoadVideoTimeoutMs(streamRequestUri);
streamRequest = ImaServerSideAdInsertionUriBuilder.createStreamRequest(streamRequestUri);
boolean isDashStream = streamRequest.getFormat().equals(StreamRequest.StreamFormat.DASH);
componentListener =
new ComponentListener(
isLiveStream
? (isDashStream ? new NoopAdEventListener() : new SinglePeriodLiveAdEventListener())
: new VodAdEventListener());
adPlaybackState = adsLoader.getAdPlaybackState(adsId);
}
@ -773,6 +778,13 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
private final class ComponentListener
implements AdEvent.AdEventListener, Player.Listener, AdPlaybackStateUpdater {
private final AdEventListener adEventListener;
/** Creates an new instance. */
public ComponentListener(AdEventListener adEventListener) {
this.adEventListener = adEventListener;
}
// Implement Player.Listener.
@Override
@ -883,56 +895,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
@MainThread
@Override
public void onAdEvent(AdEvent event) {
AdPlaybackState newAdPlaybackState = adPlaybackState;
switch (event.getType()) {
case CUEPOINTS_CHANGED:
// CUEPOINTS_CHANGED event is firing multiple times with the same queue points.
if (!isLiveStream && newAdPlaybackState.equals(AdPlaybackState.NONE)) {
newAdPlaybackState =
setVodAdGroupPlaceholders(
checkNotNull(streamManager).getCuePoints(), new AdPlaybackState(adsId));
}
break;
case LOADED:
if (isLiveStream) {
Timeline timeline = player.getCurrentTimeline();
Timeline.Window window =
timeline.getWindow(player.getCurrentMediaItemIndex(), new Timeline.Window());
if (window.lastPeriodIndex > window.firstPeriodIndex) {
// multi-period live not integrated
return;
}
long positionInWindowUs =
timeline.getPeriod(player.getCurrentPeriodIndex(), new Timeline.Period())
.positionInWindowUs;
long currentContentPeriodPositionUs =
msToUs(player.getContentPosition()) - positionInWindowUs;
Ad ad = event.getAd();
AdPodInfo adPodInfo = ad.getAdPodInfo();
newAdPlaybackState =
addLiveAdBreak(
currentContentPeriodPositionUs,
/* adDurationUs= */ secToUsRounded(ad.getDuration()),
/* adPositionInAdPod= */ adPodInfo.getAdPosition(),
/* totalAdDurationUs= */ secToUsRounded(adPodInfo.getMaxDuration()),
/* totalAdsInAdPod= */ adPodInfo.getTotalAds(),
/* adPlaybackState= */ newAdPlaybackState.equals(AdPlaybackState.NONE)
? new AdPlaybackState(adsId)
: newAdPlaybackState);
} else {
newAdPlaybackState = setVodAdInPlaceholder(event.getAd(), newAdPlaybackState);
}
break;
case SKIPPED:
if (!isLiveStream) {
newAdPlaybackState = skipAd(event.getAd(), newAdPlaybackState);
}
break;
default:
// Do nothing.
break;
}
setAdPlaybackState(newAdPlaybackState);
adEventListener.onAdEvent(event);
}
// Implement AdPlaybackStateUpdater (called on the playback thread).
@ -1356,4 +1319,70 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
}
}
}
private class VodAdEventListener implements AdEventListener {
@Override
public void onAdEvent(AdEvent event) {
AdPlaybackState newAdPlaybackState = adPlaybackState;
switch (event.getType()) {
case CUEPOINTS_CHANGED:
if (newAdPlaybackState.equals(AdPlaybackState.NONE)) {
newAdPlaybackState =
setVodAdGroupPlaceholders(
checkNotNull(streamManager).getCuePoints(), new AdPlaybackState(adsId));
}
break;
case LOADED:
newAdPlaybackState = setVodAdInPlaceholder(event.getAd(), newAdPlaybackState);
break;
case SKIPPED:
newAdPlaybackState = skipAd(event.getAd(), newAdPlaybackState);
break;
default:
// Do nothing.
break;
}
setAdPlaybackState(newAdPlaybackState);
}
}
private class SinglePeriodLiveAdEventListener implements AdEventListener {
@Override
public void onAdEvent(AdEvent event) {
if (event.getType() != AdEvent.AdEventType.LOADED) {
return;
}
AdPlaybackState newAdPlaybackState = adPlaybackState;
Timeline timeline = player.getCurrentTimeline();
long positionInWindowUs =
timeline.getPeriod(player.getCurrentPeriodIndex(), new Timeline.Period())
.positionInWindowUs;
long currentContentPeriodPositionUs =
msToUs(player.getContentPosition()) - positionInWindowUs;
Ad ad = event.getAd();
AdPodInfo adPodInfo = ad.getAdPodInfo();
newAdPlaybackState =
addLiveAdBreak(
currentContentPeriodPositionUs,
/* adDurationUs= */ secToUsRounded(ad.getDuration()),
/* adPositionInAdPod= */ adPodInfo.getAdPosition(),
/* totalAdDurationUs= */ secToUsRounded(adPodInfo.getMaxDuration()),
/* totalAdsInAdPod= */ adPodInfo.getTotalAds(),
/* adPlaybackState= */ newAdPlaybackState.equals(AdPlaybackState.NONE)
? new AdPlaybackState(adsId)
: newAdPlaybackState);
setAdPlaybackState(newAdPlaybackState);
}
}
private static class NoopAdEventListener implements AdEventListener {
@Override
public void onAdEvent(AdEvent event) {
Log.w(
"ImaSSAIMediaSource",
String.format(
"Ignoring IMA ad event %s because the current stream type is not supported.",
event.getType().name()));
}
}
}