Discard samples on the write-side of SampleQueue

PiperOrigin-RevId: 319205008
This commit is contained in:
olly 2020-07-01 13:12:54 +01:00 committed by Oliver Woodman
parent f8217140d7
commit 8bd01a7bec
6 changed files with 264 additions and 142 deletions

View File

@ -480,8 +480,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
} }
maybeNotifyDownstreamFormat(sampleQueueIndex); maybeNotifyDownstreamFormat(sampleQueueIndex);
int result = int result =
sampleQueues[sampleQueueIndex].read( sampleQueues[sampleQueueIndex].read(formatHolder, buffer, formatRequired, loadingFinished);
formatHolder, buffer, formatRequired, loadingFinished, lastSeekPositionUs);
if (result == C.RESULT_NOTHING_READ) { if (result == C.RESULT_NOTHING_READ) {
maybeStartDeferredRetry(sampleQueueIndex); maybeStartDeferredRetry(sampleQueueIndex);
} }
@ -815,6 +814,9 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
loadable.setLoadPosition( loadable.setLoadPosition(
Assertions.checkNotNull(seekMap).getSeekPoints(pendingResetPositionUs).first.position, Assertions.checkNotNull(seekMap).getSeekPoints(pendingResetPositionUs).first.position,
pendingResetPositionUs); pendingResetPositionUs);
for (SampleQueue sampleQueue : sampleQueues) {
sampleQueue.setStartTimeUs(pendingResetPositionUs);
}
pendingResetPositionUs = C.TIME_UNSET; pendingResetPositionUs = C.TIME_UNSET;
} }
extractedSamplesCountAtStartOfLoad = getExtractedSamplesCount(); extractedSamplesCountAtStartOfLoad = getExtractedSamplesCount();

View File

@ -16,6 +16,7 @@
package com.google.android.exoplayer2.source; package com.google.android.exoplayer2.source;
import android.os.Looper; import android.os.Looper;
import android.util.Log;
import androidx.annotation.CallSuper; import androidx.annotation.CallSuper;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
@ -52,6 +53,7 @@ public class SampleQueue implements TrackOutput {
} }
@VisibleForTesting /* package */ static final int SAMPLE_CAPACITY_INCREMENT = 1000; @VisibleForTesting /* package */ static final int SAMPLE_CAPACITY_INCREMENT = 1000;
private static final String TAG = "SampleQueue";
private final SampleDataQueue sampleDataQueue; private final SampleDataQueue sampleDataQueue;
private final SampleExtrasHolder extrasHolder; private final SampleExtrasHolder extrasHolder;
@ -77,6 +79,7 @@ public class SampleQueue implements TrackOutput {
private int relativeFirstIndex; private int relativeFirstIndex;
private int readPosition; private int readPosition;
private long startTimeUs;
private long largestDiscardedTimestampUs; private long largestDiscardedTimestampUs;
private long largestQueuedTimestampUs; private long largestQueuedTimestampUs;
private boolean isLastSampleQueued; private boolean isLastSampleQueued;
@ -87,6 +90,8 @@ public class SampleQueue implements TrackOutput {
@Nullable private Format upstreamFormat; @Nullable private Format upstreamFormat;
@Nullable private Format upstreamCommittedFormat; @Nullable private Format upstreamCommittedFormat;
private int upstreamSourceId; private int upstreamSourceId;
private boolean upstreamAllSamplesAreSyncSamples;
private boolean loggedUnexpectedNonSyncSample;
private long sampleOffsetUs; private long sampleOffsetUs;
private boolean pendingSplice; private boolean pendingSplice;
@ -119,6 +124,7 @@ public class SampleQueue implements TrackOutput {
sizes = new int[capacity]; sizes = new int[capacity];
cryptoDatas = new CryptoData[capacity]; cryptoDatas = new CryptoData[capacity];
formats = new Format[capacity]; formats = new Format[capacity];
startTimeUs = Long.MIN_VALUE;
largestDiscardedTimestampUs = Long.MIN_VALUE; largestDiscardedTimestampUs = Long.MIN_VALUE;
largestQueuedTimestampUs = Long.MIN_VALUE; largestQueuedTimestampUs = Long.MIN_VALUE;
upstreamFormatRequired = true; upstreamFormatRequired = true;
@ -155,6 +161,7 @@ public class SampleQueue implements TrackOutput {
relativeFirstIndex = 0; relativeFirstIndex = 0;
readPosition = 0; readPosition = 0;
upstreamKeyframeRequired = true; upstreamKeyframeRequired = true;
startTimeUs = Long.MIN_VALUE;
largestDiscardedTimestampUs = Long.MIN_VALUE; largestDiscardedTimestampUs = Long.MIN_VALUE;
largestQueuedTimestampUs = Long.MIN_VALUE; largestQueuedTimestampUs = Long.MIN_VALUE;
isLastSampleQueued = false; isLastSampleQueued = false;
@ -166,6 +173,16 @@ public class SampleQueue implements TrackOutput {
} }
} }
/**
* Sets the start time for the queue. Samples with earlier timestamps will be discarded or have
* the {@link C#BUFFER_FLAG_DECODE_ONLY} flag set when read.
*
* @param startTimeUs The start time, in microseconds.
*/
public final void setStartTimeUs(long startTimeUs) {
this.startTimeUs = startTimeUs;
}
/** /**
* Sets a source identifier for subsequent samples. * Sets a source identifier for subsequent samples.
* *
@ -325,8 +342,6 @@ public class SampleQueue implements TrackOutput {
* it's not changing. A sample will never be read if set to true, however it is still possible * it's not changing. A sample will never be read if set to true, however it is still possible
* for the end of stream or nothing to be read. * for the end of stream or nothing to be read.
* @param loadingFinished True if an empty queue should be considered the end of the stream. * @param loadingFinished True if an empty queue should be considered the end of the stream.
* @param decodeOnlyUntilUs If a buffer is read, the {@link C#BUFFER_FLAG_DECODE_ONLY} flag will
* be set if the buffer's timestamp is less than this value.
* @return The result, which can be {@link C#RESULT_NOTHING_READ}, {@link C#RESULT_FORMAT_READ} or * @return The result, which can be {@link C#RESULT_NOTHING_READ}, {@link C#RESULT_FORMAT_READ} or
* {@link C#RESULT_BUFFER_READ}. * {@link C#RESULT_BUFFER_READ}.
*/ */
@ -335,11 +350,9 @@ public class SampleQueue implements TrackOutput {
FormatHolder formatHolder, FormatHolder formatHolder,
DecoderInputBuffer buffer, DecoderInputBuffer buffer,
boolean formatRequired, boolean formatRequired,
boolean loadingFinished, boolean loadingFinished) {
long decodeOnlyUntilUs) {
int result = int result =
readSampleMetadata( readSampleMetadata(formatHolder, buffer, formatRequired, loadingFinished, extrasHolder);
formatHolder, buffer, formatRequired, loadingFinished, decodeOnlyUntilUs, extrasHolder);
if (result == C.RESULT_BUFFER_READ && !buffer.isEndOfStream() && !buffer.isFlagsOnly()) { if (result == C.RESULT_BUFFER_READ && !buffer.isEndOfStream() && !buffer.isFlagsOnly()) {
sampleDataQueue.readToBuffer(buffer, extrasHolder); sampleDataQueue.readToBuffer(buffer, extrasHolder);
} }
@ -357,6 +370,7 @@ public class SampleQueue implements TrackOutput {
if (sampleIndex < absoluteFirstIndex || sampleIndex > absoluteFirstIndex + length) { if (sampleIndex < absoluteFirstIndex || sampleIndex > absoluteFirstIndex + length) {
return false; return false;
} }
startTimeUs = Long.MIN_VALUE;
readPosition = sampleIndex - absoluteFirstIndex; readPosition = sampleIndex - absoluteFirstIndex;
return true; return true;
} }
@ -382,6 +396,7 @@ public class SampleQueue implements TrackOutput {
if (offset == -1) { if (offset == -1) {
return false; return false;
} }
startTimeUs = timeUs;
readPosition += offset; readPosition += offset;
return true; return true;
} }
@ -513,6 +528,22 @@ public class SampleQueue implements TrackOutput {
} }
timeUs += sampleOffsetUs; timeUs += sampleOffsetUs;
if (upstreamAllSamplesAreSyncSamples) {
if (timeUs < startTimeUs) {
// If we know that all samples are sync samples, we can discard those that come before the
// start time on the write side of the queue.
return;
}
if ((flags & C.BUFFER_FLAG_KEY_FRAME) == 0) {
// The flag should always be set unless the source content has incorrect sample metadata.
// Log a warning (once per format change, to avoid log spam) and override the flag.
if (!loggedUnexpectedNonSyncSample) {
Log.w(TAG, "Overriding unexpected non-sync sample for format: " + upstreamFormat);
loggedUnexpectedNonSyncSample = true;
}
flags |= C.BUFFER_FLAG_KEY_FRAME;
}
}
if (pendingSplice) { if (pendingSplice) {
if (!isKeyframe || !attemptSplice(timeUs)) { if (!isKeyframe || !attemptSplice(timeUs)) {
return; return;
@ -568,25 +599,9 @@ public class SampleQueue implements TrackOutput {
DecoderInputBuffer buffer, DecoderInputBuffer buffer,
boolean formatRequired, boolean formatRequired,
boolean loadingFinished, boolean loadingFinished,
long decodeOnlyUntilUs,
SampleExtrasHolder extrasHolder) { SampleExtrasHolder extrasHolder) {
buffer.waitingForKeys = false; buffer.waitingForKeys = false;
// This is a temporary fix for https://github.com/google/ExoPlayer/issues/6155. if (!hasNextSample()) {
// TODO: Remove it and replace it with a fix that discards samples when writing to the queue.
boolean hasNextSample;
int relativeReadIndex = C.INDEX_UNSET;
while ((hasNextSample = hasNextSample())) {
relativeReadIndex = getRelativeIndex(readPosition);
long timeUs = timesUs[relativeReadIndex];
if (timeUs < decodeOnlyUntilUs
&& MimeTypes.allSamplesAreSyncSamples(formats[relativeReadIndex].sampleMimeType)) {
readPosition++;
} else {
break;
}
}
if (!hasNextSample) {
if (loadingFinished || isLastSampleQueued) { if (loadingFinished || isLastSampleQueued) {
buffer.setFlags(C.BUFFER_FLAG_END_OF_STREAM); buffer.setFlags(C.BUFFER_FLAG_END_OF_STREAM);
return C.RESULT_BUFFER_READ; return C.RESULT_BUFFER_READ;
@ -598,6 +613,7 @@ public class SampleQueue implements TrackOutput {
} }
} }
int relativeReadIndex = getRelativeIndex(readPosition);
if (formatRequired || formats[relativeReadIndex] != downstreamFormat) { if (formatRequired || formats[relativeReadIndex] != downstreamFormat) {
onFormatResult(formats[relativeReadIndex], formatHolder); onFormatResult(formats[relativeReadIndex], formatHolder);
return C.RESULT_FORMAT_READ; return C.RESULT_FORMAT_READ;
@ -610,7 +626,7 @@ public class SampleQueue implements TrackOutput {
buffer.setFlags(flags[relativeReadIndex]); buffer.setFlags(flags[relativeReadIndex]);
buffer.timeUs = timesUs[relativeReadIndex]; buffer.timeUs = timesUs[relativeReadIndex];
if (buffer.timeUs < decodeOnlyUntilUs) { if (buffer.timeUs < startTimeUs) {
buffer.addFlag(C.BUFFER_FLAG_DECODE_ONLY); buffer.addFlag(C.BUFFER_FLAG_DECODE_ONLY);
} }
if (buffer.isFlagsOnly()) { if (buffer.isFlagsOnly()) {
@ -631,16 +647,19 @@ public class SampleQueue implements TrackOutput {
// current upstreamFormat so we can detect format changes on the read side using cheap // current upstreamFormat so we can detect format changes on the read side using cheap
// referential quality. // referential quality.
return false; return false;
} else if (Util.areEqual(format, upstreamCommittedFormat)) { }
if (Util.areEqual(format, upstreamCommittedFormat)) {
// The format has changed back to the format of the last committed sample. If they are // The format has changed back to the format of the last committed sample. If they are
// different objects, we revert back to using upstreamCommittedFormat as the upstreamFormat // different objects, we revert back to using upstreamCommittedFormat as the upstreamFormat
// so we can detect format changes on the read side using cheap referential equality. // so we can detect format changes on the read side using cheap referential equality.
upstreamFormat = upstreamCommittedFormat; upstreamFormat = upstreamCommittedFormat;
return true;
} else { } else {
upstreamFormat = format; upstreamFormat = format;
return true;
} }
upstreamAllSamplesAreSyncSamples =
MimeTypes.allSamplesAreSyncSamples(upstreamFormat.sampleMimeType);
loggedUnexpectedNonSyncSample = false;
return true;
} }
private synchronized long discardSampleMetadataTo( private synchronized long discardSampleMetadataTo(

View File

@ -87,7 +87,6 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S
private long lastSeekPositionUs; private long lastSeekPositionUs;
private int nextNotifyPrimaryFormatMediaChunkIndex; private int nextNotifyPrimaryFormatMediaChunkIndex;
/* package */ long decodeOnlyUntilPositionUs;
/* package */ boolean loadingFinished; /* package */ boolean loadingFinished;
/** /**
@ -282,14 +281,12 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S
if (seekToMediaChunk != null) { if (seekToMediaChunk != null) {
// When seeking to the start of a chunk we use the index of the first sample in the chunk // When seeking to the start of a chunk we use the index of the first sample in the chunk
// rather than the seek position. This ensures we seek to the keyframe at the start of the // rather than the seek position. This ensures we seek to the keyframe at the start of the
// chunk even if the sample timestamps are slightly offset from the chunk start times. // chunk even if its timestamp is slightly earlier than the advertised chunk start time.
seekInsideBuffer = primarySampleQueue.seekTo(seekToMediaChunk.getFirstSampleIndex(0)); seekInsideBuffer = primarySampleQueue.seekTo(seekToMediaChunk.getFirstSampleIndex(0));
decodeOnlyUntilPositionUs = 0;
} else { } else {
seekInsideBuffer = seekInsideBuffer =
primarySampleQueue.seekTo( primarySampleQueue.seekTo(
positionUs, /* allowTimeBeyondBuffer= */ positionUs < getNextLoadPositionUs()); positionUs, /* allowTimeBeyondBuffer= */ positionUs < getNextLoadPositionUs());
decodeOnlyUntilPositionUs = lastSeekPositionUs;
} }
if (seekInsideBuffer) { if (seekInsideBuffer) {
@ -383,8 +380,7 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S
} }
maybeNotifyPrimaryTrackFormatChanged(); maybeNotifyPrimaryTrackFormatChanged();
return primarySampleQueue.read( return primarySampleQueue.read(formatHolder, buffer, formatRequired, loadingFinished);
formatHolder, buffer, formatRequired, loadingFinished, decodeOnlyUntilPositionUs);
} }
@Override @Override
@ -577,9 +573,16 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S
if (isMediaChunk(loadable)) { if (isMediaChunk(loadable)) {
BaseMediaChunk mediaChunk = (BaseMediaChunk) loadable; BaseMediaChunk mediaChunk = (BaseMediaChunk) loadable;
if (pendingReset) { if (pendingReset) {
boolean resetToMediaChunk = mediaChunk.startTimeUs == pendingResetPositionUs; // Only set the queue start times if we're not seeking to a chunk boundary. If we are
// Only enable setting of the decode only flag if we're not resetting to a chunk boundary. // seeking to a chunk boundary then we want the queue to pass through all of the samples in
decodeOnlyUntilPositionUs = resetToMediaChunk ? 0 : pendingResetPositionUs; // the chunk. Doing this ensures we'll always output the keyframe at the start of the chunk,
// even if its timestamp is slightly earlier than the advertised chunk start time.
if (mediaChunk.startTimeUs != pendingResetPositionUs) {
primarySampleQueue.setStartTimeUs(pendingResetPositionUs);
for (SampleQueue embeddedSampleQueue : embeddedSampleQueues) {
embeddedSampleQueue.setStartTimeUs(pendingResetPositionUs);
}
}
pendingResetPositionUs = C.TIME_UNSET; pendingResetPositionUs = C.TIME_UNSET;
} }
mediaChunk.init(chunkOutput); mediaChunk.init(chunkOutput);
@ -799,12 +802,7 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S
return C.RESULT_NOTHING_READ; return C.RESULT_NOTHING_READ;
} }
maybeNotifyDownstreamFormat(); maybeNotifyDownstreamFormat();
return sampleQueue.read( return sampleQueue.read(formatHolder, buffer, formatRequired, loadingFinished);
formatHolder,
buffer,
formatRequired,
loadingFinished,
decodeOnlyUntilPositionUs);
} }
public void release() { public void release() {

View File

@ -42,6 +42,7 @@ import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.upstream.DefaultAllocator; import com.google.android.exoplayer2.upstream.DefaultAllocator;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.MediaSourceEventDispatcher; import com.google.android.exoplayer2.util.MediaSourceEventDispatcher;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
import com.google.common.primitives.Bytes; import com.google.common.primitives.Bytes;
import java.io.IOException; import java.io.IOException;
@ -180,6 +181,7 @@ public final class SampleQueueTest {
assertReadSample( assertReadSample(
/* timeUs= */ i * 1000, /* timeUs= */ i * 1000,
/* isKeyFrame= */ true, /* isKeyFrame= */ true,
/* isDecodeOnly= */ false,
/* isEncrypted= */ false, /* isEncrypted= */ false,
/* sampleData= */ new byte[1], /* sampleData= */ new byte[1],
/* offset= */ 0, /* offset= */ 0,
@ -226,9 +228,23 @@ public final class SampleQueueTest {
sampleQueue.sampleMetadata(1000, C.BUFFER_FLAG_KEY_FRAME, ALLOCATION_SIZE, 0, null); sampleQueue.sampleMetadata(1000, C.BUFFER_FLAG_KEY_FRAME, ALLOCATION_SIZE, 0, null);
assertReadFormat(false, FORMAT_1); assertReadFormat(false, FORMAT_1);
assertReadSample(0, true, /* isEncrypted= */ false, DATA, 0, ALLOCATION_SIZE); assertReadSample(
0,
/* isKeyFrame= */ true,
/* isDecodeOnly= */ false,
/* isEncrypted= */ false,
DATA,
/* offset= */ 0,
ALLOCATION_SIZE);
// Assert the second sample is read without a format change. // Assert the second sample is read without a format change.
assertReadSample(1000, true, /* isEncrypted= */ false, DATA, 0, ALLOCATION_SIZE); assertReadSample(
1000,
/* isKeyFrame= */ true,
/* isDecodeOnly= */ false,
/* isEncrypted= */ false,
DATA,
/* offset= */ 0,
ALLOCATION_SIZE);
// The same applies if the queue is empty when the formats are written. // The same applies if the queue is empty when the formats are written.
sampleQueue.format(FORMAT_2); sampleQueue.format(FORMAT_2);
@ -237,7 +253,14 @@ public final class SampleQueueTest {
sampleQueue.sampleMetadata(2000, C.BUFFER_FLAG_KEY_FRAME, ALLOCATION_SIZE, 0, null); sampleQueue.sampleMetadata(2000, C.BUFFER_FLAG_KEY_FRAME, ALLOCATION_SIZE, 0, null);
// Assert the third sample is read without a format change. // Assert the third sample is read without a format change.
assertReadSample(2000, true, /* isEncrypted= */ false, DATA, 0, ALLOCATION_SIZE); assertReadSample(
2000,
/* isKeyFrame= */ true,
/* isDecodeOnly= */ false,
/* isEncrypted= */ false,
DATA,
/* offset= */ 0,
ALLOCATION_SIZE);
} }
@Test @Test
@ -260,7 +283,14 @@ public final class SampleQueueTest {
// If formatRequired, should read the format rather than the sample. // If formatRequired, should read the format rather than the sample.
assertReadFormat(true, FORMAT_1); assertReadFormat(true, FORMAT_1);
// Otherwise should read the sample. // Otherwise should read the sample.
assertReadSample(1000, true, /* isEncrypted= */ false, DATA, 0, ALLOCATION_SIZE); assertReadSample(
1000,
/* isKeyFrame= */ true,
/* isDecodeOnly= */ false,
/* isEncrypted= */ false,
DATA,
/* offset= */ 0,
ALLOCATION_SIZE);
// Allocation should still be held. // Allocation should still be held.
assertAllocationCount(1); assertAllocationCount(1);
sampleQueue.discardToRead(); sampleQueue.discardToRead();
@ -277,7 +307,14 @@ public final class SampleQueueTest {
// If formatRequired, should read the format rather than the sample. // If formatRequired, should read the format rather than the sample.
assertReadFormat(true, FORMAT_1); assertReadFormat(true, FORMAT_1);
// Read the sample. // Read the sample.
assertReadSample(2000, false, /* isEncrypted= */ false, DATA, 0, ALLOCATION_SIZE - 1); assertReadSample(
2000,
/* isKeyFrame= */ false,
/* isDecodeOnly= */ false,
/* isEncrypted= */ false,
DATA,
/* offset= */ 0,
ALLOCATION_SIZE - 1);
// Allocation should still be held. // Allocation should still be held.
assertAllocationCount(1); assertAllocationCount(1);
sampleQueue.discardToRead(); sampleQueue.discardToRead();
@ -291,7 +328,14 @@ public final class SampleQueueTest {
// If formatRequired, should read the format rather than the sample. // If formatRequired, should read the format rather than the sample.
assertReadFormat(true, FORMAT_1); assertReadFormat(true, FORMAT_1);
// Read the sample. // Read the sample.
assertReadSample(3000, false, /* isEncrypted= */ false, DATA, ALLOCATION_SIZE - 1, 1); assertReadSample(
3000,
/* isKeyFrame= */ false,
/* isDecodeOnly= */ false,
/* isEncrypted= */ false,
DATA,
ALLOCATION_SIZE - 1,
1);
// Allocation should still be held. // Allocation should still be held.
assertAllocationCount(1); assertAllocationCount(1);
sampleQueue.discardToRead(); sampleQueue.discardToRead();
@ -394,11 +438,7 @@ public final class SampleQueueTest {
int result = int result =
sampleQueue.read( sampleQueue.read(
formatHolder, formatHolder, inputBuffer, /* formatRequired= */ false, /* loadingFinished= */ false);
inputBuffer,
/* formatRequired= */ false,
/* loadingFinished= */ false,
/* decodeOnlyUntilUs= */ 0);
assertThat(result).isEqualTo(RESULT_FORMAT_READ); assertThat(result).isEqualTo(RESULT_FORMAT_READ);
assertThat(formatHolder.drmSession).isSameInstanceAs(mockDrmSession); assertThat(formatHolder.drmSession).isSameInstanceAs(mockDrmSession);
assertReadEncryptedSample(/* sampleIndex= */ 0); assertReadEncryptedSample(/* sampleIndex= */ 0);
@ -407,21 +447,13 @@ public final class SampleQueueTest {
assertThat(formatHolder.drmSession).isNull(); assertThat(formatHolder.drmSession).isNull();
result = result =
sampleQueue.read( sampleQueue.read(
formatHolder, formatHolder, inputBuffer, /* formatRequired= */ false, /* loadingFinished= */ false);
inputBuffer,
/* formatRequired= */ false,
/* loadingFinished= */ false,
/* decodeOnlyUntilUs= */ 0);
assertThat(result).isEqualTo(RESULT_FORMAT_READ); assertThat(result).isEqualTo(RESULT_FORMAT_READ);
assertThat(formatHolder.drmSession).isNull(); assertThat(formatHolder.drmSession).isNull();
assertReadEncryptedSample(/* sampleIndex= */ 2); assertReadEncryptedSample(/* sampleIndex= */ 2);
result = result =
sampleQueue.read( sampleQueue.read(
formatHolder, formatHolder, inputBuffer, /* formatRequired= */ false, /* loadingFinished= */ false);
inputBuffer,
/* formatRequired= */ false,
/* loadingFinished= */ false,
/* decodeOnlyUntilUs= */ 0);
assertThat(result).isEqualTo(RESULT_FORMAT_READ); assertThat(result).isEqualTo(RESULT_FORMAT_READ);
assertThat(formatHolder.drmSession).isSameInstanceAs(mockDrmSession); assertThat(formatHolder.drmSession).isSameInstanceAs(mockDrmSession);
} }
@ -438,11 +470,7 @@ public final class SampleQueueTest {
int result = int result =
sampleQueue.read( sampleQueue.read(
formatHolder, formatHolder, inputBuffer, /* formatRequired= */ false, /* loadingFinished= */ false);
inputBuffer,
/* formatRequired= */ false,
/* loadingFinished= */ false,
/* decodeOnlyUntilUs= */ 0);
assertThat(result).isEqualTo(RESULT_FORMAT_READ); assertThat(result).isEqualTo(RESULT_FORMAT_READ);
assertThat(formatHolder.drmSession).isSameInstanceAs(mockDrmSession); assertThat(formatHolder.drmSession).isSameInstanceAs(mockDrmSession);
assertReadEncryptedSample(/* sampleIndex= */ 0); assertReadEncryptedSample(/* sampleIndex= */ 0);
@ -451,21 +479,13 @@ public final class SampleQueueTest {
assertThat(formatHolder.drmSession).isNull(); assertThat(formatHolder.drmSession).isNull();
result = result =
sampleQueue.read( sampleQueue.read(
formatHolder, formatHolder, inputBuffer, /* formatRequired= */ false, /* loadingFinished= */ false);
inputBuffer,
/* formatRequired= */ false,
/* loadingFinished= */ false,
/* decodeOnlyUntilUs= */ 0);
assertThat(result).isEqualTo(RESULT_FORMAT_READ); assertThat(result).isEqualTo(RESULT_FORMAT_READ);
assertThat(formatHolder.drmSession).isSameInstanceAs(mockPlaceholderDrmSession); assertThat(formatHolder.drmSession).isSameInstanceAs(mockPlaceholderDrmSession);
assertReadEncryptedSample(/* sampleIndex= */ 2); assertReadEncryptedSample(/* sampleIndex= */ 2);
result = result =
sampleQueue.read( sampleQueue.read(
formatHolder, formatHolder, inputBuffer, /* formatRequired= */ false, /* loadingFinished= */ false);
inputBuffer,
/* formatRequired= */ false,
/* loadingFinished= */ false,
/* decodeOnlyUntilUs= */ 0);
assertThat(result).isEqualTo(RESULT_FORMAT_READ); assertThat(result).isEqualTo(RESULT_FORMAT_READ);
assertThat(formatHolder.drmSession).isSameInstanceAs(mockDrmSession); assertThat(formatHolder.drmSession).isSameInstanceAs(mockDrmSession);
assertReadEncryptedSample(/* sampleIndex= */ 3); assertReadEncryptedSample(/* sampleIndex= */ 3);
@ -495,11 +515,7 @@ public final class SampleQueueTest {
int result = int result =
sampleQueue.read( sampleQueue.read(
formatHolder, formatHolder, inputBuffer, /* formatRequired= */ false, /* loadingFinished= */ false);
inputBuffer,
/* formatRequired= */ false,
/* loadingFinished= */ false,
/* decodeOnlyUntilUs= */ 0);
assertThat(result).isEqualTo(RESULT_FORMAT_READ); assertThat(result).isEqualTo(RESULT_FORMAT_READ);
// Fill cryptoInfo.iv with non-zero data. When the 8 byte initialization vector is written into // Fill cryptoInfo.iv with non-zero data. When the 8 byte initialization vector is written into
@ -509,11 +525,7 @@ public final class SampleQueueTest {
result = result =
sampleQueue.read( sampleQueue.read(
formatHolder, formatHolder, inputBuffer, /* formatRequired= */ false, /* loadingFinished= */ false);
inputBuffer,
/* formatRequired= */ false,
/* loadingFinished= */ false,
/* decodeOnlyUntilUs= */ 0);
assertThat(result).isEqualTo(RESULT_BUFFER_READ); assertThat(result).isEqualTo(RESULT_BUFFER_READ);
// Assert cryptoInfo.iv contains the 8-byte initialization vector and that the trailing 8 bytes // Assert cryptoInfo.iv contains the 8-byte initialization vector and that the trailing 8 bytes
@ -608,7 +620,14 @@ public final class SampleQueueTest {
sampleQueue.sampleMetadata(0, C.BUFFER_FLAG_KEY_FRAME, ALLOCATION_SIZE, 0, null); 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. // Once the metadata has been written, check the sample can be read as expected.
assertReadSample(0, true, /* isEncrypted= */ false, DATA, 0, ALLOCATION_SIZE); assertReadSample(
/* timeUs= */ 0,
/* isKeyFrame= */ true,
/* isDecodeOnly= */ false,
/* isEncrypted= */ false,
DATA,
/* offset= */ 0,
ALLOCATION_SIZE);
assertNoSamplesToRead(FORMAT_1); assertNoSamplesToRead(FORMAT_1);
assertAllocationCount(1); assertAllocationCount(1);
sampleQueue.discardToRead(); sampleQueue.discardToRead();
@ -641,7 +660,7 @@ public final class SampleQueueTest {
int skipCount = sampleQueue.advanceTo(LAST_SAMPLE_TIMESTAMP); int skipCount = sampleQueue.advanceTo(LAST_SAMPLE_TIMESTAMP);
// Should advance to 2nd keyframe (the 4th frame). // Should advance to 2nd keyframe (the 4th frame).
assertThat(skipCount).isEqualTo(4); assertThat(skipCount).isEqualTo(4);
assertReadTestData(null, DATA_SECOND_KEYFRAME_INDEX); assertReadTestData(/* startFormat= */ null, DATA_SECOND_KEYFRAME_INDEX);
assertNoSamplesToRead(FORMAT_2); assertNoSamplesToRead(FORMAT_2);
} }
@ -651,7 +670,7 @@ public final class SampleQueueTest {
int skipCount = sampleQueue.advanceTo(LAST_SAMPLE_TIMESTAMP + 1); int skipCount = sampleQueue.advanceTo(LAST_SAMPLE_TIMESTAMP + 1);
// Should advance to 2nd keyframe (the 4th frame). // Should advance to 2nd keyframe (the 4th frame).
assertThat(skipCount).isEqualTo(4); assertThat(skipCount).isEqualTo(4);
assertReadTestData(null, DATA_SECOND_KEYFRAME_INDEX); assertReadTestData(/* startFormat= */ null, DATA_SECOND_KEYFRAME_INDEX);
assertNoSamplesToRead(FORMAT_2); assertNoSamplesToRead(FORMAT_2);
} }
@ -681,7 +700,12 @@ public final class SampleQueueTest {
boolean success = sampleQueue.seekTo(LAST_SAMPLE_TIMESTAMP, false); boolean success = sampleQueue.seekTo(LAST_SAMPLE_TIMESTAMP, false);
assertThat(success).isTrue(); assertThat(success).isTrue();
assertThat(sampleQueue.getReadIndex()).isEqualTo(4); assertThat(sampleQueue.getReadIndex()).isEqualTo(4);
assertReadTestData(null, DATA_SECOND_KEYFRAME_INDEX); assertReadTestData(
/* startFormat= */ null,
DATA_SECOND_KEYFRAME_INDEX,
/* sampleCount= */ SAMPLE_TIMESTAMPS.length - DATA_SECOND_KEYFRAME_INDEX,
/* sampleOffsetUs= */ 0,
/* decodeOnlyUntilUs= */ LAST_SAMPLE_TIMESTAMP);
assertNoSamplesToRead(FORMAT_2); assertNoSamplesToRead(FORMAT_2);
} }
@ -701,7 +725,12 @@ public final class SampleQueueTest {
boolean success = sampleQueue.seekTo(LAST_SAMPLE_TIMESTAMP + 1, true); boolean success = sampleQueue.seekTo(LAST_SAMPLE_TIMESTAMP + 1, true);
assertThat(success).isTrue(); assertThat(success).isTrue();
assertThat(sampleQueue.getReadIndex()).isEqualTo(4); assertThat(sampleQueue.getReadIndex()).isEqualTo(4);
assertReadTestData(null, DATA_SECOND_KEYFRAME_INDEX); assertReadTestData(
/* startFormat= */ null,
DATA_SECOND_KEYFRAME_INDEX,
/* sampleCount= */ SAMPLE_TIMESTAMPS.length - DATA_SECOND_KEYFRAME_INDEX,
/* sampleOffsetUs= */ 0,
/* decodeOnlyUntilUs= */ LAST_SAMPLE_TIMESTAMP + 1);
assertNoSamplesToRead(FORMAT_2); assertNoSamplesToRead(FORMAT_2);
} }
@ -711,7 +740,13 @@ public final class SampleQueueTest {
boolean success = sampleQueue.seekTo(LAST_SAMPLE_TIMESTAMP, false); boolean success = sampleQueue.seekTo(LAST_SAMPLE_TIMESTAMP, false);
assertThat(success).isTrue(); assertThat(success).isTrue();
assertThat(sampleQueue.getReadIndex()).isEqualTo(4); assertThat(sampleQueue.getReadIndex()).isEqualTo(4);
assertReadTestData(null, DATA_SECOND_KEYFRAME_INDEX); assertReadTestData(
/* startFormat= */ null,
DATA_SECOND_KEYFRAME_INDEX,
/* sampleCount= */ SAMPLE_TIMESTAMPS.length - DATA_SECOND_KEYFRAME_INDEX,
/* sampleOffsetUs= */ 0,
/* decodeOnlyUntilUs= */ LAST_SAMPLE_TIMESTAMP);
assertNoSamplesToRead(FORMAT_2); assertNoSamplesToRead(FORMAT_2);
// Seek back to the start. // Seek back to the start.
success = sampleQueue.seekTo(SAMPLE_TIMESTAMPS[0], false); success = sampleQueue.seekTo(SAMPLE_TIMESTAMPS[0], false);
@ -721,6 +756,51 @@ public final class SampleQueueTest {
assertNoSamplesToRead(FORMAT_2); assertNoSamplesToRead(FORMAT_2);
} }
@Test
public void setStartTimeUs_allSamplesAreSyncSamples_discardsOnWriteSide() {
// The format uses a MIME type for which MimeTypes.allSamplesAreSyncSamples() is true.
Format format = new Format.Builder().setSampleMimeType(MimeTypes.AUDIO_RAW).build();
Format[] sampleFormats = new Format[SAMPLE_SIZES.length];
Arrays.fill(sampleFormats, format);
int[] sampleFlags = new int[SAMPLE_SIZES.length];
Arrays.fill(sampleFlags, BUFFER_FLAG_KEY_FRAME);
sampleQueue.setStartTimeUs(LAST_SAMPLE_TIMESTAMP);
writeTestData(
DATA, SAMPLE_SIZES, SAMPLE_OFFSETS, SAMPLE_TIMESTAMPS, sampleFormats, sampleFlags);
assertThat(sampleQueue.getReadIndex()).isEqualTo(0);
assertReadFormat(/* formatRequired= */ false, format);
assertReadSample(
SAMPLE_TIMESTAMPS[7],
/* isKeyFrame= */ true,
/* isDecodeOnly= */ false,
/* isEncrypted= */ false,
DATA,
DATA.length - SAMPLE_OFFSETS[7] - SAMPLE_SIZES[7],
SAMPLE_SIZES[7]);
}
@Test
public void setStartTimeUs_notAllSamplesAreSyncSamples_discardsOnReadSide() {
// The format uses a MIME type for which MimeTypes.allSamplesAreSyncSamples() is false.
Format format = new Format.Builder().setSampleMimeType(MimeTypes.VIDEO_H264).build();
Format[] sampleFormats = new Format[SAMPLE_SIZES.length];
Arrays.fill(sampleFormats, format);
sampleQueue.setStartTimeUs(LAST_SAMPLE_TIMESTAMP);
writeTestData();
assertThat(sampleQueue.getReadIndex()).isEqualTo(0);
assertReadTestData(
/* startFormat= */ null,
/* firstSampleIndex= */ 0,
/* sampleCount= */ SAMPLE_TIMESTAMPS.length,
/* sampleOffsetUs= */ 0,
/* decodeOnlyUntilUs= */ LAST_SAMPLE_TIMESTAMP);
}
@Test @Test
public void discardToEnd() { public void discardToEnd() {
writeTestData(); writeTestData();
@ -745,7 +825,7 @@ public final class SampleQueueTest {
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(/* startFormat= */ null, 0, 1);
// Shouldn't discard anything. // Shouldn't discard anything.
sampleQueue.discardTo(SAMPLE_TIMESTAMPS[1] - 1, false, true); sampleQueue.discardTo(SAMPLE_TIMESTAMPS[1] - 1, false, true);
assertThat(sampleQueue.getFirstIndex()).isEqualTo(0); assertThat(sampleQueue.getFirstIndex()).isEqualTo(0);
@ -835,7 +915,7 @@ public final class SampleQueueTest {
writeTestData(); writeTestData();
sampleQueue.discardUpstreamSamples(4); sampleQueue.discardUpstreamSamples(4);
assertAllocationCount(4); assertAllocationCount(4);
assertReadTestData(null, 0, 4); assertReadTestData(/* startFormat= */ null, 0, 4);
assertReadFormat(false, FORMAT_2); assertReadFormat(false, FORMAT_2);
assertNoSamplesToRead(FORMAT_2); assertNoSamplesToRead(FORMAT_2);
} }
@ -843,7 +923,7 @@ public final class SampleQueueTest {
@Test @Test
public void discardUpstreamAfterRead() { public void discardUpstreamAfterRead() {
writeTestData(); writeTestData();
assertReadTestData(null, 0, 3); assertReadTestData(/* startFormat= */ null, 0, 3);
sampleQueue.discardUpstreamSamples(8); sampleQueue.discardUpstreamSamples(8);
assertAllocationCount(10); assertAllocationCount(10);
sampleQueue.discardToRead(); sampleQueue.discardToRead();
@ -908,7 +988,11 @@ public final class SampleQueueTest {
sampleQueue.setSampleOffsetUs(sampleOffsetUs); sampleQueue.setSampleOffsetUs(sampleOffsetUs);
writeTestData(); writeTestData();
assertReadTestData( assertReadTestData(
/* startFormat= */ null, /* firstSampleIndex= */ 0, /* sampleCount= */ 8, sampleOffsetUs); /* startFormat= */ null,
/* firstSampleIndex= */ 0,
/* sampleCount= */ 8,
sampleOffsetUs,
/* decodeOnlyUntilUs= */ 0);
assertReadEndOfStream(/* formatRequired= */ false); assertReadEndOfStream(/* formatRequired= */ false);
} }
@ -931,6 +1015,7 @@ public final class SampleQueueTest {
assertReadSample( assertReadSample(
unadjustedTimestampUs + sampleOffsetUs, unadjustedTimestampUs + sampleOffsetUs,
/* isKeyFrame= */ false, /* isKeyFrame= */ false,
/* isDecodeOnly= */ false,
/* isEncrypted= */ false, /* isEncrypted= */ false,
DATA, DATA,
/* offset= */ 0, /* offset= */ 0,
@ -986,6 +1071,7 @@ public final class SampleQueueTest {
assertReadSample( assertReadSample(
/* timeUs= */ 0, /* timeUs= */ 0,
/* isKeyFrame= */ true, /* isKeyFrame= */ true,
/* isDecodeOnly= */ false,
/* isEncrypted= */ false, /* isEncrypted= */ false,
DATA, DATA,
/* offset= */ 0, /* offset= */ 0,
@ -994,6 +1080,7 @@ public final class SampleQueueTest {
assertReadSample( assertReadSample(
/* timeUs= */ 1, /* timeUs= */ 1,
/* isKeyFrame= */ false, /* isKeyFrame= */ false,
/* isDecodeOnly= */ false,
/* isEncrypted= */ false, /* isEncrypted= */ false,
DATA, DATA,
/* offset= */ 0, /* offset= */ 0,
@ -1009,16 +1096,23 @@ public final class SampleQueueTest {
long spliceSampleTimeUs = SAMPLE_TIMESTAMPS[4]; long spliceSampleTimeUs = SAMPLE_TIMESTAMPS[4];
writeFormat(FORMAT_SPLICED); writeFormat(FORMAT_SPLICED);
writeSample(DATA, spliceSampleTimeUs, C.BUFFER_FLAG_KEY_FRAME); writeSample(DATA, spliceSampleTimeUs, C.BUFFER_FLAG_KEY_FRAME);
assertReadTestData(null, 0, 4); assertReadTestData(/* startFormat= */ null, 0, 4);
assertReadFormat(false, FORMAT_SPLICED); assertReadFormat(false, FORMAT_SPLICED);
assertReadSample(spliceSampleTimeUs, true, /* isEncrypted= */ false, DATA, 0, DATA.length); assertReadSample(
spliceSampleTimeUs,
/* isKeyFrame= */ true,
/* isDecodeOnly= */ false,
/* isEncrypted= */ false,
DATA,
/* offset= */ 0,
DATA.length);
assertReadEndOfStream(false); assertReadEndOfStream(false);
} }
@Test @Test
public void spliceAfterRead() { public void spliceAfterRead() {
writeTestData(); writeTestData();
assertReadTestData(null, 0, 4); assertReadTestData(/* startFormat= */ null, 0, 4);
sampleQueue.splice(); sampleQueue.splice();
// Splice should fail, leaving the last 4 samples unchanged. // Splice should fail, leaving the last 4 samples unchanged.
long spliceSampleTimeUs = SAMPLE_TIMESTAMPS[3]; long spliceSampleTimeUs = SAMPLE_TIMESTAMPS[3];
@ -1028,14 +1122,21 @@ public final class SampleQueueTest {
assertReadEndOfStream(false); assertReadEndOfStream(false);
sampleQueue.seekTo(0); sampleQueue.seekTo(0);
assertReadTestData(null, 0, 4); assertReadTestData(/* startFormat= */ null, 0, 4);
sampleQueue.splice(); sampleQueue.splice();
// Splice should succeed, replacing the last 4 samples with the sample being written // Splice should succeed, replacing the last 4 samples with the sample being written
spliceSampleTimeUs = SAMPLE_TIMESTAMPS[3] + 1; spliceSampleTimeUs = SAMPLE_TIMESTAMPS[3] + 1;
writeFormat(FORMAT_SPLICED); writeFormat(FORMAT_SPLICED);
writeSample(DATA, spliceSampleTimeUs, C.BUFFER_FLAG_KEY_FRAME); writeSample(DATA, spliceSampleTimeUs, C.BUFFER_FLAG_KEY_FRAME);
assertReadFormat(false, FORMAT_SPLICED); assertReadFormat(false, FORMAT_SPLICED);
assertReadSample(spliceSampleTimeUs, true, /* isEncrypted= */ false, DATA, 0, DATA.length); assertReadSample(
spliceSampleTimeUs,
/* isKeyFrame= */ true,
/* isDecodeOnly= */ false,
/* isEncrypted= */ false,
DATA,
/* offset= */ 0,
DATA.length);
assertReadEndOfStream(false); assertReadEndOfStream(false);
} }
@ -1049,14 +1150,23 @@ public final class SampleQueueTest {
long spliceSampleTimeUs = SAMPLE_TIMESTAMPS[4]; long spliceSampleTimeUs = SAMPLE_TIMESTAMPS[4];
writeFormat(FORMAT_SPLICED); writeFormat(FORMAT_SPLICED);
writeSample(DATA, spliceSampleTimeUs, C.BUFFER_FLAG_KEY_FRAME); writeSample(DATA, spliceSampleTimeUs, C.BUFFER_FLAG_KEY_FRAME);
assertReadTestData(null, 0, 4, sampleOffsetUs); assertReadTestData(/* startFormat= */ null, 0, 4, sampleOffsetUs, /* decodeOnlyUntilUs= */ 0);
assertReadFormat( assertReadFormat(
false, FORMAT_SPLICED.buildUpon().setSubsampleOffsetUs(sampleOffsetUs).build()); false, FORMAT_SPLICED.buildUpon().setSubsampleOffsetUs(sampleOffsetUs).build());
assertReadSample( assertReadSample(
spliceSampleTimeUs + sampleOffsetUs, true, /* isEncrypted= */ false, DATA, 0, DATA.length); spliceSampleTimeUs + sampleOffsetUs,
/* isKeyFrame= */ true,
/* isDecodeOnly= */ false,
/* isEncrypted= */ false,
DATA,
/* offset= */ 0,
DATA.length);
assertReadEndOfStream(false); assertReadEndOfStream(false);
} }
@Test
public void setStartTime() {}
// Internal methods. // Internal methods.
/** /**
@ -1119,7 +1229,7 @@ public final class SampleQueueTest {
* Asserts correct reading of standard test data from {@code sampleQueue}. * Asserts correct reading of standard test data from {@code sampleQueue}.
*/ */
private void assertReadTestData() { private void assertReadTestData() {
assertReadTestData(null, 0); assertReadTestData(/* startFormat= */ null, 0);
} }
/** /**
@ -1149,7 +1259,12 @@ public final class SampleQueueTest {
* @param sampleCount The number of samples to read. * @param sampleCount The number of samples to read.
*/ */
private void assertReadTestData(Format startFormat, int firstSampleIndex, int sampleCount) { private void assertReadTestData(Format startFormat, int firstSampleIndex, int sampleCount) {
assertReadTestData(startFormat, firstSampleIndex, sampleCount, 0); assertReadTestData(
startFormat,
firstSampleIndex,
sampleCount,
/* sampleOffsetUs= */ 0,
/* decodeOnlyUntilUs= */ 0);
} }
/** /**
@ -1161,7 +1276,11 @@ public final class SampleQueueTest {
* @param sampleOffsetUs The expected sample offset. * @param sampleOffsetUs The expected sample offset.
*/ */
private void assertReadTestData( private void assertReadTestData(
Format startFormat, int firstSampleIndex, int sampleCount, long sampleOffsetUs) { Format startFormat,
int firstSampleIndex,
int sampleCount,
long sampleOffsetUs,
long decodeOnlyUntilUs) {
Format format = adjustFormat(startFormat, sampleOffsetUs); Format format = adjustFormat(startFormat, sampleOffsetUs);
for (int i = firstSampleIndex; i < firstSampleIndex + sampleCount; i++) { for (int i = firstSampleIndex; i < firstSampleIndex + sampleCount; i++) {
// Use equals() on the read side despite using referential equality on the write side, since // Use equals() on the read side despite using referential equality on the write side, since
@ -1175,9 +1294,11 @@ public final class SampleQueueTest {
// If we require the format, we should always read it. // If we require the format, we should always read it.
assertReadFormat(true, testSampleFormat); assertReadFormat(true, testSampleFormat);
// Assert the sample is as expected. // Assert the sample is as expected.
long expectedTimeUs = SAMPLE_TIMESTAMPS[i] + sampleOffsetUs;
assertReadSample( assertReadSample(
SAMPLE_TIMESTAMPS[i] + sampleOffsetUs, expectedTimeUs,
(SAMPLE_FLAGS[i] & C.BUFFER_FLAG_KEY_FRAME) != 0, (SAMPLE_FLAGS[i] & C.BUFFER_FLAG_KEY_FRAME) != 0,
/* isDecodeOnly= */ expectedTimeUs < decodeOnlyUntilUs,
/* isEncrypted= */ false, /* isEncrypted= */ false,
DATA, DATA,
DATA.length - SAMPLE_OFFSETS[i] - SAMPLE_SIZES[i], DATA.length - SAMPLE_OFFSETS[i] - SAMPLE_SIZES[i],
@ -1221,12 +1342,7 @@ public final class SampleQueueTest {
private void assertReadNothing(boolean formatRequired) { private void assertReadNothing(boolean formatRequired) {
clearFormatHolderAndInputBuffer(); clearFormatHolderAndInputBuffer();
int result = int result =
sampleQueue.read( sampleQueue.read(formatHolder, inputBuffer, formatRequired, /* loadingFinished= */ false);
formatHolder,
inputBuffer,
formatRequired,
/* loadingFinished= */ false,
/* decodeOnlyUntilUs= */ 0);
assertThat(result).isEqualTo(RESULT_NOTHING_READ); assertThat(result).isEqualTo(RESULT_NOTHING_READ);
// formatHolder should not be populated. // formatHolder should not be populated.
assertThat(formatHolder.format).isNull(); assertThat(formatHolder.format).isNull();
@ -1244,12 +1360,7 @@ public final class SampleQueueTest {
private void assertReadEndOfStream(boolean formatRequired) { private void assertReadEndOfStream(boolean formatRequired) {
clearFormatHolderAndInputBuffer(); clearFormatHolderAndInputBuffer();
int result = int result =
sampleQueue.read( sampleQueue.read(formatHolder, inputBuffer, formatRequired, /* loadingFinished= */ true);
formatHolder,
inputBuffer,
formatRequired,
/* loadingFinished= */ true,
/* decodeOnlyUntilUs= */ 0);
assertThat(result).isEqualTo(RESULT_BUFFER_READ); assertThat(result).isEqualTo(RESULT_BUFFER_READ);
// formatHolder should not be populated. // formatHolder should not be populated.
assertThat(formatHolder.format).isNull(); assertThat(formatHolder.format).isNull();
@ -1270,12 +1381,7 @@ public final class SampleQueueTest {
private void assertReadFormat(boolean formatRequired, Format format) { private void assertReadFormat(boolean formatRequired, Format format) {
clearFormatHolderAndInputBuffer(); clearFormatHolderAndInputBuffer();
int result = int result =
sampleQueue.read( sampleQueue.read(formatHolder, inputBuffer, formatRequired, /* loadingFinished= */ false);
formatHolder,
inputBuffer,
formatRequired,
/* loadingFinished= */ false,
/* decodeOnlyUntilUs= */ 0);
assertThat(result).isEqualTo(RESULT_FORMAT_READ); assertThat(result).isEqualTo(RESULT_FORMAT_READ);
// formatHolder should be populated. // formatHolder should be populated.
assertThat(formatHolder.format).isEqualTo(format); assertThat(formatHolder.format).isEqualTo(format);
@ -1292,6 +1398,7 @@ public final class SampleQueueTest {
assertReadSample( assertReadSample(
ENCRYPTED_SAMPLE_TIMESTAMPS[sampleIndex], ENCRYPTED_SAMPLE_TIMESTAMPS[sampleIndex],
isKeyFrame, isKeyFrame,
/* isDecodeOnly= */ false,
isEncrypted, isEncrypted,
sampleData, sampleData,
/* offset= */ 0, /* offset= */ 0,
@ -1304,6 +1411,7 @@ public final class SampleQueueTest {
* *
* @param timeUs The expected buffer timestamp. * @param timeUs The expected buffer timestamp.
* @param isKeyFrame The expected keyframe flag. * @param isKeyFrame The expected keyframe flag.
* @param isDecodeOnly The expected decodeOnly flag.
* @param isEncrypted The expected encrypted flag. * @param isEncrypted The expected encrypted flag.
* @param sampleData An array containing the expected sample data. * @param sampleData An array containing the expected sample data.
* @param offset The offset in {@code sampleData} of the expected sample data. * @param offset The offset in {@code sampleData} of the expected sample data.
@ -1312,6 +1420,7 @@ public final class SampleQueueTest {
private void assertReadSample( private void assertReadSample(
long timeUs, long timeUs,
boolean isKeyFrame, boolean isKeyFrame,
boolean isDecodeOnly,
boolean isEncrypted, boolean isEncrypted,
byte[] sampleData, byte[] sampleData,
int offset, int offset,
@ -1319,18 +1428,14 @@ public final class SampleQueueTest {
clearFormatHolderAndInputBuffer(); clearFormatHolderAndInputBuffer();
int result = int result =
sampleQueue.read( sampleQueue.read(
formatHolder, formatHolder, inputBuffer, /* formatRequired= */ false, /* loadingFinished= */ false);
inputBuffer,
/* formatRequired= */ false,
/* loadingFinished= */ false,
/* decodeOnlyUntilUs= */ 0);
assertThat(result).isEqualTo(RESULT_BUFFER_READ); assertThat(result).isEqualTo(RESULT_BUFFER_READ);
// formatHolder should not be populated. // formatHolder should not be populated.
assertThat(formatHolder.format).isNull(); assertThat(formatHolder.format).isNull();
// inputBuffer should be populated. // inputBuffer should be populated.
assertThat(inputBuffer.timeUs).isEqualTo(timeUs); assertThat(inputBuffer.timeUs).isEqualTo(timeUs);
assertThat(inputBuffer.isKeyFrame()).isEqualTo(isKeyFrame); assertThat(inputBuffer.isKeyFrame()).isEqualTo(isKeyFrame);
assertThat(inputBuffer.isDecodeOnly()).isFalse(); assertThat(inputBuffer.isDecodeOnly()).isEqualTo(isDecodeOnly);
assertThat(inputBuffer.isEncrypted()).isEqualTo(isEncrypted); assertThat(inputBuffer.isEncrypted()).isEqualTo(isEncrypted);
inputBuffer.flip(); inputBuffer.flip();
assertThat(inputBuffer.data.limit()).isEqualTo(length); assertThat(inputBuffer.data.limit()).isEqualTo(length);

View File

@ -380,11 +380,7 @@ public final class PlayerEmsgHandler implements Handler.Callback {
buffer.clear(); buffer.clear();
int result = int result =
sampleQueue.read( sampleQueue.read(
formatHolder, formatHolder, buffer, /* formatRequired= */ false, /* loadingFinished= */ false);
buffer,
/* formatRequired= */ false,
/* loadingFinished= */ false,
/* decodeOnlyUntilUs= */ 0);
if (result == C.RESULT_BUFFER_READ) { if (result == C.RESULT_BUFFER_READ) {
buffer.flip(); buffer.flip();
return buffer; return buffer;

View File

@ -560,8 +560,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
} }
int result = int result =
sampleQueues[sampleQueueIndex].read( sampleQueues[sampleQueueIndex].read(formatHolder, buffer, requireFormat, loadingFinished);
formatHolder, buffer, requireFormat, loadingFinished, lastSeekPositionUs);
if (result == C.RESULT_FORMAT_READ) { if (result == C.RESULT_FORMAT_READ) {
Format format = Assertions.checkNotNull(formatHolder.format); Format format = Assertions.checkNotNull(formatHolder.format);
if (sampleQueueIndex == primarySampleQueueIndex) { if (sampleQueueIndex == primarySampleQueueIndex) {
@ -641,6 +640,9 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
if (isPendingReset()) { if (isPendingReset()) {
chunkQueue = Collections.emptyList(); chunkQueue = Collections.emptyList();
loadPositionUs = pendingResetPositionUs; loadPositionUs = pendingResetPositionUs;
for (SampleQueue sampleQueue : sampleQueues) {
sampleQueue.setStartTimeUs(pendingResetPositionUs);
}
} else { } else {
chunkQueue = readOnlyMediaChunks; chunkQueue = readOnlyMediaChunks;
HlsMediaChunk lastMediaChunk = getLastMediaChunk(); HlsMediaChunk lastMediaChunk = getLastMediaChunk();