Expose first index in SampleQueue

This will be needed when retaining a back-buffer. Being able to
query the first index allows us to work out when we've discarded
all samples that were obtained from a particular chunk, which
we'll use to determine when to remove chunks from
ChunkSampleStream.mediaChunks.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=175172085
This commit is contained in:
olly 2017-11-09 09:58:18 -08:00 committed by Oliver Woodman
parent 694bd997cb
commit eb4e05199f
3 changed files with 60 additions and 27 deletions

View File

@ -51,8 +51,8 @@ import com.google.android.exoplayer2.util.Util;
private Format[] formats; private Format[] formats;
private int length; private int length;
private int absoluteStartIndex; private int absoluteFirstIndex;
private int relativeStartIndex; private int relativeFirstIndex;
private int readPosition; private int readPosition;
private long largestDiscardedTimestampUs; private long largestDiscardedTimestampUs;
@ -87,8 +87,8 @@ import com.google.android.exoplayer2.util.Util;
*/ */
public void reset(boolean resetUpstreamFormat) { public void reset(boolean resetUpstreamFormat) {
length = 0; length = 0;
absoluteStartIndex = 0; absoluteFirstIndex = 0;
relativeStartIndex = 0; relativeFirstIndex = 0;
readPosition = 0; readPosition = 0;
upstreamKeyframeRequired = true; upstreamKeyframeRequired = true;
largestDiscardedTimestampUs = Long.MIN_VALUE; largestDiscardedTimestampUs = Long.MIN_VALUE;
@ -103,7 +103,7 @@ import com.google.android.exoplayer2.util.Util;
* Returns the current absolute write index. * Returns the current absolute write index.
*/ */
public int getWriteIndex() { public int getWriteIndex() {
return absoluteStartIndex + length; return absoluteFirstIndex + length;
} }
/** /**
@ -132,11 +132,18 @@ import com.google.android.exoplayer2.util.Util;
// Called by the consuming thread. // Called by the consuming thread.
/**
* Returns the current absolute start index.
*/
public int getFirstIndex() {
return absoluteFirstIndex;
}
/** /**
* Returns the current absolute read index. * Returns the current absolute read index.
*/ */
public int getReadIndex() { public int getReadIndex() {
return absoluteStartIndex + readPosition; return absoluteFirstIndex + readPosition;
} }
/** /**
@ -297,11 +304,11 @@ import com.google.android.exoplayer2.util.Util;
* {@link C#POSITION_UNSET} if no discarding of data is necessary. * {@link C#POSITION_UNSET} if no discarding of data is necessary.
*/ */
public synchronized long discardTo(long timeUs, boolean toKeyframe, boolean stopAtReadPosition) { public synchronized long discardTo(long timeUs, boolean toKeyframe, boolean stopAtReadPosition) {
if (length == 0 || timeUs < timesUs[relativeStartIndex]) { if (length == 0 || timeUs < timesUs[relativeFirstIndex]) {
return C.POSITION_UNSET; return C.POSITION_UNSET;
} }
int searchLength = stopAtReadPosition && readPosition != length ? readPosition + 1 : length; int searchLength = stopAtReadPosition && readPosition != length ? readPosition + 1 : length;
int discardCount = findSampleBefore(relativeStartIndex, searchLength, timeUs, toKeyframe); int discardCount = findSampleBefore(relativeFirstIndex, searchLength, timeUs, toKeyframe);
if (discardCount == -1) { if (discardCount == -1) {
return C.POSITION_UNSET; return C.POSITION_UNSET;
} }
@ -382,15 +389,15 @@ import com.google.android.exoplayer2.util.Util;
int[] newSizes = new int[newCapacity]; int[] newSizes = new int[newCapacity];
CryptoData[] newCryptoDatas = new CryptoData[newCapacity]; CryptoData[] newCryptoDatas = new CryptoData[newCapacity];
Format[] newFormats = new Format[newCapacity]; Format[] newFormats = new Format[newCapacity];
int beforeWrap = capacity - relativeStartIndex; int beforeWrap = capacity - relativeFirstIndex;
System.arraycopy(offsets, relativeStartIndex, newOffsets, 0, beforeWrap); System.arraycopy(offsets, relativeFirstIndex, newOffsets, 0, beforeWrap);
System.arraycopy(timesUs, relativeStartIndex, newTimesUs, 0, beforeWrap); System.arraycopy(timesUs, relativeFirstIndex, newTimesUs, 0, beforeWrap);
System.arraycopy(flags, relativeStartIndex, newFlags, 0, beforeWrap); System.arraycopy(flags, relativeFirstIndex, newFlags, 0, beforeWrap);
System.arraycopy(sizes, relativeStartIndex, newSizes, 0, beforeWrap); System.arraycopy(sizes, relativeFirstIndex, newSizes, 0, beforeWrap);
System.arraycopy(cryptoDatas, relativeStartIndex, newCryptoDatas, 0, beforeWrap); System.arraycopy(cryptoDatas, relativeFirstIndex, newCryptoDatas, 0, beforeWrap);
System.arraycopy(formats, relativeStartIndex, newFormats, 0, beforeWrap); System.arraycopy(formats, relativeFirstIndex, newFormats, 0, beforeWrap);
System.arraycopy(sourceIds, relativeStartIndex, newSourceIds, 0, beforeWrap); System.arraycopy(sourceIds, relativeFirstIndex, newSourceIds, 0, beforeWrap);
int afterWrap = relativeStartIndex; int afterWrap = relativeFirstIndex;
System.arraycopy(offsets, 0, newOffsets, beforeWrap, afterWrap); System.arraycopy(offsets, 0, newOffsets, beforeWrap, afterWrap);
System.arraycopy(timesUs, 0, newTimesUs, beforeWrap, afterWrap); System.arraycopy(timesUs, 0, newTimesUs, beforeWrap, afterWrap);
System.arraycopy(flags, 0, newFlags, beforeWrap, afterWrap); System.arraycopy(flags, 0, newFlags, beforeWrap, afterWrap);
@ -405,7 +412,7 @@ import com.google.android.exoplayer2.util.Util;
cryptoDatas = newCryptoDatas; cryptoDatas = newCryptoDatas;
formats = newFormats; formats = newFormats;
sourceIds = newSourceIds; sourceIds = newSourceIds;
relativeStartIndex = 0; relativeFirstIndex = 0;
length = capacity; length = capacity;
capacity = newCapacity; capacity = newCapacity;
} }
@ -440,7 +447,7 @@ import com.google.android.exoplayer2.util.Util;
relativeSampleIndex = capacity - 1; relativeSampleIndex = capacity - 1;
} }
} }
discardUpstreamSamples(absoluteStartIndex + retainCount); discardUpstreamSamples(absoluteFirstIndex + retainCount);
return true; return true;
} }
@ -454,7 +461,7 @@ import com.google.android.exoplayer2.util.Util;
* @param length The length of the range being searched. * @param length The length of the range being searched.
* @param timeUs The specified time. * @param timeUs The specified time.
* @param keyframe Whether only keyframes should be considered. * @param keyframe Whether only keyframes should be considered.
* @return The offset from {@code relativeStartIndex} to the found sample, or -1 if no matching * @return The offset from {@code relativeFirstIndex} to the found sample, or -1 if no matching
* sample was found. * sample was found.
*/ */
private int findSampleBefore(int relativeStartIndex, int length, long timeUs, boolean keyframe) { private int findSampleBefore(int relativeStartIndex, int length, long timeUs, boolean keyframe) {
@ -487,20 +494,20 @@ import com.google.android.exoplayer2.util.Util;
largestDiscardedTimestampUs = Math.max(largestDiscardedTimestampUs, largestDiscardedTimestampUs = Math.max(largestDiscardedTimestampUs,
getLargestTimestamp(discardCount)); getLargestTimestamp(discardCount));
length -= discardCount; length -= discardCount;
absoluteStartIndex += discardCount; absoluteFirstIndex += discardCount;
relativeStartIndex += discardCount; relativeFirstIndex += discardCount;
if (relativeStartIndex >= capacity) { if (relativeFirstIndex >= capacity) {
relativeStartIndex -= capacity; relativeFirstIndex -= capacity;
} }
readPosition -= discardCount; readPosition -= discardCount;
if (readPosition < 0) { if (readPosition < 0) {
readPosition = 0; readPosition = 0;
} }
if (length == 0) { if (length == 0) {
int relativeLastDiscardIndex = (relativeStartIndex == 0 ? capacity : relativeStartIndex) - 1; int relativeLastDiscardIndex = (relativeFirstIndex == 0 ? capacity : relativeFirstIndex) - 1;
return offsets[relativeLastDiscardIndex] + sizes[relativeLastDiscardIndex]; return offsets[relativeLastDiscardIndex] + sizes[relativeLastDiscardIndex];
} else { } else {
return offsets[relativeStartIndex]; return offsets[relativeFirstIndex];
} }
} }
@ -537,7 +544,7 @@ import com.google.android.exoplayer2.util.Util;
* @param offset The offset, which must be in the range [0, length]. * @param offset The offset, which must be in the range [0, length].
*/ */
private int getRelativeIndex(int offset) { private int getRelativeIndex(int offset) {
int relativeIndex = relativeStartIndex + offset; int relativeIndex = relativeFirstIndex + offset;
return relativeIndex < capacity ? relativeIndex : relativeIndex - capacity; return relativeIndex < capacity ? relativeIndex : relativeIndex - capacity;
} }

View File

@ -181,6 +181,13 @@ public final class SampleQueue implements TrackOutput {
return metadataQueue.hasNextSample(); return metadataQueue.hasNextSample();
} }
/**
* Returns the absolute index of the first sample.
*/
public int getFirstIndex() {
return metadataQueue.getFirstIndex();
}
/** /**
* Returns the current absolute read index. * Returns the current absolute read index.
*/ */

View File

@ -215,12 +215,14 @@ public final class SampleQueueTest {
public void testReadMultiWithRewind() { public void testReadMultiWithRewind() {
writeTestData(); writeTestData();
assertReadTestData(); assertReadTestData();
assertThat(sampleQueue.getFirstIndex()).isEqualTo(0);
assertThat(sampleQueue.getReadIndex()).isEqualTo(8); assertThat(sampleQueue.getReadIndex()).isEqualTo(8);
assertAllocationCount(10); assertAllocationCount(10);
// Rewind. // Rewind.
sampleQueue.rewind(); sampleQueue.rewind();
assertAllocationCount(10); assertAllocationCount(10);
// Read again. // Read again.
assertThat(sampleQueue.getFirstIndex()).isEqualTo(0);
assertThat(sampleQueue.getReadIndex()).isEqualTo(0); assertThat(sampleQueue.getReadIndex()).isEqualTo(0);
assertReadTestData(); assertReadTestData();
} }
@ -230,11 +232,14 @@ public final class SampleQueueTest {
writeTestData(); writeTestData();
assertReadTestData(); assertReadTestData();
sampleQueue.discardToRead(); sampleQueue.discardToRead();
assertThat(sampleQueue.getFirstIndex()).isEqualTo(8);
assertThat(sampleQueue.getReadIndex()).isEqualTo(8);
assertAllocationCount(0); assertAllocationCount(0);
// Rewind. // Rewind.
sampleQueue.rewind(); sampleQueue.rewind();
assertAllocationCount(0); assertAllocationCount(0);
// Can't read again. // Can't read again.
assertThat(sampleQueue.getFirstIndex()).isEqualTo(8);
assertThat(sampleQueue.getReadIndex()).isEqualTo(8); assertThat(sampleQueue.getReadIndex()).isEqualTo(8);
assertReadEndOfStream(false); assertReadEndOfStream(false);
} }
@ -332,6 +337,7 @@ public final class SampleQueueTest {
writeTestData(); writeTestData();
// Should discard everything. // Should discard everything.
sampleQueue.discardToEnd(); sampleQueue.discardToEnd();
assertThat(sampleQueue.getFirstIndex()).isEqualTo(8);
assertThat(sampleQueue.getReadIndex()).isEqualTo(8); assertThat(sampleQueue.getReadIndex()).isEqualTo(8);
assertAllocationCount(0); assertAllocationCount(0);
// We should still be able to read the upstream format. // We should still be able to read the upstream format.
@ -346,28 +352,39 @@ public final class SampleQueueTest {
writeTestData(); writeTestData();
// Shouldn't discard anything. // Shouldn't discard anything.
sampleQueue.discardTo(LAST_SAMPLE_TIMESTAMP, false, true); sampleQueue.discardTo(LAST_SAMPLE_TIMESTAMP, false, true);
assertThat(sampleQueue.getFirstIndex()).isEqualTo(0);
assertThat(sampleQueue.getReadIndex()).isEqualTo(0); assertThat(sampleQueue.getReadIndex()).isEqualTo(0);
assertAllocationCount(10); assertAllocationCount(10);
// Read the first sample. // Read the first sample.
assertReadTestData(null, 0, 1); assertReadTestData(null, 0, 1);
// Shouldn't discard anything. // Shouldn't discard anything.
sampleQueue.discardTo(TEST_SAMPLE_TIMESTAMPS[1] - 1, false, true); sampleQueue.discardTo(TEST_SAMPLE_TIMESTAMPS[1] - 1, false, true);
assertThat(sampleQueue.getFirstIndex()).isEqualTo(0);
assertThat(sampleQueue.getReadIndex()).isEqualTo(1); assertThat(sampleQueue.getReadIndex()).isEqualTo(1);
assertAllocationCount(10); assertAllocationCount(10);
// Should discard the read sample. // Should discard the read sample.
sampleQueue.discardTo(TEST_SAMPLE_TIMESTAMPS[1], false, true); sampleQueue.discardTo(TEST_SAMPLE_TIMESTAMPS[1], false, true);
assertThat(sampleQueue.getFirstIndex()).isEqualTo(1);
assertThat(sampleQueue.getReadIndex()).isEqualTo(1);
assertAllocationCount(9); assertAllocationCount(9);
// Shouldn't discard anything. // Shouldn't discard anything.
sampleQueue.discardTo(LAST_SAMPLE_TIMESTAMP, false, true); sampleQueue.discardTo(LAST_SAMPLE_TIMESTAMP, false, true);
assertThat(sampleQueue.getFirstIndex()).isEqualTo(1);
assertThat(sampleQueue.getReadIndex()).isEqualTo(1);
assertAllocationCount(9); assertAllocationCount(9);
// Should be able to read the remaining samples. // Should be able to read the remaining samples.
assertReadTestData(TEST_FORMAT_1, 1, 7); assertReadTestData(TEST_FORMAT_1, 1, 7);
assertThat(sampleQueue.getFirstIndex()).isEqualTo(1);
assertThat(sampleQueue.getReadIndex()).isEqualTo(8); assertThat(sampleQueue.getReadIndex()).isEqualTo(8);
// Should discard up to the second last sample // Should discard up to the second last sample
sampleQueue.discardTo(LAST_SAMPLE_TIMESTAMP - 1, false, true); sampleQueue.discardTo(LAST_SAMPLE_TIMESTAMP - 1, false, true);
assertThat(sampleQueue.getFirstIndex()).isEqualTo(6);
assertThat(sampleQueue.getReadIndex()).isEqualTo(8);
assertAllocationCount(3); assertAllocationCount(3);
// Should discard up the last sample // Should discard up the last sample
sampleQueue.discardTo(LAST_SAMPLE_TIMESTAMP, false, true); sampleQueue.discardTo(LAST_SAMPLE_TIMESTAMP, false, true);
assertThat(sampleQueue.getFirstIndex()).isEqualTo(7);
assertThat(sampleQueue.getReadIndex()).isEqualTo(8);
assertAllocationCount(1); assertAllocationCount(1);
} }
@ -376,10 +393,12 @@ public final class SampleQueueTest {
writeTestData(); writeTestData();
// Shouldn't discard anything. // Shouldn't discard anything.
sampleQueue.discardTo(TEST_SAMPLE_TIMESTAMPS[1] - 1, false, false); sampleQueue.discardTo(TEST_SAMPLE_TIMESTAMPS[1] - 1, false, false);
assertThat(sampleQueue.getFirstIndex()).isEqualTo(0);
assertThat(sampleQueue.getReadIndex()).isEqualTo(0); assertThat(sampleQueue.getReadIndex()).isEqualTo(0);
assertAllocationCount(10); assertAllocationCount(10);
// Should discard the first sample. // Should discard the first sample.
sampleQueue.discardTo(TEST_SAMPLE_TIMESTAMPS[1], false, false); sampleQueue.discardTo(TEST_SAMPLE_TIMESTAMPS[1], false, false);
assertThat(sampleQueue.getFirstIndex()).isEqualTo(1);
assertThat(sampleQueue.getReadIndex()).isEqualTo(1); assertThat(sampleQueue.getReadIndex()).isEqualTo(1);
assertAllocationCount(9); assertAllocationCount(9);
// Should be able to read the remaining samples. // Should be able to read the remaining samples.