Search in sources :

Example 41 with ExtractorInput

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

the class MatroskaExtractor method writeSampleData.

private void writeSampleData(ExtractorInput input, Track track, int size) throws IOException, InterruptedException {
    if (CODEC_ID_SUBRIP.equals(track.codecId)) {
        int sizeWithPrefix = SUBRIP_PREFIX.length + size;
        if (subripSample.capacity() < sizeWithPrefix) {
            // Initialize subripSample to contain the required prefix and have space to hold a subtitle
            // twice as long as this one.
            subripSample.data = Arrays.copyOf(SUBRIP_PREFIX, sizeWithPrefix + size);
        }
        input.readFully(subripSample.data, SUBRIP_PREFIX.length, size);
        subripSample.setPosition(0);
        subripSample.setLimit(sizeWithPrefix);
        // the correct end timecode, which we might not have yet.
        return;
    }
    TrackOutput output = track.output;
    if (!sampleEncodingHandled) {
        if (track.hasContentEncryption) {
            // If the sample is encrypted, read its encryption signal byte and set the IV size.
            // Clear the encrypted flag.
            blockFlags &= ~C.BUFFER_FLAG_ENCRYPTED;
            if (!sampleSignalByteRead) {
                input.readFully(scratch.data, 0, 1);
                sampleBytesRead++;
                if ((scratch.data[0] & 0x80) == 0x80) {
                    throw new ParserException("Extension bit is set in signal byte");
                }
                sampleSignalByte = scratch.data[0];
                sampleSignalByteRead = true;
            }
            boolean isEncrypted = (sampleSignalByte & 0x01) == 0x01;
            if (isEncrypted) {
                boolean hasSubsampleEncryption = (sampleSignalByte & 0x02) == 0x02;
                blockFlags |= C.BUFFER_FLAG_ENCRYPTED;
                if (!sampleInitializationVectorRead) {
                    input.readFully(encryptionInitializationVector.data, 0, ENCRYPTION_IV_SIZE);
                    sampleBytesRead += ENCRYPTION_IV_SIZE;
                    sampleInitializationVectorRead = true;
                    // Write the signal byte, containing the IV size and the subsample encryption flag.
                    scratch.data[0] = (byte) (ENCRYPTION_IV_SIZE | (hasSubsampleEncryption ? 0x80 : 0x00));
                    scratch.setPosition(0);
                    output.sampleData(scratch, 1);
                    sampleBytesWritten++;
                    // Write the IV.
                    encryptionInitializationVector.setPosition(0);
                    output.sampleData(encryptionInitializationVector, ENCRYPTION_IV_SIZE);
                    sampleBytesWritten += ENCRYPTION_IV_SIZE;
                }
                if (hasSubsampleEncryption) {
                    if (!samplePartitionCountRead) {
                        input.readFully(scratch.data, 0, 1);
                        sampleBytesRead++;
                        scratch.setPosition(0);
                        samplePartitionCount = scratch.readUnsignedByte();
                        samplePartitionCountRead = true;
                    }
                    int samplePartitionDataSize = samplePartitionCount * 4;
                    scratch.reset(samplePartitionDataSize);
                    input.readFully(scratch.data, 0, samplePartitionDataSize);
                    sampleBytesRead += samplePartitionDataSize;
                    short subsampleCount = (short) (1 + (samplePartitionCount / 2));
                    int subsampleDataSize = 2 + 6 * subsampleCount;
                    if (encryptionSubsampleDataBuffer == null || encryptionSubsampleDataBuffer.capacity() < subsampleDataSize) {
                        encryptionSubsampleDataBuffer = ByteBuffer.allocate(subsampleDataSize);
                    }
                    encryptionSubsampleDataBuffer.position(0);
                    encryptionSubsampleDataBuffer.putShort(subsampleCount);
                    // Loop through the partition offsets and write out the data in the way ExoPlayer
                    // wants it (ISO 23001-7 Part 7):
                    //   2 bytes - sub sample count.
                    //   for each sub sample:
                    //     2 bytes - clear data size.
                    //     4 bytes - encrypted data size.
                    int partitionOffset = 0;
                    for (int i = 0; i < samplePartitionCount; i++) {
                        int previousPartitionOffset = partitionOffset;
                        partitionOffset = scratch.readUnsignedIntToInt();
                        if ((i % 2) == 0) {
                            encryptionSubsampleDataBuffer.putShort((short) (partitionOffset - previousPartitionOffset));
                        } else {
                            encryptionSubsampleDataBuffer.putInt(partitionOffset - previousPartitionOffset);
                        }
                    }
                    int finalPartitionSize = size - sampleBytesRead - partitionOffset;
                    if ((samplePartitionCount % 2) == 1) {
                        encryptionSubsampleDataBuffer.putInt(finalPartitionSize);
                    } else {
                        encryptionSubsampleDataBuffer.putShort((short) finalPartitionSize);
                        encryptionSubsampleDataBuffer.putInt(0);
                    }
                    encryptionSubsampleData.reset(encryptionSubsampleDataBuffer.array(), subsampleDataSize);
                    output.sampleData(encryptionSubsampleData, subsampleDataSize);
                    sampleBytesWritten += subsampleDataSize;
                }
            }
        } else if (track.sampleStrippedBytes != null) {
            // If the sample has header stripping, prepare to read/output the stripped bytes first.
            sampleStrippedBytes.reset(track.sampleStrippedBytes, track.sampleStrippedBytes.length);
        }
        sampleEncodingHandled = true;
    }
    size += sampleStrippedBytes.limit();
    if (CODEC_ID_H264.equals(track.codecId) || CODEC_ID_H265.equals(track.codecId)) {
        // TODO: Deduplicate with Mp4Extractor.
        // Zero the top three bytes of the array that we'll use to decode nal unit lengths, in case
        // they're only 1 or 2 bytes long.
        byte[] nalLengthData = nalLength.data;
        nalLengthData[0] = 0;
        nalLengthData[1] = 0;
        nalLengthData[2] = 0;
        int nalUnitLengthFieldLength = track.nalUnitLengthFieldLength;
        int nalUnitLengthFieldLengthDiff = 4 - track.nalUnitLengthFieldLength;
        // start codes as we encounter them.
        while (sampleBytesRead < size) {
            if (sampleCurrentNalBytesRemaining == 0) {
                // Read the NAL length so that we know where we find the next one.
                readToTarget(input, nalLengthData, nalUnitLengthFieldLengthDiff, nalUnitLengthFieldLength);
                nalLength.setPosition(0);
                sampleCurrentNalBytesRemaining = nalLength.readUnsignedIntToInt();
                // Write a start code for the current NAL unit.
                nalStartCode.setPosition(0);
                output.sampleData(nalStartCode, 4);
                sampleBytesWritten += 4;
            } else {
                // Write the payload of the NAL unit.
                sampleCurrentNalBytesRemaining -= readToOutput(input, output, sampleCurrentNalBytesRemaining);
            }
        }
    } else {
        while (sampleBytesRead < size) {
            readToOutput(input, output, size - sampleBytesRead);
        }
    }
    if (CODEC_ID_VORBIS.equals(track.codecId)) {
        // Vorbis decoder in android MediaCodec [1] expects the last 4 bytes of the sample to be the
        // number of samples in the current page. This definition holds good only for Ogg and
        // irrelevant for Matroska. So we always set this to -1 (the decoder will ignore this value if
        // we set it to -1). The android platform media extractor [2] does the same.
        // [1] https://android.googlesource.com/platform/frameworks/av/+/lollipop-release/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp#314
        // [2] https://android.googlesource.com/platform/frameworks/av/+/lollipop-release/media/libstagefright/NuMediaExtractor.cpp#474
        vorbisNumPageSamples.setPosition(0);
        output.sampleData(vorbisNumPageSamples, 4);
        sampleBytesWritten += 4;
    }
}
Also used : ParserException(com.google.android.exoplayer2.ParserException) TrackOutput(com.google.android.exoplayer2.extractor.TrackOutput)

Example 42 with ExtractorInput

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

the class Mp3Extractor method setupSeeker.

/**
   * Returns a {@link Seeker} to seek using metadata read from {@code input}, which should provide
   * data from the start of the first frame in the stream. On returning, the input's position will
   * be set to the start of the first frame of audio.
   *
   * @param input The {@link ExtractorInput} from which to read.
   * @throws IOException Thrown if there was an error reading from the stream. Not expected if the
   *     next two frames were already peeked during synchronization.
   * @throws InterruptedException Thrown if reading from the stream was interrupted. Not expected if
   *     the next two frames were already peeked during synchronization.
   * @return a {@link Seeker}.
   */
private Seeker setupSeeker(ExtractorInput input) throws IOException, InterruptedException {
    // Read the first frame which may contain a Xing or VBRI header with seeking metadata.
    ParsableByteArray frame = new ParsableByteArray(synchronizedHeader.frameSize);
    input.peekFully(frame.data, 0, synchronizedHeader.frameSize);
    long position = input.getPosition();
    long length = input.getLength();
    int headerData = 0;
    Seeker seeker = null;
    // Check if there is a Xing header.
    int xingBase = (synchronizedHeader.version & 1) != 0 ? // MPEG 1
    (synchronizedHeader.channels != 1 ? 36 : 21) : // MPEG 2 or 2.5
    (synchronizedHeader.channels != 1 ? 21 : 13);
    if (frame.limit() >= xingBase + 4) {
        frame.setPosition(xingBase);
        headerData = frame.readInt();
    }
    if (headerData == XING_HEADER || headerData == INFO_HEADER) {
        seeker = XingSeeker.create(synchronizedHeader, frame, position, length);
        if (seeker != null && !gaplessInfoHolder.hasGaplessInfo()) {
            // If there is a Xing header, read gapless playback metadata at a fixed offset.
            input.resetPeekPosition();
            input.advancePeekPosition(xingBase + 141);
            input.peekFully(scratch.data, 0, 3);
            scratch.setPosition(0);
            gaplessInfoHolder.setFromXingHeaderValue(scratch.readUnsignedInt24());
        }
        input.skipFully(synchronizedHeader.frameSize);
    } else if (frame.limit() >= 40) {
        // Check if there is a VBRI header.
        // MPEG audio header (4 bytes) + 32 bytes.
        frame.setPosition(36);
        headerData = frame.readInt();
        if (headerData == VBRI_HEADER) {
            seeker = VbriSeeker.create(synchronizedHeader, frame, position, length);
            input.skipFully(synchronizedHeader.frameSize);
        }
    }
    if (seeker == null || (!seeker.isSeekable() && (flags & FLAG_ENABLE_CONSTANT_BITRATE_SEEKING) != 0)) {
        // Repopulate the synchronized header in case we had to skip an invalid seeking header, which
        // would give an invalid CBR bitrate.
        input.resetPeekPosition();
        input.peekFully(scratch.data, 0, 4);
        scratch.setPosition(0);
        MpegAudioHeader.populateHeader(scratch.readInt(), synchronizedHeader);
        seeker = new ConstantBitrateSeeker(input.getPosition(), synchronizedHeader.bitrate, length);
    }
    return seeker;
}
Also used : ParsableByteArray(com.google.android.exoplayer2.util.ParsableByteArray)

Example 43 with ExtractorInput

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

the class Mp4Extractor method readSample.

/**
   * Attempts to extract the next sample in the current mdat atom for the specified track.
   * <p>
   * Returns {@link #RESULT_SEEK} if the source should be reloaded from the position in
   * {@code positionHolder}.
   * <p>
   * Returns {@link #RESULT_END_OF_INPUT} if no samples are left. Otherwise, returns
   * {@link #RESULT_CONTINUE}.
   *
   * @param input The {@link ExtractorInput} from which to read data.
   * @param positionHolder If {@link #RESULT_SEEK} is returned, this holder is updated to hold the
   *     position of the required data.
   * @return One of the {@code RESULT_*} flags in {@link Extractor}.
   * @throws IOException If an error occurs reading from the input.
   * @throws InterruptedException If the thread is interrupted.
   */
private int readSample(ExtractorInput input, PositionHolder positionHolder) throws IOException, InterruptedException {
    int trackIndex = getTrackIndexOfEarliestCurrentSample();
    if (trackIndex == C.INDEX_UNSET) {
        return RESULT_END_OF_INPUT;
    }
    Mp4Track track = tracks[trackIndex];
    TrackOutput trackOutput = track.trackOutput;
    int sampleIndex = track.sampleIndex;
    long position = track.sampleTable.offsets[sampleIndex];
    int sampleSize = track.sampleTable.sizes[sampleIndex];
    if (track.track.sampleTransformation == Track.TRANSFORMATION_CEA608_CDAT) {
        // The sample information is contained in a cdat atom. The header must be discarded for
        // committing.
        position += Atom.HEADER_SIZE;
        sampleSize -= Atom.HEADER_SIZE;
    }
    long skipAmount = position - input.getPosition() + sampleBytesWritten;
    if (skipAmount < 0 || skipAmount >= RELOAD_MINIMUM_SEEK_DISTANCE) {
        positionHolder.position = position;
        return RESULT_SEEK;
    }
    input.skipFully((int) skipAmount);
    if (track.track.nalUnitLengthFieldLength != 0) {
        // Zero the top three bytes of the array that we'll use to decode nal unit lengths, in case
        // they're only 1 or 2 bytes long.
        byte[] nalLengthData = nalLength.data;
        nalLengthData[0] = 0;
        nalLengthData[1] = 0;
        nalLengthData[2] = 0;
        int nalUnitLengthFieldLength = track.track.nalUnitLengthFieldLength;
        int nalUnitLengthFieldLengthDiff = 4 - track.track.nalUnitLengthFieldLength;
        // start codes as we encounter them.
        while (sampleBytesWritten < sampleSize) {
            if (sampleCurrentNalBytesRemaining == 0) {
                // Read the NAL length so that we know where we find the next one.
                input.readFully(nalLength.data, nalUnitLengthFieldLengthDiff, nalUnitLengthFieldLength);
                nalLength.setPosition(0);
                sampleCurrentNalBytesRemaining = nalLength.readUnsignedIntToInt();
                // Write a start code for the current NAL unit.
                nalStartCode.setPosition(0);
                trackOutput.sampleData(nalStartCode, 4);
                sampleBytesWritten += 4;
                sampleSize += nalUnitLengthFieldLengthDiff;
            } else {
                // Write the payload of the NAL unit.
                int writtenBytes = trackOutput.sampleData(input, sampleCurrentNalBytesRemaining, false);
                sampleBytesWritten += writtenBytes;
                sampleCurrentNalBytesRemaining -= writtenBytes;
            }
        }
    } else {
        while (sampleBytesWritten < sampleSize) {
            int writtenBytes = trackOutput.sampleData(input, sampleSize - sampleBytesWritten, false);
            sampleBytesWritten += writtenBytes;
            sampleCurrentNalBytesRemaining -= writtenBytes;
        }
    }
    trackOutput.sampleMetadata(track.sampleTable.timestampsUs[sampleIndex], track.sampleTable.flags[sampleIndex], sampleSize, 0, null);
    track.sampleIndex++;
    sampleBytesWritten = 0;
    sampleCurrentNalBytesRemaining = 0;
    return RESULT_CONTINUE;
}
Also used : TrackOutput(com.google.android.exoplayer2.extractor.TrackOutput)

Example 44 with ExtractorInput

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

the class OggExtractor method sniff.

@Override
public boolean sniff(ExtractorInput input) throws IOException, InterruptedException {
    try {
        OggPageHeader header = new OggPageHeader();
        if (!header.populate(input, true) || (header.type & 0x02) != 0x02) {
            return false;
        }
        int length = Math.min(header.bodySize, MAX_VERIFICATION_BYTES);
        ParsableByteArray scratch = new ParsableByteArray(length);
        input.peekFully(scratch.data, 0, length);
        if (FlacReader.verifyBitstreamType(resetPosition(scratch))) {
            streamReader = new FlacReader();
        } else if (VorbisReader.verifyBitstreamType(resetPosition(scratch))) {
            streamReader = new VorbisReader();
        } else if (OpusReader.verifyBitstreamType(resetPosition(scratch))) {
            streamReader = new OpusReader();
        } else {
            return false;
        }
        return true;
    } catch (ParserException e) {
        return false;
    }
}
Also used : ParsableByteArray(com.google.android.exoplayer2.util.ParsableByteArray) ParserException(com.google.android.exoplayer2.ParserException)

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