@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 { while (true) { switch (parserState) { case STATE_READING_ATOM_HEADER: if (!readAtomHeader(input)) { return Extractor.RESULT_END_OF_INPUT; } break; case STATE_READING_ATOM_PAYLOAD: readAtomPayload(input); break; case STATE_READING_ENCRYPTION_DATA: readEncryptionData(input); break; default: if (readSample(input)) { return RESULT_CONTINUE; } } } }
/** * Updates the position of the holder to Cues element's position if the extractor configuration * permits use of master seek entry. After building Cues sets the holder's position back to where * it was before. * * @param seekPosition The holder whose position will be updated. * @param currentPosition Current position of the input. * @return true if the seek position was updated, false otherwise. */ private boolean maybeSeekForCues(PositionHolder seekPosition, long currentPosition) { if (seekForCues) { seekPositionAfterBuildingCues = currentPosition; seekPosition.position = cuesContentPosition; cuesState = CUES_STATE_BUILDING; seekForCues = false; return true; } // After parsing Cues, Seek back to original position if available. We will not do this unless // we seeked to get to the Cues in the first place. if (cuesState == CUES_STATE_BUILT && seekPositionAfterBuildingCues != UNKNOWN) { seekPosition.position = seekPositionAfterBuildingCues; seekPositionAfterBuildingCues = UNKNOWN; return true; } return false; }
@Override public int read(ExtractorInput input, PositionHolder seekPosition) throws IOException, InterruptedException { int currentFileSize = (int) input.getLength(); // Increase the size of sampleData if necessary. if (sampleSize == sampleData.length) { sampleData = Arrays.copyOf(sampleData, (currentFileSize != C.LENGTH_UNBOUNDED ? currentFileSize : sampleData.length) * 3 / 2); } // Consume to the input. int bytesRead = input.read(sampleData, sampleSize, sampleData.length - sampleSize); if (bytesRead != C.RESULT_END_OF_INPUT) { sampleSize += bytesRead; if (currentFileSize == C.LENGTH_UNBOUNDED || sampleSize != currentFileSize) { return Extractor.RESULT_CONTINUE; } } // We've reached the end of the input, which corresponds to the end of the current file. processSample(); return Extractor.RESULT_END_OF_INPUT; }
@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. if (!startedPacket) { // Pass data to the reader as though it's contained within a single infinitely long packet. adtsReader.packetStarted(firstSampleTimestampUs, true); startedPacket = true; } adtsReader.consume(packetBuffer); return RESULT_CONTINUE; }
/** * 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; }
@Override public final int read(ExtractorInput input, PositionHolder seekPosition) throws IOException, InterruptedException { while (true) { switch (parserState) { case STATE_READING_ATOM_HEADER: if (!readAtomHeader(input)) { return Extractor.RESULT_END_OF_INPUT; } break; case STATE_READING_ATOM_PAYLOAD: readAtomPayload(input); break; case STATE_READING_ENCRYPTION_DATA: readEncryptionData(input); break; default: if (readSample(input)) { return RESULT_CONTINUE; } } } }
@Override public int read(ExtractorInput input, PositionHolder seekPosition) throws IOException, InterruptedException { while (true) { switch (parserState) { case STATE_READING_FLV_HEADER: if (!readFlvHeader(input)) { return RESULT_END_OF_INPUT; } break; case STATE_SKIPPING_TO_TAG_HEADER: skipToTagHeader(input); break; case STATE_READING_TAG_HEADER: if (!readTagHeader(input)) { return RESULT_END_OF_INPUT; } break; case STATE_READING_TAG_DATA: if (readTagData(input)) { return RESULT_CONTINUE; } break; } } }
@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); }
/** * Updates the position of the holder to Cues element's position if the extractor configuration * permits use of master seek entry. After building Cues sets the holder's position back to where * it was before. * * @param seekPosition The holder whose position will be updated. * @param currentPosition Current position of the input. * @return true if the seek position was updated, false otherwise. */ private boolean maybeSeekForCues(PositionHolder seekPosition, long currentPosition) { if (seekForCues) { seekPositionAfterBuildingCues = currentPosition; seekPosition.position = cuesContentPosition; seekForCues = false; return true; } // After parsing Cues, seek back to original position if available. We will not do this unless // we seeked to get to the Cues in the first place. if (sentSeekMap && seekPositionAfterBuildingCues != UNKNOWN) { seekPosition.position = seekPositionAfterBuildingCues; seekPositionAfterBuildingCues = UNKNOWN; return true; } return false; }
@Override public int read(ExtractorInput input, PositionHolder seekPosition) throws IOException, InterruptedException { while (true) { switch (parserState) { case STATE_AFTER_SEEK: if (input.getPosition() == 0) { enterReadingAtomHeaderState(); } else { parserState = STATE_READING_SAMPLE; } break; case STATE_READING_ATOM_HEADER: if (!readAtomHeader(input)) { return RESULT_END_OF_INPUT; } break; case STATE_READING_ATOM_PAYLOAD: if (readAtomPayload(input, seekPosition)) { return RESULT_SEEK; } break; default: return readSample(input, seekPosition); } } }
/** * 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; }
@Override public int read(ExtractorInput extractorInput, PositionHolder seekPosition) throws IOException, InterruptedException { if (synchronizedHeaderData == 0 && synchronizeCatchingEndOfInput(extractorInput) == RESULT_END_OF_INPUT) { return RESULT_END_OF_INPUT; } return readSample(extractorInput); }
@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; }
public static void consumeTestData(Extractor extractor, byte[] data) throws IOException, InterruptedException { FakeExtractorInput input = new FakeExtractorInput.Builder().setData(data).build(); PositionHolder seekPositionHolder = new PositionHolder(); int readResult = Extractor.RESULT_CONTINUE; while (readResult != Extractor.RESULT_END_OF_INPUT) { readResult = extractor.read(input, seekPositionHolder); if (readResult == Extractor.RESULT_SEEK) { long seekPosition = seekPositionHolder.position; Assertions.checkState(0 < seekPosition && seekPosition <= Integer.MAX_VALUE); input.setPosition((int) seekPosition); } } }
public static void consumeTestData(Extractor extractor, byte[] data) throws IOException, InterruptedException { ExtractorInput input = createTestExtractorInput(data); PositionHolder seekPositionHolder = new PositionHolder(); int readResult = Extractor.RESULT_CONTINUE; while (readResult != Extractor.RESULT_END_OF_INPUT) { readResult = extractor.read(input, seekPositionHolder); if (readResult == Extractor.RESULT_SEEK) { input = createTestExtractorInput(data, (int) seekPositionHolder.position); } } }
@Override public int read(ExtractorInput input, PositionHolder seekPosition) throws IOException, InterruptedException { if (!input.readFully(tsPacketBuffer.data, 0, TS_PACKET_SIZE, true)) { return RESULT_END_OF_INPUT; } // Note: see ISO/IEC 13818-1, section 2.4.3.2 for detailed information on the format of // the header. tsPacketBuffer.setPosition(0); tsPacketBuffer.setLimit(TS_PACKET_SIZE); int syncByte = tsPacketBuffer.readUnsignedByte(); if (syncByte != TS_SYNC_BYTE) { return RESULT_CONTINUE; } tsPacketBuffer.readBytes(tsScratch, 3); tsScratch.skipBits(1); // transport_error_indicator boolean payloadUnitStartIndicator = tsScratch.readBit(); tsScratch.skipBits(1); // transport_priority int pid = tsScratch.readBits(13); tsScratch.skipBits(2); // transport_scrambling_control boolean adaptationFieldExists = tsScratch.readBit(); boolean payloadExists = tsScratch.readBit(); // Last 4 bits of scratch are skipped: continuity_counter // Skip the adaptation field. if (adaptationFieldExists) { int adaptationFieldLength = tsPacketBuffer.readUnsignedByte(); tsPacketBuffer.skipBytes(adaptationFieldLength); } // Read the payload. if (payloadExists) { TsPayloadReader payloadReader = tsPayloadReaders.get(pid); if (payloadReader != null) { payloadReader.consume(tsPacketBuffer, payloadUnitStartIndicator, output); } } return RESULT_CONTINUE; }
@Override public int read(final ExtractorInput input, PositionHolder seekPosition) throws IOException, InterruptedException { decoder.setData(input); if (!metadataParsed) { FlacStreamInfo streamInfo = decoder.decodeMetadata(); if (streamInfo == null) { throw new IOException("Metadata decoding failed"); } metadataParsed = true; output.seekMap(new SeekMap() { final boolean isSeekable = decoder.getSeekPosition(0) != -1; @Override public boolean isSeekable() { return isSeekable; } @Override public long getPosition(long timeUs) { return isSeekable ? decoder.getSeekPosition(timeUs) : 0; } }); MediaFormat mediaFormat = MediaFormat.createAudioFormat(null, MimeTypes.AUDIO_RAW, streamInfo.bitRate(), MediaFormat.NO_VALUE, streamInfo.durationUs(), streamInfo.channels, streamInfo.sampleRate, null, null, C.ENCODING_PCM_16BIT); trackOutput.format(mediaFormat); outputBuffer = new ParsableByteArray(streamInfo.maxDecodedFrameSize()); outputByteBuffer = ByteBuffer.wrap(outputBuffer.data); } outputBuffer.reset(); int size = decoder.decodeSample(outputByteBuffer); if (size <= 0) { return RESULT_END_OF_INPUT; } trackOutput.sampleData(outputBuffer, size); trackOutput.sampleMetadata(decoder.getLastSampleTimestamp(), C.SAMPLE_FLAG_SYNC, size, 0, null); return decoder.isEndOfData() ? RESULT_END_OF_INPUT : RESULT_CONTINUE; }
@Override public int read(ExtractorInput input, PositionHolder seekPosition) throws IOException, InterruptedException { if (wavHeader == null) { wavHeader = WavHeaderReader.peek(input); if (wavHeader == null) { // Someone tried to read a non-WAV or unsupported WAV without sniffing first. throw new ParserException("Error initializing WavHeader. Did you sniff first?"); } bytesPerFrame = wavHeader.getBytesPerFrame(); } // If we haven't read in the data start and size, read and store them. if (!wavHeader.hasDataBounds()) { WavHeaderReader.skipToData(input, wavHeader); trackOutput.format( MediaFormat.createAudioFormat( null, MimeTypes.AUDIO_RAW, wavHeader.getBitrate(), MAX_INPUT_SIZE, wavHeader.getDurationUs(), wavHeader.getNumChannels(), wavHeader.getSampleRateHz(), null, null, wavHeader.getEncoding())); extractorOutput.seekMap(this); } int bytesAppended = trackOutput.sampleData(input, MAX_INPUT_SIZE - pendingBytes, true); if (bytesAppended != RESULT_END_OF_INPUT) { pendingBytes += bytesAppended; } // Round down the pending number of bytes to the nearest frame. int frameBytes = pendingBytes / bytesPerFrame * bytesPerFrame; if (frameBytes > 0) { long sampleStartPosition = input.getPosition() - pendingBytes; pendingBytes -= frameBytes; trackOutput.sampleMetadata( wavHeader.getTimeUs(sampleStartPosition), C.SAMPLE_FLAG_SYNC, frameBytes, pendingBytes, null); } if (bytesAppended == RESULT_END_OF_INPUT) { return RESULT_END_OF_INPUT; } return RESULT_CONTINUE; }
@Override public int read(ExtractorInput input, PositionHolder seekPosition) throws IOException, InterruptedException { return streamReader.read(input, seekPosition); }
@Override public int read(ExtractorInput input, PositionHolder seekPosition) throws IOException, InterruptedException { long position = input.getPosition(); if (!oggParser.readPacket(input, scratch)) { return Extractor.RESULT_END_OF_INPUT; } byte[] data = scratch.data; if (streamInfo == null) { streamInfo = new FlacStreamInfo(data, 17); byte[] metadata = Arrays.copyOfRange(data, 9, scratch.limit()); metadata[4] = (byte) 0x80; // Set the last metadata block flag, ignore the other blocks List<byte[]> initializationData = Collections.singletonList(metadata); MediaFormat mediaFormat = MediaFormat.createAudioFormat(null, MimeTypes.AUDIO_FLAC, streamInfo.bitRate(), MediaFormat.NO_VALUE, streamInfo.durationUs(), streamInfo.channels, streamInfo.sampleRate, initializationData, null); trackOutput.format(mediaFormat); } else if (data[0] == AUDIO_PACKET_TYPE) { if (!firstAudioPacketProcessed) { if (seekTable != null) { extractorOutput.seekMap(seekTable.createSeekMap(position, streamInfo.sampleRate)); seekTable = null; } else { extractorOutput.seekMap(SeekMap.UNSEEKABLE); } firstAudioPacketProcessed = true; } trackOutput.sampleData(scratch, scratch.limit()); scratch.setPosition(0); long timeUs = FlacUtil.extractSampleTimestamp(streamInfo, scratch); trackOutput.sampleMetadata(timeUs, C.SAMPLE_FLAG_SYNC, scratch.limit(), 0, null); } else if ((data[0] & 0x7F) == SEEKTABLE_PACKET_TYPE && seekTable == null) { seekTable = FlacSeekTable.parseSeekTable(scratch); } scratch.reset(); return Extractor.RESULT_CONTINUE; }
/** * @see Extractor#read(ExtractorInput, PositionHolder) */ abstract int read(ExtractorInput input, PositionHolder seekPosition) throws IOException, InterruptedException;