Search in sources :

Example 11 with ExtractorInput

use of com.google.android.exoplayer2.extractor.ExtractorInput in project ExoPlayer by google.

the class PsExtractor method read.

@Override
public int read(ExtractorInput input, PositionHolder seekPosition) throws IOException, InterruptedException {
    // First peek and check what type of start code is next.
    if (!input.peekFully(psPacketBuffer.data, 0, 4, true)) {
        return RESULT_END_OF_INPUT;
    }
    psPacketBuffer.setPosition(0);
    int nextStartCode = psPacketBuffer.readInt();
    if (nextStartCode == MPEG_PROGRAM_END_CODE) {
        return RESULT_END_OF_INPUT;
    } else if (nextStartCode == PACK_START_CODE) {
        // Now peek the rest of the pack_header.
        input.peekFully(psPacketBuffer.data, 0, 10);
        // We only care about the pack_stuffing_length in here, skip the first 77 bits.
        psPacketBuffer.setPosition(9);
        // Last 3 bits is the length.
        int packStuffingLength = psPacketBuffer.readUnsignedByte() & 0x07;
        // Now skip the stuffing and the pack header.
        input.skipFully(packStuffingLength + 14);
        return RESULT_CONTINUE;
    } else if (nextStartCode == SYSTEM_HEADER_START_CODE) {
        // We just skip all this, but we need to get the length first.
        input.peekFully(psPacketBuffer.data, 0, 2);
        // Length is the next 2 bytes.
        psPacketBuffer.setPosition(0);
        int systemHeaderLength = psPacketBuffer.readUnsignedShort();
        input.skipFully(systemHeaderLength + 6);
        return RESULT_CONTINUE;
    } else if (((nextStartCode & 0xFFFFFF00) >> 8) != PACKET_START_CODE_PREFIX) {
        // Skip bytes until we see a valid start code again.
        input.skipFully(1);
        return RESULT_CONTINUE;
    }
    // We're at the start of a regular PES packet now.
    // Get the stream ID off the last byte of the start code.
    int streamId = nextStartCode & 0xFF;
    // Check to see if we have this one in our map yet, and if not, then add it.
    PesReader payloadReader = psPayloadReaders.get(streamId);
    if (!foundAllTracks) {
        if (payloadReader == null) {
            ElementaryStreamReader elementaryStreamReader = null;
            if (!foundAudioTrack && streamId == PRIVATE_STREAM_1) {
                // Private stream, used for AC3 audio.
                // NOTE: This may need further parsing to determine if its DTS, but that's likely only
                // valid for DVDs.
                elementaryStreamReader = new Ac3Reader();
                foundAudioTrack = true;
            } else if (!foundAudioTrack && (streamId & AUDIO_STREAM_MASK) == AUDIO_STREAM) {
                elementaryStreamReader = new MpegAudioReader();
                foundAudioTrack = true;
            } else if (!foundVideoTrack && (streamId & VIDEO_STREAM_MASK) == VIDEO_STREAM) {
                elementaryStreamReader = new H262Reader();
                foundVideoTrack = true;
            }
            if (elementaryStreamReader != null) {
                TrackIdGenerator idGenerator = new TrackIdGenerator(streamId, MAX_STREAM_ID_PLUS_ONE);
                elementaryStreamReader.createTracks(output, idGenerator);
                payloadReader = new PesReader(elementaryStreamReader, timestampAdjuster);
                psPayloadReaders.put(streamId, payloadReader);
            }
        }
        if ((foundAudioTrack && foundVideoTrack) || input.getPosition() > MAX_SEARCH_LENGTH) {
            foundAllTracks = true;
            output.endTracks();
        }
    }
    // The next 2 bytes are the length. Once we have that we can consume the complete packet.
    input.peekFully(psPacketBuffer.data, 0, 2);
    psPacketBuffer.setPosition(0);
    int payloadLength = psPacketBuffer.readUnsignedShort();
    int pesLength = payloadLength + 6;
    if (payloadReader == null) {
        // Just skip this data.
        input.skipFully(pesLength);
    } else {
        psPacketBuffer.reset(pesLength);
        // Read the whole packet and the header for consumption.
        input.readFully(psPacketBuffer.data, 0, pesLength);
        psPacketBuffer.setPosition(6);
        payloadReader.consume(psPacketBuffer);
        psPacketBuffer.setLimit(psPacketBuffer.capacity());
    }
    return RESULT_CONTINUE;
}
Also used : TrackIdGenerator(com.google.android.exoplayer2.extractor.ts.TsPayloadReader.TrackIdGenerator)

Example 12 with ExtractorInput

use of com.google.android.exoplayer2.extractor.ExtractorInput in project ExoPlayer by google.

the class WavHeaderReader method peek.

/**
   * Peeks and returns a {@code WavHeader}.
   *
   * @param input Input stream to peek the WAV header from.
   * @throws ParserException If the input file is an incorrect RIFF WAV.
   * @throws IOException If peeking from the input fails.
   * @throws InterruptedException If interrupted while peeking from input.
   * @return A new {@code WavHeader} peeked from {@code input}, or null if the input is not a
   *     supported WAV format.
   */
public static WavHeader peek(ExtractorInput input) throws IOException, InterruptedException {
    Assertions.checkNotNull(input);
    // Allocate a scratch buffer large enough to store the format chunk.
    ParsableByteArray scratch = new ParsableByteArray(16);
    // Attempt to read the RIFF chunk.
    ChunkHeader chunkHeader = ChunkHeader.peek(input, scratch);
    if (chunkHeader.id != Util.getIntegerCodeForString("RIFF")) {
        return null;
    }
    input.peekFully(scratch.data, 0, 4);
    scratch.setPosition(0);
    int riffFormat = scratch.readInt();
    if (riffFormat != Util.getIntegerCodeForString("WAVE")) {
        Log.e(TAG, "Unsupported RIFF format: " + riffFormat);
        return null;
    }
    // Skip chunks until we find the format chunk.
    chunkHeader = ChunkHeader.peek(input, scratch);
    while (chunkHeader.id != Util.getIntegerCodeForString("fmt ")) {
        input.advancePeekPosition((int) chunkHeader.size);
        chunkHeader = ChunkHeader.peek(input, scratch);
    }
    Assertions.checkState(chunkHeader.size >= 16);
    input.peekFully(scratch.data, 0, 16);
    scratch.setPosition(0);
    int type = scratch.readLittleEndianUnsignedShort();
    int numChannels = scratch.readLittleEndianUnsignedShort();
    int sampleRateHz = scratch.readLittleEndianUnsignedIntToInt();
    int averageBytesPerSecond = scratch.readLittleEndianUnsignedIntToInt();
    int blockAlignment = scratch.readLittleEndianUnsignedShort();
    int bitsPerSample = scratch.readLittleEndianUnsignedShort();
    int expectedBlockAlignment = numChannels * bitsPerSample / 8;
    if (blockAlignment != expectedBlockAlignment) {
        throw new ParserException("Expected block alignment: " + expectedBlockAlignment + "; got: " + blockAlignment);
    }
    @C.PcmEncoding int encoding = Util.getPcmEncoding(bitsPerSample);
    if (encoding == C.ENCODING_INVALID) {
        Log.e(TAG, "Unsupported WAV bit depth: " + bitsPerSample);
        return null;
    }
    if (type != TYPE_PCM && type != TYPE_WAVE_FORMAT_EXTENSIBLE) {
        Log.e(TAG, "Unsupported WAV format type: " + type);
        return null;
    }
    // If present, skip extensionSize, validBitsPerSample, channelMask, subFormatGuid, ...
    input.advancePeekPosition((int) chunkHeader.size - 16);
    return new WavHeader(numChannels, sampleRateHz, averageBytesPerSecond, blockAlignment, bitsPerSample, encoding);
}
Also used : ParsableByteArray(com.google.android.exoplayer2.util.ParsableByteArray) ParserException(com.google.android.exoplayer2.ParserException)

Example 13 with ExtractorInput

use of com.google.android.exoplayer2.extractor.ExtractorInput in project ExoPlayer by google.

the class Mp4Extractor method readAtomHeader.

private boolean readAtomHeader(ExtractorInput input) throws IOException, InterruptedException {
    if (atomHeaderBytesRead == 0) {
        // Read the standard length atom header.
        if (!input.readFully(atomHeader.data, 0, Atom.HEADER_SIZE, true)) {
            return false;
        }
        atomHeaderBytesRead = Atom.HEADER_SIZE;
        atomHeader.setPosition(0);
        atomSize = atomHeader.readUnsignedInt();
        atomType = atomHeader.readInt();
    }
    if (atomSize == Atom.LONG_SIZE_PREFIX) {
        // Read the extended atom size.
        int headerBytesRemaining = Atom.LONG_HEADER_SIZE - Atom.HEADER_SIZE;
        input.readFully(atomHeader.data, Atom.HEADER_SIZE, headerBytesRemaining);
        atomHeaderBytesRead += headerBytesRemaining;
        atomSize = atomHeader.readUnsignedLongToLong();
    }
    if (shouldParseContainerAtom(atomType)) {
        long endPosition = input.getPosition() + atomSize - atomHeaderBytesRead;
        containerAtoms.add(new ContainerAtom(atomType, endPosition));
        if (atomSize == atomHeaderBytesRead) {
            processAtomEnded(endPosition);
        } else {
            // Start reading the first child atom.
            enterReadingAtomHeaderState();
        }
    } else if (shouldParseLeafAtom(atomType)) {
        // We don't support parsing of leaf atoms that define extended atom sizes, or that have
        // lengths greater than Integer.MAX_VALUE.
        Assertions.checkState(atomHeaderBytesRead == Atom.HEADER_SIZE);
        Assertions.checkState(atomSize <= Integer.MAX_VALUE);
        atomData = new ParsableByteArray((int) atomSize);
        System.arraycopy(atomHeader.data, 0, atomData.data, 0, Atom.HEADER_SIZE);
        parserState = STATE_READING_ATOM_PAYLOAD;
    } else {
        atomData = null;
        parserState = STATE_READING_ATOM_PAYLOAD;
    }
    return true;
}
Also used : ParsableByteArray(com.google.android.exoplayer2.util.ParsableByteArray) ContainerAtom(com.google.android.exoplayer2.extractor.mp4.Atom.ContainerAtom)

Example 14 with ExtractorInput

use of com.google.android.exoplayer2.extractor.ExtractorInput in project ExoPlayer by google.

the class Sniffer method sniffInternal.

private static boolean sniffInternal(ExtractorInput input, boolean fragmented) throws IOException, InterruptedException {
    long inputLength = input.getLength();
    int bytesToSearch = (int) (inputLength == C.LENGTH_UNSET || inputLength > SEARCH_LENGTH ? SEARCH_LENGTH : inputLength);
    ParsableByteArray buffer = new ParsableByteArray(64);
    int bytesSearched = 0;
    boolean foundGoodFileType = false;
    boolean isFragmented = false;
    while (bytesSearched < bytesToSearch) {
        // Read an atom header.
        int headerSize = Atom.HEADER_SIZE;
        buffer.reset(headerSize);
        input.peekFully(buffer.data, 0, headerSize);
        long atomSize = buffer.readUnsignedInt();
        int atomType = buffer.readInt();
        if (atomSize == Atom.LONG_SIZE_PREFIX) {
            headerSize = Atom.LONG_HEADER_SIZE;
            input.peekFully(buffer.data, Atom.HEADER_SIZE, Atom.LONG_HEADER_SIZE - Atom.HEADER_SIZE);
            buffer.setLimit(Atom.LONG_HEADER_SIZE);
            atomSize = buffer.readUnsignedLongToLong();
        }
        if (atomSize < headerSize) {
            // The file is invalid because the atom size is too small for its header.
            return false;
        }
        bytesSearched += headerSize;
        if (atomType == Atom.TYPE_moov) {
            // Check for an mvex atom inside the moov atom to identify whether the file is fragmented.
            continue;
        }
        if (atomType == Atom.TYPE_moof || atomType == Atom.TYPE_mvex) {
            // The movie is fragmented. Stop searching as we must have read any ftyp atom already.
            isFragmented = true;
            break;
        }
        if (bytesSearched + atomSize - headerSize >= bytesToSearch) {
            // Stop searching as peeking this atom would exceed the search limit.
            break;
        }
        int atomDataSize = (int) (atomSize - headerSize);
        bytesSearched += atomDataSize;
        if (atomType == Atom.TYPE_ftyp) {
            // Parse the atom and check the file type/brand is compatible with the extractors.
            if (atomDataSize < 8) {
                return false;
            }
            buffer.reset(atomDataSize);
            input.peekFully(buffer.data, 0, atomDataSize);
            int brandsCount = atomDataSize / 4;
            for (int i = 0; i < brandsCount; i++) {
                if (i == 1) {
                    // This index refers to the minorVersion, not a brand, so skip it.
                    buffer.skipBytes(4);
                } else if (isCompatibleBrand(buffer.readInt())) {
                    foundGoodFileType = true;
                    break;
                }
            }
            if (!foundGoodFileType) {
                // The types were not compatible and there is only one ftyp atom, so reject the file.
                return false;
            }
        } else if (atomDataSize != 0) {
            // Skip the atom.
            input.advancePeekPosition(atomDataSize);
        }
    }
    return foundGoodFileType && fragmented == isFragmented;
}
Also used : ParsableByteArray(com.google.android.exoplayer2.util.ParsableByteArray)

Example 15 with ExtractorInput

use of com.google.android.exoplayer2.extractor.ExtractorInput in project ExoPlayer by google.

the class StreamReader method readPayload.

private int readPayload(ExtractorInput input, PositionHolder seekPosition) throws IOException, InterruptedException {
    long position = oggSeeker.read(input);
    if (position >= 0) {
        seekPosition.position = position;
        return Extractor.RESULT_SEEK;
    } else if (position < -1) {
        onSeekEnd(-(position + 2));
    }
    if (!seekMapSet) {
        SeekMap seekMap = oggSeeker.createSeekMap();
        extractorOutput.seekMap(seekMap);
        seekMapSet = true;
    }
    if (lengthOfReadPacket > 0 || oggPacket.populate(input)) {
        lengthOfReadPacket = 0;
        ParsableByteArray payload = oggPacket.getPayload();
        long granulesInPacket = preparePayload(payload);
        if (granulesInPacket >= 0 && currentGranule + granulesInPacket >= targetGranule) {
            // calculate time and send payload data to codec
            long timeUs = convertGranuleToTime(currentGranule);
            trackOutput.sampleData(payload, payload.limit());
            trackOutput.sampleMetadata(timeUs, C.BUFFER_FLAG_KEY_FRAME, payload.limit(), 0, null);
            targetGranule = -1;
        }
        currentGranule += granulesInPacket;
    } else {
        state = STATE_END_OF_INPUT;
        return Extractor.RESULT_END_OF_INPUT;
    }
    return Extractor.RESULT_CONTINUE;
}
Also used : ParsableByteArray(com.google.android.exoplayer2.util.ParsableByteArray) SeekMap(com.google.android.exoplayer2.extractor.SeekMap)

Aggregations

ExtractorInput (com.google.android.exoplayer2.extractor.ExtractorInput)20 FakeExtractorInput (com.google.android.exoplayer2.testutil.FakeExtractorInput)19 ParsableByteArray (com.google.android.exoplayer2.util.ParsableByteArray)12 ParserException (com.google.android.exoplayer2.ParserException)8 DefaultExtractorInput (com.google.android.exoplayer2.extractor.DefaultExtractorInput)5 DataSpec (com.google.android.exoplayer2.upstream.DataSpec)5 TrackOutput (com.google.android.exoplayer2.extractor.TrackOutput)4 SeekMap (com.google.android.exoplayer2.extractor.SeekMap)3 Format (com.google.android.exoplayer2.Format)2 Extractor (com.google.android.exoplayer2.extractor.Extractor)2 ContainerAtom (com.google.android.exoplayer2.extractor.mp4.Atom.ContainerAtom)2 EOFException (java.io.EOFException)2 LeafAtom (com.google.android.exoplayer2.extractor.mp4.Atom.LeafAtom)1 VorbisSetup (com.google.android.exoplayer2.extractor.ogg.VorbisReader.VorbisSetup)1 TrackIdGenerator (com.google.android.exoplayer2.extractor.ts.TsPayloadReader.TrackIdGenerator)1 Metadata (com.google.android.exoplayer2.metadata.Metadata)1 PrivFrame (com.google.android.exoplayer2.metadata.id3.PrivFrame)1 SimulatedIOException (com.google.android.exoplayer2.testutil.FakeExtractorInput.SimulatedIOException)1 FlacStreamInfo (com.google.android.exoplayer2.util.FlacStreamInfo)1 ParsableBitArray (com.google.android.exoplayer2.util.ParsableBitArray)1