From 81a9293072faabdd95f241f84b29e32217982ac9 Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Thu, 11 Oct 2018 04:27:22 -0700 Subject: [PATCH] Project start position for preroll ad to content transitions ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=216675981 --- RELEASENOTES.md | 3 ++ .../android/exoplayer2/MediaPeriodQueue.java | 41 +++++++++++++------ 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 6433788846..90ba31c623 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -22,6 +22,9 @@ * Fix issue where buffered position is not updated correctly when transitioning between periods ([#4899](https://github.com/google/ExoPlayer/issues/4899)). +* IMA extension: + * For preroll to live stream transitions, project forward the loading position + to avoid being behind the live window. ### 2.9.0 ### diff --git a/library/core/src/main/java/com/google/android/exoplayer2/MediaPeriodQueue.java b/library/core/src/main/java/com/google/android/exoplayer2/MediaPeriodQueue.java index 6370299334..40bc658953 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/MediaPeriodQueue.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/MediaPeriodQueue.java @@ -533,6 +533,11 @@ import com.google.android.exoplayer2.util.Assertions; // until the timeline is updated. Store whether the next timeline period is ready when the // timeline is updated, to avoid repeatedly checking the same timeline. MediaPeriodInfo mediaPeriodInfo = mediaPeriodHolder.info; + // The expected delay until playback transitions to the new period is equal the duration of + // media that's currently buffered (assuming no interruptions). This is used to project forward + // the start position for transitions to new windows. + long bufferedDurationUs = + mediaPeriodHolder.getRendererOffset() + mediaPeriodInfo.durationUs - rendererPositionUs; if (mediaPeriodInfo.isLastInTimelinePeriod) { int currentPeriodIndex = timeline.getIndexOfPeriod(mediaPeriodInfo.id.periodUid); int nextPeriodIndex = @@ -550,19 +555,15 @@ import com.google.android.exoplayer2.util.Assertions; long windowSequenceNumber = mediaPeriodInfo.id.windowSequenceNumber; if (timeline.getWindow(nextWindowIndex, window).firstPeriodIndex == nextPeriodIndex) { // We're starting to buffer a new window. When playback transitions to this window we'll - // want it to be from its default start position. The expected delay until playback - // transitions is equal the duration of media that's currently buffered (assuming no - // interruptions). Hence we project the default start position forward by the duration of - // the buffer, and start buffering from this point. - long defaultPositionProjectionUs = - mediaPeriodHolder.getRendererOffset() + mediaPeriodInfo.durationUs - rendererPositionUs; + // want it to be from its default start position, so project the default start position + // forward by the duration of the buffer, and start buffering from this point. Pair defaultPosition = timeline.getPeriodPosition( window, period, nextWindowIndex, - C.TIME_UNSET, - Math.max(0, defaultPositionProjectionUs)); + /* windowPositionUs= */ C.TIME_UNSET, + /* defaultPositionProjectionUs= */ Math.max(0, bufferedDurationUs)); if (defaultPosition == null) { return null; } @@ -603,11 +604,27 @@ import com.google.android.exoplayer2.util.Assertions; mediaPeriodInfo.contentPositionUs, currentPeriodId.windowSequenceNumber); } else { - // Play content from the ad group position. + // Play content from the ad group position. As a special case, if we're transitioning from a + // preroll ad group to content and there are no other ad groups, project the start position + // forward as if this were a transition to a new window. No attempt is made to handle + // midrolls in live streams, as it's unclear what content position should play after an ad + // (server-side dynamic ad insertion is more appropriate for this use case). + long startPositionUs = mediaPeriodInfo.contentPositionUs; + if (period.getAdGroupCount() == 1 && period.getAdGroupTimeUs(0) == 0) { + Pair defaultPosition = + timeline.getPeriodPosition( + window, + period, + period.windowIndex, + /* windowPositionUs= */ C.TIME_UNSET, + /* defaultPositionProjectionUs= */ Math.max(0, bufferedDurationUs)); + if (defaultPosition == null) { + return null; + } + startPositionUs = defaultPosition.second; + } return getMediaPeriodInfoForContent( - currentPeriodId.periodUid, - mediaPeriodInfo.contentPositionUs, - currentPeriodId.windowSequenceNumber); + currentPeriodId.periodUid, startPositionUs, currentPeriodId.windowSequenceNumber); } } else if (mediaPeriodInfo.id.endPositionUs != C.TIME_END_OF_SOURCE) { // Play the next ad group if it's available.