blob: ce5ff84759ae13abc0d88be24d13fc982600b719 [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2.source.hls.playlist;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
import android.net.Uri;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ParserException;
import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist.Segment;
import com.google.android.exoplayer2.util.Util;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
/** Test for {@link HlsMediaPlaylistParserTest}. */
@RunWith(AndroidJUnit4.class)
public class HlsMediaPlaylistParserTest {
@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"
+ "#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"
+ "#EXT-X-BYTERANGE:51501\n" // @2147535149
+ "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"
+ "#EXTINF:7.975\n" // Trailing comma is omitted.
+ "#EXT-X-BYTERANGE:51740\n" // @2147586650
+ "https://priv.example.com/fileSequence2682.ts\n"
+ "\n"
+ "#EXTINF:7.975,\n"
+ "https://priv.example.com/fileSequence2683.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();
List<Segment> segments = mediaPlaylist.segments;
assertThat(segments).isNotNull();
assertThat(segments).hasSize(5);
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(Util.toUpperInvariant(segment.encryptionIV)).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(Util.toUpperInvariant(segment.encryptionIV)).isEqualTo("A7B");
assertThat(segment.byterangeLength).isEqualTo(C.LENGTH_UNSET);
assertThat(segment.byterangeOffset).isEqualTo(0);
assertThat(segment.url).isEqualTo("https://priv.example.com/fileSequence2683.ts");
}
@Test
public void parseSampleAesMethod() throws Exception {
Uri playlistUri = Uri.parse("https://example.com/test.m3u8");
String playlistString =
"#EXTM3U\n"
+ "#EXT-X-MEDIA-SEQUENCE:0\n"
+ "#EXTINF:8,\n"
+ "https://priv.example.com/1.ts\n"
+ "\n"
+ "#EXT-X-KEY:METHOD=SAMPLE-AES,URI="
+ "\"data:text/plain;base64,VGhpcyBpcyBhbiBlYXN0ZXIgZWdn\","
+ "IV=0x9358382AEB449EE23C3D809DA0B9CCD3,KEYFORMATVERSIONS=\"1\","
+ "KEYFORMAT=\"com.widevine\",IV=0x1566B\n"
+ "#EXTINF:8,\n"
+ "https://priv.example.com/2.ts\n"
+ "#EXT-X-ENDLIST\n";
InputStream inputStream = new ByteArrayInputStream(Util.getUtf8Bytes(playlistString));
HlsMediaPlaylist playlist =
(HlsMediaPlaylist) new HlsPlaylistParser().parse(playlistUri, inputStream);
assertThat(playlist.protectionSchemes.schemeType).isEqualTo(C.CENC_TYPE_cbcs);
assertThat(playlist.protectionSchemes.get(0).matches(C.WIDEVINE_UUID)).isTrue();
assertThat(playlist.protectionSchemes.get(0).hasData()).isFalse();
assertThat(playlist.segments.get(0).drmInitData).isNull();
assertThat(playlist.segments.get(1).drmInitData.get(0).matches(C.WIDEVINE_UUID)).isTrue();
assertThat(playlist.segments.get(1).drmInitData.get(0).hasData()).isTrue();
}
@Test
public void parseSampleAesCencMethod() throws Exception {
Uri playlistUri = Uri.parse("https://example.com/test.m3u8");
String playlistString =
"#EXTM3U\n"
+ "#EXT-X-MEDIA-SEQUENCE:0\n"
+ "#EXTINF:8,\n"
+ "https://priv.example.com/1.ts\n"
+ "\n"
+ "#EXT-X-KEY:URI=\"data:text/plain;base64,VGhpcyBpcyBhbiBlYXN0ZXIgZWdn\","
+ "IV=0x9358382AEB449EE23C3D809DA0B9CCD3,KEYFORMATVERSIONS=\"1\","
+ "KEYFORMAT=\"urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed\","
+ "IV=0x1566B,METHOD=SAMPLE-AES-CENC \n"
+ "#EXTINF:8,\n"
+ "https://priv.example.com/2.ts\n"
+ "#EXT-X-ENDLIST\n";
InputStream inputStream = new ByteArrayInputStream(Util.getUtf8Bytes(playlistString));
HlsMediaPlaylist playlist =
(HlsMediaPlaylist) new HlsPlaylistParser().parse(playlistUri, inputStream);
assertThat(playlist.protectionSchemes.schemeType).isEqualTo(C.CENC_TYPE_cenc);
assertThat(playlist.protectionSchemes.get(0).matches(C.WIDEVINE_UUID)).isTrue();
assertThat(playlist.protectionSchemes.get(0).hasData()).isFalse();
}
@Test
public void parseSampleAesCtrMethod() throws Exception {
Uri playlistUri = Uri.parse("https://example.com/test.m3u8");
String playlistString =
"#EXTM3U\n"
+ "#EXT-X-MEDIA-SEQUENCE:0\n"
+ "#EXTINF:8,\n"
+ "https://priv.example.com/1.ts\n"
+ "\n"
+ "#EXT-X-KEY:METHOD=SAMPLE-AES-CTR,URI="
+ "\"data:text/plain;base64,VGhpcyBpcyBhbiBlYXN0ZXIgZWdn\","
+ "IV=0x9358382AEB449EE23C3D809DA0B9CCD3,KEYFORMATVERSIONS=\"1\","
+ "KEYFORMAT=\"com.widevine\",IV=0x1566B\n"
+ "#EXTINF:8,\n"
+ "https://priv.example.com/2.ts\n"
+ "#EXT-X-ENDLIST\n";
InputStream inputStream = new ByteArrayInputStream(Util.getUtf8Bytes(playlistString));
HlsMediaPlaylist playlist =
(HlsMediaPlaylist) new HlsPlaylistParser().parse(playlistUri, inputStream);
assertThat(playlist.protectionSchemes.schemeType).isEqualTo(C.CENC_TYPE_cenc);
assertThat(playlist.protectionSchemes.get(0).matches(C.WIDEVINE_UUID)).isTrue();
assertThat(playlist.protectionSchemes.get(0).hasData()).isFalse();
}
@Test
public void multipleExtXKeysForSingleSegment() throws Exception {
Uri playlistUri = Uri.parse("https://example.com/test.m3u8");
String playlistString =
"#EXTM3U\n"
+ "#EXT-X-VERSION:6\n"
+ "#EXT-X-TARGETDURATION:6\n"
+ "#EXT-X-MAP:URI=\"map.mp4\"\n"
+ "#EXTINF:5.005,\n"
+ "s000000.mp4\n"
+ "#EXT-X-KEY:METHOD=SAMPLE-AES,"
+ "KEYFORMAT=\"urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed\","
+ "KEYFORMATVERSIONS=\"1\","
+ "URI=\"data:text/plain;base64,Tm90aGluZyB0byBzZWUgaGVyZQ==\"\n"
+ "#EXT-X-KEY:METHOD=SAMPLE-AES,KEYFORMAT=\"com.microsoft.playready\","
+ "KEYFORMATVERSIONS=\"1\","
+ "URI=\"data:text/plain;charset=UTF-16;base64,VGhpcyBpcyBhbiBlYXN0ZXIgZWdn\"\n"
+ "#EXT-X-KEY:METHOD=SAMPLE-AES,KEYFORMAT=\"com.apple.streamingkeydelivery\","
+ "KEYFORMATVERSIONS=\"1\",URI=\"skd://QW5vdGhlciBlYXN0ZXIgZWdn\"\n"
+ "#EXT-X-MAP:URI=\"map.mp4\"\n"
+ "#EXTINF:5.005,\n"
+ "s000000.mp4\n"
+ "#EXTINF:5.005,\n"
+ "s000001.mp4\n"
+ "#EXT-X-KEY:METHOD=SAMPLE-AES,"
+ "KEYFORMAT=\"urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed\","
+ "KEYFORMATVERSIONS=\"1\","
+ "URI=\"data:text/plain;base64,RG9uJ3QgeW91IGdldCB0aXJlZCBvZiBkb2luZyB0aGlzPw==\""
+ "\n"
+ "#EXT-X-KEY:METHOD=SAMPLE-AES,KEYFORMAT=\"com.microsoft.playready\","
+ "KEYFORMATVERSIONS=\"1\","
+ "URI=\"data:text/plain;charset=UTF-16;base64,T2ssIGl0J3Mgbm90IGZ1biBhbnltb3Jl\"\n"
+ "#EXT-X-KEY:METHOD=SAMPLE-AES,KEYFORMAT=\"com.apple.streamingkeydelivery\","
+ "KEYFORMATVERSIONS=\"1\","
+ "URI=\"skd://V2FpdCB1bnRpbCB5b3Ugc2VlIHRoZSBuZXh0IG9uZSE=\"\n"
+ "#EXTINF:5.005,\n"
+ "s000024.mp4\n"
+ "#EXTINF:5.005,\n"
+ "s000025.mp4\n"
+ "#EXT-X-KEY:METHOD=NONE\n"
+ "#EXTINF:5.005,\n"
+ "s000026.mp4\n"
+ "#EXTINF:5.005,\n"
+ "s000026.mp4\n";
InputStream inputStream = new ByteArrayInputStream(Util.getUtf8Bytes(playlistString));
HlsMediaPlaylist playlist =
(HlsMediaPlaylist) new HlsPlaylistParser().parse(playlistUri, inputStream);
assertThat(playlist.protectionSchemes.schemeType).isEqualTo(C.CENC_TYPE_cbcs);
// Unsupported protection schemes like com.apple.streamingkeydelivery are ignored.
assertThat(playlist.protectionSchemes.schemeDataCount).isEqualTo(2);
assertThat(playlist.protectionSchemes.get(0).matches(C.PLAYREADY_UUID)).isTrue();
assertThat(playlist.protectionSchemes.get(0).hasData()).isFalse();
assertThat(playlist.protectionSchemes.get(1).matches(C.WIDEVINE_UUID)).isTrue();
assertThat(playlist.protectionSchemes.get(1).hasData()).isFalse();
assertThat(playlist.segments.get(0).drmInitData).isNull();
assertThat(playlist.segments.get(1).drmInitData.get(0).matches(C.PLAYREADY_UUID)).isTrue();
assertThat(playlist.segments.get(1).drmInitData.get(0).hasData()).isTrue();
assertThat(playlist.segments.get(1).drmInitData.get(1).matches(C.WIDEVINE_UUID)).isTrue();
assertThat(playlist.segments.get(1).drmInitData.get(1).hasData()).isTrue();
assertThat(playlist.segments.get(1).drmInitData)
.isEqualTo(playlist.segments.get(2).drmInitData);
assertThat(playlist.segments.get(2).drmInitData)
.isNotEqualTo(playlist.segments.get(3).drmInitData);
assertThat(playlist.segments.get(3).drmInitData.get(0).matches(C.PLAYREADY_UUID)).isTrue();
assertThat(playlist.segments.get(3).drmInitData.get(0).hasData()).isTrue();
assertThat(playlist.segments.get(3).drmInitData.get(1).matches(C.WIDEVINE_UUID)).isTrue();
assertThat(playlist.segments.get(3).drmInitData.get(1).hasData()).isTrue();
assertThat(playlist.segments.get(3).drmInitData)
.isEqualTo(playlist.segments.get(4).drmInitData);
assertThat(playlist.segments.get(5).drmInitData).isNull();
assertThat(playlist.segments.get(6).drmInitData).isNull();
}
@Test
public void gapTag() throws IOException {
Uri playlistUri = Uri.parse("https://example.com/test2.m3u8");
String playlistString =
"#EXTM3U\n"
+ "#EXT-X-VERSION:3\n"
+ "#EXT-X-TARGETDURATION:5\n"
+ "#EXT-X-PLAYLIST-TYPE:VOD\n"
+ "#EXT-X-MEDIA-SEQUENCE:0\n"
+ "#EXT-X-PROGRAM-DATE-TIME:2016-09-22T02:00:01+00:00\n"
+ "#EXT-X-KEY:METHOD=AES-128,URI=\"https://example.com/key?value=something\"\n"
+ "#EXTINF:5.005,\n"
+ "02/00/27.ts\n"
+ "#EXTINF:5.005,\n"
+ "02/00/32.ts\n"
+ "#EXT-X-KEY:METHOD=NONE\n"
+ "#EXTINF:5.005,\n"
+ "#EXT-X-GAP \n"
+ "../dummy.ts\n"
+ "#EXT-X-KEY:METHOD=AES-128,URI=\"https://key-service.bamgrid.com/1.0/key?"
+ "hex-value=9FB8989D15EEAAF8B21B860D7ED3072A\",IV=0x410C8AC18AA42EFA18B5155484F5FC34\n"
+ "#EXTINF:5.005,\n"
+ "02/00/42.ts\n"
+ "#EXTINF:5.005,\n"
+ "02/00/47.ts\n";
InputStream inputStream = new ByteArrayInputStream(Util.getUtf8Bytes(playlistString));
HlsMediaPlaylist playlist =
(HlsMediaPlaylist) new HlsPlaylistParser().parse(playlistUri, inputStream);
assertThat(playlist.hasEndTag).isFalse();
assertThat(playlist.segments.get(1).hasGapTag).isFalse();
assertThat(playlist.segments.get(2).hasGapTag).isTrue();
assertThat(playlist.segments.get(3).hasGapTag).isFalse();
}
@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");
}
@Test
public void encryptedMapTag() 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"
+ "#EXT-X-KEY:METHOD=AES-128,"
+ "URI=\"https://priv.example.com/key.php?r=2680\",IV=0x1566B\n"
+ "#EXT-X-MAP:URI=\"init1.ts\""
+ "#EXTINF:5.005,\n"
+ "02/00/32.ts\n"
+ "#EXT-X-KEY:METHOD=NONE\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;
Segment initSegment1 = segments.get(0).initializationSegment;
assertThat(initSegment1.fullSegmentEncryptionKeyUri)
.isEqualTo("https://priv.example.com/key.php?r=2680");
assertThat(initSegment1.encryptionIV).isEqualTo("0x1566B");
Segment initSegment2 = segments.get(1).initializationSegment;
assertThat(initSegment2.fullSegmentEncryptionKeyUri).isNull();
assertThat(initSegment2.encryptionIV).isNull();
}
@Test
public void encryptedMapTagWithNoIvFails() 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"
+ "#EXT-X-KEY:METHOD=AES-128,"
+ "URI=\"https://priv.example.com/key.php?r=2680\"\n"
+ "#EXT-X-MAP:URI=\"init1.ts\""
+ "#EXTINF:5.005,\n"
+ "02/00/32.ts\n";
InputStream inputStream = new ByteArrayInputStream(Util.getUtf8Bytes(playlistString));
try {
new HlsPlaylistParser().parse(playlistUri, inputStream);
fail();
} catch (ParserException e) {
// Expected because the initialization segment does not have a defined initialization vector,
// although it is affected by an EXT-X-KEY tag.
}
}
@Test
public void masterPlaylistAttributeInheritance() 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 standalonePlaylist =
(HlsMediaPlaylist) new HlsPlaylistParser().parse(playlistUri, inputStream);
assertThat(standalonePlaylist.hasIndependentSegments).isFalse();
inputStream.reset();
HlsMasterPlaylist masterPlaylist =
new HlsMasterPlaylist(
/* baseUri= */ "https://example.com/",
/* tags= */ Collections.emptyList(),
/* variants= */ Collections.emptyList(),
/* videos= */ Collections.emptyList(),
/* audios= */ Collections.emptyList(),
/* subtitles= */ Collections.emptyList(),
/* closedCaptions= */ Collections.emptyList(),
/* muxedAudioFormat= */ null,
/* muxedCaptionFormats= */ null,
/* hasIndependentSegments= */ true,
/* variableDefinitions= */ Collections.emptyMap(),
/* sessionKeyDrmInitData= */ Collections.emptyList());
HlsMediaPlaylist playlistWithInheritance =
(HlsMediaPlaylist) new HlsPlaylistParser(masterPlaylist).parse(playlistUri, inputStream);
assertThat(playlistWithInheritance.hasIndependentSegments).isTrue();
}
@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}");
}
@Test
public void inheritedVariableSubstitution() throws IOException {
Uri playlistUri = Uri.parse("https://example.com/test3.m3u8");
String playlistString =
"#EXTM3U\n"
+ "#EXT-X-VERSION:8\n"
+ "#EXT-X-TARGETDURATION:5\n"
+ "#EXT-X-MEDIA-SEQUENCE:10\n"
+ "#EXT-X-DEFINE:IMPORT=\"imported_base\"\n"
+ "#EXTINF:5.005,\n"
+ "{$imported_base}1.ts\n"
+ "#EXTINF:5.005,\n"
+ "{$imported_base}2.ts\n"
+ "#EXTINF:5.005,\n"
+ "{$imported_base}3.ts\n"
+ "#EXTINF:5.005,\n"
+ "{$imported_base}4.ts\n";
InputStream inputStream = new ByteArrayInputStream(Util.getUtf8Bytes(playlistString));
HashMap<String, String> variableDefinitions = new HashMap<>();
variableDefinitions.put("imported_base", "long_path");
HlsMasterPlaylist masterPlaylist =
new HlsMasterPlaylist(
/* baseUri= */ "",
/* tags= */ Collections.emptyList(),
/* variants= */ Collections.emptyList(),
/* videos= */ Collections.emptyList(),
/* audios= */ Collections.emptyList(),
/* subtitles= */ Collections.emptyList(),
/* closedCaptions= */ Collections.emptyList(),
/* muxedAudioFormat= */ null,
/* muxedCaptionFormats= */ Collections.emptyList(),
/* hasIndependentSegments= */ false,
variableDefinitions,
/* sessionKeyDrmInitData= */ Collections.emptyList());
HlsMediaPlaylist playlist =
(HlsMediaPlaylist) new HlsPlaylistParser(masterPlaylist).parse(playlistUri, inputStream);
for (int i = 1; i <= 4; i++) {
assertThat(playlist.segments.get(i - 1).url).isEqualTo("long_path" + i + ".ts");
}
}
}