diff --git a/library/core/src/androidTest/java/com/google/android/exoplayer2/source/SampleQueueTest.java b/library/core/src/androidTest/java/com/google/android/exoplayer2/source/SampleQueueTest.java index 76ea0e34cf..77e61e39a9 100644 --- a/library/core/src/androidTest/java/com/google/android/exoplayer2/source/SampleQueueTest.java +++ b/library/core/src/androidTest/java/com/google/android/exoplayer2/source/SampleQueueTest.java @@ -258,45 +258,45 @@ public class SampleQueueTest extends TestCase { public void testAdvanceToBeforeBuffer() { writeTestData(); - boolean result = sampleQueue.advanceTo(TEST_SAMPLE_TIMESTAMPS[0] - 1, true, false); + int skipCount = sampleQueue.advanceTo(TEST_SAMPLE_TIMESTAMPS[0] - 1, true, false); // Should fail and have no effect. - assertFalse(result); + assertEquals(SampleQueue.ADVANCE_FAILED, skipCount); assertReadTestData(); assertNoSamplesToRead(TEST_FORMAT_2); } public void testAdvanceToStartOfBuffer() { writeTestData(); - boolean result = sampleQueue.advanceTo(TEST_SAMPLE_TIMESTAMPS[0], true, false); + int skipCount = sampleQueue.advanceTo(TEST_SAMPLE_TIMESTAMPS[0], true, false); // Should succeed but have no effect (we're already at the first frame). - assertTrue(result); + assertEquals(0, skipCount); assertReadTestData(); assertNoSamplesToRead(TEST_FORMAT_2); } public void testAdvanceToEndOfBuffer() { writeTestData(); - boolean result = sampleQueue.advanceTo(LAST_SAMPLE_TIMESTAMP, true, false); - // Should succeed and skip to 2nd keyframe. - assertTrue(result); + int skipCount = sampleQueue.advanceTo(LAST_SAMPLE_TIMESTAMP, true, false); + // Should succeed and skip to 2nd keyframe (the 4th frame). + assertEquals(4, skipCount); assertReadTestData(null, TEST_DATA_SECOND_KEYFRAME_INDEX); assertNoSamplesToRead(TEST_FORMAT_2); } public void testAdvanceToAfterBuffer() { writeTestData(); - boolean result = sampleQueue.advanceTo(LAST_SAMPLE_TIMESTAMP + 1, true, false); + int skipCount = sampleQueue.advanceTo(LAST_SAMPLE_TIMESTAMP + 1, true, false); // Should fail and have no effect. - assertFalse(result); + assertEquals(SampleQueue.ADVANCE_FAILED, skipCount); assertReadTestData(); assertNoSamplesToRead(TEST_FORMAT_2); } public void testAdvanceToAfterBufferAllowed() { writeTestData(); - boolean result = sampleQueue.advanceTo(LAST_SAMPLE_TIMESTAMP + 1, true, true); - // Should succeed and skip to 2nd keyframe. - assertTrue(result); + int skipCount = sampleQueue.advanceTo(LAST_SAMPLE_TIMESTAMP + 1, true, true); + // Should succeed and skip to 2nd keyframe (the 4th frame). + assertEquals(4, skipCount); assertReadTestData(null, TEST_DATA_SECOND_KEYFRAME_INDEX); assertNoSamplesToRead(TEST_FORMAT_2); } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/BaseRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/BaseRenderer.java index a88a1dd615..7f14837965 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/BaseRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/BaseRenderer.java @@ -296,9 +296,10 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities { * {@code positionUs} is beyond it. * * @param positionUs The position in microseconds. + * @return The number of samples that were skipped. */ - protected void skipSource(long positionUs) { - stream.skipData(positionUs - streamOffsetUs); + protected int skipSource(long positionUs) { + return stream.skipData(positionUs - streamOffsetUs); } /** diff --git a/library/core/src/main/java/com/google/android/exoplayer2/decoder/DecoderCounters.java b/library/core/src/main/java/com/google/android/exoplayer2/decoder/DecoderCounters.java index 3c2d6d96e9..7a532110d3 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/decoder/DecoderCounters.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/decoder/DecoderCounters.java @@ -36,6 +36,12 @@ public final class DecoderCounters { * The number of queued input buffers. */ public int inputBufferCount; + /** + * The number of skipped input buffers. + *

+ * A skipped input buffer is an input buffer that was deliberately not sent to the decoder. + */ + public int skippedInputBufferCount; /** * The number of rendered output buffers. */ @@ -79,6 +85,7 @@ public final class DecoderCounters { decoderInitCount += other.decoderInitCount; decoderReleaseCount += other.decoderReleaseCount; inputBufferCount += other.inputBufferCount; + skippedInputBufferCount += other.skippedInputBufferCount; renderedOutputBufferCount += other.renderedOutputBufferCount; skippedOutputBufferCount += other.skippedOutputBufferCount; droppedOutputBufferCount += other.droppedOutputBufferCount; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java index 01229c1104..7c0549de25 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java @@ -530,7 +530,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { while (feedInputBuffer()) {} TraceUtil.endSection(); } else { - skipSource(positionUs); + decoderCounters.skippedInputBufferCount += skipSource(positionUs); // We need to read any format changes despite not having a codec so that drmSession can be // updated, and so that we have the most recent format should the codec be initialized. We may // also reach the end of the stream. Note that readSource will not read a sample into a diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/ClippingMediaPeriod.java b/library/core/src/main/java/com/google/android/exoplayer2/source/ClippingMediaPeriod.java index 12f58d9a21..a8c33b4625 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/ClippingMediaPeriod.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/ClippingMediaPeriod.java @@ -286,8 +286,8 @@ public final class ClippingMediaPeriod implements MediaPeriod, MediaPeriod.Callb } @Override - public void skipData(long positionUs) { - stream.skipData(startUs + positionUs); + public int skipData(long positionUs) { + return stream.skipData(startUs + positionUs); } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/EmptySampleStream.java b/library/core/src/main/java/com/google/android/exoplayer2/source/EmptySampleStream.java index 7aab22d8a0..299b816cc8 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/EmptySampleStream.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/EmptySampleStream.java @@ -43,8 +43,8 @@ public final class EmptySampleStream implements SampleStream { } @Override - public void skipData(long positionUs) { - // Do nothing. + public int skipData(long positionUs) { + return 0; } } 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 e7273f834b..511f7f4a8a 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 @@ -238,7 +238,7 @@ import java.util.Arrays; // sample queue, or if we haven't read anything from the queue since the previous seek // (this case is common for sparse tracks such as metadata tracks). In all other cases a // seek is required. - seekRequired = !sampleQueue.advanceTo(positionUs, true, true) + seekRequired = sampleQueue.advanceTo(positionUs, true, true) == SampleQueue.ADVANCE_FAILED && sampleQueue.getReadIndex() != 0; } } @@ -371,12 +371,13 @@ import java.util.Arrays; lastSeekPositionUs); } - /* package */ void skipData(int track, long positionUs) { + /* package */ int skipData(int track, long positionUs) { SampleQueue sampleQueue = sampleQueues[track]; if (loadingFinished && positionUs > sampleQueue.getLargestQueuedTimestampUs()) { - sampleQueue.advanceToEnd(); + return sampleQueue.advanceToEnd(); } else { - sampleQueue.advanceTo(positionUs, true, true); + int skipCount = sampleQueue.advanceTo(positionUs, true, true); + return skipCount == SampleQueue.ADVANCE_FAILED ? 0 : skipCount; } } @@ -558,7 +559,8 @@ import java.util.Arrays; for (int i = 0; i < trackCount; i++) { SampleQueue sampleQueue = sampleQueues[i]; sampleQueue.rewind(); - boolean seekInsideQueue = sampleQueue.advanceTo(positionUs, true, false); + boolean seekInsideQueue = sampleQueue.advanceTo(positionUs, true, false) + != SampleQueue.ADVANCE_FAILED; // 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 @@ -632,8 +634,8 @@ import java.util.Arrays; } @Override - public void skipData(long positionUs) { - ExtractorMediaPeriod.this.skipData(track, positionUs); + public int skipData(long positionUs) { + return ExtractorMediaPeriod.this.skipData(track, positionUs); } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/SampleMetadataQueue.java b/library/core/src/main/java/com/google/android/exoplayer2/source/SampleMetadataQueue.java index 03b2e3b715..d70c59b195 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/SampleMetadataQueue.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/SampleMetadataQueue.java @@ -253,32 +253,35 @@ import com.google.android.exoplayer2.util.Util; * @param allowTimeBeyondBuffer Whether the operation can succeed if {@code timeUs} is beyond the * end of the queue, by advancing the read position to the last sample (or keyframe) in the * queue. - * @return Whether the operation was a success. A successful advance is one in which the read - * position was unchanged or advanced, and is now at a sample meeting the specified criteria. + * @return The number of samples that were skipped if the operation was successful, which may be + * equal to 0, or {@link SampleQueue#ADVANCE_FAILED} if the operation was not successful. A + * successful advance is one in which the read position was unchanged or advanced, and is now + * at a sample meeting the specified criteria. */ - public synchronized boolean advanceTo(long timeUs, boolean toKeyframe, + public synchronized int advanceTo(long timeUs, boolean toKeyframe, boolean allowTimeBeyondBuffer) { int relativeReadIndex = getRelativeIndex(readPosition); if (!hasNextSample() || timeUs < timesUs[relativeReadIndex] || (timeUs > largestQueuedTimestampUs && !allowTimeBeyondBuffer)) { - return false; + return SampleQueue.ADVANCE_FAILED; } int offset = findSampleBefore(relativeReadIndex, length - readPosition, timeUs, toKeyframe); if (offset == -1) { - return false; + return SampleQueue.ADVANCE_FAILED; } readPosition += offset; - return true; + return offset; } /** * Advances the read position to the end of the queue. + * + * @return The number of samples that were skipped. */ - public synchronized void advanceToEnd() { - if (!hasNextSample()) { - return; - } + public synchronized int advanceToEnd() { + int skipCount = length - readPosition; readPosition = length; + return skipCount; } /** 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 c7bae8f8b4..b83cf7df5b 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 @@ -49,6 +49,8 @@ public final class SampleQueue implements TrackOutput { } + public static final int ADVANCE_FAILED = -1; + private static final int INITIAL_SCRATCH_SIZE = 32; private final Allocator allocator; @@ -255,9 +257,11 @@ public final class SampleQueue implements TrackOutput { /** * Advances the read position to the end of the queue. + * + * @return The number of samples that were skipped. */ - public void advanceToEnd() { - metadataQueue.advanceToEnd(); + public int advanceToEnd() { + return metadataQueue.advanceToEnd(); } /** @@ -268,10 +272,12 @@ public final class SampleQueue implements TrackOutput { * time, rather than to any sample before or at that time. * @param allowTimeBeyondBuffer Whether the operation can succeed if {@code timeUs} is beyond the * end of the queue, by advancing the read position to the last sample (or keyframe). - * @return Whether the operation was a success. A successful advance is one in which the read - * position was unchanged or advanced, and is now at a sample meeting the specified criteria. + * @return The number of samples that were skipped if the operation was successful, which may be + * equal to 0, or {@link #ADVANCE_FAILED} if the operation was not successful. A successful + * advance is one in which the read position was unchanged or advanced, and is now at a sample + * meeting the specified criteria. */ - public boolean advanceTo(long timeUs, boolean toKeyframe, boolean allowTimeBeyondBuffer) { + public int advanceTo(long timeUs, boolean toKeyframe, boolean allowTimeBeyondBuffer) { return metadataQueue.advanceTo(timeUs, toKeyframe, allowTimeBeyondBuffer); } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/SampleStream.java b/library/core/src/main/java/com/google/android/exoplayer2/source/SampleStream.java index dc58c29c22..06efc980e2 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/SampleStream.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/SampleStream.java @@ -70,7 +70,8 @@ public interface SampleStream { * {@code positionUs} is beyond it. * * @param positionUs The specified time. + * @return The number of samples that were skipped. */ - void skipData(long positionUs); + int skipData(long positionUs); } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaPeriod.java b/library/core/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaPeriod.java index 3435c01eeb..b19f398d86 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaPeriod.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaPeriod.java @@ -235,10 +235,12 @@ import java.util.Arrays; } @Override - public void skipData(long positionUs) { - if (positionUs > 0) { + public int skipData(long positionUs) { + if (positionUs > 0 && streamState != STREAM_STATE_END_OF_STREAM) { streamState = STREAM_STATE_END_OF_STREAM; + return 1; } + return 0; } } 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 0fc3d5881e..f2609a0ffd 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 @@ -160,6 +160,7 @@ public class ChunkSampleStream implements SampleStream, S * @return An estimate of the absolute position in microseconds up to which data is buffered, or * {@link C#TIME_END_OF_SOURCE} if the track is fully buffered. */ + @Override public long getBufferedPositionUs() { if (loadingFinished) { return C.TIME_END_OF_SOURCE; @@ -185,8 +186,8 @@ public class ChunkSampleStream implements SampleStream, S public void seekToUs(long positionUs) { lastSeekPositionUs = positionUs; // If we're not pending a reset, see if we can seek within the primary sample queue. - boolean seekInsideBuffer = !isPendingReset() && primarySampleQueue.advanceTo(positionUs, true, - positionUs < getNextLoadPositionUs()); + boolean seekInsideBuffer = !isPendingReset() && (primarySampleQueue.advanceTo(positionUs, true, + positionUs < getNextLoadPositionUs()) != SampleQueue.ADVANCE_FAILED); if (seekInsideBuffer) { // We succeeded. Discard samples and corresponding chunks prior to the seek position. discardDownstreamMediaChunks(primarySampleQueue.getReadIndex()); @@ -266,13 +267,19 @@ public class ChunkSampleStream implements SampleStream, S } @Override - public void skipData(long positionUs) { + public int skipData(long positionUs) { + int skipCount; if (loadingFinished && positionUs > primarySampleQueue.getLargestQueuedTimestampUs()) { primarySampleQueue.advanceToEnd(); + skipCount = primarySampleQueue.advanceToEnd(); } else { - primarySampleQueue.advanceTo(positionUs, true, true); + skipCount = primarySampleQueue.advanceTo(positionUs, true, true); + if (skipCount == SampleQueue.ADVANCE_FAILED) { + skipCount = 0; + } } primarySampleQueue.discardToRead(); + return skipCount; } // Loader.Callback implementation. @@ -470,11 +477,12 @@ public class ChunkSampleStream implements SampleStream, S } @Override - public void skipData(long positionUs) { + public int skipData(long positionUs) { if (loadingFinished && positionUs > sampleQueue.getLargestQueuedTimestampUs()) { - sampleQueue.advanceToEnd(); + return sampleQueue.advanceToEnd(); } else { - sampleQueue.advanceTo(positionUs, true, true); + int skipCount = sampleQueue.advanceTo(positionUs, true, true); + return skipCount == SampleQueue.ADVANCE_FAILED ? 0 : skipCount; } } diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStream.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStream.java index 450644f60f..e423a682f3 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStream.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStream.java @@ -50,8 +50,8 @@ import java.io.IOException; } @Override - public void skipData(long positionUs) { - sampleStreamWrapper.skipData(group, positionUs); + public int skipData(long positionUs) { + return sampleStreamWrapper.skipData(group, positionUs); } } 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 0b6d1863bd..00a3cd4a85 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 @@ -229,7 +229,7 @@ import java.util.LinkedList; // sample queue, or if we haven't read anything from the queue since the previous seek // (this case is common for sparse tracks such as metadata tracks). In all other cases a // seek is required. - seekRequired = !sampleQueue.advanceTo(positionUs, true, true) + seekRequired = sampleQueue.advanceTo(positionUs, true, true) == SampleQueue.ADVANCE_FAILED && sampleQueue.getReadIndex() != 0; } } @@ -320,6 +320,7 @@ import java.util.LinkedList; return true; } + @Override public long getBufferedPositionUs() { if (loadingFinished) { return C.TIME_END_OF_SOURCE; @@ -402,12 +403,13 @@ import java.util.LinkedList; lastSeekPositionUs); } - /* package */ void skipData(int trackGroupIndex, long positionUs) { + /* package */ int skipData(int trackGroupIndex, long positionUs) { SampleQueue sampleQueue = sampleQueues[trackGroupIndex]; if (loadingFinished && positionUs > sampleQueue.getLargestQueuedTimestampUs()) { - sampleQueue.advanceToEnd(); + return sampleQueue.advanceToEnd(); } else { - sampleQueue.advanceTo(positionUs, true, true); + int skipCount = sampleQueue.advanceTo(positionUs, true, true); + return skipCount == SampleQueue.ADVANCE_FAILED ? 0 : skipCount; } } @@ -760,7 +762,8 @@ import java.util.LinkedList; for (int i = 0; i < trackCount; i++) { SampleQueue sampleQueue = sampleQueues[i]; sampleQueue.rewind(); - boolean seekInsideQueue = sampleQueue.advanceTo(positionUs, true, false); + boolean seekInsideQueue = sampleQueue.advanceTo(positionUs, true, false) + != SampleQueue.ADVANCE_FAILED; // If we have AV tracks then an in-queue 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 diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/DebugTextViewHelper.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/DebugTextViewHelper.java index 2b8705bb74..8c4bad1862 100644 --- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/DebugTextViewHelper.java +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/DebugTextViewHelper.java @@ -183,8 +183,9 @@ public final class DebugTextViewHelper implements Runnable, Player.EventListener return ""; } counters.ensureUpdated(); - return " rb:" + counters.renderedOutputBufferCount + return " sib:" + counters.skippedInputBufferCount + " sb:" + counters.skippedOutputBufferCount + + " rb:" + counters.renderedOutputBufferCount + " db:" + counters.droppedOutputBufferCount + " mcdb:" + counters.maxConsecutiveDroppedOutputBufferCount; } diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeSampleStream.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeSampleStream.java index 4e1e32980f..699b850f73 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeSampleStream.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeSampleStream.java @@ -60,8 +60,8 @@ public final class FakeSampleStream implements SampleStream { } @Override - public void skipData(long positionUs) { - // Do nothing. + public int skipData(long positionUs) { + return 0; } }