Search in sources :

Example 11 with HlsMediaPlaylist

use of androidx.media3.exoplayer.hls.playlist.HlsMediaPlaylist in project media by androidx.

the class HlsChunkSource method getChunkPublicationState.

/**
 * Returns the publication state of the given chunk.
 *
 * @param mediaChunk The media chunk for which to evaluate the publication state.
 * @return Whether the media chunk is {@link #CHUNK_PUBLICATION_STATE_PRELOAD a preload chunk},
 *     has been {@link #CHUNK_PUBLICATION_STATE_REMOVED removed} or is definitely {@link
 *     #CHUNK_PUBLICATION_STATE_PUBLISHED published}.
 */
@ChunkPublicationState
public int getChunkPublicationState(HlsMediaChunk mediaChunk) {
    if (mediaChunk.partIndex == C.INDEX_UNSET) {
        // Chunks based on full segments can't be removed and are always published.
        return CHUNK_PUBLICATION_STATE_PUBLISHED;
    }
    Uri playlistUrl = playlistUrls[trackGroup.indexOf(mediaChunk.trackFormat)];
    HlsMediaPlaylist mediaPlaylist = checkNotNull(playlistTracker.getPlaylistSnapshot(playlistUrl, /* isForPlayback= */
    false));
    int segmentIndexInPlaylist = (int) (mediaChunk.chunkIndex - mediaPlaylist.mediaSequence);
    if (segmentIndexInPlaylist < 0) {
        // The parent segment of the previous chunk is not in the current playlist anymore.
        return CHUNK_PUBLICATION_STATE_PUBLISHED;
    }
    List<HlsMediaPlaylist.Part> partsInCurrentPlaylist = segmentIndexInPlaylist < mediaPlaylist.segments.size() ? mediaPlaylist.segments.get(segmentIndexInPlaylist).parts : mediaPlaylist.trailingParts;
    if (mediaChunk.partIndex >= partsInCurrentPlaylist.size()) {
        // sequence in the new playlist.
        return CHUNK_PUBLICATION_STATE_REMOVED;
    }
    HlsMediaPlaylist.Part newPart = partsInCurrentPlaylist.get(mediaChunk.partIndex);
    if (newPart.isPreload) {
        // The playlist did not change and the part in the new playlist is still a preload hint.
        return CHUNK_PUBLICATION_STATE_PRELOAD;
    }
    Uri newUri = Uri.parse(UriUtil.resolve(mediaPlaylist.baseUri, newPart.url));
    return Util.areEqual(newUri, mediaChunk.dataSpec.uri) ? CHUNK_PUBLICATION_STATE_PUBLISHED : CHUNK_PUBLICATION_STATE_REMOVED;
}
Also used : HlsMediaPlaylist(androidx.media3.exoplayer.hls.playlist.HlsMediaPlaylist) Uri(android.net.Uri)

Example 12 with HlsMediaPlaylist

use of androidx.media3.exoplayer.hls.playlist.HlsMediaPlaylist 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 13 with HlsMediaPlaylist

use of androidx.media3.exoplayer.hls.playlist.HlsMediaPlaylist in project media by androidx.

the class HlsMediaSourceTest method refreshPlaylist_targetLiveOffsetRemainsInWindow.

@Test
public void refreshPlaylist_targetLiveOffsetRemainsInWindow() throws TimeoutException, IOException {
    String playlistUri1 = "fake://foo.bar/media0/playlist1.m3u8";
    // The playlist has a duration of 16 seconds and a hold back of 12 seconds.
    String playlist1 = "#EXTM3U\n" + "#EXT-X-TARGETDURATION:4\n" + "#EXT-X-VERSION:3\n" + "#EXT-X-MEDIA-SEQUENCE:0\n" + "#EXTINF:4.00000,\n" + "fileSequence0.ts\n" + "#EXTINF:4.00000,\n" + "fileSequence1.ts\n" + "#EXTINF:4.00000,\n" + "fileSequence2.ts\n" + "#EXTINF:4.00000,\n" + "fileSequence3.ts\n" + "#EXT-X-SERVER-CONTROL:HOLD-BACK:12";
    // The second playlist defines a different hold back.
    String playlistUri2 = "fake://foo.bar/media0/playlist2.m3u8";
    String playlist2 = "#EXTM3U\n" + "#EXT-X-TARGETDURATION:4\n" + "#EXT-X-VERSION:3\n" + "#EXT-X-MEDIA-SEQUENCE:4\n" + "#EXTINF:4.00000,\n" + "fileSequence4.ts\n" + "#EXTINF:4.00000,\n" + "fileSequence5.ts\n" + "#EXTINF:4.00000,\n" + "fileSequence6.ts\n" + "#EXTINF:4.00000,\n" + "fileSequence7.ts\n" + "#EXT-X-SERVER-CONTROL:HOLD-BACK:14";
    // The third playlist has a duration of 8 seconds.
    String playlistUri3 = "fake://foo.bar/media0/playlist3.m3u8";
    String playlist3 = "#EXTM3U\n" + "#EXT-X-TARGETDURATION:4\n" + "#EXT-X-VERSION:3\n" + "#EXT-X-MEDIA-SEQUENCE:4\n" + "#EXTINF:4.00000,\n" + "fileSequence8.ts\n" + "#EXTINF:4.00000,\n" + "fileSequence9.ts\n" + "#EXTINF:4.00000,\n" + "#EXT-X-SERVER-CONTROL:HOLD-BACK:12";
    // The third playlist has a duration of 16 seconds but the target live offset should remain at
    // 8 seconds.
    String playlistUri4 = "fake://foo.bar/media0/playlist4.m3u8";
    String playlist4 = "#EXTM3U\n" + "#EXT-X-TARGETDURATION:4\n" + "#EXT-X-VERSION:3\n" + "#EXT-X-MEDIA-SEQUENCE:4\n" + "#EXTINF:4.00000,\n" + "fileSequence10.ts\n" + "#EXTINF:4.00000,\n" + "fileSequence11.ts\n" + "#EXTINF:4.00000,\n" + "fileSequence12.ts\n" + "#EXTINF:4.00000,\n" + "fileSequence13.ts\n" + "#EXTINF:4.00000,\n" + "#EXT-X-SERVER-CONTROL:HOLD-BACK:12";
    HlsMediaSource.Factory factory = createHlsMediaSourceFactory(playlistUri1, playlist1);
    MediaItem mediaItem = new MediaItem.Builder().setUri(playlistUri1).build();
    HlsMediaSource mediaSource = factory.createMediaSource(mediaItem);
    HlsMediaPlaylist secondPlaylist = parseHlsMediaPlaylist(playlistUri2, playlist2);
    HlsMediaPlaylist thirdPlaylist = parseHlsMediaPlaylist(playlistUri3, playlist3);
    HlsMediaPlaylist fourthPlaylist = parseHlsMediaPlaylist(playlistUri4, playlist4);
    List<Timeline> timelines = new ArrayList<>();
    MediaSource.MediaSourceCaller mediaSourceCaller = (source, timeline) -> timelines.add(timeline);
    mediaSource.prepareSource(mediaSourceCaller, /* mediaTransferListener= */
    null, PlayerId.UNSET);
    runMainLooperUntil(() -> timelines.size() == 1);
    mediaSource.onPrimaryPlaylistRefreshed(secondPlaylist);
    runMainLooperUntil(() -> timelines.size() == 2);
    mediaSource.onPrimaryPlaylistRefreshed(thirdPlaylist);
    runMainLooperUntil(() -> timelines.size() == 3);
    mediaSource.onPrimaryPlaylistRefreshed(fourthPlaylist);
    runMainLooperUntil(() -> timelines.size() == 4);
    Timeline.Window window = new Timeline.Window();
    assertThat(timelines.get(0).getWindow(0, window).liveConfiguration.targetOffsetMs).isEqualTo(12000);
    assertThat(timelines.get(1).getWindow(0, window).liveConfiguration.targetOffsetMs).isEqualTo(12000);
    assertThat(timelines.get(2).getWindow(0, window).liveConfiguration.targetOffsetMs).isEqualTo(8000);
    assertThat(timelines.get(3).getWindow(0, window).liveConfiguration.targetOffsetMs).isEqualTo(8000);
}
Also used : MediaSource(androidx.media3.exoplayer.source.MediaSource) Uri(android.net.Uri) RunWith(org.junit.runner.RunWith) TimeoutException(java.util.concurrent.TimeoutException) SystemClock(android.os.SystemClock) IOException(java.io.IOException) Test(org.junit.Test) Truth.assertThat(com.google.common.truth.Truth.assertThat) Util(androidx.media3.common.util.Util) AndroidJUnit4(androidx.test.ext.junit.runners.AndroidJUnit4) AtomicReference(java.util.concurrent.atomic.AtomicReference) ArrayList(java.util.ArrayList) FakeDataSource(androidx.media3.test.utils.FakeDataSource) C(androidx.media3.common.C) HlsMediaPlaylist(androidx.media3.exoplayer.hls.playlist.HlsMediaPlaylist) List(java.util.List) ParserException(androidx.media3.common.ParserException) ByteArrayInputStream(java.io.ByteArrayInputStream) Timeline(androidx.media3.common.Timeline) PlayerId(androidx.media3.exoplayer.analytics.PlayerId) FakeDataSet(androidx.media3.test.utils.FakeDataSet) MediaItem(androidx.media3.common.MediaItem) HlsPlaylistParser(androidx.media3.exoplayer.hls.playlist.HlsPlaylistParser) RobolectricUtil.runMainLooperUntil(androidx.media3.test.utils.robolectric.RobolectricUtil.runMainLooperUntil) ArrayList(java.util.ArrayList) Timeline(androidx.media3.common.Timeline) MediaSource(androidx.media3.exoplayer.source.MediaSource) MediaItem(androidx.media3.common.MediaItem) HlsMediaPlaylist(androidx.media3.exoplayer.hls.playlist.HlsMediaPlaylist) Test(org.junit.Test)

Example 14 with HlsMediaPlaylist

use of androidx.media3.exoplayer.hls.playlist.HlsMediaPlaylist in project media by androidx.

the class HlsDownloader method addSegment.

private void addSegment(HlsMediaPlaylist mediaPlaylist, HlsMediaPlaylist.Segment segment, HashSet<Uri> seenEncryptionKeyUris, ArrayList<Segment> out) {
    String baseUri = mediaPlaylist.baseUri;
    long startTimeUs = mediaPlaylist.startTimeUs + segment.relativeStartTimeUs;
    if (segment.fullSegmentEncryptionKeyUri != null) {
        Uri keyUri = UriUtil.resolveToUri(baseUri, segment.fullSegmentEncryptionKeyUri);
        if (seenEncryptionKeyUris.add(keyUri)) {
            out.add(new Segment(startTimeUs, SegmentDownloader.getCompressibleDataSpec(keyUri)));
        }
    }
    Uri segmentUri = UriUtil.resolveToUri(baseUri, segment.url);
    DataSpec dataSpec = new DataSpec(segmentUri, segment.byteRangeOffset, segment.byteRangeLength);
    out.add(new Segment(startTimeUs, dataSpec));
}
Also used : DataSpec(androidx.media3.datasource.DataSpec) Uri(android.net.Uri)

Example 15 with HlsMediaPlaylist

use of androidx.media3.exoplayer.hls.playlist.HlsMediaPlaylist in project media by androidx.

the class HlsMediaPlaylistParserTest method variableSubstitution.

@Test
public void variableSubstitution() throws IOException {
    Uri playlistUri = Uri.parse("https://example.com/substitution.m3u8");
    String playlistString = "#EXTM3U\n" + "#EXT-X-VERSION:8\n" + "#EXT-X-DEFINE:NAME=\"underscore_1\",VALUE=\"{\"\n" + "#EXT-X-DEFINE:NAME=\"dash-1\",VALUE=\"replaced_value.ts\"\n" + "#EXT-X-TARGETDURATION:5\n" + "#EXT-X-MEDIA-SEQUENCE:10\n" + "#EXTINF:5.005,\n" + "segment1.ts\n" + "#EXT-X-MAP:URI=\"{$dash-1}\"" + "#EXTINF:5.005,\n" + "segment{$underscore_1}$name_1}\n";
    InputStream inputStream = new ByteArrayInputStream(Util.getUtf8Bytes(playlistString));
    HlsMediaPlaylist playlist = (HlsMediaPlaylist) new HlsPlaylistParser().parse(playlistUri, inputStream);
    Segment segment = playlist.segments.get(1);
    assertThat(segment.initializationSegment.url).isEqualTo("replaced_value.ts");
    assertThat(segment.url).isEqualTo("segment{$name_1}");
}
Also used : ByteArrayInputStream(java.io.ByteArrayInputStream) ByteArrayInputStream(java.io.ByteArrayInputStream) InputStream(java.io.InputStream) Uri(android.net.Uri) Segment(androidx.media3.exoplayer.hls.playlist.HlsMediaPlaylist.Segment) Test(org.junit.Test)

Aggregations

HlsMediaPlaylist (androidx.media3.exoplayer.hls.playlist.HlsMediaPlaylist)19 Test (org.junit.Test)18 Uri (android.net.Uri)16 Segment (androidx.media3.exoplayer.hls.playlist.HlsMediaPlaylist.Segment)13 InputStream (java.io.InputStream)10 ArrayList (java.util.ArrayList)10 Nullable (androidx.annotation.Nullable)9 DataSpec (androidx.media3.datasource.DataSpec)8 ByteArrayInputStream (java.io.ByteArrayInputStream)8 HlsPlaylistParser (androidx.media3.exoplayer.hls.playlist.HlsPlaylistParser)4 BaseMediaChunkIterator (androidx.media3.exoplayer.source.chunk.BaseMediaChunkIterator)2 MediaChunkIterator (androidx.media3.exoplayer.source.chunk.MediaChunkIterator)2 FakeDataSource (androidx.media3.test.utils.FakeDataSource)2 IOException (java.io.IOException)2 SystemClock (android.os.SystemClock)1 Pair (android.util.Pair)1 VisibleForTesting (androidx.annotation.VisibleForTesting)1 C (androidx.media3.common.C)1 DrmInitData (androidx.media3.common.DrmInitData)1 SchemeData (androidx.media3.common.DrmInitData.SchemeData)1