mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Migrate HLS to Format.Builder
Issue: #2863 PiperOrigin-RevId: 296482726
This commit is contained in:
parent
f342df2047
commit
6fc5e6b905
8 changed files with 170 additions and 274 deletions
|
|
@ -1347,39 +1347,6 @@ public final class Format implements Parcelable {
|
||||||
return buildUpon().setLabel(label).build();
|
return buildUpon().setLabel(label).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Inline into HlsSampleStreamWrapper and remove.
|
|
||||||
public Format copyWithContainerInfo(
|
|
||||||
@Nullable String id,
|
|
||||||
@Nullable String label,
|
|
||||||
@Nullable String sampleMimeType,
|
|
||||||
@Nullable String codecs,
|
|
||||||
@Nullable Metadata metadata,
|
|
||||||
int bitrate,
|
|
||||||
int width,
|
|
||||||
int height,
|
|
||||||
int channelCount,
|
|
||||||
@C.SelectionFlags int selectionFlags,
|
|
||||||
@Nullable String language) {
|
|
||||||
if (this.metadata != null) {
|
|
||||||
metadata = this.metadata.copyWithAppendedEntriesFrom(metadata);
|
|
||||||
}
|
|
||||||
|
|
||||||
return buildUpon()
|
|
||||||
.setId(id)
|
|
||||||
.setLabel(label)
|
|
||||||
.setLanguage(language)
|
|
||||||
.setSelectionFlags(selectionFlags)
|
|
||||||
.setAverageBitrate(bitrate)
|
|
||||||
.setPeakBitrate(bitrate)
|
|
||||||
.setMetadata(metadata)
|
|
||||||
.setCodecs(codecs)
|
|
||||||
.setSampleMimeType(sampleMimeType)
|
|
||||||
.setWidth(width)
|
|
||||||
.setHeight(height)
|
|
||||||
.setChannelCount(channelCount)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @deprecated Use {@link #withManifestFormatInfo(Format)}. */
|
/** @deprecated Use {@link #withManifestFormatInfo(Format)}. */
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public Format copyWithManifestFormatInfo(Format manifestFormat) {
|
public Format copyWithManifestFormatInfo(Format manifestFormat) {
|
||||||
|
|
|
||||||
|
|
@ -235,15 +235,11 @@ public final class DefaultHlsExtractorFactory implements HlsExtractorFactory {
|
||||||
// closed caption track on channel 0.
|
// closed caption track on channel 0.
|
||||||
muxedCaptionFormats =
|
muxedCaptionFormats =
|
||||||
Collections.singletonList(
|
Collections.singletonList(
|
||||||
Format.createTextSampleFormat(
|
new Format.Builder().setSampleMimeType(MimeTypes.APPLICATION_CEA608).build());
|
||||||
/* id= */ null,
|
|
||||||
MimeTypes.APPLICATION_CEA608,
|
|
||||||
/* selectionFlags= */ 0,
|
|
||||||
/* language= */ null));
|
|
||||||
} else {
|
} else {
|
||||||
muxedCaptionFormats = Collections.emptyList();
|
muxedCaptionFormats = Collections.emptyList();
|
||||||
}
|
}
|
||||||
String codecs = format.codecs;
|
@Nullable String codecs = format.codecs;
|
||||||
if (!TextUtils.isEmpty(codecs)) {
|
if (!TextUtils.isEmpty(codecs)) {
|
||||||
// Sometimes AAC and H264 streams are declared in TS chunks even though they don't really
|
// Sometimes AAC and H264 streams are declared in TS chunks even though they don't really
|
||||||
// exist. If we know from the codec attribute that they don't exist, then we can
|
// exist. If we know from the codec attribute that they don't exist, then we can
|
||||||
|
|
|
||||||
|
|
@ -659,7 +659,11 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
|
||||||
}
|
}
|
||||||
|
|
||||||
TrackGroup id3TrackGroup =
|
TrackGroup id3TrackGroup =
|
||||||
new TrackGroup(Format.createSampleFormat(/* id= */ "ID3", MimeTypes.APPLICATION_ID3));
|
new TrackGroup(
|
||||||
|
new Format.Builder()
|
||||||
|
.setId("ID3")
|
||||||
|
.setSampleMimeType(MimeTypes.APPLICATION_ID3)
|
||||||
|
.build());
|
||||||
muxedTrackGroups.add(id3TrackGroup);
|
muxedTrackGroups.add(id3TrackGroup);
|
||||||
|
|
||||||
sampleStreamWrapper.prepareWithMasterPlaylistInfo(
|
sampleStreamWrapper.prepareWithMasterPlaylistInfo(
|
||||||
|
|
@ -785,33 +789,34 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Format deriveVideoFormat(Format variantFormat) {
|
private static Format deriveVideoFormat(Format variantFormat) {
|
||||||
String codecs = Util.getCodecsOfType(variantFormat.codecs, C.TRACK_TYPE_VIDEO);
|
@Nullable String codecs = Util.getCodecsOfType(variantFormat.codecs, C.TRACK_TYPE_VIDEO);
|
||||||
String sampleMimeType = MimeTypes.getMediaMimeType(codecs);
|
@Nullable String sampleMimeType = MimeTypes.getMediaMimeType(codecs);
|
||||||
return Format.createVideoContainerFormat(
|
return new Format.Builder()
|
||||||
variantFormat.id,
|
.setId(variantFormat.id)
|
||||||
variantFormat.label,
|
.setLabel(variantFormat.label)
|
||||||
variantFormat.containerMimeType,
|
.setContainerMimeType(variantFormat.containerMimeType)
|
||||||
sampleMimeType,
|
.setSampleMimeType(sampleMimeType)
|
||||||
codecs,
|
.setCodecs(codecs)
|
||||||
variantFormat.metadata,
|
.setMetadata(variantFormat.metadata)
|
||||||
variantFormat.bitrate,
|
.setAverageBitrate(variantFormat.averageBitrate)
|
||||||
variantFormat.width,
|
.setPeakBitrate(variantFormat.peakBitrate)
|
||||||
variantFormat.height,
|
.setWidth(variantFormat.width)
|
||||||
variantFormat.frameRate,
|
.setHeight(variantFormat.height)
|
||||||
/* initializationData= */ null,
|
.setFrameRate(variantFormat.frameRate)
|
||||||
variantFormat.selectionFlags,
|
.setSelectionFlags(variantFormat.selectionFlags)
|
||||||
variantFormat.roleFlags);
|
.setRoleFlags(variantFormat.roleFlags)
|
||||||
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Format deriveAudioFormat(
|
private static Format deriveAudioFormat(
|
||||||
Format variantFormat, @Nullable Format mediaTagFormat, boolean isPrimaryTrackInVariant) {
|
Format variantFormat, @Nullable Format mediaTagFormat, boolean isPrimaryTrackInVariant) {
|
||||||
String codecs;
|
@Nullable String codecs;
|
||||||
Metadata metadata;
|
@Nullable Metadata metadata;
|
||||||
int channelCount = Format.NO_VALUE;
|
int channelCount = Format.NO_VALUE;
|
||||||
int selectionFlags = 0;
|
int selectionFlags = 0;
|
||||||
int roleFlags = 0;
|
int roleFlags = 0;
|
||||||
String language = null;
|
@Nullable String language = null;
|
||||||
String label = null;
|
@Nullable String label = null;
|
||||||
if (mediaTagFormat != null) {
|
if (mediaTagFormat != null) {
|
||||||
codecs = mediaTagFormat.codecs;
|
codecs = mediaTagFormat.codecs;
|
||||||
metadata = mediaTagFormat.metadata;
|
metadata = mediaTagFormat.metadata;
|
||||||
|
|
@ -831,22 +836,23 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
|
||||||
label = variantFormat.label;
|
label = variantFormat.label;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
String sampleMimeType = MimeTypes.getMediaMimeType(codecs);
|
@Nullable String sampleMimeType = MimeTypes.getMediaMimeType(codecs);
|
||||||
int bitrate = isPrimaryTrackInVariant ? variantFormat.bitrate : Format.NO_VALUE;
|
int averageBitrate = isPrimaryTrackInVariant ? variantFormat.averageBitrate : Format.NO_VALUE;
|
||||||
return Format.createAudioContainerFormat(
|
int peakBitrate = isPrimaryTrackInVariant ? variantFormat.peakBitrate : Format.NO_VALUE;
|
||||||
variantFormat.id,
|
return new Format.Builder()
|
||||||
label,
|
.setId(variantFormat.id)
|
||||||
variantFormat.containerMimeType,
|
.setLabel(label)
|
||||||
sampleMimeType,
|
.setContainerMimeType(variantFormat.containerMimeType)
|
||||||
codecs,
|
.setSampleMimeType(sampleMimeType)
|
||||||
metadata,
|
.setCodecs(codecs)
|
||||||
bitrate,
|
.setMetadata(metadata)
|
||||||
channelCount,
|
.setAverageBitrate(averageBitrate)
|
||||||
/* sampleRate= */ Format.NO_VALUE,
|
.setPeakBitrate(peakBitrate)
|
||||||
/* initializationData= */ null,
|
.setChannelCount(channelCount)
|
||||||
selectionFlags,
|
.setSelectionFlags(selectionFlags)
|
||||||
roleFlags,
|
.setRoleFlags(roleFlags)
|
||||||
language);
|
.setLanguage(language)
|
||||||
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1269,38 +1269,50 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||||
*
|
*
|
||||||
* @param playlistFormat The format information obtained from the master playlist.
|
* @param playlistFormat The format information obtained from the master playlist.
|
||||||
* @param sampleFormat The format information obtained from the samples.
|
* @param sampleFormat The format information obtained from the samples.
|
||||||
* @param propagateBitrate Whether the bitrate from the playlist format should be included in the
|
* @param propagateBitrates Whether the bitrates from the playlist format should be included in
|
||||||
* derived format.
|
* the derived format.
|
||||||
* @return The derived track format.
|
* @return The derived track format.
|
||||||
*/
|
*/
|
||||||
private static Format deriveFormat(
|
private static Format deriveFormat(
|
||||||
@Nullable Format playlistFormat, Format sampleFormat, boolean propagateBitrate) {
|
@Nullable Format playlistFormat, Format sampleFormat, boolean propagateBitrates) {
|
||||||
if (playlistFormat == null) {
|
if (playlistFormat == null) {
|
||||||
return sampleFormat;
|
return sampleFormat;
|
||||||
}
|
}
|
||||||
int bitrate = propagateBitrate ? playlistFormat.bitrate : Format.NO_VALUE;
|
|
||||||
int channelCount =
|
|
||||||
playlistFormat.channelCount != Format.NO_VALUE
|
|
||||||
? playlistFormat.channelCount
|
|
||||||
: sampleFormat.channelCount;
|
|
||||||
int sampleTrackType = MimeTypes.getTrackType(sampleFormat.sampleMimeType);
|
int sampleTrackType = MimeTypes.getTrackType(sampleFormat.sampleMimeType);
|
||||||
@Nullable String codecs = Util.getCodecsOfType(playlistFormat.codecs, sampleTrackType);
|
@Nullable String codecs = Util.getCodecsOfType(playlistFormat.codecs, sampleTrackType);
|
||||||
@Nullable String mimeType = MimeTypes.getMediaMimeType(codecs);
|
@Nullable String sampleMimeType = MimeTypes.getMediaMimeType(codecs);
|
||||||
if (mimeType == null) {
|
|
||||||
mimeType = sampleFormat.sampleMimeType;
|
Format.Builder formatBuilder =
|
||||||
|
sampleFormat
|
||||||
|
.buildUpon()
|
||||||
|
.setId(playlistFormat.id)
|
||||||
|
.setLabel(playlistFormat.label)
|
||||||
|
.setLanguage(playlistFormat.language)
|
||||||
|
.setSelectionFlags(playlistFormat.selectionFlags)
|
||||||
|
.setAverageBitrate(propagateBitrates ? playlistFormat.averageBitrate : Format.NO_VALUE)
|
||||||
|
.setPeakBitrate(propagateBitrates ? playlistFormat.peakBitrate : Format.NO_VALUE)
|
||||||
|
.setCodecs(codecs)
|
||||||
|
.setWidth(playlistFormat.width)
|
||||||
|
.setHeight(playlistFormat.height);
|
||||||
|
|
||||||
|
if (sampleMimeType != null) {
|
||||||
|
formatBuilder.setSampleMimeType(sampleMimeType);
|
||||||
}
|
}
|
||||||
return sampleFormat.copyWithContainerInfo(
|
|
||||||
playlistFormat.id,
|
if (playlistFormat.channelCount != Format.NO_VALUE) {
|
||||||
playlistFormat.label,
|
formatBuilder.setChannelCount(playlistFormat.channelCount);
|
||||||
mimeType,
|
}
|
||||||
codecs,
|
|
||||||
playlistFormat.metadata,
|
if (playlistFormat.metadata != null) {
|
||||||
bitrate,
|
Metadata metadata = playlistFormat.metadata;
|
||||||
playlistFormat.width,
|
if (sampleFormat.metadata != null) {
|
||||||
playlistFormat.height,
|
metadata = sampleFormat.metadata.copyWithAppendedEntriesFrom(metadata);
|
||||||
channelCount,
|
}
|
||||||
playlistFormat.selectionFlags,
|
formatBuilder.setMetadata(metadata);
|
||||||
playlistFormat.language);
|
}
|
||||||
|
|
||||||
|
return formatBuilder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isMediaChunk(Chunk chunk) {
|
private static boolean isMediaChunk(Chunk chunk) {
|
||||||
|
|
@ -1409,9 +1421,9 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||||
|
|
||||||
// TODO(ibaker): Create a Formats util class with common constants like this.
|
// TODO(ibaker): Create a Formats util class with common constants like this.
|
||||||
private static final Format ID3_FORMAT =
|
private static final Format ID3_FORMAT =
|
||||||
Format.createSampleFormat(/* id= */ null, MimeTypes.APPLICATION_ID3);
|
new Format.Builder().setSampleMimeType(MimeTypes.APPLICATION_ID3).build();
|
||||||
private static final Format EMSG_FORMAT =
|
private static final Format EMSG_FORMAT =
|
||||||
Format.createSampleFormat(/* id= */ null, MimeTypes.APPLICATION_EMSG);
|
new Format.Builder().setSampleMimeType(MimeTypes.APPLICATION_EMSG).build();
|
||||||
|
|
||||||
private final EventMessageDecoder emsgDecoder;
|
private final EventMessageDecoder emsgDecoder;
|
||||||
private final TrackOutput delegate;
|
private final TrackOutput delegate;
|
||||||
|
|
|
||||||
|
|
@ -187,9 +187,11 @@ public final class WebvttExtractor implements Extractor {
|
||||||
private TrackOutput buildTrackOutput(long subsampleOffsetUs) {
|
private TrackOutput buildTrackOutput(long subsampleOffsetUs) {
|
||||||
TrackOutput trackOutput = output.track(0, C.TRACK_TYPE_TEXT);
|
TrackOutput trackOutput = output.track(0, C.TRACK_TYPE_TEXT);
|
||||||
trackOutput.format(
|
trackOutput.format(
|
||||||
Format.createTextSampleFormat(
|
new Format.Builder()
|
||||||
/* id= */ null, MimeTypes.TEXT_VTT, /* selectionFlags= */ 0, language)
|
.setSampleMimeType(MimeTypes.TEXT_VTT)
|
||||||
.copyWithSubsampleOffsetUs(subsampleOffsetUs));
|
.setLanguage(language)
|
||||||
|
.setSubsampleOffsetUs(subsampleOffsetUs)
|
||||||
|
.build());
|
||||||
output.endTracks();
|
output.endTracks();
|
||||||
return trackOutput;
|
return trackOutput;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -102,16 +102,7 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
|
||||||
*/
|
*/
|
||||||
public static Variant createMediaPlaylistVariantUrl(Uri url) {
|
public static Variant createMediaPlaylistVariantUrl(Uri url) {
|
||||||
Format format =
|
Format format =
|
||||||
Format.createContainerFormat(
|
new Format.Builder().setId("0").setContainerMimeType(MimeTypes.APPLICATION_M3U8).build();
|
||||||
"0",
|
|
||||||
/* label= */ null,
|
|
||||||
MimeTypes.APPLICATION_M3U8,
|
|
||||||
/* sampleMimeType= */ null,
|
|
||||||
/* codecs= */ null,
|
|
||||||
/* bitrate= */ Format.NO_VALUE,
|
|
||||||
/* selectionFlags= */ 0,
|
|
||||||
/* roleFlags= */ 0,
|
|
||||||
/* language= */ null);
|
|
||||||
return new Variant(
|
return new Variant(
|
||||||
url,
|
url,
|
||||||
format,
|
format,
|
||||||
|
|
|
||||||
|
|
@ -342,22 +342,17 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
|
||||||
replaceVariableReferences(
|
replaceVariableReferences(
|
||||||
iterator.next(), variableDefinitions); // #EXT-X-STREAM-INF's URI.
|
iterator.next(), variableDefinitions); // #EXT-X-STREAM-INF's URI.
|
||||||
Uri uri = UriUtil.resolveToUri(baseUri, line);
|
Uri uri = UriUtil.resolveToUri(baseUri, line);
|
||||||
// TODO: Set Format.averageBitrate.
|
|
||||||
Format format =
|
Format format =
|
||||||
Format.createVideoContainerFormat(
|
new Format.Builder()
|
||||||
/* id= */ Integer.toString(variants.size()),
|
.setId(variants.size())
|
||||||
/* label= */ null,
|
.setContainerMimeType(MimeTypes.APPLICATION_M3U8)
|
||||||
/* containerMimeType= */ MimeTypes.APPLICATION_M3U8,
|
.setCodecs(codecs)
|
||||||
/* sampleMimeType= */ null,
|
.setAverageBitrate(averageBitrate)
|
||||||
codecs,
|
.setPeakBitrate(peakBitrate)
|
||||||
/* metadata= */ null,
|
.setWidth(width)
|
||||||
peakBitrate,
|
.setHeight(height)
|
||||||
width,
|
.setFrameRate(frameRate)
|
||||||
height,
|
.build();
|
||||||
frameRate,
|
|
||||||
/* initializationData= */ null,
|
|
||||||
/* selectionFlags= */ 0,
|
|
||||||
/* roleFlags= */ 0);
|
|
||||||
Variant variant =
|
Variant variant =
|
||||||
new Variant(
|
new Variant(
|
||||||
uri, format, videoGroupId, audioGroupId, subtitlesGroupId, closedCaptionsGroupId);
|
uri, format, videoGroupId, audioGroupId, subtitlesGroupId, closedCaptionsGroupId);
|
||||||
|
|
@ -400,131 +395,89 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
|
||||||
line = mediaTags.get(i);
|
line = mediaTags.get(i);
|
||||||
String groupId = parseStringAttr(line, REGEX_GROUP_ID, variableDefinitions);
|
String groupId = parseStringAttr(line, REGEX_GROUP_ID, variableDefinitions);
|
||||||
String name = parseStringAttr(line, REGEX_NAME, variableDefinitions);
|
String name = parseStringAttr(line, REGEX_NAME, variableDefinitions);
|
||||||
String referenceUri = parseOptionalStringAttr(line, REGEX_URI, variableDefinitions);
|
Format.Builder formatBuilder =
|
||||||
Uri uri = referenceUri == null ? null : UriUtil.resolveToUri(baseUri, referenceUri);
|
new Format.Builder()
|
||||||
String language = parseOptionalStringAttr(line, REGEX_LANGUAGE, variableDefinitions);
|
.setId(groupId + ":" + name)
|
||||||
@C.SelectionFlags int selectionFlags = parseSelectionFlags(line);
|
.setLabel(name)
|
||||||
@C.RoleFlags int roleFlags = parseRoleFlags(line, variableDefinitions);
|
.setContainerMimeType(MimeTypes.APPLICATION_M3U8)
|
||||||
String formatId = groupId + ":" + name;
|
.setSelectionFlags(parseSelectionFlags(line))
|
||||||
Format format;
|
.setRoleFlags(parseRoleFlags(line, variableDefinitions))
|
||||||
|
.setLanguage(parseOptionalStringAttr(line, REGEX_LANGUAGE, variableDefinitions));
|
||||||
|
|
||||||
|
@Nullable String referenceUri = parseOptionalStringAttr(line, REGEX_URI, variableDefinitions);
|
||||||
|
@Nullable Uri uri = referenceUri == null ? null : UriUtil.resolveToUri(baseUri, referenceUri);
|
||||||
Metadata metadata =
|
Metadata metadata =
|
||||||
new Metadata(new HlsTrackMetadataEntry(groupId, name, Collections.emptyList()));
|
new Metadata(new HlsTrackMetadataEntry(groupId, name, Collections.emptyList()));
|
||||||
switch (parseStringAttr(line, REGEX_TYPE, variableDefinitions)) {
|
switch (parseStringAttr(line, REGEX_TYPE, variableDefinitions)) {
|
||||||
case TYPE_VIDEO:
|
case TYPE_VIDEO:
|
||||||
Variant variant = getVariantWithVideoGroup(variants, groupId);
|
@Nullable 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) {
|
if (variant != null) {
|
||||||
Format variantFormat = variant.format;
|
Format variantFormat = variant.format;
|
||||||
codecs = Util.getCodecsOfType(variantFormat.codecs, C.TRACK_TYPE_VIDEO);
|
@Nullable
|
||||||
width = variantFormat.width;
|
String codecs = Util.getCodecsOfType(variantFormat.codecs, C.TRACK_TYPE_VIDEO);
|
||||||
height = variantFormat.height;
|
formatBuilder
|
||||||
frameRate = variantFormat.frameRate;
|
.setCodecs(codecs)
|
||||||
|
.setSampleMimeType(MimeTypes.getMediaMimeType(codecs))
|
||||||
|
.setWidth(variantFormat.width)
|
||||||
|
.setHeight(variantFormat.height)
|
||||||
|
.setFrameRate(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) {
|
if (uri == null) {
|
||||||
// TODO: Remove this case and add a Rendition with a null uri to videos.
|
// TODO: Remove this case and add a Rendition with a null uri to videos.
|
||||||
} else {
|
} else {
|
||||||
videos.add(new Rendition(uri, format, groupId, name));
|
formatBuilder.setMetadata(metadata);
|
||||||
|
videos.add(new Rendition(uri, formatBuilder.build(), groupId, name));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case TYPE_AUDIO:
|
case TYPE_AUDIO:
|
||||||
|
@Nullable String sampleMimeType = null;
|
||||||
variant = getVariantWithAudioGroup(variants, groupId);
|
variant = getVariantWithAudioGroup(variants, groupId);
|
||||||
codecs =
|
if (variant != null) {
|
||||||
variant != null
|
@Nullable
|
||||||
? Util.getCodecsOfType(variant.format.codecs, C.TRACK_TYPE_AUDIO)
|
String codecs = Util.getCodecsOfType(variant.format.codecs, C.TRACK_TYPE_AUDIO);
|
||||||
: null;
|
formatBuilder.setCodecs(codecs);
|
||||||
sampleMimeType = codecs != null ? MimeTypes.getMediaMimeType(codecs) : null;
|
sampleMimeType = MimeTypes.getMediaMimeType(codecs);
|
||||||
|
}
|
||||||
|
@Nullable
|
||||||
String channelsString =
|
String channelsString =
|
||||||
parseOptionalStringAttr(line, REGEX_CHANNELS, variableDefinitions);
|
parseOptionalStringAttr(line, REGEX_CHANNELS, variableDefinitions);
|
||||||
int channelCount = Format.NO_VALUE;
|
|
||||||
if (channelsString != null) {
|
if (channelsString != null) {
|
||||||
channelCount = Integer.parseInt(Util.splitAtFirst(channelsString, "/")[0]);
|
int channelCount = Integer.parseInt(Util.splitAtFirst(channelsString, "/")[0]);
|
||||||
|
formatBuilder.setChannelCount(channelCount);
|
||||||
if (MimeTypes.AUDIO_E_AC3.equals(sampleMimeType) && channelsString.endsWith("/JOC")) {
|
if (MimeTypes.AUDIO_E_AC3.equals(sampleMimeType) && channelsString.endsWith("/JOC")) {
|
||||||
sampleMimeType = MimeTypes.AUDIO_E_AC3_JOC;
|
sampleMimeType = MimeTypes.AUDIO_E_AC3_JOC;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
format =
|
formatBuilder.setSampleMimeType(sampleMimeType);
|
||||||
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) {
|
if (uri == null) {
|
||||||
// TODO: Remove muxedAudioFormat and add a Rendition with a null uri to audios.
|
// TODO: Remove muxedAudioFormat and add a Rendition with a null uri to audios.
|
||||||
muxedAudioFormat = format;
|
muxedAudioFormat = formatBuilder.build();
|
||||||
} else {
|
} else {
|
||||||
audios.add(new Rendition(uri, format.copyWithMetadata(metadata), groupId, name));
|
formatBuilder.setMetadata(metadata);
|
||||||
|
audios.add(new Rendition(uri, formatBuilder.build(), groupId, name));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case TYPE_SUBTITLES:
|
case TYPE_SUBTITLES:
|
||||||
format =
|
formatBuilder.setSampleMimeType(MimeTypes.TEXT_VTT).setMetadata(metadata);
|
||||||
Format.createTextContainerFormat(
|
subtitles.add(new Rendition(uri, formatBuilder.build(), groupId, name));
|
||||||
/* id= */ formatId,
|
|
||||||
/* label= */ name,
|
|
||||||
/* containerMimeType= */ MimeTypes.APPLICATION_M3U8,
|
|
||||||
/* sampleMimeType= */ MimeTypes.TEXT_VTT,
|
|
||||||
/* codecs= */ null,
|
|
||||||
/* bitrate= */ Format.NO_VALUE,
|
|
||||||
selectionFlags,
|
|
||||||
roleFlags,
|
|
||||||
language)
|
|
||||||
.copyWithMetadata(metadata);
|
|
||||||
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);
|
||||||
String mimeType;
|
|
||||||
int accessibilityChannel;
|
int accessibilityChannel;
|
||||||
if (instreamId.startsWith("CC")) {
|
if (instreamId.startsWith("CC")) {
|
||||||
mimeType = MimeTypes.APPLICATION_CEA608;
|
sampleMimeType = MimeTypes.APPLICATION_CEA608;
|
||||||
accessibilityChannel = Integer.parseInt(instreamId.substring(2));
|
accessibilityChannel = Integer.parseInt(instreamId.substring(2));
|
||||||
} else /* starts with SERVICE */ {
|
} else /* starts with SERVICE */ {
|
||||||
mimeType = MimeTypes.APPLICATION_CEA708;
|
sampleMimeType = MimeTypes.APPLICATION_CEA708;
|
||||||
accessibilityChannel = Integer.parseInt(instreamId.substring(7));
|
accessibilityChannel = Integer.parseInt(instreamId.substring(7));
|
||||||
}
|
}
|
||||||
if (muxedCaptionFormats == null) {
|
if (muxedCaptionFormats == null) {
|
||||||
muxedCaptionFormats = new ArrayList<>();
|
muxedCaptionFormats = new ArrayList<>();
|
||||||
}
|
}
|
||||||
muxedCaptionFormats.add(
|
formatBuilder
|
||||||
Format.createTextContainerFormat(
|
.setSampleMimeType(sampleMimeType)
|
||||||
/* id= */ formatId,
|
.setAccessibilityChannel(accessibilityChannel);
|
||||||
/* label= */ name,
|
muxedCaptionFormats.add(formatBuilder.build());
|
||||||
/* 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.
|
// TODO: Remove muxedCaptionFormats and add a Rendition with a null uri to closedCaptions.
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
||||||
|
|
@ -55,11 +55,11 @@ public final class HlsMediaPeriodTest {
|
||||||
HlsMasterPlaylist testMasterPlaylist =
|
HlsMasterPlaylist testMasterPlaylist =
|
||||||
createMasterPlaylist(
|
createMasterPlaylist(
|
||||||
/* variants= */ Arrays.asList(
|
/* variants= */ Arrays.asList(
|
||||||
createAudioOnlyVariant(/* bitrate= */ 10000),
|
createAudioOnlyVariant(/* peakBitrate= */ 10000),
|
||||||
createMuxedVideoAudioVariant(/* bitrate= */ 200000),
|
createMuxedVideoAudioVariant(/* peakBitrate= */ 200000),
|
||||||
createAudioOnlyVariant(/* bitrate= */ 300000),
|
createAudioOnlyVariant(/* peakBitrate= */ 300000),
|
||||||
createMuxedVideoAudioVariant(/* bitrate= */ 400000),
|
createMuxedVideoAudioVariant(/* peakBitrate= */ 400000),
|
||||||
createMuxedVideoAudioVariant(/* bitrate= */ 600000)),
|
createMuxedVideoAudioVariant(/* peakBitrate= */ 600000)),
|
||||||
/* audios= */ Arrays.asList(
|
/* audios= */ Arrays.asList(
|
||||||
createAudioRendition(/* language= */ "spa"),
|
createAudioRendition(/* language= */ "spa"),
|
||||||
createAudioRendition(/* language= */ "ger"),
|
createAudioRendition(/* language= */ "ger"),
|
||||||
|
|
@ -121,40 +121,22 @@ public final class HlsMediaPeriodTest {
|
||||||
/* sessionKeyDrmInitData= */ Collections.emptyList());
|
/* sessionKeyDrmInitData= */ Collections.emptyList());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Variant createMuxedVideoAudioVariant(int bitrate) {
|
private static Variant createMuxedVideoAudioVariant(int peakBitrate) {
|
||||||
return createVariant(
|
return createVariant(
|
||||||
Format.createVideoContainerFormat(
|
new Format.Builder()
|
||||||
/* id= */ null,
|
.setContainerMimeType(MimeTypes.APPLICATION_M3U8)
|
||||||
/* label= */ null,
|
.setCodecs("avc1.100.41,mp4a.40.2")
|
||||||
/* containerMimeType= */ MimeTypes.APPLICATION_M3U8,
|
.setPeakBitrate(peakBitrate)
|
||||||
/* sampleMimeType= */ null,
|
.build());
|
||||||
/* codecs= */ "avc1.100.41,mp4a.40.2",
|
|
||||||
/* metadata= */ null,
|
|
||||||
bitrate,
|
|
||||||
/* width= */ Format.NO_VALUE,
|
|
||||||
/* height= */ Format.NO_VALUE,
|
|
||||||
/* frameRate= */ Format.NO_VALUE,
|
|
||||||
/* initializationData= */ null,
|
|
||||||
/* selectionFlags= */ 0,
|
|
||||||
/* roleFlags= */ 0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Variant createAudioOnlyVariant(int bitrate) {
|
private static Variant createAudioOnlyVariant(int peakBitrate) {
|
||||||
return createVariant(
|
return createVariant(
|
||||||
Format.createVideoContainerFormat(
|
new Format.Builder()
|
||||||
/* id= */ null,
|
.setContainerMimeType(MimeTypes.APPLICATION_M3U8)
|
||||||
/* label= */ null,
|
.setCodecs("mp4a.40.2")
|
||||||
/* containerMimeType= */ MimeTypes.APPLICATION_M3U8,
|
.setPeakBitrate(peakBitrate)
|
||||||
/* sampleMimeType= */ null,
|
.build());
|
||||||
/* codecs= */ "mp4a.40.2",
|
|
||||||
/* metadata= */ null,
|
|
||||||
bitrate,
|
|
||||||
/* width= */ Format.NO_VALUE,
|
|
||||||
/* height= */ Format.NO_VALUE,
|
|
||||||
/* frameRate= */ Format.NO_VALUE,
|
|
||||||
/* initializationData= */ null,
|
|
||||||
/* selectionFlags= */ 0,
|
|
||||||
/* roleFlags= */ 0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Rendition createAudioRendition(String language) {
|
private static Rendition createAudioRendition(String language) {
|
||||||
|
|
@ -174,32 +156,19 @@ public final class HlsMediaPeriodTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Format createAudioFormat(String language) {
|
private static Format createAudioFormat(String language) {
|
||||||
return Format.createAudioContainerFormat(
|
return new Format.Builder()
|
||||||
/* id= */ null,
|
.setContainerMimeType(MimeTypes.APPLICATION_M3U8)
|
||||||
/* label= */ null,
|
.setSampleMimeType(MimeTypes.getMediaMimeType("mp4a.40.2"))
|
||||||
/* containerMimeType= */ MimeTypes.APPLICATION_M3U8,
|
.setCodecs("mp4a.40.2")
|
||||||
MimeTypes.getMediaMimeType("mp4a.40.2"),
|
.setLanguage(language)
|
||||||
/* codecs= */ "mp4a.40.2",
|
.build();
|
||||||
/* metadata= */ null,
|
|
||||||
/* bitrate= */ Format.NO_VALUE,
|
|
||||||
/* channelCount= */ Format.NO_VALUE,
|
|
||||||
/* sampleRate= */ Format.NO_VALUE,
|
|
||||||
/* initializationData= */ null,
|
|
||||||
/* selectionFlags= */ 0,
|
|
||||||
/* roleFlags= */ 0,
|
|
||||||
language);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Format createSubtitleFormat(String language) {
|
private static Format createSubtitleFormat(String language) {
|
||||||
return Format.createTextContainerFormat(
|
return new Format.Builder()
|
||||||
/* id= */ null,
|
.setContainerMimeType(MimeTypes.APPLICATION_M3U8)
|
||||||
/* label= */ null,
|
.setSampleMimeType(MimeTypes.TEXT_VTT)
|
||||||
/* containerMimeType= */ MimeTypes.APPLICATION_M3U8,
|
.setLanguage(language)
|
||||||
/* sampleMimeType= */ MimeTypes.TEXT_VTT,
|
.build();
|
||||||
/* codecs= */ null,
|
|
||||||
/* bitrate= */ Format.NO_VALUE,
|
|
||||||
/* selectionFlags= */ 0,
|
|
||||||
/* roleFlags= */ 0,
|
|
||||||
language);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue