@Override public int read(ExtractorInput input, PositionHolder seekPosition) throws IOException, InterruptedException { int bytesRead = input.read(packetBuffer.data, 0, MAX_PACKET_SIZE); if (bytesRead == -1) { return RESULT_END_OF_INPUT; } // Feed whatever data we have to the reader, regardless of whether the read finished or not. packetBuffer.setPosition(0); packetBuffer.setLimit(bytesRead); // TODO: Make it possible for adtsReader to consume the dataSource directly, so that it becomes // unnecessary to copy the data through packetBuffer. adtsReader.consume(packetBuffer, firstSampleTimestampUs, firstPacket); firstPacket = false; return RESULT_CONTINUE; }
@Override public int read(ExtractorInput input, PositionHolder seekPosition) throws IOException, InterruptedException { if (synchronizedHeaderData == 0 && !synchronizeCatchingEndOfInput(input)) { return RESULT_END_OF_INPUT; } if (seeker == null) { setupSeeker(input); extractorOutput.seekMap(seeker); MediaFormat mediaFormat = MediaFormat.createAudioFormat(null, synchronizedHeader.mimeType, MediaFormat.NO_VALUE, MpegAudioHeader.MAX_FRAME_SIZE_BYTES, seeker.getDurationUs(), synchronizedHeader.channels, synchronizedHeader.sampleRate, null, null); if (gaplessInfo != null) { mediaFormat = mediaFormat.copyWithGaplessInfo(gaplessInfo.encoderDelay, gaplessInfo.encoderPadding); } trackOutput.format(mediaFormat); } return readSample(input); }
/** Attempts to read an MPEG audio header at the current offset, resynchronizing if necessary. */ private long maybeResynchronize(ExtractorInput extractorInput) throws IOException, InterruptedException { inputBuffer.mark(); if (!inputBuffer.readAllowingEndOfInput(extractorInput, scratch.data, 0, 4)) { return RESULT_END_OF_INPUT; } inputBuffer.returnToMark(); scratch.setPosition(0); int sampleHeaderData = scratch.readInt(); if ((sampleHeaderData & HEADER_MASK) == (synchronizedHeaderData & HEADER_MASK)) { int frameSize = MpegAudioHeader.getFrameSize(sampleHeaderData); if (frameSize != -1) { MpegAudioHeader.populateHeader(sampleHeaderData, synchronizedHeader); return RESULT_CONTINUE; } } synchronizedHeaderData = 0; inputBuffer.skip(extractorInput, 1); return synchronizeCatchingEndOfInput(extractorInput); }
/** * Sets {@link #seeker} to seek using metadata from {@link #inputBuffer}, which should have its * position set to the start of the first frame in the stream. On returning, * {@link #inputBuffer}'s position and mark will be set to the start of the first frame of audio. * * @param extractorInput Source of data for {@link #inputBuffer}. * @param headerPosition Position (byte offset) of the synchronized header in the stream. * @throws IOException Thrown if there was an error reading from the stream. Not expected if the * next two frames were already read during synchronization. * @throws InterruptedException Thrown if reading from the stream was interrupted. Not expected if * the next two frames were already read during synchronization. */ private void setupSeeker(ExtractorInput extractorInput, long headerPosition) throws IOException, InterruptedException { // Try to set up seeking based on a XING or VBRI header. if (parseSeekerFrame(extractorInput, headerPosition, extractorInput.getLength())) { // Discard the parsed header so we start reading from the first audio frame. inputBuffer.mark(); if (seeker != null) { return; } // If there was a header but it was not usable, synchronize to the next frame so we don't // use an invalid bitrate for CBR seeking. This read is guaranteed to succeed if the frame was // already read during synchronization. inputBuffer.read(extractorInput, scratch.data, 0, 4); scratch.setPosition(0); headerPosition += synchronizedHeader.frameSize; MpegAudioHeader.populateHeader(scratch.readInt(), synchronizedHeader); } inputBuffer.returnToMark(); seeker = new ConstantBitrateSeeker(headerPosition, synchronizedHeader.bitrate * 1000, extractorInput.getLength()); }
/** Ensures the buffer contains enough data to read {@code length} bytes. */ private boolean ensureLoaded(ExtractorInput extractorInput, int length) throws InterruptedException, IOException { if (length + readPosition - markPosition > capacity) { throw new BufferOverflowException(); } int bytesToLoad = length - (writePosition - readPosition); if (bytesToLoad > 0) { if (!extractorInput.readFully(buffer.data, writePosition, bytesToLoad, true)) { return false; } writePosition += bytesToLoad; } return true; }
@SuppressWarnings("NonAtomicVolatileUpdate") @Override public void load() throws IOException, InterruptedException { DataSpec loadDataSpec = Util.getRemainderDataSpec(dataSpec, bytesLoaded); try { // Create and open the input. ExtractorInput input = new DefaultExtractorInput(dataSource, loadDataSpec.absoluteStreamPosition, dataSource.open(loadDataSpec)); if (bytesLoaded == 0) { // Set the target to ourselves. extractorWrapper.init(this); } // Load and parse the initialization data. try { int result = Extractor.RESULT_CONTINUE; while (result == Extractor.RESULT_CONTINUE && !loadCanceled) { result = extractorWrapper.read(input); } } finally { bytesLoaded = (int) (input.getPosition() - dataSpec.absoluteStreamPosition); } } finally { dataSource.close(); } }
/** * Peeks a variable-length unsigned EBML integer from the input. */ private long readUint(ExtractorInput input) throws IOException, InterruptedException { input.peekFully(scratch.data, 0, 1); int value = scratch.data[0] & 0xFF; if (value == 0) { return Long.MIN_VALUE; } int mask = 0x80; int length = 0; while ((value & mask) == 0) { mask >>= 1; length++; } value &= ~mask; input.peekFully(scratch.data, 1, length); for (int i = 0; i < length; i++) { value <<= 8; value += scratch.data[i + 1] & 0xFF; } peekLength += length + 1; return value; }
private static void assertEvents(ExtractorInput input, List<String> expectedEvents) throws IOException, InterruptedException { DefaultEbmlReader reader = new DefaultEbmlReader(); TestOutput output = new TestOutput(); reader.init(output); // We expect the number of successful reads to equal the number of expected events. for (int i = 0; i < expectedEvents.size(); i++) { assertTrue(reader.read(input)); } // The next read should be unsuccessful. assertFalse(reader.read(input)); // Check that we really did get to the end of input. assertFalse(input.readFully(new byte[1], 0, 1, true)); assertEquals(expectedEvents.size(), output.events.size()); for (int i = 0; i < expectedEvents.size(); i++) { assertEquals(expectedEvents.get(i), output.events.get(i)); } }
/** * Attempts to read an MPEG audio header at the current offset, resynchronizing if necessary. */ private boolean maybeResynchronize(ExtractorInput extractorInput) throws IOException, InterruptedException { extractorInput.resetPeekPosition(); if (!extractorInput.peekFully(scratch.data, 0, 4, true)) { return false; } scratch.setPosition(0); int sampleHeaderData = scratch.readInt(); if ((sampleHeaderData & HEADER_MASK) == (synchronizedHeaderData & HEADER_MASK)) { int frameSize = MpegAudioHeader.getFrameSize(sampleHeaderData); if (frameSize != -1) { MpegAudioHeader.populateHeader(sampleHeaderData, synchronizedHeader); return true; } } synchronizedHeaderData = 0; extractorInput.skipFully(1); return synchronizeCatchingEndOfInput(extractorInput); }
public void testReadVarintEndOfInputAtStart() throws IOException, InterruptedException { VarintReader reader = new VarintReader(); // Build an input with no data. ExtractorInput input = new FakeExtractorInput.Builder() .setSimulateUnknownLength(true) .build(); // End of input allowed. long result = reader.readUnsignedVarint(input, true, false, 8); assertEquals(C.RESULT_END_OF_INPUT, result); // End of input not allowed. try { reader.readUnsignedVarint(input, false, false, 8); fail(); } catch (EOFException e) { // Expected. } }
@SuppressWarnings("NonAtomicVolatileUpdate") @Override public final void load() throws IOException, InterruptedException { DataSpec loadDataSpec = Util.getRemainderDataSpec(dataSpec, bytesLoaded); try { // Create and open the input. ExtractorInput input = new DefaultExtractorInput(dataSource, loadDataSpec.absoluteStreamPosition, dataSource.open(loadDataSpec)); if (bytesLoaded == 0) { // Set the target to ourselves. extractorWrapper.init(this); } // Load and parse the sample data. try { int result = Extractor.RESULT_CONTINUE; while (result == Extractor.RESULT_CONTINUE && !loadCanceled) { result = extractorWrapper.read(input); } } finally { bytesLoaded = (int) (input.getPosition() - dataSpec.absoluteStreamPosition); } } finally { dataSource.close(); } }
/** * Does a byte by byte search to try and find the next level 1 element. This method is called if * some invalid data is encountered in the parser. * * @param input The {@link ExtractorInput} from which data has to be read. * @return id of the next level 1 element that has been found. * @throws EOFException If the end of input was encountered when searching for the next level 1 * element. * @throws IOException If an error occurs reading from the input. * @throws InterruptedException If the thread is interrupted. */ private long maybeResyncToNextLevel1Element(ExtractorInput input) throws EOFException, IOException, InterruptedException { input.resetPeekPosition(); while (true) { input.peekFully(scratch, 0, MAX_ID_BYTES); int varintLength = VarintReader.parseUnsignedVarintLength(scratch[0]); if (varintLength != -1 && varintLength <= MAX_ID_BYTES) { int potentialId = (int) VarintReader.assembleVarint(scratch, varintLength, false); if (output.isLevel1Element(potentialId)) { input.skipFully(varintLength); return potentialId; } } input.skipFully(1); } }
/** * Processes the atom payload. If {@link #atomData} is null and the size is at or above the * threshold {@link #RELOAD_MINIMUM_SEEK_DISTANCE}, {@code true} is returned and the caller should * restart loading at the position in {@code positionHolder}. Otherwise, the atom is read/skipped. */ private boolean readAtomPayload(ExtractorInput input, PositionHolder positionHolder) throws IOException, InterruptedException { long atomPayloadSize = atomSize - atomHeaderBytesRead; long atomEndPosition = input.getPosition() + atomPayloadSize; boolean seekRequired = false; if (atomData != null) { input.readFully(atomData.data, atomHeaderBytesRead, (int) atomPayloadSize); if (atomType == Atom.TYPE_ftyp) { isQuickTime = processFtypAtom(atomData); } else if (!containerAtoms.isEmpty()) { containerAtoms.peek().add(new Atom.LeafAtom(atomType, atomData)); } } else { // We don't need the data. Skip or seek, depending on how large the atom is. if (atomPayloadSize < RELOAD_MINIMUM_SEEK_DISTANCE) { input.skipFully((int) atomPayloadSize); } else { positionHolder.position = input.getPosition() + atomPayloadSize; seekRequired = true; } } processAtomEnded(atomEndPosition); return seekRequired && parserState != STATE_READING_SAMPLE; }
private void readEncryptionData(ExtractorInput input) throws IOException, InterruptedException { TrackBundle nextTrackBundle = null; long nextDataOffset = Long.MAX_VALUE; int trackBundlesSize = trackBundles.size(); for (int i = 0; i < trackBundlesSize; i++) { TrackFragment trackFragment = trackBundles.valueAt(i).fragment; if (trackFragment.sampleEncryptionDataNeedsFill && trackFragment.auxiliaryDataPosition < nextDataOffset) { nextDataOffset = trackFragment.auxiliaryDataPosition; nextTrackBundle = trackBundles.valueAt(i); } } if (nextTrackBundle == null) { parserState = STATE_READING_SAMPLE_START; return; } int bytesToSkip = (int) (nextDataOffset - input.getPosition()); if (bytesToSkip < 0) { throw new ParserException("Offset to encryption data was negative."); } input.skipFully(bytesToSkip); nextTrackBundle.fragment.fillEncryptionData(input); }
/** * Reads a tag header from the provided {@link ExtractorInput}. * * @param input The {@link ExtractorInput} from which to read. * @return True if tag header was read successfully. Otherwise, false. * @throws IOException If an error occurred reading or parsing data from the source. * @throws InterruptedException If the thread was interrupted. */ private boolean readTagHeader(ExtractorInput input) throws IOException, InterruptedException { if (!input.readFully(tagHeaderBuffer.data, 0, FLV_TAG_HEADER_SIZE, true)) { // We've reached the end of the stream. return false; } tagHeaderBuffer.setPosition(0); tagType = tagHeaderBuffer.readUnsignedByte(); tagDataSize = tagHeaderBuffer.readUnsignedInt24(); tagTimestampUs = tagHeaderBuffer.readUnsignedInt24(); tagTimestampUs = ((tagHeaderBuffer.readUnsignedByte() << 24) | tagTimestampUs) * 1000L; tagHeaderBuffer.skipBytes(3); // streamId parserState = STATE_READING_TAG_DATA; return true; }
/** * Reads the body of a tag from the provided {@link ExtractorInput}. * * @param input The {@link ExtractorInput} from which to read. * @return True if the data was consumed by a reader. False if it was skipped. * @throws IOException If an error occurred reading or parsing data from the source. * @throws InterruptedException If the thread was interrupted. */ private boolean readTagData(ExtractorInput input) throws IOException, InterruptedException { boolean wasConsumed = true; if (tagType == TAG_TYPE_AUDIO && audioReader != null) { audioReader.consume(prepareTagData(input), tagTimestampUs); } else if (tagType == TAG_TYPE_VIDEO && videoReader != null) { videoReader.consume(prepareTagData(input), tagTimestampUs); } else if (tagType == TAG_TYPE_SCRIPT_DATA && metadataReader != null) { metadataReader.consume(prepareTagData(input), tagTimestampUs); if (metadataReader.getDurationUs() != C.UNKNOWN_TIME_US) { if (audioReader != null) { audioReader.setDurationUs(metadataReader.getDurationUs()); } if (videoReader != null) { videoReader.setDurationUs(metadataReader.getDurationUs()); } } } else { input.skipFully(tagDataSize); wasConsumed = false; } bytesToNextTagHeader = 4; // There's a 4 byte previous tag size before the next header. parserState = STATE_SKIPPING_TO_TAG_HEADER; return wasConsumed; }
@Override public void load() throws IOException, InterruptedException { // If we previously fed part of this chunk to the extractor, we need to skip it this time. For // encrypted content we need to skip the data by reading it through the source, so as to ensure // correct decryption of the remainder of the chunk. For clear content, we can request the // remainder of the chunk directly. DataSpec loadDataSpec; boolean skipLoadedBytes; if (isEncrypted) { loadDataSpec = dataSpec; skipLoadedBytes = bytesLoaded != 0; } else { loadDataSpec = Util.getRemainderDataSpec(dataSpec, bytesLoaded); skipLoadedBytes = false; } try { ExtractorInput input = new DefaultExtractorInput(dataSource, loadDataSpec.absoluteStreamPosition, dataSource.open(loadDataSpec)); if (skipLoadedBytes) { input.skipFully(bytesLoaded); } try { int result = Extractor.RESULT_CONTINUE; while (result == Extractor.RESULT_CONTINUE && !loadCanceled) { result = extractorWrapper.read(input); } } finally { bytesLoaded = (int) (input.getPosition() - dataSpec.absoluteStreamPosition); } } finally { dataSource.close(); } }
public void testReadVarintExceedsMaximumAllowedLength() throws IOException, InterruptedException { VarintReader reader = new VarintReader(); ExtractorInput input = new FakeExtractorInput.Builder() .setData(DATA_8_BYTE_0) .setSimulateUnknownLength(true) .build(); long result = reader.readUnsignedVarint(input, false, true, 4); assertEquals(C.RESULT_MAX_LENGTH_EXCEEDED, result); }
@Override public boolean sniff(ExtractorInput input) throws IOException, InterruptedException { byte[] scratch = new byte[1]; for (int i = 0; i < 5; i++) { input.peekFully(scratch, 0, 1); if ((scratch[0] & 0xFF) != 0x47) { return false; } input.advancePeekPosition(TS_PACKET_SIZE - 1); } return true; }
private boolean synchronizeCatchingEndOfInput(ExtractorInput input) throws IOException, InterruptedException { // An EOFException will be raised if any peek operation was partially satisfied. If a seek // operation resulted in reading from within the last frame, we may try to peek past the end of // the file in a partially-satisfied read operation, so we need to catch the exception. try { return synchronize(input, false); } catch (EOFException e) { return false; } }
/** * Processes the atom payload. If {@link #atomData} is null and the size is at or above the * threshold {@link #RELOAD_MINIMUM_SEEK_DISTANCE}, {@code true} is returned and the caller should * restart loading at the position in {@code positionHolder}. Otherwise, the atom is read/skipped. */ private boolean readAtomPayload(ExtractorInput input, PositionHolder positionHolder) throws IOException, InterruptedException { long atomPayloadSize = atomSize - atomHeaderBytesRead; long atomEndPosition = input.getPosition() + atomPayloadSize; boolean seekRequired = false; if (atomData != null) { input.readFully(atomData.data, atomHeaderBytesRead, (int) atomPayloadSize); if (!containerAtoms.isEmpty()) { containerAtoms.peek().add(new Atom.LeafAtom(atomType, atomData)); } } else { // We don't need the data. Skip or seek, depending on how large the atom is. if (atomPayloadSize < RELOAD_MINIMUM_SEEK_DISTANCE) { input.skipFully((int) atomPayloadSize); } else { positionHolder.position = input.getPosition() + atomPayloadSize; seekRequired = true; } } while (!containerAtoms.isEmpty() && containerAtoms.peek().endPosition == atomEndPosition) { Atom.ContainerAtom containerAtom = containerAtoms.pop(); if (containerAtom.type == Atom.TYPE_moov) { // We've reached the end of the moov atom. Process it and prepare to read samples. processMoovAtom(containerAtom); containerAtoms.clear(); parserState = STATE_READING_SAMPLE; return false; } else if (!containerAtoms.isEmpty()) { containerAtoms.peek().add(containerAtom); } } enterReadingAtomHeaderState(); return seekRequired; }
/** * Reads and returns a float of length {@code byteLength} from the {@link ExtractorInput}. * * @param input The {@link ExtractorInput} from which to read. * @param byteLength The length of the float being read. * @return The read float value. * @throws IOException If an error occurs reading from the input. * @throws InterruptedException If the thread is interrupted. */ private double readFloat(ExtractorInput input, int byteLength) throws IOException, InterruptedException { long integerValue = readInteger(input, byteLength); double floatValue; if (byteLength == VALID_FLOAT32_ELEMENT_SIZE_BYTES) { floatValue = Float.intBitsToFloat((int) integerValue); } else { floatValue = Double.longBitsToDouble(integerValue); } return floatValue; }
/** * Helper to build an {@link ExtractorInput} from byte data. * * @param data Zero or more integers with values between {@code 0x00} and {@code 0xFF}. * @return An {@link ExtractorInput} from which the data can be read. */ private static ExtractorInput createTestInput(int... data) { return new FakeExtractorInput.Builder() .setData(TestUtil.createByteArray(data)) .setSimulateUnknownLength(true) .build(); }
@Override public void binaryElement(int id, int contentSize, ExtractorInput input) throws IOException, InterruptedException { byte[] bytes = new byte[contentSize]; input.readFully(bytes, 0, contentSize); events.add(formatEvent(id, "bytes=" + Arrays.toString(bytes))); }
public void testBinaryElement() throws IOException, InterruptedException { ExtractorInput input = createTestInput(0xA3, 0x88, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08); TestOutput expected = new TestOutput(); expected.binaryElement(TestOutput.ID_SIMPLE_BLOCK, 8, createTestInput(0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08)); assertEvents(input, expected.events); }
/** * Outputs up to {@code length} bytes of sample data to {@code output}, consisting of either * {@link #sampleStrippedBytes} or data read from {@code input}. */ private int readToOutput(ExtractorInput input, TrackOutput output, int length) throws IOException, InterruptedException { int bytesRead; int strippedBytesLeft = sampleStrippedBytes.bytesLeft(); if (strippedBytesLeft > 0) { bytesRead = Math.min(length, strippedBytesLeft); output.sampleData(sampleStrippedBytes, bytesRead); } else { bytesRead = output.sampleData(input, length, false); } sampleBytesRead += bytesRead; sampleBytesWritten += bytesRead; return bytesRead; }
private long synchronizeCatchingEndOfInput(ExtractorInput extractorInput) throws IOException, InterruptedException { // An EOFException will be raised if any read operation was partially satisfied. If a seek // operation resulted in reading from within the last frame, we may try to read past the end of // the file in a partially-satisfied read operation, so we need to catch the exception. try { return synchronize(extractorInput); } catch (EOFException e) { return RESULT_END_OF_INPUT; } }
private boolean readInternal(ExtractorInput extractorInput, byte[] target, int offset, int length) throws InterruptedException, IOException { if (!ensureLoaded(extractorInput, length)) { return false; } if (target != null) { System.arraycopy(buffer.data, readPosition, target, offset, length); } readPosition += length; return true; }
/** * Reads and returns an integer of length {@code byteLength} from the {@link ExtractorInput}. * * @param input The {@link ExtractorInput} from which to read. * @param byteLength The length of the integer being read. * @return The read integer value. * @throws IOException If an error occurs reading from the input. * @throws InterruptedException If the thread is interrupted. */ private long readInteger(ExtractorInput input, int byteLength) throws IOException, InterruptedException { input.readFully(scratch, 0, byteLength); long value = 0; for (int i = 0; i < byteLength; i++) { value = (value << 8) | (scratch[i] & 0xFF); } return value; }
private int readSample(ExtractorInput extractorInput) throws IOException, InterruptedException { if (sampleBytesRemaining == 0) { if (!maybeResynchronize(extractorInput)) { return RESULT_END_OF_INPUT; } if (basisTimeUs == -1) { basisTimeUs = seeker.getTimeUs(extractorInput.getPosition()); if (forcedFirstSampleTimestampUs != -1) { long embeddedFirstSampleTimestampUs = seeker.getTimeUs(0); basisTimeUs += forcedFirstSampleTimestampUs - embeddedFirstSampleTimestampUs; } } sampleBytesRemaining = synchronizedHeader.frameSize; } int bytesAppended = trackOutput.sampleData(extractorInput, sampleBytesRemaining, true); if (bytesAppended == C.RESULT_END_OF_INPUT) { return RESULT_END_OF_INPUT; } sampleBytesRemaining -= bytesAppended; if (sampleBytesRemaining > 0) { return RESULT_CONTINUE; } long timeUs = basisTimeUs + (samplesRead * C.MICROS_PER_SECOND / synchronizedHeader.sampleRate); trackOutput.sampleMetadata(timeUs, C.SAMPLE_FLAG_SYNC, synchronizedHeader.frameSize, 0, null); samplesRead += synchronizedHeader.samplesPerFrame; sampleBytesRemaining = 0; return RESULT_CONTINUE; }
/** * Reads an EBML variable-length integer (varint) from an {@link ExtractorInput} such that * reading can be resumed later if an error occurs having read only some of it. * <p> * If an value is successfully read, then the reader will automatically reset itself ready to * read another value. * <p> * If an {@link IOException} or {@link InterruptedException} is throw, the read can be resumed * later by calling this method again, passing an {@link ExtractorInput} providing data starting * where the previous one left off. * * @param input The {@link ExtractorInput} from which the integer should be read. * @param allowEndOfInput True if encountering the end of the input having read no data is * allowed, and should result in {@code -1} being returned. False if it should be * considered an error, causing an {@link EOFException} to be thrown. * @param removeLengthMask Removes the variable-length integer length mask from the value * @return The read value, or -1 if {@code allowEndOfStream} is true and the end of the input was * encountered. * @throws IOException If an error occurs reading from the input. * @throws InterruptedException If the thread is interrupted. */ public long readUnsignedVarint(ExtractorInput input, boolean allowEndOfInput, boolean removeLengthMask) throws IOException, InterruptedException { if (state == STATE_BEGIN_READING) { // Read the first byte to establish the length. if (!input.readFully(scratch, 0, 1, allowEndOfInput)) { return -1; } int firstByte = scratch[0] & 0xFF; length = -1; for (int i = 0; i < VARINT_LENGTH_MASKS.length; i++) { if ((VARINT_LENGTH_MASKS[i] & firstByte) != 0) { length = i + 1; break; } } if (length == -1) { throw new IllegalStateException("No valid varint length mask found"); } state = STATE_READ_CONTENTS; } // Read the remaining bytes. input.readFully(scratch, 1, length - 1); // Parse the value. if (removeLengthMask) { scratch[0] &= ~VARINT_LENGTH_MASKS[length - 1]; } long varint = 0; for (int i = 0; i < length; i++) { varint = (varint << 8) | (scratch[i] & 0xFF); } state = STATE_BEGIN_READING; return varint; }
@Override public int read(ExtractorInput input, PositionHolder seekPosition) throws IOException, InterruptedException { sampleRead = false; boolean continueReading = true; while (continueReading && !sampleRead) { continueReading = reader.read(input); if (continueReading && maybeSeekForCues(seekPosition, input.getPosition())) { return Extractor.RESULT_SEEK; } } return continueReading ? Extractor.RESULT_CONTINUE : Extractor.RESULT_END_OF_INPUT; }
/** * Ensures {@link #scratch} contains at least {@code requiredLength} bytes of data, reading from * the extractor input if necessary. */ private void readScratch(ExtractorInput input, int requiredLength) throws IOException, InterruptedException { if (scratch.limit() >= requiredLength) { return; } if (scratch.capacity() < requiredLength) { scratch.reset(Arrays.copyOf(scratch.data, Math.max(scratch.data.length * 2, requiredLength)), scratch.limit()); } input.readFully(scratch.data, scratch.limit(), requiredLength - scratch.limit()); scratch.setLimit(requiredLength); }
/** * Writes {@code length} bytes of sample data into {@code target} at {@code offset}, consisting of * pending {@link #sampleStrippedBytes} and any remaining data read from {@code input}. */ private void readToTarget(ExtractorInput input, byte[] target, int offset, int length) throws IOException, InterruptedException { int pendingStrippedBytes = Math.min(length, sampleStrippedBytes.bytesLeft()); input.readFully(target, offset + pendingStrippedBytes, length - pendingStrippedBytes); if (pendingStrippedBytes > 0) { sampleStrippedBytes.readBytes(target, offset, pendingStrippedBytes); } sampleBytesRead += length; }
/** * Sets data to be parsed by libflac. * @param extractorInput Source {@link ExtractorInput} */ public void setData(ExtractorInput extractorInput) { this.byteBufferData = null; this.extractorInput = extractorInput; if (tempBuffer == null) { this.tempBuffer = new byte[TEMP_BUFFER_SIZE]; } endOfExtractorInput = false; }
private ParsableByteArray prepareTagData(ExtractorInput input) throws IOException, InterruptedException { if (tagDataSize > tagData.capacity()) { tagData.reset(new byte[Math.max(tagData.capacity() * 2, tagDataSize)], 0); } else { tagData.setPosition(0); } tagData.setLimit(tagDataSize); input.readFully(tagData.data, 0, tagDataSize); return tagData; }
public void testMasterElementEmpty() throws IOException, InterruptedException { ExtractorInput input = createTestInput(0x18, 0x53, 0x80, 0x67, 0x80); TestOutput expected = new TestOutput(); expected.startMasterElement(TestOutput.ID_SEGMENT, 5, 0); expected.endMasterElement(TestOutput.ID_SEGMENT); assertEvents(input, expected.events); }