Commit 8e49cab8 by olly Committed by Oliver Woodman

Start finalizing SampleQueue methods

It's a bit messy at the moment with the deprecated methods
in there, but on the read side the new set of methods is as
follows:

Modifies the start of buffer:
- discardTo(time, keyframe, ...) [this is new]
- discardToRead()
- discardToEnd()

Modifies the read position:
- rewind()
- advanceTo(time, keyframe, ...) [this is a generalization of skipToKeyframeBefore]
- advanceToEnd() [previously called skipAll]
- read(...)

Which seems quite nice and self-consistent, and is powerful
enough for everything that we need to do as we move MediaSource
implementations over to the new methods.

TODOs for subsequent changes:
- Re-order methods in the two classes so that they're actually in
  the same order, and move the deprecated ones out of the way
- Enhance SampleQueueTest to also cover new functionality, as we
  start transitioning MediaSource implementations over to use it.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=159688660
parent 59ccd635
......@@ -56,9 +56,11 @@ public class SampleQueueTest extends TestCase {
ALLOCATION_SIZE * 9, ALLOCATION_SIZE * 8 + 1, ALLOCATION_SIZE * 7, ALLOCATION_SIZE * 6 + 1,
ALLOCATION_SIZE * 5, ALLOCATION_SIZE * 3, ALLOCATION_SIZE + 1, 0
};
private static final int[] TEST_SAMPLE_TIMESTAMPS = new int[] {
private static final long[] TEST_SAMPLE_TIMESTAMPS = new long[] {
0, 1000, 2000, 3000, 4000, 5000, 6000, 7000
};
private static final long LAST_SAMPLE_TIMESTAMP =
TEST_SAMPLE_TIMESTAMPS[TEST_SAMPLE_TIMESTAMPS.length - 1];
private static final int[] TEST_SAMPLE_FLAGS = new int[] {
C.BUFFER_FLAG_KEY_FRAME, 0, 0, 0, C.BUFFER_FLAG_KEY_FRAME, 0, 0, 0
};
......@@ -173,8 +175,7 @@ public class SampleQueueTest extends TestCase {
public void testReadMultiSamples() {
writeTestData();
assertEquals(TEST_SAMPLE_TIMESTAMPS[TEST_SAMPLE_TIMESTAMPS.length - 1],
sampleQueue.getLargestQueuedTimestampUs());
assertEquals(LAST_SAMPLE_TIMESTAMP, sampleQueue.getLargestQueuedTimestampUs());
assertAllocationCount(10);
assertReadTestData();
assertAllocationCount(10);
......@@ -219,9 +220,9 @@ public class SampleQueueTest extends TestCase {
assertReadEndOfStream(false);
}
public void testSkipAll() {
public void testAdvanceToEnd() {
writeTestData();
sampleQueue.skipAll2();
sampleQueue.advanceToEnd();
assertAllocationCount(10);
sampleQueue.discardToRead();
assertAllocationCount(0);
......@@ -232,10 +233,10 @@ public class SampleQueueTest extends TestCase {
assertNoSamplesToRead(TEST_FORMAT_2);
}
public void testSkipAllRetainsUnassignedData() {
public void testAdvanceToEndRetainsUnassignedData() {
sampleQueue.format(TEST_FORMAT_1);
sampleQueue.sampleData(new ParsableByteArray(TEST_DATA), ALLOCATION_SIZE);
sampleQueue.skipAll2();
sampleQueue.advanceToEnd();
assertAllocationCount(1);
sampleQueue.discardToRead();
// Skipping shouldn't discard data that may belong to a sample whose metadata has yet to be
......@@ -255,54 +256,107 @@ public class SampleQueueTest extends TestCase {
assertAllocationCount(0);
}
public void testSkipToKeyframeBeforeBuffer() {
public void testAdvanceToBeforeBuffer() {
writeTestData();
boolean result = sampleQueue.skipToKeyframeBefore2(TEST_SAMPLE_TIMESTAMPS[0] - 1, false);
boolean result = sampleQueue.advanceTo(TEST_SAMPLE_TIMESTAMPS[0] - 1, true, false);
// Should fail and have no effect.
assertFalse(result);
assertReadTestData();
assertNoSamplesToRead(TEST_FORMAT_2);
}
public void testSkipToKeyframeStartOfBuffer() {
public void testAdvanceToStartOfBuffer() {
writeTestData();
boolean result = sampleQueue.skipToKeyframeBefore2(TEST_SAMPLE_TIMESTAMPS[0], false);
boolean result = sampleQueue.advanceTo(TEST_SAMPLE_TIMESTAMPS[0], true, false);
// Should succeed but have no effect (we're already at the first frame).
assertTrue(result);
assertReadTestData();
assertNoSamplesToRead(TEST_FORMAT_2);
}
public void testSkipToKeyframeEndOfBuffer() {
public void testAdvanceToEndOfBuffer() {
writeTestData();
boolean result = sampleQueue.skipToKeyframeBefore2(
TEST_SAMPLE_TIMESTAMPS[TEST_SAMPLE_TIMESTAMPS.length - 1], false);
boolean result = sampleQueue.advanceTo(LAST_SAMPLE_TIMESTAMP, true, false);
// Should succeed and skip to 2nd keyframe.
assertTrue(result);
assertReadTestData(null, TEST_DATA_SECOND_KEYFRAME_INDEX);
assertNoSamplesToRead(TEST_FORMAT_2);
}
public void testSkipToKeyframeAfterBuffer() {
public void testAdvanceToAfterBuffer() {
writeTestData();
boolean result = sampleQueue.skipToKeyframeBefore2(
TEST_SAMPLE_TIMESTAMPS[TEST_SAMPLE_TIMESTAMPS.length - 1] + 1, false);
boolean result = sampleQueue.advanceTo(LAST_SAMPLE_TIMESTAMP + 1, true, false);
// Should fail and have no effect.
assertFalse(result);
assertReadTestData();
assertNoSamplesToRead(TEST_FORMAT_2);
}
public void testSkipToKeyframeAfterBufferAllowed() {
public void testAdvanceToAfterBufferAllowed() {
writeTestData();
boolean result = sampleQueue.skipToKeyframeBefore2(
TEST_SAMPLE_TIMESTAMPS[TEST_SAMPLE_TIMESTAMPS.length - 1] + 1, true);
boolean result = sampleQueue.advanceTo(LAST_SAMPLE_TIMESTAMP + 1, true, true);
// Should succeed and skip to 2nd keyframe.
assertTrue(result);
assertReadTestData(null, TEST_DATA_SECOND_KEYFRAME_INDEX);
assertNoSamplesToRead(TEST_FORMAT_2);
}
public void testDiscardToEnd() {
writeTestData();
// Should discard everything.
sampleQueue.discardToEnd();
assertEquals(8, sampleQueue.getReadIndex());
assertAllocationCount(0);
// We should still be able to read the upstream format.
assertReadFormat(false, TEST_FORMAT_2);
// We should be able to write and read subsequent samples.
writeTestData();
assertReadTestData(TEST_FORMAT_2);
}
public void testDiscardToStopAtReadPosition() {
writeTestData();
// Shouldn't discard anything.
sampleQueue.discardTo(LAST_SAMPLE_TIMESTAMP, false, true);
assertEquals(0, sampleQueue.getReadIndex());
assertAllocationCount(10);
// Read the first sample.
assertReadTestData(null, 0, 1);
// Shouldn't discard anything.
sampleQueue.discardTo(TEST_SAMPLE_TIMESTAMPS[1] - 1, false, true);
assertEquals(1, sampleQueue.getReadIndex());
assertAllocationCount(10);
// Should discard the read sample.
sampleQueue.discardTo(TEST_SAMPLE_TIMESTAMPS[1], false, true);
assertAllocationCount(9);
// Shouldn't discard anything.
sampleQueue.discardTo(LAST_SAMPLE_TIMESTAMP, false, true);
assertAllocationCount(9);
// Should be able to read the remaining samples.
assertReadTestData(TEST_FORMAT_1, 1, 7);
assertEquals(8, sampleQueue.getReadIndex());
// Should discard up to the second last sample
sampleQueue.discardTo(LAST_SAMPLE_TIMESTAMP - 1, false, true);
assertAllocationCount(3);
// Should discard up the last sample
sampleQueue.discardTo(LAST_SAMPLE_TIMESTAMP, false, true);
assertAllocationCount(1);
}
public void testDiscardToDontStopAtReadPosition() {
writeTestData();
// Shouldn't discard anything.
sampleQueue.discardTo(TEST_SAMPLE_TIMESTAMPS[1] - 1, false, false);
assertEquals(0, sampleQueue.getReadIndex());
assertAllocationCount(10);
// Should discard the first sample.
sampleQueue.discardTo(TEST_SAMPLE_TIMESTAMPS[1], false, false);
assertEquals(1, sampleQueue.getReadIndex());
assertAllocationCount(9);
// Should be able to read the remaining samples.
assertReadTestData(TEST_FORMAT_1, 1, 7);
}
public void testDiscardUpstream() {
writeTestData();
sampleQueue.discardUpstreamSamples(8);
......@@ -476,7 +530,7 @@ public class SampleQueueTest extends TestCase {
*/
private void assertReadNothing(boolean formatRequired) {
clearFormatHolderAndInputBuffer();
int result = sampleQueue.readData2(formatHolder, inputBuffer, formatRequired, false, 0);
int result = sampleQueue.read(formatHolder, inputBuffer, formatRequired, false, 0);
assertEquals(C.RESULT_NOTHING_READ, result);
// formatHolder should not be populated.
assertNull(formatHolder.format);
......@@ -493,7 +547,7 @@ public class SampleQueueTest extends TestCase {
*/
private void assertReadEndOfStream(boolean formatRequired) {
clearFormatHolderAndInputBuffer();
int result = sampleQueue.readData2(formatHolder, inputBuffer, formatRequired, true, 0);
int result = sampleQueue.read(formatHolder, inputBuffer, formatRequired, true, 0);
assertEquals(C.RESULT_BUFFER_READ, result);
// formatHolder should not be populated.
assertNull(formatHolder.format);
......@@ -513,7 +567,7 @@ public class SampleQueueTest extends TestCase {
*/
private void assertReadFormat(boolean formatRequired, Format format) {
clearFormatHolderAndInputBuffer();
int result = sampleQueue.readData2(formatHolder, inputBuffer, formatRequired, false, 0);
int result = sampleQueue.read(formatHolder, inputBuffer, formatRequired, false, 0);
assertEquals(C.RESULT_FORMAT_READ, result);
// formatHolder should be populated.
assertEquals(format, formatHolder.format);
......@@ -535,7 +589,7 @@ public class SampleQueueTest extends TestCase {
private void assertSampleRead(long timeUs, boolean isKeyframe, byte[] sampleData, int offset,
int length) {
clearFormatHolderAndInputBuffer();
int result = sampleQueue.readData2(formatHolder, inputBuffer, false, false, 0);
int result = sampleQueue.read(formatHolder, inputBuffer, false, false, 0);
assertEquals(C.RESULT_BUFFER_READ, result);
// formatHolder should not be populated.
assertNull(formatHolder.format);
......
......@@ -214,7 +214,7 @@ import com.google.android.exoplayer2.util.Util;
* or {@link C#RESULT_BUFFER_READ}.
*/
@SuppressWarnings("ReferenceEquality")
public synchronized int readData(FormatHolder formatHolder, DecoderInputBuffer buffer,
public synchronized int read(FormatHolder formatHolder, DecoderInputBuffer buffer,
boolean formatRequired, boolean loadingFinished, Format downstreamFormat,
SampleExtrasHolder extrasHolder) {
if (!hasNextSample()) {
......@@ -252,56 +252,36 @@ import com.google.android.exoplayer2.util.Util;
}
/**
* Attempts to advance the read position to the keyframe before or at the specified time. If
* {@code allowTimeBeyondBuffer} is {@code false} then it is also required that {@code timeUs}
* falls within the buffer.
* Attempts to advance the read position to the sample before or at the specified time.
*
* @param timeUs The seek time.
* @param allowTimeBeyondBuffer Whether the skip can succeed if {@code timeUs} is beyond the end
* of the buffer.
* @return Whether the read position was advanced to the keyframe before or at the specified time.
* @param timeUs The time to advance to.
* @param toKeyframe If true then attempts to advance to the keyframe before or at the specified
* 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 buffer, by advancing the read position to the last sample (or keyframe) in the
* buffer.
* @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.
*/
public synchronized boolean skipToKeyframeBefore(long timeUs, boolean allowTimeBeyondBuffer) {
public synchronized boolean advanceTo(long timeUs, boolean toKeyframe,
boolean allowTimeBeyondBuffer) {
int relativeReadIndex = (relativeStartIndex + readPosition) % capacity;
if (!hasNextSample() || timeUs < timesUs[relativeReadIndex]) {
if (!hasNextSample() || timeUs < timesUs[relativeReadIndex]
|| (timeUs > largestQueuedTimestampUs && !allowTimeBeyondBuffer)) {
return false;
}
if (timeUs > largestQueuedTimestampUs && !allowTimeBeyondBuffer) {
return false;
}
// This could be optimized to use a binary search, however in practice callers to this method
// often pass times near to the start of the buffer. Hence it's unclear whether switching to
// a binary search would yield any real benefit.
int sampleCount = 0;
int sampleCountToKeyframe = -1;
int searchIndex = relativeReadIndex;
int relativeEndIndex = (relativeStartIndex + length) % capacity;
while (searchIndex != relativeEndIndex) {
if (timesUs[searchIndex] > timeUs) {
// We've gone too far.
break;
} else if ((flags[searchIndex] & C.BUFFER_FLAG_KEY_FRAME) != 0) {
// We've found a keyframe, and we're still before the seek position.
sampleCountToKeyframe = sampleCount;
}
searchIndex = (searchIndex + 1) % capacity;
sampleCount++;
}
if (sampleCountToKeyframe == -1) {
int offset = findSampleBefore(relativeReadIndex, length - readPosition, timeUs, toKeyframe);
if (offset == -1) {
return false;
}
readPosition += sampleCountToKeyframe;
readPosition += offset;
return true;
}
/**
* Skips all samples in the buffer.
* Advances the read position to the end of the queue.
*/
public synchronized void skipAll() {
public synchronized void advanceToEnd() {
if (!hasNextSample()) {
return;
}
......@@ -309,28 +289,53 @@ import com.google.android.exoplayer2.util.Util;
}
/**
* Discards all samples in the buffer prior to the read position.
* Discards up to but not including the sample immediately before or at the specified time.
*
* @return The offset up to which data should be dropped, or {@link C#POSITION_UNSET} if no
* dropping of data is required.
* @param timeUs The time to discard up to.
* @param toKeyframe If true then discards samples up to the keyframe before or at the specified
* time, rather than just any sample before or at that time.
* @param stopAtReadPosition If true then samples are only discarded if they're before the read
* position. If false then samples at and beyond the read position may be discarded, in which
* case the read position is advanced to the first remaining sample.
* @return The corresponding offset up to which data should be discarded, or
* {@link C#POSITION_UNSET} if no discarding of data is necessary.
*/
public synchronized long discardTo(long timeUs, boolean toKeyframe, boolean stopAtReadPosition) {
if (length == 0 || timeUs < timesUs[relativeStartIndex]) {
return C.POSITION_UNSET;
}
int searchLength = stopAtReadPosition && readPosition != length ? readPosition + 1 : length;
int discardCount = findSampleBefore(relativeStartIndex, searchLength, timeUs, toKeyframe);
if (discardCount == -1) {
return C.POSITION_UNSET;
}
return discardSamples(discardCount);
}
/**
* Discards samples up to but not including the read position.
*
* @return The corresponding offset up to which data should be discarded, or
* {@link C#POSITION_UNSET} if no discarding of data is necessary.
*/
public synchronized long discardToRead() {
if (readPosition == 0) {
return C.POSITION_UNSET;
}
length -= readPosition;
absoluteStartIndex += readPosition;
relativeStartIndex += readPosition;
if (relativeStartIndex >= capacity) {
relativeStartIndex -= capacity;
}
readPosition = 0;
return discardSamples(readPosition);
}
/**
* Discards all samples in the queue. The read position is also advanced.
*
* @return The corresponding offset up to which data should be discarded, or
* {@link C#POSITION_UNSET} if no discarding of data is necessary.
*/
public synchronized long discardToEnd() {
if (length == 0) {
int relativeLastDiscardedIndex = (relativeStartIndex - 1 + capacity) % capacity;
return offsets[relativeLastDiscardedIndex] + sizes[relativeLastDiscardedIndex];
} else {
return offsets[relativeStartIndex];
return C.POSITION_UNSET;
}
return discardSamples(length);
}
// Called by the loading thread.
......@@ -434,4 +439,59 @@ import com.google.android.exoplayer2.util.Util;
return true;
}
// Internal methods.
/**
* Finds the sample in the specified range that's before or at the specified time. If
* {@code keyframe} is {@code true} then the sample is additionally required to be a keyframe.
*
* @param relativeStartIndex The relative index from which to start searching.
* @param length The length of the range being searched.
* @param timeUs The specified time.
* @param keyframe Whether only keyframes should be considered.
* @return The offset from {@code relativeStartIndex} to the found sample, or -1 if no matching
* sample was found.
*/
private int findSampleBefore(int relativeStartIndex, int length, long timeUs, boolean keyframe) {
// This could be optimized to use a binary search, however in practice callers to this method
// normally pass times near to the start of the search region. Hence it's unclear whether
// switching to a binary search would yield any real benefit.
int sampleCountToTarget = -1;
int searchIndex = relativeStartIndex;
for (int i = 0; i < length && timesUs[searchIndex] <= timeUs; i++) {
if (!keyframe || (flags[searchIndex] & C.BUFFER_FLAG_KEY_FRAME) != 0) {
// We've found a suitable sample.
sampleCountToTarget = i;
}
searchIndex = (searchIndex + 1) % capacity;
}
return sampleCountToTarget;
}
/**
* Discards the specified number of samples.
*
* @param discardCount The number of samples to discard.
* @return The corresponding offset up to which data should be discarded, or
* {@link C#POSITION_UNSET} if no discarding of data is necessary.
*/
private long discardSamples(int discardCount) {
length -= discardCount;
absoluteStartIndex += discardCount;
relativeStartIndex += discardCount;
if (relativeStartIndex >= capacity) {
relativeStartIndex -= capacity;
}
readPosition -= discardCount;
if (readPosition < 0) {
readPosition = 0;
}
if (length == 0) {
int relativeLastDiscardedIndex = (relativeStartIndex - 1 + capacity) % capacity;
return offsets[relativeLastDiscardedIndex] + sizes[relativeLastDiscardedIndex];
} else {
return offsets[relativeStartIndex];
}
}
}
......@@ -232,65 +232,84 @@ public final class SampleQueue implements TrackOutput {
}
/**
* Discards samples up to the current read position.
* Discards up to but not including the sample immediately before or at the specified time.
*
* @param timeUs The time to discard to.
* @param toKeyframe If true then discards samples up to the keyframe before or at the specified
* time, rather than any sample before or at that time.
* @param stopAtReadPosition If true then samples are only discarded if they're before the
* read position. If false then samples at and beyond the read position may be discarded, in
* which case the read position is advanced to the first remaining sample.
*/
public void discardTo(long timeUs, boolean toKeyframe, boolean stopAtReadPosition) {
discardDownstreamTo(metadataQueue.discardTo(timeUs, toKeyframe, stopAtReadPosition));
}
/**
* Discards up to but not including the read position.
*/
public void discardToRead() {
long nextOffset = metadataQueue.discardToRead();
if (nextOffset != C.POSITION_UNSET) {
dropDownstreamTo(nextOffset);
}
discardDownstreamTo(metadataQueue.discardToRead());
}
/**
* Discards to the end of the queue. The read position is also advanced.
*/
public void discardToEnd() {
discardDownstreamTo(metadataQueue.discardToEnd());
}
/**
* @deprecated Use {@link #skipAll2()} followed by {@link #discardToRead()}.
* @deprecated Use {@link #advanceToEnd()} followed by {@link #discardToRead()}.
*/
@Deprecated
public void skipAll() {
skipAll2();
advanceToEnd();
discardToRead();
}
/**
* Skips all samples currently in the buffer.
* Advances the read position to the end of the queue.
*/
public void skipAll2() {
metadataQueue.skipAll();
public void advanceToEnd() {
metadataQueue.advanceToEnd();
}
/**
* @deprecated Use {@link #skipToKeyframeBefore2(long, boolean)} followed by
* @deprecated Use {@link #advanceTo(long, boolean, boolean)} followed by
* {@link #discardToRead()}.
*/
@Deprecated
public boolean skipToKeyframeBefore(long timeUs, boolean allowTimeBeyondBuffer) {
boolean success = skipToKeyframeBefore2(timeUs, allowTimeBeyondBuffer);
boolean success = advanceTo(timeUs, true, allowTimeBeyondBuffer);
discardToRead();
return success;
}
/**
* Attempts to skip to the keyframe before or at the specified time. Succeeds only if the buffer
* contains a keyframe with a timestamp of {@code timeUs} or earlier. If
* {@code allowTimeBeyondBuffer} is {@code false} then it is also required that {@code timeUs}
* falls within the buffer.
* Attempts to advance the read position to the sample before or at the specified time.
*
* @param timeUs The seek time.
* @param allowTimeBeyondBuffer Whether the skip can succeed if {@code timeUs} is beyond the end
* of the buffer.
* @return Whether the skip was successful.
* @param timeUs The time to advance to.
* @param toKeyframe If true then attempts to advance to the keyframe before or at the specified
* 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 buffer, by advancing the read position to the last sample (or keyframe) in the
* buffer.
* @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.
*/
public boolean skipToKeyframeBefore2(long timeUs, boolean allowTimeBeyondBuffer) {
return metadataQueue.skipToKeyframeBefore(timeUs, allowTimeBeyondBuffer);
public boolean advanceTo(long timeUs, boolean toKeyframe, boolean allowTimeBeyondBuffer) {
return metadataQueue.advanceTo(timeUs, toKeyframe, allowTimeBeyondBuffer);
}
/**
* @deprecated Use {@link #readData2(FormatHolder, DecoderInputBuffer, boolean, boolean, long)}
* @deprecated Use {@link #read(FormatHolder, DecoderInputBuffer, boolean, boolean, long)}
* followed by {@link #discardToRead()}.
*/
@Deprecated
public int readData(FormatHolder formatHolder, DecoderInputBuffer buffer, boolean formatRequired,
boolean loadingFinished, long decodeOnlyUntilUs) {
int result = readData2(formatHolder, buffer, formatRequired, loadingFinished,
int result = read(formatHolder, buffer, formatRequired, loadingFinished,
decodeOnlyUntilUs);
discardToRead();
return result;
......@@ -312,9 +331,9 @@ public final class SampleQueue implements TrackOutput {
* @return The result, which can be {@link C#RESULT_NOTHING_READ}, {@link C#RESULT_FORMAT_READ} or
* {@link C#RESULT_BUFFER_READ}.
*/
public int readData2(FormatHolder formatHolder, DecoderInputBuffer buffer, boolean formatRequired,
public int read(FormatHolder formatHolder, DecoderInputBuffer buffer, boolean formatRequired,
boolean loadingFinished, long decodeOnlyUntilUs) {
int result = metadataQueue.readData(formatHolder, buffer, formatRequired, loadingFinished,
int result = metadataQueue.read(formatHolder, buffer, formatRequired, loadingFinished,
downstreamFormat, extrasHolder);
switch (result) {
case C.RESULT_FORMAT_READ:
......@@ -472,17 +491,27 @@ public final class SampleQueue implements TrackOutput {
}
/**
* Advances {@link #firstAllocationNode} to the specified absolute position. Nodes that are
* advanced over are cleared, and their underlying allocations are returned to the allocator.
* Advances {@link #firstAllocationNode} to the specified absolute position.
* {@link #readAllocationNode} is also advanced if necessary to avoid it falling behind
* {@link #firstAllocationNode}. Nodes that have been advanced past are cleared, and their
* underlying allocations are returned to the allocator.
*
* @param absolutePosition The position to which {@link #firstAllocationNode} should be advanced.
* Must never exceed the absolute position of the next sample to be read.
* May be {@link C#POSITION_UNSET}, in which case calling this method is a no-op.
*/
private void dropDownstreamTo(long absolutePosition) {
private void discardDownstreamTo(long absolutePosition) {
if (absolutePosition == C.POSITION_UNSET) {
return;
}
while (absolutePosition >= firstAllocationNode.endPosition) {
allocator.release(firstAllocationNode.allocation);
firstAllocationNode = firstAllocationNode.clear();
}
// If we discarded the node referenced by readAllocationNode then we need to advance it to the
// first remaining node.
if (readAllocationNode.startPosition < firstAllocationNode.startPosition) {
readAllocationNode = firstAllocationNode;
}
}
// Called by the loading thread.
......@@ -742,13 +771,15 @@ public final class SampleQueue implements TrackOutput {
}
/**
* Clears {@link #allocation}.
* Clears {@link #allocation} and {@link #next}.
*
* @return The next {@link AllocationNode}, for convenience.
* @return The cleared next {@link AllocationNode}.
*/
public AllocationNode clear() {
allocation = null;
return next;
AllocationNode temp = next;
next = null;
return temp;
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment