From 6c3c71b5545f74ff6541491fb296d22d4c832b20 Mon Sep 17 00:00:00 2001 From: olly Date: Tue, 11 Sep 2018 04:54:00 -0700 Subject: [PATCH] Fix SampleQueue splicing when sampleOffsetUs != 0 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=212432661 --- .../exoplayer2/source/SampleQueue.java | 2 +- .../exoplayer2/source/SampleQueueTest.java | 305 ++++++++++++------ 2 files changed, 200 insertions(+), 107 deletions(-) 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 c378a8f9a2..d6f18665f4 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 @@ -577,13 +577,13 @@ public final class SampleQueue implements TrackOutput { if (pendingFormatAdjustment) { format(lastUnadjustedFormat); } + timeUs += sampleOffsetUs; if (pendingSplice) { if ((flags & C.BUFFER_FLAG_KEY_FRAME) == 0 || !metadataQueue.attemptSplice(timeUs)) { return; } pendingSplice = false; } - timeUs += sampleOffsetUs; long absoluteOffset = totalBytesWritten - size - offset; metadataQueue.commitSample(timeUs, flags, absoluteOffset, size, cryptoData); } diff --git a/library/core/src/test/java/com/google/android/exoplayer2/source/SampleQueueTest.java b/library/core/src/test/java/com/google/android/exoplayer2/source/SampleQueueTest.java index 4d6b6dd72d..3d78ecfb4c 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/source/SampleQueueTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/source/SampleQueueTest.java @@ -23,6 +23,7 @@ import static com.google.common.truth.Truth.assertThat; import static java.lang.Long.MIN_VALUE; import static java.util.Arrays.copyOfRange; +import android.support.annotation.Nullable; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.FormatHolder; @@ -45,41 +46,51 @@ public final class SampleQueueTest { private static final int ALLOCATION_SIZE = 16; - private static final Format TEST_FORMAT_1 = Format.createSampleFormat("1", "mimeType", 0); - private static final Format TEST_FORMAT_2 = Format.createSampleFormat("2", "mimeType", 0); - private static final Format TEST_FORMAT_1_COPY = Format.createSampleFormat("1", "mimeType", 0); - private static final byte[] TEST_DATA = TestUtil.buildTestData(ALLOCATION_SIZE * 10); + private static final Format FORMAT_1 = Format.createSampleFormat("1", "mimeType", 0); + private static final Format FORMAT_2 = Format.createSampleFormat("2", "mimeType", 0); + private static final Format FORMAT_1_COPY = Format.createSampleFormat("1", "mimeType", 0); + private static final Format FORMAT_SPLICED = Format.createSampleFormat("spliced", "mimeType", 0); + private static final byte[] DATA = TestUtil.buildTestData(ALLOCATION_SIZE * 10); /* - * TEST_SAMPLE_SIZES and TEST_SAMPLE_OFFSETS are intended to test various boundary cases (with - * respect to the allocation size). TEST_SAMPLE_OFFSETS values are defined as the backward offsets - * (as expected by SampleQueue.sampleMetadata) assuming that TEST_DATA has been written to the + * SAMPLE_SIZES and SAMPLE_OFFSETS are intended to test various boundary cases (with + * respect to the allocation size). SAMPLE_OFFSETS values are defined as the backward offsets + * (as expected by SampleQueue.sampleMetadata) assuming that DATA has been written to the * sampleQueue in full. The allocations are filled as follows, where | indicates a boundary * between allocations and x indicates a byte that doesn't belong to a sample: * * x|xx|x|x|||xx| */ - private static final int[] TEST_SAMPLE_SIZES = new int[] { - ALLOCATION_SIZE - 1, ALLOCATION_SIZE - 2, ALLOCATION_SIZE - 1, ALLOCATION_SIZE - 1, - ALLOCATION_SIZE, ALLOCATION_SIZE * 2, ALLOCATION_SIZE * 2 - 2, ALLOCATION_SIZE - }; - private static final int[] TEST_SAMPLE_OFFSETS = new int[] { - 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 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 - }; - private static final Format[] TEST_SAMPLE_FORMATS = new Format[] { - TEST_FORMAT_1, TEST_FORMAT_1, TEST_FORMAT_1, TEST_FORMAT_1, TEST_FORMAT_2, TEST_FORMAT_2, - TEST_FORMAT_2, TEST_FORMAT_2 - }; - private static final int TEST_DATA_SECOND_KEYFRAME_INDEX = 4; + private static final int[] SAMPLE_SIZES = + new int[] { + ALLOCATION_SIZE - 1, + ALLOCATION_SIZE - 2, + ALLOCATION_SIZE - 1, + ALLOCATION_SIZE - 1, + ALLOCATION_SIZE, + ALLOCATION_SIZE * 2, + ALLOCATION_SIZE * 2 - 2, + ALLOCATION_SIZE + }; + private static final int[] SAMPLE_OFFSETS = + new int[] { + 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 long[] SAMPLE_TIMESTAMPS = + new long[] {0, 1000, 2000, 3000, 4000, 5000, 6000, 7000}; + private static final long LAST_SAMPLE_TIMESTAMP = SAMPLE_TIMESTAMPS[SAMPLE_TIMESTAMPS.length - 1]; + private static final int[] SAMPLE_FLAGS = + new int[] {C.BUFFER_FLAG_KEY_FRAME, 0, 0, 0, C.BUFFER_FLAG_KEY_FRAME, 0, 0, 0}; + private static final Format[] SAMPLE_FORMATS = + new Format[] {FORMAT_1, FORMAT_1, FORMAT_1, FORMAT_1, FORMAT_2, FORMAT_2, FORMAT_2, FORMAT_2}; + private static final int DATA_SECOND_KEYFRAME_INDEX = 4; private Allocator allocator; private SampleQueue sampleQueue; @@ -117,37 +128,37 @@ public final class SampleQueueTest { @Test public void testReadFormatDeduplicated() { - sampleQueue.format(TEST_FORMAT_1); - assertReadFormat(false, TEST_FORMAT_1); + sampleQueue.format(FORMAT_1); + assertReadFormat(false, FORMAT_1); // If the same format is input then it should be de-duplicated (i.e. not output again). - sampleQueue.format(TEST_FORMAT_1); - assertNoSamplesToRead(TEST_FORMAT_1); + sampleQueue.format(FORMAT_1); + assertNoSamplesToRead(FORMAT_1); // The same applies for a format that's equal (but a different object). - sampleQueue.format(TEST_FORMAT_1_COPY); - assertNoSamplesToRead(TEST_FORMAT_1); + sampleQueue.format(FORMAT_1_COPY); + assertNoSamplesToRead(FORMAT_1); } @Test public void testReadSingleSamples() { - sampleQueue.sampleData(new ParsableByteArray(TEST_DATA), ALLOCATION_SIZE); + sampleQueue.sampleData(new ParsableByteArray(DATA), ALLOCATION_SIZE); assertAllocationCount(1); // Nothing to read. assertNoSamplesToRead(null); - sampleQueue.format(TEST_FORMAT_1); + sampleQueue.format(FORMAT_1); // Read the format. - assertReadFormat(false, TEST_FORMAT_1); + assertReadFormat(false, FORMAT_1); // Nothing to read. - assertNoSamplesToRead(TEST_FORMAT_1); + assertNoSamplesToRead(FORMAT_1); sampleQueue.sampleMetadata(1000, C.BUFFER_FLAG_KEY_FRAME, ALLOCATION_SIZE, 0, null); // If formatRequired, should read the format rather than the sample. - assertReadFormat(true, TEST_FORMAT_1); + assertReadFormat(true, FORMAT_1); // Otherwise should read the sample. - assertSampleRead(1000, true, TEST_DATA, 0, ALLOCATION_SIZE); + assertReadSample(1000, true, DATA, 0, ALLOCATION_SIZE); // Allocation should still be held. assertAllocationCount(1); sampleQueue.discardToRead(); @@ -155,16 +166,16 @@ public final class SampleQueueTest { assertAllocationCount(0); // Nothing to read. - assertNoSamplesToRead(TEST_FORMAT_1); + assertNoSamplesToRead(FORMAT_1); // Write a second sample followed by one byte that does not belong to it. - sampleQueue.sampleData(new ParsableByteArray(TEST_DATA), ALLOCATION_SIZE); + sampleQueue.sampleData(new ParsableByteArray(DATA), ALLOCATION_SIZE); sampleQueue.sampleMetadata(2000, 0, ALLOCATION_SIZE - 1, 1, null); // If formatRequired, should read the format rather than the sample. - assertReadFormat(true, TEST_FORMAT_1); + assertReadFormat(true, FORMAT_1); // Read the sample. - assertSampleRead(2000, false, TEST_DATA, 0, ALLOCATION_SIZE - 1); + assertReadSample(2000, false, DATA, 0, ALLOCATION_SIZE - 1); // Allocation should still be held. assertAllocationCount(1); sampleQueue.discardToRead(); @@ -176,9 +187,9 @@ public final class SampleQueueTest { sampleQueue.sampleMetadata(3000, 0, 1, 0, null); // If formatRequired, should read the format rather than the sample. - assertReadFormat(true, TEST_FORMAT_1); + assertReadFormat(true, FORMAT_1); // Read the sample. - assertSampleRead(3000, false, TEST_DATA, ALLOCATION_SIZE - 1, 1); + assertReadSample(3000, false, DATA, ALLOCATION_SIZE - 1, 1); // Allocation should still be held. assertAllocationCount(1); sampleQueue.discardToRead(); @@ -202,8 +213,8 @@ public final class SampleQueueTest { writeTestData(); writeTestData(); assertAllocationCount(20); - assertReadTestData(TEST_FORMAT_2); - assertReadTestData(TEST_FORMAT_2); + assertReadTestData(FORMAT_2); + assertReadTestData(FORMAT_2); assertAllocationCount(20); sampleQueue.discardToRead(); assertAllocationCount(0); @@ -251,15 +262,15 @@ public final class SampleQueueTest { assertAllocationCount(0); // Despite skipping all samples, we should still read the last format, since this is the // expected format for a subsequent sample. - assertReadFormat(false, TEST_FORMAT_2); + assertReadFormat(false, FORMAT_2); // Once the format has been read, there's nothing else to read. - assertNoSamplesToRead(TEST_FORMAT_2); + assertNoSamplesToRead(FORMAT_2); } @Test public void testAdvanceToEndRetainsUnassignedData() { - sampleQueue.format(TEST_FORMAT_1); - sampleQueue.sampleData(new ParsableByteArray(TEST_DATA), ALLOCATION_SIZE); + sampleQueue.format(FORMAT_1); + sampleQueue.sampleData(new ParsableByteArray(DATA), ALLOCATION_SIZE); sampleQueue.advanceToEnd(); assertAllocationCount(1); sampleQueue.discardToRead(); @@ -267,14 +278,14 @@ public final class SampleQueueTest { // written. assertAllocationCount(1); // We should be able to read the format. - assertReadFormat(false, TEST_FORMAT_1); + assertReadFormat(false, FORMAT_1); // Once the format has been read, there's nothing else to read. - assertNoSamplesToRead(TEST_FORMAT_1); + assertNoSamplesToRead(FORMAT_1); sampleQueue.sampleMetadata(0, C.BUFFER_FLAG_KEY_FRAME, ALLOCATION_SIZE, 0, null); // Once the metadata has been written, check the sample can be read as expected. - assertSampleRead(0, true, TEST_DATA, 0, ALLOCATION_SIZE); - assertNoSamplesToRead(TEST_FORMAT_1); + assertReadSample(0, true, DATA, 0, ALLOCATION_SIZE); + assertNoSamplesToRead(FORMAT_1); assertAllocationCount(1); sampleQueue.discardToRead(); assertAllocationCount(0); @@ -283,21 +294,21 @@ public final class SampleQueueTest { @Test public void testAdvanceToBeforeBuffer() { writeTestData(); - int skipCount = sampleQueue.advanceTo(TEST_SAMPLE_TIMESTAMPS[0] - 1, true, false); + int skipCount = sampleQueue.advanceTo(SAMPLE_TIMESTAMPS[0] - 1, true, false); // Should fail and have no effect. assertThat(skipCount).isEqualTo(ADVANCE_FAILED); assertReadTestData(); - assertNoSamplesToRead(TEST_FORMAT_2); + assertNoSamplesToRead(FORMAT_2); } @Test public void testAdvanceToStartOfBuffer() { writeTestData(); - int skipCount = sampleQueue.advanceTo(TEST_SAMPLE_TIMESTAMPS[0], true, false); + int skipCount = sampleQueue.advanceTo(SAMPLE_TIMESTAMPS[0], true, false); // Should succeed but have no effect (we're already at the first frame). assertThat(skipCount).isEqualTo(0); assertReadTestData(); - assertNoSamplesToRead(TEST_FORMAT_2); + assertNoSamplesToRead(FORMAT_2); } @Test @@ -306,8 +317,8 @@ public final class SampleQueueTest { int skipCount = sampleQueue.advanceTo(LAST_SAMPLE_TIMESTAMP, true, false); // Should succeed and skip to 2nd keyframe (the 4th frame). assertThat(skipCount).isEqualTo(4); - assertReadTestData(null, TEST_DATA_SECOND_KEYFRAME_INDEX); - assertNoSamplesToRead(TEST_FORMAT_2); + assertReadTestData(null, DATA_SECOND_KEYFRAME_INDEX); + assertNoSamplesToRead(FORMAT_2); } @Test @@ -317,7 +328,7 @@ public final class SampleQueueTest { // Should fail and have no effect. assertThat(skipCount).isEqualTo(ADVANCE_FAILED); assertReadTestData(); - assertNoSamplesToRead(TEST_FORMAT_2); + assertNoSamplesToRead(FORMAT_2); } @Test @@ -326,8 +337,8 @@ public final class SampleQueueTest { int skipCount = sampleQueue.advanceTo(LAST_SAMPLE_TIMESTAMP + 1, true, true); // Should succeed and skip to 2nd keyframe (the 4th frame). assertThat(skipCount).isEqualTo(4); - assertReadTestData(null, TEST_DATA_SECOND_KEYFRAME_INDEX); - assertNoSamplesToRead(TEST_FORMAT_2); + assertReadTestData(null, DATA_SECOND_KEYFRAME_INDEX); + assertNoSamplesToRead(FORMAT_2); } @Test @@ -339,10 +350,10 @@ public final class SampleQueueTest { assertThat(sampleQueue.getReadIndex()).isEqualTo(8); assertAllocationCount(0); // We should still be able to read the upstream format. - assertReadFormat(false, TEST_FORMAT_2); + assertReadFormat(false, FORMAT_2); // We should be able to write and read subsequent samples. writeTestData(); - assertReadTestData(TEST_FORMAT_2); + assertReadTestData(FORMAT_2); } @Test @@ -356,12 +367,12 @@ public final class SampleQueueTest { // Read the first sample. assertReadTestData(null, 0, 1); // Shouldn't discard anything. - sampleQueue.discardTo(TEST_SAMPLE_TIMESTAMPS[1] - 1, false, true); + sampleQueue.discardTo(SAMPLE_TIMESTAMPS[1] - 1, false, true); assertThat(sampleQueue.getFirstIndex()).isEqualTo(0); assertThat(sampleQueue.getReadIndex()).isEqualTo(1); assertAllocationCount(10); // Should discard the read sample. - sampleQueue.discardTo(TEST_SAMPLE_TIMESTAMPS[1], false, true); + sampleQueue.discardTo(SAMPLE_TIMESTAMPS[1], false, true); assertThat(sampleQueue.getFirstIndex()).isEqualTo(1); assertThat(sampleQueue.getReadIndex()).isEqualTo(1); assertAllocationCount(9); @@ -371,7 +382,7 @@ public final class SampleQueueTest { assertThat(sampleQueue.getReadIndex()).isEqualTo(1); assertAllocationCount(9); // Should be able to read the remaining samples. - assertReadTestData(TEST_FORMAT_1, 1, 7); + assertReadTestData(FORMAT_1, 1, 7); assertThat(sampleQueue.getFirstIndex()).isEqualTo(1); assertThat(sampleQueue.getReadIndex()).isEqualTo(8); // Should discard up to the second last sample @@ -390,17 +401,17 @@ public final class SampleQueueTest { public void testDiscardToDontStopAtReadPosition() { writeTestData(); // Shouldn't discard anything. - sampleQueue.discardTo(TEST_SAMPLE_TIMESTAMPS[1] - 1, false, false); + sampleQueue.discardTo(SAMPLE_TIMESTAMPS[1] - 1, false, false); assertThat(sampleQueue.getFirstIndex()).isEqualTo(0); assertThat(sampleQueue.getReadIndex()).isEqualTo(0); assertAllocationCount(10); // Should discard the first sample. - sampleQueue.discardTo(TEST_SAMPLE_TIMESTAMPS[1], false, false); + sampleQueue.discardTo(SAMPLE_TIMESTAMPS[1], false, false); assertThat(sampleQueue.getFirstIndex()).isEqualTo(1); assertThat(sampleQueue.getReadIndex()).isEqualTo(1); assertAllocationCount(9); // Should be able to read the remaining samples. - assertReadTestData(TEST_FORMAT_1, 1, 7); + assertReadTestData(FORMAT_1, 1, 7); } @Test @@ -424,8 +435,8 @@ public final class SampleQueueTest { assertAllocationCount(1); sampleQueue.discardUpstreamSamples(0); assertAllocationCount(0); - assertReadFormat(false, TEST_FORMAT_2); - assertNoSamplesToRead(TEST_FORMAT_2); + assertReadFormat(false, FORMAT_2); + assertNoSamplesToRead(FORMAT_2); } @Test @@ -435,8 +446,8 @@ public final class SampleQueueTest { assertAllocationCount(4); sampleQueue.discardUpstreamSamples(0); assertAllocationCount(0); - assertReadFormat(false, TEST_FORMAT_2); - assertNoSamplesToRead(TEST_FORMAT_2); + assertReadFormat(false, FORMAT_2); + assertNoSamplesToRead(FORMAT_2); } @Test @@ -445,8 +456,8 @@ public final class SampleQueueTest { sampleQueue.discardUpstreamSamples(4); assertAllocationCount(4); assertReadTestData(null, 0, 4); - assertReadFormat(false, TEST_FORMAT_2); - assertNoSamplesToRead(TEST_FORMAT_2); + assertReadFormat(false, FORMAT_2); + assertNoSamplesToRead(FORMAT_2); } @Test @@ -467,18 +478,18 @@ public final class SampleQueueTest { assertAllocationCount(1); sampleQueue.discardUpstreamSamples(3); assertAllocationCount(0); - assertReadFormat(false, TEST_FORMAT_2); - assertNoSamplesToRead(TEST_FORMAT_2); + assertReadFormat(false, FORMAT_2); + assertNoSamplesToRead(FORMAT_2); } @Test public void testLargestQueuedTimestampWithDiscardUpstream() { writeTestData(); assertThat(sampleQueue.getLargestQueuedTimestampUs()).isEqualTo(LAST_SAMPLE_TIMESTAMP); - sampleQueue.discardUpstreamSamples(TEST_SAMPLE_TIMESTAMPS.length - 1); + sampleQueue.discardUpstreamSamples(SAMPLE_TIMESTAMPS.length - 1); // Discarding from upstream should reduce the largest timestamp. assertThat(sampleQueue.getLargestQueuedTimestampUs()) - .isEqualTo(TEST_SAMPLE_TIMESTAMPS[TEST_SAMPLE_TIMESTAMPS.length - 2]); + .isEqualTo(SAMPLE_TIMESTAMPS[SAMPLE_TIMESTAMPS.length - 2]); sampleQueue.discardUpstreamSamples(0); // Discarding everything from upstream without reading should unset the largest timestamp. assertThat(sampleQueue.getLargestQueuedTimestampUs()).isEqualTo(MIN_VALUE); @@ -487,14 +498,14 @@ public final class SampleQueueTest { @Test 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); + writeTestData( + DATA, SAMPLE_SIZES, SAMPLE_OFFSETS, decodeOrderTimestamps, SAMPLE_FORMATS, SAMPLE_FLAGS); assertThat(sampleQueue.getLargestQueuedTimestampUs()).isEqualTo(7000); - sampleQueue.discardUpstreamSamples(TEST_SAMPLE_TIMESTAMPS.length - 2); + sampleQueue.discardUpstreamSamples(SAMPLE_TIMESTAMPS.length - 2); // Discarding the last two samples should not change the largest timestamp, due to the decode // ordering of the timestamps. assertThat(sampleQueue.getLargestQueuedTimestampUs()).isEqualTo(7000); - sampleQueue.discardUpstreamSamples(TEST_SAMPLE_TIMESTAMPS.length - 3); + sampleQueue.discardUpstreamSamples(SAMPLE_TIMESTAMPS.length - 3); // Once a third sample is discarded, the largest timestamp should have changed. assertThat(sampleQueue.getLargestQueuedTimestampUs()).isEqualTo(4000); sampleQueue.discardUpstreamSamples(0); @@ -511,21 +522,77 @@ public final class SampleQueueTest { assertThat(sampleQueue.getLargestQueuedTimestampUs()).isEqualTo(LAST_SAMPLE_TIMESTAMP); } + @Test + public void testSetSampleOffset() { + long sampleOffsetUs = 1000; + sampleQueue.setSampleOffsetUs(sampleOffsetUs); + writeTestData(); + assertReadTestData(null, 0, 8, sampleOffsetUs); + assertReadEndOfStream(false); + } + + @Test + public void testSplice() { + writeTestData(); + sampleQueue.splice(); + // Splice should succeed, replacing the last 4 samples with the sample being written. + long spliceSampleTimeUs = SAMPLE_TIMESTAMPS[4]; + writeSample(DATA, spliceSampleTimeUs, FORMAT_SPLICED, C.BUFFER_FLAG_KEY_FRAME); + assertReadTestData(null, 0, 4); + assertReadFormat(false, FORMAT_SPLICED); + assertReadSample(spliceSampleTimeUs, true, DATA, 0, DATA.length); + assertReadEndOfStream(false); + } + + @Test + public void testSpliceAfterRead() { + writeTestData(); + assertReadTestData(null, 0, 4); + sampleQueue.splice(); + // Splice should fail, leaving the last 4 samples unchanged. + long spliceSampleTimeUs = SAMPLE_TIMESTAMPS[3]; + writeSample(DATA, spliceSampleTimeUs, FORMAT_SPLICED, C.BUFFER_FLAG_KEY_FRAME); + assertReadTestData(SAMPLE_FORMATS[3], 4, 4); + assertReadEndOfStream(false); + + sampleQueue.rewind(); + assertReadTestData(null, 0, 4); + sampleQueue.splice(); + // Splice should succeed, replacing the last 4 samples with the sample being written + spliceSampleTimeUs = SAMPLE_TIMESTAMPS[3] + 1; + writeSample(DATA, spliceSampleTimeUs, FORMAT_SPLICED, C.BUFFER_FLAG_KEY_FRAME); + assertReadFormat(false, FORMAT_SPLICED); + assertReadSample(spliceSampleTimeUs, true, DATA, 0, DATA.length); + assertReadEndOfStream(false); + } + + @Test + public void testSpliceWithSampleOffset() { + long sampleOffsetUs = 30000; + sampleQueue.setSampleOffsetUs(sampleOffsetUs); + writeTestData(); + sampleQueue.splice(); + // Splice should succeed, replacing the last 4 samples with the sample being written. + long spliceSampleTimeUs = SAMPLE_TIMESTAMPS[4]; + writeSample(DATA, spliceSampleTimeUs, FORMAT_SPLICED, C.BUFFER_FLAG_KEY_FRAME); + assertReadTestData(null, 0, 4, sampleOffsetUs); + assertReadFormat(false, FORMAT_SPLICED.copyWithSubsampleOffsetUs(sampleOffsetUs)); + assertReadSample(spliceSampleTimeUs + sampleOffsetUs, true, DATA, 0, DATA.length); + assertReadEndOfStream(false); + } + // Internal methods. /** * Writes standard test data to {@code sampleQueue}. */ - @SuppressWarnings("ReferenceEquality") private void writeTestData() { - writeTestData(TEST_DATA, TEST_SAMPLE_SIZES, TEST_SAMPLE_OFFSETS, TEST_SAMPLE_TIMESTAMPS, - TEST_SAMPLE_FORMATS, TEST_SAMPLE_FLAGS); + writeTestData( + DATA, SAMPLE_SIZES, SAMPLE_OFFSETS, SAMPLE_TIMESTAMPS, SAMPLE_FORMATS, SAMPLE_FLAGS); } /** * Writes the specified test data to {@code sampleQueue}. - * - * */ @SuppressWarnings("ReferenceEquality") private void writeTestData(byte[] data, int[] sampleSizes, int[] sampleOffsets, @@ -542,6 +609,13 @@ public final class SampleQueueTest { } } + /** Writes a single sample to {@code sampleQueue}. */ + private void writeSample(byte[] data, long timestampUs, Format format, int sampleFlags) { + sampleQueue.format(format); + sampleQueue.sampleData(new ParsableByteArray(data), data.length); + sampleQueue.sampleMetadata(timestampUs, sampleFlags, data.length, 0, null); + } + /** * Asserts correct reading of standard test data from {@code sampleQueue}. */ @@ -565,8 +639,7 @@ public final class SampleQueueTest { * @param firstSampleIndex The index of the first sample that's expected to be read. */ private void assertReadTestData(Format startFormat, int firstSampleIndex) { - assertReadTestData(startFormat, firstSampleIndex, - TEST_SAMPLE_TIMESTAMPS.length - firstSampleIndex); + assertReadTestData(startFormat, firstSampleIndex, SAMPLE_TIMESTAMPS.length - firstSampleIndex); } /** @@ -577,23 +650,38 @@ public final class SampleQueueTest { * @param sampleCount The number of samples to read. */ private void assertReadTestData(Format startFormat, int firstSampleIndex, int sampleCount) { - Format format = startFormat; + assertReadTestData(startFormat, firstSampleIndex, sampleCount, 0); + } + + /** + * Asserts correct reading of standard test data from {@code sampleQueue}. + * + * @param startFormat The format of the last sample previously read from {@code sampleQueue}. + * @param firstSampleIndex The index of the first sample that's expected to be read. + * @param sampleCount The number of samples to read. + * @param sampleOffsetUs The expected sample offset. + */ + private void assertReadTestData( + Format startFormat, int firstSampleIndex, int sampleCount, long sampleOffsetUs) { + Format format = adjustFormat(startFormat, sampleOffsetUs); for (int i = firstSampleIndex; i < firstSampleIndex + sampleCount; i++) { // Use equals() on the read side despite using referential equality on the write side, since // sampleQueue de-duplicates written formats using equals(). - if (!TEST_SAMPLE_FORMATS[i].equals(format)) { + Format testSampleFormat = adjustFormat(SAMPLE_FORMATS[i], sampleOffsetUs); + if (!testSampleFormat.equals(format)) { // If the format has changed, we should read it. - assertReadFormat(false, TEST_SAMPLE_FORMATS[i]); - format = TEST_SAMPLE_FORMATS[i]; + assertReadFormat(false, testSampleFormat); + format = testSampleFormat; } // If we require the format, we should always read it. - assertReadFormat(true, TEST_SAMPLE_FORMATS[i]); + assertReadFormat(true, testSampleFormat); // Assert the sample is as expected. - assertSampleRead(TEST_SAMPLE_TIMESTAMPS[i], - (TEST_SAMPLE_FLAGS[i] & C.BUFFER_FLAG_KEY_FRAME) != 0, - TEST_DATA, - TEST_DATA.length - TEST_SAMPLE_OFFSETS[i] - TEST_SAMPLE_SIZES[i], - TEST_SAMPLE_SIZES[i]); + assertReadSample( + SAMPLE_TIMESTAMPS[i] + sampleOffsetUs, + (SAMPLE_FLAGS[i] & C.BUFFER_FLAG_KEY_FRAME) != 0, + DATA, + DATA.length - SAMPLE_OFFSETS[i] - SAMPLE_SIZES[i], + SAMPLE_SIZES[i]); } } @@ -688,8 +776,8 @@ public final class SampleQueueTest { * @param offset The offset in {@code sampleData} of the expected sample data. * @param length The length of the expected sample data. */ - private void assertSampleRead(long timeUs, boolean isKeyframe, byte[] sampleData, int offset, - int length) { + private void assertReadSample( + long timeUs, boolean isKeyframe, byte[] sampleData, int offset, int length) { clearFormatHolderAndInputBuffer(); int result = sampleQueue.read(formatHolder, inputBuffer, false, false, 0); assertThat(result).isEqualTo(RESULT_BUFFER_READ); @@ -738,4 +826,9 @@ public final class SampleQueueTest { inputBuffer.clear(); } + private static Format adjustFormat(@Nullable Format format, long sampleOffsetUs) { + return format == null || sampleOffsetUs == 0 + ? format + : format.copyWithSubsampleOffsetUs(sampleOffsetUs); + } }