Kaynağa Gözat

Add RFC7845 downmix algorithm (#12300)

gnattu 10 ay önce
ebeveyn
işleme
0a1a109b2e

+ 1 - 1
Jellyfin.Api/Controllers/DynamicHlsController.cs

@@ -1733,7 +1733,7 @@ public class DynamicHlsController : BaseJellyfinApiController
 
 
         var channels = state.OutputAudioChannels;
         var channels = state.OutputAudioChannels;
 
 
-        var useDownMixAlgorithm = state.AudioStream.Channels is 6 && _encodingOptions.DownMixStereoAlgorithm != DownMixStereoAlgorithms.None;
+        var useDownMixAlgorithm = DownMixAlgorithmsHelper.AlgorithmFilterStrings.ContainsKey((_encodingOptions.DownMixStereoAlgorithm, DownMixAlgorithmsHelper.InferChannelLayout(state.AudioStream)));
 
 
         if (channels.HasValue
         if (channels.HasValue
             && (channels.Value != 2
             && (channels.Value != 2

+ 65 - 0
MediaBrowser.Controller/MediaEncoding/DownMixAlgorithmsHelper.cs

@@ -0,0 +1,65 @@
+using System.Collections.Generic;
+using MediaBrowser.Model.Entities;
+
+namespace MediaBrowser.Controller.MediaEncoding;
+
+/// <summary>
+/// Describes the downmix algorithms capabilities.
+/// </summary>
+public static class DownMixAlgorithmsHelper
+{
+    /// <summary>
+    /// The filter string of the DownMixStereoAlgorithms.
+    /// The index is the tuple of (algorithm, layout).
+    /// </summary>
+    public static readonly Dictionary<(DownMixStereoAlgorithms, string), string> AlgorithmFilterStrings = new()
+    {
+        { (DownMixStereoAlgorithms.Dave750, "5.1"), "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" },
+        { (DownMixStereoAlgorithms.NightmodeDialogue, "5.1"), "pan=stereo|c0=c2+0.30*c0+0.30*c4|c1=c2+0.30*c1+0.30*c5" },
+        { (DownMixStereoAlgorithms.Rfc7845, "3.0"), "pan=stereo|c0=0.414214*c2+0.585786*c0|c1=0.414214*c2+0.585786*c1" },
+        { (DownMixStereoAlgorithms.Rfc7845, "quad"), "pan=stereo|c0=0.422650*c0+0.366025*c2+0.211325*c3|c1=0.422650*c1+0.366025*c3+0.211325*c2" },
+        { (DownMixStereoAlgorithms.Rfc7845, "5.0"), "pan=stereo|c0=0.460186*c2+0.650802*c0+0.563611*c3+0.325401*c4|c1=0.460186*c2+0.650802*c1+0.563611*c4+0.325401*c3" },
+        { (DownMixStereoAlgorithms.Rfc7845, "5.1"), "pan=stereo|c0=0.374107*c2+0.529067*c0+0.458186*c4+0.264534*c5+0.374107*c3|c1=0.374107*c2+0.529067*c1+0.458186*c5+0.264534*c4+0.374107*c3" },
+        { (DownMixStereoAlgorithms.Rfc7845, "6.1"), "pan=stereo|c0=0.321953*c2+0.455310*c0+0.394310*c5+0.227655*c6+0.278819*c4+0.321953*c3|c1=0.321953*c2+0.455310*c1+0.394310*c6+0.227655*c5+0.278819*c4+0.321953*c3" },
+        { (DownMixStereoAlgorithms.Rfc7845, "7.1"), "pan=stereo|c0=0.274804*c2+0.388631*c0+0.336565*c6+0.194316*c7+0.336565*c4+0.194316*c5+0.274804*c3|c1=0.274804*c2+0.388631*c1+0.336565*c7+0.194316*c6+0.336565*c5+0.194316*c4+0.274804*c3" },
+    };
+
+    /// <summary>
+    /// Get the audio channel layout string from the audio stream
+    /// If the input audio string does not have a valid layout string, guess from channel count.
+    /// </summary>
+    /// <param name="audioStream">The audio stream to get layout.</param>
+    /// <returns>Channel Layout string.</returns>
+    public static string InferChannelLayout(MediaStream audioStream)
+    {
+        if (!string.IsNullOrWhiteSpace(audioStream.ChannelLayout))
+        {
+            // Note: BDMVs do not derive this string from ffmpeg, which would cause ambiguity with 4-channel audio
+            // "quad" => 2 front and 2 rear, "4.0" => 3 front and 1 rear
+            // BDMV will always use "4.0" in this case
+            // Because the quad layout is super rare in BDs, we will use "4.0" as is here
+            return audioStream.ChannelLayout;
+        }
+
+        if (audioStream.Channels is null)
+        {
+            return string.Empty;
+        }
+
+        // When we don't have definitive channel layout, we have to guess from the channel count
+        // Guessing is not always correct, but for most videos we don't have to guess like this as the definitive layout is recorded during scan
+        var inferredLayout = audioStream.Channels.Value switch
+        {
+            1 => "mono",
+            2 => "stereo",
+            3 => "2.1", // Could also be 3.0, prefer 2.1
+            4 => "4.0", // Could also be quad (with rear left and rear right) and 3.1 with LFE. prefer 4.0 with front center and back center
+            5 => "5.0",
+            6 => "5.1", // Could also be 6.0 or hexagonal, prefer 5.1
+            7 => "6.1", // Could also be 7.0, prefer 6.1
+            8 => "7.1", // Could also be 8.0, prefer 7.1
+            _ => string.Empty // Return empty string for not supported layout
+        };
+        return inferredLayout;
+    }
+}

+ 10 - 18
MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs

@@ -2666,28 +2666,17 @@ namespace MediaBrowser.Controller.MediaEncoding
 
 
             var filters = new List<string>();
             var filters = new List<string>();
 
 
-            if (channels.HasValue
-                && channels.Value == 2
-                && state.AudioStream is not null
-                && state.AudioStream.Channels.HasValue
-                && state.AudioStream.Channels.Value == 6)
+            if (channels is 2 && state.AudioStream?.Channels is > 2)
             {
             {
-                if (!encodingOptions.DownMixAudioBoost.Equals(1))
+                var hasDownMixFilter = DownMixAlgorithmsHelper.AlgorithmFilterStrings.TryGetValue((encodingOptions.DownMixStereoAlgorithm, DownMixAlgorithmsHelper.InferChannelLayout(state.AudioStream)), out var downMixFilterString);
+                if (hasDownMixFilter)
                 {
                 {
-                    filters.Add("volume=" + encodingOptions.DownMixAudioBoost.ToString(CultureInfo.InvariantCulture));
+                    filters.Add(downMixFilterString);
                 }
                 }
 
 
-                switch (encodingOptions.DownMixStereoAlgorithm)
+                if (!encodingOptions.DownMixAudioBoost.Equals(1))
                 {
                 {
-                    case DownMixStereoAlgorithms.Dave750:
-                        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:
-                        break;
+                    filters.Add("volume=" + encodingOptions.DownMixAudioBoost.ToString(CultureInfo.InvariantCulture));
                 }
                 }
             }
             }
 
 
@@ -7008,7 +6997,10 @@ namespace MediaBrowser.Controller.MediaEncoding
 
 
             var channels = state.OutputAudioChannels;
             var channels = state.OutputAudioChannels;
 
 
-            if (channels.HasValue && ((channels.Value != 2 && state.AudioStream?.Channels != 6) || encodingOptions.DownMixStereoAlgorithm == DownMixStereoAlgorithms.None))
+            var useDownMixAlgorithm = state.AudioStream is not null
+                                      && DownMixAlgorithmsHelper.AlgorithmFilterStrings.ContainsKey((encodingOptions.DownMixStereoAlgorithm, DownMixAlgorithmsHelper.InferChannelLayout(state.AudioStream)));
+
+            if (channels.HasValue && !useDownMixAlgorithm)
             {
             {
                 args += " -ac " + channels.Value;
                 args += " -ac " + channels.Value;
             }
             }

+ 6 - 1
MediaBrowser.Model/Entities/DownMixStereoAlgorithms.cs

@@ -19,5 +19,10 @@ public enum DownMixStereoAlgorithms
     /// <summary>
     /// <summary>
     /// Nightmode Dialogue algorithm.
     /// Nightmode Dialogue algorithm.
     /// </summary>
     /// </summary>
-    NightmodeDialogue = 2
+    NightmodeDialogue = 2,
+
+    /// <summary>
+    /// RFC7845 Section 5.1.1.5 defined algorithm.
+    /// </summary>
+    Rfc7845 = 3
 }
 }