diff --git a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs index a58da17d5..0e620e72a 100644 --- a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs +++ b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs @@ -304,6 +304,8 @@ public class DynamicHlsHelper AppendPlaylistCodecsField(playlistBuilder, state); + AppendPlaylistSupplementalCodecsField(playlistBuilder, state); + AppendPlaylistResolutionField(playlistBuilder, state); AppendPlaylistFramerateField(playlistBuilder, state); @@ -406,6 +408,48 @@ public class DynamicHlsHelper } } + /// + /// Appends a SUPPLEMENTAL-CODECS field containing formatted strings of + /// the active streams output Dolby Vision Videos. + /// + /// + /// + /// StringBuilder to append the field to. + /// StreamState of the current stream. + private void AppendPlaylistSupplementalCodecsField(StringBuilder builder, StreamState state) + { + // Dolby Vision currently cannot exist when transcoding + if (!EncodingHelper.IsCopyCodec(state.OutputVideoCodec)) + { + return; + } + + var dvProfile = state.VideoStream.DvProfile; + var dvLevel = state.VideoStream.DvLevel; + var dvRangeString = state.VideoStream.VideoRangeType switch + { + VideoRangeType.DOVIWithHDR10 => "db1p", + VideoRangeType.DOVIWithHLG => "db4h", + _ => string.Empty + }; + + if (dvProfile is null || dvLevel is null || string.IsNullOrEmpty(dvRangeString)) + { + return; + } + + var dvFourCc = string.Equals(state.ActualOutputVideoCodec, "av1", StringComparison.OrdinalIgnoreCase) ? "dav1" : "dvh1"; + builder.Append(",SUPPLEMENTAL-CODECS=\"") + .Append(dvFourCc) + .Append('.') + .Append(dvProfile.Value.ToString("D2", CultureInfo.InvariantCulture)) + .Append('.') + .Append(dvLevel.Value.ToString("D2", CultureInfo.InvariantCulture)) + .Append('/') + .Append(dvRangeString) + .Append('"'); + } + /// /// Appends a RESOLUTION field containing the resolution of the output stream. ///