mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Add SUPPLEMENTAL-CODECS support in both DASH and HLS
This commit is contained in:
parent
b650119d4e
commit
79078c390d
4 changed files with 163 additions and 0 deletions
|
|
@ -62,6 +62,8 @@ import java.util.UUID;
|
||||||
* <li>{@link #averageBitrate}
|
* <li>{@link #averageBitrate}
|
||||||
* <li>{@link #peakBitrate}
|
* <li>{@link #peakBitrate}
|
||||||
* <li>{@link #codecs}
|
* <li>{@link #codecs}
|
||||||
|
* <li>{@link #supplementalCodecs}
|
||||||
|
* <li>{@link #supplementalProfiles}
|
||||||
* <li>{@link #metadata}
|
* <li>{@link #metadata}
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
|
|
@ -102,6 +104,7 @@ import java.util.UUID;
|
||||||
* <li>{@link #projectionData}
|
* <li>{@link #projectionData}
|
||||||
* <li>{@link #stereoMode}
|
* <li>{@link #stereoMode}
|
||||||
* <li>{@link #colorInfo}
|
* <li>{@link #colorInfo}
|
||||||
|
* <li>{@link #videoRange}
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* <h2 id="audio-formats">Fields relevant to audio formats</h2>
|
* <h2 id="audio-formats">Fields relevant to audio formats</h2>
|
||||||
|
|
@ -151,6 +154,8 @@ public final class Format {
|
||||||
private int averageBitrate;
|
private int averageBitrate;
|
||||||
private int peakBitrate;
|
private int peakBitrate;
|
||||||
@Nullable private String codecs;
|
@Nullable private String codecs;
|
||||||
|
@Nullable private String supplementalCodecs;
|
||||||
|
@Nullable private String supplementalProfiles;
|
||||||
@Nullable private Metadata metadata;
|
@Nullable private Metadata metadata;
|
||||||
@Nullable private Object customData;
|
@Nullable private Object customData;
|
||||||
|
|
||||||
|
|
@ -178,6 +183,7 @@ public final class Format {
|
||||||
@Nullable private byte[] projectionData;
|
@Nullable private byte[] projectionData;
|
||||||
private @C.StereoMode int stereoMode;
|
private @C.StereoMode int stereoMode;
|
||||||
@Nullable private ColorInfo colorInfo;
|
@Nullable private ColorInfo colorInfo;
|
||||||
|
@Nullable private String videoRange;
|
||||||
|
|
||||||
// Audio specific.
|
// Audio specific.
|
||||||
|
|
||||||
|
|
@ -246,6 +252,8 @@ public final class Format {
|
||||||
this.averageBitrate = format.averageBitrate;
|
this.averageBitrate = format.averageBitrate;
|
||||||
this.peakBitrate = format.peakBitrate;
|
this.peakBitrate = format.peakBitrate;
|
||||||
this.codecs = format.codecs;
|
this.codecs = format.codecs;
|
||||||
|
this.supplementalCodecs = format.supplementalCodecs;
|
||||||
|
this.supplementalProfiles = format.supplementalProfiles;
|
||||||
this.metadata = format.metadata;
|
this.metadata = format.metadata;
|
||||||
this.customData = format.customData;
|
this.customData = format.customData;
|
||||||
// Container specific.
|
// Container specific.
|
||||||
|
|
@ -267,6 +275,7 @@ public final class Format {
|
||||||
this.projectionData = format.projectionData;
|
this.projectionData = format.projectionData;
|
||||||
this.stereoMode = format.stereoMode;
|
this.stereoMode = format.stereoMode;
|
||||||
this.colorInfo = format.colorInfo;
|
this.colorInfo = format.colorInfo;
|
||||||
|
this.videoRange = format.videoRange;
|
||||||
// Audio specific.
|
// Audio specific.
|
||||||
this.channelCount = format.channelCount;
|
this.channelCount = format.channelCount;
|
||||||
this.sampleRate = format.sampleRate;
|
this.sampleRate = format.sampleRate;
|
||||||
|
|
@ -429,6 +438,28 @@ public final class Format {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets {@link Format#supplementalCodecs}. The default value is {@code null}.
|
||||||
|
*
|
||||||
|
* @param supplementalCodecs The {@link Format#supplementalCodecs}.
|
||||||
|
* @return The builder.
|
||||||
|
*/
|
||||||
|
public Builder setSupplementalCodecs(@Nullable String supplementalCodecs) {
|
||||||
|
this.supplementalCodecs = supplementalCodecs;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets {@link Format#supplementalProfiles}. The default value is {@code null}.
|
||||||
|
*
|
||||||
|
* @param supplementalProfiles The {@link Format#supplementalProfiles}.
|
||||||
|
* @return The builder.
|
||||||
|
*/
|
||||||
|
public Builder setSupplementalProfiles(@Nullable String supplementalProfiles) {
|
||||||
|
this.supplementalProfiles = supplementalProfiles;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets {@link Format#metadata}. The default value is {@code null}.
|
* Sets {@link Format#metadata}. The default value is {@code null}.
|
||||||
*
|
*
|
||||||
|
|
@ -655,6 +686,17 @@ public final class Format {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets {@link Format#videoRange}. The default value is {@code null}.
|
||||||
|
*
|
||||||
|
* @param videoRange The {@link Format#videoRange}.
|
||||||
|
* @return The builder.
|
||||||
|
*/
|
||||||
|
public Builder setVideoRange(@Nullable String videoRange) {
|
||||||
|
this.videoRange = videoRange;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
// Audio specific.
|
// Audio specific.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -916,6 +958,12 @@ public final class Format {
|
||||||
/** Codecs of the format as described in RFC 6381, or null if unknown or not applicable. */
|
/** Codecs of the format as described in RFC 6381, or null if unknown or not applicable. */
|
||||||
@Nullable public final String codecs;
|
@Nullable public final String codecs;
|
||||||
|
|
||||||
|
/** The supplemental codecs for compatibility playback, or null if not applicable. */
|
||||||
|
@Nullable public final String supplementalCodecs;
|
||||||
|
|
||||||
|
/** The supplemental profiles for compatibility playback, or null if not applicable. */
|
||||||
|
@Nullable public final String supplementalProfiles;
|
||||||
|
|
||||||
/** Metadata, or null if unknown or not applicable. */
|
/** Metadata, or null if unknown or not applicable. */
|
||||||
@UnstableApi @Nullable public final Metadata metadata;
|
@UnstableApi @Nullable public final Metadata metadata;
|
||||||
|
|
||||||
|
|
@ -1008,6 +1056,9 @@ public final class Format {
|
||||||
/** The color metadata associated with the video, or null if not applicable. */
|
/** The color metadata associated with the video, or null if not applicable. */
|
||||||
@UnstableApi @Nullable public final ColorInfo colorInfo;
|
@UnstableApi @Nullable public final ColorInfo colorInfo;
|
||||||
|
|
||||||
|
/** The reference opto-electronic transfer characteristic functions, or null if not applicable. */
|
||||||
|
@Nullable public final String videoRange;
|
||||||
|
|
||||||
// Audio specific.
|
// Audio specific.
|
||||||
|
|
||||||
/** The number of audio channels, or {@link #NO_VALUE} if unknown or not applicable. */
|
/** The number of audio channels, or {@link #NO_VALUE} if unknown or not applicable. */
|
||||||
|
|
@ -1103,6 +1154,8 @@ public final class Format {
|
||||||
peakBitrate = builder.peakBitrate;
|
peakBitrate = builder.peakBitrate;
|
||||||
bitrate = peakBitrate != NO_VALUE ? peakBitrate : averageBitrate;
|
bitrate = peakBitrate != NO_VALUE ? peakBitrate : averageBitrate;
|
||||||
codecs = builder.codecs;
|
codecs = builder.codecs;
|
||||||
|
supplementalCodecs = builder.supplementalCodecs;
|
||||||
|
supplementalProfiles = builder.supplementalProfiles;
|
||||||
metadata = builder.metadata;
|
metadata = builder.metadata;
|
||||||
customData = builder.customData;
|
customData = builder.customData;
|
||||||
// Container specific.
|
// Container specific.
|
||||||
|
|
@ -1126,6 +1179,7 @@ public final class Format {
|
||||||
projectionData = builder.projectionData;
|
projectionData = builder.projectionData;
|
||||||
stereoMode = builder.stereoMode;
|
stereoMode = builder.stereoMode;
|
||||||
colorInfo = builder.colorInfo;
|
colorInfo = builder.colorInfo;
|
||||||
|
videoRange = builder.videoRange;
|
||||||
// Audio specific.
|
// Audio specific.
|
||||||
channelCount = builder.channelCount;
|
channelCount = builder.channelCount;
|
||||||
sampleRate = builder.sampleRate;
|
sampleRate = builder.sampleRate;
|
||||||
|
|
@ -1190,6 +1244,10 @@ public final class Format {
|
||||||
codecs = codecsOfType;
|
codecs = codecsOfType;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (MimeTypes.VIDEO_DOLBY_VISION.equals(sampleMimeType) &&
|
||||||
|
manifestFormat.supplementalCodecs != null) {
|
||||||
|
codecs = manifestFormat.supplementalCodecs;
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
Metadata metadata =
|
Metadata metadata =
|
||||||
|
|
|
||||||
|
|
@ -399,6 +399,8 @@ public class DashManifestParser extends DefaultHandler
|
||||||
|
|
||||||
String mimeType = xpp.getAttributeValue(null, "mimeType");
|
String mimeType = xpp.getAttributeValue(null, "mimeType");
|
||||||
String codecs = xpp.getAttributeValue(null, "codecs");
|
String codecs = xpp.getAttributeValue(null, "codecs");
|
||||||
|
String supplementalCodecs = xpp.getAttributeValue(null, "scte214:supplementalCodecs");
|
||||||
|
String supplementalProfiles = xpp.getAttributeValue(null, "scte214:supplementalProfiles");
|
||||||
int width = parseInt(xpp, "width", Format.NO_VALUE);
|
int width = parseInt(xpp, "width", Format.NO_VALUE);
|
||||||
int height = parseInt(xpp, "height", Format.NO_VALUE);
|
int height = parseInt(xpp, "height", Format.NO_VALUE);
|
||||||
float frameRate = parseFrameRate(xpp, Format.NO_VALUE);
|
float frameRate = parseFrameRate(xpp, Format.NO_VALUE);
|
||||||
|
|
@ -455,6 +457,8 @@ public class DashManifestParser extends DefaultHandler
|
||||||
!baseUrls.isEmpty() ? baseUrls : parentBaseUrls,
|
!baseUrls.isEmpty() ? baseUrls : parentBaseUrls,
|
||||||
mimeType,
|
mimeType,
|
||||||
codecs,
|
codecs,
|
||||||
|
supplementalCodecs,
|
||||||
|
supplementalProfiles,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
frameRate,
|
frameRate,
|
||||||
|
|
@ -673,6 +677,8 @@ public class DashManifestParser extends DefaultHandler
|
||||||
List<BaseUrl> parentBaseUrls,
|
List<BaseUrl> parentBaseUrls,
|
||||||
@Nullable String adaptationSetMimeType,
|
@Nullable String adaptationSetMimeType,
|
||||||
@Nullable String adaptationSetCodecs,
|
@Nullable String adaptationSetCodecs,
|
||||||
|
@Nullable String adaptationSetSupplementalCodecs,
|
||||||
|
@Nullable String adaptationSetSupplementalProfiles,
|
||||||
int adaptationSetWidth,
|
int adaptationSetWidth,
|
||||||
int adaptationSetHeight,
|
int adaptationSetHeight,
|
||||||
float adaptationSetFrameRate,
|
float adaptationSetFrameRate,
|
||||||
|
|
@ -696,6 +702,10 @@ public class DashManifestParser extends DefaultHandler
|
||||||
|
|
||||||
String mimeType = parseString(xpp, "mimeType", adaptationSetMimeType);
|
String mimeType = parseString(xpp, "mimeType", adaptationSetMimeType);
|
||||||
String codecs = parseString(xpp, "codecs", adaptationSetCodecs);
|
String codecs = parseString(xpp, "codecs", adaptationSetCodecs);
|
||||||
|
String supplementalCodecs =
|
||||||
|
parseString(xpp, "scte214:supplementalCodecs", adaptationSetSupplementalCodecs);
|
||||||
|
String supplementalProfiles =
|
||||||
|
parseString(xpp, "scte214:supplementalProfiles", adaptationSetSupplementalProfiles);
|
||||||
int width = parseInt(xpp, "width", adaptationSetWidth);
|
int width = parseInt(xpp, "width", adaptationSetWidth);
|
||||||
int height = parseInt(xpp, "height", adaptationSetHeight);
|
int height = parseInt(xpp, "height", adaptationSetHeight);
|
||||||
float frameRate = parseFrameRate(xpp, adaptationSetFrameRate);
|
float frameRate = parseFrameRate(xpp, adaptationSetFrameRate);
|
||||||
|
|
@ -781,6 +791,8 @@ public class DashManifestParser extends DefaultHandler
|
||||||
adaptationSetRoleDescriptors,
|
adaptationSetRoleDescriptors,
|
||||||
adaptationSetAccessibilityDescriptors,
|
adaptationSetAccessibilityDescriptors,
|
||||||
codecs,
|
codecs,
|
||||||
|
supplementalCodecs,
|
||||||
|
supplementalProfiles,
|
||||||
essentialProperties,
|
essentialProperties,
|
||||||
supplementalProperties);
|
supplementalProperties);
|
||||||
segmentBase = segmentBase != null ? segmentBase : new SingleSegmentBase();
|
segmentBase = segmentBase != null ? segmentBase : new SingleSegmentBase();
|
||||||
|
|
@ -797,6 +809,27 @@ public class DashManifestParser extends DefaultHandler
|
||||||
Representation.REVISION_ID_DEFAULT);
|
Representation.REVISION_ID_DEFAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected boolean isDolbyVisionFormat(
|
||||||
|
@Nullable String codecs, @Nullable String supplementalCodecs) {
|
||||||
|
if (codecs == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (codecs.startsWith("dvhe") || codecs.startsWith("dvh1")) {
|
||||||
|
// profile 5
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (supplementalCodecs == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (supplementalCodecs.startsWith("dvhe") && codecs.startsWith("hev1")) || // profile 8
|
||||||
|
(supplementalCodecs.startsWith("dvh1") && codecs.startsWith("hvc1")) || // profile 8
|
||||||
|
(supplementalCodecs.startsWith("dvav") && codecs.startsWith("avc3")) || // profile 9
|
||||||
|
(supplementalCodecs.startsWith("dva1") && codecs.startsWith("avc1")) || // profile 9
|
||||||
|
(supplementalCodecs.startsWith("dav1") && codecs.startsWith("av01")); // profile 10
|
||||||
|
}
|
||||||
|
|
||||||
protected Format buildFormat(
|
protected Format buildFormat(
|
||||||
@Nullable String id,
|
@Nullable String id,
|
||||||
@Nullable String containerMimeType,
|
@Nullable String containerMimeType,
|
||||||
|
|
@ -810,6 +843,8 @@ public class DashManifestParser extends DefaultHandler
|
||||||
List<Descriptor> roleDescriptors,
|
List<Descriptor> roleDescriptors,
|
||||||
List<Descriptor> accessibilityDescriptors,
|
List<Descriptor> accessibilityDescriptors,
|
||||||
@Nullable String codecs,
|
@Nullable String codecs,
|
||||||
|
@Nullable String supplementalCodecs,
|
||||||
|
@Nullable String supplementalProfiles,
|
||||||
List<Descriptor> essentialProperties,
|
List<Descriptor> essentialProperties,
|
||||||
List<Descriptor> supplementalProperties) {
|
List<Descriptor> supplementalProperties) {
|
||||||
@Nullable String sampleMimeType = getSampleMimeType(containerMimeType, codecs);
|
@Nullable String sampleMimeType = getSampleMimeType(containerMimeType, codecs);
|
||||||
|
|
@ -819,6 +854,10 @@ public class DashManifestParser extends DefaultHandler
|
||||||
codecs = MimeTypes.CODEC_E_AC3_JOC;
|
codecs = MimeTypes.CODEC_E_AC3_JOC;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (isDolbyVisionFormat(codecs, supplementalCodecs)) {
|
||||||
|
sampleMimeType = MimeTypes.VIDEO_DOLBY_VISION;
|
||||||
|
codecs = supplementalCodecs != null ? supplementalCodecs : codecs;
|
||||||
|
}
|
||||||
@C.SelectionFlags int selectionFlags = parseSelectionFlagsFromRoleDescriptors(roleDescriptors);
|
@C.SelectionFlags int selectionFlags = parseSelectionFlagsFromRoleDescriptors(roleDescriptors);
|
||||||
@C.RoleFlags int roleFlags = parseRoleFlagsFromRoleDescriptors(roleDescriptors);
|
@C.RoleFlags int roleFlags = parseRoleFlagsFromRoleDescriptors(roleDescriptors);
|
||||||
roleFlags |= parseRoleFlagsFromAccessibilityDescriptors(accessibilityDescriptors);
|
roleFlags |= parseRoleFlagsFromAccessibilityDescriptors(accessibilityDescriptors);
|
||||||
|
|
|
||||||
|
|
@ -847,15 +847,59 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
return drmInitDataBySchemeType;
|
return drmInitDataBySchemeType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean isDolbyVisionFormat(
|
||||||
|
@Nullable String videoRange,
|
||||||
|
@Nullable String codecs,
|
||||||
|
@Nullable String supplementalCodecs,
|
||||||
|
@Nullable String supplementalProfiles) {
|
||||||
|
if (codecs == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (codecs.startsWith("dvhe") || codecs.startsWith("dvh1")) {
|
||||||
|
// profile 5
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (supplementalCodecs == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// For Dolby Vision, the compatibility brand (i.e. supplemental profiles) and the VIDEO-RANGE
|
||||||
|
// attribute act as cross-checks. Leaving out either one is incorrect.
|
||||||
|
if (videoRange == null || supplementalProfiles == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ((videoRange.equals("PQ") && !supplementalProfiles.equals("db1p")) ||
|
||||||
|
(videoRange.equals("SDR") && !supplementalProfiles.equals("db2g")) ||
|
||||||
|
(videoRange.equals("HLG") && !supplementalProfiles.startsWith("db4"))) { // db4g or db4h
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (supplementalCodecs.startsWith("dvhe") && codecs.startsWith("hev1")) || // profile 8
|
||||||
|
(supplementalCodecs.startsWith("dvh1") && codecs.startsWith("hvc1")) || // profile 8
|
||||||
|
(supplementalCodecs.startsWith("dvav") && codecs.startsWith("avc3")) || // profile 9
|
||||||
|
(supplementalCodecs.startsWith("dva1") && codecs.startsWith("avc1")) || // profile 9
|
||||||
|
(supplementalCodecs.startsWith("dav1") && codecs.startsWith("av01")); // profile 10
|
||||||
|
}
|
||||||
|
|
||||||
private static Format deriveVideoFormat(Format variantFormat) {
|
private static Format deriveVideoFormat(Format variantFormat) {
|
||||||
|
@Nullable String videoRange = variantFormat.videoRange;
|
||||||
@Nullable String codecs = Util.getCodecsOfType(variantFormat.codecs, C.TRACK_TYPE_VIDEO);
|
@Nullable String codecs = Util.getCodecsOfType(variantFormat.codecs, C.TRACK_TYPE_VIDEO);
|
||||||
|
@Nullable String supplementalCodecs = variantFormat.supplementalCodecs;
|
||||||
|
@Nullable String supplementalProfiles = variantFormat.supplementalProfiles;
|
||||||
@Nullable String sampleMimeType = MimeTypes.getMediaMimeType(codecs);
|
@Nullable String sampleMimeType = MimeTypes.getMediaMimeType(codecs);
|
||||||
|
|
||||||
|
if (isDolbyVisionFormat(videoRange, codecs, supplementalCodecs, supplementalProfiles)) {
|
||||||
|
sampleMimeType = MimeTypes.VIDEO_DOLBY_VISION;
|
||||||
|
codecs = supplementalCodecs != null ? supplementalCodecs : codecs;
|
||||||
|
}
|
||||||
|
|
||||||
return new Format.Builder()
|
return new Format.Builder()
|
||||||
.setId(variantFormat.id)
|
.setId(variantFormat.id)
|
||||||
.setLabel(variantFormat.label)
|
.setLabel(variantFormat.label)
|
||||||
.setLabels(variantFormat.labels)
|
.setLabels(variantFormat.labels)
|
||||||
.setContainerMimeType(variantFormat.containerMimeType)
|
.setContainerMimeType(variantFormat.containerMimeType)
|
||||||
.setSampleMimeType(sampleMimeType)
|
.setSampleMimeType(sampleMimeType)
|
||||||
|
.setVideoRange(videoRange)
|
||||||
.setCodecs(codecs)
|
.setCodecs(codecs)
|
||||||
.setMetadata(variantFormat.metadata)
|
.setMetadata(variantFormat.metadata)
|
||||||
.setAverageBitrate(variantFormat.averageBitrate)
|
.setAverageBitrate(variantFormat.averageBitrate)
|
||||||
|
|
|
||||||
|
|
@ -138,7 +138,10 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
|
||||||
private static final Pattern REGEX_CLOSED_CAPTIONS = Pattern.compile("CLOSED-CAPTIONS=\"(.+?)\"");
|
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=\"(.+?)\"");
|
||||||
|
// VIDEO-RANGE attribute: https://datatracker.ietf.org/doc/html/draft-pantos-hls-rfc8216bis-15
|
||||||
|
private static final Pattern REGEX_VIDEO_RANGE = Pattern.compile("VIDEO-RANGE=(SDR|PQ|HLG)");
|
||||||
private static final Pattern REGEX_CODECS = Pattern.compile("CODECS=\"(.+?)\"");
|
private static final Pattern REGEX_CODECS = Pattern.compile("CODECS=\"(.+?)\"");
|
||||||
|
private static final Pattern REGEX_SUPPLEMENTAL_CODECS = Pattern.compile("SUPPLEMENTAL-CODECS=\"(.+?)\"");
|
||||||
private static final Pattern REGEX_RESOLUTION = Pattern.compile("RESOLUTION=(\\d+x\\d+)");
|
private static final Pattern REGEX_RESOLUTION = Pattern.compile("RESOLUTION=(\\d+x\\d+)");
|
||||||
private static final Pattern REGEX_FRAME_RATE = Pattern.compile("FRAME-RATE=([\\d\\.]+)\\b");
|
private static final Pattern REGEX_FRAME_RATE = Pattern.compile("FRAME-RATE=([\\d\\.]+)\\b");
|
||||||
private static final Pattern REGEX_TARGET_DURATION =
|
private static final Pattern REGEX_TARGET_DURATION =
|
||||||
|
|
@ -374,7 +377,23 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
|
||||||
int roleFlags = isIFrameOnlyVariant ? C.ROLE_FLAG_TRICK_PLAY : 0;
|
int roleFlags = isIFrameOnlyVariant ? C.ROLE_FLAG_TRICK_PLAY : 0;
|
||||||
int peakBitrate = parseIntAttr(line, REGEX_BANDWIDTH);
|
int peakBitrate = parseIntAttr(line, REGEX_BANDWIDTH);
|
||||||
int averageBitrate = parseOptionalIntAttr(line, REGEX_AVERAGE_BANDWIDTH, -1);
|
int averageBitrate = parseOptionalIntAttr(line, REGEX_AVERAGE_BANDWIDTH, -1);
|
||||||
|
String videoRange = parseOptionalStringAttr(line, REGEX_VIDEO_RANGE, variableDefinitions);
|
||||||
String codecs = parseOptionalStringAttr(line, REGEX_CODECS, variableDefinitions);
|
String codecs = parseOptionalStringAttr(line, REGEX_CODECS, variableDefinitions);
|
||||||
|
String supplementalCodecsStrings =
|
||||||
|
parseOptionalStringAttr(line, REGEX_SUPPLEMENTAL_CODECS, variableDefinitions);
|
||||||
|
String supplementalCodecs = null;
|
||||||
|
String supplementalProfiles = null; // i.e. Compatibility brand
|
||||||
|
if (supplementalCodecsStrings != null) {
|
||||||
|
String[] supplementalCodecsString = Util.split(supplementalCodecsStrings, ",");
|
||||||
|
if (!supplementalCodecsString[0].isEmpty()) {
|
||||||
|
// TODO: Support more than one element
|
||||||
|
String[] codecsAndProfiles = Util.split(supplementalCodecsString[0], "/");
|
||||||
|
supplementalCodecs = codecsAndProfiles[0];
|
||||||
|
if (codecsAndProfiles.length > 1) {
|
||||||
|
supplementalProfiles = codecsAndProfiles[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
String resolutionString =
|
String resolutionString =
|
||||||
parseOptionalStringAttr(line, REGEX_RESOLUTION, variableDefinitions);
|
parseOptionalStringAttr(line, REGEX_RESOLUTION, variableDefinitions);
|
||||||
int width;
|
int width;
|
||||||
|
|
@ -421,7 +440,10 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
|
||||||
new Format.Builder()
|
new Format.Builder()
|
||||||
.setId(variants.size())
|
.setId(variants.size())
|
||||||
.setContainerMimeType(MimeTypes.APPLICATION_M3U8)
|
.setContainerMimeType(MimeTypes.APPLICATION_M3U8)
|
||||||
|
.setVideoRange(videoRange)
|
||||||
.setCodecs(codecs)
|
.setCodecs(codecs)
|
||||||
|
.setSupplementalCodecs(supplementalCodecs)
|
||||||
|
.setSupplementalProfiles(supplementalProfiles)
|
||||||
.setAverageBitrate(averageBitrate)
|
.setAverageBitrate(averageBitrate)
|
||||||
.setPeakBitrate(peakBitrate)
|
.setPeakBitrate(peakBitrate)
|
||||||
.setWidth(width)
|
.setWidth(width)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue