SampleStream/SampleQueue: Introduce read flags

- SampleQueue.peek is replaced with SampleQueue.read with
  FLAG_PEEK. This also exposes peek functionality through
  SampleStream.
- Use of DecoderInputBuffer.isFlagsOnly is replaced with
  FLAG_OMIT_SAMPLE_DATA. This flag can be used with or
  without FLAG_PEEK, where-as previously the read position
  would never be advanced for an isFlagsOnly buffer.
- formatRequired is replaced with FLAG_FORMAT_REQUIRED.

PiperOrigin-RevId: 363460105
This commit is contained in:
olly 2021-03-17 18:05:46 +00:00 committed by Ian Baker
parent 31f65f63ff
commit 2d9177b7c2
28 changed files with 293 additions and 224 deletions

View file

@ -111,12 +111,8 @@ public class DecoderInputBuffer extends Buffer {
@BufferReplacementMode private final int bufferReplacementMode;
private final int paddingSize;
/**
* Creates a new instance for which {@link #isFlagsOnly()} will return true.
*
* @return A new flags only input buffer.
*/
public static DecoderInputBuffer newFlagsOnlyInstance() {
/** Returns a new instance that's not able to hold any data. */
public static DecoderInputBuffer newNoDataInstance() {
return new DecoderInputBuffer(BUFFER_REPLACEMENT_MODE_DISABLED);
}
@ -200,14 +196,6 @@ public class DecoderInputBuffer extends Buffer {
data = newData;
}
/**
* Returns whether the buffer is only able to hold flags, meaning {@link #data} is null and
* its replacement mode is {@link #BUFFER_REPLACEMENT_MODE_DISABLED}.
*/
public final boolean isFlagsOnly() {
return data == null && bufferReplacementMode == BUFFER_REPLACEMENT_MODE_DISABLED;
}
/**
* Returns whether the {@link C#BUFFER_FLAG_ENCRYPTED} flag is set.
*/

View file

@ -21,6 +21,8 @@ import androidx.annotation.Nullable;
import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
import com.google.android.exoplayer2.decoder.DecoderInputBuffer.InsufficientCapacityException;
import com.google.android.exoplayer2.source.SampleStream;
import com.google.android.exoplayer2.source.SampleStream.ReadDataResult;
import com.google.android.exoplayer2.source.SampleStream.ReadFlags;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.MediaClock;
import java.io.IOException;
@ -225,8 +227,7 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities {
* @param formats The enabled formats.
* @param startPositionUs The start position of the new stream in renderer time (microseconds).
* @param offsetUs The offset that will be added to the timestamps of buffers read via {@link
* #readSource(FormatHolder, DecoderInputBuffer, boolean)} so that decoder input buffers have
* monotonically increasing timestamps.
* #readSource} so that decoder input buffers have monotonically increasing timestamps.
* @throws ExoPlaybackException If an error occurs.
*/
protected void onStreamChanged(Format[] formats, long startPositionUs, long offsetUs)
@ -383,20 +384,17 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities {
* @param buffer A {@link DecoderInputBuffer} to populate in the case of reading a sample or the
* end of the stream. If the end of the stream has been reached, the {@link
* C#BUFFER_FLAG_END_OF_STREAM} flag will be set on the buffer.
* @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.
* @return The status of read, one of {@link SampleStream.ReadDataResult}.
* @throws InsufficientCapacityException If the {@code buffer} is not a {@link
* DecoderInputBuffer#isFlagsOnly() flags-only} buffer and has insufficient capacity to hold
* @param readFlags Flags controlling the behavior of this read operation.
* @return The {@link ReadDataResult result} of the read operation.
* @throws InsufficientCapacityException If the {@code buffer} has insufficient capacity to hold
* the data of a sample being read. The buffer {@link DecoderInputBuffer#timeUs timestamp} and
* flags are populated if this exception is thrown, but the read position is not advanced.
*/
@SampleStream.ReadDataResult
@ReadDataResult
protected final int readSource(
FormatHolder formatHolder, DecoderInputBuffer buffer, boolean formatRequired) {
@SampleStream.ReadDataResult
int result = Assertions.checkNotNull(stream).readData(formatHolder, buffer, formatRequired);
FormatHolder formatHolder, DecoderInputBuffer buffer, @ReadFlags int readFlags) {
@ReadDataResult
int result = Assertions.checkNotNull(stream).readData(formatHolder, buffer, readFlags);
if (result == C.RESULT_BUFFER_READ) {
if (buffer.isEndOfStream()) {
readingPositionUs = C.TIME_END_OF_SOURCE;

View file

@ -18,6 +18,7 @@ package com.google.android.exoplayer2.audio;
import static com.google.android.exoplayer2.decoder.DecoderReuseEvaluation.DISCARD_REASON_DRM_SESSION_CHANGED;
import static com.google.android.exoplayer2.decoder.DecoderReuseEvaluation.DISCARD_REASON_REUSE_NOT_IMPLEMENTED;
import static com.google.android.exoplayer2.decoder.DecoderReuseEvaluation.REUSE_RESULT_NO;
import static com.google.android.exoplayer2.source.SampleStream.FLAG_REQUIRE_FORMAT;
import static java.lang.Math.max;
import android.os.Handler;
@ -45,7 +46,7 @@ import com.google.android.exoplayer2.decoder.SimpleOutputBuffer;
import com.google.android.exoplayer2.drm.DrmSession;
import com.google.android.exoplayer2.drm.DrmSession.DrmSessionException;
import com.google.android.exoplayer2.drm.ExoMediaCrypto;
import com.google.android.exoplayer2.source.SampleStream;
import com.google.android.exoplayer2.source.SampleStream.ReadDataResult;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Log;
import com.google.android.exoplayer2.util.MediaClock;
@ -189,7 +190,7 @@ public abstract class DecoderAudioRenderer<
eventDispatcher = new EventDispatcher(eventHandler, eventListener);
this.audioSink = audioSink;
audioSink.setListener(new AudioSinkListener());
flagsOnlyBuffer = DecoderInputBuffer.newFlagsOnlyInstance();
flagsOnlyBuffer = DecoderInputBuffer.newNoDataInstance();
decoderReinitializationState = REINITIALIZATION_STATE_NONE;
audioTrackNeedsConfigure = true;
}
@ -273,7 +274,7 @@ public abstract class DecoderAudioRenderer<
// We don't have a format yet, so try and read one.
FormatHolder formatHolder = getFormatHolder();
flagsOnlyBuffer.clear();
@SampleStream.ReadDataResult int result = readSource(formatHolder, flagsOnlyBuffer, true);
@ReadDataResult int result = readSource(formatHolder, flagsOnlyBuffer, FLAG_REQUIRE_FORMAT);
if (result == C.RESULT_FORMAT_READ) {
onInputFormatChanged(formatHolder);
} else if (result == C.RESULT_BUFFER_READ) {
@ -438,7 +439,7 @@ public abstract class DecoderAudioRenderer<
}
FormatHolder formatHolder = getFormatHolder();
switch (readSource(formatHolder, inputBuffer, /* formatRequired= */ false)) {
switch (readSource(formatHolder, inputBuffer, /* readFlags= */ 0)) {
case C.RESULT_NOTHING_READ:
return false;
case C.RESULT_FORMAT_READ:

View file

@ -23,6 +23,9 @@ import static com.google.android.exoplayer2.decoder.DecoderReuseEvaluation.REUSE
import static com.google.android.exoplayer2.decoder.DecoderReuseEvaluation.REUSE_RESULT_YES_WITHOUT_RECONFIGURATION;
import static com.google.android.exoplayer2.decoder.DecoderReuseEvaluation.REUSE_RESULT_YES_WITH_FLUSH;
import static com.google.android.exoplayer2.decoder.DecoderReuseEvaluation.REUSE_RESULT_YES_WITH_RECONFIGURATION;
import static com.google.android.exoplayer2.source.SampleStream.FLAG_OMIT_SAMPLE_DATA;
import static com.google.android.exoplayer2.source.SampleStream.FLAG_PEEK;
import static com.google.android.exoplayer2.source.SampleStream.FLAG_REQUIRE_FORMAT;
import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
import static com.google.android.exoplayer2.util.Assertions.checkState;
import static java.lang.Math.max;
@ -57,6 +60,8 @@ import com.google.android.exoplayer2.drm.FrameworkMediaCrypto;
import com.google.android.exoplayer2.mediacodec.MediaCodecUtil.DecoderQueryException;
import com.google.android.exoplayer2.source.MediaPeriod;
import com.google.android.exoplayer2.source.SampleStream;
import com.google.android.exoplayer2.source.SampleStream.ReadDataResult;
import com.google.android.exoplayer2.source.SampleStream.ReadFlags;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Log;
import com.google.android.exoplayer2.util.MimeTypes;
@ -294,7 +299,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
private final MediaCodecSelector mediaCodecSelector;
private final boolean enableDecoderFallback;
private final float assumedMinimumCodecOperatingRate;
private final DecoderInputBuffer flagsOnlyBuffer;
private final DecoderInputBuffer noDataBuffer;
private final DecoderInputBuffer buffer;
private final DecoderInputBuffer bypassSampleBuffer;
private final BatchBuffer bypassBatchBuffer;
@ -388,7 +393,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
this.mediaCodecSelector = checkNotNull(mediaCodecSelector);
this.enableDecoderFallback = enableDecoderFallback;
this.assumedMinimumCodecOperatingRate = assumedMinimumCodecOperatingRate;
flagsOnlyBuffer = DecoderInputBuffer.newFlagsOnlyInstance();
noDataBuffer = DecoderInputBuffer.newNoDataInstance();
buffer = new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_DISABLED);
bypassSampleBuffer = new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_DIRECT);
bypassBatchBuffer = new BatchBuffer();
@ -816,7 +821,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
renderToEndOfStream();
return;
}
if (inputFormat == null && !readToFlagsOnlyBuffer(/* requireFormat= */ true)) {
if (inputFormat == null && !readSourceOmittingSampleData(FLAG_REQUIRE_FORMAT)) {
// We still don't have a format and can't make progress without one.
return;
}
@ -837,9 +842,9 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
decoderCounters.skippedInputBufferCount += skipSource(positionUs);
// We need to read any format changes despite not having a codec so that drmSession can be
// updated, and so that we have the most recent format should the codec be initialized. We
// may also reach the end of the stream. Note that readSource will not read a sample into a
// flags-only buffer.
readToFlagsOnlyBuffer(/* requireFormat= */ false);
// may also reach the end of the stream. FLAG_PEEK is used because we don't want to advance
// the source further than skipSource has already done.
readSourceOmittingSampleData(FLAG_PEEK);
}
decoderCounters.ensureUpdated();
} catch (IllegalStateException e) {
@ -972,16 +977,24 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
return new MediaCodecDecoderException(cause, codecInfo);
}
/** Reads into {@link #flagsOnlyBuffer} and returns whether a {@link Format} was read. */
private boolean readToFlagsOnlyBuffer(boolean requireFormat) throws ExoPlaybackException {
/**
* Reads from the source when sample data is not required. If a format or an end of stream buffer
* is read, it will be handled before the call returns.
*
* @param readFlags Additional {@link ReadFlags}. {@link SampleStream#FLAG_OMIT_SAMPLE_DATA} is
* added internally, and so does not need to be passed.
* @return Whether a format was read and processed.
*/
private boolean readSourceOmittingSampleData(@SampleStream.ReadFlags int readFlags)
throws ExoPlaybackException {
FormatHolder formatHolder = getFormatHolder();
flagsOnlyBuffer.clear();
@SampleStream.ReadDataResult
int result = readSource(formatHolder, flagsOnlyBuffer, requireFormat);
noDataBuffer.clear();
@ReadDataResult
int result = readSource(formatHolder, noDataBuffer, readFlags | FLAG_OMIT_SAMPLE_DATA);
if (result == C.RESULT_FORMAT_READ) {
onInputFormatChanged(formatHolder);
return true;
} else if (result == C.RESULT_BUFFER_READ && flagsOnlyBuffer.isEndOfStream()) {
} else if (result == C.RESULT_BUFFER_READ && noDataBuffer.isEndOfStream()) {
inputStreamEnded = true;
processEndOfStream();
}
@ -1248,8 +1261,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
int adaptiveReconfigurationBytes = buffer.data.position();
FormatHolder formatHolder = getFormatHolder();
@SampleStream.ReadDataResult
int result = readSource(formatHolder, buffer, /* formatRequired= */ false);
@ReadDataResult int result = readSource(formatHolder, buffer, /* readFlags= */ 0);
if (hasReadStreamToEnd()) {
// Notify output queue of the last buffer's timestamp.
@ -2264,8 +2276,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
bypassSampleBuffer.clear();
while (true) {
bypassSampleBuffer.clear();
@SampleStream.ReadDataResult
int result = readSource(formatHolder, bypassSampleBuffer, /* formatRequired= */ false);
@ReadDataResult int result = readSource(formatHolder, bypassSampleBuffer, /* readFlags= */ 0);
switch (result) {
case C.RESULT_FORMAT_READ:
onInputFormatChanged(formatHolder);

View file

@ -27,7 +27,7 @@ import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.FormatHolder;
import com.google.android.exoplayer2.RendererCapabilities;
import com.google.android.exoplayer2.source.SampleStream;
import com.google.android.exoplayer2.source.SampleStream.ReadDataResult;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util;
import java.util.ArrayList;
@ -125,7 +125,7 @@ public final class MetadataRenderer extends BaseRenderer implements Callback {
if (!inputStreamEnded && pendingMetadataCount < MAX_PENDING_METADATA_COUNT) {
buffer.clear();
FormatHolder formatHolder = getFormatHolder();
@SampleStream.ReadDataResult int result = readSource(formatHolder, buffer, false);
@ReadDataResult int result = readSource(formatHolder, buffer, /* readFlags= */ 0);
if (result == C.RESULT_BUFFER_READ) {
if (buffer.isEndOfStream()) {
inputStreamEnded = true;

View file

@ -299,7 +299,7 @@ public final class ClippingMediaPeriod implements MediaPeriod, MediaPeriod.Callb
@Override
public int readData(
FormatHolder formatHolder, DecoderInputBuffer buffer, boolean requireFormat) {
FormatHolder formatHolder, DecoderInputBuffer buffer, @ReadFlags int readFlags) {
if (isPendingInitialDiscontinuity()) {
return C.RESULT_NOTHING_READ;
}
@ -307,7 +307,7 @@ public final class ClippingMediaPeriod implements MediaPeriod, MediaPeriod.Callb
buffer.setFlags(C.BUFFER_FLAG_END_OF_STREAM);
return C.RESULT_BUFFER_READ;
}
@ReadDataResult int result = childStream.readData(formatHolder, buffer, requireFormat);
@ReadDataResult int result = childStream.readData(formatHolder, buffer, readFlags);
if (result == C.RESULT_FORMAT_READ) {
Format format = Assertions.checkNotNull(formatHolder.format);
if (format.encoderDelay != 0 || format.encoderPadding != 0) {

View file

@ -35,8 +35,8 @@ public final class EmptySampleStream implements SampleStream {
}
@Override
public int readData(FormatHolder formatHolder, DecoderInputBuffer buffer,
boolean formatRequired) {
public int readData(
FormatHolder formatHolder, DecoderInputBuffer buffer, @ReadFlags int readFlags) {
buffer.setFlags(C.BUFFER_FLAG_END_OF_STREAM);
return C.RESULT_BUFFER_READ;
}

View file

@ -440,8 +440,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
@Override
public int readData(
FormatHolder formatHolder, DecoderInputBuffer buffer, boolean formatRequired) {
int readResult = sampleStream.readData(formatHolder, buffer, formatRequired);
FormatHolder formatHolder, DecoderInputBuffer buffer, @ReadFlags int readFlags) {
int readResult = sampleStream.readData(formatHolder, buffer, readFlags);
if (readResult == C.RESULT_BUFFER_READ) {
buffer.timeUs = max(0, buffer.timeUs + timeOffsetUs);
}

View file

@ -39,6 +39,7 @@ import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.metadata.Metadata;
import com.google.android.exoplayer2.metadata.icy.IcyHeaders;
import com.google.android.exoplayer2.source.SampleQueue.UpstreamFormatChangedListener;
import com.google.android.exoplayer2.source.SampleStream.ReadFlags;
import com.google.android.exoplayer2.trackselection.ExoTrackSelection;
import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.upstream.DataSource;
@ -478,13 +479,13 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
int sampleQueueIndex,
FormatHolder formatHolder,
DecoderInputBuffer buffer,
boolean formatRequired) {
@ReadFlags int readFlags) {
if (suppressRead()) {
return C.RESULT_NOTHING_READ;
}
maybeNotifyDownstreamFormat(sampleQueueIndex);
int result =
sampleQueues[sampleQueueIndex].read(formatHolder, buffer, formatRequired, loadingFinished);
sampleQueues[sampleQueueIndex].read(formatHolder, buffer, readFlags, loadingFinished);
if (result == C.RESULT_NOTHING_READ) {
maybeStartDeferredRetry(sampleQueueIndex);
}
@ -947,9 +948,9 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
}
@Override
public int readData(FormatHolder formatHolder, DecoderInputBuffer buffer,
boolean formatRequired) {
return ProgressiveMediaPeriod.this.readData(track, formatHolder, buffer, formatRequired);
public int readData(
FormatHolder formatHolder, DecoderInputBuffer buffer, @ReadFlags int readFlags) {
return ProgressiveMediaPeriod.this.readData(track, formatHolder, buffer, readFlags);
}
@Override

View file

@ -15,6 +15,9 @@
*/
package com.google.android.exoplayer2.source;
import static com.google.android.exoplayer2.source.SampleStream.FLAG_OMIT_SAMPLE_DATA;
import static com.google.android.exoplayer2.source.SampleStream.FLAG_PEEK;
import static com.google.android.exoplayer2.source.SampleStream.FLAG_REQUIRE_FORMAT;
import static com.google.android.exoplayer2.util.Assertions.checkArgument;
import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
import static java.lang.Math.max;
@ -36,6 +39,7 @@ import com.google.android.exoplayer2.drm.DrmSessionEventListener;
import com.google.android.exoplayer2.drm.DrmSessionManager;
import com.google.android.exoplayer2.drm.DrmSessionManager.DrmSessionReference;
import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.source.SampleStream.ReadFlags;
import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.upstream.DataReader;
import com.google.android.exoplayer2.util.Assertions;
@ -380,20 +384,6 @@ public class SampleQueue implements TrackOutput {
return mayReadSample(getRelativeIndex(readPosition));
}
/** Equivalent to {@link #read}, except it never advances the read position. */
public final int peek(
FormatHolder formatHolder,
DecoderInputBuffer buffer,
boolean formatRequired,
boolean loadingFinished) {
int result =
peekSampleMetadata(formatHolder, buffer, formatRequired, loadingFinished, extrasHolder);
if (result == C.RESULT_BUFFER_READ && !buffer.isEndOfStream() && !buffer.isFlagsOnly()) {
sampleDataQueue.peekToBuffer(buffer, extrasHolder);
}
return result;
}
/**
* Attempts to read from the queue.
*
@ -403,18 +393,12 @@ public class SampleQueue implements TrackOutput {
* @param formatHolder A {@link FormatHolder} to populate in the case of reading a format.
* @param buffer A {@link DecoderInputBuffer} to populate in the case of reading a sample or the
* end of the stream. If the end of the stream has been reached, the {@link
* C#BUFFER_FLAG_END_OF_STREAM} flag will be set on the buffer. If a {@link
* DecoderInputBuffer#isFlagsOnly() flags-only} buffer is passed and a sample is read, then
* only the buffer {@link DecoderInputBuffer#timeUs timestamp} and flags will be populated,
* and the read position will not be advanced.
* @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.
* C#BUFFER_FLAG_END_OF_STREAM} flag will be set on the buffer.
* @param readFlags Flags controlling the behavior of this read operation.
* @param loadingFinished True if an empty queue should be considered the end of the stream.
* @return The result, which can be {@link C#RESULT_NOTHING_READ}, {@link C#RESULT_FORMAT_READ} or
* {@link C#RESULT_BUFFER_READ}.
* @throws InsufficientCapacityException If the {@code buffer} is not a {@link
* DecoderInputBuffer#isFlagsOnly() flags-only} buffer and has insufficient capacity to hold
* @throws InsufficientCapacityException If the {@code buffer} has insufficient capacity to hold
* the data of a sample being read. The buffer {@link DecoderInputBuffer#timeUs timestamp} and
* flags are populated if this exception is thrown, but the read position is not advanced.
*/
@ -422,13 +406,27 @@ public class SampleQueue implements TrackOutput {
public int read(
FormatHolder formatHolder,
DecoderInputBuffer buffer,
boolean formatRequired,
@ReadFlags int readFlags,
boolean loadingFinished) {
int result =
peekSampleMetadata(formatHolder, buffer, formatRequired, loadingFinished, extrasHolder);
if (result == C.RESULT_BUFFER_READ && !buffer.isEndOfStream() && !buffer.isFlagsOnly()) {
sampleDataQueue.readToBuffer(buffer, extrasHolder);
readPosition++;
peekSampleMetadata(
formatHolder,
buffer,
/* formatRequired= */ (readFlags & FLAG_REQUIRE_FORMAT) != 0,
loadingFinished,
extrasHolder);
if (result == C.RESULT_BUFFER_READ && !buffer.isEndOfStream()) {
boolean peek = (readFlags & FLAG_PEEK) != 0;
if ((readFlags & FLAG_OMIT_SAMPLE_DATA) == 0) {
if (peek) {
sampleDataQueue.peekToBuffer(buffer, extrasHolder);
} else {
sampleDataQueue.readToBuffer(buffer, extrasHolder);
}
}
if (!peek) {
readPosition++;
}
}
return result;
}

View file

@ -30,7 +30,43 @@ import java.lang.annotation.RetentionPolicy;
*/
public interface SampleStream {
/** Return values of {@link #readData(FormatHolder, DecoderInputBuffer, boolean)}. */
/**
* Flags that can be specified when calling {@link #readData}. Possible flag values are {@link
* #FLAG_PEEK}, {@link #FLAG_REQUIRE_FORMAT} and {@link #FLAG_OMIT_SAMPLE_DATA}.
*/
@Documented
@Retention(RetentionPolicy.SOURCE)
@IntDef(
flag = true,
value = {FLAG_PEEK, FLAG_REQUIRE_FORMAT, FLAG_OMIT_SAMPLE_DATA})
@interface ReadFlags {}
/** Specifies that the read position should not be advanced if a sample buffer is read. */
int FLAG_PEEK = 1;
/**
* Specifies that if a sample buffer would normally be read next, the format of the stream should
* be read instead. In detail, the effect of this flag is as follows:
*
* <ul>
* <li>If a sample buffer would be read were the flag not set, then the stream format will be
* read instead.
* <li>If nothing would be read were the flag not set, then the stream format will be read if
* it's known. If the stream format is not known then behavior is unchanged.
* <li>If an end of stream buffer would be read were the flag not set, then behavior is
* unchanged.
* </ul>
*/
int FLAG_REQUIRE_FORMAT = 1 << 1;
/**
* Specifies that {@link DecoderInputBuffer#data}, {@link DecoderInputBuffer#supplementalData} and
* {@link DecoderInputBuffer#cryptoInfo} should not be populated when reading a sample buffer.
*
* <p>This flag is useful for efficiently reading or (when combined with {@link #FLAG_PEEK})
* peeking sample metadata. It can also be used for efficiency by a caller wishing to skip a
* sample buffer.
*/
int FLAG_OMIT_SAMPLE_DATA = 1 << 2;
/** Return values of {@link #readData}. */
@Documented
@Retention(RetentionPolicy.SOURCE)
@IntDef({C.RESULT_NOTHING_READ, C.RESULT_FORMAT_READ, C.RESULT_BUFFER_READ})
@ -38,10 +74,9 @@ public interface SampleStream {
/**
* Returns whether data is available to be read.
* <p>
* Note: If the stream has ended then a buffer with the end of stream flag can always be read from
* {@link #readData(FormatHolder, DecoderInputBuffer, boolean)}. Hence an ended stream is always
* ready.
*
* <p>Note: If the stream has ended then a buffer with the end of stream flag can always be read
* from {@link #readData}. Hence an ended stream is always ready.
*
* @return Whether data is available to be read.
*/
@ -66,21 +101,15 @@ public interface SampleStream {
* @param formatHolder A {@link FormatHolder} to populate in the case of reading a format.
* @param buffer A {@link DecoderInputBuffer} to populate in the case of reading a sample or the
* end of the stream. If the end of the stream has been reached, the {@link
* C#BUFFER_FLAG_END_OF_STREAM} flag will be set on the buffer. If a {@link
* DecoderInputBuffer#isFlagsOnly() flags-only} buffer is passed and a sample is read, then
* only the buffer {@link DecoderInputBuffer#timeUs timestamp} and flags will be populated,
* and the read position will not be advanced.
* @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.
* @return The status of read, one of {@link ReadDataResult}.
* @throws InsufficientCapacityException If the {@code buffer} is not a {@link
* DecoderInputBuffer#isFlagsOnly() flags-only} buffer and has insufficient capacity to hold
* C#BUFFER_FLAG_END_OF_STREAM} flag will be set on the buffer.
* @param readFlags Flags controlling the behavior of this read operation.
* @return The {@link ReadDataResult result} of the read operation.
* @throws InsufficientCapacityException If the {@code buffer} has insufficient capacity to hold
* the data of a sample being read. The buffer {@link DecoderInputBuffer#timeUs timestamp} and
* flags are populated if this exception is thrown, but the read position is not advanced.
*/
@ReadDataResult
int readData(FormatHolder formatHolder, DecoderInputBuffer buffer, boolean formatRequired);
int readData(FormatHolder formatHolder, DecoderInputBuffer buffer, @ReadFlags int readFlags);
/**
* Attempts to skip to the keyframe before the specified position, or to the end of the stream if
@ -90,5 +119,4 @@ public interface SampleStream {
* @return The number of samples that were skipped.
*/
int skipData(long positionUs);
}

View file

@ -292,8 +292,8 @@ public final class SilenceMediaSource extends BaseMediaSource {
@Override
public int readData(
FormatHolder formatHolder, DecoderInputBuffer buffer, boolean formatRequired) {
if (!sentFormat || formatRequired) {
FormatHolder formatHolder, DecoderInputBuffer buffer, @ReadFlags int readFlags) {
if (!sentFormat || (readFlags & FLAG_REQUIRE_FORMAT) != 0) {
formatHolder.format = FORMAT;
sentFormat = true;
return C.RESULT_FORMAT_READ;
@ -307,14 +307,14 @@ public final class SilenceMediaSource extends BaseMediaSource {
buffer.timeUs = getAudioPositionUs(positionBytes);
buffer.addFlag(C.BUFFER_FLAG_KEY_FRAME);
if (buffer.isFlagsOnly()) {
return C.RESULT_BUFFER_READ;
}
int bytesToWrite = (int) min(SILENCE_SAMPLE.length, bytesRemaining);
buffer.ensureSpaceForWrite(bytesToWrite);
buffer.data.put(SILENCE_SAMPLE, /* offset= */ 0, bytesToWrite);
positionBytes += bytesToWrite;
if ((readFlags & FLAG_OMIT_SAMPLE_DATA) == 0) {
buffer.ensureSpaceForWrite(bytesToWrite);
buffer.data.put(SILENCE_SAMPLE, /* offset= */ 0, bytesToWrite);
}
if ((readFlags & FLAG_PEEK) == 0) {
positionBytes += bytesToWrite;
}
return C.RESULT_BUFFER_READ;
}

View file

@ -349,31 +349,39 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
@Override
public int readData(
FormatHolder formatHolder, DecoderInputBuffer buffer, boolean requireFormat) {
FormatHolder formatHolder, DecoderInputBuffer buffer, @ReadFlags int readFlags) {
maybeNotifyDownstreamFormat();
if (streamState == STREAM_STATE_END_OF_STREAM) {
buffer.addFlag(C.BUFFER_FLAG_END_OF_STREAM);
return C.RESULT_BUFFER_READ;
} else if (requireFormat || streamState == STREAM_STATE_SEND_FORMAT) {
}
if ((readFlags & FLAG_REQUIRE_FORMAT) != 0 || streamState == STREAM_STATE_SEND_FORMAT) {
formatHolder.format = format;
streamState = STREAM_STATE_SEND_SAMPLE;
return C.RESULT_FORMAT_READ;
} else if (loadingFinished) {
if (sampleData != null) {
buffer.addFlag(C.BUFFER_FLAG_KEY_FRAME);
buffer.timeUs = 0;
if (buffer.isFlagsOnly()) {
return C.RESULT_BUFFER_READ;
}
buffer.ensureSpaceForWrite(sampleSize);
buffer.data.put(sampleData, 0, sampleSize);
} else {
buffer.addFlag(C.BUFFER_FLAG_END_OF_STREAM);
}
}
if (!loadingFinished) {
return C.RESULT_NOTHING_READ;
}
if (sampleData == null) {
buffer.addFlag(C.BUFFER_FLAG_END_OF_STREAM);
streamState = STREAM_STATE_END_OF_STREAM;
return C.RESULT_BUFFER_READ;
}
return C.RESULT_NOTHING_READ;
buffer.addFlag(C.BUFFER_FLAG_KEY_FRAME);
buffer.timeUs = 0;
if ((readFlags & FLAG_OMIT_SAMPLE_DATA) == 0) {
buffer.ensureSpaceForWrite(sampleSize);
buffer.data.put(sampleData, 0, sampleSize);
}
if ((readFlags & FLAG_PEEK) == 0) {
streamState = STREAM_STATE_END_OF_STREAM;
}
return C.RESULT_BUFFER_READ;
}
@Override

View file

@ -381,8 +381,8 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S
}
@Override
public int readData(FormatHolder formatHolder, DecoderInputBuffer buffer,
boolean formatRequired) {
public int readData(
FormatHolder formatHolder, DecoderInputBuffer buffer, @ReadFlags int readFlags) {
if (isPendingReset()) {
return C.RESULT_NOTHING_READ;
}
@ -395,7 +395,7 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S
}
maybeNotifyPrimaryTrackFormatChanged();
return primarySampleQueue.read(formatHolder, buffer, formatRequired, loadingFinished);
return primarySampleQueue.read(formatHolder, buffer, readFlags, loadingFinished);
}
@Override
@ -862,8 +862,8 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S
}
@Override
public int readData(FormatHolder formatHolder, DecoderInputBuffer buffer,
boolean formatRequired) {
public int readData(
FormatHolder formatHolder, DecoderInputBuffer buffer, @ReadFlags int readFlags) {
if (isPendingReset()) {
return C.RESULT_NOTHING_READ;
}
@ -875,7 +875,7 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S
return C.RESULT_NOTHING_READ;
}
maybeNotifyDownstreamFormat();
return sampleQueue.read(formatHolder, buffer, formatRequired, loadingFinished);
return sampleQueue.read(formatHolder, buffer, readFlags, loadingFinished);
}
public void release() {

View file

@ -29,7 +29,7 @@ import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.FormatHolder;
import com.google.android.exoplayer2.RendererCapabilities;
import com.google.android.exoplayer2.source.SampleStream;
import com.google.android.exoplayer2.source.SampleStream.ReadDataResult;
import com.google.android.exoplayer2.util.Log;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.Util;
@ -273,7 +273,7 @@ public final class TextRenderer extends BaseRenderer implements Callback {
return;
}
// Try and read the next subtitle from the source.
@SampleStream.ReadDataResult int result = readSource(formatHolder, nextInputBuffer, false);
@ReadDataResult int result = readSource(formatHolder, nextInputBuffer, /* readFlags= */ 0);
if (result == C.RESULT_BUFFER_READ) {
if (nextInputBuffer.isEndOfStream()) {
inputStreamEnded = true;

View file

@ -18,6 +18,7 @@ package com.google.android.exoplayer2.video;
import static com.google.android.exoplayer2.decoder.DecoderReuseEvaluation.DISCARD_REASON_DRM_SESSION_CHANGED;
import static com.google.android.exoplayer2.decoder.DecoderReuseEvaluation.DISCARD_REASON_REUSE_NOT_IMPLEMENTED;
import static com.google.android.exoplayer2.decoder.DecoderReuseEvaluation.REUSE_RESULT_NO;
import static com.google.android.exoplayer2.source.SampleStream.FLAG_REQUIRE_FORMAT;
import static java.lang.Math.max;
import android.os.Handler;
@ -41,7 +42,7 @@ import com.google.android.exoplayer2.decoder.DecoderReuseEvaluation;
import com.google.android.exoplayer2.drm.DrmSession;
import com.google.android.exoplayer2.drm.DrmSession.DrmSessionException;
import com.google.android.exoplayer2.drm.ExoMediaCrypto;
import com.google.android.exoplayer2.source.SampleStream;
import com.google.android.exoplayer2.source.SampleStream.ReadDataResult;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Log;
import com.google.android.exoplayer2.util.TimedValueQueue;
@ -165,7 +166,7 @@ public abstract class DecoderVideoRenderer extends BaseRenderer {
joiningDeadlineMs = C.TIME_UNSET;
clearReportedVideoSize();
formatQueue = new TimedValueQueue<>();
flagsOnlyBuffer = DecoderInputBuffer.newFlagsOnlyInstance();
flagsOnlyBuffer = DecoderInputBuffer.newNoDataInstance();
eventDispatcher = new EventDispatcher(eventHandler, eventListener);
decoderReinitializationState = REINITIALIZATION_STATE_NONE;
outputMode = C.VIDEO_OUTPUT_MODE_NONE;
@ -183,7 +184,7 @@ public abstract class DecoderVideoRenderer extends BaseRenderer {
// We don't have a format yet, so try and read one.
FormatHolder formatHolder = getFormatHolder();
flagsOnlyBuffer.clear();
@SampleStream.ReadDataResult int result = readSource(formatHolder, flagsOnlyBuffer, true);
@ReadDataResult int result = readSource(formatHolder, flagsOnlyBuffer, FLAG_REQUIRE_FORMAT);
if (result == C.RESULT_FORMAT_READ) {
onInputFormatChanged(formatHolder);
} else if (result == C.RESULT_BUFFER_READ) {
@ -745,7 +746,7 @@ public abstract class DecoderVideoRenderer extends BaseRenderer {
}
FormatHolder formatHolder = getFormatHolder();
switch (readSource(formatHolder, inputBuffer, /* formatRequired= */ false)) {
switch (readSource(formatHolder, inputBuffer, /* readFlags= */ 0)) {
case C.RESULT_NOTHING_READ:
return false;
case C.RESULT_FORMAT_READ:

View file

@ -24,7 +24,7 @@ import com.google.android.exoplayer2.FormatHolder;
import com.google.android.exoplayer2.Renderer;
import com.google.android.exoplayer2.RendererCapabilities;
import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
import com.google.android.exoplayer2.source.SampleStream;
import com.google.android.exoplayer2.source.SampleStream.ReadDataResult;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.ParsableByteArray;
import com.google.android.exoplayer2.util.Util;
@ -94,8 +94,7 @@ public final class CameraMotionRenderer extends BaseRenderer {
while (!hasReadStreamToEnd() && lastTimestampUs < positionUs + SAMPLE_WINDOW_DURATION_US) {
buffer.clear();
FormatHolder formatHolder = getFormatHolder();
@SampleStream.ReadDataResult
int result = readSource(formatHolder, buffer, /* formatRequired= */ false);
@ReadDataResult int result = readSource(formatHolder, buffer, /* readFlags= */ 0);
if (result != C.RESULT_BUFFER_READ || buffer.isEndOfStream()) {
return;
}

View file

@ -15,6 +15,7 @@
*/
package com.google.android.exoplayer2.source;
import static com.google.android.exoplayer2.source.SampleStream.FLAG_REQUIRE_FORMAT;
import static com.google.android.exoplayer2.testutil.FakeSampleStream.FakeSampleStreamItem.END_OF_STREAM_ITEM;
import static com.google.android.exoplayer2.testutil.FakeSampleStream.FakeSampleStreamItem.oneByteSample;
import static com.google.common.truth.Truth.assertThat;
@ -93,11 +94,11 @@ public final class MergingMediaPeriodTest {
FormatHolder formatHolder = new FormatHolder();
DecoderInputBuffer inputBuffer =
new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_NORMAL);
assertThat(streams[1].readData(formatHolder, inputBuffer, /* formatRequired= */ true))
assertThat(streams[1].readData(formatHolder, inputBuffer, FLAG_REQUIRE_FORMAT))
.isEqualTo(C.RESULT_FORMAT_READ);
assertThat(formatHolder.format).isEqualTo(childFormat12);
assertThat(streams[2].readData(formatHolder, inputBuffer, /* formatRequired= */ true))
assertThat(streams[2].readData(formatHolder, inputBuffer, FLAG_REQUIRE_FORMAT))
.isEqualTo(C.RESULT_FORMAT_READ);
assertThat(formatHolder.format).isEqualTo(childFormat21);
}
@ -134,20 +135,20 @@ public final class MergingMediaPeriodTest {
FormatHolder formatHolder = new FormatHolder();
DecoderInputBuffer inputBuffer =
new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_NORMAL);
streams[0].readData(formatHolder, inputBuffer, /* formatRequired= */ true);
streams[1].readData(formatHolder, inputBuffer, /* formatRequired= */ true);
streams[0].readData(formatHolder, inputBuffer, FLAG_REQUIRE_FORMAT);
streams[1].readData(formatHolder, inputBuffer, FLAG_REQUIRE_FORMAT);
FakeMediaPeriodWithSelectTracksPosition childMediaPeriod1 =
(FakeMediaPeriodWithSelectTracksPosition) mergingMediaPeriod.getChildPeriod(0);
assertThat(childMediaPeriod1.selectTracksPositionUs).isEqualTo(0);
assertThat(streams[0].readData(formatHolder, inputBuffer, /* formatRequired= */ false))
assertThat(streams[0].readData(formatHolder, inputBuffer, /* readFlags= */ 0))
.isEqualTo(C.RESULT_BUFFER_READ);
assertThat(inputBuffer.timeUs).isEqualTo(123_000L);
FakeMediaPeriodWithSelectTracksPosition childMediaPeriod2 =
(FakeMediaPeriodWithSelectTracksPosition) mergingMediaPeriod.getChildPeriod(1);
assertThat(childMediaPeriod2.selectTracksPositionUs).isEqualTo(3000L);
assertThat(streams[1].readData(formatHolder, inputBuffer, /* formatRequired= */ false))
assertThat(streams[1].readData(formatHolder, inputBuffer, /* readFlags= */ 0))
.isEqualTo(C.RESULT_BUFFER_READ);
assertThat(inputBuffer.timeUs).isEqualTo(456_000 - 3000);
}

View file

@ -20,6 +20,8 @@ import static com.google.android.exoplayer2.C.BUFFER_FLAG_KEY_FRAME;
import static com.google.android.exoplayer2.C.RESULT_BUFFER_READ;
import static com.google.android.exoplayer2.C.RESULT_FORMAT_READ;
import static com.google.android.exoplayer2.C.RESULT_NOTHING_READ;
import static com.google.android.exoplayer2.source.SampleStream.FLAG_OMIT_SAMPLE_DATA;
import static com.google.android.exoplayer2.source.SampleStream.FLAG_PEEK;
import static com.google.common.truth.Truth.assertThat;
import static java.lang.Long.MAX_VALUE;
import static java.lang.Long.MIN_VALUE;
@ -208,14 +210,11 @@ public final class SampleQueueTest {
sampleQueue.format(FORMAT_1);
clearFormatHolderAndInputBuffer();
int result =
sampleQueue.peek(
formatHolder, inputBuffer, /* formatRequired= */ false, /* loadingFinished= */ false);
sampleQueue.read(formatHolder, inputBuffer, FLAG_PEEK, /* loadingFinished= */ false);
assertThat(result).isEqualTo(RESULT_FORMAT_READ);
// formatHolder should be populated.
assertThat(formatHolder.format).isEqualTo(FORMAT_1);
result =
sampleQueue.peek(
formatHolder, inputBuffer, /* formatRequired= */ false, /* loadingFinished= */ false);
result = sampleQueue.read(formatHolder, inputBuffer, FLAG_PEEK, /* loadingFinished= */ false);
assertThat(result).isEqualTo(RESULT_NOTHING_READ);
}
@ -454,7 +453,7 @@ public final class SampleQueueTest {
int result =
sampleQueue.read(
formatHolder, inputBuffer, /* formatRequired= */ false, /* loadingFinished= */ false);
formatHolder, inputBuffer, /* readFlags= */ 0, /* loadingFinished= */ false);
assertThat(result).isEqualTo(RESULT_FORMAT_READ);
assertThat(formatHolder.drmSession).isSameInstanceAs(mockDrmSession);
assertReadEncryptedSample(/* sampleIndex= */ 0);
@ -463,13 +462,13 @@ public final class SampleQueueTest {
assertThat(formatHolder.drmSession).isNull();
result =
sampleQueue.read(
formatHolder, inputBuffer, /* formatRequired= */ false, /* loadingFinished= */ false);
formatHolder, inputBuffer, /* readFlags= */ 0, /* loadingFinished= */ false);
assertThat(result).isEqualTo(RESULT_FORMAT_READ);
assertThat(formatHolder.drmSession).isNull();
assertReadEncryptedSample(/* sampleIndex= */ 2);
result =
sampleQueue.read(
formatHolder, inputBuffer, /* formatRequired= */ false, /* loadingFinished= */ false);
formatHolder, inputBuffer, /* readFlags= */ 0, /* loadingFinished= */ false);
assertThat(result).isEqualTo(RESULT_FORMAT_READ);
assertThat(formatHolder.drmSession).isSameInstanceAs(mockDrmSession);
}
@ -484,7 +483,7 @@ public final class SampleQueueTest {
int result =
sampleQueue.read(
formatHolder, inputBuffer, /* formatRequired= */ false, /* loadingFinished= */ false);
formatHolder, inputBuffer, /* readFlags= */ 0, /* loadingFinished= */ false);
assertThat(result).isEqualTo(RESULT_FORMAT_READ);
assertThat(formatHolder.drmSession).isSameInstanceAs(mockDrmSession);
assertReadEncryptedSample(/* sampleIndex= */ 0);
@ -493,13 +492,13 @@ public final class SampleQueueTest {
assertThat(formatHolder.drmSession).isNull();
result =
sampleQueue.read(
formatHolder, inputBuffer, /* formatRequired= */ false, /* loadingFinished= */ false);
formatHolder, inputBuffer, /* readFlags= */ 0, /* loadingFinished= */ false);
assertThat(result).isEqualTo(RESULT_FORMAT_READ);
assertThat(formatHolder.drmSession).isSameInstanceAs(mockPlaceholderDrmSession);
assertReadEncryptedSample(/* sampleIndex= */ 2);
result =
sampleQueue.read(
formatHolder, inputBuffer, /* formatRequired= */ false, /* loadingFinished= */ false);
formatHolder, inputBuffer, /* readFlags= */ 0, /* loadingFinished= */ false);
assertThat(result).isEqualTo(RESULT_FORMAT_READ);
assertThat(formatHolder.drmSession).isSameInstanceAs(mockDrmSession);
assertReadEncryptedSample(/* sampleIndex= */ 3);
@ -527,7 +526,7 @@ public final class SampleQueueTest {
int result =
sampleQueue.read(
formatHolder, inputBuffer, /* formatRequired= */ false, /* loadingFinished= */ false);
formatHolder, inputBuffer, /* readFlags= */ 0, /* loadingFinished= */ false);
assertThat(result).isEqualTo(RESULT_FORMAT_READ);
// Fill cryptoInfo.iv with non-zero data. When the 8 byte initialization vector is written into
@ -537,7 +536,7 @@ public final class SampleQueueTest {
result =
sampleQueue.read(
formatHolder, inputBuffer, /* formatRequired= */ false, /* loadingFinished= */ false);
formatHolder, inputBuffer, /* readFlags= */ 0, /* loadingFinished= */ false);
assertThat(result).isEqualTo(RESULT_BUFFER_READ);
// Assert cryptoInfo.iv contains the 8-byte initialization vector and that the trailing 8 bytes
@ -1558,7 +1557,11 @@ public final class SampleQueueTest {
private void assertReadNothing(boolean formatRequired) {
clearFormatHolderAndInputBuffer();
int result =
sampleQueue.read(formatHolder, inputBuffer, formatRequired, /* loadingFinished= */ false);
sampleQueue.read(
formatHolder,
inputBuffer,
formatRequired ? SampleStream.FLAG_REQUIRE_FORMAT : 0,
/* loadingFinished= */ false);
assertThat(result).isEqualTo(RESULT_NOTHING_READ);
// formatHolder should not be populated.
assertThat(formatHolder.format).isNull();
@ -1576,7 +1579,11 @@ public final class SampleQueueTest {
private void assertReadEndOfStream(boolean formatRequired) {
clearFormatHolderAndInputBuffer();
int result =
sampleQueue.read(formatHolder, inputBuffer, formatRequired, /* loadingFinished= */ true);
sampleQueue.read(
formatHolder,
inputBuffer,
formatRequired ? SampleStream.FLAG_REQUIRE_FORMAT : 0,
/* loadingFinished= */ true);
assertThat(result).isEqualTo(RESULT_BUFFER_READ);
// formatHolder should not be populated.
assertThat(formatHolder.format).isNull();
@ -1597,7 +1604,11 @@ public final class SampleQueueTest {
private void assertReadFormat(boolean formatRequired, Format format) {
clearFormatHolderAndInputBuffer();
int result =
sampleQueue.read(formatHolder, inputBuffer, formatRequired, /* loadingFinished= */ false);
sampleQueue.read(
formatHolder,
inputBuffer,
formatRequired ? SampleStream.FLAG_REQUIRE_FORMAT : 0,
/* loadingFinished= */ false);
assertThat(result).isEqualTo(RESULT_FORMAT_READ);
// formatHolder should be populated.
assertThat(formatHolder.format).isEqualTo(format);
@ -1641,24 +1652,51 @@ public final class SampleQueueTest {
byte[] sampleData,
int offset,
int length) {
// Check that peeks yields the expected values.
clearFormatHolderAndInputBuffer();
// Check that peek whilst omitting data yields the expected values.
formatHolder.format = null;
DecoderInputBuffer flagsOnlyBuffer = DecoderInputBuffer.newNoDataInstance();
int result =
sampleQueue.peek(
formatHolder, inputBuffer, /* formatRequired= */ false, /* loadingFinished= */ false);
assertBufferReadResult(
sampleQueue.read(
formatHolder,
flagsOnlyBuffer,
FLAG_OMIT_SAMPLE_DATA | FLAG_PEEK,
/* loadingFinished= */ false);
assertSampleBufferReadResult(
flagsOnlyBuffer, result, timeUs, isKeyFrame, isDecodeOnly, isEncrypted);
// Check that peek yields the expected values.
clearFormatHolderAndInputBuffer();
result = sampleQueue.read(formatHolder, inputBuffer, FLAG_PEEK, /* loadingFinished= */ false);
assertSampleBufferReadResult(
result, timeUs, isKeyFrame, isDecodeOnly, isEncrypted, sampleData, offset, length);
// Check that read yields the expected values.
clearFormatHolderAndInputBuffer();
result =
sampleQueue.read(
formatHolder, inputBuffer, /* formatRequired= */ false, /* loadingFinished= */ false);
assertBufferReadResult(
formatHolder, inputBuffer, /* readFlags= */ 0, /* loadingFinished= */ false);
assertSampleBufferReadResult(
result, timeUs, isKeyFrame, isDecodeOnly, isEncrypted, sampleData, offset, length);
}
private void assertBufferReadResult(
private void assertSampleBufferReadResult(
DecoderInputBuffer inputBuffer,
int result,
long timeUs,
boolean isKeyFrame,
boolean isDecodeOnly,
boolean isEncrypted) {
assertThat(result).isEqualTo(RESULT_BUFFER_READ);
// formatHolder should not be populated.
assertThat(formatHolder.format).isNull();
// inputBuffer should be populated with metadata.
assertThat(inputBuffer.timeUs).isEqualTo(timeUs);
assertThat(inputBuffer.isKeyFrame()).isEqualTo(isKeyFrame);
assertThat(inputBuffer.isDecodeOnly()).isEqualTo(isDecodeOnly);
assertThat(inputBuffer.isEncrypted()).isEqualTo(isEncrypted);
}
private void assertSampleBufferReadResult(
int result,
long timeUs,
boolean isKeyFrame,
@ -1667,14 +1705,9 @@ public final class SampleQueueTest {
byte[] sampleData,
int offset,
int length) {
assertThat(result).isEqualTo(RESULT_BUFFER_READ);
// formatHolder should not be populated.
assertThat(formatHolder.format).isNull();
// inputBuffer should be populated.
assertThat(inputBuffer.timeUs).isEqualTo(timeUs);
assertThat(inputBuffer.isKeyFrame()).isEqualTo(isKeyFrame);
assertThat(inputBuffer.isDecodeOnly()).isEqualTo(isDecodeOnly);
assertThat(inputBuffer.isEncrypted()).isEqualTo(isEncrypted);
assertSampleBufferReadResult(
inputBuffer, result, timeUs, isKeyFrame, isDecodeOnly, isEncrypted);
// inputBuffer should be populated with data.
inputBuffer.flip();
assertThat(inputBuffer.data.limit()).isEqualTo(length);
byte[] readData = new byte[length];

View file

@ -98,9 +98,9 @@ import java.io.IOException;
}
@Override
public int readData(FormatHolder formatHolder, DecoderInputBuffer buffer,
boolean formatRequired) {
if (formatRequired || !isFormatSentDownstream) {
public int readData(
FormatHolder formatHolder, DecoderInputBuffer buffer, @ReadFlags int readFlags) {
if ((readFlags & FLAG_REQUIRE_FORMAT) != 0 || !isFormatSentDownstream) {
formatHolder.format = upstreamFormat;
isFormatSentDownstream = true;
return C.RESULT_FORMAT_READ;

View file

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

View file

@ -58,7 +58,7 @@ public final class EventSampleStreamTest {
}
/**
* Tests that {@link EventSampleStream#readData(FormatHolder, DecoderInputBuffer, boolean)} will
* Tests that {@link EventSampleStream#readData(FormatHolder, DecoderInputBuffer, int)} will
* return format for the first call.
*/
@Test
@ -106,7 +106,7 @@ public final class EventSampleStreamTest {
}
/**
* Tests that {@link EventSampleStream#readData(FormatHolder, DecoderInputBuffer, boolean)} will
* Tests that {@link EventSampleStream#readData(FormatHolder, DecoderInputBuffer, int)} will
* return sample data after the first call.
*/
@Test
@ -127,8 +127,8 @@ public final class EventSampleStreamTest {
/**
* Tests that {@link EventSampleStream#skipData(long)} will skip until the given position, and the
* next {@link EventSampleStream#readData(FormatHolder, DecoderInputBuffer, boolean)} call will
* return sample data from that position.
* next {@link EventSampleStream#readData(FormatHolder, DecoderInputBuffer, int)} call will return
* sample data from that position.
*/
@Test
public void skipDataThenReadDataReturnDataFromSkippedPosition() {
@ -153,7 +153,7 @@ public final class EventSampleStreamTest {
/**
* Tests that {@link EventSampleStream#seekToUs(long)} (long)} will seek to the given position,
* and the next {@link EventSampleStream#readData(FormatHolder, DecoderInputBuffer, boolean)} call
* and the next {@link EventSampleStream#readData(FormatHolder, DecoderInputBuffer, int)} call
* will return sample data from that position.
*/
@Test
@ -179,8 +179,8 @@ public final class EventSampleStreamTest {
/**
* Tests that {@link EventSampleStream#updateEventStream(EventStream, boolean)} will update the
* underlying event stream, but keep the read timestamp, so the next {@link
* EventSampleStream#readData(FormatHolder, DecoderInputBuffer, boolean)} call will return sample
* data from after the last read sample timestamp.
* EventSampleStream#readData(FormatHolder, DecoderInputBuffer, int)} call will return sample data
* from after the last read sample timestamp.
*/
@Test
public void updateEventStreamContinueToReadAfterLastReadSamplePresentationTime() {
@ -213,8 +213,8 @@ public final class EventSampleStreamTest {
/**
* Tests that {@link EventSampleStream#updateEventStream(EventStream, boolean)} will update the
* underlying event stream, but keep the timestamp the stream has skipped to, so the next {@link
* EventSampleStream#readData(FormatHolder, DecoderInputBuffer, boolean)} call will return sample
* data from the skipped position.
* EventSampleStream#readData(FormatHolder, DecoderInputBuffer, int)} call will return sample data
* from the skipped position.
*/
@Test
public void skipDataThenUpdateStreamContinueToReadFromSkippedPosition() {
@ -246,8 +246,8 @@ public final class EventSampleStreamTest {
* Tests that {@link EventSampleStream#skipData(long)} will only skip to the point right after it
* last event. A following {@link EventSampleStream#updateEventStream(EventStream, boolean)} will
* update the underlying event stream and keep the timestamp the stream has skipped to, so the
* next {@link EventSampleStream#readData(FormatHolder, DecoderInputBuffer, boolean)} call will
* return sample data from the skipped position.
* next {@link EventSampleStream#readData(FormatHolder, DecoderInputBuffer, int)} call will return
* sample data from the skipped position.
*/
@Test
public void skipDataThenUpdateStreamContinueToReadDoNotSkippedMoreThanAvailable() {
@ -280,8 +280,8 @@ public final class EventSampleStreamTest {
/**
* Tests that {@link EventSampleStream#updateEventStream(EventStream, boolean)} will update the
* underlying event stream, but keep the timestamp the stream has seek to, so the next {@link
* EventSampleStream#readData(FormatHolder, DecoderInputBuffer, boolean)} call will return sample
* data from the seek position.
* EventSampleStream#readData(FormatHolder, DecoderInputBuffer, int)} call will return sample data
* from the seek position.
*/
@Test
public void seekToUsThenUpdateStreamContinueToReadFromSeekPosition() {
@ -312,8 +312,8 @@ public final class EventSampleStreamTest {
/**
* Tests that {@link EventSampleStream#updateEventStream(EventStream, boolean)} will update the
* underlying event stream, but keep the timestamp the stream has seek to, so the next {@link
* EventSampleStream#readData(FormatHolder, DecoderInputBuffer, boolean)} call will return sample
* data from the seek position.
* EventSampleStream#readData(FormatHolder, DecoderInputBuffer, int)} call will return sample data
* from the seek position.
*/
@Test
public void seekToThenUpdateStreamContinueToReadFromSeekPositionEvenSeekMoreThanAvailable() {
@ -343,7 +343,7 @@ public final class EventSampleStreamTest {
private int readData(EventSampleStream sampleStream) {
inputBuffer.clear();
return sampleStream.readData(formatHolder, inputBuffer, false);
return sampleStream.readData(formatHolder, inputBuffer, /* readFlags= */ 0);
}
private EventMessage newEventMessageWithId(int id) {

View file

@ -70,13 +70,14 @@ import java.io.IOException;
}
@Override
public int readData(FormatHolder formatHolder, DecoderInputBuffer buffer, boolean requireFormat) {
public int readData(
FormatHolder formatHolder, DecoderInputBuffer buffer, @ReadFlags int readFlags) {
if (sampleQueueIndex == HlsSampleStreamWrapper.SAMPLE_QUEUE_INDEX_NO_MAPPING_NON_FATAL) {
buffer.addFlag(C.BUFFER_FLAG_END_OF_STREAM);
return C.RESULT_BUFFER_READ;
}
return hasValidSampleQueueIndex()
? sampleStreamWrapper.readData(sampleQueueIndex, formatHolder, buffer, requireFormat)
? sampleStreamWrapper.readData(sampleQueueIndex, formatHolder, buffer, readFlags)
: C.RESULT_NOTHING_READ;
}

View file

@ -48,6 +48,7 @@ import com.google.android.exoplayer2.source.MediaSourceEventListener;
import com.google.android.exoplayer2.source.SampleQueue;
import com.google.android.exoplayer2.source.SampleQueue.UpstreamFormatChangedListener;
import com.google.android.exoplayer2.source.SampleStream;
import com.google.android.exoplayer2.source.SampleStream.ReadFlags;
import com.google.android.exoplayer2.source.SequenceableLoader;
import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.source.TrackGroupArray;
@ -569,8 +570,11 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
chunkSource.maybeThrowError();
}
public int readData(int sampleQueueIndex, FormatHolder formatHolder, DecoderInputBuffer buffer,
boolean requireFormat) {
public int readData(
int sampleQueueIndex,
FormatHolder formatHolder,
DecoderInputBuffer buffer,
@ReadFlags int readFlags) {
if (isPendingReset()) {
return C.RESULT_NOTHING_READ;
}
@ -602,7 +606,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
}
int result =
sampleQueues[sampleQueueIndex].read(formatHolder, buffer, requireFormat, loadingFinished);
sampleQueues[sampleQueueIndex].read(formatHolder, buffer, readFlags, loadingFinished);
if (result == C.RESULT_FORMAT_READ) {
Format format = Assertions.checkNotNull(formatHolder.format);
if (sampleQueueIndex == primarySampleQueueIndex) {

View file

@ -16,6 +16,7 @@
package com.google.android.exoplayer2.transformer;
import static com.google.android.exoplayer2.source.SampleStream.FLAG_REQUIRE_FORMAT;
import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
import static com.google.android.exoplayer2.util.Assertions.checkState;
import static java.lang.Math.min;
@ -31,7 +32,7 @@ import com.google.android.exoplayer2.audio.AudioProcessor;
import com.google.android.exoplayer2.audio.AudioProcessor.AudioFormat;
import com.google.android.exoplayer2.audio.SonicAudioProcessor;
import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
import com.google.android.exoplayer2.source.SampleStream;
import com.google.android.exoplayer2.source.SampleStream.ReadDataResult;
import java.io.IOException;
import java.nio.ByteBuffer;
@ -273,8 +274,8 @@ import java.nio.ByteBuffer;
}
decoderInputBuffer.clear();
@SampleStream.ReadDataResult
int result = readSource(getFormatHolder(), decoderInputBuffer, /* formatRequired= */ false);
@ReadDataResult
int result = readSource(getFormatHolder(), decoderInputBuffer, /* readFlags= */ 0);
switch (result) {
case C.RESULT_BUFFER_READ:
mediaClock.updateTimeForTrackType(getTrackType(), decoderInputBuffer.timeUs);
@ -373,8 +374,7 @@ import java.nio.ByteBuffer;
}
FormatHolder formatHolder = getFormatHolder();
@SampleStream.ReadDataResult
int result = readSource(formatHolder, decoderInputBuffer, /* formatRequired= */ true);
@ReadDataResult int result = readSource(formatHolder, decoderInputBuffer, FLAG_REQUIRE_FORMAT);
if (result != C.RESULT_FORMAT_READ) {
return false;
}

View file

@ -16,6 +16,7 @@
package com.google.android.exoplayer2.transformer;
import static com.google.android.exoplayer2.source.SampleStream.FLAG_REQUIRE_FORMAT;
import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
import androidx.annotation.Nullable;
@ -24,7 +25,7 @@ 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.source.SampleStream;
import com.google.android.exoplayer2.source.SampleStream.ReadDataResult;
import java.nio.ByteBuffer;
@RequiresApi(18)
@ -59,8 +60,7 @@ import java.nio.ByteBuffer;
if (!formatRead) {
FormatHolder formatHolder = getFormatHolder();
@SampleStream.ReadDataResult
int result = readSource(formatHolder, buffer, /* formatRequired= */ true);
@ReadDataResult int result = readSource(formatHolder, buffer, FLAG_REQUIRE_FORMAT);
if (result != C.RESULT_FORMAT_READ) {
return;
}
@ -102,8 +102,7 @@ import java.nio.ByteBuffer;
*/
private boolean readAndTransformBuffer() {
buffer.clear();
@SampleStream.ReadDataResult
int result = readSource(getFormatHolder(), buffer, /* formatRequired= */ false);
@ReadDataResult int result = readSource(getFormatHolder(), buffer, /* readFlags= */ 0);
if (result == C.RESULT_FORMAT_READ) {
throw new IllegalStateException("Format changes are not supported.");
} else if (result == C.RESULT_NOTHING_READ) {

View file

@ -25,7 +25,7 @@ import com.google.android.exoplayer2.Renderer;
import com.google.android.exoplayer2.RendererCapabilities;
import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
import com.google.android.exoplayer2.drm.DrmSession;
import com.google.android.exoplayer2.source.SampleStream;
import com.google.android.exoplayer2.source.SampleStream.ReadDataResult;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.Util;
@ -92,8 +92,7 @@ public class FakeRenderer extends BaseRenderer {
if (!hasPendingBuffer) {
FormatHolder formatHolder = getFormatHolder();
buffer.clear();
@SampleStream.ReadDataResult
int result = readSource(formatHolder, buffer, /* formatRequired= */ false);
@ReadDataResult int result = readSource(formatHolder, buffer, /* readFlags= */ 0);
if (result == C.RESULT_FORMAT_READ) {
DrmSession.replaceSession(currentDrmSession, formatHolder.drmSession);

View file

@ -43,7 +43,7 @@ import java.util.List;
*/
public class FakeSampleStream implements SampleStream {
/** Item to customize a return value of {@link FakeSampleStream#readData}. */
/** Item to customize a return value of {@link SampleStream#readData}. */
public static final class FakeSampleStreamItem {
/** Item that designates the end of stream has been reached. */
@ -265,12 +265,12 @@ public class FakeSampleStream implements SampleStream {
@Override
public int readData(
FormatHolder formatHolder, DecoderInputBuffer buffer, boolean formatRequired) {
int result = sampleQueue.read(formatHolder, buffer, formatRequired, loadingFinished);
FormatHolder formatHolder, DecoderInputBuffer buffer, @ReadFlags int readFlags) {
int result = sampleQueue.read(formatHolder, buffer, readFlags, loadingFinished);
if (result == C.RESULT_FORMAT_READ) {
downstreamFormat = checkNotNull(formatHolder.format);
}
if (result == C.RESULT_BUFFER_READ && !buffer.isFlagsOnly()) {
if (result == C.RESULT_BUFFER_READ && (readFlags & FLAG_OMIT_SAMPLE_DATA) == 0) {
maybeNotifyDownstreamFormat(buffer.timeUs);
}
return result;