aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.MediaEncoding/Encoder
diff options
context:
space:
mode:
authorgnattu <gnattu@users.noreply.github.com>2024-07-18 01:50:32 +0800
committerGitHub <noreply@github.com>2024-07-17 13:50:32 -0400
commit5262439300884680b1425b94de9c2f9c898d83ae (patch)
treec72793db0c57ce230ae1fed7f85bc19ffbc6628f /MediaBrowser.MediaEncoding/Encoder
parent8851ace5436c6a22b9489f88d20e1aab582ffddf (diff)
Enable hardware Trickplay processing pipeline for VideoToolbox (#11510)
Diffstat (limited to 'MediaBrowser.MediaEncoding/Encoder')
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs31
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs19
2 files changed, 49 insertions, 1 deletions
diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
index 06e2d3783..a865b0e4c 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<string> 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(?<codec>[\\w|-]+)\\s+.+$", RegexOptions.Multiline)]
private static partial Regex CodecRegex();
diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
index de1b65482..e7e48d6d8 100644
--- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
@@ -74,6 +74,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
private IDictionary<int, bool> _filtersWithOption = new Dictionary<int, bool>();
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);