mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Don't apply speed adjustment if windowStartTime is unknown.
This may happen for HLS live streams without program date time information. Issue: #8560 #minor-release PiperOrigin-RevId: 356227729
This commit is contained in:
parent
d21a47c4f1
commit
fbc8d6c801
3 changed files with 47 additions and 9 deletions
|
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
### dev-v2 (not yet released)
|
### dev-v2 (not yet released)
|
||||||
|
|
||||||
|
* Core library:
|
||||||
|
* Fix playback issue for HLS live streams without program date time
|
||||||
|
information ([#8560](https://github.com/google/ExoPlayer/issues/8560)).
|
||||||
* UI:
|
* UI:
|
||||||
* Add builder for `PlayerNotificationManager`.
|
* Add builder for `PlayerNotificationManager`.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -872,7 +872,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
// Adjust live playback speed to new position.
|
// Adjust live playback speed to new position.
|
||||||
if (playbackInfo.playWhenReady
|
if (playbackInfo.playWhenReady
|
||||||
&& playbackInfo.playbackState == Player.STATE_READY
|
&& playbackInfo.playbackState == Player.STATE_READY
|
||||||
&& isCurrentPeriodInMovingLiveWindow()
|
&& shouldUseLivePlaybackSpeedControl(playbackInfo.timeline, playbackInfo.periodId)
|
||||||
&& playbackInfo.playbackParameters.speed == 1f) {
|
&& playbackInfo.playbackParameters.speed == 1f) {
|
||||||
float adjustedSpeed =
|
float adjustedSpeed =
|
||||||
livePlaybackSpeedControl.getAdjustedPlaybackSpeed(
|
livePlaybackSpeedControl.getAdjustedPlaybackSpeed(
|
||||||
|
|
@ -1043,17 +1043,14 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
- (periodPositionUs + period.getPositionInWindowUs());
|
- (periodPositionUs + period.getPositionInWindowUs());
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isCurrentPeriodInMovingLiveWindow() {
|
private boolean shouldUseLivePlaybackSpeedControl(
|
||||||
return isInMovingLiveWindow(playbackInfo.timeline, playbackInfo.periodId);
|
Timeline timeline, MediaPeriodId mediaPeriodId) {
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isInMovingLiveWindow(Timeline timeline, MediaPeriodId mediaPeriodId) {
|
|
||||||
if (mediaPeriodId.isAd() || timeline.isEmpty()) {
|
if (mediaPeriodId.isAd() || timeline.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
int windowIndex = timeline.getPeriodByUid(mediaPeriodId.periodUid, period).windowIndex;
|
int windowIndex = timeline.getPeriodByUid(mediaPeriodId.periodUid, period).windowIndex;
|
||||||
timeline.getWindow(windowIndex, window);
|
timeline.getWindow(windowIndex, window);
|
||||||
return window.isLive() && window.isDynamic;
|
return window.isLive() && window.isDynamic && window.windowStartTimeMs != C.TIME_UNSET;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void scheduleNextWork(long thisOperationStartTimeMs, long intervalMs) {
|
private void scheduleNextWork(long thisOperationStartTimeMs, long intervalMs) {
|
||||||
|
|
@ -1717,7 +1714,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
}
|
}
|
||||||
// Renderers are ready and we're loading. Ask the LoadControl whether to transition.
|
// Renderers are ready and we're loading. Ask the LoadControl whether to transition.
|
||||||
long targetLiveOffsetUs =
|
long targetLiveOffsetUs =
|
||||||
isInMovingLiveWindow(playbackInfo.timeline, queue.getPlayingPeriod().info.id)
|
shouldUseLivePlaybackSpeedControl(playbackInfo.timeline, queue.getPlayingPeriod().info.id)
|
||||||
? livePlaybackSpeedControl.getTargetLiveOffsetUs()
|
? livePlaybackSpeedControl.getTargetLiveOffsetUs()
|
||||||
: C.TIME_UNSET;
|
: C.TIME_UNSET;
|
||||||
MediaPeriodHolder loadingHolder = queue.getLoadingPeriod();
|
MediaPeriodHolder loadingHolder = queue.getLoadingPeriod();
|
||||||
|
|
@ -1823,7 +1820,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
Timeline oldTimeline,
|
Timeline oldTimeline,
|
||||||
MediaPeriodId oldPeriodId,
|
MediaPeriodId oldPeriodId,
|
||||||
long positionForTargetOffsetOverrideUs) {
|
long positionForTargetOffsetOverrideUs) {
|
||||||
if (newTimeline.isEmpty() || !isInMovingLiveWindow(newTimeline, newPeriodId)) {
|
if (newTimeline.isEmpty() || !shouldUseLivePlaybackSpeedControl(newTimeline, newPeriodId)) {
|
||||||
// Live playback speed control is unused.
|
// Live playback speed control is unused.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,7 @@ import com.google.android.exoplayer2.source.MediaSource;
|
||||||
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
|
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
|
||||||
import com.google.android.exoplayer2.source.MediaSourceEventListener;
|
import com.google.android.exoplayer2.source.MediaSourceEventListener;
|
||||||
import com.google.android.exoplayer2.source.SilenceMediaSource;
|
import com.google.android.exoplayer2.source.SilenceMediaSource;
|
||||||
|
import com.google.android.exoplayer2.source.SinglePeriodTimeline;
|
||||||
import com.google.android.exoplayer2.source.TrackGroup;
|
import com.google.android.exoplayer2.source.TrackGroup;
|
||||||
import com.google.android.exoplayer2.source.TrackGroupArray;
|
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||||
import com.google.android.exoplayer2.source.ads.AdPlaybackState;
|
import com.google.android.exoplayer2.source.ads.AdPlaybackState;
|
||||||
|
|
@ -83,6 +84,7 @@ import com.google.android.exoplayer2.testutil.ExoPlayerTestRunner;
|
||||||
import com.google.android.exoplayer2.testutil.FakeAdaptiveDataSet;
|
import com.google.android.exoplayer2.testutil.FakeAdaptiveDataSet;
|
||||||
import com.google.android.exoplayer2.testutil.FakeAdaptiveMediaSource;
|
import com.google.android.exoplayer2.testutil.FakeAdaptiveMediaSource;
|
||||||
import com.google.android.exoplayer2.testutil.FakeChunkSource;
|
import com.google.android.exoplayer2.testutil.FakeChunkSource;
|
||||||
|
import com.google.android.exoplayer2.testutil.FakeClock;
|
||||||
import com.google.android.exoplayer2.testutil.FakeDataSource;
|
import com.google.android.exoplayer2.testutil.FakeDataSource;
|
||||||
import com.google.android.exoplayer2.testutil.FakeMediaClockRenderer;
|
import com.google.android.exoplayer2.testutil.FakeMediaClockRenderer;
|
||||||
import com.google.android.exoplayer2.testutil.FakeMediaPeriod;
|
import com.google.android.exoplayer2.testutil.FakeMediaPeriod;
|
||||||
|
|
@ -8803,6 +8805,42 @@ public final class ExoPlayerTest {
|
||||||
assertThat(liveOffsetAtEnd).isIn(Range.closed(1_900L, 2_100L));
|
assertThat(liveOffsetAtEnd).isIn(Range.closed(1_900L, 2_100L));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void targetLiveOffsetInMedia_unknownWindowStartTime_doesNotAdjustLiveOffset()
|
||||||
|
throws Exception {
|
||||||
|
FakeClock fakeClock = new AutoAdvancingFakeClock(/* initialTimeMs= */ 987_654_321L);
|
||||||
|
ExoPlayer player = new TestExoPlayerBuilder(context).setClock(fakeClock).build();
|
||||||
|
MediaItem mediaItem =
|
||||||
|
new MediaItem.Builder().setUri(Uri.EMPTY).setLiveTargetOffsetMs(4_000).build();
|
||||||
|
Timeline liveTimeline =
|
||||||
|
new SinglePeriodTimeline(
|
||||||
|
/* presentationStartTimeMs= */ C.TIME_UNSET,
|
||||||
|
/* windowStartTimeMs= */ C.TIME_UNSET,
|
||||||
|
/* elapsedRealtimeEpochOffsetMs= */ C.TIME_UNSET,
|
||||||
|
/* periodDurationUs= */ 1000 * C.MICROS_PER_SECOND,
|
||||||
|
/* windowDurationUs= */ 1000 * C.MICROS_PER_SECOND,
|
||||||
|
/* windowPositionInPeriodUs= */ 0,
|
||||||
|
/* windowDefaultStartPositionUs= */ 0,
|
||||||
|
/* isSeekable= */ true,
|
||||||
|
/* isDynamic= */ true,
|
||||||
|
/* manifest= */ null,
|
||||||
|
mediaItem,
|
||||||
|
mediaItem.liveConfiguration);
|
||||||
|
player.pause();
|
||||||
|
player.setMediaSource(new FakeMediaSource(liveTimeline));
|
||||||
|
player.prepare();
|
||||||
|
TestPlayerRunHelper.runUntilPlaybackState(player, Player.STATE_READY);
|
||||||
|
|
||||||
|
long playbackStartTimeMs = fakeClock.elapsedRealtime();
|
||||||
|
TestPlayerRunHelper.playUntilPosition(player, /* windowIndex= */ 0, /* positionMs= */ 999_000);
|
||||||
|
long playbackEndTimeMs = fakeClock.elapsedRealtime();
|
||||||
|
player.release();
|
||||||
|
|
||||||
|
// Assert that the time it took to play 999 seconds of media is 999 seconds (asserting that no
|
||||||
|
// playback speed adjustment was used).
|
||||||
|
assertThat(playbackEndTimeMs - playbackStartTimeMs).isEqualTo(999_000);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void noTargetLiveOffsetInMedia_doesNotAdjustLiveOffset() throws Exception {
|
public void noTargetLiveOffsetInMedia_doesNotAdjustLiveOffset() throws Exception {
|
||||||
long windowStartUnixTimeMs = 987_654_321_000L;
|
long windowStartUnixTimeMs = 987_654_321_000L;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue