Propagate skipped input buffers through to CodecCounters

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=163683081
This commit is contained in:
olly 2017-07-31 05:49:58 -07:00 committed by Oliver Woodman
parent cdeea27973
commit c2d7e05429
16 changed files with 95 additions and 61 deletions

View File

@ -258,45 +258,45 @@ public class SampleQueueTest extends TestCase {
public void testAdvanceToBeforeBuffer() { public void testAdvanceToBeforeBuffer() {
writeTestData(); 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. // Should fail and have no effect.
assertFalse(result); assertEquals(SampleQueue.ADVANCE_FAILED, skipCount);
assertReadTestData(); assertReadTestData();
assertNoSamplesToRead(TEST_FORMAT_2); assertNoSamplesToRead(TEST_FORMAT_2);
} }
public void testAdvanceToStartOfBuffer() { public void testAdvanceToStartOfBuffer() {
writeTestData(); 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). // Should succeed but have no effect (we're already at the first frame).
assertTrue(result); assertEquals(0, skipCount);
assertReadTestData(); assertReadTestData();
assertNoSamplesToRead(TEST_FORMAT_2); assertNoSamplesToRead(TEST_FORMAT_2);
} }
public void testAdvanceToEndOfBuffer() { public void testAdvanceToEndOfBuffer() {
writeTestData(); writeTestData();
boolean result = sampleQueue.advanceTo(LAST_SAMPLE_TIMESTAMP, true, false); int skipCount = sampleQueue.advanceTo(LAST_SAMPLE_TIMESTAMP, true, false);
// Should succeed and skip to 2nd keyframe. // Should succeed and skip to 2nd keyframe (the 4th frame).
assertTrue(result); assertEquals(4, skipCount);
assertReadTestData(null, TEST_DATA_SECOND_KEYFRAME_INDEX); assertReadTestData(null, TEST_DATA_SECOND_KEYFRAME_INDEX);
assertNoSamplesToRead(TEST_FORMAT_2); assertNoSamplesToRead(TEST_FORMAT_2);
} }
public void testAdvanceToAfterBuffer() { public void testAdvanceToAfterBuffer() {
writeTestData(); 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. // Should fail and have no effect.
assertFalse(result); assertEquals(SampleQueue.ADVANCE_FAILED, skipCount);
assertReadTestData(); assertReadTestData();
assertNoSamplesToRead(TEST_FORMAT_2); assertNoSamplesToRead(TEST_FORMAT_2);
} }
public void testAdvanceToAfterBufferAllowed() { public void testAdvanceToAfterBufferAllowed() {
writeTestData(); writeTestData();
boolean result = sampleQueue.advanceTo(LAST_SAMPLE_TIMESTAMP + 1, true, true); int skipCount = sampleQueue.advanceTo(LAST_SAMPLE_TIMESTAMP + 1, true, true);
// Should succeed and skip to 2nd keyframe. // Should succeed and skip to 2nd keyframe (the 4th frame).
assertTrue(result); assertEquals(4, skipCount);
assertReadTestData(null, TEST_DATA_SECOND_KEYFRAME_INDEX); assertReadTestData(null, TEST_DATA_SECOND_KEYFRAME_INDEX);
assertNoSamplesToRead(TEST_FORMAT_2); assertNoSamplesToRead(TEST_FORMAT_2);
} }

View File

@ -296,9 +296,10 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities {
* {@code positionUs} is beyond it. * {@code positionUs} is beyond it.
* *
* @param positionUs The position in microseconds. * @param positionUs The position in microseconds.
* @return The number of samples that were skipped.
*/ */
protected void skipSource(long positionUs) { protected int skipSource(long positionUs) {
stream.skipData(positionUs - streamOffsetUs); return stream.skipData(positionUs - streamOffsetUs);
} }
/** /**

View File

@ -36,6 +36,12 @@ public final class DecoderCounters {
* The number of queued input buffers. * The number of queued input buffers.
*/ */
public int inputBufferCount; public int inputBufferCount;
/**
* The number of skipped input buffers.
* <p>
* 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. * The number of rendered output buffers.
*/ */
@ -79,6 +85,7 @@ public final class DecoderCounters {
decoderInitCount += other.decoderInitCount; decoderInitCount += other.decoderInitCount;
decoderReleaseCount += other.decoderReleaseCount; decoderReleaseCount += other.decoderReleaseCount;
inputBufferCount += other.inputBufferCount; inputBufferCount += other.inputBufferCount;
skippedInputBufferCount += other.skippedInputBufferCount;
renderedOutputBufferCount += other.renderedOutputBufferCount; renderedOutputBufferCount += other.renderedOutputBufferCount;
skippedOutputBufferCount += other.skippedOutputBufferCount; skippedOutputBufferCount += other.skippedOutputBufferCount;
droppedOutputBufferCount += other.droppedOutputBufferCount; droppedOutputBufferCount += other.droppedOutputBufferCount;

View File

@ -530,7 +530,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
while (feedInputBuffer()) {} while (feedInputBuffer()) {}
TraceUtil.endSection(); TraceUtil.endSection();
} else { } else {
skipSource(positionUs); decoderCounters.skippedInputBufferCount += skipSource(positionUs);
// We need to read any format changes despite not having a codec so that drmSession can be // 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 // 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 // also reach the end of the stream. Note that readSource will not read a sample into a

View File

@ -286,8 +286,8 @@ public final class ClippingMediaPeriod implements MediaPeriod, MediaPeriod.Callb
} }
@Override @Override
public void skipData(long positionUs) { public int skipData(long positionUs) {
stream.skipData(startUs + positionUs); return stream.skipData(startUs + positionUs);
} }
} }

View File

@ -43,8 +43,8 @@ public final class EmptySampleStream implements SampleStream {
} }
@Override @Override
public void skipData(long positionUs) { public int skipData(long positionUs) {
// Do nothing. return 0;
} }
} }

View File

@ -238,7 +238,7 @@ import java.util.Arrays;
// sample queue, or if we haven't read anything from the queue since the previous seek // 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 // (this case is common for sparse tracks such as metadata tracks). In all other cases a
// seek is required. // seek is required.
seekRequired = !sampleQueue.advanceTo(positionUs, true, true) seekRequired = sampleQueue.advanceTo(positionUs, true, true) == SampleQueue.ADVANCE_FAILED
&& sampleQueue.getReadIndex() != 0; && sampleQueue.getReadIndex() != 0;
} }
} }
@ -371,12 +371,13 @@ import java.util.Arrays;
lastSeekPositionUs); lastSeekPositionUs);
} }
/* package */ void skipData(int track, long positionUs) { /* package */ int skipData(int track, long positionUs) {
SampleQueue sampleQueue = sampleQueues[track]; SampleQueue sampleQueue = sampleQueues[track];
if (loadingFinished && positionUs > sampleQueue.getLargestQueuedTimestampUs()) { if (loadingFinished && positionUs > sampleQueue.getLargestQueuedTimestampUs()) {
sampleQueue.advanceToEnd(); return sampleQueue.advanceToEnd();
} else { } 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++) { for (int i = 0; i < trackCount; i++) {
SampleQueue sampleQueue = sampleQueues[i]; SampleQueue sampleQueue = sampleQueues[i];
sampleQueue.rewind(); 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 // 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 // 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 // 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 @Override
public void skipData(long positionUs) { public int skipData(long positionUs) {
ExtractorMediaPeriod.this.skipData(track, positionUs); return ExtractorMediaPeriod.this.skipData(track, positionUs);
} }
} }

View File

@ -253,32 +253,35 @@ import com.google.android.exoplayer2.util.Util;
* @param allowTimeBeyondBuffer Whether the operation can succeed if {@code timeUs} is beyond the * @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 * end of the queue, by advancing the read position to the last sample (or keyframe) in the
* queue. * queue.
* @return Whether the operation was a success. A successful advance is one in which the read * @return The number of samples that were skipped if the operation was successful, which may be
* position was unchanged or advanced, and is now at a sample meeting the specified criteria. * 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) { boolean allowTimeBeyondBuffer) {
int relativeReadIndex = getRelativeIndex(readPosition); int relativeReadIndex = getRelativeIndex(readPosition);
if (!hasNextSample() || timeUs < timesUs[relativeReadIndex] if (!hasNextSample() || timeUs < timesUs[relativeReadIndex]
|| (timeUs > largestQueuedTimestampUs && !allowTimeBeyondBuffer)) { || (timeUs > largestQueuedTimestampUs && !allowTimeBeyondBuffer)) {
return false; return SampleQueue.ADVANCE_FAILED;
} }
int offset = findSampleBefore(relativeReadIndex, length - readPosition, timeUs, toKeyframe); int offset = findSampleBefore(relativeReadIndex, length - readPosition, timeUs, toKeyframe);
if (offset == -1) { if (offset == -1) {
return false; return SampleQueue.ADVANCE_FAILED;
} }
readPosition += offset; readPosition += offset;
return true; return offset;
} }
/** /**
* Advances the read position to the end of the queue. * Advances the read position to the end of the queue.
*
* @return The number of samples that were skipped.
*/ */
public synchronized void advanceToEnd() { public synchronized int advanceToEnd() {
if (!hasNextSample()) { int skipCount = length - readPosition;
return;
}
readPosition = length; readPosition = length;
return skipCount;
} }
/** /**

View File

@ -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 static final int INITIAL_SCRATCH_SIZE = 32;
private final Allocator allocator; private final Allocator allocator;
@ -255,9 +257,11 @@ public final class SampleQueue implements TrackOutput {
/** /**
* Advances the read position to the end of the queue. * Advances the read position to the end of the queue.
*
* @return The number of samples that were skipped.
*/ */
public void advanceToEnd() { public int advanceToEnd() {
metadataQueue.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. * time, rather than to any sample before or at that time.
* @param allowTimeBeyondBuffer Whether the operation can succeed if {@code timeUs} is beyond the * @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). * 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 * @return The number of samples that were skipped if the operation was successful, which may be
* position was unchanged or advanced, and is now at a sample meeting the specified criteria. * 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); return metadataQueue.advanceTo(timeUs, toKeyframe, allowTimeBeyondBuffer);
} }

View File

@ -70,7 +70,8 @@ public interface SampleStream {
* {@code positionUs} is beyond it. * {@code positionUs} is beyond it.
* *
* @param positionUs The specified time. * @param positionUs The specified time.
* @return The number of samples that were skipped.
*/ */
void skipData(long positionUs); int skipData(long positionUs);
} }

View File

@ -235,10 +235,12 @@ import java.util.Arrays;
} }
@Override @Override
public void skipData(long positionUs) { public int skipData(long positionUs) {
if (positionUs > 0) { if (positionUs > 0 && streamState != STREAM_STATE_END_OF_STREAM) {
streamState = STREAM_STATE_END_OF_STREAM; streamState = STREAM_STATE_END_OF_STREAM;
return 1;
} }
return 0;
} }
} }

View File

@ -160,6 +160,7 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S
* @return An estimate of the absolute position in microseconds up to which data is buffered, or * @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. * {@link C#TIME_END_OF_SOURCE} if the track is fully buffered.
*/ */
@Override
public long getBufferedPositionUs() { public long getBufferedPositionUs() {
if (loadingFinished) { if (loadingFinished) {
return C.TIME_END_OF_SOURCE; return C.TIME_END_OF_SOURCE;
@ -185,8 +186,8 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S
public void seekToUs(long positionUs) { public void seekToUs(long positionUs) {
lastSeekPositionUs = positionUs; lastSeekPositionUs = positionUs;
// If we're not pending a reset, see if we can seek within the primary sample queue. // If we're not pending a reset, see if we can seek within the primary sample queue.
boolean seekInsideBuffer = !isPendingReset() && primarySampleQueue.advanceTo(positionUs, true, boolean seekInsideBuffer = !isPendingReset() && (primarySampleQueue.advanceTo(positionUs, true,
positionUs < getNextLoadPositionUs()); positionUs < getNextLoadPositionUs()) != SampleQueue.ADVANCE_FAILED);
if (seekInsideBuffer) { if (seekInsideBuffer) {
// We succeeded. Discard samples and corresponding chunks prior to the seek position. // We succeeded. Discard samples and corresponding chunks prior to the seek position.
discardDownstreamMediaChunks(primarySampleQueue.getReadIndex()); discardDownstreamMediaChunks(primarySampleQueue.getReadIndex());
@ -266,13 +267,19 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S
} }
@Override @Override
public void skipData(long positionUs) { public int skipData(long positionUs) {
int skipCount;
if (loadingFinished && positionUs > primarySampleQueue.getLargestQueuedTimestampUs()) { if (loadingFinished && positionUs > primarySampleQueue.getLargestQueuedTimestampUs()) {
primarySampleQueue.advanceToEnd(); primarySampleQueue.advanceToEnd();
skipCount = primarySampleQueue.advanceToEnd();
} else { } else {
primarySampleQueue.advanceTo(positionUs, true, true); skipCount = primarySampleQueue.advanceTo(positionUs, true, true);
if (skipCount == SampleQueue.ADVANCE_FAILED) {
skipCount = 0;
}
} }
primarySampleQueue.discardToRead(); primarySampleQueue.discardToRead();
return skipCount;
} }
// Loader.Callback implementation. // Loader.Callback implementation.
@ -470,11 +477,12 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S
} }
@Override @Override
public void skipData(long positionUs) { public int skipData(long positionUs) {
if (loadingFinished && positionUs > sampleQueue.getLargestQueuedTimestampUs()) { if (loadingFinished && positionUs > sampleQueue.getLargestQueuedTimestampUs()) {
sampleQueue.advanceToEnd(); return sampleQueue.advanceToEnd();
} else { } else {
sampleQueue.advanceTo(positionUs, true, true); int skipCount = sampleQueue.advanceTo(positionUs, true, true);
return skipCount == SampleQueue.ADVANCE_FAILED ? 0 : skipCount;
} }
} }

View File

@ -50,8 +50,8 @@ import java.io.IOException;
} }
@Override @Override
public void skipData(long positionUs) { public int skipData(long positionUs) {
sampleStreamWrapper.skipData(group, positionUs); return sampleStreamWrapper.skipData(group, positionUs);
} }
} }

View File

@ -229,7 +229,7 @@ import java.util.LinkedList;
// sample queue, or if we haven't read anything from the queue since the previous seek // 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 // (this case is common for sparse tracks such as metadata tracks). In all other cases a
// seek is required. // seek is required.
seekRequired = !sampleQueue.advanceTo(positionUs, true, true) seekRequired = sampleQueue.advanceTo(positionUs, true, true) == SampleQueue.ADVANCE_FAILED
&& sampleQueue.getReadIndex() != 0; && sampleQueue.getReadIndex() != 0;
} }
} }
@ -320,6 +320,7 @@ import java.util.LinkedList;
return true; return true;
} }
@Override
public long getBufferedPositionUs() { public long getBufferedPositionUs() {
if (loadingFinished) { if (loadingFinished) {
return C.TIME_END_OF_SOURCE; return C.TIME_END_OF_SOURCE;
@ -402,12 +403,13 @@ import java.util.LinkedList;
lastSeekPositionUs); lastSeekPositionUs);
} }
/* package */ void skipData(int trackGroupIndex, long positionUs) { /* package */ int skipData(int trackGroupIndex, long positionUs) {
SampleQueue sampleQueue = sampleQueues[trackGroupIndex]; SampleQueue sampleQueue = sampleQueues[trackGroupIndex];
if (loadingFinished && positionUs > sampleQueue.getLargestQueuedTimestampUs()) { if (loadingFinished && positionUs > sampleQueue.getLargestQueuedTimestampUs()) {
sampleQueue.advanceToEnd(); return sampleQueue.advanceToEnd();
} else { } 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++) { for (int i = 0; i < trackCount; i++) {
SampleQueue sampleQueue = sampleQueues[i]; SampleQueue sampleQueue = sampleQueues[i];
sampleQueue.rewind(); 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 // 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 // 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 // they may be sparse or poorly interleaved. If we only have non-AV tracks then a seek is

View File

@ -183,8 +183,9 @@ public final class DebugTextViewHelper implements Runnable, Player.EventListener
return ""; return "";
} }
counters.ensureUpdated(); counters.ensureUpdated();
return " rb:" + counters.renderedOutputBufferCount return " sib:" + counters.skippedInputBufferCount
+ " sb:" + counters.skippedOutputBufferCount + " sb:" + counters.skippedOutputBufferCount
+ " rb:" + counters.renderedOutputBufferCount
+ " db:" + counters.droppedOutputBufferCount + " db:" + counters.droppedOutputBufferCount
+ " mcdb:" + counters.maxConsecutiveDroppedOutputBufferCount; + " mcdb:" + counters.maxConsecutiveDroppedOutputBufferCount;
} }

View File

@ -60,8 +60,8 @@ public final class FakeSampleStream implements SampleStream {
} }
@Override @Override
public void skipData(long positionUs) { public int skipData(long positionUs) {
// Do nothing. return 0;
} }
} }