mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Merge pull request #1785 from DolbyLaboratories:dlb/dovi-supplemental-codecs/dev
PiperOrigin-RevId: 712867412
This commit is contained in:
commit
38363acc8d
@ -42,10 +42,15 @@
|
|||||||
* Cronet Extension:
|
* Cronet Extension:
|
||||||
* RTMP Extension:
|
* RTMP Extension:
|
||||||
* HLS Extension:
|
* HLS Extension:
|
||||||
|
* Parse `SUPPLEMENTAL-CODECS` tag from HLS playlist to detect Dolby Vision
|
||||||
|
formats ([#1785](https://github.com/androidx/media/pull/1785)).
|
||||||
* DASH Extension:
|
* DASH Extension:
|
||||||
* Fix issue when calculating the update interval for ad insertion in
|
* Fix issue when calculating the update interval for ad insertion in
|
||||||
multi-period live streams
|
multi-period live streams
|
||||||
([#1698](https://github.com/androidx/media/issues/1698)).
|
([#1698](https://github.com/androidx/media/issues/1698)).
|
||||||
|
* Parse `scte214:supplementalCodecs` attribute from DASH manifest to
|
||||||
|
detect Dolby Vision formats
|
||||||
|
([#1785](https://github.com/androidx/media/pull/1785)).
|
||||||
* Smooth Streaming Extension:
|
* Smooth Streaming Extension:
|
||||||
* RTSP Extension:
|
* RTSP Extension:
|
||||||
* Decoder Extensions (FFmpeg, VP9, AV1, etc.):
|
* Decoder Extensions (FFmpeg, VP9, AV1, etc.):
|
||||||
|
@ -580,6 +580,36 @@ public final class MimeTypes {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the given {@code codecs} and {@code supplementalCodecs} correspond to a valid
|
||||||
|
* Dolby Vision codec.
|
||||||
|
*
|
||||||
|
* @param codecs An RFC 6381 codecs string for the base codec. may be null.
|
||||||
|
* @param supplementalCodecs An optional RFC 6381 codecs string for supplemental codecs.
|
||||||
|
* @return Whether the given {@code codecs} and {@code supplementalCodecs} correspond to a valid
|
||||||
|
* Dolby Vision codec.
|
||||||
|
*/
|
||||||
|
@UnstableApi
|
||||||
|
public static boolean isDolbyVisionCodec(
|
||||||
|
@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;
|
||||||
|
}
|
||||||
|
// profiles 8, 9 and 10
|
||||||
|
return (supplementalCodecs.startsWith("dvhe") && codecs.startsWith("hev1"))
|
||||||
|
|| (supplementalCodecs.startsWith("dvh1") && codecs.startsWith("hvc1"))
|
||||||
|
|| (supplementalCodecs.startsWith("dvav") && codecs.startsWith("avc3"))
|
||||||
|
|| (supplementalCodecs.startsWith("dva1") && codecs.startsWith("avc1"))
|
||||||
|
|| (supplementalCodecs.startsWith("dav1") && codecs.startsWith("av01"));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the {@link C.TrackType track type} constant corresponding to a specified MIME type,
|
* Returns the {@link C.TrackType track type} constant corresponding to a specified MIME type,
|
||||||
* which may be {@link C#TRACK_TYPE_UNKNOWN} if it could not be determined.
|
* which may be {@link C#TRACK_TYPE_UNKNOWN} if it could not be determined.
|
||||||
|
@ -2125,6 +2125,33 @@ public final class Util {
|
|||||||
return builder.length() > 0 ? builder.toString() : null;
|
return builder.length() > 0 ? builder.toString() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a copy of {@code codecs} without the codecs whose track type matches {@code trackType}.
|
||||||
|
*
|
||||||
|
* @param codecs A codec sequence string, as defined in RFC 6381.
|
||||||
|
* @param trackType The {@link C.TrackType track type}.
|
||||||
|
* @return A copy of {@code codecs} without the codecs whose track type matches {@code trackType}.
|
||||||
|
* If this ends up empty, or {@code codecs} is null, returns null.
|
||||||
|
*/
|
||||||
|
@UnstableApi
|
||||||
|
@Nullable
|
||||||
|
public static String getCodecsWithoutType(@Nullable String codecs, @C.TrackType int trackType) {
|
||||||
|
String[] codecArray = splitCodecs(codecs);
|
||||||
|
if (codecArray.length == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
for (String codec : codecArray) {
|
||||||
|
if (trackType != MimeTypes.getTrackTypeOfCodec(codec)) {
|
||||||
|
if (builder.length() > 0) {
|
||||||
|
builder.append(",");
|
||||||
|
}
|
||||||
|
builder.append(codec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return builder.length() > 0 ? builder.toString() : null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Splits a codecs sequence string, as defined in RFC 6381, into individual codec strings.
|
* Splits a codecs sequence string, as defined in RFC 6381, into individual codec strings.
|
||||||
*
|
*
|
||||||
|
@ -414,6 +414,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);
|
||||||
@ -470,6 +472,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,
|
||||||
@ -688,6 +692,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,
|
||||||
@ -711,6 +717,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);
|
||||||
@ -796,6 +806,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();
|
||||||
@ -825,6 +837,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);
|
||||||
@ -834,6 +848,10 @@ public class DashManifestParser extends DefaultHandler
|
|||||||
codecs = MimeTypes.CODEC_E_AC3_JOC;
|
codecs = MimeTypes.CODEC_E_AC3_JOC;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (MimeTypes.isDolbyVisionCodec(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);
|
||||||
|
@ -146,7 +146,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=\"(.+?)\"");
|
||||||
|
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 =
|
||||||
@ -353,6 +356,30 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
|
|||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean isDolbyVisionFormat(
|
||||||
|
@Nullable String videoRange,
|
||||||
|
@Nullable String codecs,
|
||||||
|
@Nullable String supplementalCodecs,
|
||||||
|
@Nullable String supplementalProfiles) {
|
||||||
|
if (!MimeTypes.isDolbyVisionCodec(codecs, supplementalCodecs)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (supplementalCodecs == null) {
|
||||||
|
// Dolby Vision profile 5 that doesn't define supplemental codecs.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (videoRange == null || supplementalProfiles == null) {
|
||||||
|
// Video range and supplemental profiles need to be defined for a full validity check.
|
||||||
|
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 true;
|
||||||
|
}
|
||||||
|
|
||||||
private static HlsMultivariantPlaylist parseMultivariantPlaylist(
|
private static HlsMultivariantPlaylist parseMultivariantPlaylist(
|
||||||
LineIterator iterator, String baseUri) throws IOException {
|
LineIterator iterator, String baseUri) throws IOException {
|
||||||
HashMap<Uri, ArrayList<VariantInfo>> urlToVariantInfos = new HashMap<>();
|
HashMap<Uri, ArrayList<VariantInfo>> urlToVariantInfos = new HashMap<>();
|
||||||
@ -404,7 +431,29 @@ 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.splitAtFirst(supplementalCodecsStrings, ",");
|
||||||
|
// TODO: Support more than one element
|
||||||
|
String[] codecsAndProfiles = Util.split(supplementalCodecsString[0], "/");
|
||||||
|
supplementalCodecs = codecsAndProfiles[0];
|
||||||
|
if (codecsAndProfiles.length > 1) {
|
||||||
|
supplementalProfiles = codecsAndProfiles[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String videoCodecs = Util.getCodecsOfType(codecs, C.TRACK_TYPE_VIDEO);
|
||||||
|
if (isDolbyVisionFormat(
|
||||||
|
videoRange, videoCodecs, supplementalCodecs, supplementalProfiles)) {
|
||||||
|
videoCodecs = supplementalCodecs != null ? supplementalCodecs : videoCodecs;
|
||||||
|
String nonVideoCodecs = Util.getCodecsWithoutType(codecs, C.TRACK_TYPE_VIDEO);
|
||||||
|
codecs = nonVideoCodecs != null ? videoCodecs + "," + nonVideoCodecs : videoCodecs;
|
||||||
|
}
|
||||||
|
|
||||||
String resolutionString =
|
String resolutionString =
|
||||||
parseOptionalStringAttr(line, REGEX_RESOLUTION, variableDefinitions);
|
parseOptionalStringAttr(line, REGEX_RESOLUTION, variableDefinitions);
|
||||||
int width;
|
int width;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user