int readData(int group, FormatHolder formatHolder, DecoderInputBuffer buffer, boolean requireFormat) { if (isPendingReset()) { return C.RESULT_NOTHING_READ; } while (mediaChunks.size() > 1 && finishedReadingChunk(mediaChunks.getFirst())) { mediaChunks.removeFirst(); } HlsMediaChunk currentChunk = mediaChunks.getFirst(); Format trackFormat = currentChunk.trackFormat; if (!trackFormat.equals(downstreamTrackFormat)) { eventDispatcher.downstreamFormatChanged(trackType, trackFormat, currentChunk.trackSelectionReason, currentChunk.trackSelectionData, currentChunk.startTimeUs); } downstreamTrackFormat = trackFormat; return sampleQueues.valueAt(group).readData(formatHolder, buffer, requireFormat, loadingFinished, lastSeekPositionUs); }
/** * Reads from the enabled upstream source. If the upstream source has been read to the end then * {@link C#RESULT_BUFFER_READ} is only returned if {@link #setCurrentStreamFinal()} has been * called. {@link C#RESULT_NOTHING_READ} is returned otherwise. * * @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. * @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 result, which can be {@link C#RESULT_NOTHING_READ}, {@link C#RESULT_FORMAT_READ} or * {@link C#RESULT_BUFFER_READ}. */ protected final int readSource(FormatHolder formatHolder, DecoderInputBuffer buffer, boolean formatRequired) { int result = stream.readData(formatHolder, buffer, formatRequired); if (result == C.RESULT_BUFFER_READ) { if (buffer.isEndOfStream()) { readEndOfStream = true; return streamIsFinal ? C.RESULT_BUFFER_READ : C.RESULT_NOTHING_READ; } buffer.timeUs += streamOffsetUs; } else if (result == C.RESULT_FORMAT_READ) { Format format = formatHolder.format; if (format.subsampleOffsetUs != Format.OFFSET_SAMPLE_RELATIVE) { format = format.copyWithSubsampleOffsetUs(format.subsampleOffsetUs + streamOffsetUs); formatHolder.format = format; } } return result; }
@Override public int readData(FormatHolder formatHolder, DecoderInputBuffer buffer, boolean requireFormat) { if (pendingDiscontinuity) { return C.RESULT_NOTHING_READ; } if (sentEos) { buffer.setFlags(C.BUFFER_FLAG_END_OF_STREAM); return C.RESULT_BUFFER_READ; } int result = stream.readData(formatHolder, buffer, requireFormat); // TODO: Clear gapless playback metadata if a format was read (if applicable). if (endUs != C.TIME_END_OF_SOURCE && ((result == C.RESULT_BUFFER_READ && buffer.timeUs >= endUs) || (result == C.RESULT_NOTHING_READ && mediaPeriod.getBufferedPositionUs() == C.TIME_END_OF_SOURCE))) { buffer.clear(); buffer.setFlags(C.BUFFER_FLAG_END_OF_STREAM); sentEos = true; return C.RESULT_BUFFER_READ; } if (result == C.RESULT_BUFFER_READ && !buffer.isEndOfStream()) { buffer.timeUs -= startUs; } return result; }
@Override public int readData(FormatHolder formatHolder, DecoderInputBuffer buffer, boolean requireFormat) { 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) { formatHolder.format = format; streamState = STREAM_STATE_SEND_SAMPLE; return C.RESULT_FORMAT_READ; } Assertions.checkState(streamState == STREAM_STATE_SEND_SAMPLE); if (!loadingFinished) { return C.RESULT_NOTHING_READ; } else { buffer.timeUs = 0; buffer.addFlag(C.BUFFER_FLAG_KEY_FRAME); buffer.ensureSpaceForWrite(sampleSize); buffer.data.put(sampleData, 0, sampleSize); streamState = STREAM_STATE_END_OF_STREAM; return C.RESULT_BUFFER_READ; } }
/** * @param trackType The track type that the renderer handles. One of the {@code C.TRACK_TYPE_*} * constants defined in {@link C}. * @param mediaCodecSelector A decoder selector. * @param drmSessionManager For use with encrypted media. May be null if support for encrypted * media is not required. * @param playClearSamplesWithoutKeys Encrypted media may contain clear (un-encrypted) regions. * For example a media file may start with a short clear region so as to allow playback to * begin in parallel with key acquisition. This parameter specifies whether the renderer is * permitted to play clear regions of encrypted media files before {@code drmSessionManager} * has obtained the keys necessary to decrypt encrypted regions of the media. */ public MediaCodecRenderer(int trackType, MediaCodecSelector mediaCodecSelector, DrmSessionManager<FrameworkMediaCrypto> drmSessionManager, boolean playClearSamplesWithoutKeys) { super(trackType); Assertions.checkState(Util.SDK_INT >= 16); this.mediaCodecSelector = Assertions.checkNotNull(mediaCodecSelector); this.drmSessionManager = drmSessionManager; this.playClearSamplesWithoutKeys = playClearSamplesWithoutKeys; buffer = new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_DISABLED); flagsOnlyBuffer = DecoderInputBuffer.newFlagsOnlyInstance(); formatHolder = new FormatHolder(); decodeOnlyPresentationTimestamps = new ArrayList<>(); outputBufferInfo = new MediaCodec.BufferInfo(); codecReconfigurationState = RECONFIGURATION_STATE_NONE; codecReinitializationState = REINITIALIZATION_STATE_NONE; }
/** * @param trackType The track type that the renderer handles. One of the {@code C.TRACK_TYPE_*} * constants defined in {@link C}. * @param mediaCodecSelector A decoder selector. * @param drmSessionManager For use with encrypted media. May be null if support for encrypted * media is not required. * @param playClearSamplesWithoutKeys Encrypted media may contain clear (un-encrypted) regions. * For example a media file may start with a short clear region so as to allow playback to * begin in parallel with key acquisition. This parameter specifies whether the renderer is * permitted to play clear regions of encrypted media files before {@code drmSessionManager} * has obtained the keys necessary to decrypt encrypted regions of the media. */ public MediaCodecRenderer(int trackType, MediaCodecSelector mediaCodecSelector, DrmSessionManager<FrameworkMediaCrypto> drmSessionManager, boolean playClearSamplesWithoutKeys) { super(trackType); Assertions.checkState(Util.SDK_INT >= 16); this.mediaCodecSelector = Assertions.checkNotNull(mediaCodecSelector); this.drmSessionManager = drmSessionManager; this.playClearSamplesWithoutKeys = playClearSamplesWithoutKeys; buffer = new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_DISABLED); formatHolder = new FormatHolder(); decodeOnlyPresentationTimestamps = new ArrayList<>(); outputBufferInfo = new MediaCodec.BufferInfo(); codecReconfigurationState = RECONFIGURATION_STATE_NONE; codecReinitializationState = REINITIALIZATION_STATE_NONE; }
int readData(int group, FormatHolder formatHolder, DecoderInputBuffer buffer) { if (isPendingReset()) { return C.RESULT_NOTHING_READ; } while (mediaChunks.size() > 1 && finishedReadingChunk(mediaChunks.getFirst())) { mediaChunks.removeFirst(); } HlsMediaChunk currentChunk = mediaChunks.getFirst(); Format trackFormat = currentChunk.trackFormat; if (!trackFormat.equals(downstreamTrackFormat)) { eventDispatcher.downstreamFormatChanged(trackType, trackFormat, currentChunk.trackSelectionReason, currentChunk.trackSelectionData, currentChunk.startTimeUs); } downstreamTrackFormat = trackFormat; return sampleQueues.valueAt(group).readData(formatHolder, buffer, loadingFinished, lastSeekPositionUs); }
@Override public int readData(FormatHolder formatHolder, DecoderInputBuffer buffer) { if (isPendingReset()) { return C.RESULT_NOTHING_READ; } while (mediaChunks.size() > 1 && mediaChunks.get(1).getFirstSampleIndex() <= sampleQueue.getReadIndex()) { mediaChunks.removeFirst(); } BaseMediaChunk currentChunk = mediaChunks.getFirst(); Format trackFormat = currentChunk.trackFormat; if (!trackFormat.equals(downstreamTrackFormat)) { eventDispatcher.downstreamFormatChanged(trackType, trackFormat, currentChunk.trackSelectionReason, currentChunk.trackSelectionData, currentChunk.startTimeUs); } downstreamTrackFormat = trackFormat; return sampleQueue.readData(formatHolder, buffer, loadingFinished, lastSeekPositionUs); }
@Override public int readData(FormatHolder formatHolder, DecoderInputBuffer buffer) { if (streamState == STREAM_STATE_END_OF_STREAM) { buffer.addFlag(C.BUFFER_FLAG_END_OF_STREAM); return C.RESULT_BUFFER_READ; } else if (streamState == STREAM_STATE_SEND_FORMAT) { formatHolder.format = format; streamState = STREAM_STATE_SEND_SAMPLE; return C.RESULT_FORMAT_READ; } Assertions.checkState(streamState == STREAM_STATE_SEND_SAMPLE); if (!loadingFinished) { return C.RESULT_NOTHING_READ; } else { buffer.timeUs = 0; buffer.addFlag(C.BUFFER_FLAG_KEY_FRAME); buffer.ensureSpaceForWrite(sampleSize); buffer.data.put(sampleData, 0, sampleSize); streamState = STREAM_STATE_END_OF_STREAM; return C.RESULT_BUFFER_READ; } }
/** * Creates a VP9 decoder. * * @param numInputBuffers The number of input buffers. * @param numOutputBuffers The number of output buffers. * @param initialInputBufferSize The initial size of each input buffer. * @param exoMediaCrypto The {@link ExoMediaCrypto} object required for decoding encrypted * content. Maybe null and can be ignored if decoder does not handle encrypted content. * @throws VpxDecoderException Thrown if an exception occurs when initializing the decoder. */ public VpxDecoder(int numInputBuffers, int numOutputBuffers, int initialInputBufferSize, ExoMediaCrypto exoMediaCrypto) throws VpxDecoderException { super(new DecoderInputBuffer[numInputBuffers], new VpxOutputBuffer[numOutputBuffers]); if (!VpxLibrary.isAvailable()) { throw new VpxDecoderException("Failed to load decoder native libraries."); } this.exoMediaCrypto = exoMediaCrypto; if (exoMediaCrypto != null && !VpxLibrary.vpxIsSecureDecodeSupported()) { throw new VpxDecoderException("Vpx decoder does not support secure decode."); } vpxDecContext = vpxInit(); if (vpxDecContext == 0) { throw new VpxDecoderException("Failed to initialize decoder"); } setInitialInputBufferSize(initialInputBufferSize); }
/** * Creates a Flac decoder. * * @param numInputBuffers The number of input buffers. * @param numOutputBuffers The number of output buffers. * @param initializationData Codec-specific initialization data. It should contain only one entry * which is the flac file header. * @throws FlacDecoderException Thrown if an exception occurs when initializing the decoder. */ public FlacDecoder(int numInputBuffers, int numOutputBuffers, List<byte[]> initializationData) throws FlacDecoderException { super(new DecoderInputBuffer[numInputBuffers], new SimpleOutputBuffer[numOutputBuffers]); if (initializationData.size() != 1) { throw new FlacDecoderException("Initialization data must be of length 1"); } decoderJni = new FlacDecoderJni(); decoderJni.setData(ByteBuffer.wrap(initializationData.get(0))); FlacStreamInfo streamInfo; try { streamInfo = decoderJni.decodeMetadata(); } catch (IOException | InterruptedException e) { // Never happens. throw new IllegalStateException(e); } if (streamInfo == null) { throw new FlacDecoderException("Metadata decoding failed"); } setInitialInputBufferSize(streamInfo.maxFrameSize); maxOutputBufferSize = streamInfo.maxDecodedFrameSize(); }
@Override public FlacDecoderException decode(DecoderInputBuffer inputBuffer, SimpleOutputBuffer outputBuffer, boolean reset) { if (reset) { decoderJni.flush(); } decoderJni.setData(inputBuffer.data); ByteBuffer outputData = outputBuffer.init(inputBuffer.timeUs, maxOutputBufferSize); int result; try { result = decoderJni.decodeSample(outputData); } catch (IOException | InterruptedException e) { // Never happens. throw new IllegalStateException(e); } if (result < 0) { return new FlacDecoderException("Frame decoding failed"); } outputData.position(0); outputData.limit(result); return null; }
@Override public FfmpegDecoderException decode(DecoderInputBuffer inputBuffer, SimpleOutputBuffer outputBuffer, boolean reset) { if (reset) { nativeContext = ffmpegReset(nativeContext, extraData); if (nativeContext == 0) { return new FfmpegDecoderException("Error resetting (see logcat)."); } } ByteBuffer inputData = inputBuffer.data; int inputSize = inputData.limit(); ByteBuffer outputData = outputBuffer.init(inputBuffer.timeUs, OUTPUT_BUFFER_SIZE); int result = ffmpegDecode(nativeContext, inputData, inputSize, outputData, OUTPUT_BUFFER_SIZE); if (result < 0) { return new FfmpegDecoderException("Error decoding (see logcat). Code: " + result); } if (!hasOutputFormat) { channelCount = ffmpegGetChannelCount(nativeContext); sampleRate = ffmpegGetSampleRate(nativeContext); hasOutputFormat = true; } outputBuffer.data.position(0); outputBuffer.data.limit(result); return null; }
/** * @param scaleToFit Whether video frames should be scaled to fit when rendering. * @param allowedJoiningTimeMs The maximum duration in milliseconds for which this video renderer * can attempt to seamlessly join an ongoing playback. * @param eventHandler A handler to use when delivering events to {@code eventListener}. May be * null if delivery of events is not required. * @param eventListener A listener of events. May be null if delivery of events is not required. * @param maxDroppedFramesToNotify The maximum number of frames that can be dropped between * invocations of {@link VideoRendererEventListener#onDroppedFrames(int, long)}. * @param drmSessionManager For use with encrypted media. May be null if support for encrypted * media is not required. * @param playClearSamplesWithoutKeys Encrypted media may contain clear (un-encrypted) regions. * For example a media file may start with a short clear region so as to allow playback to * begin in parallel with key acquisition. This parameter specifies whether the renderer is * permitted to play clear regions of encrypted media files before {@code drmSessionManager} * has obtained the keys necessary to decrypt encrypted regions of the media. */ public LibvpxVideoRenderer(boolean scaleToFit, long allowedJoiningTimeMs, Handler eventHandler, VideoRendererEventListener eventListener, int maxDroppedFramesToNotify, DrmSessionManager<ExoMediaCrypto> drmSessionManager, boolean playClearSamplesWithoutKeys) { super(C.TRACK_TYPE_VIDEO); this.scaleToFit = scaleToFit; this.allowedJoiningTimeMs = allowedJoiningTimeMs; this.maxDroppedFramesToNotify = maxDroppedFramesToNotify; this.drmSessionManager = drmSessionManager; this.playClearSamplesWithoutKeys = playClearSamplesWithoutKeys; joiningDeadlineMs = C.TIME_UNSET; clearReportedVideoSize(); formatHolder = new FormatHolder(); flagsOnlyBuffer = DecoderInputBuffer.newFlagsOnlyInstance(); eventDispatcher = new EventDispatcher(eventHandler, eventListener); outputMode = VpxDecoder.OUTPUT_MODE_NONE; decoderReinitializationState = REINITIALIZATION_STATE_NONE; }
public FfmpegDecoder(int numInputBuffers, int numOutputBuffers, int initialInputBufferSize, String mimeType, List<byte[]> initializationData, boolean outputFloat) throws FfmpegDecoderException { super(new DecoderInputBuffer[numInputBuffers], new SimpleOutputBuffer[numOutputBuffers]); if (!FfmpegLibrary.isAvailable()) { throw new FfmpegDecoderException("Failed to load decoder native libraries."); } codecName = FfmpegLibrary.getCodecName(mimeType); extraData = getExtraData(mimeType, initializationData); encoding = outputFloat ? C.ENCODING_PCM_FLOAT : C.ENCODING_PCM_16BIT; outputBufferSize = outputFloat ? OUTPUT_BUFFER_SIZE_32BIT : OUTPUT_BUFFER_SIZE_16BIT; nativeContext = ffmpegInitialize(codecName, extraData, outputFloat); if (nativeContext == 0) { throw new FfmpegDecoderException("Initialization failed."); } setInitialInputBufferSize(initialInputBufferSize); }
@Override public int readData(FormatHolder formatHolder, DecoderInputBuffer buffer, boolean requireFormat) { 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) { formatHolder.format = format; streamState = STREAM_STATE_SEND_SAMPLE; return C.RESULT_FORMAT_READ; } else if (loadingFinished) { if (loadingSucceeded) { buffer.timeUs = 0; buffer.addFlag(C.BUFFER_FLAG_KEY_FRAME); buffer.ensureSpaceForWrite(sampleSize); buffer.data.put(sampleData, 0, sampleSize); } else { buffer.addFlag(C.BUFFER_FLAG_END_OF_STREAM); } streamState = STREAM_STATE_END_OF_STREAM; return C.RESULT_BUFFER_READ; } return C.RESULT_NOTHING_READ; }
/** * @param trackType The track type that the renderer handles. One of the {@code C.TRACK_TYPE_*} * constants defined in {@link C}. * @param mediaCodecSelector A decoder selector. * @param drmSessionManager For use with encrypted media. May be null if support for encrypted * media is not required. * @param playClearSamplesWithoutKeys Encrypted media may contain clear (un-encrypted) regions. * For example a media file may start with a short clear region so as to allow playback to * begin in parallel with key acquisition. This parameter specifies whether the renderer is * permitted to play clear regions of encrypted media files before {@code drmSessionManager} * has obtained the keys necessary to decrypt encrypted regions of the media. */ public MediaCodecRenderer(int trackType, MediaCodecSelector mediaCodecSelector, @Nullable DrmSessionManager<FrameworkMediaCrypto> drmSessionManager, boolean playClearSamplesWithoutKeys) { super(trackType); Assertions.checkState(Util.SDK_INT >= 16); this.mediaCodecSelector = Assertions.checkNotNull(mediaCodecSelector); this.drmSessionManager = drmSessionManager; this.playClearSamplesWithoutKeys = playClearSamplesWithoutKeys; buffer = new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_DISABLED); flagsOnlyBuffer = DecoderInputBuffer.newFlagsOnlyInstance(); formatHolder = new FormatHolder(); decodeOnlyPresentationTimestamps = new ArrayList<>(); outputBufferInfo = new MediaCodec.BufferInfo(); codecReconfigurationState = RECONFIGURATION_STATE_NONE; codecReinitializationState = REINITIALIZATION_STATE_NONE; }
@Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); audioRenderer = new SimpleDecoderAudioRenderer(null, null, null, false, mockAudioSink) { @Override protected int supportsFormatInternal(DrmSessionManager<ExoMediaCrypto> drmSessionManager, Format format) { return FORMAT_HANDLED; } @Override protected SimpleDecoder<DecoderInputBuffer, ? extends SimpleOutputBuffer, ? extends AudioDecoderException> createDecoder(Format format, ExoMediaCrypto mediaCrypto) throws AudioDecoderException { return new FakeDecoder(); } }; }
/** * Attempts to read from the queue. * * @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. * @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 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 * {@link C#RESULT_BUFFER_READ}. */ public int readData(FormatHolder formatHolder, DecoderInputBuffer buffer, boolean formatRequired, boolean loadingFinished, long decodeOnlyUntilUs) { int result = infoQueue.readData(formatHolder, buffer, formatRequired, loadingFinished, downstreamFormat, extrasHolder); switch (result) { case C.RESULT_FORMAT_READ: downstreamFormat = formatHolder.format; return C.RESULT_FORMAT_READ; case C.RESULT_BUFFER_READ: if (!buffer.isEndOfStream()) { if (buffer.timeUs < decodeOnlyUntilUs) { buffer.addFlag(C.BUFFER_FLAG_DECODE_ONLY); } // Read encryption data if the sample is encrypted. if (buffer.isEncrypted()) { readEncryptionData(buffer, extrasHolder); } // Write the sample data into the holder. buffer.ensureSpaceForWrite(extrasHolder.size); readData(extrasHolder.offset, buffer.data, extrasHolder.size); // Advance the read head. dropDownstreamTo(extrasHolder.nextOffset); } return C.RESULT_BUFFER_READ; case C.RESULT_NOTHING_READ: return C.RESULT_NOTHING_READ; default: throw new IllegalStateException(); } }
int readData(int track, FormatHolder formatHolder, DecoderInputBuffer buffer, boolean formatRequired) { if (notifyReset || isPendingReset()) { return C.RESULT_NOTHING_READ; } return sampleQueues.valueAt(track).readData(formatHolder, buffer, formatRequired, loadingFinished, lastSeekPositionUs); }
@Override public int readData(FormatHolder formatHolder, DecoderInputBuffer buffer, boolean formatRequired) { if (isPendingReset()) { return C.RESULT_NOTHING_READ; } discardDownstreamMediaChunks(primarySampleQueue.getReadIndex()); return primarySampleQueue.readData(formatHolder, buffer, formatRequired, loadingFinished, lastSeekPositionUs); }
@Override public int readData(FormatHolder formatHolder, DecoderInputBuffer buffer, boolean formatRequired) { if (isPendingReset()) { return C.RESULT_NOTHING_READ; } return sampleQueue.readData(formatHolder, buffer, formatRequired, loadingFinished, lastSeekPositionUs); }
private static MediaCodec.CryptoInfo getFrameworkCryptoInfo(DecoderInputBuffer buffer, int adaptiveReconfigurationBytes) { MediaCodec.CryptoInfo cryptoInfo = buffer.cryptoInfo.getFrameworkCryptoInfoV16(); if (adaptiveReconfigurationBytes == 0) { return cryptoInfo; } // There must be at least one sub-sample, although numBytesOfClearData is permitted to be // null if it contains no clear data. Instantiate it if needed, and add the reconfiguration // bytes to the clear byte count of the first sub-sample. if (cryptoInfo.numBytesOfClearData == null) { cryptoInfo.numBytesOfClearData = new int[1]; } cryptoInfo.numBytesOfClearData[0] += adaptiveReconfigurationBytes; return cryptoInfo; }
/** * Attempts to read from the queue. * * @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. * @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 * {@link C#RESULT_BUFFER_READ}. */ public int readData(FormatHolder formatHolder, DecoderInputBuffer buffer, boolean loadingFinished, long decodeOnlyUntilUs) { switch (infoQueue.readData(formatHolder, buffer, downstreamFormat, extrasHolder)) { case C.RESULT_NOTHING_READ: if (loadingFinished) { buffer.setFlags(C.BUFFER_FLAG_END_OF_STREAM); return C.RESULT_BUFFER_READ; } return C.RESULT_NOTHING_READ; case C.RESULT_FORMAT_READ: downstreamFormat = formatHolder.format; return C.RESULT_FORMAT_READ; case C.RESULT_BUFFER_READ: if (buffer.timeUs < decodeOnlyUntilUs) { buffer.addFlag(C.BUFFER_FLAG_DECODE_ONLY); } // Read encryption data if the sample is encrypted. if (buffer.isEncrypted()) { readEncryptionData(buffer, extrasHolder); } // Write the sample data into the holder. buffer.ensureSpaceForWrite(extrasHolder.size); readData(extrasHolder.offset, buffer.data, extrasHolder.size); // Advance the read head. dropDownstreamTo(extrasHolder.nextOffset); return C.RESULT_BUFFER_READ; default: throw new IllegalStateException(); } }
/** * Attempts to read from the queue. * * @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 a sample is read then the buffer is populated with information * about the sample, but not its data. The size and absolute position of the data in the * rolling buffer is stored in {@code extrasHolder}, along with an encryption id if present * and the absolute position of the first byte that may still be required after the current * sample has been read. * @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. * @param extrasHolder The holder into which extra sample information should be written. * @return The result, which can be {@link C#RESULT_NOTHING_READ}, {@link C#RESULT_FORMAT_READ} * or {@link C#RESULT_BUFFER_READ}. */ public synchronized int readData(FormatHolder formatHolder, DecoderInputBuffer buffer, Format downstreamFormat, BufferExtrasHolder extrasHolder) { if (queueSize == 0) { if (upstreamFormat != null && upstreamFormat != downstreamFormat) { formatHolder.format = upstreamFormat; return C.RESULT_FORMAT_READ; } return C.RESULT_NOTHING_READ; } if (formats[relativeReadIndex] != downstreamFormat) { formatHolder.format = formats[relativeReadIndex]; return C.RESULT_FORMAT_READ; } buffer.timeUs = timesUs[relativeReadIndex]; buffer.setFlags(flags[relativeReadIndex]); extrasHolder.size = sizes[relativeReadIndex]; extrasHolder.offset = offsets[relativeReadIndex]; extrasHolder.encryptionKeyId = encryptionKeys[relativeReadIndex]; largestDequeuedTimestampUs = Math.max(largestDequeuedTimestampUs, buffer.timeUs); queueSize--; relativeReadIndex++; absoluteReadIndex++; if (relativeReadIndex == capacity) { // Wrap around. relativeReadIndex = 0; } extrasHolder.nextOffset = queueSize > 0 ? offsets[relativeReadIndex] : extrasHolder.offset + extrasHolder.size; return C.RESULT_BUFFER_READ; }