Re-apply HA fork patches on upstream v12.0 #3

Merged
benvin merged 2139 commits from benvin/rebase-v12 into main 2026-09-12 12:48:36 +10:00
2 changed files with 99 additions and 0 deletions
Showing only changes of commit e5bbb1ea0c - Show all commits
+78
View File
@@ -209,6 +209,25 @@ public class DynamicHlsHelper
AddSubtitles(state, subtitleStreams, builder, _httpContextAccessor.HttpContext.User);
}
// For DoVi profiles without a compatible base layer (P5 HEVC, P10/bl0 AV1),
// add a spec-compliant dvh1/dav1 variant before the hvc1 hack variant.
// SUPPLEMENTAL-CODECS cannot be used for these profiles (no compatible BL to supplement).
// The DoVi variant is listed first so spec-compliant clients (Apple TV, webOS 24+)
// select it over the fallback when both have identical BANDWIDTH.
// Only emit for clients that explicitly declared DOVI support to avoid breaking
// non-compliant players that don't recognize dvh1/dav1 CODECS strings.
if (state.VideoStream is not null
&& state.VideoRequest is not null
&& EncodingHelper.IsCopyCodec(state.OutputVideoCodec)
&& state.VideoStream.VideoRangeType == VideoRangeType.DOVI
&& state.VideoStream.DvProfile.HasValue
&& state.VideoStream.DvLevel.HasValue
&& state.GetRequestedRangeTypes(state.VideoStream.Codec)
.Contains(VideoRangeType.DOVI.ToString(), StringComparison.OrdinalIgnoreCase))
{
AppendDoviPlaylist(builder, state, playlistUrl, totalBitrate, subtitleGroup);
}
var basicPlaylist = AppendPlaylist(builder, state, playlistUrl, totalBitrate, subtitleGroup);
if (state.VideoStream is not null && state.VideoRequest is not null)
@@ -355,6 +374,65 @@ public class DynamicHlsHelper
return playlistBuilder;
}
/// <summary>
/// Appends a Dolby Vision variant with dvh1/dav1 CODECS for profiles without a compatible
/// base layer (P5 HEVC, P10/bl0 AV1). This enables spec-compliant HLS clients to detect
/// DoVi from the manifest rather than relying on init segment inspection.
/// </summary>
/// <param name="builder">StringBuilder for the master playlist.</param>
/// <param name="state">StreamState of the current stream.</param>
/// <param name="url">Playlist URL for this variant.</param>
/// <param name="bitrate">Bitrate for the BANDWIDTH field.</param>
/// <param name="subtitleGroup">Subtitle group identifier, or null.</param>
private void AppendDoviPlaylist(StringBuilder builder, StreamState state, string url, int bitrate, string? subtitleGroup)
{
var dvProfile = state.VideoStream.DvProfile;
var dvLevel = state.VideoStream.DvLevel;
if (dvProfile is null || dvLevel is null)
{
return;
}
var playlistBuilder = new StringBuilder();
playlistBuilder.Append("#EXT-X-STREAM-INF:BANDWIDTH=")
.Append(bitrate.ToString(CultureInfo.InvariantCulture))
.Append(",AVERAGE-BANDWIDTH=")
.Append(bitrate.ToString(CultureInfo.InvariantCulture));
playlistBuilder.Append(",VIDEO-RANGE=PQ");
var dvCodec = HlsCodecStringHelpers.GetDoviString(dvProfile.Value, dvLevel.Value, state.ActualOutputVideoCodec);
string audioCodecs = string.Empty;
if (!string.IsNullOrEmpty(state.ActualOutputAudioCodec))
{
audioCodecs = GetPlaylistAudioCodecs(state);
}
playlistBuilder.Append(",CODECS=\"")
.Append(dvCodec);
if (!string.IsNullOrEmpty(audioCodecs))
{
playlistBuilder.Append(',').Append(audioCodecs);
}
playlistBuilder.Append('"');
AppendPlaylistResolutionField(playlistBuilder, state);
AppendPlaylistFramerateField(playlistBuilder, state);
if (!string.IsNullOrWhiteSpace(subtitleGroup))
{
playlistBuilder.Append(",SUBTITLES=\"")
.Append(subtitleGroup)
.Append('"');
}
playlistBuilder.AppendLine();
playlistBuilder.AppendLine(url);
builder.Append(playlistBuilder);
}
/// <summary>
/// Appends a VIDEO-RANGE field containing the range of the output video stream.
/// </summary>
@@ -346,4 +346,25 @@ public static class HlsCodecStringHelpers
return result.ToString();
}
/// <summary>
/// Gets a Dolby Vision codec string for profiles without a compatible base layer.
/// </summary>
/// <param name="dvProfile">Dolby Vision profile number.</param>
/// <param name="dvLevel">Dolby Vision level number.</param>
/// <param name="codec">Video codec name (e.g. "hevc", "av1") to determine the DoVi FourCC.</param>
/// <returns>Dolby Vision codec string.</returns>
public static string GetDoviString(int dvProfile, int dvLevel, string codec)
{
// HEVC DoVi uses dvh1, AV1 DoVi uses dav1 (out-of-band parameter sets, recommended by Apple HLS spec Rule 1.10)
var fourCc = string.Equals(codec, "av1", StringComparison.OrdinalIgnoreCase) ? "dav1" : "dvh1";
StringBuilder result = new StringBuilder(fourCc, 12);
result.Append('.')
.AppendFormat(CultureInfo.InvariantCulture, "{0:D2}", dvProfile)
.Append('.')
.AppendFormat(CultureInfo.InvariantCulture, "{0:D2}", dvLevel);
return result.ToString();
}
}