use of com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.Variant in project Telegram-FOSS by Telegram-FOSS-Team.
the class HlsMediaPeriod method buildAndPrepareMainSampleStreamWrapper.
/**
* This method creates and starts preparation of the main {@link HlsSampleStreamWrapper}.
*
* <p>The main sample stream wrapper is the first element of {@link #sampleStreamWrappers}. It
* provides {@link SampleStream}s for the variant urls in the master playlist. It may be adaptive
* and may contain multiple muxed tracks.
*
* <p>If chunkless preparation is allowed, the media period will try preparation without segment
* downloads. This is only possible if variants contain the CODECS attribute. If not, traditional
* preparation with segment downloads will take place. The following points apply to chunkless
* preparation:
*
* <ul>
* <li>A muxed audio track will be exposed if the codecs list contain an audio entry and the
* master playlist either contains an EXT-X-MEDIA tag without the URI attribute or does not
* contain any EXT-X-MEDIA tag.
* <li>Closed captions will only be exposed if they are declared by the master playlist.
* <li>An ID3 track is exposed preemptively, in case the segments contain an ID3 track.
* </ul>
*
* @param masterPlaylist The HLS master playlist.
* @param positionUs If preparation requires any chunk downloads, the position in microseconds at
* which downloading should start. Ignored otherwise.
* @param sampleStreamWrappers List to which the built main sample stream wrapper should be added.
* @param manifestUrlIndicesPerWrapper List to which the selected variant indices should be added.
* @param overridingDrmInitData Overriding {@link DrmInitData}, keyed by protection scheme type
* (i.e. {@link DrmInitData#schemeType}).
*/
private void buildAndPrepareMainSampleStreamWrapper(HlsMasterPlaylist masterPlaylist, long positionUs, List<HlsSampleStreamWrapper> sampleStreamWrappers, List<int[]> manifestUrlIndicesPerWrapper, Map<String, DrmInitData> overridingDrmInitData) {
int[] variantTypes = new int[masterPlaylist.variants.size()];
int videoVariantCount = 0;
int audioVariantCount = 0;
for (int i = 0; i < masterPlaylist.variants.size(); i++) {
Variant variant = masterPlaylist.variants.get(i);
Format format = variant.format;
if (format.height > 0 || Util.getCodecsOfType(format.codecs, C.TRACK_TYPE_VIDEO) != null) {
variantTypes[i] = C.TRACK_TYPE_VIDEO;
videoVariantCount++;
} else if (Util.getCodecsOfType(format.codecs, C.TRACK_TYPE_AUDIO) != null) {
variantTypes[i] = C.TRACK_TYPE_AUDIO;
audioVariantCount++;
} else {
variantTypes[i] = C.TRACK_TYPE_UNKNOWN;
}
}
boolean useVideoVariantsOnly = false;
boolean useNonAudioVariantsOnly = false;
int selectedVariantsCount = variantTypes.length;
if (videoVariantCount > 0) {
// We've identified some variants as definitely containing video. Assume variants within the
// master playlist are marked consistently, and hence that we have the full set. Filter out
// any other variants, which are likely to be audio only.
useVideoVariantsOnly = true;
selectedVariantsCount = videoVariantCount;
} else if (audioVariantCount < variantTypes.length) {
// We've identified some variants, but not all, as being audio only. Filter them out to leave
// the remaining variants, which are likely to contain video.
useNonAudioVariantsOnly = true;
selectedVariantsCount = variantTypes.length - audioVariantCount;
}
Uri[] selectedPlaylistUrls = new Uri[selectedVariantsCount];
Format[] selectedPlaylistFormats = new Format[selectedVariantsCount];
int[] selectedVariantIndices = new int[selectedVariantsCount];
int outIndex = 0;
for (int i = 0; i < masterPlaylist.variants.size(); i++) {
if ((!useVideoVariantsOnly || variantTypes[i] == C.TRACK_TYPE_VIDEO) && (!useNonAudioVariantsOnly || variantTypes[i] != C.TRACK_TYPE_AUDIO)) {
Variant variant = masterPlaylist.variants.get(i);
selectedPlaylistUrls[outIndex] = variant.url;
selectedPlaylistFormats[outIndex] = variant.format;
selectedVariantIndices[outIndex++] = i;
}
}
String codecs = selectedPlaylistFormats[0].codecs;
HlsSampleStreamWrapper sampleStreamWrapper = buildSampleStreamWrapper(C.TRACK_TYPE_DEFAULT, selectedPlaylistUrls, selectedPlaylistFormats, masterPlaylist.muxedAudioFormat, masterPlaylist.muxedCaptionFormats, overridingDrmInitData, positionUs);
sampleStreamWrappers.add(sampleStreamWrapper);
manifestUrlIndicesPerWrapper.add(selectedVariantIndices);
if (allowChunklessPreparation && codecs != null) {
boolean variantsContainVideoCodecs = Util.getCodecsOfType(codecs, C.TRACK_TYPE_VIDEO) != null;
boolean variantsContainAudioCodecs = Util.getCodecsOfType(codecs, C.TRACK_TYPE_AUDIO) != null;
List<TrackGroup> muxedTrackGroups = new ArrayList<>();
if (variantsContainVideoCodecs) {
Format[] videoFormats = new Format[selectedVariantsCount];
for (int i = 0; i < videoFormats.length; i++) {
videoFormats[i] = deriveVideoFormat(selectedPlaylistFormats[i]);
}
muxedTrackGroups.add(new TrackGroup(videoFormats));
if (variantsContainAudioCodecs && (masterPlaylist.muxedAudioFormat != null || masterPlaylist.audios.isEmpty())) {
muxedTrackGroups.add(new TrackGroup(deriveAudioFormat(selectedPlaylistFormats[0], masterPlaylist.muxedAudioFormat, /* isPrimaryTrackInVariant= */
false)));
}
List<Format> ccFormats = masterPlaylist.muxedCaptionFormats;
if (ccFormats != null) {
for (int i = 0; i < ccFormats.size(); i++) {
muxedTrackGroups.add(new TrackGroup(ccFormats.get(i)));
}
}
} else if (variantsContainAudioCodecs) {
// Variants only contain audio.
Format[] audioFormats = new Format[selectedVariantsCount];
for (int i = 0; i < audioFormats.length; i++) {
audioFormats[i] = deriveAudioFormat(/* variantFormat= */
selectedPlaylistFormats[i], masterPlaylist.muxedAudioFormat, /* isPrimaryTrackInVariant= */
true);
}
muxedTrackGroups.add(new TrackGroup(audioFormats));
} else {
// Variants contain codecs but no video or audio entries could be identified.
throw new IllegalArgumentException("Unexpected codecs attribute: " + codecs);
}
TrackGroup id3TrackGroup = new TrackGroup(Format.createSampleFormat(/* id= */
"ID3", MimeTypes.APPLICATION_ID3, /* codecs= */
null, /* bitrate= */
Format.NO_VALUE, /* drmInitData= */
null));
muxedTrackGroups.add(id3TrackGroup);
sampleStreamWrapper.prepareWithMasterPlaylistInfo(muxedTrackGroups.toArray(new TrackGroup[0]), /* primaryTrackGroupIndex= */
0, /* optionalTrackGroupsIndices= */
muxedTrackGroups.indexOf(id3TrackGroup));
}
}
use of com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.Variant in project hms-wiseplay-demo-exoplayer by HMS-Core.
the class HlsMediaPeriod method buildAndPrepareMainSampleStreamWrapper.
/**
* This method creates and starts preparation of the main {@link HlsSampleStreamWrapper}.
*
* <p>The main sample stream wrapper is the first element of {@link #sampleStreamWrappers}. It
* provides {@link SampleStream}s for the variant urls in the master playlist. It may be adaptive
* and may contain multiple muxed tracks.
*
* <p>If chunkless preparation is allowed, the media period will try preparation without segment
* downloads. This is only possible if variants contain the CODECS attribute. If not, traditional
* preparation with segment downloads will take place. The following points apply to chunkless
* preparation:
*
* <ul>
* <li>A muxed audio track will be exposed if the codecs list contain an audio entry and the
* master playlist either contains an EXT-X-MEDIA tag without the URI attribute or does not
* contain any EXT-X-MEDIA tag.
* <li>Closed captions will only be exposed if they are declared by the master playlist.
* <li>An ID3 track is exposed preemptively, in case the segments contain an ID3 track.
* </ul>
*
* @param masterPlaylist The HLS master playlist.
* @param positionUs If preparation requires any chunk downloads, the position in microseconds at
* which downloading should start. Ignored otherwise.
* @param sampleStreamWrappers List to which the built main sample stream wrapper should be added.
* @param manifestUrlIndicesPerWrapper List to which the selected variant indices should be added.
* @param overridingDrmInitData Overriding {@link DrmInitData}, keyed by protection scheme type
* (i.e. {@link DrmInitData#schemeType}).
*/
private void buildAndPrepareMainSampleStreamWrapper(HlsMasterPlaylist masterPlaylist, long positionUs, List<HlsSampleStreamWrapper> sampleStreamWrappers, List<int[]> manifestUrlIndicesPerWrapper, Map<String, DrmInitData> overridingDrmInitData) {
int[] variantTypes = new int[masterPlaylist.variants.size()];
int videoVariantCount = 0;
int audioVariantCount = 0;
for (int i = 0; i < masterPlaylist.variants.size(); i++) {
Variant variant = masterPlaylist.variants.get(i);
Format format = variant.format;
if (format.height > 0 || Util.getCodecsOfType(format.codecs, C.TRACK_TYPE_VIDEO) != null) {
variantTypes[i] = C.TRACK_TYPE_VIDEO;
videoVariantCount++;
} else if (Util.getCodecsOfType(format.codecs, C.TRACK_TYPE_AUDIO) != null) {
variantTypes[i] = C.TRACK_TYPE_AUDIO;
audioVariantCount++;
} else {
variantTypes[i] = C.TRACK_TYPE_UNKNOWN;
}
}
boolean useVideoVariantsOnly = false;
boolean useNonAudioVariantsOnly = false;
int selectedVariantsCount = variantTypes.length;
if (videoVariantCount > 0) {
// We've identified some variants as definitely containing video. Assume variants within the
// master playlist are marked consistently, and hence that we have the full set. Filter out
// any other variants, which are likely to be audio only.
useVideoVariantsOnly = true;
selectedVariantsCount = videoVariantCount;
} else if (audioVariantCount < variantTypes.length) {
// We've identified some variants, but not all, as being audio only. Filter them out to leave
// the remaining variants, which are likely to contain video.
useNonAudioVariantsOnly = true;
selectedVariantsCount = variantTypes.length - audioVariantCount;
}
Uri[] selectedPlaylistUrls = new Uri[selectedVariantsCount];
Format[] selectedPlaylistFormats = new Format[selectedVariantsCount];
int[] selectedVariantIndices = new int[selectedVariantsCount];
int outIndex = 0;
for (int i = 0; i < masterPlaylist.variants.size(); i++) {
if ((!useVideoVariantsOnly || variantTypes[i] == C.TRACK_TYPE_VIDEO) && (!useNonAudioVariantsOnly || variantTypes[i] != C.TRACK_TYPE_AUDIO)) {
Variant variant = masterPlaylist.variants.get(i);
selectedPlaylistUrls[outIndex] = variant.url;
selectedPlaylistFormats[outIndex] = variant.format;
selectedVariantIndices[outIndex++] = i;
}
}
String codecs = selectedPlaylistFormats[0].codecs;
HlsSampleStreamWrapper sampleStreamWrapper = buildSampleStreamWrapper(C.TRACK_TYPE_DEFAULT, selectedPlaylistUrls, selectedPlaylistFormats, masterPlaylist.muxedAudioFormat, masterPlaylist.muxedCaptionFormats, overridingDrmInitData, positionUs);
sampleStreamWrappers.add(sampleStreamWrapper);
manifestUrlIndicesPerWrapper.add(selectedVariantIndices);
if (allowChunklessPreparation && codecs != null) {
boolean variantsContainVideoCodecs = Util.getCodecsOfType(codecs, C.TRACK_TYPE_VIDEO) != null;
boolean variantsContainAudioCodecs = Util.getCodecsOfType(codecs, C.TRACK_TYPE_AUDIO) != null;
List<TrackGroup> muxedTrackGroups = new ArrayList<>();
if (variantsContainVideoCodecs) {
Format[] videoFormats = new Format[selectedVariantsCount];
for (int i = 0; i < videoFormats.length; i++) {
videoFormats[i] = deriveVideoFormat(selectedPlaylistFormats[i]);
}
muxedTrackGroups.add(new TrackGroup(videoFormats));
if (variantsContainAudioCodecs && (masterPlaylist.muxedAudioFormat != null || masterPlaylist.audios.isEmpty())) {
muxedTrackGroups.add(new TrackGroup(deriveAudioFormat(selectedPlaylistFormats[0], masterPlaylist.muxedAudioFormat, /* isPrimaryTrackInVariant= */
false)));
}
List<Format> ccFormats = masterPlaylist.muxedCaptionFormats;
if (ccFormats != null) {
for (int i = 0; i < ccFormats.size(); i++) {
muxedTrackGroups.add(new TrackGroup(ccFormats.get(i)));
}
}
} else if (variantsContainAudioCodecs) {
// Variants only contain audio.
Format[] audioFormats = new Format[selectedVariantsCount];
for (int i = 0; i < audioFormats.length; i++) {
audioFormats[i] = deriveAudioFormat(/* variantFormat= */
selectedPlaylistFormats[i], masterPlaylist.muxedAudioFormat, /* isPrimaryTrackInVariant= */
true);
}
muxedTrackGroups.add(new TrackGroup(audioFormats));
} else {
// Variants contain codecs but no video or audio entries could be identified.
throw new IllegalArgumentException("Unexpected codecs attribute: " + codecs);
}
TrackGroup id3TrackGroup = new TrackGroup(Format.createSampleFormat(/* id= */
"ID3", MimeTypes.APPLICATION_ID3, /* codecs= */
null, /* bitrate= */
Format.NO_VALUE, /* drmInitData= */
null));
muxedTrackGroups.add(id3TrackGroup);
sampleStreamWrapper.prepareWithMasterPlaylistInfo(muxedTrackGroups.toArray(new TrackGroup[0]), /* primaryTrackGroupIndex= */
0, /* optionalTrackGroupsIndices= */
muxedTrackGroups.indexOf(id3TrackGroup));
}
}
use of com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.Variant in project hms-wiseplay-demo-exoplayer by HMS-Core.
the class HlsMasterPlaylistParserTest method parseMasterPlaylist_withTtmlSubtitle.
@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");
}
use of com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.Variant in project hms-wiseplay-demo-exoplayer by HMS-Core.
the class HlsPlaylistParser method parseMasterPlaylist.
private static HlsMasterPlaylist parseMasterPlaylist(LineIterator iterator, String baseUri) throws IOException {
HashMap<Uri, ArrayList<VariantInfo>> urlToVariantInfos = new HashMap<>();
HashMap<String, String> variableDefinitions = new HashMap<>();
ArrayList<Variant> variants = new ArrayList<>();
ArrayList<Rendition> videos = new ArrayList<>();
ArrayList<Rendition> audios = new ArrayList<>();
ArrayList<Rendition> subtitles = new ArrayList<>();
ArrayList<Rendition> closedCaptions = new ArrayList<>();
ArrayList<String> mediaTags = new ArrayList<>();
ArrayList<DrmInitData> sessionKeyDrmInitData = new ArrayList<>();
ArrayList<String> tags = new ArrayList<>();
Format muxedAudioFormat = null;
List<Format> muxedCaptionFormats = null;
boolean noClosedCaptions = false;
boolean hasIndependentSegmentsTag = false;
String line;
while (iterator.hasNext()) {
line = iterator.next();
if (line.startsWith(TAG_PREFIX)) {
// We expose all tags through the playlist.
tags.add(line);
}
if (line.startsWith(TAG_DEFINE)) {
variableDefinitions.put(/* key= */
parseStringAttr(line, REGEX_NAME, variableDefinitions), /* value= */
parseStringAttr(line, REGEX_VALUE, variableDefinitions));
} else if (line.equals(TAG_INDEPENDENT_SEGMENTS)) {
hasIndependentSegmentsTag = true;
} else if (line.startsWith(TAG_MEDIA)) {
// Media tags are parsed at the end to include codec information from #EXT-X-STREAM-INF
// tags.
mediaTags.add(line);
} else if (line.startsWith(TAG_SESSION_KEY)) {
String keyFormat = parseOptionalStringAttr(line, REGEX_KEYFORMAT, KEYFORMAT_IDENTITY, variableDefinitions);
SchemeData schemeData = parseDrmSchemeData(line, keyFormat, variableDefinitions);
if (schemeData != null) {
String method = parseStringAttr(line, REGEX_METHOD, variableDefinitions);
String scheme = parseEncryptionScheme(method);
sessionKeyDrmInitData.add(new DrmInitData(scheme, schemeData));
}
} else if (line.startsWith(TAG_STREAM_INF)) {
noClosedCaptions |= line.contains(ATTR_CLOSED_CAPTIONS_NONE);
int bitrate = parseIntAttr(line, REGEX_BANDWIDTH);
// TODO: Plumb this into Format.
int averageBitrate = parseOptionalIntAttr(line, REGEX_AVERAGE_BANDWIDTH, -1);
String codecs = parseOptionalStringAttr(line, REGEX_CODECS, variableDefinitions);
String resolutionString = parseOptionalStringAttr(line, REGEX_RESOLUTION, variableDefinitions);
int width;
int height;
if (resolutionString != null) {
String[] widthAndHeight = resolutionString.split("x");
width = Integer.parseInt(widthAndHeight[0]);
height = Integer.parseInt(widthAndHeight[1]);
if (width <= 0 || height <= 0) {
// Resolution string is invalid.
width = Format.NO_VALUE;
height = Format.NO_VALUE;
}
} else {
width = Format.NO_VALUE;
height = Format.NO_VALUE;
}
float frameRate = Format.NO_VALUE;
String frameRateString = parseOptionalStringAttr(line, REGEX_FRAME_RATE, variableDefinitions);
if (frameRateString != null) {
frameRate = Float.parseFloat(frameRateString);
}
String videoGroupId = parseOptionalStringAttr(line, REGEX_VIDEO, variableDefinitions);
String audioGroupId = parseOptionalStringAttr(line, REGEX_AUDIO, variableDefinitions);
String subtitlesGroupId = parseOptionalStringAttr(line, REGEX_SUBTITLES, variableDefinitions);
String closedCaptionsGroupId = parseOptionalStringAttr(line, REGEX_CLOSED_CAPTIONS, variableDefinitions);
if (!iterator.hasNext()) {
throw new ParserException("#EXT-X-STREAM-INF tag must be followed by another line");
}
line = replaceVariableReferences(iterator.next(), // #EXT-X-STREAM-INF's URI.
variableDefinitions);
Uri uri = UriUtil.resolveToUri(baseUri, line);
Format format = Format.createVideoContainerFormat(/* id= */
Integer.toString(variants.size()), /* label= */
null, /* containerMimeType= */
MimeTypes.APPLICATION_M3U8, /* sampleMimeType= */
null, codecs, /* metadata= */
null, bitrate, width, height, frameRate, /* initializationData= */
null, /* selectionFlags= */
0, /* roleFlags= */
0);
Variant variant = new Variant(uri, format, videoGroupId, audioGroupId, subtitlesGroupId, closedCaptionsGroupId);
variants.add(variant);
ArrayList<VariantInfo> variantInfosForUrl = urlToVariantInfos.get(uri);
if (variantInfosForUrl == null) {
variantInfosForUrl = new ArrayList<>();
urlToVariantInfos.put(uri, variantInfosForUrl);
}
variantInfosForUrl.add(new VariantInfo(bitrate, videoGroupId, audioGroupId, subtitlesGroupId, closedCaptionsGroupId));
}
}
// TODO: Don't deduplicate variants by URL.
ArrayList<Variant> deduplicatedVariants = new ArrayList<>();
HashSet<Uri> urlsInDeduplicatedVariants = new HashSet<>();
for (int i = 0; i < variants.size(); i++) {
Variant variant = variants.get(i);
if (urlsInDeduplicatedVariants.add(variant.url)) {
Assertions.checkState(variant.format.metadata == null);
HlsTrackMetadataEntry hlsMetadataEntry = new HlsTrackMetadataEntry(/* groupId= */
null, /* name= */
null, Assertions.checkNotNull(urlToVariantInfos.get(variant.url)));
deduplicatedVariants.add(variant.copyWithFormat(variant.format.copyWithMetadata(new Metadata(hlsMetadataEntry))));
}
}
for (int i = 0; i < mediaTags.size(); i++) {
line = mediaTags.get(i);
String groupId = parseStringAttr(line, REGEX_GROUP_ID, variableDefinitions);
String name = parseStringAttr(line, REGEX_NAME, variableDefinitions);
String referenceUri = parseOptionalStringAttr(line, REGEX_URI, variableDefinitions);
Uri uri = referenceUri == null ? null : UriUtil.resolveToUri(baseUri, referenceUri);
String language = parseOptionalStringAttr(line, REGEX_LANGUAGE, variableDefinitions);
@C.SelectionFlags int selectionFlags = parseSelectionFlags(line);
@C.RoleFlags int roleFlags = parseRoleFlags(line, variableDefinitions);
String formatId = groupId + ":" + name;
Format format;
Metadata metadata = new Metadata(new HlsTrackMetadataEntry(groupId, name, Collections.emptyList()));
switch(parseStringAttr(line, REGEX_TYPE, variableDefinitions)) {
case TYPE_VIDEO:
Variant variant = getVariantWithVideoGroup(variants, groupId);
String codecs = null;
int width = Format.NO_VALUE;
int height = Format.NO_VALUE;
float frameRate = Format.NO_VALUE;
if (variant != null) {
Format variantFormat = variant.format;
codecs = Util.getCodecsOfType(variantFormat.codecs, C.TRACK_TYPE_VIDEO);
width = variantFormat.width;
height = variantFormat.height;
frameRate = variantFormat.frameRate;
}
String sampleMimeType = codecs != null ? MimeTypes.getMediaMimeType(codecs) : null;
format = Format.createVideoContainerFormat(/* id= */
formatId, /* label= */
name, /* containerMimeType= */
MimeTypes.APPLICATION_M3U8, sampleMimeType, codecs, /* metadata= */
null, /* bitrate= */
Format.NO_VALUE, width, height, frameRate, /* initializationData= */
null, selectionFlags, roleFlags).copyWithMetadata(metadata);
if (uri == null) {
// TODO: Remove this case and add a Rendition with a null uri to videos.
} else {
videos.add(new Rendition(uri, format, groupId, name));
}
break;
case TYPE_AUDIO:
variant = getVariantWithAudioGroup(variants, groupId);
codecs = variant != null ? Util.getCodecsOfType(variant.format.codecs, C.TRACK_TYPE_AUDIO) : null;
sampleMimeType = codecs != null ? MimeTypes.getMediaMimeType(codecs) : null;
String channelsString = parseOptionalStringAttr(line, REGEX_CHANNELS, variableDefinitions);
int channelCount = Format.NO_VALUE;
if (channelsString != null) {
channelCount = Integer.parseInt(Util.splitAtFirst(channelsString, "/")[0]);
if (MimeTypes.AUDIO_E_AC3.equals(sampleMimeType) && channelsString.endsWith("/JOC")) {
sampleMimeType = MimeTypes.AUDIO_E_AC3_JOC;
}
}
format = Format.createAudioContainerFormat(/* id= */
formatId, /* label= */
name, /* containerMimeType= */
MimeTypes.APPLICATION_M3U8, sampleMimeType, codecs, /* metadata= */
null, /* bitrate= */
Format.NO_VALUE, channelCount, /* sampleRate= */
Format.NO_VALUE, /* initializationData= */
null, selectionFlags, roleFlags, language);
if (uri == null) {
// TODO: Remove muxedAudioFormat and add a Rendition with a null uri to audios.
muxedAudioFormat = format;
} else {
audios.add(new Rendition(uri, format.copyWithMetadata(metadata), groupId, name));
}
break;
case TYPE_SUBTITLES:
codecs = null;
sampleMimeType = null;
variant = getVariantWithSubtitleGroup(variants, groupId);
if (variant != null) {
codecs = Util.getCodecsOfType(variant.format.codecs, C.TRACK_TYPE_TEXT);
sampleMimeType = MimeTypes.getMediaMimeType(codecs);
}
if (sampleMimeType == null) {
sampleMimeType = MimeTypes.TEXT_VTT;
}
format = Format.createTextContainerFormat(/* id= */
formatId, /* label= */
name, /* containerMimeType= */
MimeTypes.APPLICATION_M3U8, sampleMimeType, codecs, /* bitrate= */
Format.NO_VALUE, selectionFlags, roleFlags, language).copyWithMetadata(metadata);
subtitles.add(new Rendition(uri, format, groupId, name));
break;
case TYPE_CLOSED_CAPTIONS:
String instreamId = parseStringAttr(line, REGEX_INSTREAM_ID, variableDefinitions);
String mimeType;
int accessibilityChannel;
if (instreamId.startsWith("CC")) {
mimeType = MimeTypes.APPLICATION_CEA608;
accessibilityChannel = Integer.parseInt(instreamId.substring(2));
} else /* starts with SERVICE */
{
mimeType = MimeTypes.APPLICATION_CEA708;
accessibilityChannel = Integer.parseInt(instreamId.substring(7));
}
if (muxedCaptionFormats == null) {
muxedCaptionFormats = new ArrayList<>();
}
muxedCaptionFormats.add(Format.createTextContainerFormat(/* id= */
formatId, /* label= */
name, /* containerMimeType= */
null, /* sampleMimeType= */
mimeType, /* codecs= */
null, /* bitrate= */
Format.NO_VALUE, selectionFlags, roleFlags, language, accessibilityChannel));
// TODO: Remove muxedCaptionFormats and add a Rendition with a null uri to closedCaptions.
break;
default:
// Do nothing.
break;
}
}
if (noClosedCaptions) {
muxedCaptionFormats = Collections.emptyList();
}
return new HlsMasterPlaylist(baseUri, tags, deduplicatedVariants, videos, audios, subtitles, closedCaptions, muxedAudioFormat, muxedCaptionFormats, hasIndependentSegmentsTag, variableDefinitions, sessionKeyDrmInitData);
}
use of com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.Variant in project ExoPlayer by google.
the class HlsSampleStreamWrapper method deriveFormat.
/**
* Derives a track sample format from the corresponding format in the multivariant playlist, and a
* sample format that may have been obtained from a chunk belonging to a different track in the
* same track group.
*
* <p>Note: Since the sample format may have been obtained from a chunk belonging to a different
* track, it should not be used as a source for data that may vary between tracks.
*
* @param playlistFormat The format information obtained from the multivariant playlist.
* @param sampleFormat The format information obtained from samples within a chunk. The chunk may
* belong to a different track in the same track group.
* @param propagateBitrates Whether the bitrates from the playlist format should be included in
* the derived format.
* @return The derived track format.
*/
private static Format deriveFormat(@Nullable Format playlistFormat, Format sampleFormat, boolean propagateBitrates) {
if (playlistFormat == null) {
return sampleFormat;
}
int sampleTrackType = MimeTypes.getTrackType(sampleFormat.sampleMimeType);
@Nullable String sampleMimeType;
@Nullable String codecs;
if (Util.getCodecCountOfType(playlistFormat.codecs, sampleTrackType) == 1) {
// We can unequivocally map this track to a playlist variant because only one codec string
// matches this track's type.
codecs = Util.getCodecsOfType(playlistFormat.codecs, sampleTrackType);
sampleMimeType = MimeTypes.getMediaMimeType(codecs);
} else {
// The variant assigns more than one codec string to this track. We choose whichever codec
// string matches the sample mime type. This can happen when different languages are encoded
// using different codecs.
codecs = MimeTypes.getCodecsCorrespondingToMimeType(playlistFormat.codecs, sampleFormat.sampleMimeType);
sampleMimeType = sampleFormat.sampleMimeType;
}
Format.Builder formatBuilder = sampleFormat.buildUpon().setId(playlistFormat.id).setLabel(playlistFormat.label).setLanguage(playlistFormat.language).setSelectionFlags(playlistFormat.selectionFlags).setRoleFlags(playlistFormat.roleFlags).setAverageBitrate(propagateBitrates ? playlistFormat.averageBitrate : Format.NO_VALUE).setPeakBitrate(propagateBitrates ? playlistFormat.peakBitrate : Format.NO_VALUE).setCodecs(codecs);
if (sampleTrackType == C.TRACK_TYPE_VIDEO) {
formatBuilder.setWidth(playlistFormat.width).setHeight(playlistFormat.height).setFrameRate(playlistFormat.frameRate);
}
if (sampleMimeType != null) {
formatBuilder.setSampleMimeType(sampleMimeType);
}
if (playlistFormat.channelCount != Format.NO_VALUE && sampleTrackType == C.TRACK_TYPE_AUDIO) {
formatBuilder.setChannelCount(playlistFormat.channelCount);
}
if (playlistFormat.metadata != null) {
Metadata metadata = playlistFormat.metadata;
if (sampleFormat.metadata != null) {
metadata = sampleFormat.metadata.copyWithAppendedEntriesFrom(metadata);
}
formatBuilder.setMetadata(metadata);
}
return formatBuilder.build();
}
Aggregations