diff options
Diffstat (limited to 'MediaBrowser.MediaEncoding')
7 files changed, 64 insertions, 31 deletions
diff --git a/MediaBrowser.MediaEncoding/BdInfo/BdInfoDirectoryInfo.cs b/MediaBrowser.MediaEncoding/BdInfo/BdInfoDirectoryInfo.cs index fca17d4c0..7c0be5a9f 100644 --- a/MediaBrowser.MediaEncoding/BdInfo/BdInfoDirectoryInfo.cs +++ b/MediaBrowser.MediaEncoding/BdInfo/BdInfoDirectoryInfo.cs @@ -84,7 +84,7 @@ public class BdInfoDirectoryInfo : IDirectoryInfo /// Gets the files matching a pattern. /// </summary> /// <param name="searchPattern">The search pattern.</param> - /// <returns>All files of the directory matchign the search pattern.</returns> + /// <returns>All files of the directory matching the search pattern.</returns> public IFileInfo[] GetFiles(string searchPattern) { return _fileSystem.GetFiles(_impl.FullName, new[] { searchPattern }, false, false) @@ -96,8 +96,8 @@ public class BdInfoDirectoryInfo : IDirectoryInfo /// Gets the files matching a pattern and search options. /// </summary> /// <param name="searchPattern">The search pattern.</param> - /// <param name="searchOption">The search optin.</param> - /// <returns>All files of the directory matchign the search pattern and options.</returns> + /// <param name="searchOption">The search option.</param> + /// <returns>All files of the directory matching the search pattern and options.</returns> public IFileInfo[] GetFiles(string searchPattern, SearchOption searchOption) { return _fileSystem.GetFiles( diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index b49fbf2ab..23d9ca7ef 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -150,7 +150,7 @@ namespace MediaBrowser.MediaEncoding.Encoder private static readonly Dictionary<int, string[]> _filterOptionsDict = new Dictionary<int, string[]> { - { 0, new string[] { "scale_cuda", "Output format (default \"same\")" } }, + { 0, new string[] { "scale_cuda", "format" } }, { 1, new string[] { "tonemap_cuda", "GPU accelerated HDR to SDR tonemapping" } }, { 2, new string[] { "tonemap_opencl", "bt2390" } }, { 3, new string[] { "overlay_opencl", "Action to take when encountering EOF from secondary input" } }, diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 6f8769252..1eef181cb 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -62,7 +62,7 @@ namespace MediaBrowser.MediaEncoding.Encoder private readonly AsyncNonKeyedLocker _thumbnailResourcePool; - private readonly object _runningProcessesLock = new object(); + private readonly Lock _runningProcessesLock = new(); private readonly List<ProcessWrapper> _runningProcesses = new List<ProcessWrapper>(); // MediaEncoder is registered as a Singleton @@ -650,6 +650,8 @@ namespace MediaBrowser.MediaEncoding.Encoder { ArgumentException.ThrowIfNullOrEmpty(inputPath); + var useTradeoff = _config.GetFFmpegImgExtractPerfTradeoff(); + var outputExtension = targetFormat?.GetExtension() ?? ".jpg"; var tempExtractPath = Path.Combine(_configurationManager.ApplicationPaths.TempDirectory, Guid.NewGuid() + outputExtension); @@ -684,7 +686,7 @@ namespace MediaBrowser.MediaEncoding.Encoder // Use ffmpeg to sample 100 (we can drop this if required using thumbnail=50 for 50 frames) frames and pick the best thumbnail. Have a fall back just in case. // mpegts need larger batch size otherwise the corrupted thumbnail will be created. Larger batch size will lower the processing speed. - var enableThumbnail = useIFrame && !string.Equals("wtv", container, StringComparison.OrdinalIgnoreCase); + var enableThumbnail = !useTradeoff && useIFrame && !string.Equals("wtv", container, StringComparison.OrdinalIgnoreCase); if (enableThumbnail) { var useLargerBatchSize = string.Equals("mpegts", container, StringComparison.OrdinalIgnoreCase); @@ -699,8 +701,9 @@ namespace MediaBrowser.MediaEncoding.Encoder { if (SupportsFilter("tonemapx")) { + var peak = videoStream.VideoRangeType == VideoRangeType.DOVI ? "400" : "100"; enableHdrExtraction = true; - filters.Add("tonemapx=tonemap=bt2390:desat=0:peak=100:t=bt709:m=bt709:p=bt709:format=yuv420p"); + filters.Add($"tonemapx=tonemap=bt2390:desat=0:peak={peak}:t=bt709:m=bt709:p=bt709:format=yuv420p"); } else if (SupportsFilter("zscale") && videoStream.VideoRangeType != VideoRangeType.DOVI) { @@ -718,6 +721,11 @@ namespace MediaBrowser.MediaEncoding.Encoder args = string.Format(CultureInfo.InvariantCulture, "-ss {0} ", GetTimeParameter(offset.Value)) + args; } + if (useIFrame && useTradeoff) + { + args = "-skip_frame nokey " + args; + } + if (!string.IsNullOrWhiteSpace(container)) { var inputFormat = EncodingHelper.GetInputFormat(container); @@ -804,7 +812,8 @@ namespace MediaBrowser.MediaEncoding.Encoder || (hardwareAccelerationType == HardwareAccelerationType.amf && OperatingSystem.IsWindows()) || (hardwareAccelerationType == HardwareAccelerationType.qsv && options.PreferSystemNativeHwDecoder) || hardwareAccelerationType == HardwareAccelerationType.vaapi - || hardwareAccelerationType == HardwareAccelerationType.videotoolbox; + || hardwareAccelerationType == HardwareAccelerationType.videotoolbox + || hardwareAccelerationType == HardwareAccelerationType.rkmpp; if (!supportsKeyFrameOnly) { // Disable hardware acceleration when the hardware decoder does not support keyframe only mode. @@ -930,13 +939,14 @@ 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}{5}-f {6} \"{7}\"", + "-loglevel error {0} -an -sn {1} -threads {2} -c:v {3} {4}{5}{6}-f {7} \"{8}\"", inputArg, filterParam, outputThreads.GetValueOrDefault(_threads), vidEncoder, encoderQualityOption + encoderQuality + " ", vidEncoder.Contains("videotoolbox", StringComparison.InvariantCultureIgnoreCase) ? "-allow_sw 1 " : string.Empty, // allow_sw fallback for some intel macs + EncodingHelper.GetVideoSyncOption("0", EncoderVersion).Trim() + " ", // passthrough timestamp "image2", outputPath); @@ -1025,6 +1035,16 @@ namespace MediaBrowser.MediaEncoding.Encoder if (exitCode == -1) { _logger.LogError("ffmpeg image extraction failed for {ProcessDescription}", processDescription); + // Cleanup temp folder here, because the targetDirectory is not returned and the cleanup for failed ffmpeg process is not possible for caller. + // Ideally the ffmpeg should not write any files if it fails, but it seems like it is not guaranteed. + try + { + Directory.Delete(targetDirectory, true); + } + catch (Exception e) + { + _logger.LogError(e, "Failed to delete ffmpeg temp directory {TargetDirectory}", targetDirectory); + } throw new FfmpegException(string.Format(CultureInfo.InvariantCulture, "ffmpeg image extraction failed for {0}", processDescription)); } @@ -1081,14 +1101,14 @@ namespace MediaBrowser.MediaEncoding.Encoder private void StopProcesses() { - List<ProcessWrapper> proceses; + List<ProcessWrapper> processes; lock (_runningProcessesLock) { - proceses = _runningProcesses.ToList(); + processes = _runningProcesses.ToList(); _runningProcesses.Clear(); } - foreach (var process in proceses) + foreach (var process in processes) { if (!process.HasExited) { @@ -1102,7 +1122,11 @@ namespace MediaBrowser.MediaEncoding.Encoder // https://ffmpeg.org/ffmpeg-filters.html#Notes-on-filtergraph-escaping // We need to double escape - return path.Replace('\\', '/').Replace(":", "\\:", StringComparison.Ordinal).Replace("'", @"'\\\''", StringComparison.Ordinal); + return path + .Replace('\\', '/') + .Replace(":", "\\:", StringComparison.Ordinal) + .Replace("'", @"'\\\''", StringComparison.Ordinal) + .Replace("\"", "\\\"", StringComparison.Ordinal); } /// <inheritdoc /> diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj index be63513a7..be7eeda92 100644 --- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj +++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj @@ -6,7 +6,7 @@ </PropertyGroup> <PropertyGroup> - <TargetFramework>net8.0</TargetFramework> + <TargetFramework>net9.0</TargetFramework> <GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateDocumentationFile>true</GenerateDocumentationFile> </PropertyGroup> diff --git a/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs b/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs index 1b5b5262a..6f51e1a6a 100644 --- a/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs +++ b/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs @@ -24,7 +24,7 @@ namespace MediaBrowser.MediaEncoding.Probing if (result.Streams is not null) { - // Convert all dictionaries to case insensitive + // Convert all dictionaries to case-insensitive foreach (var stream in result.Streams) { if (stream.Tags is not null) @@ -70,7 +70,7 @@ namespace MediaBrowser.MediaEncoding.Probing } /// <summary> - /// Converts a dictionary to case insensitive. + /// Converts a dictionary to case-insensitive. /// </summary> /// <param name="dict">The dict.</param> /// <returns>Dictionary{System.StringSystem.String}.</returns> diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index 0dee77db8..c6a2ca5a4 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -1652,7 +1652,7 @@ namespace MediaBrowser.MediaEncoding.Probing using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 1)) { - fs.Read(packetBuffer); + fs.ReadExactly(packetBuffer); } if (packetBuffer[0] == 71) diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs index a79d801fb..d060b247d 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs @@ -17,7 +17,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles public class SubtitleEditParser : ISubtitleParser { private readonly ILogger<SubtitleEditParser> _logger; - private readonly Dictionary<string, SubtitleFormat[]> _subtitleFormats; + private readonly Dictionary<string, List<Type>> _subtitleFormatTypes; /// <summary> /// Initializes a new instance of the <see cref="SubtitleEditParser"/> class. @@ -26,10 +26,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles public SubtitleEditParser(ILogger<SubtitleEditParser> logger) { _logger = logger; - _subtitleFormats = GetSubtitleFormats() - .Where(subtitleFormat => !string.IsNullOrEmpty(subtitleFormat.Extension)) - .GroupBy(subtitleFormat => subtitleFormat.Extension.TrimStart('.'), StringComparer.OrdinalIgnoreCase) - .ToDictionary(g => g.Key, g => g.ToArray(), StringComparer.OrdinalIgnoreCase); + _subtitleFormatTypes = GetSubtitleFormatTypes(); } /// <inheritdoc /> @@ -38,13 +35,14 @@ namespace MediaBrowser.MediaEncoding.Subtitles var subtitle = new Subtitle(); var lines = stream.ReadAllLines().ToList(); - if (!_subtitleFormats.TryGetValue(fileExtension, out var subtitleFormats)) + if (!_subtitleFormatTypes.TryGetValue(fileExtension, out var subtitleFormatTypesForExtension)) { throw new ArgumentException($"Unsupported file extension: {fileExtension}", nameof(fileExtension)); } - foreach (var subtitleFormat in subtitleFormats) + foreach (var subtitleFormatType in subtitleFormatTypesForExtension) { + var subtitleFormat = (SubtitleFormat)Activator.CreateInstance(subtitleFormatType, true)!; _logger.LogDebug( "Trying to parse '{FileExtension}' subtitle using the {SubtitleFormatParser} format parser", fileExtension, @@ -97,11 +95,11 @@ namespace MediaBrowser.MediaEncoding.Subtitles /// <inheritdoc /> public bool SupportsFileExtension(string fileExtension) - => _subtitleFormats.ContainsKey(fileExtension); + => _subtitleFormatTypes.ContainsKey(fileExtension); - private List<SubtitleFormat> GetSubtitleFormats() + private Dictionary<string, List<Type>> GetSubtitleFormatTypes() { - var subtitleFormats = new List<SubtitleFormat>(); + var subtitleFormatTypes = new Dictionary<string, List<Type>>(StringComparer.OrdinalIgnoreCase); var assembly = typeof(SubtitleFormat).Assembly; foreach (var type in assembly.GetTypes()) @@ -113,9 +111,20 @@ namespace MediaBrowser.MediaEncoding.Subtitles try { - // It shouldn't be null, but the exception is caught if it is - var subtitleFormat = (SubtitleFormat)Activator.CreateInstance(type, true)!; - subtitleFormats.Add(subtitleFormat); + var tempInstance = (SubtitleFormat)Activator.CreateInstance(type, true)!; + var extension = tempInstance.Extension.TrimStart('.'); + if (!string.IsNullOrEmpty(extension)) + { + // Store only the type, we will instantiate from it later + if (!subtitleFormatTypes.TryGetValue(extension, out var subtitleFormatTypesForExtension)) + { + subtitleFormatTypes[extension] = [type]; + } + else + { + subtitleFormatTypesForExtension.Add(type); + } + } } catch (Exception ex) { @@ -123,7 +132,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles } } - return subtitleFormats; + return subtitleFormatTypes; } } } |
