diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/ProgressiveMediaPeriod.java b/library/core/src/main/java/com/google/android/exoplayer2/source/ProgressiveMediaPeriod.java index 7bc1c36ba7..cd1b49d101 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/ProgressiveMediaPeriod.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/ProgressiveMediaPeriod.java @@ -499,12 +499,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; } maybeNotifyDownstreamFormat(track); SampleQueue sampleQueue = sampleQueues[track]; - int skipCount; - if (loadingFinished && positionUs > sampleQueue.getLargestQueuedTimestampUs()) { - skipCount = sampleQueue.advanceToEnd(); - } else { - skipCount = sampleQueue.advanceTo(positionUs); - } + int skipCount = sampleQueue.getSkipCount(positionUs, loadingFinished); + sampleQueue.skip(skipCount); if (skipCount == 0) { maybeStartDeferredRetry(track); } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/SampleQueue.java b/library/core/src/main/java/com/google/android/exoplayer2/source/SampleQueue.java index ab6e0e3d97..b45d8e06fe 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/SampleQueue.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/SampleQueue.java @@ -15,6 +15,8 @@ */ package com.google.android.exoplayer2.source; +import static com.google.android.exoplayer2.util.Assertions.checkArgument; + import android.os.Looper; import android.util.Log; import androidx.annotation.CallSuper; @@ -402,34 +404,39 @@ public class SampleQueue implements TrackOutput { } /** - * Advances the read position to the keyframe before or at the specified time. + * Returns the number of samples that need to be {@link #skip(int) skipped} to advance the read + * position to the keyframe before or at the specified time. * * @param timeUs The time to advance to. - * @return The number of samples that were skipped, which may be equal to 0. + * @param allowEndOfQueue Whether the end of the queue is considered a keyframe when {@code + * timeUs} is larger than the largest queued timestamp. + * @return The number of samples that need to be skipped, which may be equal to 0. */ - public final synchronized int advanceTo(long timeUs) { + public final synchronized int getSkipCount(long timeUs, boolean allowEndOfQueue) { int relativeReadIndex = getRelativeIndex(readPosition); if (!hasNextSample() || timeUs < timesUs[relativeReadIndex]) { return 0; } + if (timeUs > largestQueuedTimestampUs && allowEndOfQueue) { + return length - readPosition; + } int offset = findSampleBefore(relativeReadIndex, length - readPosition, timeUs, /* keyframe= */ true); if (offset == -1) { return 0; } - readPosition += offset; return offset; } /** - * Advances the read position to the end of the queue. + * Advances the read position by the specified number of samples. * - * @return The number of samples that were skipped. + * @param count The number of samples to advance the read position by. Must be at least 0 and at + * most {@link #getWriteIndex()} - {@link #getReadIndex()}. */ - public final synchronized int advanceToEnd() { - int skipCount = length - readPosition; - readPosition = length; - return skipCount; + public final synchronized void skip(int count) { + checkArgument(count >= 0 && readPosition + count <= length); + readPosition += count; } /** @@ -788,7 +795,7 @@ public class SampleQueue implements TrackOutput { private long discardUpstreamSampleMetadata(int discardFromIndex) { int discardCount = getWriteIndex() - discardFromIndex; - Assertions.checkArgument(0 <= discardCount && discardCount <= (length - readPosition)); + checkArgument(0 <= discardCount && discardCount <= (length - readPosition)); length -= discardCount; largestQueuedTimestampUs = Math.max(largestDiscardedTimestampUs, getLargestTimestamp(length)); isLastSampleQueued = discardCount == 0 && isLastSampleQueued; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/ChunkSampleStream.java b/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/ChunkSampleStream.java index 2491432bb7..9238ef1c7c 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/ChunkSampleStream.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/ChunkSampleStream.java @@ -392,12 +392,8 @@ public class ChunkSampleStream implements SampleStream, S if (isPendingReset()) { return 0; } - int skipCount; - if (loadingFinished && positionUs > primarySampleQueue.getLargestQueuedTimestampUs()) { - skipCount = primarySampleQueue.advanceToEnd(); - } else { - skipCount = primarySampleQueue.advanceTo(positionUs); - } + int skipCount = primarySampleQueue.getSkipCount(positionUs, loadingFinished); + primarySampleQueue.skip(skipCount); maybeNotifyPrimaryTrackFormatChanged(); return skipCount; } @@ -789,12 +785,8 @@ public class ChunkSampleStream implements SampleStream, S return 0; } maybeNotifyDownstreamFormat(); - int skipCount; - if (loadingFinished && positionUs > sampleQueue.getLargestQueuedTimestampUs()) { - skipCount = sampleQueue.advanceToEnd(); - } else { - skipCount = sampleQueue.advanceTo(positionUs); - } + int skipCount = sampleQueue.getSkipCount(positionUs, loadingFinished); + sampleQueue.skip(skipCount); return skipCount; } diff --git a/library/core/src/test/java/com/google/android/exoplayer2/source/SampleQueueTest.java b/library/core/src/test/java/com/google/android/exoplayer2/source/SampleQueueTest.java index 16444b99bf..4583c542b3 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/source/SampleQueueTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/source/SampleQueueTest.java @@ -21,6 +21,7 @@ import static com.google.android.exoplayer2.C.RESULT_BUFFER_READ; import static com.google.android.exoplayer2.C.RESULT_FORMAT_READ; import static com.google.android.exoplayer2.C.RESULT_NOTHING_READ; import static com.google.common.truth.Truth.assertThat; +import static java.lang.Long.MAX_VALUE; import static java.lang.Long.MIN_VALUE; import static java.util.Arrays.copyOfRange; import static org.junit.Assert.assertArrayEquals; @@ -590,9 +591,10 @@ public final class SampleQueueTest { } @Test - public void advanceToEnd() { + public void skipToEnd() { writeTestData(); - sampleQueue.advanceToEnd(); + sampleQueue.skip( + sampleQueue.getSkipCount(/* timeUs= */ MAX_VALUE, /* allowEndOfQueue= */ true)); assertAllocationCount(10); sampleQueue.discardToRead(); assertAllocationCount(0); @@ -604,10 +606,11 @@ public final class SampleQueueTest { } @Test - public void advanceToEndRetainsUnassignedData() { + public void skipToEndRetainsUnassignedData() { sampleQueue.format(FORMAT_1); sampleQueue.sampleData(new ParsableByteArray(DATA), ALLOCATION_SIZE); - sampleQueue.advanceToEnd(); + sampleQueue.skip( + sampleQueue.getSkipCount(/* timeUs= */ MAX_VALUE, /* allowEndOfQueue= */ true)); assertAllocationCount(1); sampleQueue.discardToRead(); // Skipping shouldn't discard data that may belong to a sample whose metadata has yet to be @@ -635,41 +638,47 @@ public final class SampleQueueTest { } @Test - public void advanceToBeforeBuffer() { + public void skipToBeforeBuffer() { writeTestData(); - int skipCount = sampleQueue.advanceTo(SAMPLE_TIMESTAMPS[0] - 1); + int skipCount = + sampleQueue.getSkipCount(SAMPLE_TIMESTAMPS[0] - 1, /* allowEndOfQueue= */ false); // Should have no effect (we're already at the first frame). assertThat(skipCount).isEqualTo(0); + sampleQueue.skip(skipCount); assertReadTestData(); assertNoSamplesToRead(FORMAT_2); } @Test - public void advanceToStartOfBuffer() { + public void skipToStartOfBuffer() { writeTestData(); - int skipCount = sampleQueue.advanceTo(SAMPLE_TIMESTAMPS[0]); + int skipCount = sampleQueue.getSkipCount(SAMPLE_TIMESTAMPS[0], /* allowEndOfQueue= */ false); // Should have no effect (we're already at the first frame). assertThat(skipCount).isEqualTo(0); + sampleQueue.skip(skipCount); assertReadTestData(); assertNoSamplesToRead(FORMAT_2); } @Test - public void advanceToEndOfBuffer() { + public void skipToEndOfBuffer() { writeTestData(); - int skipCount = sampleQueue.advanceTo(LAST_SAMPLE_TIMESTAMP); + int skipCount = sampleQueue.getSkipCount(LAST_SAMPLE_TIMESTAMP, /* allowEndOfQueue= */ false); // Should advance to 2nd keyframe (the 4th frame). assertThat(skipCount).isEqualTo(4); + sampleQueue.skip(skipCount); assertReadTestData(/* startFormat= */ null, DATA_SECOND_KEYFRAME_INDEX); assertNoSamplesToRead(FORMAT_2); } @Test - public void advanceToAfterBuffer() { + public void skipToAfterBuffer() { writeTestData(); - int skipCount = sampleQueue.advanceTo(LAST_SAMPLE_TIMESTAMP + 1); + int skipCount = + sampleQueue.getSkipCount(LAST_SAMPLE_TIMESTAMP + 1, /* allowEndOfQueue= */ false); // Should advance to 2nd keyframe (the 4th frame). assertThat(skipCount).isEqualTo(4); + sampleQueue.skip(skipCount); assertReadTestData(/* startFormat= */ null, DATA_SECOND_KEYFRAME_INDEX); assertNoSamplesToRead(FORMAT_2); } diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java index 530f9cb366..e7f55807f8 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java @@ -595,11 +595,9 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; } SampleQueue sampleQueue = sampleQueues[sampleQueueIndex]; - if (loadingFinished && positionUs > sampleQueue.getLargestQueuedTimestampUs()) { - return sampleQueue.advanceToEnd(); - } else { - return sampleQueue.advanceTo(positionUs); - } + int skipCount = sampleQueue.getSkipCount(positionUs, loadingFinished); + sampleQueue.skip(skipCount); + return skipCount; } // SequenceableLoader implementation