mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +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
|
ad view group
|
||||||
([#7344](https://github.com/google/ExoPlayer/issues/7344)),
|
([#7344](https://github.com/google/ExoPlayer/issues/7344)),
|
||||||
([#8339](https://github.com/google/ExoPlayer/issues/8339)).
|
([#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:
|
* FFmpeg extension:
|
||||||
* Link the FFmpeg library statically, saving 350KB in binary size on
|
* Link the FFmpeg library statically, saving 350KB in binary size on
|
||||||
average.
|
average.
|
||||||
|
|
|
||||||
|
|
@ -1086,7 +1086,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
MediaPeriodId periodId;
|
MediaPeriodId periodId;
|
||||||
long periodPositionUs;
|
long periodPositionUs;
|
||||||
long requestedContentPosition;
|
long requestedContentPositionUs;
|
||||||
boolean seekPositionAdjusted;
|
boolean seekPositionAdjusted;
|
||||||
@Nullable
|
@Nullable
|
||||||
Pair<Object, Long> resolvedSeekPosition =
|
Pair<Object, Long> resolvedSeekPosition =
|
||||||
|
|
@ -1105,17 +1105,17 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
getPlaceholderFirstMediaPeriodPosition(playbackInfo.timeline);
|
getPlaceholderFirstMediaPeriodPosition(playbackInfo.timeline);
|
||||||
periodId = firstPeriodAndPosition.first;
|
periodId = firstPeriodAndPosition.first;
|
||||||
periodPositionUs = firstPeriodAndPosition.second;
|
periodPositionUs = firstPeriodAndPosition.second;
|
||||||
requestedContentPosition = C.TIME_UNSET;
|
requestedContentPositionUs = C.TIME_UNSET;
|
||||||
seekPositionAdjusted = !playbackInfo.timeline.isEmpty();
|
seekPositionAdjusted = !playbackInfo.timeline.isEmpty();
|
||||||
} else {
|
} else {
|
||||||
// Update the resolved seek position to take ads into account.
|
// Update the resolved seek position to take ads into account.
|
||||||
Object periodUid = resolvedSeekPosition.first;
|
Object periodUid = resolvedSeekPosition.first;
|
||||||
long resolvedContentPosition = resolvedSeekPosition.second;
|
long resolvedContentPositionUs = resolvedSeekPosition.second;
|
||||||
requestedContentPosition =
|
requestedContentPositionUs =
|
||||||
seekPosition.windowPositionUs == C.TIME_UNSET ? C.TIME_UNSET : resolvedContentPosition;
|
seekPosition.windowPositionUs == C.TIME_UNSET ? C.TIME_UNSET : resolvedContentPositionUs;
|
||||||
periodId =
|
periodId =
|
||||||
queue.resolveMediaPeriodIdForAds(
|
queue.resolveMediaPeriodIdForAds(
|
||||||
playbackInfo.timeline, periodUid, resolvedContentPosition);
|
playbackInfo.timeline, periodUid, resolvedContentPositionUs);
|
||||||
if (periodId.isAd()) {
|
if (periodId.isAd()) {
|
||||||
playbackInfo.timeline.getPeriodByUid(periodId.periodUid, period);
|
playbackInfo.timeline.getPeriodByUid(periodId.periodUid, period);
|
||||||
periodPositionUs =
|
periodPositionUs =
|
||||||
|
|
@ -1124,7 +1124,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
: 0;
|
: 0;
|
||||||
seekPositionAdjusted = true;
|
seekPositionAdjusted = true;
|
||||||
} else {
|
} else {
|
||||||
periodPositionUs = resolvedContentPosition;
|
periodPositionUs = resolvedContentPositionUs;
|
||||||
seekPositionAdjusted = seekPosition.windowPositionUs == C.TIME_UNSET;
|
seekPositionAdjusted = seekPosition.windowPositionUs == C.TIME_UNSET;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1175,11 +1175,11 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
/* newPeriodId= */ periodId,
|
/* newPeriodId= */ periodId,
|
||||||
/* oldTimeline= */ playbackInfo.timeline,
|
/* oldTimeline= */ playbackInfo.timeline,
|
||||||
/* oldPeriodId= */ playbackInfo.periodId,
|
/* oldPeriodId= */ playbackInfo.periodId,
|
||||||
/* positionForTargetOffsetOverrideUs= */ requestedContentPosition);
|
/* positionForTargetOffsetOverrideUs= */ requestedContentPositionUs);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
playbackInfo =
|
playbackInfo =
|
||||||
handlePositionDiscontinuity(periodId, periodPositionUs, requestedContentPosition);
|
handlePositionDiscontinuity(periodId, periodPositionUs, requestedContentPositionUs);
|
||||||
if (seekPositionAdjusted) {
|
if (seekPositionAdjusted) {
|
||||||
playbackInfoUpdate.setPositionDiscontinuity(Player.DISCONTINUITY_REASON_SEEK_ADJUSTMENT);
|
playbackInfoUpdate.setPositionDiscontinuity(Player.DISCONTINUITY_REASON_SEEK_ADJUSTMENT);
|
||||||
}
|
}
|
||||||
|
|
@ -2242,6 +2242,12 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
? emptyTrackSelectorResult
|
? emptyTrackSelectorResult
|
||||||
: playingPeriodHolder.getTrackSelectorResult();
|
: playingPeriodHolder.getTrackSelectorResult();
|
||||||
staticMetadata = extractMetadataFromTrackSelectionArray(trackSelectorResult.selections);
|
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)) {
|
} else if (!mediaPeriodId.equals(playbackInfo.periodId)) {
|
||||||
// Reset previously kept track info if unprepared and the period changes.
|
// Reset previously kept track info if unprepared and the period changes.
|
||||||
trackGroupArray = TrackGroupArray.EMPTY;
|
trackGroupArray = TrackGroupArray.EMPTY;
|
||||||
|
|
|
||||||
|
|
@ -2682,6 +2682,64 @@ public final class ExoPlayerTest {
|
||||||
assertThat(mediaSource.getCreatedMediaPeriods().get(3).adGroupIndex).isEqualTo(C.INDEX_UNSET);
|
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
|
@Test
|
||||||
public void repeatedSeeksToUnpreparedPeriodInSameWindowKeepsWindowSequenceNumber()
|
public void repeatedSeeksToUnpreparedPeriodInSameWindowKeepsWindowSequenceNumber()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue