com.google.android.exoplayer.extractor.TrackOutput

Here are the examples of the java api class com.google.android.exoplayer.extractor.TrackOutput taken from open source projects.

1. FragmentedMp4Extractor#appendSampleEncryptionData()

Project: ExoPlayer
File: FragmentedMp4Extractor.java
/**
   * Appends the corresponding encryption data to the {@link TrackOutput} contained in the given
   * {@link TrackBundle}.
   *
   * @param trackBundle The {@link TrackBundle} that contains the {@link Track} for which the
   *     Sample encryption data must be output.
   * @return The number of written bytes.
   */
private int appendSampleEncryptionData(TrackBundle trackBundle) {
    TrackFragment trackFragment = trackBundle.fragment;
    ParsableByteArray sampleEncryptionData = trackFragment.sampleEncryptionData;
    int sampleDescriptionIndex = trackFragment.header.sampleDescriptionIndex;
    TrackEncryptionBox encryptionBox = trackFragment.trackEncryptionBox != null ? trackFragment.trackEncryptionBox : trackBundle.track.sampleDescriptionEncryptionBoxes[sampleDescriptionIndex];
    int vectorSize = encryptionBox.initializationVectorSize;
    boolean subsampleEncryption = trackFragment.sampleHasSubsampleEncryptionTable[trackBundle.currentSampleIndex];
    // Write the signal byte, containing the vector size and the subsample encryption flag.
    encryptionSignalByte.data[0] = (byte) (vectorSize | (subsampleEncryption ? 0x80 : 0));
    encryptionSignalByte.setPosition(0);
    TrackOutput output = trackBundle.output;
    output.sampleData(encryptionSignalByte, 1);
    // Write the vector.
    output.sampleData(sampleEncryptionData, vectorSize);
    // If we don't have subsample encryption data, we're done.
    if (!subsampleEncryption) {
        return 1 + vectorSize;
    }
    // Write the subsample encryption data.
    int subsampleCount = sampleEncryptionData.readUnsignedShort();
    sampleEncryptionData.skipBytes(-2);
    int subsampleDataLength = 2 + 6 * subsampleCount;
    output.sampleData(sampleEncryptionData, subsampleDataLength);
    return 1 + vectorSize + subsampleDataLength;
}

2. WebvttExtractor#processSample()

Project: ExoPlayer
File: WebvttExtractor.java
private void processSample() throws ParserException {
    ParsableByteArray webvttData = new ParsableByteArray(sampleData);
    // Validate the first line of the header.
    WebvttParserUtil.validateWebvttHeaderLine(webvttData);
    // Defaults to use if the header doesn't contain an X-TIMESTAMP-MAP header.
    long vttTimestampUs = 0;
    long tsTimestampUs = 0;
    // Parse the remainder of the header looking for X-TIMESTAMP-MAP.
    String line;
    while (!TextUtils.isEmpty(line = webvttData.readLine())) {
        if (line.startsWith("X-TIMESTAMP-MAP")) {
            Matcher localTimestampMatcher = LOCAL_TIMESTAMP.matcher(line);
            if (!localTimestampMatcher.find()) {
                throw new ParserException("X-TIMESTAMP-MAP doesn't contain local timestamp: " + line);
            }
            Matcher mediaTimestampMatcher = MEDIA_TIMESTAMP.matcher(line);
            if (!mediaTimestampMatcher.find()) {
                throw new ParserException("X-TIMESTAMP-MAP doesn't contain media timestamp: " + line);
            }
            vttTimestampUs = WebvttParserUtil.parseTimestampUs(localTimestampMatcher.group(1));
            tsTimestampUs = PtsTimestampAdjuster.ptsToUs(Long.parseLong(mediaTimestampMatcher.group(1)));
        }
    }
    // Find the first cue header and parse the start time.
    Matcher cueHeaderMatcher = WebvttCueParser.findNextCueHeader(webvttData);
    if (cueHeaderMatcher == null) {
        // No cues found. Don't output a sample, but still output a corresponding track.
        buildTrackOutput(0);
        return;
    }
    long firstCueTimeUs = WebvttParserUtil.parseTimestampUs(cueHeaderMatcher.group(1));
    long sampleTimeUs = ptsTimestampAdjuster.adjustTimestamp(PtsTimestampAdjuster.usToPts(firstCueTimeUs + tsTimestampUs - vttTimestampUs));
    long subsampleOffsetUs = sampleTimeUs - firstCueTimeUs;
    // Output the track.
    TrackOutput trackOutput = buildTrackOutput(subsampleOffsetUs);
    // Output the sample.
    sampleDataWrapper.reset(sampleData, sampleSize);
    trackOutput.sampleData(sampleDataWrapper, sampleSize);
    trackOutput.sampleMetadata(sampleTimeUs, C.SAMPLE_FLAG_SYNC, sampleSize, 0, null);
}

3. WebvttExtractor#buildTrackOutput()

Project: ExoPlayer
File: WebvttExtractor.java
private TrackOutput buildTrackOutput(long subsampleOffsetUs) {
    TrackOutput trackOutput = output.track(0);
    trackOutput.format(MediaFormat.createTextFormat("id", MimeTypes.TEXT_VTT, MediaFormat.NO_VALUE, C.UNKNOWN_TIME_US, "en", subsampleOffsetUs));
    output.endTracks();
    return trackOutput;
}

4. Mp4Extractor#readSample()

Project: ExoPlayer
File: Mp4Extractor.java
/**
   * 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 == TrackSampleTable.NO_SAMPLE) {
        return RESULT_END_OF_INPUT;
    }
    Mp4Track track = tracks[trackIndex];
    TrackOutput trackOutput = track.trackOutput;
    int sampleIndex = track.sampleIndex;
    long position = track.sampleTable.offsets[sampleIndex];
    long skipAmount = position - input.getPosition() + sampleBytesWritten;
    if (skipAmount < 0 || skipAmount >= RELOAD_MINIMUM_SEEK_DISTANCE) {
        positionHolder.position = position;
        return RESULT_SEEK;
    }
    input.skipFully((int) skipAmount);
    sampleSize = track.sampleTable.sizes[sampleIndex];
    if (track.track.nalUnitLengthFieldLength != -1) {
        // Zero the top three bytes of the array that we'll use to parse 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;
}

5. FragmentedMp4Extractor#readSample()

Project: ExoPlayer
File: FragmentedMp4Extractor.java
/**
   * Attempts to extract the next sample in the current mdat atom.
   * <p>
   * If there are no more samples in the current mdat atom then the parser state is transitioned
   * to {@link #STATE_READING_ATOM_HEADER} and {@code false} is returned.
   * <p>
   * It is possible for a sample to be extracted in part in the case that an exception is thrown. In
   * this case the method can be called again to extract the remainder of the sample.
   *
   * @param input The {@link ExtractorInput} from which to read data.
   * @return True if a sample was extracted. False otherwise.
   * @throws IOException If an error occurs reading from the input.
   * @throws InterruptedException If the thread is interrupted.
   */
private boolean readSample(ExtractorInput input) throws IOException, InterruptedException {
    if (parserState == STATE_READING_SAMPLE_START) {
        if (currentTrackBundle == null) {
            currentTrackBundle = getNextFragmentRun(trackBundles);
            if (currentTrackBundle == null) {
                // We've run out of samples in the current mdat. Discard any trailing data and prepare to
                // read the header of the next atom.
                int bytesToSkip = (int) (endOfMdatPosition - input.getPosition());
                if (bytesToSkip < 0) {
                    throw new ParserException("Offset to end of mdat was negative.");
                }
                input.skipFully(bytesToSkip);
                enterReadingAtomHeaderState();
                return false;
            }
            long nextDataPosition = currentTrackBundle.fragment.dataPosition;
            // We skip bytes preceding the next sample to read.
            int bytesToSkip = (int) (nextDataPosition - input.getPosition());
            if (bytesToSkip < 0) {
                throw new ParserException("Offset to sample data was negative.");
            }
            input.skipFully(bytesToSkip);
        }
        sampleSize = currentTrackBundle.fragment.sampleSizeTable[currentTrackBundle.currentSampleIndex];
        if (currentTrackBundle.fragment.definesEncryptionData) {
            sampleBytesWritten = appendSampleEncryptionData(currentTrackBundle);
            sampleSize += sampleBytesWritten;
        } else {
            sampleBytesWritten = 0;
        }
        parserState = STATE_READING_SAMPLE_CONTINUE;
        sampleCurrentNalBytesRemaining = 0;
    }
    TrackFragment fragment = currentTrackBundle.fragment;
    Track track = currentTrackBundle.track;
    TrackOutput output = currentTrackBundle.output;
    int sampleIndex = currentTrackBundle.currentSampleIndex;
    if (track.nalUnitLengthFieldLength != -1) {
        // Zero the top three bytes of the array that we'll use to parse 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 (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);
                output.sampleData(nalStartCode, 4);
                sampleBytesWritten += 4;
                sampleSize += nalUnitLengthFieldLengthDiff;
            } else {
                // Write the payload of the NAL unit.
                int writtenBytes = output.sampleData(input, sampleCurrentNalBytesRemaining, false);
                sampleBytesWritten += writtenBytes;
                sampleCurrentNalBytesRemaining -= writtenBytes;
            }
        }
    } else {
        while (sampleBytesWritten < sampleSize) {
            int writtenBytes = output.sampleData(input, sampleSize - sampleBytesWritten, false);
            sampleBytesWritten += writtenBytes;
        }
    }
    long sampleTimeUs = fragment.getSamplePresentationTime(sampleIndex) * 1000L;
    int sampleFlags = (fragment.definesEncryptionData ? C.SAMPLE_FLAG_ENCRYPTED : 0) | (fragment.sampleIsSyncFrameTable[sampleIndex] ? C.SAMPLE_FLAG_SYNC : 0);
    int sampleDescriptionIndex = fragment.header.sampleDescriptionIndex;
    byte[] encryptionKey = null;
    if (fragment.definesEncryptionData) {
        encryptionKey = fragment.trackEncryptionBox != null ? fragment.trackEncryptionBox.keyId : track.sampleDescriptionEncryptionBoxes[sampleDescriptionIndex].keyId;
    }
    output.sampleMetadata(sampleTimeUs, sampleFlags, sampleSize, 0, encryptionKey);
    currentTrackBundle.currentSampleIndex++;
    if (currentTrackBundle.currentSampleIndex == fragment.length) {
        currentTrackBundle = null;
    }
    parserState = STATE_READING_SAMPLE_START;
    return true;
}

6. WebmExtractor#writeSampleData()

Project: ExoPlayer
File: WebmExtractor.java
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.SAMPLE_FLAG_ENCRYPTED;
            input.readFully(scratch.data, 0, 1);
            sampleBytesRead++;
            if ((scratch.data[0] & 0x80) == 0x80) {
                throw new ParserException("Extension bit is set in signal byte");
            }
            if ((scratch.data[0] & 0x01) == 0x01) {
                scratch.data[0] = (byte) ENCRYPTION_IV_SIZE;
                scratch.setPosition(0);
                output.sampleData(scratch, 1);
                sampleBytesWritten++;
                blockFlags |= C.SAMPLE_FLAG_ENCRYPTED;
            }
        } 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 parse 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 WebM. 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;
    }
}

7. OggExtractor#init()

Project: ExoPlayer
File: OggExtractor.java
@Override
public void init(ExtractorOutput output) {
    TrackOutput trackOutput = output.track(0);
    output.endTracks();
    streamReader.init(output, trackOutput);
}