/** * @param flags Flags that control the extractor's behavior. * @param timestampAdjuster Adjusts sample timestamps. May be null if no adjustment is needed. * @param sideloadedTrack Sideloaded track information, in the case that the extractor * will not receive a moov box in the input data. */ public FragmentedMp4Extractor(@Flags int flags, TimestampAdjuster timestampAdjuster, Track sideloadedTrack) { this.flags = flags | (sideloadedTrack != null ? FLAG_SIDELOADED : 0); this.timestampAdjuster = timestampAdjuster; this.sideloadedTrack = sideloadedTrack; atomHeader = new ParsableByteArray(Atom.LONG_HEADER_SIZE); nalStartCode = new ParsableByteArray(NalUnitUtil.NAL_START_CODE); nalPrefix = new ParsableByteArray(5); nalBuffer = new ParsableByteArray(); encryptionSignalByte = new ParsableByteArray(1); extendedTypeScratch = new byte[16]; containerAtoms = new Stack<>(); pendingMetadataSampleInfos = new LinkedList<>(); trackBundles = new SparseArray<>(); durationUs = C.TIME_UNSET; segmentIndexEarliestPresentationTimeUs = C.TIME_UNSET; enterReadingAtomHeaderState(); }
MatroskaExtractor(EbmlReader reader, @Flags int flags) { this.reader = reader; this.reader.init(new InnerEbmlReaderOutput()); seekForCuesEnabled = (flags & FLAG_DISABLE_SEEK_FOR_CUES) == 0; varintReader = new VarintReader(); tracks = new SparseArray<>(); scratch = new ParsableByteArray(4); vorbisNumPageSamples = new ParsableByteArray(ByteBuffer.allocate(4).putInt(-1).array()); seekEntryIdBytes = new ParsableByteArray(4); nalStartCode = new ParsableByteArray(NalUnitUtil.NAL_START_CODE); nalLength = new ParsableByteArray(4); sampleStrippedBytes = new ParsableByteArray(); subripSample = new ParsableByteArray(); encryptionInitializationVector = new ParsableByteArray(ENCRYPTION_IV_SIZE); encryptionSubsampleData = new ParsableByteArray(); }
/** * @param flags Flags that control the extractor's behavior. * @param sideloadedTrack Sideloaded track information, in the case that the extractor * will not receive a moov box in the input data. * @param timestampAdjuster Adjusts sample timestamps. May be null if no adjustment is needed. */ public FragmentedMp4Extractor(@Flags int flags, Track sideloadedTrack, TimestampAdjuster timestampAdjuster) { this.sideloadedTrack = sideloadedTrack; this.flags = flags | (sideloadedTrack != null ? FLAG_SIDELOADED : 0); this.timestampAdjuster = timestampAdjuster; atomHeader = new ParsableByteArray(Atom.LONG_HEADER_SIZE); nalStartCode = new ParsableByteArray(NalUnitUtil.NAL_START_CODE); nalLength = new ParsableByteArray(4); encryptionSignalByte = new ParsableByteArray(1); extendedTypeScratch = new byte[16]; containerAtoms = new Stack<>(); trackBundles = new SparseArray<>(); durationUs = C.TIME_UNSET; enterReadingAtomHeaderState(); }
/** * @param flags Flags that control the extractor's behavior. * @param timestampAdjuster Adjusts sample timestamps. May be null if no adjustment is needed. * @param sideloadedTrack Sideloaded track information, in the case that the extractor * will not receive a moov box in the input data. Null if a moov box is expected. * @param sideloadedDrmInitData The {@link DrmInitData} to use for encrypted tracks. If null, the * pssh boxes (if present) will be used. * @param closedCaptionFormats For tracks that contain SEI messages, the formats of the closed * caption channels to expose. */ public FragmentedMp4Extractor(@Flags int flags, TimestampAdjuster timestampAdjuster, Track sideloadedTrack, DrmInitData sideloadedDrmInitData, List<Format> closedCaptionFormats) { this.flags = flags | (sideloadedTrack != null ? FLAG_SIDELOADED : 0); this.timestampAdjuster = timestampAdjuster; this.sideloadedTrack = sideloadedTrack; this.sideloadedDrmInitData = sideloadedDrmInitData; this.closedCaptionFormats = Collections.unmodifiableList(closedCaptionFormats); atomHeader = new ParsableByteArray(Atom.LONG_HEADER_SIZE); nalStartCode = new ParsableByteArray(NalUnitUtil.NAL_START_CODE); nalPrefix = new ParsableByteArray(5); nalBuffer = new ParsableByteArray(); encryptionSignalByte = new ParsableByteArray(1); defaultInitializationVector = new ParsableByteArray(); extendedTypeScratch = new byte[16]; containerAtoms = new Stack<>(); pendingMetadataSampleInfos = new LinkedList<>(); trackBundles = new SparseArray<>(); durationUs = C.TIME_UNSET; segmentIndexEarliestPresentationTimeUs = C.TIME_UNSET; enterReadingAtomHeaderState(); }
MatroskaExtractor(EbmlReader reader, @Flags int flags) { this.reader = reader; this.reader.init(new InnerEbmlReaderOutput()); seekForCuesEnabled = (flags & FLAG_DISABLE_SEEK_FOR_CUES) == 0; varintReader = new VarintReader(); tracks = new SparseArray<>(); scratch = new ParsableByteArray(4); vorbisNumPageSamples = new ParsableByteArray(ByteBuffer.allocate(4).putInt(-1).array()); seekEntryIdBytes = new ParsableByteArray(4); nalStartCode = new ParsableByteArray(NalUnitUtil.NAL_START_CODE); nalLength = new ParsableByteArray(4); sampleStrippedBytes = new ParsableByteArray(); subtitleSample = new ParsableByteArray(); encryptionInitializationVector = new ParsableByteArray(ENCRYPTION_IV_SIZE); encryptionSubsampleData = new ParsableByteArray(); }
@Override public void seek() { NalUnitUtil.clearPrefixFlags(prefixFlags); csdBuffer.reset(); pesPtsUsAvailable = false; foundFirstFrameInGroup = false; totalBytesWritten = 0; }
@Override public void seek() { NalUnitUtil.clearPrefixFlags(prefixFlags); sps.reset(); pps.reset(); sei.reset(); sampleReader.reset(); totalBytesWritten = 0; }
@Override public void seek() { NalUnitUtil.clearPrefixFlags(prefixFlags); vps.reset(); sps.reset(); pps.reset(); prefixSei.reset(); suffixSei.reset(); sampleReader.reset(); totalBytesWritten = 0; }
/** * Parses AVC configuration data. * * @param data A {@link ParsableByteArray}, whose position is set to the start of the AVC * configuration data to parse. * @return A parsed representation of the HEVC configuration data. * @throws ParserException If an error occurred parsing the data. */ public static AvcConfig parse(ParsableByteArray data) throws ParserException { try { data.skipBytes(4); // Skip to the AVCDecoderConfigurationRecord (defined in 14496-15) int nalUnitLengthFieldLength = (data.readUnsignedByte() & 0x3) + 1; if (nalUnitLengthFieldLength == 3) { throw new IllegalStateException(); } List<byte[]> initializationData = new ArrayList<>(); int numSequenceParameterSets = data.readUnsignedByte() & 0x1F; for (int j = 0; j < numSequenceParameterSets; j++) { initializationData.add(buildNalUnitForChild(data)); } int numPictureParameterSets = data.readUnsignedByte(); for (int j = 0; j < numPictureParameterSets; j++) { initializationData.add(buildNalUnitForChild(data)); } int width = Format.NO_VALUE; int height = Format.NO_VALUE; float pixelWidthAspectRatio = 1; if (numSequenceParameterSets > 0) { byte[] sps = initializationData.get(0); SpsData spsData = NalUnitUtil.parseSpsNalUnit(initializationData.get(0), nalUnitLengthFieldLength, sps.length); width = spsData.width; height = spsData.height; pixelWidthAspectRatio = spsData.pixelWidthAspectRatio; } return new AvcConfig(initializationData, nalUnitLengthFieldLength, width, height, pixelWidthAspectRatio); } catch (ArrayIndexOutOfBoundsException e) { throw new ParserException("Error parsing AVC config", e); } }
MatroskaExtractor(EbmlReader reader) { this.reader = reader; this.reader.init(new InnerEbmlReaderOutput()); varintReader = new VarintReader(); tracks = new SparseArray<>(); scratch = new ParsableByteArray(4); vorbisNumPageSamples = new ParsableByteArray(ByteBuffer.allocate(4).putInt(-1).array()); seekEntryIdBytes = new ParsableByteArray(4); nalStartCode = new ParsableByteArray(NalUnitUtil.NAL_START_CODE); nalLength = new ParsableByteArray(4); sampleStrippedBytes = new ParsableByteArray(); subripSample = new ParsableByteArray(); encryptionInitializationVector = new ParsableByteArray(ENCRYPTION_IV_SIZE); encryptionSubsampleData = new ParsableByteArray(); }
public Mp4Extractor() { atomHeader = new ParsableByteArray(Atom.LONG_HEADER_SIZE); containerAtoms = new Stack<>(); nalStartCode = new ParsableByteArray(NalUnitUtil.NAL_START_CODE); nalLength = new ParsableByteArray(4); enterReadingAtomHeaderState(); }
@Override public void seek() { NalUnitUtil.clearPrefixFlags(prefixFlags); csdBuffer.reset(); totalBytesWritten = 0; startedFirstSample = false; }
/** * Creates a new extractor for unfragmented MP4 streams, using the specified flags to control the * extractor's behavior. * * @param flags Flags that control the extractor's behavior. */ public Mp4Extractor(@Flags int flags) { this.flags = flags; atomHeader = new ParsableByteArray(Atom.LONG_HEADER_SIZE); containerAtoms = new Stack<>(); nalStartCode = new ParsableByteArray(NalUnitUtil.NAL_START_CODE); nalLength = new ParsableByteArray(4); }
@Override public void consume(ParsableByteArray data) { int offset = data.getPosition(); int limit = data.limit(); byte[] dataArray = data.data; // Append the data to the buffer. totalBytesWritten += data.bytesLeft(); output.sampleData(data, data.bytesLeft()); int searchOffset = offset; while (true) { int startCodeOffset = NalUnitUtil.findNalUnit(dataArray, searchOffset, limit, prefixFlags); if (startCodeOffset == limit) { // We've scanned to the end of the data without finding another start code. if (!hasOutputFormat) { csdBuffer.onData(dataArray, offset, limit); } return; } // We've found a start code with the following value. int startCodeValue = data.data[startCodeOffset + 3] & 0xFF; if (!hasOutputFormat) { // This is the number of bytes from the current offset to the start of the next start // code. It may be negative if the start code started in the previously consumed data. int lengthToStartCode = startCodeOffset - offset; if (lengthToStartCode > 0) { csdBuffer.onData(dataArray, offset, startCodeOffset); } // This is the number of bytes belonging to the next start code that have already been // passed to csdDataTargetBuffer. int bytesAlreadyPassed = lengthToStartCode < 0 ? -lengthToStartCode : 0; if (csdBuffer.onStartCode(startCodeValue, bytesAlreadyPassed)) { // The csd data is complete, so we can decode and output the media format. Pair<Format, Long> result = parseCsdBuffer(csdBuffer, formatId); output.format(result.first); frameDurationUs = result.second; hasOutputFormat = true; } } if (hasOutputFormat && (startCodeValue == START_GROUP || startCodeValue == START_PICTURE)) { int bytesWrittenPastStartCode = limit - startCodeOffset; if (foundFirstFrameInGroup) { @C.BufferFlags int flags = isKeyframe ? C.BUFFER_FLAG_KEY_FRAME : 0; int size = (int) (totalBytesWritten - framePosition) - bytesWrittenPastStartCode; output.sampleMetadata(frameTimeUs, flags, size, bytesWrittenPastStartCode, null); isKeyframe = false; } if (startCodeValue == START_GROUP) { foundFirstFrameInGroup = false; isKeyframe = true; } else /* startCodeValue == START_PICTURE */ { frameTimeUs = pesPtsUsAvailable ? pesTimeUs : (frameTimeUs + frameDurationUs); framePosition = totalBytesWritten - bytesWrittenPastStartCode; pesPtsUsAvailable = false; foundFirstFrameInGroup = true; } } offset = startCodeOffset; searchOffset = offset + 3; } }
@Override public void consume(ParsableByteArray data) { int offset = data.getPosition(); int limit = data.limit(); byte[] dataArray = data.data; // Append the data to the buffer. totalBytesWritten += data.bytesLeft(); output.sampleData(data, data.bytesLeft()); // Scan the appended data, processing NAL units as they are encountered while (true) { int nalUnitOffset = NalUnitUtil.findNalUnit(dataArray, offset, limit, prefixFlags); if (nalUnitOffset == limit) { // We've scanned to the end of the data without finding the start of another NAL unit. nalUnitData(dataArray, offset, limit); return; } // We've seen the start of a NAL unit of the following type. int nalUnitType = NalUnitUtil.getNalUnitType(dataArray, nalUnitOffset); // This is the number of bytes from the current offset to the start of the next NAL unit. // It may be negative if the NAL unit started in the previously consumed data. int lengthToNalUnit = nalUnitOffset - offset; if (lengthToNalUnit > 0) { nalUnitData(dataArray, offset, nalUnitOffset); } int bytesWrittenPastPosition = limit - nalUnitOffset; long absolutePosition = totalBytesWritten - bytesWrittenPastPosition; // Indicate the end of the previous NAL unit. If the length to the start of the next unit // is negative then we wrote too many bytes to the NAL buffers. Discard the excess bytes // when notifying that the unit has ended. endNalUnit(absolutePosition, bytesWrittenPastPosition, lengthToNalUnit < 0 ? -lengthToNalUnit : 0, pesTimeUs); // Indicate the start of the next NAL unit. startNalUnit(absolutePosition, nalUnitType, pesTimeUs); // Continue scanning the data. offset = nalUnitOffset + 3; } }
public void putSps(NalUnitUtil.SpsData spsData) { sps.append(spsData.seqParameterSetId, spsData); }
public void putPps(NalUnitUtil.PpsData ppsData) { pps.append(ppsData.picParameterSetId, ppsData); }
@Override public void consume(ParsableByteArray data) { while (data.bytesLeft() > 0) { int offset = data.getPosition(); int limit = data.limit(); byte[] dataArray = data.data; // Append the data to the buffer. totalBytesWritten += data.bytesLeft(); output.sampleData(data, data.bytesLeft()); // Scan the appended data, processing NAL units as they are encountered while (offset < limit) { int nalUnitOffset = NalUnitUtil.findNalUnit(dataArray, offset, limit, prefixFlags); if (nalUnitOffset == limit) { // We've scanned to the end of the data without finding the start of another NAL unit. nalUnitData(dataArray, offset, limit); return; } // We've seen the start of a NAL unit of the following type. int nalUnitType = NalUnitUtil.getH265NalUnitType(dataArray, nalUnitOffset); // This is the number of bytes from the current offset to the start of the next NAL unit. // It may be negative if the NAL unit started in the previously consumed data. int lengthToNalUnit = nalUnitOffset - offset; if (lengthToNalUnit > 0) { nalUnitData(dataArray, offset, nalUnitOffset); } int bytesWrittenPastPosition = limit - nalUnitOffset; long absolutePosition = totalBytesWritten - bytesWrittenPastPosition; // Indicate the end of the previous NAL unit. If the length to the start of the next unit // is negative then we wrote too many bytes to the NAL buffers. Discard the excess bytes // when notifying that the unit has ended. endNalUnit(absolutePosition, bytesWrittenPastPosition, lengthToNalUnit < 0 ? -lengthToNalUnit : 0, pesTimeUs); // Indicate the start of the next NAL unit. startNalUnit(absolutePosition, bytesWrittenPastPosition, nalUnitType, pesTimeUs); // Continue scanning the data. offset = nalUnitOffset + 3; } } }
public Mp4Extractor() { atomHeader = new ParsableByteArray(Atom.LONG_HEADER_SIZE); containerAtoms = new Stack<>(); nalStartCode = new ParsableByteArray(NalUnitUtil.NAL_START_CODE); nalLength = new ParsableByteArray(4); }
/** * @param output A {@link TrackOutput} to which samples should be written. */ public VideoTagPayloadReader(TrackOutput output) { super(output); nalStartCode = new ParsableByteArray(NalUnitUtil.NAL_START_CODE); nalLength = new ParsableByteArray(4); }