Browse Source

Fix transcode video matching and add tests for Transcode and Safari

Isaac Gordezky 3 years ago
parent
commit
84a3db6f84

+ 13 - 6
MediaBrowser.Model/Dlna/StreamBuilder.cs

@@ -749,13 +749,20 @@ namespace MediaBrowser.Model.Dlna
                 transcodingProfiles = transcodingProfiles.ToLookup(transcodingProfile =>
                 {
                     var videoCodecs = ContainerProfile.SplitValue(transcodingProfile.VideoCodec);
-                    var match = ContainerProfile.ContainsContainer(videoCodecs, item.VideoStream.Codec) &&
-                        options.Profile.CodecProfiles
-                        .Any(i => i.Type == CodecType.Video &&
-                            i.ContainsAnyCodec(transcodingProfile.VideoCodec, transcodingProfile.Container) &&
-                            i.ApplyConditions.Any(applyCondition => !ConditionProcessor.IsVideoConditionSatisfied(applyCondition, videoStream?.Width, videoStream?.Height, videoStream?.BitDepth, videoStream?.BitRate, videoStream?.Profile, videoStream?.Level, videoFramerate,  videoStream?.PacketLength, timestamp, videoStream?.IsAnamorphic, videoStream?.IsInterlaced, videoStream?.RefFrames, numVideoStreams, numAudioStreams, videoStream?.CodecTag, videoStream?.IsAVC)));
 
-                    return match ? 1 : 2;
+                    if (ContainerProfile.ContainsContainer(videoCodecs, item.VideoStream.Codec)) {
+                        var videoCodec = transcodingProfile.VideoCodec;
+                        var container = transcodingProfile.Container;
+                        var appliedVideoConditions = options.Profile.CodecProfiles
+                            .Where(i => i.Type == CodecType.Video &&
+                                i.ContainsAnyCodec(videoCodec, container))
+                            .Select(i =>
+                                i.ApplyConditions.Any(applyCondition => ConditionProcessor.IsVideoConditionSatisfied(applyCondition, videoStream?.Width, videoStream?.Height, videoStream?.BitDepth, videoStream?.BitRate, videoStream?.Profile, videoStream?.Level, videoFramerate, videoStream?.PacketLength, timestamp, videoStream?.IsAnamorphic, videoStream?.IsInterlaced, videoStream?.RefFrames, numVideoStreams, numAudioStreams, videoStream?.CodecTag, videoStream?.IsAVC)));
+                        var conditionsSatisfied = !appliedVideoConditions.Any() || !appliedVideoConditions.Any(satisfied => !satisfied);
+                        return conditionsSatisfied ? 1 : 2;
+                    }
+
+                    return 3;
                 })
                 .OrderBy(lookup => lookup.Key)
                 .SelectMany(lookup => lookup);

+ 24 - 17
tests/Jellyfin.Dlna.Tests/StreamBuilderTests.cs

@@ -47,9 +47,9 @@ namespace Jellyfin.MediaBrowser.Model.Tests
         [InlineData("SafariNext", "mp4-h264-ac3-aacDef-srt-2600k", PlayMethod.DirectPlay)] // #6450
         [InlineData("SafariNext", "mp4-h264-ac3-aacExt-srt-2600k", PlayMethod.DirectPlay)] // #6450
         [InlineData("SafariNext", "mp4-h264-ac3-srt-2600k", PlayMethod.DirectPlay)] // #6450
-        [InlineData("SafariNext", "mp4-hevc-aac-srt-15200k", PlayMethod.DirectPlay)] // #6450
-        [InlineData("SafariNext", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.DirectPlay)] // #6450
-        [InlineData("SafariNext", "mp4-hevc-ac3-aacExt-srt-15200k", PlayMethod.DirectPlay)] // #6450
+        [InlineData("SafariNext", "mp4-hevc-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Remux", "HLS.mp4")] // #6450
+        [InlineData("SafariNext", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Remux", "HLS.mp4")] // #6450
+        [InlineData("SafariNext", "mp4-hevc-ac3-aacExt-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Remux", "HLS.mp4")] // #6450
         // AndroidPixel
         [InlineData("AndroidPixel", "mp4-h264-aac-srt-2600k", PlayMethod.DirectPlay)] // #6450
         [InlineData("AndroidPixel", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectPlay)] // #6450
@@ -93,14 +93,16 @@ namespace Jellyfin.MediaBrowser.Model.Tests
         [InlineData("Chrome-NoHLS", "mkv-vp9-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450
         [InlineData("Chrome-NoHLS", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectPlay, TranscodeReason.None, "Remux")] // #6450
         // TranscodeMedia
-        [InlineData("TranscodeMedia", "mp4-h264-aac-vtt-2600k", PlayMethod.Transcode, TranscodeReason.DirectPlayError, "Transcode")]
-        [InlineData("TranscodeMedia", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.Transcode, TranscodeReason.DirectPlayError, "Transcode")]
-        [InlineData("TranscodeMedia", "mp4-h264-ac3-srt-2600k", PlayMethod.Transcode, TranscodeReason.DirectPlayError, "Transcode")]
-        [InlineData("TranscodeMedia", "mp4-hevc-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.DirectPlayError, "Transcode")]
-        [InlineData("TranscodeMedia", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.DirectPlayError, "Transcode")]
-        [InlineData("TranscodeMedia", "mkv-vp9-aac-srt-2600k", PlayMethod.Transcode, TranscodeReason.DirectPlayError, "Transcode")]
-        [InlineData("TranscodeMedia", "mkv-vp9-ac3-srt-2600k", PlayMethod.Transcode, TranscodeReason.DirectPlayError, "Transcode")]
-        [InlineData("TranscodeMedia", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.Transcode, TranscodeReason.DirectPlayError, "Transcode")]
+        [InlineData("TranscodeMedia", "mp4-h264-aac-vtt-2600k", PlayMethod.Transcode, TranscodeReason.DirectPlayError, "Remux", "HLS.mp4")]
+        [InlineData("TranscodeMedia", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.Transcode, TranscodeReason.DirectPlayError, "Remux", "HLS.mp4")]
+        [InlineData("TranscodeMedia", "mp4-h264-ac3-srt-2600k", PlayMethod.Transcode, TranscodeReason.DirectPlayError, "DirectStream", "HLS.mp4")]
+        [InlineData("TranscodeMedia", "mp4-hevc-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.DirectPlayError, "Remux", "HLS.mp4")]
+        [InlineData("TranscodeMedia", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.DirectPlayError, "Remux", "HLS.mp4")]
+        [InlineData("TranscodeMedia", "mkv-av1-aac-srt-2600k", PlayMethod.Transcode, TranscodeReason.DirectPlayError, "DirectStream", "http")]
+        [InlineData("TranscodeMedia", "mkv-av1-vorbis-srt-2600k", PlayMethod.Transcode, TranscodeReason.DirectPlayError, "Remux", "http")]
+        [InlineData("TranscodeMedia", "mkv-vp9-aac-srt-2600k", PlayMethod.Transcode, TranscodeReason.DirectPlayError, "DirectStream", "http")]
+        [InlineData("TranscodeMedia", "mkv-vp9-ac3-srt-2600k", PlayMethod.Transcode, TranscodeReason.DirectPlayError, "DirectStream", "http")]
+        [InlineData("TranscodeMedia", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.Transcode, TranscodeReason.DirectPlayError, "Remux", "http")]
         // DirectMedia
         [InlineData("DirectMedia", "mp4-h264-aac-vtt-2600k", PlayMethod.DirectPlay, TranscodeReason.None, "Remux")]
         [InlineData("DirectMedia", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectPlay, TranscodeReason.None, "Remux")]
@@ -129,8 +131,6 @@ namespace Jellyfin.MediaBrowser.Model.Tests
         [InlineData("Null", "mkv-vp9-aac-srt-2600k", null, TranscodeReason.ContainerBitrateExceedsLimit)]
         [InlineData("Null", "mkv-vp9-ac3-srt-2600k", null, TranscodeReason.ContainerBitrateExceedsLimit)]
         [InlineData("Null", "mkv-vp9-vorbis-vtt-2600k", null, TranscodeReason.ContainerBitrateExceedsLimit)]
-
-        // [InlineData("Chrome", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported | TranscodeReason.AudioCodecNotSupported, "Transcode")]
         public async Task BuildVideoItemSimple(string deviceName, string mediaSource, PlayMethod? playMethod, TranscodeReason why = TranscodeReason.None, string transcodeMode = "DirectStream", string transcodeProtocol = "")
         {
             var options = await GetVideoOptions(deviceName, mediaSource);
@@ -165,9 +165,9 @@ namespace Jellyfin.MediaBrowser.Model.Tests
         [InlineData("SafariNext", "mp4-h264-ac3-aacDef-srt-2600k", PlayMethod.DirectPlay)] // #6450
         [InlineData("SafariNext", "mp4-h264-ac3-aacExt-srt-2600k", PlayMethod.DirectPlay)] // #6450
         [InlineData("SafariNext", "mp4-h264-ac3-srt-2600k", PlayMethod.DirectPlay)] // #6450
-        [InlineData("SafariNext", "mp4-hevc-aac-srt-15200k", PlayMethod.DirectPlay)] // #6450
-        [InlineData("SafariNext", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.DirectPlay)] // #6450
-        [InlineData("SafariNext", "mp4-hevc-ac3-aacExt-srt-15200k", PlayMethod.DirectPlay)] // #6450
+        [InlineData("SafariNext", "mp4-hevc-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Remux", "HLS.mp4")] // #6450
+        [InlineData("SafariNext", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Remux", "HLS.mp4")] // #6450
+        [InlineData("SafariNext", "mp4-hevc-ac3-aacExt-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Remux", "HLS.mp4")] // #6450
         // AndroidPixel
         [InlineData("AndroidPixel", "mp4-h264-aac-srt-2600k", PlayMethod.DirectPlay)] // #6450
         [InlineData("AndroidPixel", "mp4-h264-ac3-aacDef-srt-2600k", PlayMethod.DirectPlay)] // #6450
@@ -239,7 +239,7 @@ namespace Jellyfin.MediaBrowser.Model.Tests
         {
             if (string.IsNullOrEmpty(transcodeProtocol))
             {
-                transcodeProtocol = playMethod == PlayMethod.DirectStream ? "http" : "hls";
+                transcodeProtocol = playMethod == PlayMethod.DirectStream ? "http" : "HLS.ts";
             }
 
             var builder = GetStreamBuilder();
@@ -302,6 +302,13 @@ namespace Jellyfin.MediaBrowser.Model.Tests
                     Assert.Equal("stream", uri.Filename);
                     Assert.Equal("http", val.SubProtocol);
                 }
+                else if (transcodeProtocol == "HLS.mp4")
+                {
+                    Assert.Equal("mp4", val.Container);
+                    Assert.Equal("m3u8", uri.Extension);
+                    Assert.Equal("master", uri.Filename);
+                    Assert.Equal("hls", val.SubProtocol);
+                }
                 else
                 {
                     Assert.Equal("ts", val.Container);

+ 23 - 37
tests/Jellyfin.Dlna.Tests/Test Data/DeviceProfile-SafariNext.json

@@ -24,14 +24,14 @@
         {
             "Container": "mp4,m4v",
             "AudioCodec": "aac,mp3,ac3,eac3,flac,alac,vorbis",
-            "VideoCodec": "hevc,h264,vp8,vp9",
+            "VideoCodec": "h264,vp8,vp9",
             "Type": "Video",
             "$type": "DirectPlayProfile"
         },
         {
             "Container": "mov",
             "AudioCodec": "aac,mp3,ac3,eac3,flac,alac,vorbis",
-            "VideoCodec": "hevc,h264",
+            "VideoCodec": "h264",
             "Type": "Video",
             "$type": "DirectPlayProfile"
         },
@@ -217,57 +217,43 @@
             "$type": "TranscodingProfile"
         },
         {
-            "Container": "ts",
+            "Container": "mp4",
             "Type": "Video",
+            "AudioCodec": "aac,ac3,eac3,flac,alac",
             "VideoCodec": "hevc,h264",
-            "AudioCodec": "aac,mp3,ac3,eac3",
+            "Context": "Streaming",
             "Protocol": "hls",
-            "EstimateContentLength": false,
-            "EnableMpegtsM2TsMode": false,
-            "TranscodeSeekInfo": "Auto",
-            "CopyTimestamps": false,
+            "MaxAudioChannels": "2",
+            "MinSegments": "2",
+            "BreakOnNonKeyFrames": true
+        },
+        {
+            "Container": "ts",
+            "Type": "Video",
+            "AudioCodec": "aac,mp3,ac3,eac3",
+            "VideoCodec": "h264",
             "Context": "Streaming",
-            "EnableSubtitlesInManifest": false,
-            "MaxAudioChannels": "6",
-            "MinSegments": 2,
-            "SegmentLength": 0,
-            "BreakOnNonKeyFrames": true,
-            "$type": "TranscodingProfile"
+            "Protocol": "hls",
+            "MaxAudioChannels": "2",
+            "MinSegments": "2",
+            "BreakOnNonKeyFrames": true
         },
         {
             "Container": "webm",
             "Type": "Video",
-            "VideoCodec": "vp8,vp9,vpx",
             "AudioCodec": "vorbis",
-            "Protocol": "http",
-            "EstimateContentLength": false,
-            "EnableMpegtsM2TsMode": false,
-            "TranscodeSeekInfo": "Auto",
-            "CopyTimestamps": false,
+            "VideoCodec": "vp8,vpx",
             "Context": "Streaming",
-            "EnableSubtitlesInManifest": false,
-            "MaxAudioChannels": "6",
-            "MinSegments": 0,
-            "SegmentLength": 0,
-            "BreakOnNonKeyFrames": false,
-            "$type": "TranscodingProfile"
+            "Protocol": "http",
+            "MaxAudioChannels": "2"
         },
         {
             "Container": "mp4",
             "Type": "Video",
-            "VideoCodec": "hevc,h264",
             "AudioCodec": "aac,mp3,ac3,eac3,flac,alac,vorbis",
-            "Protocol": "http",
-            "EstimateContentLength": false,
-            "EnableMpegtsM2TsMode": false,
-            "TranscodeSeekInfo": "Auto",
-            "CopyTimestamps": false,
+            "VideoCodec": "h264",
             "Context": "Static",
-            "EnableSubtitlesInManifest": false,
-            "MinSegments": 0,
-            "SegmentLength": 0,
-            "BreakOnNonKeyFrames": false,
-            "$type": "TranscodingProfile"
+            "Protocol": "http"
         }
     ],
     "CodecProfiles": [

+ 27 - 11
tests/Jellyfin.Dlna.Tests/Test Data/DeviceProfile-TranscodeMedia.json

@@ -19,22 +19,38 @@
             "BreakOnNonKeyFrames": false,
             "$type": "TranscodingProfile"
         },
+        {
+            "Container": "mp4",
+            "Type": "Video",
+            "AudioCodec": "aac,flac,alac",
+            "VideoCodec": "hevc,h264",
+            "Context": "Streaming",
+            "Protocol": "hls",
+            "MaxAudioChannels": "2",
+            "MinSegments": "2",
+            "BreakOnNonKeyFrames": true,
+            "$type": "TranscodingProfile"
+        },
         {
             "Container": "ts",
             "Type": "Video",
-            "VideoCodec": "h264,h265,hevc,mpeg4,mpeg2video",
-            "AudioCodec": "aac,mp3,ac3,opus,flac,vorbis",
+            "AudioCodec": "aac,mp3",
+            "VideoCodec": "h264",
+            "Context": "Streaming",
             "Protocol": "hls",
-            "EstimateContentLength": false,
-            "EnableMpegtsM2TsMode": false,
-            "TranscodeSeekInfo": "Auto",
-            "CopyTimestamps": false,
+            "MaxAudioChannels": "2",
+            "MinSegments": "2",
+            "BreakOnNonKeyFrames": true,
+            "$type": "TranscodingProfile"
+        },
+        {
+            "Container": "webm",
+            "Type": "Video",
+            "AudioCodec": "vorbis",
+            "VideoCodec": "vp9,vp8,vpx,av1",
             "Context": "Streaming",
-            "EnableSubtitlesInManifest": false,
-            "MaxAudioChannels": "6",
-            "MinSegments": 0,
-            "SegmentLength": 0,
-            "BreakOnNonKeyFrames": false,
+            "Protocol": "http",
+            "MaxAudioChannels": "2",
             "$type": "TranscodingProfile"
         },
         {

+ 73 - 0
tests/Jellyfin.Dlna.Tests/Test Data/MediaSourceInfo-mkv-av1-aac-srt-2600k.json

@@ -0,0 +1,73 @@
+{
+    "Id": "a766d122b58e45d9492d17af66748bf5",
+    "Path": "/Media/MyVideo-720p.mkv",
+    "Container": "mkv,webm",
+    "Size": 835317696,
+    "Name": "MyVideo-1080p",
+    "ETag": "579a34c6d5dfb23f61539a51220b6a23",
+    "RunTimeTicks": 25801230336,
+    "SupportsTranscoding": true,
+    "SupportsDirectStream": true,
+    "SupportsDirectPlay": true,
+    "SupportsProbing": true,
+    "MediaStreams": [
+        {
+            "Codec": "av1",
+            "Language": "eng",
+            "ColorTransfer": "bt709",
+            "ColorPrimaries": "bt709",
+            "TimeBase": "1/11988",
+            "VideoRange": "SDR",
+            "DisplayTitle": "1080p AV1 SDR",
+            "NalLengthSize": "0",
+            "BitRate": 2032876,
+            "BitDepth": 8,
+            "RefFrames": 1,
+            "IsDefault": true,
+            "Height": 720,
+            "Width": 1280,
+            "AverageFrameRate": 23.976,
+            "RealFrameRate": 23.976,
+            "Profile": "Main",
+            "Type": 1,
+            "AspectRatio": "16:9",
+            "PixelFormat": "yuv420p",
+            "Level": -99
+        },
+        {
+            "Codec": "aac",
+            "CodecTag": "mp4a",
+            "Language": "eng",
+            "TimeBase": "1/48000",
+            "DisplayTitle": "En - AAC - Stereo - Default",
+            "ChannelLayout": "stereo",
+            "BitRate": 164741,
+            "Channels": 2,
+            "SampleRate": 48000,
+            "IsDefault": true,
+            "Profile": "LC",
+            "Index": 1,
+            "Score": 203
+        },
+        {
+            "Codec": "srt",
+            "Language": "eng",
+            "TimeBase": "1/1000000",
+            "localizedUndefined": "Undefined",
+            "localizedDefault": "Default",
+            "localizedForced": "Forced",
+            "DisplayTitle": "En - Default",
+            "BitRate": 92,
+            "IsDefault": true,
+            "Type": 2,
+            "Index": 2,
+            "Score": 6421,
+            "IsExternal": true,
+            "IsTextSubtitleStream": true,
+            "SupportsExternalStream": true
+        }
+    ],
+    "Bitrate": 2590008,
+    "DefaultAudioStreamIndex": 1,
+    "DefaultSubtitleStreamIndex": 2
+}

+ 72 - 0
tests/Jellyfin.Dlna.Tests/Test Data/MediaSourceInfo-mkv-av1-vorbis-srt-2600k.json

@@ -0,0 +1,72 @@
+{
+    "Id": "a766d122b58e45d9492d17af66748bf5",
+    "Path": "/Media/MyVideo-720p.mkv",
+    "Container": "mkv,webm",
+    "Size": 835317696,
+    "Name": "MyVideo-1080p",
+    "ETag": "579a34c6d5dfb23f61539a51220b6a23",
+    "RunTimeTicks": 25801230336,
+    "SupportsTranscoding": true,
+    "SupportsDirectStream": true,
+    "SupportsDirectPlay": true,
+    "SupportsProbing": true,
+    "MediaStreams": [
+        {
+            "Codec": "av1",
+            "Language": "eng",
+            "ColorTransfer": "bt709",
+            "ColorPrimaries": "bt709",
+            "TimeBase": "1/11988",
+            "VideoRange": "SDR",
+            "DisplayTitle": "1080p AV1 SDR",
+            "NalLengthSize": "0",
+            "BitRate": 2032876,
+            "BitDepth": 8,
+            "RefFrames": 1,
+            "IsDefault": true,
+            "Height": 720,
+            "Width": 1280,
+            "AverageFrameRate": 23.976,
+            "RealFrameRate": 23.976,
+            "Profile": "Main",
+            "Type": 1,
+            "AspectRatio": "16:9",
+            "PixelFormat": "yuv420p",
+            "Level": -99
+        },
+        {
+            "Codec": "vorbis",
+            "CodecTag": "ogg",
+            "Language": "eng",
+            "TimeBase": "1/48000",
+            "DisplayTitle": "En - Vorbis - Stereo - Default",
+            "ChannelLayout": "stereo",
+            "BitRate": 164741,
+            "Channels": 2,
+            "SampleRate": 48000,
+            "IsDefault": true,
+            "Index": 1,
+            "Score": 203
+        },
+        {
+            "Codec": "srt",
+            "Language": "eng",
+            "TimeBase": "1/1000000",
+            "localizedUndefined": "Undefined",
+            "localizedDefault": "Default",
+            "localizedForced": "Forced",
+            "DisplayTitle": "En - Default",
+            "BitRate": 92,
+            "IsDefault": true,
+            "Type": 2,
+            "Index": 2,
+            "Score": 6421,
+            "IsExternal": true,
+            "IsTextSubtitleStream": true,
+            "SupportsExternalStream": true
+        }
+    ],
+    "Bitrate": 2590008,
+    "DefaultAudioStreamIndex": 1,
+    "DefaultSubtitleStreamIndex": 2
+}