aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.MediaEncoding/Encoder
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.MediaEncoding/Encoder')
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs21
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs2
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs119
3 files changed, 57 insertions, 85 deletions
diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
index a865b0e4c..6c43be3ab 100644
--- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
@@ -62,10 +62,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
"libx264",
"libx265",
"libsvtav1",
- "mpeg4",
- "msmpeg4",
- "libvpx",
- "libvpx-vp9",
"aac",
"aac_at",
"libfdk_aac",
@@ -116,25 +112,31 @@ namespace MediaBrowser.MediaEncoding.Encoder
"yadif_cuda",
"tonemap_cuda",
"overlay_cuda",
+ "transpose_cuda",
"hwupload_cuda",
// opencl
"scale_opencl",
"tonemap_opencl",
"overlay_opencl",
+ "transpose_opencl",
// vaapi
"scale_vaapi",
"deinterlace_vaapi",
"tonemap_vaapi",
"procamp_vaapi",
"overlay_vaapi",
+ "transpose_vaapi",
"hwupload_vaapi",
// vulkan
"libplacebo",
"scale_vulkan",
"overlay_vulkan",
+ "transpose_vulkan",
+ "flip_vulkan",
// videotoolbox
"yadif_videotoolbox",
"scale_vt",
+ "transpose_vt",
"overlay_videotoolbox",
"tonemap_videotoolbox",
// rkrga
@@ -150,7 +152,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
{ 2, new string[] { "tonemap_opencl", "bt2390" } },
{ 3, new string[] { "overlay_opencl", "Action to take when encountering EOF from secondary input" } },
{ 4, new string[] { "overlay_vaapi", "Action to take when encountering EOF from secondary input" } },
- { 5, new string[] { "overlay_vulkan", "Action to take when encountering EOF from secondary input" } }
+ { 5, new string[] { "overlay_vulkan", "Action to take when encountering EOF from secondary input" } },
+ { 6, new string[] { "transpose_opencl", "rotate by half-turn" } }
};
// These are the library versions that corresponds to our minimum ffmpeg version 4.4 according to the version table below
@@ -171,6 +174,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
private readonly string _encoderPath;
+ private readonly Version _minFFmpegMultiThreadedCli = new Version(7, 0);
+
public EncoderValidator(ILogger logger, string encoderPath)
{
_logger = logger;
@@ -480,7 +485,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
return false;
}
- public bool CheckSupportedRuntimeKey(string keyDesc)
+ public bool CheckSupportedRuntimeKey(string keyDesc, Version? ffmpegVersion)
{
if (string.IsNullOrEmpty(keyDesc))
{
@@ -490,7 +495,9 @@ namespace MediaBrowser.MediaEncoding.Encoder
string output;
try
{
- output = GetProcessOutput(_encoderPath, "-hide_banner -f lavfi -i nullsrc=s=1x1:d=500 -f null -", true, "?");
+ // With multi-threaded cli support, FFmpeg 7 is less sensitive to keyboard input
+ var duration = ffmpegVersion >= _minFFmpegMultiThreadedCli ? 10000 : 1000;
+ output = GetProcessOutput(_encoderPath, $"-hide_banner -f lavfi -i nullsrc=s=1x1:d={duration} -f null -", true, "?");
}
catch (Exception ex)
{
diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs
index c5f500e76..2daeac734 100644
--- a/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs
@@ -42,7 +42,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
// If there's more than one we'll need to use the concat command
if (inputFiles.Count > 1)
{
- var files = string.Join("|", inputFiles.Select(NormalizePath));
+ var files = string.Join('|', inputFiles.Select(NormalizePath));
return string.Format(CultureInfo.InvariantCulture, "concat:\"{0}\"", files);
}
diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
index 5cfead502..764230feb 100644
--- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
@@ -12,6 +12,7 @@ using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using AsyncKeyedLock;
+using Jellyfin.Data.Enums;
using Jellyfin.Extensions;
using Jellyfin.Extensions.Json;
using Jellyfin.Extensions.Json.Converters;
@@ -79,8 +80,14 @@ namespace MediaBrowser.MediaEncoding.Encoder
private bool _isVaapiDeviceAmd = false;
private bool _isVaapiDeviceInteliHD = false;
private bool _isVaapiDeviceInteli965 = false;
+ private bool _isVaapiDeviceSupportVulkanDrmModifier = false;
private bool _isVaapiDeviceSupportVulkanDrmInterop = false;
+ private static string[] _vulkanImageDrmFmtModifierExts =
+ {
+ "VK_EXT_image_drm_format_modifier",
+ };
+
private static string[] _vulkanExternalMemoryDmaBufExts =
{
"VK_KHR_external_memory_fd",
@@ -141,34 +148,50 @@ namespace MediaBrowser.MediaEncoding.Encoder
public bool IsVaapiDeviceInteli965 => _isVaapiDeviceInteli965;
/// <inheritdoc />
+ public bool IsVaapiDeviceSupportVulkanDrmModifier => _isVaapiDeviceSupportVulkanDrmModifier;
+
+ /// <inheritdoc />
public bool IsVaapiDeviceSupportVulkanDrmInterop => _isVaapiDeviceSupportVulkanDrmInterop;
[GeneratedRegex(@"[^\/\\]+?(\.[^\/\\\n.]+)?$")]
private static partial Regex FfprobePathRegex();
/// <summary>
- /// Run at startup or if the user removes a Custom path from transcode page.
+ /// Run at startup to validate ffmpeg.
/// Sets global variables FFmpegPath.
- /// Precedence is: Config > CLI > $PATH.
+ /// Precedence is: CLI/Env var > Config > $PATH.
/// </summary>
- public void SetFFmpegPath()
+ /// <returns>bool indicates whether a valid ffmpeg is found.</returns>
+ public bool SetFFmpegPath()
{
+ var skipValidation = _config.GetFFmpegSkipValidation();
+ if (skipValidation)
+ {
+ _logger.LogWarning("FFmpeg: Skipping FFmpeg Validation due to FFmpeg:novalidation set to true");
+ return true;
+ }
+
// 1) Check if the --ffmpeg CLI switch has been given
var ffmpegPath = _startupOptionFFmpegPath;
+ string ffmpegPathSetMethodText = "command line or environment variable";
if (string.IsNullOrEmpty(ffmpegPath))
{
// 2) Custom path stored in config/encoding xml file under tag <EncoderAppPath> should be used as a fallback
ffmpegPath = _configurationManager.GetEncodingOptions().EncoderAppPath;
+ ffmpegPathSetMethodText = "encoding.xml config file";
if (string.IsNullOrEmpty(ffmpegPath))
{
// 3) Check "ffmpeg"
ffmpegPath = "ffmpeg";
+ ffmpegPathSetMethodText = "system $PATH";
}
}
if (!ValidatePath(ffmpegPath))
{
_ffmpegPath = null;
+ _logger.LogError("FFmpeg: Path set by {FfmpegPathSetMethodText} is invalid", ffmpegPathSetMethodText);
+ return false;
}
// Write the FFmpeg path to the config/encoding.xml file as <EncoderAppPathDisplay> so it appears in UI
@@ -194,7 +217,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
_threads = EncodingHelper.GetNumberOfThreads(null, options, null);
- _isPkeyPauseSupported = validator.CheckSupportedRuntimeKey("p pause transcoding");
+ _isPkeyPauseSupported = validator.CheckSupportedRuntimeKey("p pause transcoding", _ffmpegVersion);
_isLowPriorityHwDecodeSupported = validator.CheckSupportedHwaccelFlag("low_priority");
// Check the Vaapi device vendor
@@ -206,6 +229,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
_isVaapiDeviceAmd = validator.CheckVaapiDeviceByDriverName("Mesa Gallium driver", options.VaapiDevice);
_isVaapiDeviceInteliHD = validator.CheckVaapiDeviceByDriverName("Intel iHD driver", options.VaapiDevice);
_isVaapiDeviceInteli965 = validator.CheckVaapiDeviceByDriverName("Intel i965 driver", options.VaapiDevice);
+ _isVaapiDeviceSupportVulkanDrmModifier = validator.CheckVulkanDrmDeviceByExtensionName(options.VaapiDevice, _vulkanImageDrmFmtModifierExts);
_isVaapiDeviceSupportVulkanDrmInterop = validator.CheckVulkanDrmDeviceByExtensionName(options.VaapiDevice, _vulkanExternalMemoryDmaBufExts);
if (_isVaapiDeviceAmd)
@@ -221,6 +245,11 @@ namespace MediaBrowser.MediaEncoding.Encoder
_logger.LogInformation("VAAPI device {RenderNodePath} is Intel GPU (i965)", options.VaapiDevice);
}
+ if (_isVaapiDeviceSupportVulkanDrmModifier)
+ {
+ _logger.LogInformation("VAAPI device {RenderNodePath} supports Vulkan DRM modifier", options.VaapiDevice);
+ }
+
if (_isVaapiDeviceSupportVulkanDrmInterop)
{
_logger.LogInformation("VAAPI device {RenderNodePath} supports Vulkan DRM interop", options.VaapiDevice);
@@ -229,65 +258,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
}
_logger.LogInformation("FFmpeg: {FfmpegPath}", _ffmpegPath ?? string.Empty);
- }
-
- /// <summary>
- /// Triggered from the Settings > Transcoding UI page when users submits Custom FFmpeg path to use.
- /// Only write the new path to xml if it exists. Do not perform validation checks on ffmpeg here.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <param name="pathType">The path type.</param>
- public void UpdateEncoderPath(string path, string pathType)
- {
- var config = _configurationManager.GetEncodingOptions();
-
- // Filesystem may not be case insensitive, but EncoderAppPathDisplay should always point to a valid file?
- if (string.IsNullOrEmpty(config.EncoderAppPath)
- && string.Equals(config.EncoderAppPathDisplay, path, StringComparison.OrdinalIgnoreCase))
- {
- _logger.LogDebug("Existing ffmpeg path is empty and the new path is the same as {EncoderAppPathDisplay}. Skipping", nameof(config.EncoderAppPathDisplay));
- return;
- }
-
- string newPath;
-
- _logger.LogInformation("Attempting to update encoder path to {Path}. pathType: {PathType}", path ?? string.Empty, pathType ?? string.Empty);
-
- if (!string.Equals(pathType, "custom", StringComparison.OrdinalIgnoreCase))
- {
- throw new ArgumentException("Unexpected pathType value");
- }
-
- if (string.IsNullOrWhiteSpace(path))
- {
- // User had cleared the custom path in UI
- newPath = string.Empty;
- }
- else
- {
- if (Directory.Exists(path))
- {
- // Given path is directory, so resolve down to filename
- newPath = GetEncoderPathFromDirectory(path, "ffmpeg");
- }
- else
- {
- newPath = path;
- }
-
- if (!new EncoderValidator(_logger, newPath).ValidateVersion())
- {
- throw new ResourceNotFoundException();
- }
- }
-
- // Write the new ffmpeg path to the xml as <EncoderAppPath>
- // This ensures its not lost on next startup
- config.EncoderAppPath = newPath;
- _configurationManager.SaveConfiguration("encoding", config);
-
- // Trigger SetFFmpegPath so we validate the new path and setup probe path
- SetFFmpegPath();
+ return !string.IsNullOrWhiteSpace(ffmpegPath);
}
/// <summary>
@@ -306,7 +277,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
bool rc = new EncoderValidator(_logger, path).ValidateVersion();
if (!rc)
{
- _logger.LogWarning("FFmpeg: Failed version check: {Path}", path);
+ _logger.LogError("FFmpeg: Failed version check: {Path}", path);
return false;
}
@@ -710,18 +681,18 @@ namespace MediaBrowser.MediaEncoding.Encoder
filters.Add("thumbnail=n=" + (useLargerBatchSize ? "50" : "24"));
}
- // Use SW tonemap on HDR10/HLG video stream only when the zscale or tonemapx filter is available.
+ // Use SW tonemap on HDR video stream only when the zscale or tonemapx filter is available.
+ // Only enable Dolby Vision tonemap when tonemapx is available
var enableHdrExtraction = false;
- if (string.Equals(videoStream?.ColorTransfer, "smpte2084", StringComparison.OrdinalIgnoreCase)
- || string.Equals(videoStream?.ColorTransfer, "arib-std-b67", StringComparison.OrdinalIgnoreCase))
+ if (videoStream?.VideoRange == VideoRange.HDR)
{
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"))
+ else if (SupportsFilter("zscale") && videoStream.VideoRangeType != VideoRangeType.DOVI)
{
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");
@@ -764,8 +735,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
using (var processWrapper = new ProcessWrapper(process, this))
{
- bool ranToCompletion;
-
using (await _thumbnailResourcePool.LockAsync(cancellationToken).ConfigureAwait(false))
{
StartProcess(processWrapper);
@@ -779,22 +748,18 @@ namespace MediaBrowser.MediaEncoding.Encoder
try
{
await process.WaitForExitAsync(TimeSpan.FromMilliseconds(timeoutMs)).ConfigureAwait(false);
- ranToCompletion = true;
}
- catch (OperationCanceledException)
+ catch (OperationCanceledException ex)
{
process.Kill(true);
- ranToCompletion = false;
+ throw new FfmpegException(string.Format(CultureInfo.InvariantCulture, "ffmpeg image extraction timed out for {0} after {1}ms", inputPath, timeoutMs), ex);
}
}
- var exitCode = ranToCompletion ? processWrapper.ExitCode ?? 0 : -1;
var file = _fileSystem.GetFileInfo(tempExtractPath);
- if (exitCode == -1 || !file.Exists || file.Length == 0)
+ if (processWrapper.ExitCode > 0 || !file.Exists || file.Length == 0)
{
- _logger.LogError("ffmpeg image extraction failed for {Path}", inputPath);
-
throw new FfmpegException(string.Format(CultureInfo.InvariantCulture, "ffmpeg image extraction failed for {0}", inputPath));
}