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); }
@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; } }
/** * @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; }
@Override public void render(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException { if (!isEnded) { buffer.clear(); // Verify the format matches the expected format. FormatHolder formatHolder = new FormatHolder(); int result = readSource(formatHolder, buffer, false); if (result == C.RESULT_FORMAT_READ) { formatReadCount++; Assert.assertTrue(expectedFormats.contains(formatHolder.format)); } else if (result == C.RESULT_BUFFER_READ) { bufferReadCount++; if (buffer.isEndOfStream()) { isEnded = true; } } } isReady = buffer.timeUs >= positionUs; }
@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; }
/** * 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(); } }
/** * @param output The output. * @param outputLooper The looper associated with the thread on which the output should be called. * If the output makes use of standard Android UI components, then this should normally be the * looper associated with the application's main thread, which can be obtained using * {@link android.app.Activity#getMainLooper()}. Null may be passed if the output should be * called directly on the player's internal rendering thread. * @param decoderFactory A factory from which to obtain {@link MetadataDecoder} instances. */ public MetadataRenderer(Output output, Looper outputLooper, MetadataDecoderFactory decoderFactory) { super(C.TRACK_TYPE_METADATA); this.output = Assertions.checkNotNull(output); this.outputHandler = outputLooper == null ? null : new Handler(outputLooper, this); this.decoderFactory = Assertions.checkNotNull(decoderFactory); formatHolder = new FormatHolder(); buffer = new MetadataInputBuffer(); pendingMetadata = new Metadata[MAX_PENDING_METADATA_COUNT]; pendingMetadataTimestamps = new long[MAX_PENDING_METADATA_COUNT]; }
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); }
/** * 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; }
/** * @param output The output. * @param outputLooper The looper associated with the thread on which the output should be called. * If the output makes use of standard Android UI components, then this should normally be the * looper associated with the application's main thread, which can be obtained using * {@link android.app.Activity#getMainLooper()}. Null may be passed if the output should be * called directly on the player's internal rendering thread. * @param metadataDecoder A decoder for the metadata. */ public MetadataRenderer(Output<T> output, Looper outputLooper, MetadataDecoder<T> metadataDecoder) { super(C.TRACK_TYPE_METADATA); this.output = Assertions.checkNotNull(output); this.outputHandler = outputLooper == null ? null : new Handler(outputLooper, this); this.metadataDecoder = Assertions.checkNotNull(metadataDecoder); formatHolder = new FormatHolder(); buffer = new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_NORMAL); }
int readData(int track, FormatHolder formatHolder, DecoderInputBuffer buffer) { if (notifyReset || isPendingReset()) { return C.RESULT_NOTHING_READ; } return sampleQueues.valueAt(track).readData(formatHolder, buffer, loadingFinished, lastSeekPositionUs); }
/** * @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 audioCapabilities The audio capabilities for playback on this device. May be null if the * default capabilities (no encoded audio passthrough support) should be assumed. * @param streamType The type of audio stream for the {@link AudioTrack}. */ public SimpleDecoderAudioRenderer(Handler eventHandler, AudioRendererEventListener eventListener, AudioCapabilities audioCapabilities, int streamType) { super(C.TRACK_TYPE_AUDIO); eventDispatcher = new EventDispatcher(eventHandler, eventListener); audioSessionId = AudioTrack.SESSION_ID_NOT_SET; audioTrack = new AudioTrack(audioCapabilities, streamType); formatHolder = new FormatHolder(); }
@Override public int readData(FormatHolder formatHolder, DecoderInputBuffer buffer, boolean formatRequired) { if (formatRequired || !readFormat) { formatHolder.format = format; readFormat = true; return C.RESULT_FORMAT_READ; } else { buffer.setFlags(C.BUFFER_FLAG_END_OF_STREAM); return C.RESULT_BUFFER_READ; } }
public int readData(int trackGroupIndex, FormatHolder formatHolder, DecoderInputBuffer buffer, boolean requireFormat) { if (isPendingReset()) { return C.RESULT_NOTHING_READ; } int result = sampleQueues[trackGroupIndex].read(formatHolder, buffer, requireFormat, loadingFinished, lastSeekPositionUs); if (result == C.RESULT_BUFFER_READ) { discardToRead(); } return result; }
/** * @param output The output. * @param outputLooper The looper associated with the thread on which the output should be called. * If the output makes use of standard Android UI components, then this should normally be the * looper associated with the application's main thread, which can be obtained using * {@link android.app.Activity#getMainLooper()}. Null may be passed if the output should be * called directly on the player's internal rendering thread. * @param decoderFactory A factory from which to obtain {@link MetadataDecoder} instances. */ public MetadataRenderer(MetadataOutput output, Looper outputLooper, MetadataDecoderFactory decoderFactory) { super(C.TRACK_TYPE_METADATA); this.output = Assertions.checkNotNull(output); this.outputHandler = outputLooper == null ? null : new Handler(outputLooper, this); this.decoderFactory = Assertions.checkNotNull(decoderFactory); formatHolder = new FormatHolder(); buffer = new MetadataInputBuffer(); pendingMetadata = new Metadata[MAX_PENDING_METADATA_COUNT]; pendingMetadataTimestamps = new long[MAX_PENDING_METADATA_COUNT]; }
/** * 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 read(FormatHolder formatHolder, DecoderInputBuffer buffer, boolean formatRequired, boolean loadingFinished, long decodeOnlyUntilUs) { int result = metadataQueue.read(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); } return C.RESULT_BUFFER_READ; case C.RESULT_NOTHING_READ: return C.RESULT_NOTHING_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. May be null if the caller requires that the format of the stream be * read even if it's not changing. * @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 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}. */ @SuppressWarnings("ReferenceEquality") public synchronized int read(FormatHolder formatHolder, DecoderInputBuffer buffer, boolean formatRequired, boolean loadingFinished, Format downstreamFormat, SampleExtrasHolder extrasHolder) { if (!hasNextSample()) { if (loadingFinished) { buffer.setFlags(C.BUFFER_FLAG_END_OF_STREAM); return C.RESULT_BUFFER_READ; } else if (upstreamFormat != null && (formatRequired || upstreamFormat != downstreamFormat)) { formatHolder.format = upstreamFormat; return C.RESULT_FORMAT_READ; } else { return C.RESULT_NOTHING_READ; } } int relativeReadIndex = getRelativeIndex(readPosition); if (formatRequired || formats[relativeReadIndex] != downstreamFormat) { formatHolder.format = formats[relativeReadIndex]; return C.RESULT_FORMAT_READ; } if (buffer.isFlagsOnly()) { return C.RESULT_NOTHING_READ; } buffer.timeUs = timesUs[relativeReadIndex]; buffer.setFlags(flags[relativeReadIndex]); extrasHolder.size = sizes[relativeReadIndex]; extrasHolder.offset = offsets[relativeReadIndex]; extrasHolder.cryptoData = cryptoDatas[relativeReadIndex]; readPosition++; return C.RESULT_BUFFER_READ; }
@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); if (result == C.RESULT_FORMAT_READ) { // Clear gapless playback metadata if the start/end points don't match the media. Format format = formatHolder.format; int encoderDelay = startUs != 0 ? 0 : format.encoderDelay; int encoderPadding = endUs != C.TIME_END_OF_SOURCE ? 0 : format.encoderPadding; formatHolder.format = format.copyWithGaplessInfo(encoderDelay, encoderPadding); return C.RESULT_FORMAT_READ; } if (endUs != C.TIME_END_OF_SOURCE && ((result == C.RESULT_BUFFER_READ && buffer.timeUs >= endUs) || (result == C.RESULT_NOTHING_READ && 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; }
int readData(int track, FormatHolder formatHolder, DecoderInputBuffer buffer, boolean formatRequired) { if (suppressRead()) { return C.RESULT_NOTHING_READ; } return sampleQueues[track].read(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()); int result = primarySampleQueue.read(formatHolder, buffer, formatRequired, loadingFinished, lastSeekPositionUs); if (result == C.RESULT_BUFFER_READ) { primarySampleQueue.discardToRead(); } return result; }
@Override public int readData(FormatHolder formatHolder, DecoderInputBuffer buffer, boolean formatRequired) { if (isPendingReset()) { return C.RESULT_NOTHING_READ; } return sampleQueue.read(formatHolder, buffer, formatRequired, loadingFinished, lastSeekPositionUs); }