diff --git a/library/src/main/java/com/google/android/exoplayer/ExoPlayerImplInternal.java b/library/src/main/java/com/google/android/exoplayer/ExoPlayerImplInternal.java index f18bfe1463..af2f85b003 100644 --- a/library/src/main/java/com/google/android/exoplayer/ExoPlayerImplInternal.java +++ b/library/src/main/java/com/google/android/exoplayer/ExoPlayerImplInternal.java @@ -377,8 +377,8 @@ import java.util.concurrent.atomic.AtomicInteger; TraceUtil.beginSection("doSomeWork"); if (enabledRenderers.length > 0) { - // Process reset if there is one, else update the position. - if (!checkForSourceResetInternal()) { + // Process a source discontinuity if there is one, else update the position. + if (!checkForSourceDiscontinuityInternal()) { updatePositionUs(); sampleSource = timeline.getSampleSource(); } @@ -442,21 +442,21 @@ import java.util.concurrent.atomic.AtomicInteger; } } - private void seekToInternal(int sourceIndex, long positionMs) throws ExoPlaybackException { + private void seekToInternal(int sourceIndex, long seekPositionMs) throws ExoPlaybackException { try { - if (positionMs == (positionUs / 1000)) { + if (seekPositionMs == (positionUs / 1000)) { // Seek is to the current position. Do nothing. return; } + long seekPositionUs = seekPositionMs * 1000; rebuffering = false; - positionUs = positionMs * 1000; - internalPositionUs = sourceOffsetUs + positionUs; standaloneMediaClock.stop(); - standaloneMediaClock.setPositionUs(internalPositionUs); - sampleSource = timeline.seekTo(sourceIndex, positionUs); + + sampleSource = timeline.seekToSource(sourceIndex); if (sampleSource == null) { // The source isn't prepared. + setNewSourcePositionInternal(seekPositionUs); return; } @@ -464,9 +464,10 @@ import java.util.concurrent.atomic.AtomicInteger; for (TrackRenderer renderer : enabledRenderers) { ensureStopped(renderer); } - checkForSourceResetInternal(); + seekPositionUs = sampleSource.seekToUs(seekPositionUs); } + setNewSourcePositionInternal(seekPositionUs); resumeInternal(); } finally { pendingSeekCount.decrementAndGet(); @@ -496,17 +497,22 @@ import java.util.concurrent.atomic.AtomicInteger; handler.sendEmptyMessage(MSG_DO_SOME_WORK); } - private boolean checkForSourceResetInternal() throws ExoPlaybackException { - long resetPositionUs = sampleSource.readReset(); - if (resetPositionUs == C.UNSET_TIME_US) { + private boolean checkForSourceDiscontinuityInternal() throws ExoPlaybackException { + long newSourcePositionUs = sampleSource.readDiscontinuity(); + if (newSourcePositionUs == C.UNSET_TIME_US) { return false; } - internalPositionUs = sourceOffsetUs + resetPositionUs; + setNewSourcePositionInternal(newSourcePositionUs); + return true; + } + + private void setNewSourcePositionInternal(long sourcePositionUs) throws ExoPlaybackException { + positionUs = sourcePositionUs; + internalPositionUs = sourceOffsetUs + sourcePositionUs; standaloneMediaClock.setPositionUs(internalPositionUs); for (TrackRenderer renderer : enabledRenderers) { renderer.reset(internalPositionUs); } - return true; } private void stopInternal() { @@ -672,8 +678,6 @@ import java.util.concurrent.atomic.AtomicInteger; playingSourceEndPositionUs = C.UNSET_TIME_US; } else if (playingSource.nextSource != null && playingSource.nextSource.prepared) { readingSource = playingSource.nextSource; - // Suppress reading a reset so that the transition can be seamless. - readingSource.sampleSource.readReset(); // Replace enabled renderers' TrackStreams if they will continue to be enabled when the // new source starts playing, so that the transition can be seamless. TrackSelectionArray newTrackSelections = readingSource.trackSelections; @@ -711,7 +715,7 @@ import java.util.concurrent.atomic.AtomicInteger; return playingSource.sampleSource; } - public SampleSource seekTo(int sourceIndex, long sourcePositionUs) throws ExoPlaybackException { + public SampleSource seekToSource(int sourceIndex) throws ExoPlaybackException { // Clear the timeline, but keep the requested source if it is already prepared. Source source = playingSource; Source newPlayingSource = null; @@ -729,10 +733,8 @@ import java.util.concurrent.atomic.AtomicInteger; setPlayingSource(newPlayingSource, sourceOffsetUs); bufferingSource = playingSource; bufferingSourceOffsetUs = sourceOffsetUs; - if (playingSource.hasEnabledTracks) { - sampleSource.seekToUs(sourcePositionUs); - } } else { + // TODO[REFACTOR]: Presumably we need to disable the renderers somewhere in here? playingSource = null; readingSource = null; bufferingSource = null; diff --git a/library/src/main/java/com/google/android/exoplayer/MultiSampleSource.java b/library/src/main/java/com/google/android/exoplayer/MultiSampleSource.java index bcd4b31a1c..234fb37258 100644 --- a/library/src/main/java/com/google/android/exoplayer/MultiSampleSource.java +++ b/library/src/main/java/com/google/android/exoplayer/MultiSampleSource.java @@ -124,17 +124,14 @@ public final class MultiSampleSource implements SampleSource { } @Override - public long readReset() { - long resetPositionUs = C.UNSET_TIME_US; + public long readDiscontinuity() { for (SampleSource source : enabledSources) { - long childResetPositionUs = source.readReset(); - if (resetPositionUs == C.UNSET_TIME_US) { - resetPositionUs = childResetPositionUs; - } else if (childResetPositionUs != C.UNSET_TIME_US) { - resetPositionUs = Math.min(resetPositionUs, childResetPositionUs); + if (source.readDiscontinuity() != C.UNSET_TIME_US) { + // Children are not allowed to report discontinuities. + throw new IllegalStateException("Child reported discontinuity"); } } - return resetPositionUs; + return C.UNSET_TIME_US; } @Override @@ -150,10 +147,15 @@ public final class MultiSampleSource implements SampleSource { } @Override - public void seekToUs(long positionUs) { - for (SampleSource source : enabledSources) { - source.seekToUs(positionUs); + public long seekToUs(long positionUs) { + positionUs = enabledSources[0].seekToUs(positionUs); + for (int i = 1; i < enabledSources.length; i++) { + // Additional sources must seek to the same position. + if (enabledSources[i].seekToUs(positionUs) != positionUs) { + throw new IllegalStateException("Children seeked to different positions"); + } } + return positionUs; } @Override diff --git a/library/src/main/java/com/google/android/exoplayer/SampleSource.java b/library/src/main/java/com/google/android/exoplayer/SampleSource.java index 51577ec4dd..c8ed5334ac 100644 --- a/library/src/main/java/com/google/android/exoplayer/SampleSource.java +++ b/library/src/main/java/com/google/android/exoplayer/SampleSource.java @@ -88,12 +88,15 @@ public interface SampleSource { void continueBuffering(long positionUs); /** - * Attempts to read a pending reset. + * Attempts to read a discontinuity. + *
+ * After this method has returned a value other than {@link C#UNSET_TIME_US}, all + * {@link TrackStream}s provided by the source are guaranteed to start from a key frame. * - * @return If a reset was read then the playback position in microseconds after the reset. Else - * {@link C#UNSET_TIME_US}. + * @return If a discontinuity was read then the playback position in microseconds after the + * discontinuity. Else {@link C#UNSET_TIME_US}. */ - long readReset(); + long readDiscontinuity(); /** * Returns an estimate of the position up to which data is buffered for the enabled tracks. @@ -106,13 +109,17 @@ public interface SampleSource { long getBufferedPositionUs(); /** - * Seeks to the specified position in microseconds. + * Attempts to seek to the specified position in microseconds. + *
+ * After this method has been called, all {@link TrackStream}s provided by the source are + * guaranteed to start from a key frame. *
* This method should only be called when at least one track is selected.
*
* @param positionUs The seek position in microseconds.
+ * @return The actual position to which the source was seeked, in microseconds.
*/
- void seekToUs(long positionUs);
+ long seekToUs(long positionUs);
/**
* Releases the source.
diff --git a/library/src/main/java/com/google/android/exoplayer/SingleSampleSource.java b/library/src/main/java/com/google/android/exoplayer/SingleSampleSource.java
index 1543fb7ea5..4f228724bc 100644
--- a/library/src/main/java/com/google/android/exoplayer/SingleSampleSource.java
+++ b/library/src/main/java/com/google/android/exoplayer/SingleSampleSource.java
@@ -74,7 +74,6 @@ public final class SingleSampleSource implements SampleSource, TrackStream,
private final EventListener eventListener;
private final int eventSourceId;
- private long pendingResetPositionUs;
private boolean loadingFinished;
private int streamState;
@@ -140,7 +139,6 @@ public final class SingleSampleSource implements SampleSource, TrackStream,
if (!newSelections.isEmpty()) {
newStreams[0] = this;
streamState = STREAM_STATE_SEND_FORMAT;
- pendingResetPositionUs = C.UNSET_TIME_US;
maybeStartLoading();
}
return newStreams;
@@ -157,11 +155,11 @@ public final class SingleSampleSource implements SampleSource, TrackStream,
}
@Override
- public void seekToUs(long positionUs) {
+ public long seekToUs(long positionUs) {
if (streamState == STREAM_STATE_END_OF_STREAM) {
- pendingResetPositionUs = positionUs;
streamState = STREAM_STATE_SEND_SAMPLE;
}
+ return positionUs;
}
@Override
@@ -182,10 +180,8 @@ public final class SingleSampleSource implements SampleSource, TrackStream,
}
@Override
- public long readReset() {
- long resetPositionUs = pendingResetPositionUs;
- pendingResetPositionUs = C.UNSET_TIME_US;
- return resetPositionUs;
+ public long readDiscontinuity() {
+ return C.UNSET_TIME_US;
}
@Override
diff --git a/library/src/main/java/com/google/android/exoplayer/dash/DashSampleSource.java b/library/src/main/java/com/google/android/exoplayer/dash/DashSampleSource.java
index c30e56bb64..ad80a9b15e 100644
--- a/library/src/main/java/com/google/android/exoplayer/dash/DashSampleSource.java
+++ b/library/src/main/java/com/google/android/exoplayer/dash/DashSampleSource.java
@@ -92,8 +92,6 @@ public final class DashSampleSource implements SampleSource {
private long elapsedRealtimeOffset;
private TrackGroupArray trackGroups;
private int[] trackGroupAdaptationSetIndices;
- private boolean pendingReset;
- private long lastSeekPositionUs;
private ChunkTrackStream