瀏覽代碼

Backport pull request #13209 from jellyfin/release-10.10.z

Transcode to audio codec satisfied other conditions when copy check failed.

Original-merge: 8aa41d59041c792571530c514dd6d21ba22a1881

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Bond_009 <bond.009@outlook.com>
gnattu 4 月之前
父節點
當前提交
8cb11692a9
共有 2 個文件被更改,包括 70 次插入5 次删除
  1. 41 5
      MediaBrowser.Model/Dlna/StreamBuilder.cs
  2. 29 0
      MediaBrowser.Model/Dlna/TranscodingProfile.cs

+ 41 - 5
MediaBrowser.Model/Dlna/StreamBuilder.cs

@@ -862,18 +862,37 @@ namespace MediaBrowser.Model.Dlna
 
                     if (options.AllowAudioStreamCopy)
                     {
-                        if (ContainerHelper.ContainsContainer(transcodingProfile.AudioCodec, audioCodec))
+                        // For Audio stream, we prefer the audio codec that can be directly copied, then the codec that can otherwise satisfies
+                        // the transcoding conditions, then the one does not satisfy the transcoding conditions.
+                        // For example: A client can support both aac and flac, but flac only supports 2 channels while aac supports 6.
+                        // When the source audio is 6 channel flac, we should transcode to 6 channel aac, instead of down-mix to 2 channel flac.
+                        var transcodingAudioCodecs = ContainerHelper.Split(transcodingProfile.AudioCodec);
+
+                        foreach (var transcodingAudioCodec in transcodingAudioCodecs)
                         {
                             var appliedVideoConditions = options.Profile.CodecProfiles
                                 .Where(i => i.Type == CodecType.VideoAudio &&
-                                    i.ContainsAnyCodec(audioCodec, container) &&
+                                    i.ContainsAnyCodec(transcodingAudioCodec, container) &&
                                     i.ApplyConditions.All(applyCondition => ConditionProcessor.IsVideoAudioConditionSatisfied(applyCondition, audioChannels, audioBitrate, audioSampleRate, audioBitDepth, audioProfile, false)))
                                 .Select(i =>
                                     i.Conditions.All(condition => ConditionProcessor.IsVideoAudioConditionSatisfied(condition, audioChannels, audioBitrate, audioSampleRate, audioBitDepth, audioProfile, false)));
 
                             // An empty appliedVideoConditions means that the codec has no conditions for the current audio stream
                             var conditionsSatisfied = appliedVideoConditions.All(satisfied => satisfied);
-                            rank.Audio = conditionsSatisfied ? 1 : 2;
+
+                            var rankAudio = 3;
+
+                            if (conditionsSatisfied)
+                            {
+                                rankAudio = string.Equals(transcodingAudioCodec, audioCodec, StringComparison.OrdinalIgnoreCase) ? 1 : 2;
+                            }
+
+                            rank.Audio = Math.Min(rank.Audio, rankAudio);
+
+                            if (rank.Audio == 1)
+                            {
+                                break;
+                            }
                         }
                     }
 
@@ -963,9 +982,26 @@ namespace MediaBrowser.Model.Dlna
 
             var audioStreamWithSupportedCodec = candidateAudioStreams.Where(stream => ContainerHelper.ContainsContainer(audioCodecs, false, stream.Codec)).FirstOrDefault();
 
-            var directAudioStream = audioStreamWithSupportedCodec?.Channels is not null && audioStreamWithSupportedCodec.Channels.Value <= (playlistItem.TranscodingMaxAudioChannels ?? int.MaxValue) ? audioStreamWithSupportedCodec : null;
+            var channelsExceedsLimit = audioStreamWithSupportedCodec is not null && audioStreamWithSupportedCodec.Channels > (playlistItem.TranscodingMaxAudioChannels ?? int.MaxValue);
+
+            var directAudioStreamSatisfied = audioStreamWithSupportedCodec is not null && !channelsExceedsLimit
+                && options.Profile.CodecProfiles
+                    .Where(i => i.Type == CodecType.VideoAudio
+                        && i.ContainsAnyCodec(audioStreamWithSupportedCodec.Codec, container)
+                        && i.ApplyConditions.All(applyCondition => ConditionProcessor.IsVideoAudioConditionSatisfied(applyCondition, audioStreamWithSupportedCodec.Channels, audioStreamWithSupportedCodec.BitRate, audioStreamWithSupportedCodec.SampleRate, audioStreamWithSupportedCodec.BitDepth, audioStreamWithSupportedCodec.Profile, false)))
+                    .Select(i => i.Conditions.All(condition =>
+                    {
+                        var satisfied = ConditionProcessor.IsVideoAudioConditionSatisfied(condition, audioStreamWithSupportedCodec.Channels, audioStreamWithSupportedCodec.BitRate, audioStreamWithSupportedCodec.SampleRate, audioStreamWithSupportedCodec.BitDepth, audioStreamWithSupportedCodec.Profile, false);
+                        if (!satisfied)
+                        {
+                            playlistItem.TranscodeReasons |= GetTranscodeReasonForFailedCondition(condition);
+                        }
+
+                        return satisfied;
+                    }))
+                    .All(satisfied => satisfied);
 
-            var channelsExceedsLimit = audioStreamWithSupportedCodec is not null && directAudioStream is null;
+            var directAudioStream = directAudioStreamSatisfied ? audioStreamWithSupportedCodec : null;
 
             if (channelsExceedsLimit && playlistItem.TargetAudioStream is not null)
             {

+ 29 - 0
MediaBrowser.Model/Dlna/TranscodingProfile.cs

@@ -1,3 +1,4 @@
+using System;
 using System.ComponentModel;
 using System.Xml.Serialization;
 using Jellyfin.Data.Enums;
@@ -6,6 +7,7 @@ namespace MediaBrowser.Model.Dlna;
 
 /// <summary>
 /// A class for transcoding profile information.
+/// Note for client developers: Conditions defined in <see cref="CodecProfile"/> has higher priority and can override values defined here.
 /// </summary>
 public class TranscodingProfile
 {
@@ -17,6 +19,33 @@ public class TranscodingProfile
         Conditions = [];
     }
 
+    /// <summary>
+    /// Initializes a new instance of the <see cref="TranscodingProfile" /> class copying the values from another instance.
+    /// </summary>
+    /// <param name="other">Another instance of <see cref="TranscodingProfile" /> to be copied.</param>
+    public TranscodingProfile(TranscodingProfile other)
+    {
+        ArgumentNullException.ThrowIfNull(other);
+
+        Container = other.Container;
+        Type = other.Type;
+        VideoCodec = other.VideoCodec;
+        AudioCodec = other.AudioCodec;
+        Protocol = other.Protocol;
+        EstimateContentLength = other.EstimateContentLength;
+        EnableMpegtsM2TsMode = other.EnableMpegtsM2TsMode;
+        TranscodeSeekInfo = other.TranscodeSeekInfo;
+        CopyTimestamps = other.CopyTimestamps;
+        Context = other.Context;
+        EnableSubtitlesInManifest = other.EnableSubtitlesInManifest;
+        MaxAudioChannels = other.MaxAudioChannels;
+        MinSegments = other.MinSegments;
+        SegmentLength = other.SegmentLength;
+        BreakOnNonKeyFrames = other.BreakOnNonKeyFrames;
+        Conditions = other.Conditions;
+        EnableAudioVbrEncoding = other.EnableAudioVbrEncoding;
+    }
+
     /// <summary>
     /// Gets or sets the container.
     /// </summary>