mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Fix period transition with non-zero start position.
Period transitions with non-zero start position happen too early as the playing period is advanced as soon as the renderer offset is reached not taking into account that the start position needs to be added to that. Issue:#4583 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=206310328
This commit is contained in:
parent
6a2f94ec4b
commit
a237ae1810
4 changed files with 77 additions and 3 deletions
|
|
@ -77,6 +77,8 @@
|
||||||
`MediaCodecRenderer`, and provide an (optional) `MediaCodecSelector` that
|
`MediaCodecRenderer`, and provide an (optional) `MediaCodecSelector` that
|
||||||
falls back to less preferred decoders like `MediaCodec.createDecoderByType`
|
falls back to less preferred decoders like `MediaCodec.createDecoderByType`
|
||||||
([#273](https://github.com/google/ExoPlayer/issues/273)).
|
([#273](https://github.com/google/ExoPlayer/issues/273)).
|
||||||
|
* Fix where transitions to clipped media sources happened too early
|
||||||
|
([#4583](https://github.com/google/ExoPlayer/issues/4583)).
|
||||||
|
|
||||||
### 2.8.3 ###
|
### 2.8.3 ###
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1403,8 +1403,9 @@ import java.util.Collections;
|
||||||
MediaPeriodHolder playingPeriodHolder = queue.getPlayingPeriod();
|
MediaPeriodHolder playingPeriodHolder = queue.getPlayingPeriod();
|
||||||
MediaPeriodHolder readingPeriodHolder = queue.getReadingPeriod();
|
MediaPeriodHolder readingPeriodHolder = queue.getReadingPeriod();
|
||||||
boolean advancedPlayingPeriod = false;
|
boolean advancedPlayingPeriod = false;
|
||||||
while (playWhenReady && playingPeriodHolder != readingPeriodHolder
|
while (playWhenReady
|
||||||
&& rendererPositionUs >= playingPeriodHolder.next.rendererPositionOffsetUs) {
|
&& playingPeriodHolder != readingPeriodHolder
|
||||||
|
&& rendererPositionUs >= playingPeriodHolder.next.getStartPositionRendererTime()) {
|
||||||
// All enabled renderers' streams have been read to the end, and the playback position reached
|
// All enabled renderers' streams have been read to the end, and the playback position reached
|
||||||
// the end of the playing period, so advance playback to the next period.
|
// the end of the playing period, so advance playback to the next period.
|
||||||
if (advancedPlayingPeriod) {
|
if (advancedPlayingPeriod) {
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,6 @@ import com.google.android.exoplayer2.util.Assertions;
|
||||||
public final SampleStream[] sampleStreams;
|
public final SampleStream[] sampleStreams;
|
||||||
public final boolean[] mayRetainStreamFlags;
|
public final boolean[] mayRetainStreamFlags;
|
||||||
|
|
||||||
public long rendererPositionOffsetUs;
|
|
||||||
public boolean prepared;
|
public boolean prepared;
|
||||||
public boolean hasEnabledTracks;
|
public boolean hasEnabledTracks;
|
||||||
public MediaPeriodInfo info;
|
public MediaPeriodInfo info;
|
||||||
|
|
@ -51,6 +50,7 @@ import com.google.android.exoplayer2.util.Assertions;
|
||||||
private final TrackSelector trackSelector;
|
private final TrackSelector trackSelector;
|
||||||
private final MediaSource mediaSource;
|
private final MediaSource mediaSource;
|
||||||
|
|
||||||
|
private long rendererPositionOffsetUs;
|
||||||
private TrackSelectorResult periodTrackSelectorResult;
|
private TrackSelectorResult periodTrackSelectorResult;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -105,6 +105,10 @@ import com.google.android.exoplayer2.util.Assertions;
|
||||||
return rendererPositionOffsetUs;
|
return rendererPositionOffsetUs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long getStartPositionRendererTime() {
|
||||||
|
return info.startPositionUs + rendererPositionOffsetUs;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isFullyBuffered() {
|
public boolean isFullyBuffered() {
|
||||||
return prepared
|
return prepared
|
||||||
&& (!hasEnabledTracks || mediaPeriod.getBufferedPositionUs() == C.TIME_END_OF_SOURCE);
|
&& (!hasEnabledTracks || mediaPeriod.getBufferedPositionUs() == C.TIME_END_OF_SOURCE);
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,10 @@ import static org.junit.Assert.fail;
|
||||||
|
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
|
import com.google.android.exoplayer2.Player.DiscontinuityReason;
|
||||||
import com.google.android.exoplayer2.Player.EventListener;
|
import com.google.android.exoplayer2.Player.EventListener;
|
||||||
import com.google.android.exoplayer2.Timeline.Window;
|
import com.google.android.exoplayer2.Timeline.Window;
|
||||||
|
import com.google.android.exoplayer2.source.ClippingMediaSource;
|
||||||
import com.google.android.exoplayer2.source.ConcatenatingMediaSource;
|
import com.google.android.exoplayer2.source.ConcatenatingMediaSource;
|
||||||
import com.google.android.exoplayer2.source.MediaSource;
|
import com.google.android.exoplayer2.source.MediaSource;
|
||||||
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
|
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
|
||||||
|
|
@ -32,6 +34,7 @@ import com.google.android.exoplayer2.source.ads.AdPlaybackState;
|
||||||
import com.google.android.exoplayer2.testutil.ActionSchedule;
|
import com.google.android.exoplayer2.testutil.ActionSchedule;
|
||||||
import com.google.android.exoplayer2.testutil.ActionSchedule.PlayerRunnable;
|
import com.google.android.exoplayer2.testutil.ActionSchedule.PlayerRunnable;
|
||||||
import com.google.android.exoplayer2.testutil.ActionSchedule.PlayerTarget;
|
import com.google.android.exoplayer2.testutil.ActionSchedule.PlayerTarget;
|
||||||
|
import com.google.android.exoplayer2.testutil.AutoAdvancingFakeClock;
|
||||||
import com.google.android.exoplayer2.testutil.ExoPlayerTestRunner;
|
import com.google.android.exoplayer2.testutil.ExoPlayerTestRunner;
|
||||||
import com.google.android.exoplayer2.testutil.ExoPlayerTestRunner.Builder;
|
import com.google.android.exoplayer2.testutil.ExoPlayerTestRunner.Builder;
|
||||||
import com.google.android.exoplayer2.testutil.FakeMediaClockRenderer;
|
import com.google.android.exoplayer2.testutil.FakeMediaClockRenderer;
|
||||||
|
|
@ -46,6 +49,7 @@ import com.google.android.exoplayer2.testutil.FakeTrackSelector;
|
||||||
import com.google.android.exoplayer2.testutil.RobolectricUtil;
|
import com.google.android.exoplayer2.testutil.RobolectricUtil;
|
||||||
import com.google.android.exoplayer2.upstream.Allocator;
|
import com.google.android.exoplayer2.upstream.Allocator;
|
||||||
import com.google.android.exoplayer2.upstream.TransferListener;
|
import com.google.android.exoplayer2.upstream.TransferListener;
|
||||||
|
import com.google.android.exoplayer2.util.Clock;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
@ -2211,6 +2215,69 @@ public final class ExoPlayerTest {
|
||||||
assertThat(eventListenerPlayWhenReady).containsExactly(true, true, true, false).inOrder();
|
assertThat(eventListenerPlayWhenReady).containsExactly(true, true, true, false).inOrder();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testClippedLoopedPeriodsArePlayedFully() throws Exception {
|
||||||
|
long startPositionUs = 300_000;
|
||||||
|
long expectedDurationUs = 700_000;
|
||||||
|
MediaSource mediaSource =
|
||||||
|
new ClippingMediaSource(
|
||||||
|
new FakeMediaSource(new FakeTimeline(/* windowCount= */ 1), /* manifest= */ null),
|
||||||
|
startPositionUs,
|
||||||
|
startPositionUs + expectedDurationUs);
|
||||||
|
Clock clock = new AutoAdvancingFakeClock();
|
||||||
|
AtomicReference<Player> playerReference = new AtomicReference<>();
|
||||||
|
AtomicReference<Long> positionAtDiscontinuityMs = new AtomicReference<>();
|
||||||
|
AtomicReference<Long> clockAtStartMs = new AtomicReference<>();
|
||||||
|
AtomicReference<Long> clockAtDiscontinuityMs = new AtomicReference<>();
|
||||||
|
EventListener eventListener =
|
||||||
|
new EventListener() {
|
||||||
|
@Override
|
||||||
|
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
|
||||||
|
if (playbackState == Player.STATE_READY && clockAtStartMs.get() == null) {
|
||||||
|
clockAtStartMs.set(clock.elapsedRealtime());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPositionDiscontinuity(@DiscontinuityReason int reason) {
|
||||||
|
if (reason == Player.DISCONTINUITY_REASON_PERIOD_TRANSITION) {
|
||||||
|
positionAtDiscontinuityMs.set(playerReference.get().getCurrentPosition());
|
||||||
|
clockAtDiscontinuityMs.set(clock.elapsedRealtime());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ActionSchedule actionSchedule =
|
||||||
|
new ActionSchedule.Builder("testClippedLoopedPeriodsArePlayedFully")
|
||||||
|
.executeRunnable(
|
||||||
|
new PlayerRunnable() {
|
||||||
|
@Override
|
||||||
|
public void run(SimpleExoPlayer player) {
|
||||||
|
playerReference.set(player);
|
||||||
|
player.addListener(eventListener);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.pause()
|
||||||
|
.setRepeatMode(Player.REPEAT_MODE_ALL)
|
||||||
|
.waitForPlaybackState(Player.STATE_READY)
|
||||||
|
// Play until the media repeats once.
|
||||||
|
.playUntilPosition(/* windowIndex= */ 0, /* positionMs= */ 1)
|
||||||
|
.playUntilStartOfWindow(/* windowIndex= */ 0)
|
||||||
|
.setRepeatMode(Player.REPEAT_MODE_OFF)
|
||||||
|
.play()
|
||||||
|
.build();
|
||||||
|
new ExoPlayerTestRunner.Builder()
|
||||||
|
.setClock(clock)
|
||||||
|
.setMediaSource(mediaSource)
|
||||||
|
.setActionSchedule(actionSchedule)
|
||||||
|
.build()
|
||||||
|
.start()
|
||||||
|
.blockUntilEnded(TIMEOUT_MS);
|
||||||
|
|
||||||
|
assertThat(positionAtDiscontinuityMs.get()).isAtLeast(0L);
|
||||||
|
assertThat(clockAtDiscontinuityMs.get() - clockAtStartMs.get())
|
||||||
|
.isAtLeast(C.usToMs(expectedDurationUs));
|
||||||
|
}
|
||||||
|
|
||||||
// Internal methods.
|
// Internal methods.
|
||||||
|
|
||||||
private static ActionSchedule.Builder addSurfaceSwitch(ActionSchedule.Builder builder) {
|
private static ActionSchedule.Builder addSurfaceSwitch(ActionSchedule.Builder builder) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue