Commit 559ce22d by bachinger Committed by Tianyi Feng

Do not use content timeline to calculate current position

In multi-period live streams, we can't use the content timeline if we want to
lookup a period from the public timeline by index or uid because it may be
that the content timeline has already been refreshed in the
`ImaServerSideAdInsertionMediaSource` but hasn't yet arrived in `ExoPlayerImpl`.

This change is taking the current position that needs to be reported
to the SDK every 200ms is not relying on the content timeline.

PiperOrigin-RevId: 520328126
parent 2d1875fd
...@@ -59,6 +59,7 @@ import com.google.ads.interactivemedia.v3.api.ImaSdkSettings; ...@@ -59,6 +59,7 @@ import com.google.ads.interactivemedia.v3.api.ImaSdkSettings;
import com.google.ads.interactivemedia.v3.api.StreamDisplayContainer; import com.google.ads.interactivemedia.v3.api.StreamDisplayContainer;
import com.google.ads.interactivemedia.v3.api.StreamManager; import com.google.ads.interactivemedia.v3.api.StreamManager;
import com.google.ads.interactivemedia.v3.api.StreamRequest; import com.google.ads.interactivemedia.v3.api.StreamRequest;
import com.google.ads.interactivemedia.v3.api.StreamRequest.StreamFormat;
import com.google.ads.interactivemedia.v3.api.player.VideoProgressUpdate; import com.google.ads.interactivemedia.v3.api.player.VideoProgressUpdate;
import com.google.ads.interactivemedia.v3.api.player.VideoStreamPlayer; import com.google.ads.interactivemedia.v3.api.player.VideoStreamPlayer;
import com.google.android.exoplayer2.Bundleable; import com.google.android.exoplayer2.Bundleable;
...@@ -101,6 +102,7 @@ import java.util.Collection; ...@@ -101,6 +102,7 @@ import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import org.checkerframework.checker.nullness.qual.EnsuresNonNull; import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
...@@ -156,7 +158,10 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou ...@@ -156,7 +158,10 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
public MediaSource createMediaSource(MediaItem mediaItem) { public MediaSource createMediaSource(MediaItem mediaItem) {
checkNotNull(mediaItem.localConfiguration); checkNotNull(mediaItem.localConfiguration);
Player player = checkNotNull(adsLoader.player); Player player = checkNotNull(adsLoader.player);
StreamPlayer streamPlayer = new StreamPlayer(player, mediaItem); Uri streamRequestUri = checkNotNull(mediaItem.localConfiguration).uri;
StreamRequest streamRequest =
ImaServerSideAdInsertionUriBuilder.createStreamRequest(streamRequestUri);
StreamPlayer streamPlayer = new StreamPlayer(player, mediaItem, streamRequest);
ImaSdkFactory imaSdkFactory = ImaSdkFactory.getInstance(); ImaSdkFactory imaSdkFactory = ImaSdkFactory.getInstance();
StreamDisplayContainer streamDisplayContainer = StreamDisplayContainer streamDisplayContainer =
createStreamDisplayContainer(imaSdkFactory, adsLoader.configuration, streamPlayer); createStreamDisplayContainer(imaSdkFactory, adsLoader.configuration, streamPlayer);
...@@ -165,8 +170,9 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou ...@@ -165,8 +170,9 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
adsLoader.context, adsLoader.configuration.imaSdkSettings, streamDisplayContainer); adsLoader.context, adsLoader.configuration.imaSdkSettings, streamDisplayContainer);
ImaServerSideAdInsertionMediaSource mediaSource = ImaServerSideAdInsertionMediaSource mediaSource =
new ImaServerSideAdInsertionMediaSource( new ImaServerSideAdInsertionMediaSource(
mediaItem,
player, player,
mediaItem,
streamRequest,
adsLoader, adsLoader,
imaAdsLoader, imaAdsLoader,
streamPlayer, streamPlayer,
...@@ -510,16 +516,18 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou ...@@ -510,16 +516,18 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
private AdPlaybackState adPlaybackState; private AdPlaybackState adPlaybackState;
private ImaServerSideAdInsertionMediaSource( private ImaServerSideAdInsertionMediaSource(
MediaItem mediaItem,
Player player, Player player,
MediaItem mediaItem,
StreamRequest streamRequest,
AdsLoader adsLoader, AdsLoader adsLoader,
com.google.ads.interactivemedia.v3.api.AdsLoader sdkAdsLoader, com.google.ads.interactivemedia.v3.api.AdsLoader sdkAdsLoader,
StreamPlayer streamPlayer, StreamPlayer streamPlayer,
MediaSource.Factory contentMediaSourceFactory, MediaSource.Factory contentMediaSourceFactory,
@Nullable AdEventListener applicationAdEventListener, @Nullable AdEventListener applicationAdEventListener,
@Nullable AdErrorEvent.AdErrorListener applicationAdErrorListener) { @Nullable AdErrorListener applicationAdErrorListener) {
this.mediaItem = mediaItem;
this.player = player; this.player = player;
this.mediaItem = mediaItem;
this.streamRequest = streamRequest;
this.adsLoader = adsLoader; this.adsLoader = adsLoader;
this.sdkAdsLoader = sdkAdsLoader; this.sdkAdsLoader = sdkAdsLoader;
this.streamPlayer = streamPlayer; this.streamPlayer = streamPlayer;
...@@ -533,7 +541,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou ...@@ -533,7 +541,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
adsId = ImaServerSideAdInsertionUriBuilder.getAdsId(streamRequestUri); adsId = ImaServerSideAdInsertionUriBuilder.getAdsId(streamRequestUri);
loadVideoTimeoutMs = ImaServerSideAdInsertionUriBuilder.getLoadVideoTimeoutMs(streamRequestUri); loadVideoTimeoutMs = ImaServerSideAdInsertionUriBuilder.getLoadVideoTimeoutMs(streamRequestUri);
streamRequest = ImaServerSideAdInsertionUriBuilder.createStreamRequest(streamRequestUri); streamRequest = ImaServerSideAdInsertionUriBuilder.createStreamRequest(streamRequestUri);
boolean isDashStream = streamRequest.getFormat().equals(StreamRequest.StreamFormat.DASH); boolean isDashStream = Objects.equals(streamRequest.getFormat(), StreamFormat.DASH);
componentListener = componentListener =
new ComponentListener( new ComponentListener(
isLiveStream isLiveStream
...@@ -677,7 +685,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou ...@@ -677,7 +685,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
private void invalidateServerSideAdInsertionAdPlaybackState() { private void invalidateServerSideAdInsertionAdPlaybackState() {
if (!adPlaybackState.equals(AdPlaybackState.NONE) && contentTimeline != null) { if (!adPlaybackState.equals(AdPlaybackState.NONE) && contentTimeline != null) {
ImmutableMap<Object, AdPlaybackState> splitAdPlaybackStates; ImmutableMap<Object, AdPlaybackState> splitAdPlaybackStates;
if (streamRequest.getFormat() == StreamRequest.StreamFormat.DASH) { if (streamRequest.getFormat() == StreamFormat.DASH) {
// DASH ad groups are always split by period. // DASH ad groups are always split by period.
splitAdPlaybackStates = splitAdPlaybackStateForPeriods(adPlaybackState, contentTimeline); splitAdPlaybackStates = splitAdPlaybackStateForPeriods(adPlaybackState, contentTimeline);
} else { } else {
...@@ -1094,6 +1102,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou ...@@ -1094,6 +1102,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
private final MediaItem mediaItem; private final MediaItem mediaItem;
private final Timeline.Window window; private final Timeline.Window window;
private final Timeline.Period period; private final Timeline.Period period;
private final boolean isDashStream;
private ImmutableMap<Object, AdPlaybackState> adPlaybackStates; private ImmutableMap<Object, AdPlaybackState> adPlaybackStates;
@Nullable private Timeline contentTimeline; @Nullable private Timeline contentTimeline;
...@@ -1101,9 +1110,10 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou ...@@ -1101,9 +1110,10 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
@Nullable private StreamLoadListener streamLoadListener; @Nullable private StreamLoadListener streamLoadListener;
/** Creates an instance. */ /** Creates an instance. */
public StreamPlayer(Player player, MediaItem mediaItem) { public StreamPlayer(Player player, MediaItem mediaItem, StreamRequest streamRequest) {
this.player = player; this.player = player;
this.mediaItem = mediaItem; this.mediaItem = mediaItem;
this.isDashStream = streamRequest.getFormat() == StreamFormat.DASH;
callbacks = new ArrayList<>(/* initialCapacity= */ 1); callbacks = new ArrayList<>(/* initialCapacity= */ 1);
adPlaybackStates = ImmutableMap.of(); adPlaybackStates = ImmutableMap.of();
window = new Timeline.Window(); window = new Timeline.Window();
...@@ -1169,30 +1179,42 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou ...@@ -1169,30 +1179,42 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
int currentPeriodIndex = player.getCurrentPeriodIndex(); int currentPeriodIndex = player.getCurrentPeriodIndex();
timeline.getPeriod(currentPeriodIndex, period, /* setIds= */ true); timeline.getPeriod(currentPeriodIndex, period, /* setIds= */ true);
timeline.getWindow(player.getCurrentMediaItemIndex(), window); timeline.getWindow(player.getCurrentMediaItemIndex(), window);
long streamPositionMs;
// We need the period of the content timeline because its period UIDs are the key used in the if (isDashStream && window.isLive()) {
// ad playback state map. The period UIDs of the public timeline are different (masking). // In multi-period live streams, we can't assume to find the same period in both timelines
Timeline.Period contentPeriod = // with a given period index. Calculate stream position from the period structure instead.
streamPositionMs =
player.isPlayingAd()
? window.windowStartTimeMs
+ usToMs(period.positionInWindowUs)
+ player.getCurrentPosition()
: window.windowStartTimeMs + player.getContentPosition();
} else {
// The map of ad playback states is keyed with the period UID of the content timeline. In
// timelines that do not change the periods (VOD and single period live), we can use the
// period index in both timelines.
Timeline.Period contentPeriod =
checkNotNull(contentTimeline)
.getPeriod(
currentPeriodIndex - window.firstPeriodIndex,
new Timeline.Period(),
/* setIds= */ true);
AdPlaybackState adPlaybackState = checkNotNull(adPlaybackStates.get(contentPeriod.uid));
// Calculate the stream position from the current position and the playback state.
streamPositionMs =
usToMs(ServerSideAdInsertionUtil.getStreamPositionUs(player, adPlaybackState));
if (window.windowStartTimeMs != C.TIME_UNSET) {
// Add the time since epoch at start of the window for live streams.
streamPositionMs += window.windowStartTimeMs + period.getPositionInWindowMs();
} else if (currentPeriodIndex > window.firstPeriodIndex) {
// Add the end position of the previous period in the underlying stream.
checkNotNull(contentTimeline) checkNotNull(contentTimeline)
.getPeriod( .getPeriod(
currentPeriodIndex - window.firstPeriodIndex, currentPeriodIndex - window.firstPeriodIndex - 1,
new Timeline.Period(), contentPeriod,
/* setIds= */ true); /* setIds= */ true);
AdPlaybackState adPlaybackState = checkNotNull(adPlaybackStates.get(contentPeriod.uid)); streamPositionMs += usToMs(contentPeriod.positionInWindowUs + contentPeriod.durationUs);
}
long streamPositionMs =
usToMs(ServerSideAdInsertionUtil.getStreamPositionUs(player, adPlaybackState));
if (window.windowStartTimeMs != C.TIME_UNSET) {
// Add the time since epoch at start of the window for live streams.
streamPositionMs += window.windowStartTimeMs + period.getPositionInWindowMs();
} else if (currentPeriodIndex > window.firstPeriodIndex) {
// Add the end position of the previous period in the underlying stream.
checkNotNull(contentTimeline)
.getPeriod(
currentPeriodIndex - window.firstPeriodIndex - 1,
contentPeriod,
/* setIds= */ true);
streamPositionMs += usToMs(contentPeriod.positionInWindowUs + contentPeriod.durationUs);
} }
return new VideoProgressUpdate( return new VideoProgressUpdate(
streamPositionMs, streamPositionMs,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment