Avoid clipping live offset override to min/max offsets

The live offset override is used to replace the media-defined
live offset after user seeks to ensure the live adjustment adjusts
to the new user-provided live offset and doesn't go back to the
original one.

However, the code currently clips the override to the min/max
live offsets defined in LiveConfiguration. This is useful to
clip the default value (in case of inconsistent values in the media),
but the clipping shouldn't be applied to user overrides as
the player will then adjust the position back to the min/max
and doesn't stay at the desired user position.

See 2416d99857 (r132871601)

#minor-release

PiperOrigin-RevId: 584311004
This commit is contained in:
tonihei 2023-11-21 06:42:49 -08:00 committed by Copybara-Service
parent fde142d66e
commit af0282b9db
4 changed files with 68 additions and 13 deletions

View File

@ -24,6 +24,9 @@
by default with null `ImageOutput` and `ImageDecoder.Factory.DEFAULT`.
* Emit `Player.Listener.onPositionDiscontinuity` event when silence is
skipped ([#765](https://github.com/androidx/media/issues/765)).
* Fix issue where manual seeks outside of the
`LiveConfiguration.min/maxOffset` range keep adjusting the offset back
to `min/maxOffset`.
* Transformer:
* Add support for flattening H.265/HEVC SEF slow motion videos.
* Increase transmuxing speed, especially for 'remove video' edits.

View File

@ -379,15 +379,16 @@ public final class DefaultLivePlaybackSpeedControl implements LivePlaybackSpeedC
private void maybeResetTargetLiveOffsetUs() {
long idealOffsetUs = C.TIME_UNSET;
if (mediaConfigurationTargetLiveOffsetUs != C.TIME_UNSET) {
idealOffsetUs =
targetLiveOffsetOverrideUs != C.TIME_UNSET
? targetLiveOffsetOverrideUs
: mediaConfigurationTargetLiveOffsetUs;
if (minTargetLiveOffsetUs != C.TIME_UNSET && idealOffsetUs < minTargetLiveOffsetUs) {
idealOffsetUs = minTargetLiveOffsetUs;
}
if (maxTargetLiveOffsetUs != C.TIME_UNSET && idealOffsetUs > maxTargetLiveOffsetUs) {
idealOffsetUs = maxTargetLiveOffsetUs;
if (targetLiveOffsetOverrideUs != C.TIME_UNSET) {
idealOffsetUs = targetLiveOffsetOverrideUs;
} else {
idealOffsetUs = mediaConfigurationTargetLiveOffsetUs;
if (minTargetLiveOffsetUs != C.TIME_UNSET && idealOffsetUs < minTargetLiveOffsetUs) {
idealOffsetUs = minTargetLiveOffsetUs;
}
if (maxTargetLiveOffsetUs != C.TIME_UNSET && idealOffsetUs > maxTargetLiveOffsetUs) {
idealOffsetUs = maxTargetLiveOffsetUs;
}
}
}
if (idealTargetLiveOffsetUs == idealOffsetUs) {

View File

@ -112,7 +112,7 @@ public class DefaultLivePlaybackSpeedControlTest {
@Test
public void
getTargetLiveOffsetUs_afterSetTargetLiveOffsetOverrideUsGreaterThanMax_returnsMaxLiveOffset() {
getTargetLiveOffsetUs_afterSetTargetLiveOffsetOverrideUsGreaterThanMax_returnsOverride() {
DefaultLivePlaybackSpeedControl defaultLivePlaybackSpeedControl =
new DefaultLivePlaybackSpeedControl.Builder().build();
@ -128,12 +128,12 @@ public class DefaultLivePlaybackSpeedControlTest {
long targetLiveOffsetUs = defaultLivePlaybackSpeedControl.getTargetLiveOffsetUs();
assertThat(targetLiveOffsetUs).isEqualTo(400_000);
assertThat(targetLiveOffsetUs).isEqualTo(123_456_789);
}
@Test
public void
getTargetLiveOffsetUs_afterSetTargetLiveOffsetOverrideUsLessThanMin_returnsMinLiveOffset() {
getTargetLiveOffsetUs_afterSetTargetLiveOffsetOverrideUsLessThanMin_returnsOverride() {
DefaultLivePlaybackSpeedControl defaultLivePlaybackSpeedControl =
new DefaultLivePlaybackSpeedControl.Builder().build();
@ -149,7 +149,7 @@ public class DefaultLivePlaybackSpeedControlTest {
long targetLiveOffsetUs = defaultLivePlaybackSpeedControl.getTargetLiveOffsetUs();
assertThat(targetLiveOffsetUs).isEqualTo(5_000);
assertThat(targetLiveOffsetUs).isEqualTo(3_141);
}
@Test

View File

@ -10598,6 +10598,57 @@ public final class ExoPlayerTest {
assertThat(liveOffsetAtEnd).isIn(Range.closed(1_900L, 2_100L));
}
@Test
public void targetLiveOffsetInMedia_withUserSeekOutsideMaxLivOffset_adjustsLiveOffsetToSeek()
throws Exception {
long windowStartUnixTimeMs = 987_654_321_000L;
long nowUnixTimeMs = windowStartUnixTimeMs + 20_000;
ExoPlayer player =
new TestExoPlayerBuilder(context)
.setClock(
new FakeClock(/* initialTimeMs= */ nowUnixTimeMs, /* isAutoAdvancing= */ true))
.build();
Timeline timeline =
new FakeTimeline(
new TimelineWindowDefinition(
/* periodCount= */ 1,
/* id= */ 0,
/* isSeekable= */ true,
/* isDynamic= */ true,
/* isLive= */ true,
/* isPlaceholder= */ false,
/* durationUs= */ 1000 * C.MICROS_PER_SECOND,
/* defaultPositionUs= */ 8 * C.MICROS_PER_SECOND,
/* windowOffsetInFirstPeriodUs= */ Util.msToUs(windowStartUnixTimeMs),
ImmutableList.of(AdPlaybackState.NONE),
new MediaItem.Builder()
.setUri(Uri.EMPTY)
.setLiveConfiguration(
new MediaItem.LiveConfiguration.Builder()
.setTargetOffsetMs(9_000)
.setMaxOffsetMs(10_000)
.build())
.build()));
player.pause();
player.setMediaSource(new FakeMediaSource(timeline));
player.prepare();
TestPlayerRunHelper.runUntilPlaybackState(player, Player.STATE_READY);
long liveOffsetAtStart = player.getCurrentLiveOffset();
// Verify test setup (now = 20 seconds in live window, default start position = 8 seconds).
assertThat(liveOffsetAtStart).isIn(Range.closed(11_900L, 12_100L));
// Seek to a live offset of 15 seconds (outside of declared max offset of 10 seconds).
player.seekTo(5_000);
// Play until close to the end of the available live window.
TestPlayerRunHelper.playUntilPosition(
player, /* mediaItemIndex= */ 0, /* positionMs= */ 999_000);
long liveOffsetAtEnd = player.getCurrentLiveOffset();
player.release();
// Assert the live offset adjustment was permanent.
assertThat(liveOffsetAtEnd).isIn(Range.closed(14_100L, 15_900L));
}
@Test
public void targetLiveOffsetInMedia_withTimelineUpdate_adjustsLiveOffsetToLatestTimeline()
throws Exception {