From 3c5688de73e8f6aa5825c20b187476f7d26dc9b1 Mon Sep 17 00:00:00 2001 From: olly Date: Tue, 11 Jul 2017 10:18:25 -0700 Subject: [PATCH] Optimize in-queue seeking when non-AV tracks are present Let's do it this way for now. Note there's an implicit assumption in here that non-AV tracks consist of only key-frames, but I think we'll not encounter any issues in the real world as a result, we already make this assumption in ChunkSampleStream, and actually tagging every queue with this information explicitly is a very painful amount of plumbing. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=161545383 --- .../source/ExtractorMediaPeriod.java | 54 ++++++++++++------- 1 file changed, 34 insertions(+), 20 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaPeriod.java b/library/core/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaPeriod.java index ea6b105705a..4173519d9a3 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaPeriod.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaPeriod.java @@ -80,7 +80,7 @@ private boolean prepared; private boolean seenFirstTrackSelection; - private boolean notifyReset; + private boolean notifyDiscontinuity; private int enabledTrackCount; private TrackGroupArray tracks; private long durationUs; @@ -229,7 +229,7 @@ public long selectTracks(TrackSelection[] selections, boolean[] mayRetainStreamF } } if (enabledTrackCount == 0) { - notifyReset = false; + notifyDiscontinuity = false; if (loader.isLoading()) { // Discard as much as we can synchronously. for (SampleQueue sampleQueue : sampleQueues) { @@ -282,8 +282,8 @@ public long getNextLoadPositionUs() { @Override public long readDiscontinuity() { - if (notifyReset) { - notifyReset = false; + if (notifyDiscontinuity) { + notifyDiscontinuity = false; return lastSeekPositionUs; } return C.TIME_UNSET; @@ -319,18 +319,9 @@ public long seekToUs(long positionUs) { // Treat all seeks into non-seekable media as being to t=0. positionUs = seekMap.isSeekable() ? positionUs : 0; lastSeekPositionUs = positionUs; + notifyDiscontinuity = false; // If we're not pending a reset, see if we can seek within the sample queues. - boolean seekInsideBuffer = !isPendingReset(); - int trackCount = sampleQueues.length; - for (int i = 0; seekInsideBuffer && i < trackCount; i++) { - SampleQueue sampleQueue = sampleQueues[i]; - sampleQueue.rewind(); - // TODO: For sparse tracks (e.g. text, metadata) this may return false when an in-buffer - // seek should be allowed. If there are non-sparse tracks (e.g. video, audio) for which - // in-buffer seeking is successful, we should perform an in-buffer seek unconditionally. - seekInsideBuffer = sampleQueue.advanceTo(positionUs, true, false); - sampleQueue.discardToRead(); - } + boolean seekInsideBuffer = !isPendingReset() && seekInsideBufferUs(positionUs); // If we failed to seek within the sample queues, we need to restart. if (!seekInsideBuffer) { pendingResetPositionUs = positionUs; @@ -338,12 +329,11 @@ public long seekToUs(long positionUs) { if (loader.isLoading()) { loader.cancelLoading(); } else { - for (int i = 0; i < trackCount; i++) { - sampleQueues[i].reset(); + for (SampleQueue sampleQueue : sampleQueues) { + sampleQueue.reset(); } } } - notifyReset = false; return positionUs; } @@ -359,7 +349,7 @@ public long seekToUs(long positionUs) { /* package */ int readData(int track, FormatHolder formatHolder, DecoderInputBuffer buffer, boolean formatRequired) { - if (notifyReset || isPendingReset()) { + if (notifyDiscontinuity || isPendingReset()) { return C.RESULT_NOTHING_READ; } return sampleQueues[track].read(formatHolder, buffer, formatRequired, loadingFinished, @@ -536,7 +526,7 @@ private void configureRetry(ExtractingLoadable loadable) { // previous load finished, so it's necessary to load from the start whenever commencing // a new load. lastSeekPositionUs = 0; - notifyReset = prepared; + notifyDiscontinuity = prepared; for (SampleQueue sampleQueue : sampleQueues) { sampleQueue.reset(); } @@ -544,6 +534,30 @@ private void configureRetry(ExtractingLoadable loadable) { } } + /** + * Attempts to seek to the specified position within the sample queues. + * + * @param positionUs The seek position in microseconds. + * @return Whether the in-buffer seek was successful. + */ + private boolean seekInsideBufferUs(long positionUs) { + int trackCount = sampleQueues.length; + for (int i = 0; i < trackCount; i++) { + SampleQueue sampleQueue = sampleQueues[i]; + sampleQueue.rewind(); + boolean seekInsideQueue = sampleQueue.advanceTo(positionUs, true, false); + // If we have AV tracks then an in-buffer seek is successful if the seek into every AV queue + // is successful. We ignore whether seeks within non-AV queues are successful in this case, as + // they may be sparse or poorly interleaved. If we only have non-AV tracks then a seek is + // successful only if the seek into every queue succeeds. + if (!seekInsideQueue && (trackIsAudioVideoFlags[i] || !haveAudioVideoTracks)) { + return false; + } + sampleQueue.discardToRead(); + } + return true; + } + private int getExtractedSamplesCount() { int extractedSamplesCount = 0; for (SampleQueue sampleQueue : sampleQueues) {