Discard samples on the write-side of SampleQueue
PiperOrigin-RevId: 319205008
This commit is contained in:
parent
f8217140d7
commit
8bd01a7bec
@ -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();
|
||||||
|
@ -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(
|
||||||
|
@ -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() {
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
@ -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();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user