소스 검색

Fix OverflowException when scanning media with a very short duration (#13949)

Bond-009 1 개월 전
부모
커밋
74230131a1

+ 1 - 1
MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs

@@ -965,7 +965,7 @@ namespace MediaBrowser.MediaEncoding.Probing
                     // Get average bitrate info from tag "NUMBER_OF_BYTES" and "DURATION" if possible.
                     var durationInSeconds = GetRuntimeSecondsFromTags(streamInfo);
                     var bytes = GetNumberOfBytesFromTags(streamInfo);
-                    if (durationInSeconds is not null && bytes is not null)
+                    if (durationInSeconds is not null && durationInSeconds.Value >= 1 && bytes is not null)
                     {
                         bps = Convert.ToInt32(bytes * 8 / durationInSeconds, CultureInfo.InvariantCulture);
                         if (bps > 0)

+ 34 - 0
tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs

@@ -288,6 +288,40 @@ namespace Jellyfin.MediaEncoding.Tests.Probing
             Assert.True(res.VideoStream.IsDefault);
         }
 
+        [Fact]
+        public void GetMediaInfo_VideoWithSingleFrameMjpeg_Success()
+        {
+            var bytes = File.ReadAllBytes("Test Data/Probing/video_single_frame_mjpeg.json");
+
+            var internalMediaInfoResult = JsonSerializer.Deserialize<InternalMediaInfoResult>(bytes, _jsonOptions);
+            MediaInfo res = _probeResultNormalizer.GetMediaInfo(internalMediaInfoResult, VideoType.VideoFile, false, "Test Data/Probing/video_interlaced.mp4", MediaProtocol.File);
+
+            Assert.Equal(3, res.MediaStreams.Count);
+
+            Assert.NotNull(res.VideoStream);
+            Assert.Equal(res.MediaStreams[0], res.VideoStream);
+            Assert.Equal(0, res.VideoStream.Index);
+            Assert.Equal("h264", res.VideoStream.Codec);
+            Assert.Equal("High", res.VideoStream.Profile);
+            Assert.Equal(MediaStreamType.Video, res.VideoStream.Type);
+            Assert.Equal(1080, res.VideoStream.Height);
+            Assert.Equal(1920, res.VideoStream.Width);
+            Assert.False(res.VideoStream.IsInterlaced);
+            Assert.Equal("16:9", res.VideoStream.AspectRatio);
+            Assert.Equal("yuv420p", res.VideoStream.PixelFormat);
+            Assert.Equal(42d, res.VideoStream.Level);
+            Assert.Equal(1, res.VideoStream.RefFrames);
+            Assert.True(res.VideoStream.IsAVC);
+            Assert.Equal(50f, res.VideoStream.RealFrameRate);
+            Assert.Equal("1/1000", res.VideoStream.TimeBase);
+            Assert.Equal(8, res.VideoStream.BitDepth);
+            Assert.True(res.VideoStream.IsDefault);
+
+            var mjpeg = res.MediaStreams[2];
+            Assert.NotNull(mjpeg);
+            Assert.Equal("mjpeg", mjpeg.Codec);
+        }
+
         [Fact]
         public void GetMediaInfo_MusicVideo_Success()
         {

+ 209 - 0
tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/video_single_frame_mjpeg.json

@@ -0,0 +1,209 @@
+{
+    "streams": [
+        {
+            "index": 0,
+            "codec_name": "h264",
+            "codec_long_name": "H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10",
+            "profile": "High",
+            "codec_type": "video",
+            "codec_tag_string": "[0][0][0][0]",
+            "codec_tag": "0x0000",
+            "width": 1920,
+            "height": 1080,
+            "coded_width": 1920,
+            "coded_height": 1080,
+            "closed_captions": 0,
+            "film_grain": 0,
+            "has_b_frames": 0,
+            "sample_aspect_ratio": "1:1",
+            "display_aspect_ratio": "16:9",
+            "pix_fmt": "yuv420p",
+            "level": 42,
+            "chroma_location": "left",
+            "field_order": "progressive",
+            "refs": 1,
+            "is_avc": "true",
+            "nal_length_size": "4",
+            "r_frame_rate": "50/1",
+            "avg_frame_rate": "50/1",
+            "time_base": "1/1000",
+            "start_pts": 0,
+            "start_time": "0.000000",
+            "bits_per_raw_sample": "8",
+            "extradata_size": 55,
+            "disposition": {
+                "default": 1,
+                "dub": 0,
+                "original": 0,
+                "comment": 0,
+                "lyrics": 0,
+                "karaoke": 0,
+                "forced": 0,
+                "hearing_impaired": 0,
+                "visual_impaired": 0,
+                "clean_effects": 0,
+                "attached_pic": 0,
+                "timed_thumbnails": 0,
+                "non_diegetic": 0,
+                "captions": 0,
+                "descriptions": 0,
+                "metadata": 0,
+                "dependent": 0,
+                "still_image": 0
+            },
+            "tags": {
+                "language": "deu",
+                "HANDLER_NAME": "VideoHandler",
+                "VENDOR_ID": "[0][0][0][0]",
+                "BPS": "3950584",
+                "DURATION": "00:00:10.000000000",
+                "NUMBER_OF_FRAMES": "500",
+                "NUMBER_OF_BYTES": "4938231",
+                "_STATISTICS_WRITING_APP": "mkvpropedit v90.0 ('Hanging On') 64-bit",
+                "_STATISTICS_WRITING_DATE_UTC": "2025-04-19 10:37:57",
+                "_STATISTICS_TAGS": "BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES"
+            }
+        },
+        {
+            "index": 1,
+            "codec_name": "aac",
+            "codec_long_name": "AAC (Advanced Audio Coding)",
+            "profile": "LC",
+            "codec_type": "audio",
+            "codec_tag_string": "[0][0][0][0]",
+            "codec_tag": "0x0000",
+            "sample_fmt": "fltp",
+            "sample_rate": "48000",
+            "channels": 2,
+            "channel_layout": "stereo",
+            "bits_per_sample": 0,
+            "initial_padding": 0,
+            "r_frame_rate": "0/0",
+            "avg_frame_rate": "0/0",
+            "time_base": "1/1000",
+            "start_pts": 0,
+            "start_time": "0.000000",
+            "extradata_size": 2,
+            "disposition": {
+                "default": 1,
+                "dub": 0,
+                "original": 0,
+                "comment": 0,
+                "lyrics": 0,
+                "karaoke": 0,
+                "forced": 0,
+                "hearing_impaired": 0,
+                "visual_impaired": 0,
+                "clean_effects": 0,
+                "attached_pic": 0,
+                "timed_thumbnails": 0,
+                "non_diegetic": 0,
+                "captions": 0,
+                "descriptions": 0,
+                "metadata": 0,
+                "dependent": 0,
+                "still_image": 0
+            },
+            "tags": {
+                "language": "deu",
+                "HANDLER_NAME": "SoundHandler",
+                "VENDOR_ID": "[0][0][0][0]",
+                "BPS": "255785",
+                "DURATION": "00:00:09.984000000",
+                "NUMBER_OF_FRAMES": "469",
+                "NUMBER_OF_BYTES": "319220",
+                "_STATISTICS_WRITING_APP": "mkvpropedit v90.0 ('Hanging On') 64-bit",
+                "_STATISTICS_WRITING_DATE_UTC": "2025-04-19 10:37:57",
+                "_STATISTICS_TAGS": "BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES"
+            }
+        },
+        {
+            "index": 2,
+            "codec_name": "mjpeg",
+            "codec_long_name": "Motion JPEG",
+            "profile": "Baseline",
+            "codec_type": "video",
+            "codec_tag_string": "[0][0][0][0]",
+            "codec_tag": "0x0000",
+            "width": 960,
+            "height": 540,
+            "coded_width": 960,
+            "coded_height": 540,
+            "closed_captions": 0,
+            "film_grain": 0,
+            "has_b_frames": 0,
+            "sample_aspect_ratio": "1:1",
+            "display_aspect_ratio": "16:9",
+            "pix_fmt": "yuvj420p",
+            "level": -99,
+            "color_range": "pc",
+            "color_space": "bt470bg",
+            "chroma_location": "center",
+            "refs": 1,
+            "r_frame_rate": "1000/1",
+            "avg_frame_rate": "30000/1",
+            "time_base": "1/1000",
+            "start_pts": 0,
+            "start_time": "0.000000",
+            "bits_per_raw_sample": "8",
+            "disposition": {
+                "default": 0,
+                "dub": 0,
+                "original": 0,
+                "comment": 0,
+                "lyrics": 0,
+                "karaoke": 0,
+                "forced": 0,
+                "hearing_impaired": 0,
+                "visual_impaired": 0,
+                "clean_effects": 0,
+                "attached_pic": 0,
+                "timed_thumbnails": 0,
+                "non_diegetic": 0,
+                "captions": 0,
+                "descriptions": 0,
+                "metadata": 0,
+                "dependent": 0,
+                "still_image": 0
+            },
+            "tags": {
+                "BPS": "0",
+                "DURATION": "00:00:00.000011111",
+                "NUMBER_OF_FRAMES": "1",
+                "NUMBER_OF_BYTES": "155034",
+                "_STATISTICS_WRITING_APP": "mkvpropedit v90.0 ('Hanging On') 64-bit",
+                "_STATISTICS_WRITING_DATE_UTC": "2025-04-19 10:37:57",
+                "_STATISTICS_TAGS": "BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES"
+            }
+        }
+    ],
+    "chapters": [],
+    "format": {
+        "filename": "file:broken_mkv_covers - 01x03 - statistics added using mkvpropedit.mkv",
+        "nb_streams": 3,
+        "nb_programs": 0,
+        "nb_stream_groups": 0,
+        "format_name": "matroska,webm",
+        "format_long_name": "Matroska / WebM",
+        "start_time": "0.000000",
+        "duration": "10.005000",
+        "size": "5425928",
+        "bit_rate": "4338573",
+        "probe_score": 100,
+        "tags": {
+            "title": "Folge 1: Jackpot Immobilie · Wie wir klug vererben",
+            "EPISODE_SORT": "1",
+            "MAJOR_BRAND": "isom",
+            "MINOR_VERSION": "512",
+            "COMPATIBLE_BRANDS": "isomiso2avc1mp41",
+            "DATE": "20250318",
+            "SEASON_NUMBER": "1",
+            "COMMENT": "https://www.ardmediathek.de/video/Y3JpZDovL2JyLmRlL2Jyb2FkY2FzdC8zZWFmYTcxOC1mZDJmLTRmZTMtYWE4Ny03ZjdlNWViNTk1NDhfb25saW5lYnJvYWRjYXN0",
+            "DESCRIPTION": "Jeder kennt es: Enge Familienmitglieder sprechen plötzlich nicht mehr miteinander, der Streit ums Erbe entzweit die Familie. Was steht im Testament? Kann sich die Erbengemeinschaft einigen? Wie läuft das mit der Erbschaftssteuer und was sagt das Erbrecht?\n\nDas \"Lohnt sich das?\"-Team hat zwei Familien gefunden, die das Tabu rund ums Erben brechen. Sie erzählen, um wie viel Geld es geht, aber auch, was dieses Immobilienerbe mit ihrer Familie gemacht hat. Warum es einmal gelingt, Häuser in Millionenhöhe ohne jegliche Erbschaftssteuer zu vererben, während andere Geschwister sich über ihr Elternhaus komplett zerstritten haben. Erbfolge, Freibeträge, Nießbrauch - für alle Basics rund ums Erben ist Ralph Caspers zuständig, der kompetent erklärt. ",
+            "SYNOPSIS": "Jeder kennt es: Enge Familienmitglieder sprechen plötzlich nicht mehr miteinander, der Streit ums Erbe entzweit die Familie. Was steht im Testament? Kann sich die Erbengemeinschaft einigen? Wie läuft das mit der Erbschaftssteuer und was sagt das Erbrecht?\n\nDas \"Lohnt sich das?\"-Team hat zwei Familien gefunden, die das Tabu rund ums Erben brechen. Sie erzählen, um wie viel Geld es geht, aber auch, was dieses Immobilienerbe mit ihrer Familie gemacht hat. Warum es einmal gelingt, Häuser in Millionenhöhe ohne jegliche Erbschaftssteuer zu vererben, während andere Geschwister sich über ihr Elternhaus komplett zerstritten haben. Erbfolge, Freibeträge, Nießbrauch - für alle Basics rund ums Erben ist Ralph Caspers zuständig, der kompetent erklärt. ",
+            "SHOW": "Generation Wohnkrise. Lohnt sich das?",
+            "EPISODE_ID": "Folge 1: Jackpot Immobilie · Wie wir klug vererben",
+            "ENCODER": "Lavf61.7.100"
+        }
+    }
+}