DynamicHlsHelper.cs (jellyfin-10.8.9) | : | DynamicHlsHelper.cs (jellyfin-10.8.10) | ||
---|---|---|---|---|
using System; | using System; | |||
using System.Collections.Generic; | using System.Collections.Generic; | |||
using System.Globalization; | using System.Globalization; | |||
using System.Linq; | using System.Linq; | |||
using System.Net; | using System.Net; | |||
using System.Security.Claims; | using System.Security.Claims; | |||
using System.Text; | using System.Text; | |||
using System.Threading; | using System.Threading; | |||
using System.Threading.Tasks; | using System.Threading.Tasks; | |||
using Jellyfin.Api.Models.StreamingDtos; | using Jellyfin.Api.Models.StreamingDtos; | |||
using Jellyfin.Extensions; | ||||
using MediaBrowser.Common.Configuration; | using MediaBrowser.Common.Configuration; | |||
using MediaBrowser.Common.Extensions; | using MediaBrowser.Common.Extensions; | |||
using MediaBrowser.Common.Net; | using MediaBrowser.Common.Net; | |||
using MediaBrowser.Controller.Configuration; | using MediaBrowser.Controller.Configuration; | |||
using MediaBrowser.Controller.Devices; | using MediaBrowser.Controller.Devices; | |||
using MediaBrowser.Controller.Dlna; | using MediaBrowser.Controller.Dlna; | |||
using MediaBrowser.Controller.Library; | using MediaBrowser.Controller.Library; | |||
using MediaBrowser.Controller.MediaEncoding; | using MediaBrowser.Controller.MediaEncoding; | |||
using MediaBrowser.Controller.Net; | using MediaBrowser.Controller.Net; | |||
using MediaBrowser.Model.Dlna; | using MediaBrowser.Model.Dlna; | |||
skipping to change at line 206 | skipping to change at line 207 | |||
if (!string.IsNullOrWhiteSpace(subtitleGroup)) | if (!string.IsNullOrWhiteSpace(subtitleGroup)) | |||
{ | { | |||
AddSubtitles(state, subtitleStreams, builder, _httpContextAccess or.HttpContext.User); | AddSubtitles(state, subtitleStreams, builder, _httpContextAccess or.HttpContext.User); | |||
} | } | |||
var basicPlaylist = AppendPlaylist(builder, state, playlistUrl, tota lBitrate, subtitleGroup); | var basicPlaylist = AppendPlaylist(builder, state, playlistUrl, tota lBitrate, subtitleGroup); | |||
if (state.VideoStream != null && state.VideoRequest != null) | if (state.VideoStream != null && state.VideoRequest != null) | |||
{ | { | |||
// Provide a workaround for the case issue between flac and fLaC | ||||
. | ||||
var flacWaPlaylist = ApplyFlacCaseWorkaround(state, basicPlaylis | ||||
t.ToString()); | ||||
if (!string.IsNullOrEmpty(flacWaPlaylist)) | ||||
{ | ||||
builder.Append(flacWaPlaylist); | ||||
} | ||||
var encodingOptions = _serverConfigurationManager.GetEncodingOpt ions(); | var encodingOptions = _serverConfigurationManager.GetEncodingOpt ions(); | |||
// Provide SDR HEVC entrance for backward compatibility. | // Provide SDR HEVC entrance for backward compatibility. | |||
if (encodingOptions.AllowHevcEncoding | if (encodingOptions.AllowHevcEncoding | |||
&& EncodingHelper.IsCopyCodec(state.OutputVideoCodec) | && EncodingHelper.IsCopyCodec(state.OutputVideoCodec) | |||
&& !string.IsNullOrEmpty(state.VideoStream.VideoRange) | && !string.IsNullOrEmpty(state.VideoStream.VideoRange) | |||
&& string.Equals(state.VideoStream.VideoRange, "HDR", String Comparison.OrdinalIgnoreCase) | && string.Equals(state.VideoStream.VideoRange, "HDR", String Comparison.OrdinalIgnoreCase) | |||
&& string.Equals(state.ActualOutputVideoCodec, "hevc", Strin gComparison.OrdinalIgnoreCase)) | && string.Equals(state.ActualOutputVideoCodec, "hevc", Strin gComparison.OrdinalIgnoreCase)) | |||
{ | { | |||
var requestedVideoProfiles = state.GetRequestedProfiles("hev c"); | var requestedVideoProfiles = state.GetRequestedProfiles("hev c"); | |||
if (requestedVideoProfiles != null && requestedVideoProfiles .Length > 0) | if (requestedVideoProfiles != null && requestedVideoProfiles .Length > 0) | |||
{ | { | |||
// Force HEVC Main Profile and disable video stream copy . | // Force HEVC Main Profile and disable video stream copy . | |||
state.OutputVideoCodec = "hevc"; | state.OutputVideoCodec = "hevc"; | |||
var sdrVideoUrl = ReplaceProfile(playlistUrl, "hevc", st ring.Join(',', requestedVideoProfiles), "main"); | var sdrVideoUrl = ReplaceProfile(playlistUrl, "hevc", st ring.Join(',', requestedVideoProfiles), "main"); | |||
sdrVideoUrl += "&AllowVideoStreamCopy=false"; | sdrVideoUrl += "&AllowVideoStreamCopy=false"; | |||
var sdrOutputVideoBitrate = _encodingHelper.GetVideoBitr ateParamValue(state.VideoRequest, state.VideoStream, state.OutputVideoCodec); | var sdrOutputVideoBitrate = _encodingHelper.GetVideoBitr ateParamValue(state.VideoRequest, state.VideoStream, state.OutputVideoCodec); | |||
var sdrOutputAudioBitrate = _encodingHelper.GetAudioBitr | var sdrOutputAudioBitrate = 0; | |||
ateParam(state.VideoRequest, state.AudioStream) ?? 0; | if (EncodingHelper.LosslessAudioCodecs.Contains(state.Vi | |||
deoRequest.AudioCodec, StringComparison.OrdinalIgnoreCase)) | ||||
{ | ||||
sdrOutputAudioBitrate = state.AudioStream.BitRate ?? | ||||
0; | ||||
} | ||||
else | ||||
{ | ||||
sdrOutputAudioBitrate = _encodingHelper.GetAudioBitr | ||||
ateParam(state.VideoRequest, state.AudioStream, state.OutputAudioChannels) ?? 0; | ||||
} | ||||
var sdrTotalBitrate = sdrOutputAudioBitrate + sdrOutputV ideoBitrate; | var sdrTotalBitrate = sdrOutputAudioBitrate + sdrOutputV ideoBitrate; | |||
var sdrPlaylist = AppendPlaylist(builder, state, sdrVide oUrl, sdrTotalBitrate, subtitleGroup); | ||||
AppendPlaylist(builder, state, sdrVideoUrl, sdrTotalBitr | // Provide a workaround for the case issue between flac | |||
ate, subtitleGroup); | and fLaC. | |||
flacWaPlaylist = ApplyFlacCaseWorkaround(state, sdrPlayl | ||||
ist.ToString()); | ||||
if (!string.IsNullOrEmpty(flacWaPlaylist)) | ||||
{ | ||||
builder.Append(flacWaPlaylist); | ||||
} | ||||
// Restore the video codec | // Restore the video codec | |||
state.OutputVideoCodec = "copy"; | state.OutputVideoCodec = "copy"; | |||
} | } | |||
} | } | |||
// Provide Level 5.0 entrance for backward compatibility. | // Provide Level 5.0 entrance for backward compatibility. | |||
// e.g. Apple A10 chips refuse the master playlist containing SD R HEVC Main Level 5.1 video, | // e.g. Apple A10 chips refuse the master playlist containing SD R HEVC Main Level 5.1 video, | |||
// but in fact it is capable of playing videos up to Level 6.1. | // but in fact it is capable of playing videos up to Level 6.1. | |||
if (EncodingHelper.IsCopyCodec(state.OutputVideoCodec) | if (EncodingHelper.IsCopyCodec(state.OutputVideoCodec) | |||
skipping to change at line 257 | skipping to change at line 280 | |||
// Force the video level to 5.0. | // Force the video level to 5.0. | |||
var originalLevel = state.VideoStream.Level; | var originalLevel = state.VideoStream.Level; | |||
state.VideoStream.Level = 150; | state.VideoStream.Level = 150; | |||
var newPlaylistCodecsField = new StringBuilder(); | var newPlaylistCodecsField = new StringBuilder(); | |||
AppendPlaylistCodecsField(newPlaylistCodecsField, state); | AppendPlaylistCodecsField(newPlaylistCodecsField, state); | |||
// Restore the video level. | // Restore the video level. | |||
state.VideoStream.Level = originalLevel; | state.VideoStream.Level = originalLevel; | |||
var newPlaylist = ReplacePlaylistCodecsField(basicPlaylist, playlistCodecsField, newPlaylistCodecsField); | var newPlaylist = ReplacePlaylistCodecsField(basicPlaylist, playlistCodecsField, newPlaylistCodecsField); | |||
builder.Append(newPlaylist); | builder.Append(newPlaylist); | |||
// Provide a workaround for the case issue between flac and | ||||
fLaC. | ||||
flacWaPlaylist = ApplyFlacCaseWorkaround(state, newPlaylist) | ||||
; | ||||
if (!string.IsNullOrEmpty(flacWaPlaylist)) | ||||
{ | ||||
builder.Append(flacWaPlaylist); | ||||
} | ||||
} | } | |||
} | } | |||
if (EnableAdaptiveBitrateStreaming(state, isLiveStream, enableAdapti veBitrateStreaming, _httpContextAccessor.HttpContext.GetNormalizedRemoteIp())) | if (EnableAdaptiveBitrateStreaming(state, isLiveStream, enableAdapti veBitrateStreaming, _httpContextAccessor.HttpContext.GetNormalizedRemoteIp())) | |||
{ | { | |||
var requestedVideoBitrate = state.VideoRequest == null ? 0 : sta te.VideoRequest.VideoBitRate ?? 0; | var requestedVideoBitrate = state.VideoRequest == null ? 0 : sta te.VideoRequest.VideoBitRate ?? 0; | |||
// By default, vary by just 200k | // By default, vary by just 200k | |||
var variation = GetBitrateVariation(totalBitrate); | var variation = GetBitrateVariation(totalBitrate); | |||
skipping to change at line 615 | skipping to change at line 645 | |||
if (string.Equals(state.ActualOutputAudioCodec, "flac", StringCompar ison.OrdinalIgnoreCase)) | if (string.Equals(state.ActualOutputAudioCodec, "flac", StringCompar ison.OrdinalIgnoreCase)) | |||
{ | { | |||
return HlsCodecStringHelpers.GetFLACString(); | return HlsCodecStringHelpers.GetFLACString(); | |||
} | } | |||
if (string.Equals(state.ActualOutputAudioCodec, "alac", StringCompar ison.OrdinalIgnoreCase)) | if (string.Equals(state.ActualOutputAudioCodec, "alac", StringCompar ison.OrdinalIgnoreCase)) | |||
{ | { | |||
return HlsCodecStringHelpers.GetALACString(); | return HlsCodecStringHelpers.GetALACString(); | |||
} | } | |||
if (string.Equals(state.ActualOutputAudioCodec, "opus", StringCompar | ||||
ison.OrdinalIgnoreCase)) | ||||
{ | ||||
return HlsCodecStringHelpers.GetOPUSString(); | ||||
} | ||||
return string.Empty; | return string.Empty; | |||
} | } | |||
/// <summary> | /// <summary> | |||
/// Gets a formatted string of the output video codec, for use in the CO DECS field. | /// Gets a formatted string of the output video codec, for use in the CO DECS field. | |||
/// </summary> | /// </summary> | |||
/// <seealso cref="AppendPlaylistCodecsField(StringBuilder, StreamState) "/> | /// <seealso cref="AppendPlaylistCodecsField(StringBuilder, StreamState) "/> | |||
/// <seealso cref="GetPlaylistAudioCodecs(StreamState)"/> | /// <seealso cref="GetPlaylistAudioCodecs(StreamState)"/> | |||
/// <param name="state">StreamState of the current stream.</param> | /// <param name="state">StreamState of the current stream.</param> | |||
/// <param name="codec">Video codec.</param> | /// <param name="codec">Video codec.</param> | |||
skipping to change at line 713 | skipping to change at line 748 | |||
profileStr + newValue, | profileStr + newValue, | |||
StringComparison.OrdinalIgnoreCase); | StringComparison.OrdinalIgnoreCase); | |||
} | } | |||
private string ReplacePlaylistCodecsField(StringBuilder playlist, String Builder oldValue, StringBuilder newValue) | private string ReplacePlaylistCodecsField(StringBuilder playlist, String Builder oldValue, StringBuilder newValue) | |||
{ | { | |||
var oldPlaylist = playlist.ToString(); | var oldPlaylist = playlist.ToString(); | |||
return oldPlaylist.Replace( | return oldPlaylist.Replace( | |||
oldValue.ToString(), | oldValue.ToString(), | |||
newValue.ToString(), | newValue.ToString(), | |||
StringComparison.OrdinalIgnoreCase); | StringComparison.Ordinal); | |||
} | ||||
private string ApplyFlacCaseWorkaround(StreamState state, string srcPlay | ||||
list) | ||||
{ | ||||
if (!string.Equals(state.ActualOutputAudioCodec, "flac", StringCompa | ||||
rison.OrdinalIgnoreCase)) | ||||
{ | ||||
return string.Empty; | ||||
} | ||||
var newPlaylist = srcPlaylist.Replace(",flac\"", ",fLaC\"", StringCo | ||||
mparison.Ordinal); | ||||
return newPlaylist.Contains(",fLaC\"", StringComparison.Ordinal) ? n | ||||
ewPlaylist : string.Empty; | ||||
} | } | |||
} | } | |||
} | } | |||
End of changes. 8 change blocks. | ||||
5 lines changed or deleted | 64 lines changed or added |