diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index af43bb578e..e0c5bcc84b 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -22,6 +22,7 @@ using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Dlna; +using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.Model.Net; using Microsoft.AspNetCore.Authorization; @@ -1731,7 +1732,12 @@ namespace Jellyfin.Api.Controllers var channels = state.OutputAudioChannels; - if (channels.HasValue) + if (channels.HasValue + && (channels.Value != 2 + || (state.AudioStream is not null + && state.AudioStream.Channels.HasValue + && state.AudioStream.Channels.Value > 5 + && _encodingOptions.DownMixStereoAlgorithm == DownMixStereoAlgorithms.None))) { args += " -ac " + channels.Value; } diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index b40c224d5d..e94a04a7dc 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -2129,15 +2129,30 @@ namespace MediaBrowser.Controller.MediaEncoding var filters = new List(); - // Boost volume to 200% when downsampling from 6ch to 2ch if (channels.HasValue - && channels.Value <= 2 + && channels.Value == 2 && state.AudioStream is not null && state.AudioStream.Channels.HasValue - && state.AudioStream.Channels.Value > 5 - && !encodingOptions.DownMixAudioBoost.Equals(1)) + && state.AudioStream.Channels.Value > 5) { - filters.Add("volume=" + encodingOptions.DownMixAudioBoost.ToString(CultureInfo.InvariantCulture)); + switch (encodingOptions.DownMixStereoAlgorithm) + { + case DownMixStereoAlgorithms.Dave750: + filters.Add("volume=4.25"); + filters.Add("pan=stereo|c0=0.5*c2+0.707*c0+0.707*c4+0.5*c3|c1=0.5*c2+0.707*c1+0.707*c5+0.5*c3"); + break; + case DownMixStereoAlgorithms.NightmodeDialogue: + filters.Add("pan=stereo|c0=c2+0.30*c0+0.30*c4|c1=c2+0.30*c1+0.30*c5"); + break; + case DownMixStereoAlgorithms.None: + default: + if (!encodingOptions.DownMixAudioBoost.Equals(1)) + { + filters.Add("volume=" + encodingOptions.DownMixAudioBoost.ToString(CultureInfo.InvariantCulture)); + } + + break; + } } var isCopyingTimestamps = state.CopyTimestamps || state.TranscodingType != TranscodingJobType.Progressive; @@ -5711,10 +5726,9 @@ namespace MediaBrowser.Controller.MediaEncoding return args; } - // Add the number of audio channels var channels = state.OutputAudioChannels; - if (channels.HasValue) + if (channels.HasValue && ((channels.Value != 2 && state.AudioStream.Channels <= 5) || encodingOptions.DownMixStereoAlgorithm == DownMixStereoAlgorithms.None)) { args += " -ac " + channels.Value; } diff --git a/MediaBrowser.Model/Configuration/EncodingOptions.cs b/MediaBrowser.Model/Configuration/EncodingOptions.cs index f4cd2f0065..0ff95a2e1f 100644 --- a/MediaBrowser.Model/Configuration/EncodingOptions.cs +++ b/MediaBrowser.Model/Configuration/EncodingOptions.cs @@ -1,128 +1,247 @@ #nullable disable -#pragma warning disable CS1591 +using MediaBrowser.Model.Entities; -namespace MediaBrowser.Model.Configuration +namespace MediaBrowser.Model.Configuration; + +/// +/// Class EncodingOptions. +/// +public class EncodingOptions { - public class EncodingOptions + /// + /// Initializes a new instance of the class. + /// + public EncodingOptions() { - public EncodingOptions() - { - EnableFallbackFont = false; - DownMixAudioBoost = 2; - MaxMuxingQueueSize = 2048; - EnableThrottling = false; - ThrottleDelaySeconds = 180; - EncodingThreadCount = -1; - // This is a DRM device that is almost guaranteed to be there on every intel platform, - // plus it's the default one in ffmpeg if you don't specify anything - VaapiDevice = "/dev/dri/renderD128"; - EnableTonemapping = false; - EnableVppTonemapping = false; - TonemappingAlgorithm = "bt2390"; - TonemappingRange = "auto"; - TonemappingDesat = 0; - TonemappingThreshold = 0.8; - TonemappingPeak = 100; - TonemappingParam = 0; - VppTonemappingBrightness = 0; - VppTonemappingContrast = 1.2; - H264Crf = 23; - H265Crf = 28; - DeinterlaceDoubleRate = false; - DeinterlaceMethod = "yadif"; - EnableDecodingColorDepth10Hevc = true; - EnableDecodingColorDepth10Vp9 = true; - EnableEnhancedNvdecDecoder = false; - PreferSystemNativeHwDecoder = true; - EnableIntelLowPowerH264HwEncoder = false; - EnableIntelLowPowerHevcHwEncoder = false; - EnableHardwareEncoding = true; - AllowHevcEncoding = false; - EnableSubtitleExtraction = true; - AllowOnDemandMetadataBasedKeyframeExtractionForExtensions = new[] { "mkv" }; - HardwareDecodingCodecs = new string[] { "h264", "vc1" }; - } - - public int EncodingThreadCount { get; set; } - - public string TranscodingTempPath { get; set; } - - public string FallbackFontPath { get; set; } - - public bool EnableFallbackFont { get; set; } - - public double DownMixAudioBoost { get; set; } - - public int MaxMuxingQueueSize { get; set; } - - public bool EnableThrottling { get; set; } - - public int ThrottleDelaySeconds { get; set; } - - public string HardwareAccelerationType { get; set; } - - /// - /// Gets or sets the FFmpeg path as set by the user via the UI. - /// - public string EncoderAppPath { get; set; } - - /// - /// Gets or sets the current FFmpeg path being used by the system and displayed on the transcode page. - /// - public string EncoderAppPathDisplay { get; set; } - - public string VaapiDevice { get; set; } - - public bool EnableTonemapping { get; set; } - - public bool EnableVppTonemapping { get; set; } - - public string TonemappingAlgorithm { get; set; } - - public string TonemappingRange { get; set; } - - public double TonemappingDesat { get; set; } - - public double TonemappingThreshold { get; set; } - - public double TonemappingPeak { get; set; } - - public double TonemappingParam { get; set; } - - public double VppTonemappingBrightness { get; set; } - - public double VppTonemappingContrast { get; set; } - - public int H264Crf { get; set; } - - public int H265Crf { get; set; } - - public string EncoderPreset { get; set; } - - public bool DeinterlaceDoubleRate { get; set; } - - public string DeinterlaceMethod { get; set; } - - public bool EnableDecodingColorDepth10Hevc { get; set; } - - public bool EnableDecodingColorDepth10Vp9 { get; set; } - - public bool EnableEnhancedNvdecDecoder { get; set; } - - public bool PreferSystemNativeHwDecoder { get; set; } - - public bool EnableIntelLowPowerH264HwEncoder { get; set; } - - public bool EnableIntelLowPowerHevcHwEncoder { get; set; } - - public bool EnableHardwareEncoding { get; set; } - - public bool AllowHevcEncoding { get; set; } - - public bool EnableSubtitleExtraction { get; set; } - - public string[] HardwareDecodingCodecs { get; set; } - - public string[] AllowOnDemandMetadataBasedKeyframeExtractionForExtensions { get; set; } + EnableFallbackFont = false; + DownMixAudioBoost = 2; + DownMixStereoAlgorithm = DownMixStereoAlgorithms.None; + MaxMuxingQueueSize = 2048; + EnableThrottling = false; + ThrottleDelaySeconds = 180; + EncodingThreadCount = -1; + // This is a DRM device that is almost guaranteed to be there on every intel platform, + // plus it's the default one in ffmpeg if you don't specify anything + VaapiDevice = "/dev/dri/renderD128"; + EnableTonemapping = false; + EnableVppTonemapping = false; + TonemappingAlgorithm = "bt2390"; + TonemappingRange = "auto"; + TonemappingDesat = 0; + TonemappingThreshold = 0.8; + TonemappingPeak = 100; + TonemappingParam = 0; + VppTonemappingBrightness = 0; + VppTonemappingContrast = 1.2; + H264Crf = 23; + H265Crf = 28; + DeinterlaceDoubleRate = false; + DeinterlaceMethod = "yadif"; + EnableDecodingColorDepth10Hevc = true; + EnableDecodingColorDepth10Vp9 = true; + EnableEnhancedNvdecDecoder = false; + PreferSystemNativeHwDecoder = true; + EnableIntelLowPowerH264HwEncoder = false; + EnableIntelLowPowerHevcHwEncoder = false; + EnableHardwareEncoding = true; + AllowHevcEncoding = false; + EnableSubtitleExtraction = true; + AllowOnDemandMetadataBasedKeyframeExtractionForExtensions = new[] { "mkv" }; + HardwareDecodingCodecs = new string[] { "h264", "vc1" }; } + + /// + /// Gets or sets the thread count used for encoding. + /// + public int EncodingThreadCount { get; set; } + + /// + /// Gets or sets the temporary transcoding path. + /// + public string TranscodingTempPath { get; set; } + + /// + /// Gets or sets the path to the fallback font. + /// + public string FallbackFontPath { get; set; } + + /// + /// Gets or sets a value indicating whether to use the fallback font. + /// + public bool EnableFallbackFont { get; set; } + + /// + /// Gets or sets the audio boost applied when downmixing audio. + /// + public double DownMixAudioBoost { get; set; } + + /// + /// Gets or sets the algorithm used for downmixing audio to stereo. + /// + public DownMixStereoAlgorithms DownMixStereoAlgorithm { get; set; } + + /// + /// Gets or sets the maximum size of the muxing queue. + /// + public int MaxMuxingQueueSize { get; set; } + + /// + /// Gets or sets a value indicating whether throttling is enabled. + /// + public bool EnableThrottling { get; set; } + + /// + /// Gets or sets the delay after which throttling happens. + /// + public int ThrottleDelaySeconds { get; set; } + + /// + /// Gets or sets the hardware acceleration type. + /// + public string HardwareAccelerationType { get; set; } + + /// + /// Gets or sets the FFmpeg path as set by the user via the UI. + /// + public string EncoderAppPath { get; set; } + + /// + /// Gets or sets the current FFmpeg path being used by the system and displayed on the transcode page. + /// + public string EncoderAppPathDisplay { get; set; } + + /// + /// Gets or sets the VA-API device. + /// + public string VaapiDevice { get; set; } + + /// + /// Gets or sets a value indicating whether tonemapping is enabled. + /// + public bool EnableTonemapping { get; set; } + + /// + /// Gets or sets a value indicating whether VPP tonemapping is enabled. + /// + public bool EnableVppTonemapping { get; set; } + + /// + /// Gets or sets the tone-mapping algorithm. + /// + public string TonemappingAlgorithm { get; set; } + + /// + /// Gets or sets the tone-mapping range. + /// + public string TonemappingRange { get; set; } + + /// + /// Gets or sets the tone-mapping desaturation. + /// + public double TonemappingDesat { get; set; } + + /// + /// Gets or sets the tone-mapping threshold. + /// + public double TonemappingThreshold { get; set; } + + /// + /// Gets or sets the tone-mapping peak. + /// + public double TonemappingPeak { get; set; } + + /// + /// Gets or sets the tone-mapping parameters. + /// + public double TonemappingParam { get; set; } + + /// + /// Gets or sets the VPP tone-mapping brightness. + /// + public double VppTonemappingBrightness { get; set; } + + /// + /// Gets or sets the VPP tone-mapping contrast. + /// + public double VppTonemappingContrast { get; set; } + + /// + /// Gets or sets the H264 CRF. + /// + public int H264Crf { get; set; } + + /// + /// Gets or sets the H265 CRF. + /// + public int H265Crf { get; set; } + + /// + /// Gets or sets the encoder preset. + /// + public string EncoderPreset { get; set; } + + /// + /// Gets or sets a value indicating whether the framerate is doubled when deinterlacing. + /// + public bool DeinterlaceDoubleRate { get; set; } + + /// + /// Gets or sets the deinterlace method. + /// + public string DeinterlaceMethod { get; set; } + + /// + /// Gets or sets a value indicating whether 10bit HEVC decoding is enabled. + /// + public bool EnableDecodingColorDepth10Hevc { get; set; } + + /// + /// Gets or sets a value indicating whether 10bit VP9 decoding is enabled. + /// + public bool EnableDecodingColorDepth10Vp9 { get; set; } + + /// + /// Gets or sets a value indicating whether the enhanced NVDEC is enabled. + /// + public bool EnableEnhancedNvdecDecoder { get; set; } + + /// + /// Gets or sets a value indicating whether the system native hardware decoder should be used. + /// + public bool PreferSystemNativeHwDecoder { get; set; } + + /// + /// Gets or sets a value indicating whether the Intel H264 low-power hardware encoder should be used. + /// + public bool EnableIntelLowPowerH264HwEncoder { get; set; } + + /// + /// Gets or sets a value indicating whether the Intel HEVC low-power hardware encoder should be used. + /// + public bool EnableIntelLowPowerHevcHwEncoder { get; set; } + + /// + /// Gets or sets a value indicating whether hardware encoding is enabled. + /// + public bool EnableHardwareEncoding { get; set; } + + /// + /// Gets or sets a value indicating whether HEVC encoding is enabled. + /// + public bool AllowHevcEncoding { get; set; } + + /// + /// Gets or sets a value indicating whether subtitle extraction is enabled. + /// + public bool EnableSubtitleExtraction { get; set; } + + /// + /// Gets or sets the codecs hardware encoding is used for. + /// + public string[] HardwareDecodingCodecs { get; set; } + + /// + /// Gets or sets the file extensions on-demand metadata based keyframe extraction is enabled for. + /// + public string[] AllowOnDemandMetadataBasedKeyframeExtractionForExtensions { get; set; } } diff --git a/MediaBrowser.Model/Entities/DownMixStereoAlgorithms.cs b/MediaBrowser.Model/Entities/DownMixStereoAlgorithms.cs new file mode 100644 index 0000000000..385cd6a34e --- /dev/null +++ b/MediaBrowser.Model/Entities/DownMixStereoAlgorithms.cs @@ -0,0 +1,23 @@ +namespace MediaBrowser.Model.Entities; + +/// +/// An enum representing an algorithm to downmix 6ch+ to stereo. +/// Algorithms sourced from https://superuser.com/questions/852400/properly-downmix-5-1-to-stereo-using-ffmpeg/1410620#1410620. +/// +public enum DownMixStereoAlgorithms +{ + /// + /// No special algorithm. + /// + None = 0, + + /// + /// Algorithm by Dave_750. + /// + Dave750 = 1, + + /// + /// Nightmode Dialogue algorithm. + /// + NightmodeDialogue = 2 +}