mirror of
https://github.com/samsonjs/media.git
synced 2026-03-26 09:35:47 +00:00
Update requested content position for ads on seek
PiperOrigin-RevId: 352011053
This commit is contained in:
parent
d4a84b88b5
commit
e869d5dbf9
3 changed files with 76 additions and 9 deletions
|
|
@ -215,6 +215,9 @@
|
|||
ad view group
|
||||
([#7344](https://github.com/google/ExoPlayer/issues/7344)),
|
||||
([#8339](https://github.com/google/ExoPlayer/issues/8339)).
|
||||
* Fix a bug that could cause the next content position played after a
|
||||
seek to snap back to the cue point of the preceding ad, rather than
|
||||
the requested content position.
|
||||
* FFmpeg extension:
|
||||
* Link the FFmpeg library statically, saving 350KB in binary size on
|
||||
average.
|
||||
|
|
|
|||
|
|
@ -1086,7 +1086,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
|
||||
MediaPeriodId periodId;
|
||||
long periodPositionUs;
|
||||
long requestedContentPosition;
|
||||
long requestedContentPositionUs;
|
||||
boolean seekPositionAdjusted;
|
||||
@Nullable
|
||||
Pair<Object, Long> resolvedSeekPosition =
|
||||
|
|
@ -1105,17 +1105,17 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
getPlaceholderFirstMediaPeriodPosition(playbackInfo.timeline);
|
||||
periodId = firstPeriodAndPosition.first;
|
||||
periodPositionUs = firstPeriodAndPosition.second;
|
||||
requestedContentPosition = C.TIME_UNSET;
|
||||
requestedContentPositionUs = C.TIME_UNSET;
|
||||
seekPositionAdjusted = !playbackInfo.timeline.isEmpty();
|
||||
} else {
|
||||
// Update the resolved seek position to take ads into account.
|
||||
Object periodUid = resolvedSeekPosition.first;
|
||||
long resolvedContentPosition = resolvedSeekPosition.second;
|
||||
requestedContentPosition =
|
||||
seekPosition.windowPositionUs == C.TIME_UNSET ? C.TIME_UNSET : resolvedContentPosition;
|
||||
long resolvedContentPositionUs = resolvedSeekPosition.second;
|
||||
requestedContentPositionUs =
|
||||
seekPosition.windowPositionUs == C.TIME_UNSET ? C.TIME_UNSET : resolvedContentPositionUs;
|
||||
periodId =
|
||||
queue.resolveMediaPeriodIdForAds(
|
||||
playbackInfo.timeline, periodUid, resolvedContentPosition);
|
||||
playbackInfo.timeline, periodUid, resolvedContentPositionUs);
|
||||
if (periodId.isAd()) {
|
||||
playbackInfo.timeline.getPeriodByUid(periodId.periodUid, period);
|
||||
periodPositionUs =
|
||||
|
|
@ -1124,7 +1124,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
: 0;
|
||||
seekPositionAdjusted = true;
|
||||
} else {
|
||||
periodPositionUs = resolvedContentPosition;
|
||||
periodPositionUs = resolvedContentPositionUs;
|
||||
seekPositionAdjusted = seekPosition.windowPositionUs == C.TIME_UNSET;
|
||||
}
|
||||
}
|
||||
|
|
@ -1175,11 +1175,11 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
/* newPeriodId= */ periodId,
|
||||
/* oldTimeline= */ playbackInfo.timeline,
|
||||
/* oldPeriodId= */ playbackInfo.periodId,
|
||||
/* positionForTargetOffsetOverrideUs= */ requestedContentPosition);
|
||||
/* positionForTargetOffsetOverrideUs= */ requestedContentPositionUs);
|
||||
}
|
||||
} finally {
|
||||
playbackInfo =
|
||||
handlePositionDiscontinuity(periodId, periodPositionUs, requestedContentPosition);
|
||||
handlePositionDiscontinuity(periodId, periodPositionUs, requestedContentPositionUs);
|
||||
if (seekPositionAdjusted) {
|
||||
playbackInfoUpdate.setPositionDiscontinuity(Player.DISCONTINUITY_REASON_SEEK_ADJUSTMENT);
|
||||
}
|
||||
|
|
@ -2242,6 +2242,12 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
? emptyTrackSelectorResult
|
||||
: playingPeriodHolder.getTrackSelectorResult();
|
||||
staticMetadata = extractMetadataFromTrackSelectionArray(trackSelectorResult.selections);
|
||||
// Ensure the media period queue requested content position matches the new playback info.
|
||||
if (playingPeriodHolder != null
|
||||
&& playingPeriodHolder.info.requestedContentPositionUs != contentPositionUs) {
|
||||
playingPeriodHolder.info =
|
||||
playingPeriodHolder.info.copyWithRequestedContentPositionUs(contentPositionUs);
|
||||
}
|
||||
} else if (!mediaPeriodId.equals(playbackInfo.periodId)) {
|
||||
// Reset previously kept track info if unprepared and the period changes.
|
||||
trackGroupArray = TrackGroupArray.EMPTY;
|
||||
|
|
|
|||
|
|
@ -2682,6 +2682,64 @@ public final class ExoPlayerTest {
|
|||
assertThat(mediaSource.getCreatedMediaPeriods().get(3).adGroupIndex).isEqualTo(C.INDEX_UNSET);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void seekPastBufferingMidroll_playsAdAndThenContentFromSeekPosition() throws Exception {
|
||||
long adGroupWindowTimeMs = 1_000;
|
||||
long seekPositionMs = 95_000;
|
||||
long contentDurationMs = 100_000;
|
||||
AdPlaybackState adPlaybackState =
|
||||
FakeTimeline.createAdPlaybackState(
|
||||
/* adsPerAdGroup= */ 1,
|
||||
/* adGroupTimesUs...= */ TimelineWindowDefinition
|
||||
.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US
|
||||
+ C.msToUs(adGroupWindowTimeMs));
|
||||
Timeline timeline =
|
||||
new FakeTimeline(
|
||||
new TimelineWindowDefinition(
|
||||
/* periodCount= */ 1,
|
||||
/* id= */ 0,
|
||||
/* isSeekable= */ true,
|
||||
/* isDynamic= */ false,
|
||||
/* durationUs= */ C.msToUs(contentDurationMs),
|
||||
adPlaybackState));
|
||||
AtomicBoolean hasCreatedAdMediaPeriod = new AtomicBoolean();
|
||||
FakeMediaSource mediaSource =
|
||||
new FakeMediaSource(timeline) {
|
||||
@Override
|
||||
public MediaPeriod createPeriod(
|
||||
MediaPeriodId id, Allocator allocator, long startPositionUs) {
|
||||
if (id.adGroupIndex == 0) {
|
||||
hasCreatedAdMediaPeriod.set(true);
|
||||
}
|
||||
return super.createPeriod(id, allocator, startPositionUs);
|
||||
}
|
||||
};
|
||||
SimpleExoPlayer player = new TestExoPlayerBuilder(context).build();
|
||||
player.setMediaSource(mediaSource);
|
||||
// Throw on the playback thread if the player position reaches a value that is just less than
|
||||
// seek position. This ensures that playback stops and the assertion on the player position
|
||||
// below fails, even if a long time passes between detecting the discontinuity and asserting.
|
||||
player
|
||||
.createMessage(
|
||||
(messageType, payload) -> {
|
||||
throw new IllegalStateException();
|
||||
})
|
||||
.setPosition(seekPositionMs - 1)
|
||||
.send();
|
||||
player.pause();
|
||||
player.prepare();
|
||||
|
||||
// Block until the midroll has started buffering, then seek after the midroll before playing.
|
||||
runMainLooperUntil(hasCreatedAdMediaPeriod::get);
|
||||
player.seekTo(seekPositionMs);
|
||||
player.play();
|
||||
|
||||
// When the ad finishes, the player position should be at or after the requested seek position.
|
||||
TestPlayerRunHelper.runUntilPositionDiscontinuity(
|
||||
player, Player.DISCONTINUITY_REASON_AD_INSERTION);
|
||||
assertThat(player.getCurrentPosition()).isAtLeast(seekPositionMs);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void repeatedSeeksToUnpreparedPeriodInSameWindowKeepsWindowSequenceNumber()
|
||||
throws Exception {
|
||||
|
|
|
|||
Loading…
Reference in a new issue