Commit e3a8429e by olly Committed by Toni

More faithfully parse content of HLS master playlists

- Split HlsUrl into Rendition and Variant
- Add Rendition/Variant specific information to the new types

Issue: #5596
PiperOrigin-RevId: 240419763
parent ae5e5f7e
...@@ -32,6 +32,7 @@ import com.google.android.exoplayer2.source.TrackGroup; ...@@ -32,6 +32,7 @@ import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist; import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist;
import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.HlsUrl; import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.HlsUrl;
import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.Rendition;
import com.google.android.exoplayer2.source.hls.playlist.HlsPlaylistTracker; import com.google.android.exoplayer2.source.hls.playlist.HlsPlaylistTracker;
import com.google.android.exoplayer2.trackselection.TrackSelection; import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.upstream.Allocator;
...@@ -442,8 +443,8 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper ...@@ -442,8 +443,8 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
: Collections.emptyMap(); : Collections.emptyMap();
boolean hasVariants = !masterPlaylist.variants.isEmpty(); boolean hasVariants = !masterPlaylist.variants.isEmpty();
List<HlsUrl> audioRenditions = masterPlaylist.audios; List<Rendition> audioRenditions = masterPlaylist.audios;
List<HlsUrl> subtitleRenditions = masterPlaylist.subtitles; List<Rendition> subtitleRenditions = masterPlaylist.subtitles;
pendingPrepareCount = 0; pendingPrepareCount = 0;
ArrayList<HlsSampleStreamWrapper> sampleStreamWrappers = new ArrayList<>(); ArrayList<HlsSampleStreamWrapper> sampleStreamWrappers = new ArrayList<>();
...@@ -644,7 +645,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper ...@@ -644,7 +645,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
private void buildAndPrepareAudioSampleStreamWrappers( private void buildAndPrepareAudioSampleStreamWrappers(
long positionUs, long positionUs,
List<HlsUrl> audioRenditions, List<Rendition> audioRenditions,
List<HlsSampleStreamWrapper> sampleStreamWrappers, List<HlsSampleStreamWrapper> sampleStreamWrappers,
List<int[]> manifestUrlsIndicesPerWrapper, List<int[]> manifestUrlsIndicesPerWrapper,
Map<String, DrmInitData> overridingDrmInitData) { Map<String, DrmInitData> overridingDrmInitData) {
......
...@@ -119,7 +119,8 @@ public final class HlsDownloader extends SegmentDownloader<HlsPlaylist> { ...@@ -119,7 +119,8 @@ public final class HlsDownloader extends SegmentDownloader<HlsPlaylist> {
return segments; return segments;
} }
private void addMediaPlaylistDataSpecs(String baseUri, List<HlsUrl> urls, List<DataSpec> out) { private void addMediaPlaylistDataSpecs(
String baseUri, List<? extends HlsUrl> urls, List<DataSpec> out) {
for (int i = 0; i < urls.size(); i++) { for (int i = 0; i < urls.size(); i++) {
Uri playlistUri = UriUtil.resolveToUri(baseUri, urls.get(i).url); Uri playlistUri = UriUtil.resolveToUri(baseUri, urls.get(i).url);
out.add(SegmentDownloader.getCompressibleDataSpec(playlistUri)); out.add(SegmentDownloader.getCompressibleDataSpec(playlistUri));
......
...@@ -24,6 +24,7 @@ import com.google.android.exoplayer2.ParserException; ...@@ -24,6 +24,7 @@ import com.google.android.exoplayer2.ParserException;
import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher; import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher;
import com.google.android.exoplayer2.source.hls.HlsDataSourceFactory; import com.google.android.exoplayer2.source.hls.HlsDataSourceFactory;
import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.HlsUrl; import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.HlsUrl;
import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.Variant;
import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist.Segment; import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist.Segment;
import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy; import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy;
...@@ -298,7 +299,7 @@ public final class DefaultHlsPlaylistTracker ...@@ -298,7 +299,7 @@ public final class DefaultHlsPlaylistTracker
// Internal methods. // Internal methods.
private boolean maybeSelectNewPrimaryUrl() { private boolean maybeSelectNewPrimaryUrl() {
List<HlsUrl> variants = masterPlaylist.variants; List<Variant> variants = masterPlaylist.variants;
int variantsSize = variants.size(); int variantsSize = variants.size();
long currentTimeMs = SystemClock.elapsedRealtime(); long currentTimeMs = SystemClock.elapsedRealtime();
for (int i = 0; i < variantsSize; i++) { for (int i = 0; i < variantsSize; i++) {
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer2.source.hls.playlist; package com.google.android.exoplayer2.source.hls.playlist;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.drm.DrmInitData; import com.google.android.exoplayer2.drm.DrmInitData;
import com.google.android.exoplayer2.offline.StreamKey; import com.google.android.exoplayer2.offline.StreamKey;
...@@ -45,10 +46,8 @@ public final class HlsMasterPlaylist extends HlsPlaylist { ...@@ -45,10 +46,8 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
public static final int GROUP_INDEX_AUDIO = 1; public static final int GROUP_INDEX_AUDIO = 1;
public static final int GROUP_INDEX_SUBTITLE = 2; public static final int GROUP_INDEX_SUBTITLE = 2;
/** /** Represents a url in an HLS master playlist. */
* Represents a url in an HLS master playlist. public abstract static class HlsUrl {
*/
public static final class HlsUrl {
/** /**
* The http url from which the media playlist can be obtained. * The http url from which the media playlist can be obtained.
...@@ -58,19 +57,61 @@ public final class HlsMasterPlaylist extends HlsPlaylist { ...@@ -58,19 +57,61 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
* Format information associated with the HLS url. * Format information associated with the HLS url.
*/ */
public final Format format; public final Format format;
/** /**
* Value of the NAME attribute as defined by the #EXT-X-MEDIA tag, or empty if the HLS url is * @param url See {@link #url}.
* not associated with any name. * @param format See {@link #format}.
*/ */
public final String name; public HlsUrl(String url, Format format) {
this.url = url;
this.format = format;
}
}
/** A variant in a master playlist. */
public static final class Variant extends HlsUrl {
/** The video rendition group referenced by this variant, or {@code null}. */
@Nullable public final String videoGroupId;
/** The audio rendition group referenced by this variant, or {@code null}. */
@Nullable public final String audioGroupId;
/** The subtitle rendition group referenced by this variant, or {@code null}. */
@Nullable public final String subtitleGroupId;
/** The caption rendition group referenced by this variant, or {@code null}. */
@Nullable public final String captionGroupId;
/**
* @param url See {@link #url}.
* @param format See {@link #format}.
* @param videoGroupId See {@link #videoGroupId}.
* @param audioGroupId See {@link #audioGroupId}.
* @param subtitleGroupId See {@link #subtitleGroupId}.
* @param captionGroupId See {@link #captionGroupId}.
*/
public Variant(
String url,
Format format,
@Nullable String videoGroupId,
@Nullable String audioGroupId,
@Nullable String subtitleGroupId,
@Nullable String captionGroupId) {
super(url, format);
this.videoGroupId = videoGroupId;
this.audioGroupId = audioGroupId;
this.subtitleGroupId = subtitleGroupId;
this.captionGroupId = captionGroupId;
}
/** /**
* Creates an HLS url from a given http url. * Creates a variant for a given media playlist url.
* *
* @param url The url. * @param url The media playlist url.
* @return An HLS url. * @return The variant instance.
*/ */
public static HlsUrl createMediaPlaylistHlsUrl(String url) { public static Variant createMediaPlaylistVariantUrl(String url) {
Format format = Format format =
Format.createContainerFormat( Format.createContainerFormat(
"0", "0",
...@@ -82,34 +123,45 @@ public final class HlsMasterPlaylist extends HlsPlaylist { ...@@ -82,34 +123,45 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
/* selectionFlags= */ 0, /* selectionFlags= */ 0,
/* roleFlags= */ 0, /* roleFlags= */ 0,
/* language= */ null); /* language= */ null);
return new HlsUrl(url, format, /* name= */ ""); return new Variant(
url,
format,
/* videoGroupId= */ null,
/* audioGroupId= */ null,
/* subtitleGroupId= */ null,
/* captionGroupId= */ null);
} }
}
/** A rendition in a master playlist. */
public static final class Rendition extends HlsUrl {
/** The group to which this rendition belongs. */
public final String groupId;
/** The name of the rendition. */
public final String name;
/** /**
* @param url See {@link #url}. * @param url See {@link #url}.
* @param format See {@link #format}. * @param format See {@link #format}.
* @param groupId See {@link #groupId}.
* @param name See {@link #name}. * @param name See {@link #name}.
*/ */
public HlsUrl(String url, Format format, String name) { public Rendition(String url, Format format, String groupId, String name) {
this.url = url; super(url, format);
this.format = format; this.groupId = groupId;
this.name = name; this.name = name;
} }
} }
/** /** The list of variants declared by the playlist. */
* The list of variants declared by the playlist. public final List<Variant> variants;
*/ /** The list of demuxed audios declared by the playlist. */
public final List<HlsUrl> variants; public final List<Rendition> audios;
/** /** The list of subtitles declared by the playlist. */
* The list of demuxed audios declared by the playlist. public final List<Rendition> subtitles;
*/
public final List<HlsUrl> audios;
/**
* The list of subtitles declared by the playlist.
*/
public final List<HlsUrl> subtitles;
/** /**
* The format of the audio muxed in the variants. May be null if the playlist does not declare any * The format of the audio muxed in the variants. May be null if the playlist does not declare any
...@@ -142,9 +194,9 @@ public final class HlsMasterPlaylist extends HlsPlaylist { ...@@ -142,9 +194,9 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
public HlsMasterPlaylist( public HlsMasterPlaylist(
String baseUri, String baseUri,
List<String> tags, List<String> tags,
List<HlsUrl> variants, List<Variant> variants,
List<HlsUrl> audios, List<Rendition> audios,
List<HlsUrl> subtitles, List<Rendition> subtitles,
Format muxedAudioFormat, Format muxedAudioFormat,
List<Format> muxedCaptionFormats, List<Format> muxedCaptionFormats,
boolean hasIndependentSegments, boolean hasIndependentSegments,
...@@ -183,14 +235,14 @@ public final class HlsMasterPlaylist extends HlsPlaylist { ...@@ -183,14 +235,14 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
* @return A master playlist with a single variant for the provided url. * @return A master playlist with a single variant for the provided url.
*/ */
public static HlsMasterPlaylist createSingleVariantMasterPlaylist(String variantUrl) { public static HlsMasterPlaylist createSingleVariantMasterPlaylist(String variantUrl) {
List<HlsUrl> variant = Collections.singletonList(HlsUrl.createMediaPlaylistHlsUrl(variantUrl)); List<Variant> variant =
List<HlsUrl> emptyList = Collections.emptyList(); Collections.singletonList(Variant.createMediaPlaylistVariantUrl(variantUrl));
return new HlsMasterPlaylist( return new HlsMasterPlaylist(
null, null,
Collections.emptyList(), Collections.emptyList(),
variant, variant,
emptyList, Collections.emptyList(),
emptyList, Collections.emptyList(),
/* muxedAudioFormat= */ null, /* muxedAudioFormat= */ null,
/* muxedCaptionFormats= */ null, /* muxedCaptionFormats= */ null,
/* hasIndependentSegments= */ false, /* hasIndependentSegments= */ false,
...@@ -198,11 +250,11 @@ public final class HlsMasterPlaylist extends HlsPlaylist { ...@@ -198,11 +250,11 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
/* sessionKeyDrmInitData= */ Collections.emptyList()); /* sessionKeyDrmInitData= */ Collections.emptyList());
} }
private static List<HlsUrl> copyRenditionsList( private static <T extends HlsUrl> List<T> copyRenditionsList(
List<HlsUrl> renditions, int groupIndex, List<StreamKey> streamKeys) { List<T> renditions, int groupIndex, List<StreamKey> streamKeys) {
List<HlsUrl> copiedRenditions = new ArrayList<>(streamKeys.size()); List<T> copiedRenditions = new ArrayList<>(streamKeys.size());
for (int i = 0; i < renditions.size(); i++) { for (int i = 0; i < renditions.size(); i++) {
HlsUrl rendition = renditions.get(i); T rendition = renditions.get(i);
for (int j = 0; j < streamKeys.size(); j++) { for (int j = 0; j < streamKeys.size(); j++) {
StreamKey streamKey = streamKeys.get(j); StreamKey streamKey = streamKeys.get(j);
if (streamKey.groupIndex == groupIndex && streamKey.trackIndex == i) { if (streamKey.groupIndex == groupIndex && streamKey.trackIndex == i) {
......
...@@ -26,6 +26,8 @@ import com.google.android.exoplayer2.drm.DrmInitData; ...@@ -26,6 +26,8 @@ import com.google.android.exoplayer2.drm.DrmInitData;
import com.google.android.exoplayer2.drm.DrmInitData.SchemeData; import com.google.android.exoplayer2.drm.DrmInitData.SchemeData;
import com.google.android.exoplayer2.extractor.mp4.PsshAtomUtil; import com.google.android.exoplayer2.extractor.mp4.PsshAtomUtil;
import com.google.android.exoplayer2.source.UnrecognizedInputFormatException; import com.google.android.exoplayer2.source.UnrecognizedInputFormatException;
import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.Rendition;
import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.Variant;
import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist.Segment; import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist.Segment;
import com.google.android.exoplayer2.upstream.ParsingLoadable; import com.google.android.exoplayer2.upstream.ParsingLoadable;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
...@@ -100,7 +102,10 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli ...@@ -100,7 +102,10 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
private static final Pattern REGEX_AVERAGE_BANDWIDTH = private static final Pattern REGEX_AVERAGE_BANDWIDTH =
Pattern.compile("AVERAGE-BANDWIDTH=(\\d+)\\b"); Pattern.compile("AVERAGE-BANDWIDTH=(\\d+)\\b");
private static final Pattern REGEX_VIDEO = Pattern.compile("VIDEO=\"(.+?)\"");
private static final Pattern REGEX_AUDIO = Pattern.compile("AUDIO=\"(.+?)\""); private static final Pattern REGEX_AUDIO = Pattern.compile("AUDIO=\"(.+?)\"");
private static final Pattern REGEX_SUBTITLES = Pattern.compile("SUBTITLES=\"(.+?)\"");
private static final Pattern REGEX_CLOSED_CAPTIONS = Pattern.compile("CLOSED-CAPTIONS=\"(.+?)\"");
private static final Pattern REGEX_BANDWIDTH = Pattern.compile("[^-]BANDWIDTH=(\\d+)\\b"); private static final Pattern REGEX_BANDWIDTH = Pattern.compile("[^-]BANDWIDTH=(\\d+)\\b");
private static final Pattern REGEX_CHANNELS = Pattern.compile("CHANNELS=\"(.+?)\""); private static final Pattern REGEX_CHANNELS = Pattern.compile("CHANNELS=\"(.+?)\"");
private static final Pattern REGEX_CODECS = Pattern.compile("CODECS=\"(.+?)\""); private static final Pattern REGEX_CODECS = Pattern.compile("CODECS=\"(.+?)\"");
...@@ -249,9 +254,9 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli ...@@ -249,9 +254,9 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
HashSet<String> variantUrls = new HashSet<>(); HashSet<String> variantUrls = new HashSet<>();
HashMap<String, String> audioGroupIdToCodecs = new HashMap<>(); HashMap<String, String> audioGroupIdToCodecs = new HashMap<>();
HashMap<String, String> variableDefinitions = new HashMap<>(); HashMap<String, String> variableDefinitions = new HashMap<>();
ArrayList<HlsMasterPlaylist.HlsUrl> variants = new ArrayList<>(); ArrayList<Variant> variants = new ArrayList<>();
ArrayList<HlsMasterPlaylist.HlsUrl> audios = new ArrayList<>(); ArrayList<Rendition> audios = new ArrayList<>();
ArrayList<HlsMasterPlaylist.HlsUrl> subtitles = new ArrayList<>(); ArrayList<Rendition> subtitles = new ArrayList<>();
ArrayList<String> mediaTags = new ArrayList<>(); ArrayList<String> mediaTags = new ArrayList<>();
ArrayList<DrmInitData> sessionKeyDrmInitData = new ArrayList<>(); ArrayList<DrmInitData> sessionKeyDrmInitData = new ArrayList<>();
ArrayList<String> tags = new ArrayList<>(); ArrayList<String> tags = new ArrayList<>();
...@@ -321,7 +326,12 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli ...@@ -321,7 +326,12 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
if (frameRateString != null) { if (frameRateString != null) {
frameRate = Float.parseFloat(frameRateString); frameRate = Float.parseFloat(frameRateString);
} }
String videoGroupId = parseOptionalStringAttr(line, REGEX_VIDEO, variableDefinitions);
String audioGroupId = parseOptionalStringAttr(line, REGEX_AUDIO, variableDefinitions); String audioGroupId = parseOptionalStringAttr(line, REGEX_AUDIO, variableDefinitions);
String subtitlesGroupId =
parseOptionalStringAttr(line, REGEX_SUBTITLES, variableDefinitions);
String closedCaptionsGroupId =
parseOptionalStringAttr(line, REGEX_CLOSED_CAPTIONS, variableDefinitions);
if (audioGroupId != null && codecs != null) { if (audioGroupId != null && codecs != null) {
audioGroupIdToCodecs.put(audioGroupId, Util.getCodecsOfType(codecs, C.TRACK_TYPE_AUDIO)); audioGroupIdToCodecs.put(audioGroupId, Util.getCodecsOfType(codecs, C.TRACK_TYPE_AUDIO));
} }
...@@ -343,20 +353,27 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli ...@@ -343,20 +353,27 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
/* initializationData= */ null, /* initializationData= */ null,
/* selectionFlags= */ 0, /* selectionFlags= */ 0,
/* roleFlags= */ 0); /* roleFlags= */ 0);
variants.add(new HlsMasterPlaylist.HlsUrl(line, format, /* name= */ "")); variants.add(
new Variant(
line,
format,
videoGroupId,
audioGroupId,
subtitlesGroupId,
closedCaptionsGroupId));
} }
} }
} }
for (int i = 0; i < mediaTags.size(); i++) { for (int i = 0; i < mediaTags.size(); i++) {
line = mediaTags.get(i); line = mediaTags.get(i);
@C.SelectionFlags int selectionFlags = parseSelectionFlags(line); String groupId = parseStringAttr(line, REGEX_GROUP_ID, variableDefinitions);
@C.RoleFlags int roleFlags = parseRoleFlags(line, variableDefinitions);
String uri = parseOptionalStringAttr(line, REGEX_URI, variableDefinitions);
String name = parseStringAttr(line, REGEX_NAME, variableDefinitions); String name = parseStringAttr(line, REGEX_NAME, variableDefinitions);
String uri = parseOptionalStringAttr(line, REGEX_URI, variableDefinitions);
String language = parseOptionalStringAttr(line, REGEX_LANGUAGE, variableDefinitions); String language = parseOptionalStringAttr(line, REGEX_LANGUAGE, variableDefinitions);
String groupId = parseOptionalStringAttr(line, REGEX_GROUP_ID, variableDefinitions); @C.SelectionFlags int selectionFlags = parseSelectionFlags(line);
String id = groupId + ":" + name; @C.RoleFlags int roleFlags = parseRoleFlags(line, variableDefinitions);
String formatId = groupId + ":" + name;
Format format; Format format;
switch (parseStringAttr(line, REGEX_TYPE, variableDefinitions)) { switch (parseStringAttr(line, REGEX_TYPE, variableDefinitions)) {
case TYPE_AUDIO: case TYPE_AUDIO:
...@@ -365,7 +382,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli ...@@ -365,7 +382,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
String sampleMimeType = codecs != null ? MimeTypes.getMediaMimeType(codecs) : null; String sampleMimeType = codecs != null ? MimeTypes.getMediaMimeType(codecs) : null;
format = format =
Format.createAudioContainerFormat( Format.createAudioContainerFormat(
/* id= */ id, /* id= */ formatId,
/* label= */ name, /* label= */ name,
/* containerMimeType= */ MimeTypes.APPLICATION_M3U8, /* containerMimeType= */ MimeTypes.APPLICATION_M3U8,
sampleMimeType, sampleMimeType,
...@@ -380,13 +397,13 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli ...@@ -380,13 +397,13 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
if (uri == null) { if (uri == null) {
muxedAudioFormat = format; muxedAudioFormat = format;
} else { } else {
audios.add(new HlsMasterPlaylist.HlsUrl(uri, format, name)); audios.add(new Rendition(uri, format, groupId, name));
} }
break; break;
case TYPE_SUBTITLES: case TYPE_SUBTITLES:
format = format =
Format.createTextContainerFormat( Format.createTextContainerFormat(
/* id= */ id, /* id= */ formatId,
/* label= */ name, /* label= */ name,
/* containerMimeType= */ MimeTypes.APPLICATION_M3U8, /* containerMimeType= */ MimeTypes.APPLICATION_M3U8,
/* sampleMimeType= */ MimeTypes.TEXT_VTT, /* sampleMimeType= */ MimeTypes.TEXT_VTT,
...@@ -395,7 +412,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli ...@@ -395,7 +412,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
selectionFlags, selectionFlags,
roleFlags, roleFlags,
language); language);
subtitles.add(new HlsMasterPlaylist.HlsUrl(uri, format, name)); subtitles.add(new Rendition(uri, format, groupId, name));
break; break;
case TYPE_CLOSED_CAPTIONS: case TYPE_CLOSED_CAPTIONS:
String instreamId = parseStringAttr(line, REGEX_INSTREAM_ID, variableDefinitions); String instreamId = parseStringAttr(line, REGEX_INSTREAM_ID, variableDefinitions);
...@@ -413,7 +430,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli ...@@ -413,7 +430,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
} }
muxedCaptionFormats.add( muxedCaptionFormats.add(
Format.createTextContainerFormat( Format.createTextContainerFormat(
/* id= */ id, /* id= */ formatId,
/* label= */ name, /* label= */ name,
/* containerMimeType= */ null, /* containerMimeType= */ null,
/* sampleMimeType= */ mimeType, /* sampleMimeType= */ mimeType,
......
...@@ -25,7 +25,8 @@ import com.google.android.exoplayer2.source.CompositeSequenceableLoaderFactory; ...@@ -25,7 +25,8 @@ import com.google.android.exoplayer2.source.CompositeSequenceableLoaderFactory;
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId; import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher; import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher;
import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist; import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist;
import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.HlsUrl; import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.Rendition;
import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.Variant;
import com.google.android.exoplayer2.source.hls.playlist.HlsPlaylist; import com.google.android.exoplayer2.source.hls.playlist.HlsPlaylist;
import com.google.android.exoplayer2.source.hls.playlist.HlsPlaylistTracker; import com.google.android.exoplayer2.source.hls.playlist.HlsPlaylistTracker;
import com.google.android.exoplayer2.testutil.MediaPeriodAsserts; import com.google.android.exoplayer2.testutil.MediaPeriodAsserts;
...@@ -53,19 +54,19 @@ public final class HlsMediaPeriodTest { ...@@ -53,19 +54,19 @@ public final class HlsMediaPeriodTest {
HlsMasterPlaylist testMasterPlaylist = HlsMasterPlaylist testMasterPlaylist =
createMasterPlaylist( createMasterPlaylist(
/* variants= */ Arrays.asList( /* variants= */ Arrays.asList(
createAudioOnlyVariantHlsUrl(/* bitrate= */ 10000), createAudioOnlyVariant(/* bitrate= */ 10000),
createMuxedVideoAudioVariantHlsUrl(/* bitrate= */ 200000), createMuxedVideoAudioVariant(/* bitrate= */ 200000),
createAudioOnlyVariantHlsUrl(/* bitrate= */ 300000), createAudioOnlyVariant(/* bitrate= */ 300000),
createMuxedVideoAudioVariantHlsUrl(/* bitrate= */ 400000), createMuxedVideoAudioVariant(/* bitrate= */ 400000),
createMuxedVideoAudioVariantHlsUrl(/* bitrate= */ 600000)), createMuxedVideoAudioVariant(/* bitrate= */ 600000)),
/* audios= */ Arrays.asList( /* audios= */ Arrays.asList(
createAudioHlsUrl(/* language= */ "spa"), createAudioRendition(/* language= */ "spa"),
createAudioHlsUrl(/* language= */ "ger"), createAudioRendition(/* language= */ "ger"),
createAudioHlsUrl(/* language= */ "tur")), createAudioRendition(/* language= */ "tur")),
/* subtitles= */ Arrays.asList( /* subtitles= */ Arrays.asList(
createSubtitleHlsUrl(/* language= */ "spa"), createSubtitleRendition(/* language= */ "spa"),
createSubtitleHlsUrl(/* language= */ "ger"), createSubtitleRendition(/* language= */ "ger"),
createSubtitleHlsUrl(/* language= */ "tur")), createSubtitleRendition(/* language= */ "tur")),
/* muxedAudioFormat= */ createAudioFormat("eng"), /* muxedAudioFormat= */ createAudioFormat("eng"),
/* muxedCaptionFormats= */ Arrays.asList( /* muxedCaptionFormats= */ Arrays.asList(
createSubtitleFormat("eng"), createSubtitleFormat("gsw"))); createSubtitleFormat("eng"), createSubtitleFormat("gsw")));
...@@ -97,9 +98,9 @@ public final class HlsMediaPeriodTest { ...@@ -97,9 +98,9 @@ public final class HlsMediaPeriodTest {
} }
private static HlsMasterPlaylist createMasterPlaylist( private static HlsMasterPlaylist createMasterPlaylist(
List<HlsUrl> variants, List<Variant> variants,
List<HlsUrl> audios, List<Rendition> audios,
List<HlsUrl> subtitles, List<Rendition> subtitles,
Format muxedAudioFormat, Format muxedAudioFormat,
List<Format> muxedCaptionFormats) { List<Format> muxedCaptionFormats) {
return new HlsMasterPlaylist( return new HlsMasterPlaylist(
...@@ -115,8 +116,8 @@ public final class HlsMediaPeriodTest { ...@@ -115,8 +116,8 @@ public final class HlsMediaPeriodTest {
/* sessionKeyDrmInitData= */ Collections.emptyList()); /* sessionKeyDrmInitData= */ Collections.emptyList());
} }
private static HlsUrl createMuxedVideoAudioVariantHlsUrl(int bitrate) { private static Variant createMuxedVideoAudioVariant(int bitrate) {
return new HlsUrl( return createVariant(
"http://url", "http://url",
Format.createVideoContainerFormat( Format.createVideoContainerFormat(
/* id= */ null, /* id= */ null,
...@@ -130,12 +131,11 @@ public final class HlsMediaPeriodTest { ...@@ -130,12 +131,11 @@ public final class HlsMediaPeriodTest {
/* frameRate= */ Format.NO_VALUE, /* frameRate= */ Format.NO_VALUE,
/* initializationData= */ null, /* initializationData= */ null,
/* selectionFlags= */ 0, /* selectionFlags= */ 0,
/* roleFlags= */ 0), /* roleFlags= */ 0));
/* name= */ "");
} }
private static HlsUrl createAudioOnlyVariantHlsUrl(int bitrate) { private static Variant createAudioOnlyVariant(int bitrate) {
return new HlsUrl( return createVariant(
"http://url", "http://url",
Format.createVideoContainerFormat( Format.createVideoContainerFormat(
/* id= */ null, /* id= */ null,
...@@ -149,16 +149,23 @@ public final class HlsMediaPeriodTest { ...@@ -149,16 +149,23 @@ public final class HlsMediaPeriodTest {
/* frameRate= */ Format.NO_VALUE, /* frameRate= */ Format.NO_VALUE,
/* initializationData= */ null, /* initializationData= */ null,
/* selectionFlags= */ 0, /* selectionFlags= */ 0,
/* roleFlags= */ 0), /* roleFlags= */ 0));
/* name= */ "");
} }
private static HlsUrl createAudioHlsUrl(String language) { private static Rendition createAudioRendition(String language) {
return new HlsUrl("http://url", createAudioFormat(language), /* name= */ ""); return createRendition("http://url", createAudioFormat(language), "", "");
} }
private static HlsUrl createSubtitleHlsUrl(String language) { private static Rendition createSubtitleRendition(String language) {
return new HlsUrl("http://url", createSubtitleFormat(language), /* name= */ ""); return createRendition("http://url", createSubtitleFormat(language), "", "");
}
private static Variant createVariant(String url, Format format) {
return new Variant(url, format, null, null, null, null);
}
private static Rendition createRendition(String url, Format format, String groupId, String name) {
return new Rendition(url, format, groupId, name);
} }
private static Format createAudioFormat(String language) { private static Format createAudioFormat(String language) {
......
...@@ -95,7 +95,7 @@ public class HlsMasterPlaylistParserTest { ...@@ -95,7 +95,7 @@ public class HlsMasterPlaylistParserTest {
private static final String PLAYLIST_WITHOUT_CC = private static final String PLAYLIST_WITHOUT_CC =
" #EXTM3U \n" " #EXTM3U \n"
+ "#EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS," + "#EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS,GROUP-ID=\"cc1\","
+ "LANGUAGE=\"es\",NAME=\"Eng\",INSTREAM-ID=\"SERVICE4\"\n" + "LANGUAGE=\"es\",NAME=\"Eng\",INSTREAM-ID=\"SERVICE4\"\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=1280000," + "#EXT-X-STREAM-INF:BANDWIDTH=1280000,"
+ "CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128," + "CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128,"
...@@ -150,7 +150,7 @@ public class HlsMasterPlaylistParserTest { ...@@ -150,7 +150,7 @@ public class HlsMasterPlaylistParserTest {
public void testParseMasterPlaylist() throws IOException { public void testParseMasterPlaylist() throws IOException {
HlsMasterPlaylist masterPlaylist = parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_SIMPLE); HlsMasterPlaylist masterPlaylist = parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_SIMPLE);
List<HlsMasterPlaylist.HlsUrl> variants = masterPlaylist.variants; List<HlsMasterPlaylist.Variant> variants = masterPlaylist.variants;
assertThat(variants).hasSize(5); assertThat(variants).hasSize(5);
assertThat(masterPlaylist.muxedCaptionFormats).isNull(); assertThat(masterPlaylist.muxedCaptionFormats).isNull();
...@@ -191,7 +191,7 @@ public class HlsMasterPlaylistParserTest { ...@@ -191,7 +191,7 @@ public class HlsMasterPlaylistParserTest {
HlsMasterPlaylist masterPlaylist = HlsMasterPlaylist masterPlaylist =
parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_WITH_AVG_BANDWIDTH); parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_WITH_AVG_BANDWIDTH);
List<HlsMasterPlaylist.HlsUrl> variants = masterPlaylist.variants; List<HlsMasterPlaylist.Variant> variants = masterPlaylist.variants;
assertThat(variants.get(0).format.bitrate).isEqualTo(1280000); assertThat(variants.get(0).format.bitrate).isEqualTo(1280000);
assertThat(variants.get(1).format.bitrate).isEqualTo(1270000); assertThat(variants.get(1).format.bitrate).isEqualTo(1270000);
...@@ -221,7 +221,7 @@ public class HlsMasterPlaylistParserTest { ...@@ -221,7 +221,7 @@ public class HlsMasterPlaylistParserTest {
public void testPlaylistWithChannelsAttribute() throws IOException { public void testPlaylistWithChannelsAttribute() throws IOException {
HlsMasterPlaylist playlist = HlsMasterPlaylist playlist =
parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_WITH_CHANNELS_ATTRIBUTE); parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_WITH_CHANNELS_ATTRIBUTE);
List<HlsMasterPlaylist.HlsUrl> audios = playlist.audios; List<HlsMasterPlaylist.Rendition> audios = playlist.audios;
assertThat(audios).hasSize(3); assertThat(audios).hasSize(3);
assertThat(audios.get(0).format.channelCount).isEqualTo(6); assertThat(audios.get(0).format.channelCount).isEqualTo(6);
assertThat(audios.get(1).format.channelCount).isEqualTo(2); assertThat(audios.get(1).format.channelCount).isEqualTo(2);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment