blob: 05bc3ba98579393c8681bb63e0dbf0187c12e992 [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.Format;
import com.google.android.exoplayer2.ParserException;
import com.google.android.exoplayer2.metadata.Metadata;
import com.google.android.exoplayer2.source.hls.HlsTrackMetadataEntry;
import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.Variant;
import com.google.android.exoplayer2.util.MimeTypes;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
/** Test for {@link HlsMasterPlaylist}. */
@RunWith(AndroidJUnit4.class)
public class HlsMasterPlaylistParserTest {
private static final String PLAYLIST_URI = "https://example.com/test.m3u8";
private static final String PLAYLIST_SIMPLE =
" #EXTM3U \n"
+ "\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=1280000,"
+ "CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128\n"
+ "http://example.com/low.m3u8\n"
+ "\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"mp4a.40.2 , avc1.66.30 \"\n"
+ "http://example.com/spaces_in_codecs.m3u8\n"
+ "\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=2560000,FRAME-RATE=25,RESOLUTION=384x160\n"
+ "http://example.com/mid.m3u8\n"
+ "\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=7680000,FRAME-RATE=29.997\n"
+ "http://example.com/hi.m3u8\n"
+ "\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=65000,CODECS=\"mp4a.40.5\"\n"
+ "http://example.com/audio-only.m3u8";
private static final String PLAYLIST_WITH_AVG_BANDWIDTH =
" #EXTM3U \n"
+ "\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=1280000,"
+ "CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128\n"
+ "http://example.com/low.m3u8\n"
+ "\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=1280000,AVERAGE-BANDWIDTH=1270000,"
+ "CODECS=\"mp4a.40.2 , avc1.66.30 \"\n"
+ "http://example.com/spaces_in_codecs.m3u8\n";
private static final String PLAYLIST_WITH_INVALID_HEADER =
"#EXTMU3\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=1280000,"
+ "CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128\n"
+ "http://example.com/low.m3u8\n";
private static final String PLAYLIST_WITH_CC =
" #EXTM3U \n"
+ "#EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS,GROUP-ID=\"cc1\","
+ "LANGUAGE=\"es\",NAME=\"Eng\",INSTREAM-ID=\"SERVICE4\"\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=1280000,"
+ "CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128\n"
+ "http://example.com/low.m3u8\n";
private static final String PLAYLIST_WITH_CHANNELS_ATTRIBUTE =
" #EXTM3U \n"
+ "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"audio\",CHANNELS=\"6\",NAME=\"Eng6\","
+ "URI=\"something.m3u8\"\n"
+ "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"audio\",CHANNELS=\"2/6\",NAME=\"Eng26\","
+ "URI=\"something2.m3u8\"\n"
+ "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"audio\",NAME=\"Eng\","
+ "URI=\"something3.m3u8\"\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=1280000,"
+ "CODECS=\"mp4a.40.2,avc1.66.30\",AUDIO=\"audio\",RESOLUTION=304x128\n"
+ "http://example.com/low.m3u8\n";
private static final String PLAYLIST_WITHOUT_CC =
" #EXTM3U \n"
+ "#EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS,GROUP-ID=\"cc1\","
+ "LANGUAGE=\"es\",NAME=\"Eng\",INSTREAM-ID=\"SERVICE4\"\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=1280000,"
+ "CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128,"
+ "CLOSED-CAPTIONS=NONE\n"
+ "http://example.com/low.m3u8\n";
private static final String PLAYLIST_WITH_SUBTITLES =
" #EXTM3U \n"
+ "#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID=\"sub1\","
+ "LANGUAGE=\"es\",NAME=\"Eng\"\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=1280000,"
+ "CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128\n"
+ "http://example.com/low.m3u8\n";
private static final String PLAYLIST_WITH_AUDIO_MEDIA_TAG =
"#EXTM3U\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=2227464,CODECS=\"avc1.640020,mp4a.40.2\",AUDIO=\"aud1\"\n"
+ "uri1.m3u8\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=8178040,CODECS=\"avc1.64002a,mp4a.40.2\",AUDIO=\"aud1\"\n"
+ "uri2.m3u8\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=2448841,CODECS=\"avc1.640020,ac-3\",AUDIO=\"aud2\"\n"
+ "uri1.m3u8\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=8399417,CODECS=\"avc1.64002a,ac-3\",AUDIO=\"aud2\"\n"
+ "uri2.m3u8\n"
+ "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"aud1\",LANGUAGE=\"en\",NAME=\"English\","
+ "AUTOSELECT=YES,DEFAULT=YES,CHANNELS=\"2\",URI=\"a1/prog_index.m3u8\"\n"
+ "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"aud2\",LANGUAGE=\"en\",NAME=\"English\","
+ "AUTOSELECT=YES,DEFAULT=YES,CHANNELS=\"6\",URI=\"a2/prog_index.m3u8\"\n";
private static final String PLAYLIST_WITH_INDEPENDENT_SEGMENTS =
" #EXTM3U\n"
+ "\n"
+ "#EXT-X-INDEPENDENT-SEGMENTS\n"
+ "\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=1280000,"
+ "CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128\n"
+ "http://example.com/low.m3u8\n"
+ "\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"mp4a.40.2 , avc1.66.30 \"\n"
+ "http://example.com/spaces_in_codecs.m3u8\n";
private static final String PLAYLIST_WITH_VARIABLE_SUBSTITUTION =
" #EXTM3U \n"
+ "\n"
+ "#EXT-X-DEFINE:NAME=\"codecs\",VALUE=\"mp4a.40.5\"\n"
+ "#EXT-X-DEFINE:NAME=\"tricky\",VALUE=\"This/{$nested}/reference/shouldnt/work\"\n"
+ "#EXT-X-DEFINE:NAME=\"nested\",VALUE=\"This should not be inserted\"\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=65000,CODECS=\"{$codecs}\"\n"
+ "http://example.com/{$tricky}\n";
private static final String PLAYLIST_WITH_MATCHING_STREAM_INF_URLS =
"#EXTM3U\n"
+ "#EXT-X-VERSION:6\n"
+ "\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=2227464,"
+ "CLOSED-CAPTIONS=\"cc1\",AUDIO=\"aud1\",SUBTITLES=\"sub1\"\n"
+ "v5/prog_index.m3u8\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=6453202,"
+ "CLOSED-CAPTIONS=\"cc1\",AUDIO=\"aud1\",SUBTITLES=\"sub1\"\n"
+ "v8/prog_index.m3u8\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=5054232,"
+ "CLOSED-CAPTIONS=\"cc1\",AUDIO=\"aud1\",SUBTITLES=\"sub1\"\n"
+ "v7/prog_index.m3u8\n"
+ "\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=2448841,"
+ "CLOSED-CAPTIONS=\"cc1\",AUDIO=\"aud2\",SUBTITLES=\"sub1\"\n"
+ "v5/prog_index.m3u8\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=8399417,"
+ "CLOSED-CAPTIONS=\"cc1\",AUDIO=\"aud2\",SUBTITLES=\"sub1\"\n"
+ "v9/prog_index.m3u8\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=5275609,"
+ "CLOSED-CAPTIONS=\"cc1\",AUDIO=\"aud2\",SUBTITLES=\"sub1\"\n"
+ "v7/prog_index.m3u8\n"
+ "\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=2256841,"
+ "CLOSED-CAPTIONS=\"cc1\",AUDIO=\"aud3\",SUBTITLES=\"sub1\"\n"
+ "v5/prog_index.m3u8\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=8207417,"
+ "CLOSED-CAPTIONS=\"cc1\",AUDIO=\"aud3\",SUBTITLES=\"sub1\"\n"
+ "v9/prog_index.m3u8\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=6482579,"
+ "CLOSED-CAPTIONS=\"cc1\",AUDIO=\"aud3\",SUBTITLES=\"sub1\"\n"
+ "v8/prog_index.m3u8\n"
+ "\n"
+ "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"aud1\",NAME=\"English\",URI=\"a1/index.m3u8\"\n"
+ "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"aud2\",NAME=\"English\",URI=\"a2/index.m3u8\"\n"
+ "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"aud3\",NAME=\"English\",URI=\"a3/index.m3u8\"\n"
+ "\n"
+ "#EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS,"
+ "GROUP-ID=\"cc1\",NAME=\"English\",INSTREAM-ID=\"CC1\"\n"
+ "\n"
+ "#EXT-X-MEDIA:TYPE=SUBTITLES,"
+ "GROUP-ID=\"sub1\",NAME=\"English\",URI=\"s1/en/prog_index.m3u8\"\n";
private static final String PLAYLIST_WITH_TTML_SUBTITLE =
" #EXTM3U\n"
+ "\n"
+ "#EXT-X-VERSION:6\n"
+ "\n"
+ "#EXT-X-INDEPENDENT-SEGMENTS\n"
+ "\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"stpp.ttml.im1t,mp4a.40.2,avc1.66.30\",RESOLUTION=304x128,AUDIO=\"aud1\",SUBTITLES=\"sub1\"\n"
+ "http://example.com/low.m3u8\n"
+ "\n"
+ "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"aud1\",NAME=\"English\",URI=\"a1/index.m3u8\"\n"
+ "#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID=\"sub1\",NAME=\"English\",AUTOSELECT=YES,DEFAULT=YES,URI=\"s1/en/prog_index.m3u8\"\n";
private static final String PLAYLIST_WITH_IFRAME_VARIANTS =
"#EXTM3U\n"
+ "#EXT-X-VERSION:5\n"
+ "#EXT-X-MEDIA:URI=\"AUDIO_English/index.m3u8\",TYPE=AUDIO,GROUP-ID=\"audio-aac\",LANGUAGE=\"en\",NAME=\"English\",AUTOSELECT=YES\n"
+ "#EXT-X-MEDIA:URI=\"AUDIO_Spanish/index.m3u8\",TYPE=AUDIO,GROUP-ID=\"audio-aac\",LANGUAGE=\"es\",NAME=\"Spanish\",AUTOSELECT=YES\n"
+ "#EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS,GROUP-ID=\"cc1\",LANGUAGE=\"en\",NAME=\"English\",AUTOSELECT=YES,DEFAULT=YES,INSTREAM-ID=\"CC1\"\n"
+ "#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=400000,RESOLUTION=480x320,CODECS=\"mp4a.40.2,avc1.640015\",AUDIO=\"audio-aac\",CLOSED-CAPTIONS=\"cc1\"\n"
+ "400000/index.m3u8\n"
+ "#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1000000,RESOLUTION=848x480,CODECS=\"mp4a.40.2,avc1.64001f\",AUDIO=\"audio-aac\",CLOSED-CAPTIONS=\"cc1\"\n"
+ "1000000/index.m3u8\n"
+ "#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=3220000,RESOLUTION=1280x720,CODECS=\"mp4a.40.2,avc1.64001f\",AUDIO=\"audio-aac\",CLOSED-CAPTIONS=\"cc1\"\n"
+ "3220000/index.m3u8\n"
+ "#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=8940000,RESOLUTION=1920x1080,CODECS=\"mp4a.40.2,avc1.640028\",AUDIO=\"audio-aac\",CLOSED-CAPTIONS=\"cc1\"\n"
+ "8940000/index.m3u8\n"
+ "#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=1313400,RESOLUTION=1920x1080,CODECS=\"avc1.640028\",URI=\"iframe_1313400/index.m3u8\"\n";
@Test
public void parseMasterPlaylist_withSimple_success() throws IOException {
HlsMasterPlaylist masterPlaylist = parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_SIMPLE);
List<HlsMasterPlaylist.Variant> variants = masterPlaylist.variants;
assertThat(variants).hasSize(5);
assertThat(masterPlaylist.muxedCaptionFormats).isNull();
assertThat(variants.get(0).format.bitrate).isEqualTo(1280000);
assertThat(variants.get(0).format.codecs).isEqualTo("mp4a.40.2,avc1.66.30");
assertThat(variants.get(0).format.width).isEqualTo(304);
assertThat(variants.get(0).format.height).isEqualTo(128);
assertThat(variants.get(0).url).isEqualTo(Uri.parse("http://example.com/low.m3u8"));
assertThat(variants.get(1).format.bitrate).isEqualTo(1280000);
assertThat(variants.get(1).format.codecs).isEqualTo("mp4a.40.2 , avc1.66.30 ");
assertThat(variants.get(1).url)
.isEqualTo(Uri.parse("http://example.com/spaces_in_codecs.m3u8"));
assertThat(variants.get(2).format.bitrate).isEqualTo(2560000);
assertThat(variants.get(2).format.codecs).isNull();
assertThat(variants.get(2).format.width).isEqualTo(384);
assertThat(variants.get(2).format.height).isEqualTo(160);
assertThat(variants.get(2).format.frameRate).isEqualTo(25.0f);
assertThat(variants.get(2).url).isEqualTo(Uri.parse("http://example.com/mid.m3u8"));
assertThat(variants.get(3).format.bitrate).isEqualTo(7680000);
assertThat(variants.get(3).format.codecs).isNull();
assertThat(variants.get(3).format.width).isEqualTo(Format.NO_VALUE);
assertThat(variants.get(3).format.height).isEqualTo(Format.NO_VALUE);
assertThat(variants.get(3).format.frameRate).isEqualTo(29.997f);
assertThat(variants.get(3).url).isEqualTo(Uri.parse("http://example.com/hi.m3u8"));
assertThat(variants.get(4).format.bitrate).isEqualTo(65000);
assertThat(variants.get(4).format.codecs).isEqualTo("mp4a.40.5");
assertThat(variants.get(4).format.width).isEqualTo(Format.NO_VALUE);
assertThat(variants.get(4).format.height).isEqualTo(Format.NO_VALUE);
assertThat(variants.get(4).format.frameRate).isEqualTo((float) Format.NO_VALUE);
assertThat(variants.get(4).url).isEqualTo(Uri.parse("http://example.com/audio-only.m3u8"));
}
@Test
public void parseMasterPlaylist_withAverageBandwidth_success() throws IOException {
HlsMasterPlaylist masterPlaylist =
parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_WITH_AVG_BANDWIDTH);
List<HlsMasterPlaylist.Variant> variants = masterPlaylist.variants;
assertThat(variants.get(0).format.bitrate).isEqualTo(1280000);
assertThat(variants.get(1).format.bitrate).isEqualTo(1280000);
}
@Test
public void parseMasterPlaylist_withInvalidHeader_throwsException() throws IOException {
try {
parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_WITH_INVALID_HEADER);
fail("Expected exception not thrown.");
} catch (ParserException e) {
// Expected due to invalid header.
}
}
@Test
public void parseMasterPlaylist_withClosedCaption_success() throws IOException {
HlsMasterPlaylist playlist = parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_WITH_CC);
assertThat(playlist.muxedCaptionFormats).hasSize(1);
Format closedCaptionFormat = playlist.muxedCaptionFormats.get(0);
assertThat(closedCaptionFormat.sampleMimeType).isEqualTo(MimeTypes.APPLICATION_CEA708);
assertThat(closedCaptionFormat.accessibilityChannel).isEqualTo(4);
assertThat(closedCaptionFormat.language).isEqualTo("es");
}
@Test
public void parseMasterPlaylist_withChannelsAttribute_success() throws IOException {
HlsMasterPlaylist playlist =
parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_WITH_CHANNELS_ATTRIBUTE);
List<HlsMasterPlaylist.Rendition> audios = playlist.audios;
assertThat(audios).hasSize(3);
assertThat(audios.get(0).format.channelCount).isEqualTo(6);
assertThat(audios.get(1).format.channelCount).isEqualTo(2);
assertThat(audios.get(2).format.channelCount).isEqualTo(Format.NO_VALUE);
}
@Test
public void parseMasterPlaylist_withoutClosedCaption_success() throws IOException {
HlsMasterPlaylist playlist = parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_WITHOUT_CC);
assertThat(playlist.muxedCaptionFormats).isEmpty();
}
@Test
public void parseMasterPlaylist_withAudio_codecPropagated() throws IOException {
HlsMasterPlaylist playlist = parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_WITH_AUDIO_MEDIA_TAG);
Format firstAudioFormat = playlist.audios.get(0).format;
assertThat(firstAudioFormat.codecs).isEqualTo("mp4a.40.2");
assertThat(firstAudioFormat.sampleMimeType).isEqualTo(MimeTypes.AUDIO_AAC);
Format secondAudioFormat = playlist.audios.get(1).format;
assertThat(secondAudioFormat.codecs).isEqualTo("ac-3");
assertThat(secondAudioFormat.sampleMimeType).isEqualTo(MimeTypes.AUDIO_AC3);
}
@Test
public void parseMasterPlaylist_withAudio_audioIdPropagated() throws IOException {
HlsMasterPlaylist playlist = parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_WITH_AUDIO_MEDIA_TAG);
Format firstAudioFormat = playlist.audios.get(0).format;
assertThat(firstAudioFormat.id).isEqualTo("aud1:English");
Format secondAudioFormat = playlist.audios.get(1).format;
assertThat(secondAudioFormat.id).isEqualTo("aud2:English");
}
@Test
public void parseMasterPlaylist_withCc_cCIdPropagated() throws IOException {
HlsMasterPlaylist playlist = parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_WITH_CC);
Format firstTextFormat = playlist.muxedCaptionFormats.get(0);
assertThat(firstTextFormat.id).isEqualTo("cc1:Eng");
}
@Test
public void parseMasterPlaylist_withSubtitles_subtitlesIdPropagated() throws IOException {
HlsMasterPlaylist playlist = parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_WITH_SUBTITLES);
Format firstTextFormat = playlist.subtitles.get(0).format;
assertThat(firstTextFormat.id).isEqualTo("sub1:Eng");
assertThat(firstTextFormat.sampleMimeType).isEqualTo(MimeTypes.TEXT_VTT);
}
@Test
public void parseMasterPlaylist_withIndependentSegments_hasNoIndenpendentSegments()
throws IOException {
HlsMasterPlaylist playlistWithIndependentSegments =
parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_WITH_INDEPENDENT_SEGMENTS);
assertThat(playlistWithIndependentSegments.hasIndependentSegments).isTrue();
HlsMasterPlaylist playlistWithoutIndependentSegments =
parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_SIMPLE);
assertThat(playlistWithoutIndependentSegments.hasIndependentSegments).isFalse();
}
@Test
public void parseMasterPlaylist_withVariableSubstitution_success() throws IOException {
HlsMasterPlaylist playlistWithSubstitutions =
parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_WITH_VARIABLE_SUBSTITUTION);
HlsMasterPlaylist.Variant variant = playlistWithSubstitutions.variants.get(0);
assertThat(variant.format.codecs).isEqualTo("mp4a.40.5");
assertThat(variant.url)
.isEqualTo(Uri.parse("http://example.com/This/{$nested}/reference/shouldnt/work"));
}
@Test
public void parseMasterPlaylist_withTtmlSubtitle() throws IOException {
HlsMasterPlaylist playlistWithTtmlSubtitle =
parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_WITH_TTML_SUBTITLE);
HlsMasterPlaylist.Variant variant = playlistWithTtmlSubtitle.variants.get(0);
Format firstTextFormat = playlistWithTtmlSubtitle.subtitles.get(0).format;
assertThat(firstTextFormat.id).isEqualTo("sub1:English");
assertThat(firstTextFormat.containerMimeType).isEqualTo(MimeTypes.APPLICATION_M3U8);
assertThat(firstTextFormat.sampleMimeType).isEqualTo(MimeTypes.APPLICATION_TTML);
assertThat(variant.format.codecs).isEqualTo("stpp.ttml.im1t,mp4a.40.2,avc1.66.30");
}
@Test
public void parseMasterPlaylist_withMatchingStreamInfUrls_success() throws IOException {
HlsMasterPlaylist playlist =
parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_WITH_MATCHING_STREAM_INF_URLS);
assertThat(playlist.variants).hasSize(4);
assertThat(playlist.variants.get(0).format.metadata)
.isEqualTo(
createExtXStreamInfMetadata(
createVariantInfo(/* peakBitrate= */ 2227464, /* audioGroupId= */ "aud1"),
createVariantInfo(/* peakBitrate= */ 2448841, /* audioGroupId= */ "aud2"),
createVariantInfo(/* peakBitrate= */ 2256841, /* audioGroupId= */ "aud3")));
assertThat(playlist.variants.get(1).format.metadata)
.isEqualTo(
createExtXStreamInfMetadata(
createVariantInfo(/* peakBitrate= */ 6453202, /* audioGroupId= */ "aud1"),
createVariantInfo(/* peakBitrate= */ 6482579, /* audioGroupId= */ "aud3")));
assertThat(playlist.variants.get(2).format.metadata)
.isEqualTo(
createExtXStreamInfMetadata(
createVariantInfo(/* peakBitrate= */ 5054232, /* audioGroupId= */ "aud1"),
createVariantInfo(/* peakBitrate= */ 5275609, /* audioGroupId= */ "aud2")));
assertThat(playlist.variants.get(3).format.metadata)
.isEqualTo(
createExtXStreamInfMetadata(
createVariantInfo(/* peakBitrate= */ 8399417, /* audioGroupId= */ "aud2"),
createVariantInfo(/* peakBitrate= */ 8207417, /* audioGroupId= */ "aud3")));
assertThat(playlist.audios).hasSize(3);
assertThat(playlist.audios.get(0).format.metadata)
.isEqualTo(createExtXMediaMetadata(/* groupId= */ "aud1", /* name= */ "English"));
assertThat(playlist.audios.get(1).format.metadata)
.isEqualTo(createExtXMediaMetadata(/* groupId= */ "aud2", /* name= */ "English"));
assertThat(playlist.audios.get(2).format.metadata)
.isEqualTo(createExtXMediaMetadata(/* groupId= */ "aud3", /* name= */ "English"));
}
@Test
public void testIFrameVariant() throws IOException {
HlsMasterPlaylist playlist = parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_WITH_IFRAME_VARIANTS);
assertThat(playlist.variants).hasSize(5);
for (int i = 0; i < 4; i++) {
assertThat(playlist.variants.get(i).format.roleFlags).isEqualTo(0);
}
Variant iFramesOnlyVariant = playlist.variants.get(4);
assertThat(iFramesOnlyVariant.format.bitrate).isEqualTo(1313400);
assertThat(iFramesOnlyVariant.format.roleFlags & C.ROLE_FLAG_TRICK_PLAY)
.isEqualTo(C.ROLE_FLAG_TRICK_PLAY);
}
private static Metadata createExtXStreamInfMetadata(HlsTrackMetadataEntry.VariantInfo... infos) {
return new Metadata(
new HlsTrackMetadataEntry(/* groupId= */ null, /* name= */ null, Arrays.asList(infos)));
}
private static Metadata createExtXMediaMetadata(String groupId, String name) {
return new Metadata(new HlsTrackMetadataEntry(groupId, name, Collections.emptyList()));
}
private static HlsTrackMetadataEntry.VariantInfo createVariantInfo(
int peakBitrate, String audioGroupId) {
return new HlsTrackMetadataEntry.VariantInfo(
/* averageBitrate= */ Format.NO_VALUE,
/* peakBitrate= */ peakBitrate,
/* videoGroupId= */ null,
audioGroupId,
/* subtitleGroupId= */ "sub1",
/* captionGroupId= */ "cc1");
}
private static HlsMasterPlaylist parseMasterPlaylist(String uri, String playlistString)
throws IOException {
Uri playlistUri = Uri.parse(uri);
ByteArrayInputStream inputStream =
new ByteArrayInputStream(playlistString.getBytes(Charset.forName(C.UTF8_NAME)));
return (HlsMasterPlaylist) new HlsPlaylistParser().parse(playlistUri, inputStream);
}
}