Search in sources :

Example 1 with TimestampAdjuster

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);
}
Also used : ArrayList(java.util.ArrayList) Ac4Extractor(androidx.media3.extractor.ts.Ac4Extractor) Extractor(androidx.media3.extractor.Extractor) TsExtractor(androidx.media3.extractor.ts.TsExtractor) AdtsExtractor(androidx.media3.extractor.ts.AdtsExtractor) Mp3Extractor(androidx.media3.extractor.mp3.Mp3Extractor) Ac3Extractor(androidx.media3.extractor.ts.Ac3Extractor) FragmentedMp4Extractor(androidx.media3.extractor.mp4.FragmentedMp4Extractor) SuppressLint(android.annotation.SuppressLint) Nullable(androidx.annotation.Nullable)

Example 2 with 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);
}
Also used : Uri(android.net.Uri) DataSource(androidx.media3.datasource.DataSource) ParsableByteArray(androidx.media3.common.util.ParsableByteArray) DataSpec(androidx.media3.datasource.DataSpec) HlsMediaPlaylist(androidx.media3.exoplayer.hls.playlist.HlsMediaPlaylist) Id3Decoder(androidx.media3.extractor.metadata.id3.Id3Decoder) Nullable(androidx.annotation.Nullable)

Example 3 with TimestampAdjuster

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;
}
Also used : InterruptedIOException(java.io.InterruptedIOException) DefaultExtractorInput(androidx.media3.extractor.DefaultExtractorInput) EnsuresNonNull(org.checkerframework.checker.nullness.qual.EnsuresNonNull) RequiresNonNull(org.checkerframework.checker.nullness.qual.RequiresNonNull)

Example 4 with TimestampAdjuster

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);
}
Also used : FakeExtractorInput(androidx.media3.test.utils.FakeExtractorInput) ExtractorInput(androidx.media3.extractor.ExtractorInput) Test(org.junit.Test)

Example 5 with TimestampAdjuster

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);
}
Also used : FakeExtractorInput(androidx.media3.test.utils.FakeExtractorInput) ExtractorInput(androidx.media3.extractor.ExtractorInput) Test(org.junit.Test)

Aggregations

Test (org.junit.Test)12 FakeExtractorInput (androidx.media3.test.utils.FakeExtractorInput)11 ExtractorInput (androidx.media3.extractor.ExtractorInput)9 TimestampAdjuster (androidx.media3.common.util.TimestampAdjuster)8 Nullable (androidx.annotation.Nullable)7 TrackOutput (androidx.media3.extractor.TrackOutput)4 FakeExtractorOutput (androidx.media3.test.utils.FakeExtractorOutput)4 Format (androidx.media3.common.Format)3 TsExtractor (androidx.media3.extractor.ts.TsExtractor)3 SuppressLint (android.annotation.SuppressLint)2 ParsableByteArray (androidx.media3.common.util.ParsableByteArray)2 Extractor (androidx.media3.extractor.Extractor)2 PositionHolder (androidx.media3.extractor.PositionHolder)2 Mp3Extractor (androidx.media3.extractor.mp3.Mp3Extractor)2 FragmentedMp4Extractor (androidx.media3.extractor.mp4.FragmentedMp4Extractor)2 Ac3Extractor (androidx.media3.extractor.ts.Ac3Extractor)2 Ac4Extractor (androidx.media3.extractor.ts.Ac4Extractor)2 AdtsExtractor (androidx.media3.extractor.ts.AdtsExtractor)2 Before (org.junit.Before)2 Uri (android.net.Uri)1