From b9b1be4f3e3a934dde38da34e591901c6d044f3d Mon Sep 17 00:00:00 2001 From: bachinger Date: Tue, 15 Feb 2022 14:19:54 +0000 Subject: [PATCH] Store ad playback state in AdsLoader for resume after backgrounding #minor-release PiperOrigin-RevId: 428763656 --- .../ImaServerSideAdInsertionMediaSource.java | 74 ++++++++++++++++--- 1 file changed, 65 insertions(+), 9 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 d77b50fc0c..5ec5b202f6 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 @@ -151,6 +151,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou @Override public MediaSource createMediaSource(MediaItem mediaItem) { + checkNotNull(mediaItem.localConfiguration); Player player = checkNotNull(adsLoader.player); StreamPlayer streamPlayer = new StreamPlayer(player, mediaItem); ImaSdkFactory imaSdkFactory = ImaSdkFactory.getInstance(); @@ -163,6 +164,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou new ImaServerSideAdInsertionMediaSource( mediaItem, player, + adsLoader, imaAdsLoader, streamPlayer, contentMediaSourceFactory, @@ -185,6 +187,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou @Nullable private ImaSdkSettings imaSdkSettings; @Nullable private AdEventListener adEventListener; @Nullable private AdErrorEvent.AdErrorListener adErrorListener; + private State state; private ImmutableList companionAdSlots; /** @@ -197,6 +200,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou this.context = context; this.adViewProvider = adViewProvider; companionAdSlots = ImmutableList.of(); + state = new State(ImmutableMap.of()); } /** @@ -248,6 +252,19 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou return this; } + /** + * Sets the optional state to resume with. + * + *

The state can be received when {@link #release() releasing} the {@link AdsLoader}. + * + * @param state The state to resume with. + * @return This builder, for convenience. + */ + public AdsLoader.Builder setAdsLoaderState(State state) { + this.state = state; + return this; + } + /** Returns a new {@link AdsLoader}. */ public AdsLoader build() { @Nullable ImaSdkSettings imaSdkSettings = this.imaSdkSettings; @@ -263,7 +280,17 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou adErrorListener, companionAdSlots, imaSdkSettings.isDebugMode()); - return new AdsLoader(context, configuration); + return new AdsLoader(context, configuration, state); + } + } + + /** The state of the {@link AdsLoader}. */ + public static class State { + + private final ImmutableMap adPlaybackStates; + + private State(ImmutableMap adPlaybackStates) { + this.adPlaybackStates = adPlaybackStates; } } @@ -271,13 +298,19 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou private final Context context; private final Map mediaSourceResources; + private final Map adPlaybackStateMap; @Nullable private Player player; - private AdsLoader(Context context, ImaUtil.ServerSideAdInsertionConfiguration configuration) { + private AdsLoader( + Context context, ImaUtil.ServerSideAdInsertionConfiguration configuration, State state) { this.context = context.getApplicationContext(); this.configuration = configuration; mediaSourceResources = new HashMap<>(); + adPlaybackStateMap = new HashMap<>(); + for (Map.Entry entry : state.adPlaybackStates.entrySet()) { + adPlaybackStateMap.put(entry.getKey(), entry.getValue()); + } } /** @@ -290,8 +323,12 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou this.player = player; } - /** Releases resources when the ads loader is no longer needed. */ - public void release() { + /** + * Releases resources. + * + * @return The {@link State} that can be used to resume with. + */ + public State release() { for (MediaSourceResourceHolder resourceHolder : mediaSourceResources.values()) { resourceHolder.streamPlayer.release(); resourceHolder.adsLoader.release(); @@ -300,9 +337,12 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou } mediaSourceResources.clear(); player = null; + return new State(ImmutableMap.copyOf(adPlaybackStateMap)); } - /* package */ void addMediaSourceResources( + // Internal methods. + + private void addMediaSourceResources( ImaServerSideAdInsertionMediaSource mediaSource, StreamPlayer streamPlayer, com.google.ads.interactivemedia.v3.api.AdsLoader adsLoader) { @@ -310,6 +350,15 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou mediaSource, new MediaSourceResourceHolder(mediaSource, streamPlayer, adsLoader)); } + private AdPlaybackState getAdPlaybackState(String adsId) { + @Nullable AdPlaybackState adPlaybackState = adPlaybackStateMap.get(adsId); + return adPlaybackState != null ? adPlaybackState : AdPlaybackState.NONE; + } + + private void setAdPlaybackState(String adsId, AdPlaybackState adPlaybackState) { + this.adPlaybackStateMap.put(adsId, adPlaybackState); + } + private static final class MediaSourceResourceHolder { public final ImaServerSideAdInsertionMediaSource imaServerSideAdInsertionMediaSource; public final StreamPlayer streamPlayer; @@ -329,7 +378,8 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou private final MediaItem mediaItem; private final Player player; private final MediaSource.Factory contentMediaSourceFactory; - private final com.google.ads.interactivemedia.v3.api.AdsLoader adsLoader; + private final AdsLoader adsLoader; + private final com.google.ads.interactivemedia.v3.api.AdsLoader sdkAdsLoader; @Nullable private final AdEventListener applicationAdEventListener; @Nullable private final AdErrorListener applicationAdErrorListener; private final boolean isLiveStream; @@ -350,7 +400,8 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou private ImaServerSideAdInsertionMediaSource( MediaItem mediaItem, Player player, - com.google.ads.interactivemedia.v3.api.AdsLoader adsLoader, + AdsLoader adsLoader, + com.google.ads.interactivemedia.v3.api.AdsLoader sdkAdsLoader, StreamPlayer streamPlayer, MediaSource.Factory contentMediaSourceFactory, @Nullable AdEventListener applicationAdEventListener, @@ -358,18 +409,19 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou this.mediaItem = mediaItem; this.player = player; this.adsLoader = adsLoader; + this.sdkAdsLoader = sdkAdsLoader; this.streamPlayer = streamPlayer; this.contentMediaSourceFactory = contentMediaSourceFactory; this.applicationAdEventListener = applicationAdEventListener; this.applicationAdErrorListener = applicationAdErrorListener; componentListener = new ComponentListener(); - adPlaybackState = AdPlaybackState.NONE; mainHandler = Util.createHandlerForCurrentLooper(); Uri streamRequestUri = checkNotNull(mediaItem.localConfiguration).uri; isLiveStream = ImaServerSideAdInsertionUriBuilder.isLiveStream(streamRequestUri); adsId = ImaServerSideAdInsertionUriBuilder.getAdsId(streamRequestUri); loadVideoTimeoutMs = ImaServerSideAdInsertionUriBuilder.getLoadVideoTimeoutMs(streamRequestUri); streamRequest = ImaServerSideAdInsertionUriBuilder.createStreamRequest(streamRequestUri); + adPlaybackState = adsLoader.getAdPlaybackState(adsId); } @Override @@ -386,7 +438,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou player.addListener(componentListener); StreamManagerLoadable streamManagerLoadable = new StreamManagerLoadable( - adsLoader, + sdkAdsLoader, streamRequest, streamPlayer, applicationAdErrorListener, @@ -502,6 +554,10 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou splitAdPlaybackStateForPeriods(adPlaybackState, contentTimeline); streamPlayer.setAdPlaybackStates(adsId, splitAdPlaybackStates, contentTimeline); checkNotNull(serverSideAdInsertionMediaSource).setAdPlaybackStates(splitAdPlaybackStates); + if (!ImaServerSideAdInsertionUriBuilder.isLiveStream( + checkNotNull(mediaItem.localConfiguration).uri)) { + adsLoader.setAdPlaybackState(adsId, adPlaybackState); + } } }