use of androidx.media3.exoplayer.hls.playlist.HlsMediaPlaylist in project media by androidx.
the class HlsMediaPlaylistParserTest method parseMediaPlaylist.
@Test
public void parseMediaPlaylist() throws Exception {
Uri playlistUri = Uri.parse("https://example.com/test.m3u8");
String playlistString = "#EXTM3U\n" + "#EXT-X-VERSION:3\n" + "#EXT-X-PLAYLIST-TYPE:VOD\n" + "#EXT-X-START:TIME-OFFSET=-25\n" + "#EXT-X-TARGETDURATION:8\n" + "#EXT-X-MEDIA-SEQUENCE:2679\n" + "#EXT-X-DISCONTINUITY-SEQUENCE:4\n" + "#EXT-X-ALLOW-CACHE:YES\n" + "\n" + "#EXTINF:7.975,\n" + "#EXT-X-BYTERANGE:51370@0\n" + "https://priv.example.com/fileSequence2679.ts\n" + "\n" + "#EXT-X-KEY:METHOD=AES-128," + "URI=\"https://priv.example.com/key.php?r=2680\",IV=0x1566B\n" + "#EXTINF:7.975,segment title\n" + "#EXT-X-BYTERANGE:51501@2147483648\n" + "https://priv.example.com/fileSequence2680.ts\n" + "\n" + "#EXT-X-KEY:METHOD=NONE\n" + "#EXTINF:7.941,segment title .,:/# with interesting chars\n" + // @2147535149
"#EXT-X-BYTERANGE:51501\n" + "https://priv.example.com/fileSequence2681.ts\n" + "\n" + "#EXT-X-DISCONTINUITY\n" + "#EXT-X-KEY:METHOD=AES-128,URI=\"https://priv.example.com/key.php?r=2682\"\n" + // Trailing comma is omitted.
"#EXTINF:7.975\n" + // @2147586650
"#EXT-X-BYTERANGE:51740\n" + "https://priv.example.com/fileSequence2682.ts\n" + "\n" + "#EXTINF:7.975,\n" + "https://priv.example.com/fileSequence2683.ts\n" + "\n" + // 2.002 tests correct rounding, see https://github.com/google/ExoPlayer/issues/9575.
"#EXTINF:2.002,\n" + "https://priv.example.com/fileSequence2684.ts\n" + "#EXT-X-ENDLIST";
InputStream inputStream = new ByteArrayInputStream(Util.getUtf8Bytes(playlistString));
HlsPlaylist playlist = new HlsPlaylistParser().parse(playlistUri, inputStream);
HlsMediaPlaylist mediaPlaylist = (HlsMediaPlaylist) playlist;
assertThat(mediaPlaylist.playlistType).isEqualTo(HlsMediaPlaylist.PLAYLIST_TYPE_VOD);
assertThat(mediaPlaylist.startOffsetUs).isEqualTo(mediaPlaylist.durationUs - 25000000);
assertThat(mediaPlaylist.mediaSequence).isEqualTo(2679);
assertThat(mediaPlaylist.version).isEqualTo(3);
assertThat(mediaPlaylist.hasEndTag).isTrue();
assertThat(mediaPlaylist.protectionSchemes).isNull();
assertThat(mediaPlaylist.targetDurationUs).isEqualTo(8000000);
assertThat(mediaPlaylist.partTargetDurationUs).isEqualTo(C.TIME_UNSET);
List<Segment> segments = mediaPlaylist.segments;
assertThat(segments).isNotNull();
assertThat(segments).hasSize(6);
Segment segment = segments.get(0);
assertThat(mediaPlaylist.discontinuitySequence + segment.relativeDiscontinuitySequence).isEqualTo(4);
assertThat(segment.durationUs).isEqualTo(7975000);
assertThat(segment.title).isEqualTo("");
assertThat(segment.fullSegmentEncryptionKeyUri).isNull();
assertThat(segment.encryptionIV).isNull();
assertThat(segment.byteRangeLength).isEqualTo(51370);
assertThat(segment.byteRangeOffset).isEqualTo(0);
assertThat(segment.url).isEqualTo("https://priv.example.com/fileSequence2679.ts");
segment = segments.get(1);
assertThat(segment.relativeDiscontinuitySequence).isEqualTo(0);
assertThat(segment.durationUs).isEqualTo(7975000);
assertThat(segment.title).isEqualTo("segment title");
assertThat(segment.fullSegmentEncryptionKeyUri).isEqualTo("https://priv.example.com/key.php?r=2680");
assertThat(segment.encryptionIV).isEqualTo("0x1566B");
assertThat(segment.byteRangeLength).isEqualTo(51501);
assertThat(segment.byteRangeOffset).isEqualTo(2147483648L);
assertThat(segment.url).isEqualTo("https://priv.example.com/fileSequence2680.ts");
segment = segments.get(2);
assertThat(segment.relativeDiscontinuitySequence).isEqualTo(0);
assertThat(segment.durationUs).isEqualTo(7941000);
assertThat(segment.title).isEqualTo("segment title .,:/# with interesting chars");
assertThat(segment.fullSegmentEncryptionKeyUri).isNull();
assertThat(segment.encryptionIV).isEqualTo(null);
assertThat(segment.byteRangeLength).isEqualTo(51501);
assertThat(segment.byteRangeOffset).isEqualTo(2147535149L);
assertThat(segment.url).isEqualTo("https://priv.example.com/fileSequence2681.ts");
segment = segments.get(3);
assertThat(segment.relativeDiscontinuitySequence).isEqualTo(1);
assertThat(segment.durationUs).isEqualTo(7975000);
assertThat(segment.title).isEqualTo("");
assertThat(segment.fullSegmentEncryptionKeyUri).isEqualTo("https://priv.example.com/key.php?r=2682");
// 0xA7A == 2682.
assertThat(segment.encryptionIV).isNotNull();
assertThat(segment.encryptionIV).ignoringCase().isEqualTo("A7A");
assertThat(segment.byteRangeLength).isEqualTo(51740);
assertThat(segment.byteRangeOffset).isEqualTo(2147586650L);
assertThat(segment.url).isEqualTo("https://priv.example.com/fileSequence2682.ts");
segment = segments.get(4);
assertThat(segment.relativeDiscontinuitySequence).isEqualTo(1);
assertThat(segment.durationUs).isEqualTo(7975000);
assertThat(segment.title).isEqualTo("");
assertThat(segment.fullSegmentEncryptionKeyUri).isEqualTo("https://priv.example.com/key.php?r=2682");
// 0xA7B == 2683.
assertThat(segment.encryptionIV).isNotNull();
assertThat(segment.encryptionIV).ignoringCase().isEqualTo("A7B");
assertThat(segment.byteRangeLength).isEqualTo(C.LENGTH_UNSET);
assertThat(segment.byteRangeOffset).isEqualTo(0);
assertThat(segment.url).isEqualTo("https://priv.example.com/fileSequence2683.ts");
segment = segments.get(5);
assertThat(segment.durationUs).isEqualTo(2002000);
}
use of androidx.media3.exoplayer.hls.playlist.HlsMediaPlaylist in project media by androidx.
the class HlsMediaPlaylistParserTest method mapTag.
@Test
public void mapTag() throws IOException {
Uri playlistUri = Uri.parse("https://example.com/test3.m3u8");
String playlistString = "#EXTM3U\n" + "#EXT-X-VERSION:3\n" + "#EXT-X-TARGETDURATION:5\n" + "#EXT-X-MEDIA-SEQUENCE:10\n" + "#EXTINF:5.005,\n" + "02/00/27.ts\n" + "#EXT-X-MAP:URI=\"init1.ts\"" + "#EXTINF:5.005,\n" + "02/00/32.ts\n" + "#EXTINF:5.005,\n" + "02/00/42.ts\n" + "#EXT-X-MAP:URI=\"init2.ts\"" + "#EXTINF:5.005,\n" + "02/00/47.ts\n";
InputStream inputStream = new ByteArrayInputStream(Util.getUtf8Bytes(playlistString));
HlsMediaPlaylist playlist = (HlsMediaPlaylist) new HlsPlaylistParser().parse(playlistUri, inputStream);
List<Segment> segments = playlist.segments;
assertThat(segments.get(0).initializationSegment).isNull();
assertThat(segments.get(1).initializationSegment).isSameInstanceAs(segments.get(2).initializationSegment);
assertThat(segments.get(1).initializationSegment.url).isEqualTo("init1.ts");
assertThat(segments.get(3).initializationSegment.url).isEqualTo("init2.ts");
}
use of androidx.media3.exoplayer.hls.playlist.HlsMediaPlaylist in project media by androidx.
the class HlsMediaPlaylistParserTest method parseMediaPlaylist_withByteRanges.
@Test
public void parseMediaPlaylist_withByteRanges() throws Exception {
Uri playlistUri = Uri.parse("https://example.com/test.m3u8");
String playlistString = "#EXTM3U\n" + "#EXT-X-VERSION:3\n" + "#EXT-X-TARGETDURATION:5\n" + "\n" + "#EXT-X-BYTERANGE:200@100\n" + "#EXT-X-MAP:URI=\"stream.mp4\"\n" + "#EXTINF:5,\n" + "#EXT-X-BYTERANGE:400\n" + "stream.mp4\n" + "#EXTINF:5,\n" + "#EXT-X-BYTERANGE:500\n" + "stream.mp4\n" + "#EXT-X-DISCONTINUITY\n" + "#EXT-X-MAP:URI=\"init.mp4\"\n" + "#EXTINF:5,\n" + "segment.mp4\n";
InputStream inputStream = new ByteArrayInputStream(Util.getUtf8Bytes(playlistString));
HlsPlaylist playlist = new HlsPlaylistParser().parse(playlistUri, inputStream);
HlsMediaPlaylist mediaPlaylist = (HlsMediaPlaylist) playlist;
List<Segment> segments = mediaPlaylist.segments;
assertThat(segments).isNotNull();
assertThat(segments).hasSize(3);
Segment segment = segments.get(0);
assertThat(segment.initializationSegment.byteRangeOffset).isEqualTo(100);
assertThat(segment.initializationSegment.byteRangeLength).isEqualTo(200);
assertThat(segment.byteRangeOffset).isEqualTo(300);
assertThat(segment.byteRangeLength).isEqualTo(400);
segment = segments.get(1);
assertThat(segment.byteRangeOffset).isEqualTo(700);
assertThat(segment.byteRangeLength).isEqualTo(500);
segment = segments.get(2);
assertThat(segment.initializationSegment.byteRangeOffset).isEqualTo(0);
assertThat(segment.initializationSegment.byteRangeLength).isEqualTo(C.LENGTH_UNSET);
assertThat(segment.byteRangeOffset).isEqualTo(0);
assertThat(segment.byteRangeLength).isEqualTo(C.LENGTH_UNSET);
}
use of androidx.media3.exoplayer.hls.playlist.HlsMediaPlaylist in project media by androidx.
the class HlsChunkSource method createMediaChunkIterators.
/**
* Returns an array of {@link MediaChunkIterator}s for upcoming media chunks.
*
* @param previous The previous media chunk. May be null.
* @param loadPositionUs The position at which the iterators will start.
* @return Array of {@link MediaChunkIterator}s for each track.
*/
public MediaChunkIterator[] createMediaChunkIterators(@Nullable HlsMediaChunk previous, long loadPositionUs) {
int oldTrackIndex = previous == null ? C.INDEX_UNSET : trackGroup.indexOf(previous.trackFormat);
MediaChunkIterator[] chunkIterators = new MediaChunkIterator[trackSelection.length()];
for (int i = 0; i < chunkIterators.length; i++) {
int trackIndex = trackSelection.getIndexInTrackGroup(i);
Uri playlistUrl = playlistUrls[trackIndex];
if (!playlistTracker.isSnapshotValid(playlistUrl)) {
chunkIterators[i] = MediaChunkIterator.EMPTY;
continue;
}
@Nullable HlsMediaPlaylist playlist = playlistTracker.getPlaylistSnapshot(playlistUrl, /* isForPlayback= */
false);
// Playlist snapshot is valid (checked by if() above) so playlist must be non-null.
checkNotNull(playlist);
long startOfPlaylistInPeriodUs = playlist.startTimeUs - playlistTracker.getInitialStartTimeUs();
boolean switchingTrack = trackIndex != oldTrackIndex;
Pair<Long, Integer> chunkMediaSequenceAndPartIndex = getNextMediaSequenceAndPartIndex(previous, switchingTrack, playlist, startOfPlaylistInPeriodUs, loadPositionUs);
long chunkMediaSequence = chunkMediaSequenceAndPartIndex.first;
int partIndex = chunkMediaSequenceAndPartIndex.second;
chunkIterators[i] = new HlsMediaPlaylistSegmentIterator(playlist.baseUri, startOfPlaylistInPeriodUs, getSegmentBaseList(playlist, chunkMediaSequence, partIndex));
}
return chunkIterators;
}
use of androidx.media3.exoplayer.hls.playlist.HlsMediaPlaylist in project media by androidx.
the class HlsChunkSource method getSegmentBaseList.
// Package methods.
/**
* Returns a list with all segment bases in the playlist starting from {@code mediaSequence} and
* {@code partIndex} in the given playlist. The list may be empty if the starting point is not in
* the playlist.
*/
@VisibleForTesting
static /* package */
List<HlsMediaPlaylist.SegmentBase> getSegmentBaseList(HlsMediaPlaylist playlist, long mediaSequence, int partIndex) {
int firstSegmentIndexInPlaylist = (int) (mediaSequence - playlist.mediaSequence);
if (firstSegmentIndexInPlaylist < 0 || playlist.segments.size() < firstSegmentIndexInPlaylist) {
// The first media sequence is not in the playlist.
return ImmutableList.of();
}
List<HlsMediaPlaylist.SegmentBase> segmentBases = new ArrayList<>();
if (firstSegmentIndexInPlaylist < playlist.segments.size()) {
if (partIndex != C.INDEX_UNSET) {
// The iterator starts with a part that belongs to a segment.
Segment firstSegment = playlist.segments.get(firstSegmentIndexInPlaylist);
if (partIndex == 0) {
// Use the full segment instead of the first part.
segmentBases.add(firstSegment);
} else if (partIndex < firstSegment.parts.size()) {
// Add the parts from the first requested segment.
segmentBases.addAll(firstSegment.parts.subList(partIndex, firstSegment.parts.size()));
}
firstSegmentIndexInPlaylist++;
}
partIndex = 0;
// Add all remaining segments.
segmentBases.addAll(playlist.segments.subList(firstSegmentIndexInPlaylist, playlist.segments.size()));
}
if (playlist.partTargetDurationUs != C.TIME_UNSET) {
// That's a low latency playlist.
partIndex = partIndex == C.INDEX_UNSET ? 0 : partIndex;
if (partIndex < playlist.trailingParts.size()) {
segmentBases.addAll(playlist.trailingParts.subList(partIndex, playlist.trailingParts.size()));
}
}
return Collections.unmodifiableList(segmentBases);
}
Aggregations