/** * @param mode Mode for the extractor. One of {@link #MODE_MULTI_PMT}, {@link #MODE_SINGLE_PMT} * and {@link #MODE_HLS}. * @param timestampAdjuster A timestamp adjuster for offsetting and scaling sample timestamps. * @param payloadReaderFactory Factory for injecting a custom set of payload readers. */ public TsExtractor(@Mode int mode, TimestampAdjuster timestampAdjuster, TsPayloadReader.Factory payloadReaderFactory) { this.payloadReaderFactory = Assertions.checkNotNull(payloadReaderFactory); this.mode = mode; if (mode == MODE_SINGLE_PMT || mode == MODE_HLS) { timestampAdjusters = Collections.singletonList(timestampAdjuster); } else { timestampAdjusters = new ArrayList<>(); timestampAdjusters.add(timestampAdjuster); } tsPacketBuffer = new ParsableByteArray(BUFFER_SIZE); tsScratch = new ParsableBitArray(new byte[3]); trackIds = new SparseBooleanArray(); tsPayloadReaders = new SparseArray<>(); continuityCounters = new SparseIntArray(); resetPayloadReaders(); }
/** * @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(); }
/** * @param mode Mode for the extractor. One of {@link #MODE_NORMAL}, {@link #MODE_SINGLE_PMT} * and {@link #MODE_HLS}. * @param timestampAdjuster A timestamp adjuster for offsetting and scaling sample timestamps. * @param payloadReaderFactory Factory for injecting a custom set of payload readers. */ public TsExtractor(@Mode int mode, TimestampAdjuster timestampAdjuster, TsPayloadReader.Factory payloadReaderFactory) { this.payloadReaderFactory = Assertions.checkNotNull(payloadReaderFactory); this.mode = mode; if (mode == MODE_SINGLE_PMT || mode == MODE_HLS) { timestampAdjusters = Collections.singletonList(timestampAdjuster); } else { timestampAdjusters = new ArrayList<>(); timestampAdjusters.add(timestampAdjuster); } tsPacketBuffer = new ParsableByteArray(BUFFER_SIZE); tsScratch = new ParsableBitArray(new byte[3]); trackIds = new SparseBooleanArray(); tsPayloadReaders = new SparseArray<>(); continuityCounters = new SparseIntArray(); resetPayloadReaders(); }
/** * @param mode Mode for the extractor. One of {@link #MODE_MULTI_PMT}, {@link #MODE_SINGLE_PMT} * and {@link #MODE_HLS}. * @param timestampAdjuster A timestamp adjuster for offsetting and scaling sample timestamps. * @param payloadReaderFactory Factory for injecting a custom set of payload readers. */ public TsExtractor(@Mode int mode, TimestampAdjuster timestampAdjuster, TsPayloadReader.Factory payloadReaderFactory) { this.payloadReaderFactory = Assertions.checkNotNull(payloadReaderFactory); this.mode = mode; if (mode == MODE_SINGLE_PMT || mode == MODE_HLS) { timestampAdjusters = Collections.singletonList(timestampAdjuster); } else { timestampAdjusters = new ArrayList<>(); timestampAdjusters.add(timestampAdjuster); } tsPacketBuffer = new ParsableByteArray(BUFFER_SIZE); trackIds = new SparseBooleanArray(); tsPayloadReaders = new SparseArray<>(); continuityCounters = new SparseIntArray(); resetPayloadReaders(); }
/** * @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(); }
public void testCustomPesReader() throws Exception { CustomTsPayloadReaderFactory factory = new CustomTsPayloadReaderFactory(true, false); TsExtractor tsExtractor = new TsExtractor(TsExtractor.MODE_MULTI_PMT, new TimestampAdjuster(0), factory); FakeExtractorInput input = new FakeExtractorInput.Builder() .setData(TestUtil.getByteArray(getInstrumentation(), "ts/sample.ts")) .setSimulateIOErrors(false) .setSimulateUnknownLength(false) .setSimulatePartialReads(false).build(); FakeExtractorOutput output = new FakeExtractorOutput(); tsExtractor.init(output); PositionHolder seekPositionHolder = new PositionHolder(); int readResult = Extractor.RESULT_CONTINUE; while (readResult != Extractor.RESULT_END_OF_INPUT) { readResult = tsExtractor.read(input, seekPositionHolder); } CustomEsReader reader = factory.esReader; assertEquals(2, reader.packetsRead); TrackOutput trackOutput = reader.getTrackOutput(); assertTrue(trackOutput == output.trackOutputs.get(257 /* PID of audio track. */)); assertEquals( Format.createTextSampleFormat("1/257", "mime", null, 0, 0, "und", null, 0), ((FakeTrackOutput) trackOutput).format); }
public void testCustomInitialSectionReader() throws Exception { CustomTsPayloadReaderFactory factory = new CustomTsPayloadReaderFactory(false, true); TsExtractor tsExtractor = new TsExtractor(TsExtractor.MODE_MULTI_PMT, new TimestampAdjuster(0), factory); FakeExtractorInput input = new FakeExtractorInput.Builder() .setData(TestUtil.getByteArray(getInstrumentation(), "ts/sample_with_sdt.ts")) .setSimulateIOErrors(false) .setSimulateUnknownLength(false) .setSimulatePartialReads(false).build(); tsExtractor.init(new FakeExtractorOutput()); PositionHolder seekPositionHolder = new PositionHolder(); int readResult = Extractor.RESULT_CONTINUE; while (readResult != Extractor.RESULT_END_OF_INPUT) { readResult = tsExtractor.read(input, seekPositionHolder); } assertEquals(1, factory.sdtReader.consumedSdts); }
/** * @param dataSource The source from which the data should be loaded. * @param dataSpec Defines the data to be loaded. * @param initDataSpec Defines the initialization data to be fed to new extractors. May be null. * @param hlsUrl The url of the playlist from which this chunk was obtained. * @param muxedCaptionFormats List of muxed caption {@link Format}s. Null if no closed caption * information is available in the master playlist. * @param trackSelectionReason See {@link #trackSelectionReason}. * @param trackSelectionData See {@link #trackSelectionData}. * @param startTimeUs The start time of the chunk in microseconds. * @param endTimeUs The end time of the chunk in microseconds. * @param chunkIndex The media sequence number of the chunk. * @param discontinuitySequenceNumber The discontinuity sequence number of the chunk. * @param isMasterTimestampSource True if the chunk can initialize the timestamp adjuster. * @param timestampAdjuster Adjuster corresponding to the provided discontinuity sequence number. * @param previousChunk The {@link HlsMediaChunk} that preceded this one. May be null. * @param encryptionKey For AES encryption chunks, the encryption key. * @param encryptionIv For AES encryption chunks, the encryption initialization vector. */ public HlsMediaChunk(DataSource dataSource, DataSpec dataSpec, DataSpec initDataSpec, HlsUrl hlsUrl, List<Format> muxedCaptionFormats, int trackSelectionReason, Object trackSelectionData, long startTimeUs, long endTimeUs, int chunkIndex, int discontinuitySequenceNumber, boolean isMasterTimestampSource, TimestampAdjuster timestampAdjuster, HlsMediaChunk previousChunk, byte[] encryptionKey, byte[] encryptionIv) { super(buildDataSource(dataSource, encryptionKey, encryptionIv), dataSpec, hlsUrl.format, trackSelectionReason, trackSelectionData, startTimeUs, endTimeUs, chunkIndex); this.discontinuitySequenceNumber = discontinuitySequenceNumber; this.initDataSpec = initDataSpec; this.hlsUrl = hlsUrl; this.muxedCaptionFormats = muxedCaptionFormats; this.isMasterTimestampSource = isMasterTimestampSource; this.timestampAdjuster = timestampAdjuster; // Note: this.dataSource and dataSource may be different. this.isEncrypted = this.dataSource instanceof Aes128DataSource; lastPathSegment = dataSpec.uri.getLastPathSegment(); isPackedAudio = lastPathSegment.endsWith(AAC_FILE_EXTENSION) || lastPathSegment.endsWith(AC3_FILE_EXTENSION) || lastPathSegment.endsWith(EC3_FILE_EXTENSION) || lastPathSegment.endsWith(MP3_FILE_EXTENSION); if (previousChunk != null) { id3Decoder = previousChunk.id3Decoder; id3Data = previousChunk.id3Data; previousExtractor = previousChunk.extractor; shouldSpliceIn = previousChunk.hlsUrl != hlsUrl; needNewExtractor = previousChunk.discontinuitySequenceNumber != discontinuitySequenceNumber || shouldSpliceIn; } else { id3Decoder = isPackedAudio ? new Id3Decoder() : null; id3Data = isPackedAudio ? new ParsableByteArray(Id3Decoder.ID3_HEADER_LENGTH) : null; previousExtractor = null; shouldSpliceIn = false; needNewExtractor = true; } initDataSource = dataSource; uid = UID_SOURCE.getAndIncrement(); }
@Override public void init(TimestampAdjuster timestampAdjuster, ExtractorOutput extractorOutput, TsPayloadReader.TrackIdGenerator idGenerator) { this.timestampAdjuster = timestampAdjuster; idGenerator.generateNewId(); output = extractorOutput.track(idGenerator.getTrackId(), C.TRACK_TYPE_METADATA); output.format(Format.createSampleFormat(idGenerator.getFormatId(), MimeTypes.APPLICATION_SCTE35, null, Format.NO_VALUE, null)); }
/** * @param dataSource The source from which the data should be loaded. * @param dataSpec Defines the data to be loaded. * @param initDataSpec Defines the initialization data to be fed to new extractors. May be null. * @param hlsUrl The url of the playlist from which this chunk was obtained. * @param muxedCaptionFormats List of muxed caption {@link Format}s. * @param trackSelectionReason See {@link #trackSelectionReason}. * @param trackSelectionData See {@link #trackSelectionData}. * @param startTimeUs The start time of the chunk in microseconds. * @param endTimeUs The end time of the chunk in microseconds. * @param chunkIndex The media sequence number of the chunk. * @param discontinuitySequenceNumber The discontinuity sequence number of the chunk. * @param isMasterTimestampSource True if the chunk can initialize the timestamp adjuster. * @param timestampAdjuster Adjuster corresponding to the provided discontinuity sequence number. * @param previousChunk The {@link HlsMediaChunk} that preceded this one. May be null. * @param encryptionKey For AES encryption chunks, the encryption key. * @param encryptionIv For AES encryption chunks, the encryption initialization vector. */ public HlsMediaChunk(DataSource dataSource, DataSpec dataSpec, DataSpec initDataSpec, HlsUrl hlsUrl, List<Format> muxedCaptionFormats, int trackSelectionReason, Object trackSelectionData, long startTimeUs, long endTimeUs, int chunkIndex, int discontinuitySequenceNumber, boolean isMasterTimestampSource, TimestampAdjuster timestampAdjuster, HlsMediaChunk previousChunk, byte[] encryptionKey, byte[] encryptionIv) { super(buildDataSource(dataSource, encryptionKey, encryptionIv), dataSpec, hlsUrl.format, trackSelectionReason, trackSelectionData, startTimeUs, endTimeUs, chunkIndex); this.discontinuitySequenceNumber = discontinuitySequenceNumber; this.initDataSpec = initDataSpec; this.hlsUrl = hlsUrl; this.muxedCaptionFormats = muxedCaptionFormats; this.isMasterTimestampSource = isMasterTimestampSource; this.timestampAdjuster = timestampAdjuster; // Note: this.dataSource and dataSource may be different. this.isEncrypted = this.dataSource instanceof Aes128DataSource; lastPathSegment = dataSpec.uri.getLastPathSegment(); isPackedAudio = lastPathSegment.endsWith(AAC_FILE_EXTENSION) || lastPathSegment.endsWith(AC3_FILE_EXTENSION) || lastPathSegment.endsWith(EC3_FILE_EXTENSION) || lastPathSegment.endsWith(MP3_FILE_EXTENSION); if (previousChunk != null) { id3Decoder = previousChunk.id3Decoder; id3Data = previousChunk.id3Data; previousExtractor = previousChunk.extractor; shouldSpliceIn = previousChunk.hlsUrl != hlsUrl; needNewExtractor = previousChunk.discontinuitySequenceNumber != discontinuitySequenceNumber || shouldSpliceIn; } else { id3Decoder = isPackedAudio ? new Id3Decoder() : null; id3Data = isPackedAudio ? new ParsableByteArray(Id3Decoder.ID3_HEADER_LENGTH) : null; previousExtractor = null; shouldSpliceIn = false; needNewExtractor = true; } initDataSource = dataSource; uid = UID_SOURCE.getAndIncrement(); }
@Before public void setUp() { packetPayload = new byte[512]; Arrays.fill(packetPayload, (byte) 0xFF); payloadReader = new CustomSectionPayloadReader(); reader = new SectionReader(payloadReader); reader.init(new TimestampAdjuster(0), new FakeExtractorOutput(), new TsPayloadReader.TrackIdGenerator(0, 1)); }
private void loadMedia() throws IOException, InterruptedException { // If we previously fed part of this chunk to the extractor, we need to skip it this time. For // encrypted content we need to skip the data by reading it through the source, so as to ensure // correct decryption of the remainder of the chunk. For clear content, we can request the // remainder of the chunk directly. DataSpec loadDataSpec; boolean skipLoadedBytes; if (isEncrypted) { loadDataSpec = dataSpec; skipLoadedBytes = bytesLoaded != 0; } else { loadDataSpec = Util.getRemainderDataSpec(dataSpec, bytesLoaded); skipLoadedBytes = false; } if (!isMasterTimestampSource) { timestampAdjuster.waitUntilInitialized(); } else if (timestampAdjuster.getFirstSampleTimestampUs() == TimestampAdjuster.DO_NOT_OFFSET) { // We're the master and we haven't set the desired first sample timestamp yet. timestampAdjuster.setFirstSampleTimestampUs(startTimeUs); } try { ExtractorInput input = new DefaultExtractorInput(dataSource, loadDataSpec.absoluteStreamPosition, dataSource.open(loadDataSpec)); if (extractor == null) { // Media segment format is packed audio. long id3Timestamp = peekId3PrivTimestamp(input); extractor = buildPackedAudioExtractor(id3Timestamp != C.TIME_UNSET ? timestampAdjuster.adjustTsTimestamp(id3Timestamp) : startTimeUs); } if (skipLoadedBytes) { input.skipFully(bytesLoaded); } try { int result = Extractor.RESULT_CONTINUE; while (result == Extractor.RESULT_CONTINUE && !loadCanceled) { result = extractor.read(input, null); } } finally { bytesLoaded = (int) (input.getPosition() - dataSpec.absoluteStreamPosition); } } finally { Util.closeQuietly(dataSource); } loadCompleted = true; }
public WebvttExtractor(String language, TimestampAdjuster timestampAdjuster) { this.language = language; this.timestampAdjuster = timestampAdjuster; this.sampleDataWrapper = new ParsableByteArray(); sampleData = new byte[1024]; }
private void processSample() throws ParserException { ParsableByteArray webvttData = new ParsableByteArray(sampleData); // Validate the first line of the header. try { WebvttParserUtil.validateWebvttHeaderLine(webvttData); } catch (SubtitleDecoderException e) { throw new ParserException(e); } // 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 = TimestampAdjuster.ptsToUs( Long.parseLong(mediaTimestampMatcher.group(1))); } } // Find the first cue header and parse the start time. Matcher cueHeaderMatcher = WebvttParserUtil.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 = timestampAdjuster.adjustSampleTimestamp( 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.BUFFER_FLAG_KEY_FRAME, sampleSize, 0, null); }
@Override public void init(TimestampAdjuster timestampAdjuster, ExtractorOutput extractorOutput, TrackIdGenerator idGenerator) { // Do nothing. }
public PsExtractor() { this(new TimestampAdjuster(0)); }
public PsExtractor(TimestampAdjuster timestampAdjuster) { this.timestampAdjuster = timestampAdjuster; psPacketBuffer = new ParsableByteArray(4096); psPayloadReaders = new SparseArray<>(); }
public PesReader(ElementaryStreamReader pesPayloadReader, TimestampAdjuster timestampAdjuster) { this.pesPayloadReader = pesPayloadReader; this.timestampAdjuster = timestampAdjuster; pesScratch = new ParsableBitArray(new byte[PES_SCRATCH_SIZE]); }
@Override public void init(TimestampAdjuster timestampAdjuster, ExtractorOutput extractorOutput, TrackIdGenerator idGenerator) { this.timestampAdjuster = timestampAdjuster; reader.createTracks(extractorOutput, idGenerator); }
@Override public void init(TimestampAdjuster timestampAdjuster, ExtractorOutput extractorOutput, TrackIdGenerator idGenerator) { reader.init(timestampAdjuster, extractorOutput, idGenerator); waitingForPayloadStart = true; }
static SpliceInsertCommand parseFromSection(ParsableByteArray sectionData, long ptsAdjustment, TimestampAdjuster timestampAdjuster) { long spliceEventId = sectionData.readUnsignedInt(); // splice_event_cancel_indicator(1), reserved(7). boolean spliceEventCancelIndicator = (sectionData.readUnsignedByte() & 0x80) != 0; boolean outOfNetworkIndicator = false; boolean programSpliceFlag = false; boolean spliceImmediateFlag = false; long programSplicePts = C.TIME_UNSET; List<ComponentSplice> componentSplices = Collections.emptyList(); int uniqueProgramId = 0; int availNum = 0; int availsExpected = 0; boolean autoReturn = false; long duration = C.TIME_UNSET; if (!spliceEventCancelIndicator) { int headerByte = sectionData.readUnsignedByte(); outOfNetworkIndicator = (headerByte & 0x80) != 0; programSpliceFlag = (headerByte & 0x40) != 0; boolean durationFlag = (headerByte & 0x20) != 0; spliceImmediateFlag = (headerByte & 0x10) != 0; if (programSpliceFlag && !spliceImmediateFlag) { programSplicePts = TimeSignalCommand.parseSpliceTime(sectionData, ptsAdjustment); } if (!programSpliceFlag) { int componentCount = sectionData.readUnsignedByte(); componentSplices = new ArrayList<>(componentCount); for (int i = 0; i < componentCount; i++) { int componentTag = sectionData.readUnsignedByte(); long componentSplicePts = C.TIME_UNSET; if (!spliceImmediateFlag) { componentSplicePts = TimeSignalCommand.parseSpliceTime(sectionData, ptsAdjustment); } componentSplices.add(new ComponentSplice(componentTag, componentSplicePts, timestampAdjuster.adjustTsTimestamp(componentSplicePts))); } } if (durationFlag) { long firstByte = sectionData.readUnsignedByte(); autoReturn = (firstByte & 0x80) != 0; duration = ((firstByte & 0x01) << 32) | sectionData.readUnsignedInt(); } uniqueProgramId = sectionData.readUnsignedShort(); availNum = sectionData.readUnsignedByte(); availsExpected = sectionData.readUnsignedByte(); } return new SpliceInsertCommand(spliceEventId, spliceEventCancelIndicator, outOfNetworkIndicator, programSpliceFlag, spliceImmediateFlag, programSplicePts, timestampAdjuster.adjustTsTimestamp(programSplicePts), componentSplices, autoReturn, duration, uniqueProgramId, availNum, availsExpected); }
static TimeSignalCommand parseFromSection(ParsableByteArray sectionData, long ptsAdjustment, TimestampAdjuster timestampAdjuster) { long ptsTime = parseSpliceTime(sectionData, ptsAdjustment); long playbackPositionUs = timestampAdjuster.adjustTsTimestamp(ptsTime); return new TimeSignalCommand(ptsTime, playbackPositionUs); }
public TsExtractor() { this(MODE_NORMAL, new TimestampAdjuster(0), new DefaultTsPayloadReaderFactory()); }
/** * @param extractorFactory A {@link HlsExtractorFactory} from which the HLS media chunk * extractor is obtained. * @param dataSource The source from which the data should be loaded. * @param dataSpec Defines the data to be loaded. * @param initDataSpec Defines the initialization data to be fed to new extractors. May be null. * @param hlsUrl The url of the playlist from which this chunk was obtained. * @param muxedCaptionFormats List of muxed caption {@link Format}s. Null if no closed caption * information is available in the master playlist. * @param trackSelectionReason See {@link #trackSelectionReason}. * @param trackSelectionData See {@link #trackSelectionData}. * @param startTimeUs The start time of the chunk in microseconds. * @param endTimeUs The end time of the chunk in microseconds. * @param chunkIndex The media sequence number of the chunk. * @param discontinuitySequenceNumber The discontinuity sequence number of the chunk. * @param isMasterTimestampSource True if the chunk can initialize the timestamp adjuster. * @param timestampAdjuster Adjuster corresponding to the provided discontinuity sequence number. * @param previousChunk The {@link HlsMediaChunk} that preceded this one. May be null. * @param drmInitData A {@link DrmInitData} to sideload to the extractor. * @param fullSegmentEncryptionKey The key to decrypt the full segment, or null if the segment is * not fully encrypted. * @param encryptionIv The AES initialization vector, or null if the segment is not fully * encrypted. */ public HlsMediaChunk(HlsExtractorFactory extractorFactory, DataSource dataSource, DataSpec dataSpec, DataSpec initDataSpec, HlsUrl hlsUrl, List<Format> muxedCaptionFormats, int trackSelectionReason, Object trackSelectionData, long startTimeUs, long endTimeUs, int chunkIndex, int discontinuitySequenceNumber, boolean isMasterTimestampSource, TimestampAdjuster timestampAdjuster, HlsMediaChunk previousChunk, DrmInitData drmInitData, byte[] fullSegmentEncryptionKey, byte[] encryptionIv) { super(buildDataSource(dataSource, fullSegmentEncryptionKey, encryptionIv), dataSpec, hlsUrl.format, trackSelectionReason, trackSelectionData, startTimeUs, endTimeUs, chunkIndex); this.discontinuitySequenceNumber = discontinuitySequenceNumber; this.initDataSpec = initDataSpec; this.hlsUrl = hlsUrl; this.isMasterTimestampSource = isMasterTimestampSource; this.timestampAdjuster = timestampAdjuster; // Note: this.dataSource and dataSource may be different. this.isEncrypted = this.dataSource instanceof Aes128DataSource; Extractor previousExtractor = null; if (previousChunk != null) { shouldSpliceIn = previousChunk.hlsUrl != hlsUrl; previousExtractor = previousChunk.discontinuitySequenceNumber != discontinuitySequenceNumber || shouldSpliceIn ? null : previousChunk.extractor; } else { shouldSpliceIn = false; } Pair<Extractor, Boolean> extractorData = extractorFactory.createExtractor(previousExtractor, dataSpec.uri, trackFormat, muxedCaptionFormats, drmInitData, timestampAdjuster); extractor = extractorData.first; isPackedAudioExtractor = extractorData.second; reusingExtractor = extractor == previousExtractor; initLoadCompleted = reusingExtractor && initDataSpec != null; if (isPackedAudioExtractor) { id3Decoder = previousChunk != null ? previousChunk.id3Decoder : new Id3Decoder(); id3Data = previousChunk != null ? previousChunk.id3Data : new ParsableByteArray(Id3Decoder.ID3_HEADER_LENGTH); } else { id3Decoder = null; id3Data = null; } initDataSource = dataSource; uid = uidSource.getAndIncrement(); }