From 51237e8aef85a4c32099aecee58213b5555e3d1d Mon Sep 17 00:00:00 2001 From: bachinger Date: Tue, 14 Dec 2021 16:46:57 +0000 Subject: [PATCH] Add AdPlaybackStateUpdater PiperOrigin-RevId: 416314200 --- .../ads/ServerSideAdInsertionMediaSource.java | 35 ++++++++++++++++--- .../ServerSideAdInsertionMediaSourceTest.java | 19 +++++----- 2 files changed, 41 insertions(+), 13 deletions(-) diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ads/ServerSideAdInsertionMediaSource.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ads/ServerSideAdInsertionMediaSource.java index 55708bb749..e6db19a82b 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ads/ServerSideAdInsertionMediaSource.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ads/ServerSideAdInsertionMediaSource.java @@ -82,10 +82,32 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; public final class ServerSideAdInsertionMediaSource extends BaseMediaSource implements MediaSource.MediaSourceCaller, MediaSourceEventListener, DrmSessionEventListener { + /** + * Receives ad playback state update requests when the {@link Timeline} of the content media + * source has changed. + */ + public interface AdPlaybackStateUpdater { + /** + * Called when the content source has refreshed the timeline. + * + *

If true is returned the source refresh publication is deferred, to wait for an {@link + * #setAdPlaybackState(AdPlaybackState) ad playback state update}. If false is returned, the + * source refresh is immediately published. + * + *

Called on the playback thread. + * + * @param contentTimeline The {@link Timeline} of the wrapped content media source. + * @return true to defer the source refresh publication, or false to immediately publish the + * source refresh. + */ + boolean onAdPlaybackStateUpdateRequested(Timeline contentTimeline); + } + private final MediaSource mediaSource; private final ListMultimap mediaPeriods; private final MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcherWithoutId; private final DrmSessionEventListener.EventDispatcher drmEventDispatcherWithoutId; + @Nullable private final AdPlaybackStateUpdater adPlaybackStateUpdater; @GuardedBy("this") @Nullable @@ -99,11 +121,15 @@ public final class ServerSideAdInsertionMediaSource extends BaseMediaSource * Creates the media source. * * @param mediaSource The {@link MediaSource} to wrap. + * @param adPlaybackStateUpdater The optional {@link AdPlaybackStateUpdater} to be called before a + * source refresh is published. */ // Calling BaseMediaSource.createEventDispatcher from the constructor. @SuppressWarnings("nullness:method.invocation") - public ServerSideAdInsertionMediaSource(MediaSource mediaSource) { + public ServerSideAdInsertionMediaSource( + MediaSource mediaSource, @Nullable AdPlaybackStateUpdater adPlaybackStateUpdater) { this.mediaSource = mediaSource; + this.adPlaybackStateUpdater = adPlaybackStateUpdater; mediaPeriods = ArrayListMultimap.create(); adPlaybackState = AdPlaybackState.NONE; mediaSourceEventDispatcherWithoutId = createEventDispatcher(/* mediaPeriodId= */ null); @@ -193,10 +219,11 @@ public final class ServerSideAdInsertionMediaSource extends BaseMediaSource @Override public void onSourceInfoRefreshed(MediaSource source, Timeline timeline) { this.contentTimeline = timeline; - if (AdPlaybackState.NONE.equals(adPlaybackState)) { - return; + if ((adPlaybackStateUpdater == null + || !adPlaybackStateUpdater.onAdPlaybackStateUpdateRequested(timeline)) + && !AdPlaybackState.NONE.equals(adPlaybackState)) { + refreshSourceInfo(new ServerSideAdInsertionTimeline(timeline, adPlaybackState)); } - refreshSourceInfo(new ServerSideAdInsertionTimeline(timeline, adPlaybackState)); } @Override diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/ads/ServerSideAdInsertionMediaSourceTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/ads/ServerSideAdInsertionMediaSourceTest.java index ff99372c5d..b6f7ca45eb 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/ads/ServerSideAdInsertionMediaSourceTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/ads/ServerSideAdInsertionMediaSourceTest.java @@ -82,7 +82,8 @@ public final class ServerSideAdInsertionMediaSourceTest { /* windowOffsetInFirstPeriodUs= */ 42_000_000L, AdPlaybackState.NONE)); ServerSideAdInsertionMediaSource mediaSource = - new ServerSideAdInsertionMediaSource(new FakeMediaSource(wrappedTimeline)); + new ServerSideAdInsertionMediaSource( + new FakeMediaSource(wrappedTimeline), /* adPlaybackStateUpdater= */ null); // Test with one ad group before the window, and the window starting within the second ad group. AdPlaybackState adPlaybackState = new AdPlaybackState( @@ -155,8 +156,8 @@ public final class ServerSideAdInsertionMediaSourceTest { ServerSideAdInsertionMediaSource mediaSource = new ServerSideAdInsertionMediaSource( - new DefaultMediaSourceFactory(context) - .createMediaSource(MediaItem.fromUri(TEST_ASSET))); + new DefaultMediaSourceFactory(context).createMediaSource(MediaItem.fromUri(TEST_ASSET)), + /* adPlaybackStateUpdater= */ null); AdPlaybackState adPlaybackState = new AdPlaybackState(/* adsId= */ new Object()); adPlaybackState = addAdGroupToAdPlaybackState( @@ -214,8 +215,8 @@ public final class ServerSideAdInsertionMediaSourceTest { ServerSideAdInsertionMediaSource mediaSource = new ServerSideAdInsertionMediaSource( - new DefaultMediaSourceFactory(context) - .createMediaSource(MediaItem.fromUri(TEST_ASSET))); + new DefaultMediaSourceFactory(context).createMediaSource(MediaItem.fromUri(TEST_ASSET)), + /* adPlaybackStateUpdater= */ null); AdPlaybackState adPlaybackState = new AdPlaybackState(/* adsId= */ new Object()); adPlaybackState = addAdGroupToAdPlaybackState( @@ -274,8 +275,8 @@ public final class ServerSideAdInsertionMediaSourceTest { ServerSideAdInsertionMediaSource mediaSource = new ServerSideAdInsertionMediaSource( - new DefaultMediaSourceFactory(context) - .createMediaSource(MediaItem.fromUri(TEST_ASSET))); + new DefaultMediaSourceFactory(context).createMediaSource(MediaItem.fromUri(TEST_ASSET)), + /* adPlaybackStateUpdater= */ null); AdPlaybackState adPlaybackState = new AdPlaybackState(/* adsId= */ new Object()); adPlaybackState = addAdGroupToAdPlaybackState( @@ -328,8 +329,8 @@ public final class ServerSideAdInsertionMediaSourceTest { ServerSideAdInsertionMediaSource mediaSource = new ServerSideAdInsertionMediaSource( - new DefaultMediaSourceFactory(context) - .createMediaSource(MediaItem.fromUri(TEST_ASSET))); + new DefaultMediaSourceFactory(context).createMediaSource(MediaItem.fromUri(TEST_ASSET)), + /* adPlaybackStateUpdater= */ null); AdPlaybackState adPlaybackState = new AdPlaybackState(/* adsId= */ new Object()); adPlaybackState = addAdGroupToAdPlaybackState(