From afa3c628c16bccf1b587580b6312b1d18ae231ee Mon Sep 17 00:00:00 2001 From: bachinger Date: Mon, 6 Feb 2023 11:44:31 +0000 Subject: [PATCH] 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 --- .../ImaServerSideAdInsertionMediaSource.java | 131 +++++++++++------- 1 file changed, 80 insertions(+), 51 deletions(-) diff --git a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java index e029b74578..5fe2d627fc 100644 --- a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java +++ b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java @@ -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())); + } + } }