@Override public int read(NonBlockingInputStream inputStream, SampleHolder out) throws ParserException { try { int results = 0; while ((results & READ_TERMINATING_RESULTS) == 0) { switch (parserState) { case STATE_READING_ATOM_HEADER: results |= readAtomHeader(inputStream); break; case STATE_READING_ATOM_PAYLOAD: results |= readAtomPayload(inputStream); break; case STATE_READING_ENCRYPTION_DATA: results |= readEncryptionData(inputStream); break; default: results |= readOrSkipSample(inputStream, out); break; } } return results; } catch (Exception e) { throw new ParserException(e); } }
private int skipSample(NonBlockingInputStream inputStream, int sampleSize) { if (fragmentRun.definesEncryptionData) { ParsableByteArray sampleEncryptionData = fragmentRun.sampleEncryptionData; TrackEncryptionBox encryptionBox = track.sampleDescriptionEncryptionBoxes[fragmentRun.sampleDescriptionIndex]; int vectorSize = encryptionBox.initializationVectorSize; boolean subsampleEncryption = fragmentRun.sampleHasSubsampleEncryptionTable[sampleIndex]; sampleEncryptionData.skip(vectorSize); int subsampleCount = subsampleEncryption ? sampleEncryptionData.readUnsignedShort() : 1; if (subsampleEncryption) { sampleEncryptionData.skip((2 + 4) * subsampleCount); } } inputStream.skip(sampleSize); sampleIndex++; enterState(STATE_READING_SAMPLE); return 0; }
/** * Reads an element ID such that reading can be stopped and started again in a later call * if not enough bytes are available. Returns {@link #READ_RESULT_CONTINUE} if a full element ID * has been read into {@link #elementId}. Reset {@link #elementIdState} to * {@link #STATE_BEGIN_READING} before calling to indicate a new element ID should be read. * * @param inputStream The input stream from which an element ID should be read * @return One of the {@code RESULT_*} flags defined in this class */ private int readElementId(NonBlockingInputStream inputStream) { if (elementIdState == STATE_FINISHED_READING) { return READ_RESULT_CONTINUE; } if (elementIdState == STATE_BEGIN_READING) { varintBytesState = STATE_BEGIN_READING; elementIdState = STATE_READ_CONTENTS; } int result = readVarintBytes(inputStream); if (result != READ_RESULT_CONTINUE) { return result; } // Element IDs are at most 4 bytes so cast to int now. elementId = (int) getTempByteArrayValue(varintBytesLength, false); elementIdState = STATE_FINISHED_READING; return READ_RESULT_CONTINUE; }
/** * Reads an element's content size such that reading can be stopped and started again in a later * call if not enough bytes are available. * * <p>Returns {@link #READ_RESULT_CONTINUE} if an entire element size has been * read into {@link #elementContentSize}. Reset {@link #elementContentSizeState} to * {@link #STATE_BEGIN_READING} before calling to indicate a new element size should be read. * * @param inputStream The input stream from which an element size should be read * @return One of the {@code RESULT_*} flags defined in this class */ private int readElementContentSize(NonBlockingInputStream inputStream) { if (elementContentSizeState == STATE_FINISHED_READING) { return READ_RESULT_CONTINUE; } if (elementContentSizeState == STATE_BEGIN_READING) { varintBytesState = STATE_BEGIN_READING; elementContentSizeState = STATE_READ_CONTENTS; } int result = readVarintBytes(inputStream); if (result != READ_RESULT_CONTINUE) { return result; } elementContentSize = getTempByteArrayValue(varintBytesLength, true); elementContentSizeState = STATE_FINISHED_READING; return READ_RESULT_CONTINUE; }
@Override public int read( NonBlockingInputStream inputStream, SampleHolder sampleHolder) throws ParserException { this.sampleHolder = sampleHolder; this.readResults = 0; while ((readResults & READ_TERMINATING_RESULTS) == 0) { int ebmlReadResult = reader.read(inputStream); if (ebmlReadResult == EbmlReader.READ_RESULT_NEED_MORE_DATA) { readResults |= WebmExtractor.RESULT_NEED_MORE_DATA; } else if (ebmlReadResult == EbmlReader.READ_RESULT_END_OF_STREAM) { readResults |= WebmExtractor.RESULT_END_OF_STREAM; } } this.sampleHolder = null; return readResults; }
@Override public boolean prepare() throws ParserException { if (!prepared) { if (maybeSelfContained) { // Read up to the first sample. Once we're there, we know that the extractor must have // parsed a moov atom if the chunk contains one. NonBlockingInputStream inputStream = getNonBlockingInputStream(); Assertions.checkState(inputStream != null); int result = extractor.read(inputStream, null); prepared = (result & Extractor.RESULT_NEED_SAMPLE_HOLDER) != 0; } else { // We know there isn't a moov atom. The extractor must have parsed one from a separate // initialization chunk. prepared = true; } if (prepared) { mediaFormat = extractor.getFormat(); Map<UUID, byte[]> extractorPsshInfo = extractor.getPsshInfo(); if (extractorPsshInfo != null) { psshInfo = extractorPsshInfo; } } } return prepared; }
@Override public boolean prepare() throws ParserException { if (!prepared) { if (maybeSelfContained) { // Read up to the first sample. Once we're there, we know that the extractor must have // parsed a moov atom if the chunk contains one. NonBlockingInputStream inputStream = getNonBlockingInputStream(); Assertions.checkState(inputStream != null); int result = extractor.read(inputStream, null); prepared = (result & Extractor.RESULT_NEED_SAMPLE_HOLDER) != 0; } else { // We know there isn't a moov atom. The extractor must have parsed one from a separate // initialization chunk. prepared = true; } if (prepared) { mediaFormat = Assertions.checkNotNull(extractor.getFormat()); psshInfo = extractor.getPsshInfo(); } } return prepared; }
/** * Fills {@link #sampleEncryptionData} for the current run from the provided source. * * @param source A source from which to read the encryption data. * @return True if the encryption data was filled. False if the source had insufficient data. */ public boolean fillEncryptionData(NonBlockingInputStream source) { if (source.getAvailableByteCount() < sampleEncryptionDataLength) { return false; } source.read(sampleEncryptionData.data, 0, sampleEncryptionDataLength); sampleEncryptionData.setPosition(0); sampleEncryptionDataNeedsFill = false; return true; }
private int readAtomPayload(NonBlockingInputStream inputStream) { int bytesRead; if (atomData != null) { bytesRead = inputStream.read(atomData.data, atomBytesRead, atomSize - atomBytesRead); } else { bytesRead = inputStream.skip(atomSize - atomBytesRead); } if (bytesRead == -1) { return RESULT_END_OF_STREAM; } rootAtomBytesRead += bytesRead; atomBytesRead += bytesRead; if (atomBytesRead != atomSize) { return RESULT_NEED_MORE_DATA; } int results = 0; if (atomData != null) { results |= onLeafAtomRead(new LeafAtom(atomType, atomData)); } while (!containerAtomEndPoints.isEmpty() && containerAtomEndPoints.peek() == rootAtomBytesRead) { containerAtomEndPoints.pop(); results |= onContainerAtomRead(containerAtoms.pop()); } enterState(STATE_READING_ATOM_HEADER); return results; }
private int readEncryptionData(NonBlockingInputStream inputStream) { boolean success = fragmentRun.fillEncryptionData(inputStream); if (!success) { return RESULT_NEED_MORE_DATA; } enterState(STATE_READING_SAMPLE); return 0; }
@Override public long readVarint(NonBlockingInputStream inputStream) { varintBytesState = STATE_BEGIN_READING; int result = readVarintBytes(inputStream); if (result != READ_RESULT_CONTINUE) { throw new IllegalStateException("Couldn't read varint"); } return getTempByteArrayValue(varintBytesLength, true); }
@Override public void readBytes(NonBlockingInputStream inputStream, ByteBuffer byteBuffer, int totalBytes) { bytesState = 0; int result = readBytesInternal(inputStream, byteBuffer, totalBytes); if (result != READ_RESULT_CONTINUE) { throw new IllegalStateException("Couldn't read bytes into buffer"); } }
@Override public void readBytes(NonBlockingInputStream inputStream, byte[] byteArray, int totalBytes) { bytesState = 0; int result = readBytesInternal(inputStream, byteArray, totalBytes); if (result != READ_RESULT_CONTINUE) { throw new IllegalStateException("Couldn't read bytes into array"); } }
@Override public void skipBytes(NonBlockingInputStream inputStream, int totalBytes) { bytesState = 0; int result = skipBytesInternal(inputStream, totalBytes); if (result != READ_RESULT_CONTINUE) { throw new IllegalStateException("Couldn't skip bytes"); } }
/** * Reads a set amount of bytes into a {@link ByteBuffer} such that reading can be stopped * and started again later if not enough bytes are available. * * <p>Returns {@link #READ_RESULT_CONTINUE} if all bytes have been read. Reset * {@link #bytesState} to {@code 0} before calling to indicate a new set of bytes should be read. * * @param inputStream The input stream from which bytes should be read * @param byteBuffer The {@link ByteBuffer} into which bytes should be read * @param totalBytes The total size of bytes to be read * @return One of the {@code RESULT_*} flags defined in this class */ private int readBytesInternal( NonBlockingInputStream inputStream, ByteBuffer byteBuffer, int totalBytes) { if (bytesState == STATE_BEGIN_READING && totalBytes > byteBuffer.capacity()) { throw new IllegalArgumentException("Byte buffer not large enough"); } if (bytesState >= totalBytes) { return READ_RESULT_CONTINUE; } int remainingBytes = totalBytes - bytesState; int additionalBytesRead = inputStream.read(byteBuffer, remainingBytes); return updateBytesState(additionalBytesRead, totalBytes); }
/** * Reads a set amount of bytes into a {@code byte[]} such that reading can be stopped * and started again later if not enough bytes are available. * * <p>Returns {@link #READ_RESULT_CONTINUE} if all bytes have been read. Reset * {@link #bytesState} to {@code 0} before calling to indicate a new set of bytes should be read. * * @param inputStream The input stream from which bytes should be read * @param byteArray The {@code byte[]} into which bytes should be read * @param totalBytes The total size of bytes to be read * @return One of the {@code RESULT_*} flags defined in this class */ private int readBytesInternal( NonBlockingInputStream inputStream, byte[] byteArray, int totalBytes) { if (bytesState == STATE_BEGIN_READING && totalBytes > byteArray.length) { throw new IllegalArgumentException("Byte array not large enough"); } if (bytesState >= totalBytes) { return READ_RESULT_CONTINUE; } int remainingBytes = totalBytes - bytesState; int additionalBytesRead = inputStream.read(byteArray, bytesState, remainingBytes); return updateBytesState(additionalBytesRead, totalBytes); }
@Override public boolean onBinaryElement( int id, long elementOffsetBytes, int headerSizeBytes, int contentsSizeBytes, NonBlockingInputStream inputStream) throws ParserException { return WebmExtractor.this.onBinaryElement( id, elementOffsetBytes, headerSizeBytes, contentsSizeBytes, inputStream); }
@Override public boolean read(SampleHolder holder) { NonBlockingInputStream inputStream = getNonBlockingInputStream(); Assertions.checkState(inputStream != null); if (!sampleAvailable()) { return false; } int bytesLoaded = (int) bytesLoaded(); int sampleSize = bytesLoaded; if (headerData != null) { sampleSize += headerData.length; } if (holder.data == null || holder.data.capacity() < sampleSize) { holder.replaceBuffer(sampleSize); } int bytesRead; if (holder.data != null) { if (headerData != null) { holder.data.put(headerData); } bytesRead = inputStream.read(holder.data, bytesLoaded); holder.size = sampleSize; } else { bytesRead = inputStream.skip(bytesLoaded); holder.size = 0; } Assertions.checkState(bytesRead == bytesLoaded); holder.timeUs = startTimeUs; return true; }
@Override public boolean read(SampleHolder holder) throws ParserException { NonBlockingInputStream inputStream = getNonBlockingInputStream(); Assertions.checkState(inputStream != null); int result = extractor.read(inputStream, holder); boolean sampleRead = (result & Extractor.RESULT_READ_SAMPLE) != 0; if (sampleRead) { holder.timeUs -= sampleOffsetUs; } return sampleRead; }
@Override protected void consumeStream(NonBlockingInputStream stream) throws IOException { int result = extractor.read(stream, null); if (result != expectedExtractorResult) { throw new ParserException("Invalid extractor result. Expected " + expectedExtractorResult + ", got " + result); } if ((result & Extractor.RESULT_READ_INDEX) != 0) { representationHolders.get(format.id).segmentIndex = new DashWrappingSegmentIndex(extractor.getIndex(), uri, indexAnchor); } }
private int readAtomPayload(NonBlockingInputStream inputStream) { int bytesRead; if (atomData != null) { bytesRead = inputStream.read(atomData.data, atomBytesRead, atomSize - atomBytesRead); } else { bytesRead = inputStream.skip(atomSize - atomBytesRead); } if (bytesRead == -1) { return RESULT_END_OF_STREAM; } rootAtomBytesRead += bytesRead; atomBytesRead += bytesRead; if (atomBytesRead != atomSize) { return RESULT_NEED_MORE_DATA; } int results = 0; if (atomData != null) { results |= onLeafAtomRead(new LeafAtom(atomType, atomData)); } while (!containerAtoms.isEmpty() && containerAtoms.peek().endByteOffset == rootAtomBytesRead) { results |= onContainerAtomRead(containerAtoms.pop()); } enterState(STATE_READING_ATOM_HEADER); return results; }
private int readSample(NonBlockingInputStream inputStream, int sampleSize, SampleHolder out) { if (out == null) { return RESULT_NEED_SAMPLE_HOLDER; } out.timeUs = fragmentRun.getSamplePresentationTime(sampleIndex) * 1000L; out.flags = 0; if (fragmentRun.sampleIsSyncFrameTable[sampleIndex]) { out.flags |= C.SAMPLE_FLAG_SYNC; lastSyncSampleIndex = sampleIndex; } if (out.data == null || out.data.capacity() < sampleSize) { out.replaceBuffer(sampleSize); } if (fragmentRun.definesEncryptionData) { readSampleEncryptionData(fragmentRun.sampleEncryptionData, out); } ByteBuffer outputData = out.data; if (outputData == null) { inputStream.skip(sampleSize); out.size = 0; } else { inputStream.read(outputData, sampleSize); if (track.type == Track.TYPE_VIDEO) { // The mp4 file contains length-prefixed NAL units, but the decoder wants start code // delimited content. Mp4Util.replaceLengthPrefixesWithAvcStartCodes(outputData, sampleSize); } out.size = sampleSize; } sampleIndex++; enterState(STATE_READING_SAMPLE); return RESULT_READ_SAMPLE; }