From 1d2cf6f270945c06c4590c9e2c486faf9ca7966b Mon Sep 17 00:00:00 2001 From: tonihei Date: Fri, 20 Nov 2020 10:24:22 +0000 Subject: [PATCH] Fix bug in SampleQueue.discardTo for duplicate timestamps. When a stream has duplicate timestamps we currently discard to the last sample with the specified discardTo timestamp, but it should be the first one to adhere to the method doc and the intended usage. #minor-release PiperOrigin-RevId: 343458870 --- .../exoplayer2/source/SampleQueue.java | 5 ++ .../exoplayer2/source/SampleQueueTest.java | 47 +++++++++++++++++++ 2 files changed, 52 insertions(+) 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 20d9f44562..824b8dedaf 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 @@ -894,6 +894,11 @@ public class SampleQueue implements TrackOutput { if (!keyframe || (flags[searchIndex] & C.BUFFER_FLAG_KEY_FRAME) != 0) { // We've found a suitable sample. sampleCountToTarget = i; + if (timesUs[searchIndex] == timeUs) { + // Stop the search if we found a sample at the specified time to avoid returning a later + // sample with the same exactly matching timestamp. + break; + } } searchIndex++; if (searchIndex == capacity) { 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 241834fab5..11a2204f81 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 @@ -861,6 +861,53 @@ public final class SampleQueueTest { assertAllocationCount(1); } + @Test + public void discardTo_withDuplicateTimestamps_discardsOnlyToFirstMatch() { + writeTestData( + DATA, + SAMPLE_SIZES, + SAMPLE_OFFSETS, + /* sampleTimestamps= */ new long[] {0, 1000, 1000, 1000, 2000, 2000, 2000, 2000}, + SAMPLE_FORMATS, + /* sampleFlags= */ new int[] { + BUFFER_FLAG_KEY_FRAME, + 0, + BUFFER_FLAG_KEY_FRAME, + BUFFER_FLAG_KEY_FRAME, + 0, + 0, + BUFFER_FLAG_KEY_FRAME, + BUFFER_FLAG_KEY_FRAME + }); + + // Discard to first keyframe exactly matching the specified time. + sampleQueue.discardTo( + /* timeUs= */ 1000, /* toKeyframe= */ true, /* stopAtReadPosition= */ false); + assertThat(sampleQueue.getFirstIndex()).isEqualTo(2); + + // Do nothing when trying again. + sampleQueue.discardTo( + /* timeUs= */ 1000, /* toKeyframe= */ true, /* stopAtReadPosition= */ false); + sampleQueue.discardTo( + /* timeUs= */ 1000, /* toKeyframe= */ false, /* stopAtReadPosition= */ false); + assertThat(sampleQueue.getFirstIndex()).isEqualTo(2); + + // Discard to first frame exactly matching the specified time. + sampleQueue.discardTo( + /* timeUs= */ 2000, /* toKeyframe= */ false, /* stopAtReadPosition= */ false); + assertThat(sampleQueue.getFirstIndex()).isEqualTo(4); + + // Do nothing when trying again. + sampleQueue.discardTo( + /* timeUs= */ 2000, /* toKeyframe= */ false, /* stopAtReadPosition= */ false); + assertThat(sampleQueue.getFirstIndex()).isEqualTo(4); + + // Discard to first keyframe at same timestamp. + sampleQueue.discardTo( + /* timeUs= */ 2000, /* toKeyframe= */ true, /* stopAtReadPosition= */ false); + assertThat(sampleQueue.getFirstIndex()).isEqualTo(6); + } + @Test public void discardToDontStopAtReadPosition() { writeTestData();