From 79078c390d02fe882710d8574410c896ad76de9c Mon Sep 17 00:00:00 2001 From: ybai001 Date: Tue, 8 Oct 2024 10:07:43 +0800 Subject: [PATCH 1/5] Add SUPPLEMENTAL-CODECS support in both DASH and HLS --- .../java/androidx/media3/common/Format.java | 58 +++++++++++++++++++ .../dash/manifest/DashManifestParser.java | 39 +++++++++++++ .../media3/exoplayer/hls/HlsMediaPeriod.java | 44 ++++++++++++++ .../hls/playlist/HlsPlaylistParser.java | 22 +++++++ 4 files changed, 163 insertions(+) diff --git a/libraries/common/src/main/java/androidx/media3/common/Format.java b/libraries/common/src/main/java/androidx/media3/common/Format.java index e5b9bc29fc..4ff1e36876 100644 --- a/libraries/common/src/main/java/androidx/media3/common/Format.java +++ b/libraries/common/src/main/java/androidx/media3/common/Format.java @@ -62,6 +62,8 @@ import java.util.UUID; *
  • {@link #averageBitrate} *
  • {@link #peakBitrate} *
  • {@link #codecs} + *
  • {@link #supplementalCodecs} + *
  • {@link #supplementalProfiles} *
  • {@link #metadata} * * @@ -102,6 +104,7 @@ import java.util.UUID; *
  • {@link #projectionData} *
  • {@link #stereoMode} *
  • {@link #colorInfo} + *
  • {@link #videoRange} * * *

    Fields relevant to audio formats

    @@ -151,6 +154,8 @@ public final class Format { private int averageBitrate; private int peakBitrate; @Nullable private String codecs; + @Nullable private String supplementalCodecs; + @Nullable private String supplementalProfiles; @Nullable private Metadata metadata; @Nullable private Object customData; @@ -178,6 +183,7 @@ public final class Format { @Nullable private byte[] projectionData; private @C.StereoMode int stereoMode; @Nullable private ColorInfo colorInfo; + @Nullable private String videoRange; // Audio specific. @@ -246,6 +252,8 @@ public final class Format { this.averageBitrate = format.averageBitrate; this.peakBitrate = format.peakBitrate; this.codecs = format.codecs; + this.supplementalCodecs = format.supplementalCodecs; + this.supplementalProfiles = format.supplementalProfiles; this.metadata = format.metadata; this.customData = format.customData; // Container specific. @@ -267,6 +275,7 @@ public final class Format { this.projectionData = format.projectionData; this.stereoMode = format.stereoMode; this.colorInfo = format.colorInfo; + this.videoRange = format.videoRange; // Audio specific. this.channelCount = format.channelCount; this.sampleRate = format.sampleRate; @@ -429,6 +438,28 @@ public final class Format { 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}. * @@ -655,6 +686,17 @@ public final class Format { 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. /** @@ -916,6 +958,12 @@ public final class Format { /** Codecs of the format as described in RFC 6381, or null if unknown or not applicable. */ @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. */ @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. */ @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. /** 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; bitrate = peakBitrate != NO_VALUE ? peakBitrate : averageBitrate; codecs = builder.codecs; + supplementalCodecs = builder.supplementalCodecs; + supplementalProfiles = builder.supplementalProfiles; metadata = builder.metadata; customData = builder.customData; // Container specific. @@ -1126,6 +1179,7 @@ public final class Format { projectionData = builder.projectionData; stereoMode = builder.stereoMode; colorInfo = builder.colorInfo; + videoRange = builder.videoRange; // Audio specific. channelCount = builder.channelCount; sampleRate = builder.sampleRate; @@ -1190,6 +1244,10 @@ public final class Format { codecs = codecsOfType; } } + if (MimeTypes.VIDEO_DOLBY_VISION.equals(sampleMimeType) && + manifestFormat.supplementalCodecs != null) { + codecs = manifestFormat.supplementalCodecs; + } @Nullable Metadata metadata = diff --git a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/manifest/DashManifestParser.java b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/manifest/DashManifestParser.java index 8caff5bd92..735d03bc0c 100644 --- a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/manifest/DashManifestParser.java +++ b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/manifest/DashManifestParser.java @@ -399,6 +399,8 @@ public class DashManifestParser extends DefaultHandler String mimeType = xpp.getAttributeValue(null, "mimeType"); 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 height = parseInt(xpp, "height", Format.NO_VALUE); float frameRate = parseFrameRate(xpp, Format.NO_VALUE); @@ -455,6 +457,8 @@ public class DashManifestParser extends DefaultHandler !baseUrls.isEmpty() ? baseUrls : parentBaseUrls, mimeType, codecs, + supplementalCodecs, + supplementalProfiles, width, height, frameRate, @@ -673,6 +677,8 @@ public class DashManifestParser extends DefaultHandler List parentBaseUrls, @Nullable String adaptationSetMimeType, @Nullable String adaptationSetCodecs, + @Nullable String adaptationSetSupplementalCodecs, + @Nullable String adaptationSetSupplementalProfiles, int adaptationSetWidth, int adaptationSetHeight, float adaptationSetFrameRate, @@ -696,6 +702,10 @@ public class DashManifestParser extends DefaultHandler String mimeType = parseString(xpp, "mimeType", adaptationSetMimeType); 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 height = parseInt(xpp, "height", adaptationSetHeight); float frameRate = parseFrameRate(xpp, adaptationSetFrameRate); @@ -781,6 +791,8 @@ public class DashManifestParser extends DefaultHandler adaptationSetRoleDescriptors, adaptationSetAccessibilityDescriptors, codecs, + supplementalCodecs, + supplementalProfiles, essentialProperties, supplementalProperties); segmentBase = segmentBase != null ? segmentBase : new SingleSegmentBase(); @@ -797,6 +809,27 @@ public class DashManifestParser extends DefaultHandler 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( @Nullable String id, @Nullable String containerMimeType, @@ -810,6 +843,8 @@ public class DashManifestParser extends DefaultHandler List roleDescriptors, List accessibilityDescriptors, @Nullable String codecs, + @Nullable String supplementalCodecs, + @Nullable String supplementalProfiles, List essentialProperties, List supplementalProperties) { @Nullable String sampleMimeType = getSampleMimeType(containerMimeType, codecs); @@ -819,6 +854,10 @@ public class DashManifestParser extends DefaultHandler 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.RoleFlags int roleFlags = parseRoleFlagsFromRoleDescriptors(roleDescriptors); roleFlags |= parseRoleFlagsFromAccessibilityDescriptors(accessibilityDescriptors); diff --git a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsMediaPeriod.java b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsMediaPeriod.java index cfb7e82525..8ef3bb7629 100644 --- a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsMediaPeriod.java +++ b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsMediaPeriod.java @@ -847,15 +847,59 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; 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) { + @Nullable String videoRange = variantFormat.videoRange; @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); + + if (isDolbyVisionFormat(videoRange, codecs, supplementalCodecs, supplementalProfiles)) { + sampleMimeType = MimeTypes.VIDEO_DOLBY_VISION; + codecs = supplementalCodecs != null ? supplementalCodecs : codecs; + } + return new Format.Builder() .setId(variantFormat.id) .setLabel(variantFormat.label) .setLabels(variantFormat.labels) .setContainerMimeType(variantFormat.containerMimeType) .setSampleMimeType(sampleMimeType) + .setVideoRange(videoRange) .setCodecs(codecs) .setMetadata(variantFormat.metadata) .setAverageBitrate(variantFormat.averageBitrate) diff --git a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/HlsPlaylistParser.java b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/HlsPlaylistParser.java index 8dc60984f2..95cdcc212f 100644 --- a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/HlsPlaylistParser.java +++ b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/HlsPlaylistParser.java @@ -138,7 +138,10 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser 1) { + supplementalProfiles = codecsAndProfiles[1]; + } + } + } String resolutionString = parseOptionalStringAttr(line, REGEX_RESOLUTION, variableDefinitions); int width; @@ -421,7 +440,10 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser Date: Mon, 14 Oct 2024 13:51:40 +0800 Subject: [PATCH 2/5] Remove the modification on Format According to Google's comments, removing the modification on Format.java since it is redundant. --- .../java/androidx/media3/common/Format.java | 59 +------------------ .../media3/exoplayer/hls/HlsMediaPeriod.java | 43 -------------- .../hls/playlist/HlsPlaylistParser.java | 41 ++++++++++++- 3 files changed, 40 insertions(+), 103 deletions(-) diff --git a/libraries/common/src/main/java/androidx/media3/common/Format.java b/libraries/common/src/main/java/androidx/media3/common/Format.java index 4ff1e36876..72c9f47b2d 100644 --- a/libraries/common/src/main/java/androidx/media3/common/Format.java +++ b/libraries/common/src/main/java/androidx/media3/common/Format.java @@ -62,8 +62,6 @@ import java.util.UUID; *
  • {@link #averageBitrate} *
  • {@link #peakBitrate} *
  • {@link #codecs} - *
  • {@link #supplementalCodecs} - *
  • {@link #supplementalProfiles} *
  • {@link #metadata} * * @@ -104,7 +102,6 @@ import java.util.UUID; *
  • {@link #projectionData} *
  • {@link #stereoMode} *
  • {@link #colorInfo} - *
  • {@link #videoRange} * * *

    Fields relevant to audio formats

    @@ -154,8 +151,6 @@ public final class Format { private int averageBitrate; private int peakBitrate; @Nullable private String codecs; - @Nullable private String supplementalCodecs; - @Nullable private String supplementalProfiles; @Nullable private Metadata metadata; @Nullable private Object customData; @@ -183,7 +178,6 @@ public final class Format { @Nullable private byte[] projectionData; private @C.StereoMode int stereoMode; @Nullable private ColorInfo colorInfo; - @Nullable private String videoRange; // Audio specific. @@ -252,8 +246,6 @@ public final class Format { this.averageBitrate = format.averageBitrate; this.peakBitrate = format.peakBitrate; this.codecs = format.codecs; - this.supplementalCodecs = format.supplementalCodecs; - this.supplementalProfiles = format.supplementalProfiles; this.metadata = format.metadata; this.customData = format.customData; // Container specific. @@ -275,7 +267,6 @@ public final class Format { this.projectionData = format.projectionData; this.stereoMode = format.stereoMode; this.colorInfo = format.colorInfo; - this.videoRange = format.videoRange; // Audio specific. this.channelCount = format.channelCount; this.sampleRate = format.sampleRate; @@ -438,28 +429,6 @@ public final class Format { 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}. * @@ -686,17 +655,6 @@ public final class Format { 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. /** @@ -958,12 +916,6 @@ public final class Format { /** Codecs of the format as described in RFC 6381, or null if unknown or not applicable. */ @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. */ @UnstableApi @Nullable public final Metadata metadata; @@ -1056,9 +1008,6 @@ public final class Format { /** The color metadata associated with the video, or null if not applicable. */ @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. /** The number of audio channels, or {@link #NO_VALUE} if unknown or not applicable. */ @@ -1154,8 +1103,6 @@ public final class Format { peakBitrate = builder.peakBitrate; bitrate = peakBitrate != NO_VALUE ? peakBitrate : averageBitrate; codecs = builder.codecs; - supplementalCodecs = builder.supplementalCodecs; - supplementalProfiles = builder.supplementalProfiles; metadata = builder.metadata; customData = builder.customData; // Container specific. @@ -1179,7 +1126,6 @@ public final class Format { projectionData = builder.projectionData; stereoMode = builder.stereoMode; colorInfo = builder.colorInfo; - videoRange = builder.videoRange; // Audio specific. channelCount = builder.channelCount; sampleRate = builder.sampleRate; @@ -1244,9 +1190,8 @@ public final class Format { codecs = codecsOfType; } } - if (MimeTypes.VIDEO_DOLBY_VISION.equals(sampleMimeType) && - manifestFormat.supplementalCodecs != null) { - codecs = manifestFormat.supplementalCodecs; + if (MimeTypes.VIDEO_DOLBY_VISION.equals(sampleMimeType)) { + codecs = manifestFormat.codecs; } @Nullable diff --git a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsMediaPeriod.java b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsMediaPeriod.java index 8ef3bb7629..2832442c7f 100644 --- a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsMediaPeriod.java +++ b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsMediaPeriod.java @@ -847,59 +847,16 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; 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) { - @Nullable String videoRange = variantFormat.videoRange; @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); - if (isDolbyVisionFormat(videoRange, codecs, supplementalCodecs, supplementalProfiles)) { - sampleMimeType = MimeTypes.VIDEO_DOLBY_VISION; - codecs = supplementalCodecs != null ? supplementalCodecs : codecs; - } - return new Format.Builder() .setId(variantFormat.id) .setLabel(variantFormat.label) .setLabels(variantFormat.labels) .setContainerMimeType(variantFormat.containerMimeType) .setSampleMimeType(sampleMimeType) - .setVideoRange(videoRange) .setCodecs(codecs) .setMetadata(variantFormat.metadata) .setAverageBitrate(variantFormat.averageBitrate) diff --git a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/HlsPlaylistParser.java b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/HlsPlaylistParser.java index 95cdcc212f..7b8e29384e 100644 --- a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/HlsPlaylistParser.java +++ b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/HlsPlaylistParser.java @@ -326,6 +326,40 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser> urlToVariantInfos = new HashMap<>(); @@ -394,6 +428,10 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser Date: Mon, 14 Oct 2024 13:55:06 +0800 Subject: [PATCH 3/5] Remove redundant blank line Remove redundant blank line. --- .../main/java/androidx/media3/exoplayer/hls/HlsMediaPeriod.java | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsMediaPeriod.java b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsMediaPeriod.java index 2832442c7f..cfb7e82525 100644 --- a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsMediaPeriod.java +++ b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsMediaPeriod.java @@ -850,7 +850,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; private static Format deriveVideoFormat(Format variantFormat) { @Nullable String codecs = Util.getCodecsOfType(variantFormat.codecs, C.TRACK_TYPE_VIDEO); @Nullable String sampleMimeType = MimeTypes.getMediaMimeType(codecs); - return new Format.Builder() .setId(variantFormat.id) .setLabel(variantFormat.label) From 545d37c104e24b2b7daf3f5978c7a3cdb9d9701f Mon Sep 17 00:00:00 2001 From: ybai001 Date: Mon, 14 Oct 2024 17:25:36 +0800 Subject: [PATCH 4/5] Fix two bugs in the updated code If we handle the "codecs" in HlsPlaylistParser.java rather than in HlsMediaPeriod.java, since the "codecs" property includes both audio and video codec names here, we can't update it directly. Only video part should be updated by Dolby codec string. --- .../main/java/androidx/media3/common/Format.java | 2 +- .../hls/playlist/HlsPlaylistParser.java | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/libraries/common/src/main/java/androidx/media3/common/Format.java b/libraries/common/src/main/java/androidx/media3/common/Format.java index 72c9f47b2d..8797f764a1 100644 --- a/libraries/common/src/main/java/androidx/media3/common/Format.java +++ b/libraries/common/src/main/java/androidx/media3/common/Format.java @@ -1191,7 +1191,7 @@ public final class Format { } } if (MimeTypes.VIDEO_DOLBY_VISION.equals(sampleMimeType)) { - codecs = manifestFormat.codecs; + codecs = Util.getCodecsOfType(manifestFormat.codecs, C.TRACK_TYPE_VIDEO); } @Nullable diff --git a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/HlsPlaylistParser.java b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/HlsPlaylistParser.java index 7b8e29384e..22142445bd 100644 --- a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/HlsPlaylistParser.java +++ b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/HlsPlaylistParser.java @@ -428,8 +428,20 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser 0) { + builder.append(","); + } + builder.append(MimeTypes.getTrackTypeOfCodec(codec) == C.TRACK_TYPE_VIDEO + ? videoCodecs + : codec); + } + codecs = builder.length() > 0 ? builder.toString() : codecs; } String resolutionString = From 1ac847ad60f0eeb3a88bd38acc2c3b5a1db1e391 Mon Sep 17 00:00:00 2001 From: ybai001 Date: Thu, 19 Dec 2024 16:28:33 +0800 Subject: [PATCH 5/5] Update code according to review result --- .../androidx/media3/common/util/Util.java | 28 +++++++++++++++++++ .../hls/playlist/HlsPlaylistParser.java | 27 ++++++------------ 2 files changed, 36 insertions(+), 19 deletions(-) diff --git a/libraries/common/src/main/java/androidx/media3/common/util/Util.java b/libraries/common/src/main/java/androidx/media3/common/util/Util.java index a87dd46b93..4bde64b95f 100644 --- a/libraries/common/src/main/java/androidx/media3/common/util/Util.java +++ b/libraries/common/src/main/java/androidx/media3/common/util/Util.java @@ -2115,6 +2115,34 @@ public final class Util { 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. * diff --git a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/HlsPlaylistParser.java b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/HlsPlaylistParser.java index 22142445bd..0bae8d8c7b 100644 --- a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/HlsPlaylistParser.java +++ b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/playlist/HlsPlaylistParser.java @@ -418,30 +418,19 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser 1) { - supplementalProfiles = codecsAndProfiles[1]; - } + 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[] codecArray = Util.splitCodecs(codecs); - StringBuilder builder = new StringBuilder(); - for (String codec : codecArray) { - if (builder.length() > 0) { - builder.append(","); - } - builder.append(MimeTypes.getTrackTypeOfCodec(codec) == C.TRACK_TYPE_VIDEO - ? videoCodecs - : codec); - } - codecs = builder.length() > 0 ? builder.toString() : codecs; + String nonVideoCodecs = Util.getCodecsWithoutType(codecs, C.TRACK_TYPE_VIDEO); + codecs = nonVideoCodecs != null ? videoCodecs + "," + nonVideoCodecs : videoCodecs; } String resolutionString =