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
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
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
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
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
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
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
File: OggExtractor.java
@Override public void init(ExtractorOutput output) { TrackOutput trackOutput = output.track(0); output.endTracks(); streamReader.init(output, trackOutput); }