Add allowOnlyClearBuffers to SampleQueue#read

Removes the need for duplicate calls to SampleQueue#read when
implementing DecryptionResources acquisition in the MediaSources.

PiperOrigin-RevId: 250298175
This commit is contained in:
aquilescanta 2019-05-28 17:26:19 +01:00 committed by Toni
parent c495a3f55e
commit 04f3888550
7 changed files with 154 additions and 18 deletions

View File

@ -447,7 +447,12 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
maybeNotifyDownstreamFormat(track);
int result =
sampleQueues[track].read(
formatHolder, buffer, formatRequired, loadingFinished, lastSeekPositionUs);
formatHolder,
buffer,
formatRequired,
/* allowOnlyClearBuffers= */ false,
loadingFinished,
lastSeekPositionUs);
if (result == C.RESULT_NOTHING_READ) {
maybeStartDeferredRetry(track);
}

View File

@ -230,6 +230,8 @@ import com.google.android.exoplayer2.util.Util;
* @param formatRequired Whether the caller requires that the format of the stream be read even if
* 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.
* @param allowOnlyClearBuffers If set to true, this method will not return encrypted buffers,
* returning {@link C#RESULT_NOTHING_READ} (without advancing the read position) instead.
* @param loadingFinished True if an empty queue should be considered the end of the stream.
* @param downstreamFormat The current downstream {@link Format}. If the format of the next sample
* is different to the current downstream format then a format will be read.
@ -242,6 +244,7 @@ import com.google.android.exoplayer2.util.Util;
FormatHolder formatHolder,
DecoderInputBuffer buffer,
boolean formatRequired,
boolean allowOnlyClearBuffers,
boolean loadingFinished,
Format downstreamFormat,
SampleExtrasHolder extrasHolder) {
@ -264,6 +267,10 @@ import com.google.android.exoplayer2.util.Util;
return C.RESULT_FORMAT_READ;
}
if (allowOnlyClearBuffers && (flags[relativeReadIndex] & C.BUFFER_FLAG_ENCRYPTED) != 0) {
return C.RESULT_NOTHING_READ;
}
buffer.setFlags(flags[relativeReadIndex]);
buffer.timeUs = timesUs[relativeReadIndex];
if (buffer.isFlagsOnly()) {

View File

@ -324,6 +324,8 @@ public class SampleQueue implements TrackOutput {
* @param formatRequired Whether the caller requires that the format of the stream be read even if
* 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.
* @param allowOnlyClearBuffers If set to true, this method will not return encrypted buffers,
* returning {@link C#RESULT_NOTHING_READ} (without advancing the read position) instead.
* @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.
@ -334,10 +336,18 @@ public class SampleQueue implements TrackOutput {
FormatHolder formatHolder,
DecoderInputBuffer buffer,
boolean formatRequired,
boolean allowOnlyClearBuffers,
boolean loadingFinished,
long decodeOnlyUntilUs) {
int result = metadataQueue.read(formatHolder, buffer, formatRequired, loadingFinished,
downstreamFormat, extrasHolder);
int result =
metadataQueue.read(
formatHolder,
buffer,
formatRequired,
allowOnlyClearBuffers,
loadingFinished,
downstreamFormat,
extrasHolder);
switch (result) {
case C.RESULT_FORMAT_READ:
downstreamFormat = formatHolder.format;

View File

@ -409,7 +409,12 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S
}
maybeNotifyPrimaryTrackFormatChanged();
return primarySampleQueue.read(
formatHolder, buffer, formatRequired, loadingFinished, decodeOnlyUntilPositionUs);
formatHolder,
buffer,
formatRequired,
/* allowOnlyClearBuffers= */ false,
loadingFinished,
decodeOnlyUntilPositionUs);
}
@Override
@ -801,7 +806,12 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S
}
maybeNotifyDownstreamFormat();
return sampleQueue.read(
formatHolder, buffer, formatRequired, loadingFinished, decodeOnlyUntilPositionUs);
formatHolder,
buffer,
formatRequired,
/* allowOnlyClearBuffers= */ false,
loadingFinished,
decodeOnlyUntilPositionUs);
}
public void release() {

View File

@ -29,10 +29,12 @@ import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.FormatHolder;
import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.testutil.TestUtil;
import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.upstream.DefaultAllocator;
import com.google.android.exoplayer2.util.ParsableByteArray;
import java.util.Arrays;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@ -89,6 +91,8 @@ public final class SampleQueueTest {
private static final Format[] SAMPLE_FORMATS =
new Format[] {FORMAT_1, FORMAT_1, FORMAT_1, FORMAT_1, FORMAT_2, FORMAT_2, FORMAT_2, FORMAT_2};
private static final int DATA_SECOND_KEYFRAME_INDEX = 4;
private static final TrackOutput.CryptoData DUMMY_CRYPTO_DATA =
new TrackOutput.CryptoData(C.CRYPTO_MODE_AES_CTR, new byte[16], 0, 0);
private Allocator allocator;
private SampleQueue sampleQueue;
@ -511,6 +515,49 @@ public final class SampleQueueTest {
assertThat(sampleQueue.getLargestQueuedTimestampUs()).isEqualTo(MIN_VALUE);
}
@Test
public void testAllowOnlyClearBuffers() {
int[] flags =
new int[] {
C.BUFFER_FLAG_KEY_FRAME,
C.BUFFER_FLAG_ENCRYPTED,
0,
0,
0,
C.BUFFER_FLAG_KEY_FRAME | C.BUFFER_FLAG_ENCRYPTED,
0,
0
};
int[] sampleSizes = new int[flags.length];
Arrays.fill(sampleSizes, /* val= */ 1);
// Two encryption preamble bytes per encrypted sample in the sample queue.
byte[] sampleData = new byte[flags.length + 2 + 2];
Arrays.fill(sampleData, /* val= */ (byte) 1);
writeTestData(
sampleData, sampleSizes, new int[flags.length], SAMPLE_TIMESTAMPS, SAMPLE_FORMATS, flags);
assertReadFormat(/* formatRequired= */ false, FORMAT_1);
assertResult(RESULT_BUFFER_READ, /* allowOnlyClearBuffers= */ true);
assertResult(RESULT_NOTHING_READ, /* allowOnlyClearBuffers= */ true);
assertResult(RESULT_BUFFER_READ, /* allowOnlyClearBuffers= */ false);
assertResult(RESULT_BUFFER_READ, /* allowOnlyClearBuffers= */ true);
assertResult(RESULT_BUFFER_READ, /* allowOnlyClearBuffers= */ true);
assertResult(RESULT_FORMAT_READ, /* allowOnlyClearBuffers= */ true);
assertResult(RESULT_BUFFER_READ, /* allowOnlyClearBuffers= */ true);
assertResult(RESULT_NOTHING_READ, /* allowOnlyClearBuffers= */ true);
assertResult(RESULT_BUFFER_READ, /* allowOnlyClearBuffers= */ false);
assertResult(RESULT_BUFFER_READ, /* allowOnlyClearBuffers= */ true);
assertResult(RESULT_BUFFER_READ, /* allowOnlyClearBuffers= */ true);
assertResult(RESULT_NOTHING_READ, /* allowOnlyClearBuffers= */ true);
assertResult(RESULT_NOTHING_READ, /* allowOnlyClearBuffers= */ false);
}
@Test
public void testLargestQueuedTimestampWithRead() {
writeTestData();
@ -602,8 +649,12 @@ public final class SampleQueueTest {
sampleQueue.format(sampleFormats[i]);
format = sampleFormats[i];
}
sampleQueue.sampleMetadata(sampleTimestamps[i], sampleFlags[i], sampleSizes[i],
sampleOffsets[i], null);
sampleQueue.sampleMetadata(
sampleTimestamps[i],
sampleFlags[i],
sampleSizes[i],
sampleOffsets[i],
(sampleFlags[i] & C.BUFFER_FLAG_ENCRYPTED) != 0 ? DUMMY_CRYPTO_DATA : null);
}
}
@ -714,11 +765,18 @@ public final class SampleQueueTest {
/**
* Asserts {@link SampleQueue#read} returns {@link C#RESULT_NOTHING_READ}.
*
* @param formatRequired The value of {@code formatRequired} passed to readData.
* @param formatRequired The value of {@code formatRequired} passed to {@link SampleQueue#read}.
*/
private void assertReadNothing(boolean formatRequired) {
clearFormatHolderAndInputBuffer();
int result = sampleQueue.read(formatHolder, inputBuffer, formatRequired, false, 0);
int result =
sampleQueue.read(
formatHolder,
inputBuffer,
formatRequired,
/* allowOnlyClearBuffers= */ false,
/* loadingFinished= */ false,
/* decodeOnlyUntilUs= */ 0);
assertThat(result).isEqualTo(RESULT_NOTHING_READ);
// formatHolder should not be populated.
assertThat(formatHolder.format).isNull();
@ -728,14 +786,21 @@ public final class SampleQueueTest {
}
/**
* Asserts {@link SampleQueue#read} returns {@link C#RESULT_BUFFER_READ} and that the
* {@link DecoderInputBuffer#isEndOfStream()} is set.
* Asserts {@link SampleQueue#read} returns {@link C#RESULT_BUFFER_READ} and that the {@link
* DecoderInputBuffer#isEndOfStream()} is set.
*
* @param formatRequired The value of {@code formatRequired} passed to readData.
* @param formatRequired The value of {@code formatRequired} passed to {@link SampleQueue#read}.
*/
private void assertReadEndOfStream(boolean formatRequired) {
clearFormatHolderAndInputBuffer();
int result = sampleQueue.read(formatHolder, inputBuffer, formatRequired, true, 0);
int result =
sampleQueue.read(
formatHolder,
inputBuffer,
formatRequired,
/* allowOnlyClearBuffers= */ false,
/* loadingFinished= */ true,
/* decodeOnlyUntilUs= */ 0);
assertThat(result).isEqualTo(RESULT_BUFFER_READ);
// formatHolder should not be populated.
assertThat(formatHolder.format).isNull();
@ -750,12 +815,19 @@ public final class SampleQueueTest {
* Asserts {@link SampleQueue#read} returns {@link C#RESULT_FORMAT_READ} and that the format
* holder is filled with a {@link Format} that equals {@code format}.
*
* @param formatRequired The value of {@code formatRequired} passed to readData.
* @param formatRequired The value of {@code formatRequired} passed to {@link SampleQueue#read}.
* @param format The expected format.
*/
private void assertReadFormat(boolean formatRequired, Format format) {
clearFormatHolderAndInputBuffer();
int result = sampleQueue.read(formatHolder, inputBuffer, formatRequired, false, 0);
int result =
sampleQueue.read(
formatHolder,
inputBuffer,
formatRequired,
/* allowOnlyClearBuffers= */ false,
/* loadingFinished= */ false,
/* decodeOnlyUntilUs= */ 0);
assertThat(result).isEqualTo(RESULT_FORMAT_READ);
// formatHolder should be populated.
assertThat(formatHolder.format).isEqualTo(format);
@ -777,7 +849,14 @@ public final class SampleQueueTest {
private void assertReadSample(
long timeUs, boolean isKeyframe, byte[] sampleData, int offset, int length) {
clearFormatHolderAndInputBuffer();
int result = sampleQueue.read(formatHolder, inputBuffer, false, false, 0);
int result =
sampleQueue.read(
formatHolder,
inputBuffer,
/* formatRequired= */ false,
/* allowOnlyClearBuffers= */ false,
/* loadingFinished= */ false,
/* decodeOnlyUntilUs= */ 0);
assertThat(result).isEqualTo(RESULT_BUFFER_READ);
// formatHolder should not be populated.
assertThat(formatHolder.format).isNull();
@ -793,6 +872,19 @@ public final class SampleQueueTest {
assertThat(readData).isEqualTo(copyOfRange(sampleData, offset, offset + length));
}
/** Asserts {@link SampleQueue#read} returns the given result. */
private void assertResult(int expectedResult, boolean allowOnlyClearBuffers) {
int obtainedResult =
sampleQueue.read(
formatHolder,
inputBuffer,
/* formatRequired= */ false,
allowOnlyClearBuffers,
/* loadingFinished= */ false,
/* decodeOnlyUntilUs= */ 0);
assertThat(obtainedResult).isEqualTo(expectedResult);
}
/**
* Asserts the number of allocations currently in use by {@code sampleQueue}.
*

View File

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

View File

@ -491,7 +491,12 @@ import java.util.Map;
int result =
sampleQueues[sampleQueueIndex].read(
formatHolder, buffer, requireFormat, loadingFinished, lastSeekPositionUs);
formatHolder,
buffer,
requireFormat,
/* allowOnlyClearBuffers= */ false,
loadingFinished,
lastSeekPositionUs);
if (result == C.RESULT_FORMAT_READ) {
Format format = formatHolder.format;
if (sampleQueueIndex == primarySampleQueueIndex) {