use of androidx.media3.common.util.TimestampAdjuster in project media by androidx.
the class DefaultHlsExtractorFactory method createExtractor.
@Override
public BundledHlsMediaChunkExtractor createExtractor(Uri uri, Format format, @Nullable List<Format> muxedCaptionFormats, TimestampAdjuster timestampAdjuster, Map<String, List<String>> responseHeaders, ExtractorInput sniffingExtractorInput, PlayerId playerId) throws IOException {
@FileTypes.Type int formatInferredFileType = FileTypes.inferFileTypeFromMimeType(format.sampleMimeType);
@FileTypes.Type int responseHeadersInferredFileType = FileTypes.inferFileTypeFromResponseHeaders(responseHeaders);
@FileTypes.Type int uriInferredFileType = FileTypes.inferFileTypeFromUri(uri);
// Defines the order in which to try the extractors.
List<Integer> fileTypeOrder = new ArrayList<>(/* initialCapacity= */
DEFAULT_EXTRACTOR_ORDER.length);
addFileTypeIfValidAndNotPresent(formatInferredFileType, fileTypeOrder);
addFileTypeIfValidAndNotPresent(responseHeadersInferredFileType, fileTypeOrder);
addFileTypeIfValidAndNotPresent(uriInferredFileType, fileTypeOrder);
for (int fileType : DEFAULT_EXTRACTOR_ORDER) {
addFileTypeIfValidAndNotPresent(fileType, fileTypeOrder);
}
// Extractor to be used if the type is not recognized.
@Nullable Extractor fallBackExtractor = null;
sniffingExtractorInput.resetPeekPosition();
for (int i = 0; i < fileTypeOrder.size(); i++) {
int fileType = fileTypeOrder.get(i);
Extractor extractor = checkNotNull(createExtractorByFileType(fileType, format, muxedCaptionFormats, timestampAdjuster));
if (sniffQuietly(extractor, sniffingExtractorInput)) {
return new BundledHlsMediaChunkExtractor(extractor, format, timestampAdjuster);
}
if (fallBackExtractor == null && (fileType == formatInferredFileType || fileType == responseHeadersInferredFileType || fileType == uriInferredFileType || fileType == FileTypes.TS)) {
// If sniffing fails, fallback to the file types inferred from context. If all else fails,
// fallback to Transport Stream. See https://github.com/google/ExoPlayer/issues/8219.
fallBackExtractor = extractor;
}
}
return new BundledHlsMediaChunkExtractor(checkNotNull(fallBackExtractor), format, timestampAdjuster);
}
use of androidx.media3.common.util.TimestampAdjuster in project media by androidx.
the class HlsMediaChunk method createInstance.
/**
* Creates a new instance.
*
* @param extractorFactory A {@link HlsExtractorFactory} from which the {@link
* HlsMediaChunkExtractor} is obtained.
* @param dataSource The source from which the data should be loaded.
* @param format The chunk format.
* @param startOfPlaylistInPeriodUs The position of the playlist in the period in microseconds.
* @param mediaPlaylist The media playlist from which this chunk was obtained.
* @param segmentBaseHolder The segment holder.
* @param playlistUrl 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 multivariant playlist.
* @param trackSelectionReason See {@link #trackSelectionReason}.
* @param trackSelectionData See {@link #trackSelectionData}.
* @param isMasterTimestampSource True if the chunk can initialize the timestamp adjuster.
* @param timestampAdjusterProvider The provider from which to obtain the {@link
* TimestampAdjuster}.
* @param previousChunk The {@link HlsMediaChunk} that preceded this one. May be null.
* @param mediaSegmentKey The media segment decryption key, if fully encrypted. Null otherwise.
* @param initSegmentKey The initialization segment decryption key, if fully encrypted. Null
* otherwise.
* @param shouldSpliceIn Whether samples for this chunk should be spliced into existing samples.
*/
public static HlsMediaChunk createInstance(HlsExtractorFactory extractorFactory, DataSource dataSource, Format format, long startOfPlaylistInPeriodUs, HlsMediaPlaylist mediaPlaylist, HlsChunkSource.SegmentBaseHolder segmentBaseHolder, Uri playlistUrl, @Nullable List<Format> muxedCaptionFormats, @C.SelectionReason int trackSelectionReason, @Nullable Object trackSelectionData, boolean isMasterTimestampSource, TimestampAdjusterProvider timestampAdjusterProvider, @Nullable HlsMediaChunk previousChunk, @Nullable byte[] mediaSegmentKey, @Nullable byte[] initSegmentKey, boolean shouldSpliceIn, PlayerId playerId) {
// Media segment.
HlsMediaPlaylist.SegmentBase mediaSegment = segmentBaseHolder.segmentBase;
DataSpec dataSpec = new DataSpec.Builder().setUri(UriUtil.resolveToUri(mediaPlaylist.baseUri, mediaSegment.url)).setPosition(mediaSegment.byteRangeOffset).setLength(mediaSegment.byteRangeLength).setFlags(segmentBaseHolder.isPreload ? FLAG_MIGHT_NOT_USE_FULL_NETWORK_SPEED : 0).build();
boolean mediaSegmentEncrypted = mediaSegmentKey != null;
@Nullable byte[] mediaSegmentIv = mediaSegmentEncrypted ? getEncryptionIvArray(Assertions.checkNotNull(mediaSegment.encryptionIV)) : null;
DataSource mediaDataSource = buildDataSource(dataSource, mediaSegmentKey, mediaSegmentIv);
// Init segment.
HlsMediaPlaylist.Segment initSegment = mediaSegment.initializationSegment;
DataSpec initDataSpec = null;
boolean initSegmentEncrypted = false;
@Nullable DataSource initDataSource = null;
if (initSegment != null) {
initSegmentEncrypted = initSegmentKey != null;
@Nullable byte[] initSegmentIv = initSegmentEncrypted ? getEncryptionIvArray(Assertions.checkNotNull(initSegment.encryptionIV)) : null;
Uri initSegmentUri = UriUtil.resolveToUri(mediaPlaylist.baseUri, initSegment.url);
initDataSpec = new DataSpec(initSegmentUri, initSegment.byteRangeOffset, initSegment.byteRangeLength);
initDataSource = buildDataSource(dataSource, initSegmentKey, initSegmentIv);
}
long segmentStartTimeInPeriodUs = startOfPlaylistInPeriodUs + mediaSegment.relativeStartTimeUs;
long segmentEndTimeInPeriodUs = segmentStartTimeInPeriodUs + mediaSegment.durationUs;
int discontinuitySequenceNumber = mediaPlaylist.discontinuitySequence + mediaSegment.relativeDiscontinuitySequence;
@Nullable HlsMediaChunkExtractor previousExtractor = null;
Id3Decoder id3Decoder;
ParsableByteArray scratchId3Data;
if (previousChunk != null) {
boolean isSameInitData = initDataSpec == previousChunk.initDataSpec || (initDataSpec != null && previousChunk.initDataSpec != null && initDataSpec.uri.equals(previousChunk.initDataSpec.uri) && initDataSpec.position == previousChunk.initDataSpec.position);
boolean isFollowingChunk = playlistUrl.equals(previousChunk.playlistUrl) && previousChunk.loadCompleted;
id3Decoder = previousChunk.id3Decoder;
scratchId3Data = previousChunk.scratchId3Data;
previousExtractor = isSameInitData && isFollowingChunk && !previousChunk.extractorInvalidated && previousChunk.discontinuitySequenceNumber == discontinuitySequenceNumber ? previousChunk.extractor : null;
} else {
id3Decoder = new Id3Decoder();
scratchId3Data = new ParsableByteArray(Id3Decoder.ID3_HEADER_LENGTH);
}
return new HlsMediaChunk(extractorFactory, mediaDataSource, dataSpec, format, mediaSegmentEncrypted, initDataSource, initDataSpec, initSegmentEncrypted, playlistUrl, muxedCaptionFormats, trackSelectionReason, trackSelectionData, segmentStartTimeInPeriodUs, segmentEndTimeInPeriodUs, segmentBaseHolder.mediaSequence, segmentBaseHolder.partIndex, /* isPublished= */
!segmentBaseHolder.isPreload, discontinuitySequenceNumber, mediaSegment.hasGapTag, isMasterTimestampSource, /* timestampAdjuster= */
timestampAdjusterProvider.getAdjuster(discontinuitySequenceNumber), mediaSegment.drmInitData, previousExtractor, id3Decoder, scratchId3Data, shouldSpliceIn, playerId);
}
use of androidx.media3.common.util.TimestampAdjuster in project media by androidx.
the class HlsMediaChunk method prepareExtraction.
@RequiresNonNull("output")
@EnsuresNonNull("extractor")
private DefaultExtractorInput prepareExtraction(DataSource dataSource, DataSpec dataSpec, boolean initializeTimestampAdjuster) throws IOException {
long bytesToRead = dataSource.open(dataSpec);
if (initializeTimestampAdjuster) {
try {
timestampAdjuster.sharedInitializeOrWait(isMasterTimestampSource, startTimeUs);
} catch (InterruptedException e) {
throw new InterruptedIOException();
}
}
DefaultExtractorInput extractorInput = new DefaultExtractorInput(dataSource, dataSpec.position, bytesToRead);
if (extractor == null) {
long id3Timestamp = peekId3PrivTimestamp(extractorInput);
extractorInput.resetPeekPosition();
extractor = previousExtractor != null ? previousExtractor.recreate() : extractorFactory.createExtractor(dataSpec.uri, trackFormat, muxedCaptionFormats, timestampAdjuster, dataSource.getResponseHeaders(), extractorInput, playerId);
if (extractor.isPackedAudioExtractor()) {
output.setSampleOffsetUs(id3Timestamp != C.TIME_UNSET ? timestampAdjuster.adjustTsTimestamp(id3Timestamp) : startTimeUs);
} else {
// In case the container format changes mid-stream to non-packed-audio, we need to reset
// the timestamp offset.
output.setSampleOffsetUs(/* sampleOffsetUs= */
0L);
}
output.onNewExtractor();
extractor.init(output);
}
output.setDrmInitData(drmInitData);
return extractorInput;
}
use of androidx.media3.common.util.TimestampAdjuster in project media by androidx.
the class DefaultHlsExtractorFactoryTest method createExtractor_withFileTypeInFormat_returnsExtractorMatchingFormat.
@Test
public void createExtractor_withFileTypeInFormat_returnsExtractorMatchingFormat() throws Exception {
ExtractorInput webVttExtractorInput = new FakeExtractorInput.Builder().setData(TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), "media/webvtt/typical")).build();
BundledHlsMediaChunkExtractor result = new DefaultHlsExtractorFactory().createExtractor(URI_WITH_TS_EXTENSION, webVttFormat, /* muxedCaptionFormats= */
null, timestampAdjuster, ac3ResponseHeaders, webVttExtractorInput, PlayerId.UNSET);
assertThat(result.extractor.getClass()).isEqualTo(WebvttExtractor.class);
}
use of androidx.media3.common.util.TimestampAdjuster in project media by androidx.
the class DefaultHlsExtractorFactoryTest method createExtractor_withFileTypeInUri_returnsExtractorMatchingUri.
@Test
public void createExtractor_withFileTypeInUri_returnsExtractorMatchingUri() throws Exception {
ExtractorInput tsExtractorInput = new FakeExtractorInput.Builder().setData(TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), "media/ts/sample_ac3.ts")).build();
BundledHlsMediaChunkExtractor result = new DefaultHlsExtractorFactory().createExtractor(URI_WITH_TS_EXTENSION, webVttFormat, /* muxedCaptionFormats= */
null, timestampAdjuster, ac3ResponseHeaders, tsExtractorInput, PlayerId.UNSET);
assertThat(result.extractor.getClass()).isEqualTo(TsExtractor.class);
}
Aggregations