Make seeking when there's a pending reset more obviously correct

If there's already a pending reset, we need only update the pending
reset position. We can currently end up calling cancelLoading more
than once on the loader for the same task, which is hard to reason
about (although I don't think anything actually broke in practice).

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=212432266
This commit is contained in:
olly 2018-09-11 04:50:13 -07:00 committed by Oliver Woodman
parent 72efc80037
commit ba8c22aba5
3 changed files with 59 additions and 43 deletions

View File

@ -368,15 +368,22 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
boolean[] trackIsAudioVideoFlags = preparedState.trackIsAudioVideoFlags; boolean[] trackIsAudioVideoFlags = preparedState.trackIsAudioVideoFlags;
// Treat all seeks into non-seekable media as being to t=0. // Treat all seeks into non-seekable media as being to t=0.
positionUs = seekMap.isSeekable() ? positionUs : 0; positionUs = seekMap.isSeekable() ? positionUs : 0;
lastSeekPositionUs = positionUs;
notifyDiscontinuity = false; notifyDiscontinuity = false;
// If we're not playing a live stream or pending a reset, see if we can seek within the buffer. lastSeekPositionUs = positionUs;
if (isPendingReset()) {
// A reset is already pending. We only need to update its position.
pendingResetPositionUs = positionUs;
return positionUs;
}
// If we're not playing a live stream, try and seek within the buffer.
if (dataType != C.DATA_TYPE_MEDIA_PROGRESSIVE_LIVE if (dataType != C.DATA_TYPE_MEDIA_PROGRESSIVE_LIVE
&& !isPendingReset()
&& seekInsideBufferUs(trackIsAudioVideoFlags, positionUs)) { && seekInsideBufferUs(trackIsAudioVideoFlags, positionUs)) {
return positionUs; return positionUs;
} }
// We were unable to seek within the buffer, so need to reset.
// We can't seek inside the buffer, and so need to reset.
pendingDeferredRetry = false; pendingDeferredRetry = false;
pendingResetPositionUs = positionUs; pendingResetPositionUs = positionUs;
loadingFinished = false; loadingFinished = false;

View File

@ -274,52 +274,54 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S
*/ */
public void seekToUs(long positionUs) { public void seekToUs(long positionUs) {
lastSeekPositionUs = positionUs; lastSeekPositionUs = positionUs;
primarySampleQueue.rewind();
// See if we can seek within the primary sample queue.
boolean seekInsideBuffer;
if (isPendingReset()) { if (isPendingReset()) {
seekInsideBuffer = false; // A reset is already pending. We only need to update its position.
} else { pendingResetPositionUs = positionUs;
// Detect whether the seek is to the start of a chunk that's at least partially buffered. return;
BaseMediaChunk seekToMediaChunk = null; }
for (int i = 0; i < mediaChunks.size(); i++) {
BaseMediaChunk mediaChunk = mediaChunks.get(i); // Detect whether the seek is to the start of a chunk that's at least partially buffered.
long mediaChunkStartTimeUs = mediaChunk.startTimeUs; BaseMediaChunk seekToMediaChunk = null;
if (mediaChunkStartTimeUs == positionUs && mediaChunk.seekTimeUs == C.TIME_UNSET) { for (int i = 0; i < mediaChunks.size(); i++) {
seekToMediaChunk = mediaChunk; BaseMediaChunk mediaChunk = mediaChunks.get(i);
break; long mediaChunkStartTimeUs = mediaChunk.startTimeUs;
} else if (mediaChunkStartTimeUs > positionUs) { if (mediaChunkStartTimeUs == positionUs && mediaChunk.seekTimeUs == C.TIME_UNSET) {
// We're not going to find a chunk with a matching start time. seekToMediaChunk = mediaChunk;
break; break;
} } else if (mediaChunkStartTimeUs > positionUs) {
} // We're not going to find a chunk with a matching start time.
if (seekToMediaChunk != null) { break;
// When seeking to the start of a chunk we use the index of the first sample in the chunk
// rather than the seek position. This ensures we seek to the keyframe at the start of the
// chunk even if the sample timestamps are slightly offset from the chunk start times.
seekInsideBuffer =
primarySampleQueue.setReadPosition(seekToMediaChunk.getFirstSampleIndex(0));
decodeOnlyUntilPositionUs = Long.MIN_VALUE;
} else {
seekInsideBuffer =
primarySampleQueue.advanceTo(
positionUs,
/* toKeyframe= */ true,
/* allowTimeBeyondBuffer= */ positionUs < getNextLoadPositionUs())
!= SampleQueue.ADVANCE_FAILED;
decodeOnlyUntilPositionUs = lastSeekPositionUs;
} }
} }
// See if we can seek inside the primary sample queue.
boolean seekInsideBuffer;
primarySampleQueue.rewind();
if (seekToMediaChunk != null) {
// When seeking to the start of a chunk we use the index of the first sample in the chunk
// rather than the seek position. This ensures we seek to the keyframe at the start of the
// chunk even if the sample timestamps are slightly offset from the chunk start times.
seekInsideBuffer =
primarySampleQueue.setReadPosition(seekToMediaChunk.getFirstSampleIndex(0));
decodeOnlyUntilPositionUs = Long.MIN_VALUE;
} else {
seekInsideBuffer =
primarySampleQueue.advanceTo(
positionUs,
/* toKeyframe= */ true,
/* allowTimeBeyondBuffer= */ positionUs < getNextLoadPositionUs())
!= SampleQueue.ADVANCE_FAILED;
decodeOnlyUntilPositionUs = lastSeekPositionUs;
}
if (seekInsideBuffer) { if (seekInsideBuffer) {
// We succeeded. Advance the embedded sample queues to the seek position. // We can seek inside the buffer. Advance the embedded sample queues to the seek position.
for (SampleQueue embeddedSampleQueue : embeddedSampleQueues) { for (SampleQueue embeddedSampleQueue : embeddedSampleQueues) {
embeddedSampleQueue.rewind(); embeddedSampleQueue.rewind();
embeddedSampleQueue.advanceTo(positionUs, true, false); embeddedSampleQueue.advanceTo(positionUs, true, false);
} }
} else { } else {
// We failed, and need to restart. // We can't seek inside the buffer, and so need to reset.
pendingResetPositionUs = positionUs; pendingResetPositionUs = positionUs;
loadingFinished = false; loadingFinished = false;
mediaChunks.clear(); mediaChunks.clear();

View File

@ -386,11 +386,18 @@ import java.util.List;
*/ */
public boolean seekToUs(long positionUs, boolean forceReset) { public boolean seekToUs(long positionUs, boolean forceReset) {
lastSeekPositionUs = positionUs; lastSeekPositionUs = positionUs;
// If we're not forced to reset nor have a pending reset, see if we can seek within the buffer. if (isPendingReset()) {
if (sampleQueuesBuilt && !forceReset && !isPendingReset() && seekInsideBufferUs(positionUs)) { // A reset is already pending. We only need to update its position.
pendingResetPositionUs = positionUs;
return true;
}
// If we're not forced to reset, try and seek within the buffer.
if (sampleQueuesBuilt && !forceReset && seekInsideBufferUs(positionUs)) {
return false; return false;
} }
// We were unable to seek within the buffer, so need to reset.
// We can't seek inside the buffer, and so need to reset.
pendingResetPositionUs = positionUs; pendingResetPositionUs = positionUs;
loadingFinished = false; loadingFinished = false;
mediaChunks.clear(); mediaChunks.clear();