Commit 9bad78dc by olly Committed by Oliver Woodman

Fix SampleMetadataQueue.getLargestQueuedTimestampUs

This was broken prior to my recent changes, since
largestDequeuedTimestampUs was only being updated in readData.
It should have been updated in the skip methods. as well.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=159704945
parent 531eb15f
...@@ -421,6 +421,43 @@ public class SampleQueueTest extends TestCase { ...@@ -421,6 +421,43 @@ public class SampleQueueTest extends TestCase {
assertNoSamplesToRead(TEST_FORMAT_2); assertNoSamplesToRead(TEST_FORMAT_2);
} }
public void testLargestQueuedTimestampWithDiscardUpstream() {
writeTestData();
assertEquals(LAST_SAMPLE_TIMESTAMP, sampleQueue.getLargestQueuedTimestampUs());
sampleQueue.discardUpstreamSamples(TEST_SAMPLE_TIMESTAMPS.length - 1);
// Discarding from upstream should reduce the largest timestamp.
assertEquals(TEST_SAMPLE_TIMESTAMPS[TEST_SAMPLE_TIMESTAMPS.length - 2],
sampleQueue.getLargestQueuedTimestampUs());
sampleQueue.discardUpstreamSamples(0);
// Discarding everything from upstream without reading should unset the largest timestamp.
assertEquals(Long.MIN_VALUE, sampleQueue.getLargestQueuedTimestampUs());
}
public void testLargestQueuedTimestampWithDiscardUpstreamDecodeOrder() {
long[] decodeOrderTimestamps = new long[] {0, 3000, 2000, 1000, 4000, 7000, 6000, 5000};
writeTestData(TEST_DATA, TEST_SAMPLE_SIZES, TEST_SAMPLE_OFFSETS, decodeOrderTimestamps,
TEST_SAMPLE_FORMATS, TEST_SAMPLE_FLAGS);
assertEquals(7000, sampleQueue.getLargestQueuedTimestampUs());
sampleQueue.discardUpstreamSamples(TEST_SAMPLE_TIMESTAMPS.length - 2);
// Discarding the last two samples should not change the largest timestamp, due to the decode
// ordering of the timestamps.
assertEquals(7000, sampleQueue.getLargestQueuedTimestampUs());
sampleQueue.discardUpstreamSamples(TEST_SAMPLE_TIMESTAMPS.length - 3);
// Once a third sample is discarded, the largest timestamp should have changed.
assertEquals(4000, sampleQueue.getLargestQueuedTimestampUs());
sampleQueue.discardUpstreamSamples(0);
// Discarding everything from upstream without reading should unset the largest timestamp.
assertEquals(Long.MIN_VALUE, sampleQueue.getLargestQueuedTimestampUs());
}
public void testLargestQueuedTimestampWithRead() {
writeTestData();
assertEquals(LAST_SAMPLE_TIMESTAMP, sampleQueue.getLargestQueuedTimestampUs());
assertReadTestData();
// Reading everything should not reduce the largest timestamp.
assertEquals(LAST_SAMPLE_TIMESTAMP, sampleQueue.getLargestQueuedTimestampUs());
}
// Internal methods. // Internal methods.
/** /**
...@@ -428,15 +465,27 @@ public class SampleQueueTest extends TestCase { ...@@ -428,15 +465,27 @@ public class SampleQueueTest extends TestCase {
*/ */
@SuppressWarnings("ReferenceEquality") @SuppressWarnings("ReferenceEquality")
private void writeTestData() { private void writeTestData() {
sampleQueue.sampleData(new ParsableByteArray(TEST_DATA), TEST_DATA.length); writeTestData(TEST_DATA, TEST_SAMPLE_SIZES, TEST_SAMPLE_OFFSETS, TEST_SAMPLE_TIMESTAMPS,
TEST_SAMPLE_FORMATS, TEST_SAMPLE_FLAGS);
}
/**
* Writes the specified test data to {@code sampleQueue}.
*
*
*/
@SuppressWarnings("ReferenceEquality")
private void writeTestData(byte[] data, int[] sampleSizes, int[] sampleOffsets,
long[] sampleTimestamps, Format[] sampleFormats, int[] sampleFlags) {
sampleQueue.sampleData(new ParsableByteArray(data), data.length);
Format format = null; Format format = null;
for (int i = 0; i < TEST_SAMPLE_TIMESTAMPS.length; i++) { for (int i = 0; i < sampleTimestamps.length; i++) {
if (TEST_SAMPLE_FORMATS[i] != format) { if (sampleFormats[i] != format) {
sampleQueue.format(TEST_SAMPLE_FORMATS[i]); sampleQueue.format(sampleFormats[i]);
format = TEST_SAMPLE_FORMATS[i]; format = sampleFormats[i];
} }
sampleQueue.sampleMetadata(TEST_SAMPLE_TIMESTAMPS[i], TEST_SAMPLE_FLAGS[i], sampleQueue.sampleMetadata(sampleTimestamps[i], sampleFlags[i], sampleSizes[i],
TEST_SAMPLE_SIZES[i], TEST_SAMPLE_OFFSETS[i], null); sampleOffsets[i], null);
} }
} }
......
...@@ -55,7 +55,7 @@ import com.google.android.exoplayer2.util.Util; ...@@ -55,7 +55,7 @@ import com.google.android.exoplayer2.util.Util;
private int relativeStartIndex; private int relativeStartIndex;
private int readPosition; private int readPosition;
private long largestDequeuedTimestampUs; private long largestDiscardedTimestampUs;
private long largestQueuedTimestampUs; private long largestQueuedTimestampUs;
private boolean upstreamKeyframeRequired; private boolean upstreamKeyframeRequired;
private boolean upstreamFormatRequired; private boolean upstreamFormatRequired;
...@@ -71,7 +71,7 @@ import com.google.android.exoplayer2.util.Util; ...@@ -71,7 +71,7 @@ import com.google.android.exoplayer2.util.Util;
sizes = new int[capacity]; sizes = new int[capacity];
cryptoDatas = new CryptoData[capacity]; cryptoDatas = new CryptoData[capacity];
formats = new Format[capacity]; formats = new Format[capacity];
largestDequeuedTimestampUs = Long.MIN_VALUE; largestDiscardedTimestampUs = Long.MIN_VALUE;
largestQueuedTimestampUs = Long.MIN_VALUE; largestQueuedTimestampUs = Long.MIN_VALUE;
upstreamFormatRequired = true; upstreamFormatRequired = true;
upstreamKeyframeRequired = true; upstreamKeyframeRequired = true;
...@@ -88,7 +88,7 @@ import com.google.android.exoplayer2.util.Util; ...@@ -88,7 +88,7 @@ import com.google.android.exoplayer2.util.Util;
// Called by the consuming thread, but only when there is no loading thread. // Called by the consuming thread, but only when there is no loading thread.
public void resetLargestParsedTimestamps() { public void resetLargestParsedTimestamps() {
largestDequeuedTimestampUs = Long.MIN_VALUE; largestDiscardedTimestampUs = Long.MIN_VALUE;
largestQueuedTimestampUs = Long.MIN_VALUE; largestQueuedTimestampUs = Long.MIN_VALUE;
} }
...@@ -121,16 +121,8 @@ import com.google.android.exoplayer2.util.Util; ...@@ -121,16 +121,8 @@ import com.google.android.exoplayer2.util.Util;
length -= discardCount; length -= discardCount;
relativeEndIndex = (relativeEndIndex + capacity - discardCount) % capacity; relativeEndIndex = (relativeEndIndex + capacity - discardCount) % capacity;
// Update the largest queued timestamp, assuming that the timestamps prior to a keyframe are largestQueuedTimestampUs = Math.max(largestDiscardedTimestampUs,
// always less than the timestamp of the keyframe itself, and of subsequent frames. getLargestTimestamp(relativeStartIndex, length));
largestQueuedTimestampUs = Long.MIN_VALUE;
for (int i = length - 1; i >= 0; i--) {
int sampleIndex = (relativeStartIndex + i) % capacity;
largestQueuedTimestampUs = Math.max(largestQueuedTimestampUs, timesUs[sampleIndex]);
if ((flags[sampleIndex] & C.BUFFER_FLAG_KEY_FRAME) != 0) {
break;
}
}
return offsets[relativeEndIndex]; return offsets[relativeEndIndex];
} }
...@@ -182,7 +174,7 @@ import com.google.android.exoplayer2.util.Util; ...@@ -182,7 +174,7 @@ import com.google.android.exoplayer2.util.Util;
* samples have been queued. * samples have been queued.
*/ */
public synchronized long getLargestQueuedTimestampUs() { public synchronized long getLargestQueuedTimestampUs() {
return Math.max(largestDequeuedTimestampUs, largestQueuedTimestampUs); return largestQueuedTimestampUs;
} }
/** /**
...@@ -246,7 +238,6 @@ import com.google.android.exoplayer2.util.Util; ...@@ -246,7 +238,6 @@ import com.google.android.exoplayer2.util.Util;
extrasHolder.offset = offsets[relativeReadIndex]; extrasHolder.offset = offsets[relativeReadIndex];
extrasHolder.cryptoData = cryptoDatas[relativeReadIndex]; extrasHolder.cryptoData = cryptoDatas[relativeReadIndex];
largestDequeuedTimestampUs = Math.max(largestDequeuedTimestampUs, buffer.timeUs);
readPosition++; readPosition++;
return C.RESULT_BUFFER_READ; return C.RESULT_BUFFER_READ;
} }
...@@ -420,14 +411,16 @@ import com.google.android.exoplayer2.util.Util; ...@@ -420,14 +411,16 @@ import com.google.android.exoplayer2.util.Util;
} }
/** /**
* Attempts to discard samples from the tail of the queue to allow samples starting from the * Attempts to discard samples from the end of the queue to allow samples starting from the
* specified timestamp to be spliced in. * specified timestamp to be spliced in. Samples will not be discarded prior to the read position.
* *
* @param timeUs The timestamp at which the splice occurs. * @param timeUs The timestamp at which the splice occurs.
* @return Whether the splice was successful. * @return Whether the splice was successful.
*/ */
public synchronized boolean attemptSplice(long timeUs) { public synchronized boolean attemptSplice(long timeUs) {
if (largestDequeuedTimestampUs >= timeUs) { long largestReadTimestampUs = Math.max(largestDiscardedTimestampUs,
getLargestTimestamp(relativeStartIndex, readPosition));
if (largestReadTimestampUs >= timeUs) {
return false; return false;
} }
int retainCount = length; int retainCount = length;
...@@ -476,6 +469,8 @@ import com.google.android.exoplayer2.util.Util; ...@@ -476,6 +469,8 @@ 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.
*/ */
private long discardSamples(int discardCount) { private long discardSamples(int discardCount) {
largestDiscardedTimestampUs = Math.max(largestDiscardedTimestampUs,
getLargestTimestamp(relativeStartIndex, discardCount));
length -= discardCount; length -= discardCount;
absoluteStartIndex += discardCount; absoluteStartIndex += discardCount;
relativeStartIndex += discardCount; relativeStartIndex += discardCount;
...@@ -494,4 +489,24 @@ import com.google.android.exoplayer2.util.Util; ...@@ -494,4 +489,24 @@ import com.google.android.exoplayer2.util.Util;
} }
} }
/**
* Finds the largest timestamp in the specified range, assuming that the timestamps prior to a
* keyframe are always less than the timestamp of the keyframe itself, and of subsequent frames.
*
* @param relativeStartIndex The relative index from which to start searching.
* @param length The length of the range being searched.
* @return The largest timestamp, or {@link Long#MIN_VALUE} if {@code length <= 0}.
*/
private long getLargestTimestamp(int relativeStartIndex, int length) {
long largestTimestampUs = Long.MIN_VALUE;
for (int i = length - 1; i >= 0; i--) {
int sampleIndex = (relativeStartIndex + i) % capacity;
largestTimestampUs = Math.max(largestTimestampUs, timesUs[sampleIndex]);
if ((flags[sampleIndex] & C.BUFFER_FLAG_KEY_FRAME) != 0) {
break;
}
}
return largestTimestampUs;
}
} }
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