From 444060037932cc2f2c66a28004e97631b887fff9 Mon Sep 17 00:00:00 2001 From: Caidy Date: Tue, 2 Apr 2024 21:04:25 +0800 Subject: fix: rtsp live stream ffprobe timeout (#11279) --- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'MediaBrowser.MediaEncoding/Encoder') diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 6c43315a87..8076780250 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -463,6 +463,11 @@ namespace MediaBrowser.MediaEncoding.Encoder extraArgs += " -user_agent " + userAgent; } + if (request.MediaSource.Protocol == MediaProtocol.Rtsp) + { + extraArgs += " -rtsp_transport tcp+udp -rtsp_flags prefer_tcp"; + } + return extraArgs; } -- cgit v1.2.3 From 6b6aab04ceadfd43a6bd0abb416b08006ff1ca6c Mon Sep 17 00:00:00 2001 From: gnattu Date: Mon, 8 Apr 2024 21:42:47 +0800 Subject: Fix apple audio codecs (#11315) --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 10 +++++++++- MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'MediaBrowser.MediaEncoding/Encoder') diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 250e0143f9..717b53a0b6 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -107,7 +107,6 @@ namespace MediaBrowser.Controller.MediaEncoding { "wmav2", 2 }, { "libmp3lame", 2 }, { "libfdk_aac", 6 }, - { "aac_at", 6 }, { "ac3", 6 }, { "eac3", 6 }, { "dca", 6 }, @@ -752,6 +751,15 @@ namespace MediaBrowser.Controller.MediaEncoding return "dca"; } + if (string.Equals(codec, "alac", StringComparison.OrdinalIgnoreCase)) + { + // The ffmpeg upstream breaks the AudioToolbox ALAC encoder in version 6.1 but fixes it in version 7.0. + // Since ALAC is lossless in quality and the AudioToolbox encoder is not faster, + // its only benefit is a smaller file size. + // To prevent problems, use the ffmpeg native encoder instead. + return "alac"; + } + return codec.ToLowerInvariant(); } diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index ae0284e3ab..5f0779dc7b 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -69,6 +69,7 @@ namespace MediaBrowser.MediaEncoding.Encoder "aac_at", "libfdk_aac", "ac3", + "alac", "dca", "libmp3lame", "libopus", -- cgit v1.2.3 From 40444316107bed26e073f43df2bcdc38873da6e8 Mon Sep 17 00:00:00 2001 From: gnattu Date: Tue, 7 May 2024 21:23:28 +0800 Subject: Fix broken hardware encoder and filter for trickplay (#11506) --- MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs | 2 ++ MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'MediaBrowser.MediaEncoding/Encoder') diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index 5f0779dc7b..ec39f159ed 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -82,6 +82,7 @@ namespace MediaBrowser.MediaEncoding.Encoder "av1_amf", "h264_qsv", "hevc_qsv", + "mjpeg_qsv", "av1_qsv", "h264_nvenc", "hevc_nvenc", @@ -89,6 +90,7 @@ namespace MediaBrowser.MediaEncoding.Encoder "h264_vaapi", "hevc_vaapi", "av1_vaapi", + "mjpeg_vaapi", "h264_v4l2m2m", "h264_videotoolbox", "hevc_videotoolbox", diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 8076780250..39431a9fc6 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -848,7 +848,7 @@ namespace MediaBrowser.MediaEncoding.Encoder inputArg = "-threads " + threads + " " + inputArg; // HW accel args set a different input thread count, only set if disabled } - var filterParam = encodingHelper.GetVideoProcessingFilterParam(jobState, options, jobState.OutputVideoCodec).Trim(); + var filterParam = encodingHelper.GetVideoProcessingFilterParam(jobState, options, vidEncoder).Trim(); if (string.IsNullOrWhiteSpace(filterParam)) { throw new InvalidOperationException("EncodingHelper returned empty or invalid filter parameters."); -- cgit v1.2.3 From 7336427ce661fd9c8bd78eeb23b750321d7387f6 Mon Sep 17 00:00:00 2001 From: gnattu Date: Fri, 17 May 2024 13:51:51 -0400 Subject: Backport pull request #11675 from jellyfin/release-10.9.z Fix quality parameter for vaapi_mjpeg Original-merge: ddd5c302b4fb7b07a5a46aa6d0026d9b37aa9b2c Merged-by: nielsvanvelzen Backported-by: Joshua M. Boniface --- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'MediaBrowser.MediaEncoding/Encoder') diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 39431a9fc6..8ea0f58ea3 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -871,6 +871,15 @@ namespace MediaBrowser.MediaEncoding.Encoder throw new InvalidOperationException("Empty or invalid input argument."); } + float? encoderQuality = qualityScale; + if (vidEncoder.Contains("vaapi", StringComparison.OrdinalIgnoreCase)) + { + // vaapi's mjpeg encoder uses jpeg quality divided by QP2LAMBDA (118) as input, instead of ffmpeg defined qscale + // ffmpeg qscale is a value from 1-31, with 1 being best quality and 31 being worst + // jpeg quality is a value from 0-100, with 0 being worst quality and 100 being best + encoderQuality = (100 - ((qualityScale - 1) * (100 / 30))) / 118; + } + // Output arguments var targetDirectory = Path.Combine(_configurationManager.ApplicationPaths.TempDirectory, Guid.NewGuid().ToString("N")); Directory.CreateDirectory(targetDirectory); @@ -884,7 +893,7 @@ namespace MediaBrowser.MediaEncoding.Encoder filterParam, outputThreads.GetValueOrDefault(_threads), vidEncoder, - qualityScale.HasValue ? "-qscale:v " + qualityScale.Value.ToString(CultureInfo.InvariantCulture) + " " : string.Empty, + qualityScale.HasValue ? "-qscale:v " + encoderQuality.Value.ToString(CultureInfo.InvariantCulture) + " " : string.Empty, "image2", outputPath); -- cgit v1.2.3 From debd9eb8ce3ee2731ee71f508b4260f654b12d02 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Sat, 25 May 2024 11:46:12 -0400 Subject: Backport pull request #11754 from jellyfin/release-10.9.z Fix BD/DVD folder chapter image extraction Original-merge: 52be8be28fa27c0c7b4f53dc32e00ec0543616a9 Merged-by: Bond-009 Backported-by: Joshua M. Boniface --- .../MediaEncoding/EncodingHelper.cs | 12 +----------- .../MediaEncoding/IMediaEncoder.cs | 15 +++++++++++++++ MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 19 ++++++++++++++++--- 3 files changed, 32 insertions(+), 14 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Encoder') diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index ed64156d69..5d5b645f6a 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -680,16 +680,6 @@ namespace MediaBrowser.Controller.MediaEncoding return -1; } - public string GetInputPathArgument(EncodingJobInfo state) - { - return state.MediaSource.VideoType switch - { - VideoType.Dvd => _mediaEncoder.GetInputArgument(_mediaEncoder.GetPrimaryPlaylistVobFiles(state.MediaPath, null).ToList(), state.MediaSource), - VideoType.BluRay => _mediaEncoder.GetInputArgument(_mediaEncoder.GetPrimaryPlaylistM2tsFiles(state.MediaPath).ToList(), state.MediaSource), - _ => _mediaEncoder.GetInputArgument(state.MediaPath, state.MediaSource) - }; - } - /// /// Gets the audio encoder. /// @@ -1203,7 +1193,7 @@ namespace MediaBrowser.Controller.MediaEncoding else { arg.Append(" -i ") - .Append(GetInputPathArgument(state)); + .Append(_mediaEncoder.GetInputPathArgument(state)); } // sub2video for external graphical subtitles diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs index e696fa52ce..26c353a54f 100644 --- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs +++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs @@ -245,6 +245,21 @@ namespace MediaBrowser.Controller.MediaEncoding /// A playlist. IReadOnlyList GetPrimaryPlaylistM2tsFiles(string path); + /// + /// Gets the input path argument from . + /// + /// The . + /// The input path argument. + string GetInputPathArgument(EncodingJobInfo state); + + /// + /// Gets the input path argument. + /// + /// The item path. + /// The . + /// The input path argument. + string GetInputPathArgument(string path, MediaSourceInfo mediaSource); + /// /// Generates a FFmpeg concat config for the source. /// diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 8ea0f58ea3..1197fc84c9 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -30,10 +30,8 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.IO; using MediaBrowser.Model.MediaInfo; -using Microsoft.AspNetCore.Components.Forms; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; -using static Nikse.SubtitleEdit.Core.Common.IfoParser; namespace MediaBrowser.MediaEncoding.Encoder { @@ -621,7 +619,7 @@ namespace MediaBrowser.MediaEncoding.Encoder ImageFormat? targetFormat, CancellationToken cancellationToken) { - var inputArgument = GetInputArgument(inputFile, mediaSource); + var inputArgument = GetInputPathArgument(inputFile, mediaSource); if (!isAudio) { @@ -1147,6 +1145,21 @@ namespace MediaBrowser.MediaEncoding.Encoder .ToList(); } + /// + public string GetInputPathArgument(EncodingJobInfo state) + => GetInputPathArgument(state.MediaPath, state.MediaSource); + + /// + public string GetInputPathArgument(string path, MediaSourceInfo mediaSource) + { + return mediaSource.VideoType switch + { + VideoType.Dvd => GetInputArgument(GetPrimaryPlaylistVobFiles(path, null).ToList(), mediaSource), + VideoType.BluRay => GetInputArgument(GetPrimaryPlaylistM2tsFiles(path).ToList(), mediaSource), + _ => GetInputArgument(path, mediaSource) + }; + } + /// public void GenerateConcatConfig(MediaSourceInfo source, string concatFilePath) { -- cgit v1.2.3 From 5c828df5670aafa9e79786a8310ff103cc57e05d Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Sat, 25 May 2024 11:46:16 -0400 Subject: Backport pull request #11781 from jellyfin/release-10.9.z Retain order blu-ray segments Original-merge: 2ddf2a7866a9010191de1057f7c7bbbc3cb6e93d Merged-by: Bond-009 Backported-by: Joshua M. Boniface --- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Encoder') diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 1197fc84c9..b84baaa380 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -1135,13 +1135,11 @@ namespace MediaBrowser.MediaEncoding.Encoder var validPlaybackFiles = _blurayExaminer.GetDiscInfo(path).Files; // Get all files from the BDMV/STREAMING directory - var directoryFiles = _fileSystem.GetFiles(Path.Join(path, "BDMV", "STREAM")); - // Only return playable local .m2ts files - return directoryFiles - .Where(f => validPlaybackFiles.Contains(f.Name, StringComparer.OrdinalIgnoreCase)) + return validPlaybackFiles + .Select(f => _fileSystem.GetFileInfo(Path.Join(path, "BDMV", "STREAM", f))) + .Where(f => f.Exists) .Select(f => f.FullName) - .Order() .ToList(); } -- cgit v1.2.3 From 833a1da355f7e1d4c734df94d1e286015403427b Mon Sep 17 00:00:00 2001 From: NotSaifA <69015839+NotSaifA@users.noreply.github.com> Date: Sat, 25 May 2024 11:46:19 -0400 Subject: Backport pull request #11790 from jellyfin/release-10.9.z Trickplay: kill ffmpeg when task is cancelled Original-merge: 4a344bebc08303edf888000bf52e64b1a4e8036f Merged-by: crobibero Backported-by: Joshua M. Boniface --- CONTRIBUTORS.md | 1 + MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 11 +++++++---- 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Encoder') diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 47c06998c0..76d57a4783 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -183,6 +183,7 @@ - [btopherjohnson](https://github.com/btopherjohnson) - [GeorgeH005](https://github.com/GeorgeH005) - [Vedant](https://github.com/viktory36/) + - [NotSaifA](https://github.com/NotSaifA) # Emby Contributors diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index b84baaa380..79abb13ac1 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -943,7 +943,7 @@ namespace MediaBrowser.MediaEncoding.Encoder var timeoutMs = _configurationManager.Configuration.ImageExtractionTimeoutMs; timeoutMs = timeoutMs <= 0 ? DefaultHdrImageExtractionTimeout : timeoutMs; - while (isResponsive) + while (isResponsive && !cancellationToken.IsCancellationRequested) { try { @@ -957,8 +957,6 @@ namespace MediaBrowser.MediaEncoding.Encoder // We don't actually expect the process to be finished in one timeout span, just that one image has been generated. } - cancellationToken.ThrowIfCancellationRequested(); - var jpegCount = _fileSystem.GetFilePaths(targetDirectory).Count(); isResponsive = jpegCount > lastCount; @@ -967,7 +965,12 @@ namespace MediaBrowser.MediaEncoding.Encoder if (!ranToCompletion) { - _logger.LogInformation("Stopping trickplay extraction due to process inactivity."); + if (!isResponsive) + { + _logger.LogInformation("Trickplay process unresponsive."); + } + + _logger.LogInformation("Stopping trickplay extraction."); StopProcess(processWrapper, 1000); } } -- cgit v1.2.3 From ac0064110b78b5d2cc7d7bfffb1b18c6193cfaeb Mon Sep 17 00:00:00 2001 From: gnattu Date: Sat, 25 May 2024 11:46:22 -0400 Subject: Backport pull request #11798 from jellyfin/release-10.9.z Recalculate trickplay image height for anamorphic videos Original-merge: d9232e05f1280f8f4315ca5b8fc92ddc4a71a96a Merged-by: Bond-009 Backported-by: Joshua M. Boniface --- .../MediaEncoding/EncodingHelper.cs | 27 ++++++++++++++-------- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 16 +++++++++++++ 2 files changed, 33 insertions(+), 10 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Encoder') diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index bef2944291..90d448c58d 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -3162,7 +3162,9 @@ namespace MediaBrowser.Controller.MediaEncoding int? requestedMaxHeight) { var isV4l2 = string.Equals(videoEncoder, "h264_v4l2m2m", StringComparison.OrdinalIgnoreCase); + var isMjpeg = videoEncoder is not null && videoEncoder.Contains("mjpeg", StringComparison.OrdinalIgnoreCase); var scaleVal = isV4l2 ? 64 : 2; + var targetAr = isMjpeg ? "(a*sar)" : "a"; // manually calculate AR when using mjpeg encoder // If fixed dimensions were supplied if (requestedWidth.HasValue && requestedHeight.HasValue) @@ -3191,10 +3193,11 @@ namespace MediaBrowser.Controller.MediaEncoding return string.Format( CultureInfo.InvariantCulture, - @"scale=trunc(min(max(iw\,ih*a)\,min({0}\,{1}*a))/{2})*{2}:trunc(min(max(iw/a\,ih)\,min({0}/a\,{1}))/2)*2", + @"scale=trunc(min(max(iw\,ih*{3})\,min({0}\,{1}*{3}))/{2})*{2}:trunc(min(max(iw/{3}\,ih)\,min({0}/{3}\,{1}))/2)*2", maxWidthParam, maxHeightParam, - scaleVal); + scaleVal, + targetAr); } // If a fixed width was requested @@ -3210,8 +3213,9 @@ namespace MediaBrowser.Controller.MediaEncoding return string.Format( CultureInfo.InvariantCulture, - "scale={0}:trunc(ow/a/2)*2", - widthParam); + "scale={0}:trunc(ow/{1}/2)*2", + widthParam, + targetAr); } // If a fixed height was requested @@ -3221,9 +3225,10 @@ namespace MediaBrowser.Controller.MediaEncoding return string.Format( CultureInfo.InvariantCulture, - "scale=trunc(oh*a/{1})*{1}:{0}", + "scale=trunc(oh*{2}/{1})*{1}:{0}", heightParam, - scaleVal); + scaleVal, + targetAr); } // If a max width was requested @@ -3233,9 +3238,10 @@ namespace MediaBrowser.Controller.MediaEncoding return string.Format( CultureInfo.InvariantCulture, - @"scale=trunc(min(max(iw\,ih*a)\,{0})/{1})*{1}:trunc(ow/a/2)*2", + @"scale=trunc(min(max(iw\,ih*{2})\,{0})/{1})*{1}:trunc(ow/{2}/2)*2", maxWidthParam, - scaleVal); + scaleVal, + targetAr); } // If a max height was requested @@ -3245,9 +3251,10 @@ namespace MediaBrowser.Controller.MediaEncoding return string.Format( CultureInfo.InvariantCulture, - @"scale=trunc(oh*a/{1})*{1}:min(max(iw/a\,ih)\,{0})", + @"scale=trunc(oh*{2}/{1})*{1}:min(max(iw/{2}\,ih)\,{0})", maxHeightParam, - scaleVal); + scaleVal, + targetAr); } return string.Empty; diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 79abb13ac1..80ef6ecf77 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -822,6 +822,22 @@ namespace MediaBrowser.MediaEncoding.Encoder options.EnableTonemapping = false; } + if (imageStream.Width is not null && imageStream.Height is not null && !string.IsNullOrEmpty(imageStream.AspectRatio)) + { + // For hardware trickplay encoders, we need to re-calculate the size because they used fixed scale dimensions + var darParts = imageStream.AspectRatio.Split(':'); + var (wa, ha) = (double.Parse(darParts[0], CultureInfo.InvariantCulture), double.Parse(darParts[1], CultureInfo.InvariantCulture)); + // When dimension / DAR does not equal to 1:1, then the frames are most likely stored stretched. + // Note: this might be incorrect for 3D videos as the SAR stored might be per eye instead of per video, but we really can do little about it. + var shouldResetHeight = Math.Abs((imageStream.Width.Value * ha) - (imageStream.Height.Value * wa)) > .05; + if (shouldResetHeight) + { + // SAR = DAR * Height / Width + // RealHeight = Height / SAR = Height / (DAR * Height / Width) = Width / DAR + imageStream.Height = Convert.ToInt32(imageStream.Width.Value * ha / wa); + } + } + var baseRequest = new BaseEncodingJobOptions { MaxWidth = maxWidth, MaxFramerate = (float)(1.0 / interval.TotalSeconds) }; var jobState = new EncodingJobInfo(TranscodingJobType.Progressive) { -- cgit v1.2.3 From 8424ff5b61e6275025398c36edc9608a2acd89ba Mon Sep 17 00:00:00 2001 From: gnattu Date: Sat, 1 Jun 2024 18:41:05 -0400 Subject: Backport pull request #11857 from jellyfin/release-10.9.z Fix ffprobe -user_agent parameter Original-merge: d0336cd67edb3c70b9a0ec03a5ef1f991e3c9b84 Merged-by: Bond-009 Backported-by: Joshua M. Boniface --- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 4 ++-- .../Jellyfin.MediaEncoding.Tests/Probing/ProbeExternalSourcesTests.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Encoder') diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 80ef6ecf77..f85510dac7 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -456,9 +456,9 @@ namespace MediaBrowser.MediaEncoding.Encoder extraArgs += " -probesize " + ffmpegProbeSize; } - if (request.MediaSource.RequiredHttpHeaders.TryGetValue("user_agent", out var userAgent)) + if (request.MediaSource.RequiredHttpHeaders.TryGetValue("User-Agent", out var userAgent)) { - extraArgs += " -user_agent " + userAgent; + extraArgs += $" -user_agent \"{userAgent}\""; } if (request.MediaSource.Protocol == MediaProtocol.Rtsp) diff --git a/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeExternalSourcesTests.cs b/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeExternalSourcesTests.cs index 263f74c900..84008cffdc 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeExternalSourcesTests.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeExternalSourcesTests.cs @@ -35,7 +35,7 @@ namespace Jellyfin.MediaEncoding.Tests.Probing Protocol = MediaProtocol.Http, RequiredHttpHeaders = new Dictionary() { - { "user_agent", userAgent }, + { "User-Agent", userAgent }, } }, ExtractChapters = false, @@ -44,7 +44,7 @@ namespace Jellyfin.MediaEncoding.Tests.Probing var extraArg = encoder.GetExtraArguments(req); - Assert.Contains(userAgent, extraArg, StringComparison.InvariantCulture); + Assert.Contains($"-user_agent \"{userAgent}\"", extraArg, StringComparison.InvariantCulture); } } } -- cgit v1.2.3 From 7d438a748fb9e37840c29c4761ee86c5b5b7cb09 Mon Sep 17 00:00:00 2001 From: Rivenlalala Date: Sun, 23 Jun 2024 11:40:51 -0400 Subject: Backport pull request #12065 from jellyfin/release-10.9.z Make m2ts extension case-insensitive Original-merge: f2a5ccf10206218e1084ee53aaa7284b7be3ddec Merged-by: Bond-009 Backported-by: Joshua M. Boniface --- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Encoder') diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index f85510dac7..d0d41c2d38 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -1155,10 +1155,10 @@ namespace MediaBrowser.MediaEncoding.Encoder // Get all files from the BDMV/STREAMING directory // Only return playable local .m2ts files + var files = _fileSystem.GetFiles(Path.Join(path, "BDMV", "STREAM")).ToList(); return validPlaybackFiles - .Select(f => _fileSystem.GetFileInfo(Path.Join(path, "BDMV", "STREAM", f))) - .Where(f => f.Exists) - .Select(f => f.FullName) + .Select(validFile => files.FirstOrDefault(f => Path.GetFileName(f.FullName.AsSpan()).Equals(validFile, StringComparison.OrdinalIgnoreCase))?.FullName) + .Where(f => f is not null) .ToList(); } -- cgit v1.2.3 From 60c45d6273c4c3d55d0170ca75f2d8ec57f0c0b4 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Mon, 1 Jul 2024 00:21:06 +0200 Subject: Use complete paths in BD info This way we don't need to find the complete path later --- MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs | 2 +- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 17 +++-------------- MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs | 7 ++----- 3 files changed, 6 insertions(+), 20 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Encoder') diff --git a/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs b/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs index 8ebb59c59e..5bae4fbd5a 100644 --- a/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs +++ b/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs @@ -86,7 +86,7 @@ public class BdInfoExaminer : IBlurayExaminer if (playlist.StreamClips is not null && playlist.StreamClips.Count > 0) { // Get the files in the playlist - outputStream.Files = playlist.StreamClips.Select(i => i.StreamFile.Name).ToArray(); + outputStream.Files = playlist.StreamClips.Select(i => i.StreamFile.FileInfo.FullName).ToArray(); } return outputStream; diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index d0d41c2d38..0e0676b8bc 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -1149,18 +1149,7 @@ namespace MediaBrowser.MediaEncoding.Encoder /// public IReadOnlyList GetPrimaryPlaylistM2tsFiles(string path) - { - // Get all playable .m2ts files - var validPlaybackFiles = _blurayExaminer.GetDiscInfo(path).Files; - - // Get all files from the BDMV/STREAMING directory - // Only return playable local .m2ts files - var files = _fileSystem.GetFiles(Path.Join(path, "BDMV", "STREAM")).ToList(); - return validPlaybackFiles - .Select(validFile => files.FirstOrDefault(f => Path.GetFileName(f.FullName.AsSpan()).Equals(validFile, StringComparison.OrdinalIgnoreCase))?.FullName) - .Where(f => f is not null) - .ToList(); - } + => _blurayExaminer.GetDiscInfo(path).Files; /// public string GetInputPathArgument(EncodingJobInfo state) @@ -1171,8 +1160,8 @@ namespace MediaBrowser.MediaEncoding.Encoder { return mediaSource.VideoType switch { - VideoType.Dvd => GetInputArgument(GetPrimaryPlaylistVobFiles(path, null).ToList(), mediaSource), - VideoType.BluRay => GetInputArgument(GetPrimaryPlaylistM2tsFiles(path).ToList(), mediaSource), + VideoType.Dvd => GetInputArgument(GetPrimaryPlaylistVobFiles(path, null), mediaSource), + VideoType.BluRay => GetInputArgument(GetPrimaryPlaylistM2tsFiles(path), mediaSource), _ => GetInputArgument(path, mediaSource) }; } diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs index 1d4e665703..246ba2733f 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs @@ -124,11 +124,8 @@ namespace MediaBrowser.Providers.MediaInfo // Get BD disc information blurayDiscInfo = GetBDInfo(item.Path); - // Get playable .m2ts files - var m2ts = _mediaEncoder.GetPrimaryPlaylistM2tsFiles(item.Path); - // Return if no playable .m2ts files are found - if (blurayDiscInfo is null || blurayDiscInfo.Files.Length == 0 || m2ts.Count == 0) + if (blurayDiscInfo is null || blurayDiscInfo.Files.Length == 0) { _logger.LogError("No playable .m2ts files found in Blu-ray structure, skipping FFprobe."); return ItemUpdateType.MetadataImport; @@ -138,7 +135,7 @@ namespace MediaBrowser.Providers.MediaInfo mediaInfoResult = await GetMediaInfo( new Video { - Path = m2ts[0] + Path = blurayDiscInfo.Files[0] }, cancellationToken).ConfigureAwait(false); } -- cgit v1.2.3 From 1f32f95b9c99199657055d230a951f9be56ef86a Mon Sep 17 00:00:00 2001 From: gnattu Date: Tue, 2 Jul 2024 12:26:31 +0800 Subject: Add tonemapx to filter list Signed-off-by: gnattu --- MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs | 1 + 1 file changed, 1 insertion(+) (limited to 'MediaBrowser.MediaEncoding/Encoder') diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index ec39f159ed..30bb21dcb1 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -103,6 +103,7 @@ namespace MediaBrowser.MediaEncoding.Encoder // sw "alphasrc", "zscale", + "tonemapx", // qsv "scale_qsv", "vpp_qsv", -- cgit v1.2.3 From 518c166a3921fb7c398522680e56c849cbb77199 Mon Sep 17 00:00:00 2001 From: gnattu Date: Fri, 12 Jul 2024 23:15:41 +0800 Subject: Prefer tonemapx during HDR image extraction Signed-off-by: gnattu --- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Encoder') diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index d0d41c2d38..e02f2116aa 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -708,16 +708,22 @@ namespace MediaBrowser.MediaEncoding.Encoder filters.Add("thumbnail=n=" + (useLargerBatchSize ? "50" : "24")); } - // Use SW tonemap on HDR10/HLG video stream only when the zscale filter is available. + // Use SW tonemap on HDR10/HLG video stream only when the zscale or tonemapx filter is available. var enableHdrExtraction = false; - if ((string.Equals(videoStream?.ColorTransfer, "smpte2084", StringComparison.OrdinalIgnoreCase) + if (string.Equals(videoStream?.ColorTransfer, "smpte2084", StringComparison.OrdinalIgnoreCase) || string.Equals(videoStream?.ColorTransfer, "arib-std-b67", StringComparison.OrdinalIgnoreCase)) - && SupportsFilter("zscale")) { - enableHdrExtraction = true; - - filters.Add("zscale=t=linear:npl=100,format=gbrpf32le,zscale=p=bt709,tonemap=tonemap=hable:desat=0:peak=100,zscale=t=bt709:m=bt709,format=yuv420p"); + if (SupportsFilter("tonemapx")) + { + enableHdrExtraction = true; + filters.Add("tonemapx=tonemap=bt2390:desat=0:peak=100:t=bt709:m=bt709:p=bt709:format=yuv420p"); + } + else if (SupportsFilter("zscale")) + { + enableHdrExtraction = true; + filters.Add("zscale=t=linear:npl=100,format=gbrpf32le,zscale=p=bt709,tonemap=tonemap=hable:desat=0:peak=100,zscale=t=bt709:m=bt709,format=yuv420p"); + } } var vf = string.Join(',', filters); -- cgit v1.2.3 From c666f9d0501bbe0235315d2c2e668ec6f86eb345 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Mon, 15 Jul 2024 14:44:14 +0200 Subject: Use real temp dir instead of cache dir for temp files (#12226) --- Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs | 2 +- Emby.Server.Implementations/IO/ManagedFileSystem.cs | 2 +- .../Images/BaseDynamicImageProvider.cs | 1 + .../ScheduledTasks/Tasks/AudioNormalizationTask.cs | 12 +++++++----- .../Trickplay/TrickplayManager.cs | 2 +- Jellyfin.Server/Helpers/StartupHelpers.cs | 1 + MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 10 +++++++--- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 1 + MediaBrowser.MediaEncoding/Transcoding/TranscodeManager.cs | 11 +---------- src/Jellyfin.Drawing.Skia/SkiaEncoder.cs | 2 +- 10 files changed, 22 insertions(+), 22 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Encoder') diff --git a/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs b/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs index 39524be1d4..dc845b2d7e 100644 --- a/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs +++ b/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs @@ -104,6 +104,6 @@ namespace Emby.Server.Implementations.AppBase /// Gets the folder path to the temp directory within the cache folder. /// /// The temp directory. - public string TempDirectory => Path.Combine(CachePath, "temp"); + public string TempDirectory => Path.Join(Path.GetTempPath(), "jellyfin"); } } diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs index 250bec9ea9..28bb29df85 100644 --- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs +++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs @@ -466,7 +466,7 @@ namespace Emby.Server.Implementations.IO File.Copy(file1, temp1, true); File.Copy(file2, file1, true); - File.Copy(temp1, file2, true); + File.Move(temp1, file2, true); } /// diff --git a/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs b/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs index 0a3d740ccf..82db7c46b3 100644 --- a/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs +++ b/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs @@ -122,6 +122,7 @@ namespace Emby.Server.Implementations.Images } await ProviderManager.SaveImage(item, outputPath, mimeType, imageType, null, false, cancellationToken).ConfigureAwait(false); + File.Delete(outputPath); return ItemUpdateType.ImageUpdate; } diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/AudioNormalizationTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/AudioNormalizationTask.cs index df0fdcab81..301c049154 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/AudioNormalizationTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/AudioNormalizationTask.cs @@ -29,7 +29,7 @@ public partial class AudioNormalizationTask : IScheduledTask private readonly IItemRepository _itemRepository; private readonly ILibraryManager _libraryManager; private readonly IMediaEncoder _mediaEncoder; - private readonly IConfigurationManager _configurationManager; + private readonly IApplicationPaths _applicationPaths; private readonly ILocalizationManager _localization; private readonly ILogger _logger; @@ -39,21 +39,21 @@ public partial class AudioNormalizationTask : IScheduledTask /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. - /// Instance of the interface. + /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. public AudioNormalizationTask( IItemRepository itemRepository, ILibraryManager libraryManager, IMediaEncoder mediaEncoder, - IConfigurationManager configurationManager, + IApplicationPaths applicationPaths, ILocalizationManager localizationManager, ILogger logger) { _itemRepository = itemRepository; _libraryManager = libraryManager; _mediaEncoder = mediaEncoder; - _configurationManager = configurationManager; + _applicationPaths = applicationPaths; _localization = localizationManager; _logger = logger; } @@ -107,7 +107,9 @@ public partial class AudioNormalizationTask : IScheduledTask } _logger.LogInformation("Calculating LUFS for album: {Album} with id: {Id}", a.Name, a.Id); - var tempFile = Path.Join(_configurationManager.GetTranscodePath(), a.Id + ".concat"); + var tempDir = _applicationPaths.TempDirectory; + Directory.CreateDirectory(tempDir); + var tempFile = Path.Join(tempDir, a.Id + ".concat"); var inputLines = albumTracks.Select(x => string.Format(CultureInfo.InvariantCulture, "file '{0}'", x.Path.Replace("'", @"'\''", StringComparison.Ordinal))); await File.WriteAllLinesAsync(tempFile, inputLines, cancellationToken).ConfigureAwait(false); try diff --git a/Jellyfin.Server.Implementations/Trickplay/TrickplayManager.cs b/Jellyfin.Server.Implementations/Trickplay/TrickplayManager.cs index efdfc745f0..c14be032e5 100644 --- a/Jellyfin.Server.Implementations/Trickplay/TrickplayManager.cs +++ b/Jellyfin.Server.Implementations/Trickplay/TrickplayManager.cs @@ -230,7 +230,7 @@ public class TrickplayManager : ITrickplayManager throw new ArgumentException("Can't create trickplay from 0 images."); } - var workDir = Path.Combine(_appPaths.TempDirectory, Guid.NewGuid().ToString("N")); + var workDir = Path.Combine(_appPaths.TempDirectory, "trickplay_" + Guid.NewGuid().ToString("N")); Directory.CreateDirectory(workDir); var trickplayInfo = new TrickplayInfo diff --git a/Jellyfin.Server/Helpers/StartupHelpers.cs b/Jellyfin.Server/Helpers/StartupHelpers.cs index 5311a30e41..5518d6ba89 100644 --- a/Jellyfin.Server/Helpers/StartupHelpers.cs +++ b/Jellyfin.Server/Helpers/StartupHelpers.cs @@ -60,6 +60,7 @@ public static class StartupHelpers logger.LogInformation("Log directory path: {LogDirectoryPath}", appPaths.LogDirectoryPath); logger.LogInformation("Config directory path: {ConfigurationDirectoryPath}", appPaths.ConfigurationDirectoryPath); logger.LogInformation("Cache path: {CachePath}", appPaths.CachePath); + logger.LogInformation("Temp directory path: {TempDirPath}", appPaths.TempDirectory); logger.LogInformation("Web resources path: {WebPath}", appPaths.WebPath); logger.LogInformation("Application directory: {ApplicationPath}", appPaths.ProgramSystemPath); } diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 0bb7100dea..b175dc4034 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -1203,10 +1203,14 @@ namespace MediaBrowser.Controller.MediaEncoding if (state.MediaSource.VideoType == VideoType.Dvd || state.MediaSource.VideoType == VideoType.BluRay) { - var tmpConcatPath = Path.Join(_configurationManager.GetTranscodePath(), state.MediaSource.Id + ".concat"); - _mediaEncoder.GenerateConcatConfig(state.MediaSource, tmpConcatPath); + var concatFilePath = Path.Join(_configurationManager.CommonApplicationPaths.CachePath, "concat", state.MediaSource.Id + ".concat"); + if (!File.Exists(concatFilePath)) + { + _mediaEncoder.GenerateConcatConfig(state.MediaSource, concatFilePath); + } + arg.Append(" -f concat -safe 0 -i \"") - .Append(tmpConcatPath) + .Append(concatFilePath) .Append("\" "); } else diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index e02f2116aa..a7b8812219 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -1203,6 +1203,7 @@ namespace MediaBrowser.MediaEncoding.Encoder } // Generate concat configuration entries for each file and write to file + Directory.CreateDirectory(Path.GetDirectoryName(concatFilePath)); using StreamWriter sw = new StreamWriter(concatFilePath); foreach (var path in files) { diff --git a/MediaBrowser.MediaEncoding/Transcoding/TranscodeManager.cs b/MediaBrowser.MediaEncoding/Transcoding/TranscodeManager.cs index ea5dbf7f7f..0b09e57b56 100644 --- a/MediaBrowser.MediaEncoding/Transcoding/TranscodeManager.cs +++ b/MediaBrowser.MediaEncoding/Transcoding/TranscodeManager.cs @@ -235,15 +235,6 @@ public sealed class TranscodeManager : ITranscodeManager, IDisposable if (delete(job.Path!)) { await DeletePartialStreamFiles(job.Path!, job.Type, 0, 1500).ConfigureAwait(false); - if (job.MediaSource?.VideoType == VideoType.Dvd || job.MediaSource?.VideoType == VideoType.BluRay) - { - var concatFilePath = Path.Join(_serverConfigurationManager.GetTranscodePath(), job.MediaSource.Id + ".concat"); - if (File.Exists(concatFilePath)) - { - _logger.LogInformation("Deleting ffmpeg concat configuration at {Path}", concatFilePath); - File.Delete(concatFilePath); - } - } } if (closeLiveStream && !string.IsNullOrWhiteSpace(job.LiveStreamId)) @@ -419,7 +410,7 @@ public sealed class TranscodeManager : ITranscodeManager, IDisposable var attachmentPath = Path.Combine(_appPaths.CachePath, "attachments", state.MediaSource.Id); if (state.MediaSource.VideoType == VideoType.Dvd || state.MediaSource.VideoType == VideoType.BluRay) { - var concatPath = Path.Join(_serverConfigurationManager.GetTranscodePath(), state.MediaSource.Id + ".concat"); + var concatPath = Path.Join(_appPaths.CachePath, "concat", state.MediaSource.Id + ".concat"); await _attachmentExtractor.ExtractAllAttachments(concatPath, state.MediaSource, attachmentPath, cancellationTokenSource.Token).ConfigureAwait(false); } else diff --git a/src/Jellyfin.Drawing.Skia/SkiaEncoder.cs b/src/Jellyfin.Drawing.Skia/SkiaEncoder.cs index 75963226a8..ede93aaa55 100644 --- a/src/Jellyfin.Drawing.Skia/SkiaEncoder.cs +++ b/src/Jellyfin.Drawing.Skia/SkiaEncoder.cs @@ -219,7 +219,7 @@ public class SkiaEncoder : IImageEncoder return path; } - var tempPath = Path.Combine(_appPaths.TempDirectory, string.Concat(Guid.NewGuid().ToString(), Path.GetExtension(path.AsSpan()))); + var tempPath = Path.Join(_appPaths.TempDirectory, string.Concat("skia_", Guid.NewGuid().ToString(), Path.GetExtension(path.AsSpan()))); var directory = Path.GetDirectoryName(tempPath) ?? throw new ResourceNotFoundException($"Provided path ({tempPath}) is not valid."); Directory.CreateDirectory(directory); File.Copy(path, tempPath, true); -- cgit v1.2.3 From 68bfabbaba2fc6f7b5b6cd63b388f4669a476713 Mon Sep 17 00:00:00 2001 From: gnattu Date: Wed, 8 May 2024 13:50:03 +0800 Subject: Add option to extract keyframe only during trickplay image generation This would be significantly faster than decoding every frame, but it does have compatibility issues. Not all decoders support this mode, notably the VP9 decoder, CUVID decoders, and QSV decoders. Some videos with very long key-frame intervals may also perform poorly with this mode, as the image timing could become too inaccurate to reflect the actual frame. Signed-off-by: gnattu --- Jellyfin.Server.Implementations/Trickplay/TrickplayManager.cs | 1 + MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs | 2 ++ MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 6 ++++++ MediaBrowser.Model/Configuration/TrickplayOptions.cs | 6 ++++++ 4 files changed, 15 insertions(+) (limited to 'MediaBrowser.MediaEncoding/Encoder') diff --git a/Jellyfin.Server.Implementations/Trickplay/TrickplayManager.cs b/Jellyfin.Server.Implementations/Trickplay/TrickplayManager.cs index efdfc745f0..69c402f63b 100644 --- a/Jellyfin.Server.Implementations/Trickplay/TrickplayManager.cs +++ b/Jellyfin.Server.Implementations/Trickplay/TrickplayManager.cs @@ -168,6 +168,7 @@ public class TrickplayManager : ITrickplayManager options.ProcessThreads, options.Qscale, options.ProcessPriority, + options.EnableKeyFrameOnlyExtraction, _encodingHelper, cancellationToken).ConfigureAwait(false); diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs index 26c353a54f..038c6c7f69 100644 --- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs +++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs @@ -153,6 +153,7 @@ namespace MediaBrowser.Controller.MediaEncoding /// The input/output thread count for ffmpeg. /// The qscale value for ffmpeg. /// The process priority for the ffmpeg process. + /// Whether to only extract key frames. /// EncodingHelper instance. /// The cancellation token. /// Directory where images where extracted. A given image made before another will always be named with a lower number. @@ -168,6 +169,7 @@ namespace MediaBrowser.Controller.MediaEncoding int? threads, int? qualityScale, ProcessPriorityClass? priority, + bool enableKeyFrameOnlyExtraction, EncodingHelper encodingHelper, CancellationToken cancellationToken); diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index d0d41c2d38..db4e21705a 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -807,6 +807,7 @@ namespace MediaBrowser.MediaEncoding.Encoder int? threads, int? qualityScale, ProcessPriorityClass? priority, + bool enableKeyFrameOnlyExtraction, EncodingHelper encodingHelper, CancellationToken cancellationToken) { @@ -862,6 +863,11 @@ namespace MediaBrowser.MediaEncoding.Encoder inputArg = "-threads " + threads + " " + inputArg; // HW accel args set a different input thread count, only set if disabled } + if (enableKeyFrameOnlyExtraction) + { + inputArg = "-skip_frame nokey " + inputArg; + } + var filterParam = encodingHelper.GetVideoProcessingFilterParam(jobState, options, vidEncoder).Trim(); if (string.IsNullOrWhiteSpace(filterParam)) { diff --git a/MediaBrowser.Model/Configuration/TrickplayOptions.cs b/MediaBrowser.Model/Configuration/TrickplayOptions.cs index a151d3429b..578bb306a0 100644 --- a/MediaBrowser.Model/Configuration/TrickplayOptions.cs +++ b/MediaBrowser.Model/Configuration/TrickplayOptions.cs @@ -18,6 +18,12 @@ public class TrickplayOptions /// public bool EnableHwEncoding { get; set; } = false; + /// + /// Gets or sets a value indicating whether to only extract key frames. + /// Significantly faster, but is not compatible with all decoders and/or video files. + /// + public bool EnableKeyFrameOnlyExtraction { get; set; } = false; + /// /// Gets or sets the behavior used by trickplay provider on library scan/update. /// -- cgit v1.2.3 From 0340eccb523a33a7a777a3e06383df4900965895 Mon Sep 17 00:00:00 2001 From: gnattu Date: Wed, 17 Jul 2024 00:35:59 +0800 Subject: Force software decoding when hardware decoder does not support keyframe only mode but requested by user Signed-off-by: gnattu --- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'MediaBrowser.MediaEncoding/Encoder') diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index db4e21705a..f101bbb148 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -814,6 +814,22 @@ namespace MediaBrowser.MediaEncoding.Encoder var options = allowHwAccel ? _configurationManager.GetEncodingOptions() : new EncodingOptions(); threads ??= _threads; + if (enableKeyFrameOnlyExtraction) + { + var supportsKeyFrameOnly = !allowHwAccel + || (string.Equals(options.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase) && options.EnableEnhancedNvdecDecoder) + || (string.Equals(options.HardwareAccelerationType, "amf", StringComparison.OrdinalIgnoreCase) && OperatingSystem.IsWindows() && options.PreferSystemNativeHwDecoder) + || (string.Equals(options.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase) && options.PreferSystemNativeHwDecoder) + || string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase) + || string.Equals(options.HardwareAccelerationType, "videotoolbox", StringComparison.OrdinalIgnoreCase); + if (!supportsKeyFrameOnly) + { + // Disable hardware acceleration when the hardware decoder does not support keyframe only mode. + allowHwAccel = false; + options = new EncodingOptions(); + } + } + // A new EncodingOptions instance must be used as to not disable HW acceleration for all of Jellyfin. // Additionally, we must set a few fields without defaults to prevent null pointer exceptions. if (!allowHwAccel) -- cgit v1.2.3 From b28d22545a59da010ce1b949c0aac76d19c61e95 Mon Sep 17 00:00:00 2001 From: gnattu Date: Wed, 17 Jul 2024 21:25:29 +0800 Subject: Simplify condition check Signed-off-by: gnattu --- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Encoder') diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index f101bbb148..fd6bef1ad7 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -814,10 +814,9 @@ namespace MediaBrowser.MediaEncoding.Encoder var options = allowHwAccel ? _configurationManager.GetEncodingOptions() : new EncodingOptions(); threads ??= _threads; - if (enableKeyFrameOnlyExtraction) + if (allowHwAccel && enableKeyFrameOnlyExtraction) { - var supportsKeyFrameOnly = !allowHwAccel - || (string.Equals(options.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase) && options.EnableEnhancedNvdecDecoder) + var supportsKeyFrameOnly = (string.Equals(options.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase) && options.EnableEnhancedNvdecDecoder) || (string.Equals(options.HardwareAccelerationType, "amf", StringComparison.OrdinalIgnoreCase) && OperatingSystem.IsWindows() && options.PreferSystemNativeHwDecoder) || (string.Equals(options.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase) && options.PreferSystemNativeHwDecoder) || string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase) -- cgit v1.2.3 From e851bb869b0c9295372b6f9f47d78a4ea1401cea Mon Sep 17 00:00:00 2001 From: gnattu Date: Wed, 17 Jul 2024 22:25:28 +0800 Subject: Simply AMF Windows checking Co-authored-by: Nyanmisaka --- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'MediaBrowser.MediaEncoding/Encoder') diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index fd6bef1ad7..390891fcf1 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -817,7 +817,7 @@ namespace MediaBrowser.MediaEncoding.Encoder if (allowHwAccel && enableKeyFrameOnlyExtraction) { var supportsKeyFrameOnly = (string.Equals(options.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase) && options.EnableEnhancedNvdecDecoder) - || (string.Equals(options.HardwareAccelerationType, "amf", StringComparison.OrdinalIgnoreCase) && OperatingSystem.IsWindows() && options.PreferSystemNativeHwDecoder) + || (string.Equals(options.HardwareAccelerationType, "amf", StringComparison.OrdinalIgnoreCase) && OperatingSystem.IsWindows()) || (string.Equals(options.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase) && options.PreferSystemNativeHwDecoder) || string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase) || string.Equals(options.HardwareAccelerationType, "videotoolbox", StringComparison.OrdinalIgnoreCase); -- cgit v1.2.3 From 8851ace5436c6a22b9489f88d20e1aab582ffddf Mon Sep 17 00:00:00 2001 From: gnattu Date: Thu, 18 Jul 2024 00:35:40 +0800 Subject: Enable Dolby AC-4 decoder (#11486) --- Jellyfin.Api/Controllers/DynamicHlsController.cs | 7 +++++++ MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs | 1 + 2 files changed, 8 insertions(+) (limited to 'MediaBrowser.MediaEncoding/Encoder') diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index 68602c80d5..2201225a8a 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -1735,6 +1735,13 @@ public class DynamicHlsController : BaseJellyfinApiController } } + if (state.AudioStream is not null && state.AudioStream.CodecTag.Equals("ac-4", StringComparison.Ordinal)) + { + // ac-4 audio tends to hava a super weird sample rate that will fail most encoders + // force resample it to 48KHz + args += " -ar 48000"; + } + if (state.OutputAudioSampleRate.HasValue) { args += " -ar " + state.OutputAudioSampleRate.Value.ToString(CultureInfo.InvariantCulture); diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index 30bb21dcb1..06e2d37830 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -27,6 +27,7 @@ namespace MediaBrowser.MediaEncoding.Encoder "msmpeg4", "dca", "ac3", + "ac4", "aac", "mp3", "flac", -- cgit v1.2.3 From 5262439300884680b1425b94de9c2f9c898d83ae Mon Sep 17 00:00:00 2001 From: gnattu Date: Thu, 18 Jul 2024 01:50:32 +0800 Subject: Enable hardware Trickplay processing pipeline for VideoToolbox (#11510) --- .../MediaEncoding/EncodingHelper.cs | 3 ++- .../Encoder/EncoderValidator.cs | 31 ++++++++++++++++++++++ MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 19 ++++++++++++- 3 files changed, 51 insertions(+), 2 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Encoder') diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index b175dc4034..98351377e6 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -120,7 +120,8 @@ namespace MediaBrowser.Controller.MediaEncoding private static readonly Dictionary _mjpegCodecMap = new(StringComparer.OrdinalIgnoreCase) { { "vaapi", _defaultMjpegEncoder + "_vaapi" }, - { "qsv", _defaultMjpegEncoder + "_qsv" } + { "qsv", _defaultMjpegEncoder + "_qsv" }, + { "videotoolbox", _defaultMjpegEncoder + "_videotoolbox" } }; public static readonly string[] LosslessAudioCodecs = new string[] diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index 06e2d37830..a865b0e4cb 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -95,6 +95,7 @@ namespace MediaBrowser.MediaEncoding.Encoder "h264_v4l2m2m", "h264_videotoolbox", "hevc_videotoolbox", + "mjpeg_videotoolbox", "h264_rkmpp", "hevc_rkmpp" }; @@ -500,6 +501,11 @@ namespace MediaBrowser.MediaEncoding.Encoder return output.Contains(keyDesc, StringComparison.Ordinal); } + public bool CheckSupportedHwaccelFlag(string flag) + { + return !string.IsNullOrEmpty(flag) && GetProcessExitCode(_encoderPath, $"-loglevel quiet -hwaccel_flags +{flag} -hide_banner -f lavfi -i nullsrc=s=1x1:d=100 -f null -"); + } + private IEnumerable GetCodecs(Codec codec) { string codecstr = codec == Codec.Encoder ? "encoders" : "decoders"; @@ -605,6 +611,31 @@ namespace MediaBrowser.MediaEncoding.Encoder } } + private bool GetProcessExitCode(string path, string arguments) + { + using var process = new Process(); + process.StartInfo = new ProcessStartInfo(path, arguments) + { + CreateNoWindow = true, + UseShellExecute = false, + WindowStyle = ProcessWindowStyle.Hidden, + ErrorDialog = false + }; + _logger.LogDebug("Running {Path} {Arguments}", path, arguments); + + try + { + process.Start(); + process.WaitForExit(); + return process.ExitCode == 0; + } + catch (Exception ex) + { + _logger.LogError("Running {Path} {Arguments} failed with exception {Exception}", path, arguments, ex.Message); + return false; + } + } + [GeneratedRegex("^\\s\\S{6}\\s(?[\\w|-]+)\\s+.+$", RegexOptions.Multiline)] private static partial Regex CodecRegex(); diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index de1b654822..e7e48d6d89 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -74,6 +74,7 @@ namespace MediaBrowser.MediaEncoding.Encoder private IDictionary _filtersWithOption = new Dictionary(); private bool _isPkeyPauseSupported = false; + private bool _isLowPriorityHwDecodeSupported = false; private bool _isVaapiDeviceAmd = false; private bool _isVaapiDeviceInteliHD = false; @@ -194,6 +195,7 @@ namespace MediaBrowser.MediaEncoding.Encoder _threads = EncodingHelper.GetNumberOfThreads(null, options, null); _isPkeyPauseSupported = validator.CheckSupportedRuntimeKey("p pause transcoding"); + _isLowPriorityHwDecodeSupported = validator.CheckSupportedHwaccelFlag("low_priority"); // Check the Vaapi device vendor if (OperatingSystem.IsLinux() @@ -884,6 +886,12 @@ namespace MediaBrowser.MediaEncoding.Encoder inputArg = "-threads " + threads + " " + inputArg; // HW accel args set a different input thread count, only set if disabled } + if (options.HardwareAccelerationType.Contains("videotoolbox", StringComparison.OrdinalIgnoreCase) && _isLowPriorityHwDecodeSupported) + { + // VideoToolbox supports low priority decoding, which is useful for trickplay + inputArg = "-hwaccel_flags +low_priority " + inputArg; + } + if (enableKeyFrameOnlyExtraction) { inputArg = "-skip_frame nokey " + inputArg; @@ -921,6 +929,14 @@ namespace MediaBrowser.MediaEncoding.Encoder encoderQuality = (100 - ((qualityScale - 1) * (100 / 30))) / 118; } + if (vidEncoder.Contains("videotoolbox", StringComparison.OrdinalIgnoreCase)) + { + // videotoolbox's mjpeg encoder uses jpeg quality scaled to QP2LAMBDA (118) instead of ffmpeg defined qscale + // ffmpeg qscale is a value from 1-31, with 1 being best quality and 31 being worst + // jpeg quality is a value from 0-100, with 0 being worst quality and 100 being best + encoderQuality = 118 - ((qualityScale - 1) * (118 / 30)); + } + // Output arguments var targetDirectory = Path.Combine(_configurationManager.ApplicationPaths.TempDirectory, Guid.NewGuid().ToString("N")); Directory.CreateDirectory(targetDirectory); @@ -929,12 +945,13 @@ namespace MediaBrowser.MediaEncoding.Encoder // Final command arguments var args = string.Format( CultureInfo.InvariantCulture, - "-loglevel error {0} -an -sn {1} -threads {2} -c:v {3} {4}-f {5} \"{6}\"", + "-loglevel error {0} -an -sn {1} -threads {2} -c:v {3} {4}{5}-f {6} \"{7}\"", inputArg, filterParam, outputThreads.GetValueOrDefault(_threads), vidEncoder, qualityScale.HasValue ? "-qscale:v " + encoderQuality.Value.ToString(CultureInfo.InvariantCulture) + " " : string.Empty, + vidEncoder.Contains("videotoolbox", StringComparison.InvariantCultureIgnoreCase) ? "-allow_sw 1 " : string.Empty, // allow_sw fallback for some intel macs "image2", outputPath); -- cgit v1.2.3 From 1dc0a1de6c2646a686f75e23c83a194bd69aec4b Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Sun, 21 Jul 2024 00:58:06 -0400 Subject: Backport pull request #12296 from jellyfin/release-10.9.z Properly escape paths in concat file for BDMV Original-merge: 4afa6db108e2071a335e7f6f7bb3fa7b50d9b7f7 Merged-by: crobibero Backported-by: Joshua M. Boniface --- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'MediaBrowser.MediaEncoding/Encoder') diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index e7e48d6d89..5cfead5026 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -1250,7 +1250,7 @@ namespace MediaBrowser.MediaEncoding.Encoder var duration = TimeSpan.FromTicks(mediaInfoResult.RunTimeTicks.Value).TotalSeconds; // Add file path stanza to concat configuration - sw.WriteLine("file '{0}'", path); + sw.WriteLine("file '{0}'", path.Replace("'", @"'\''", StringComparison.Ordinal)); // Add duration stanza to concat configuration sw.WriteLine("duration {0}", duration); -- cgit v1.2.3