mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-07-09 03:04:24 -04:00
Enable hardware Trickplay processing pipeline for VideoToolbox (#11510)
This commit is contained in:
parent
8851ace543
commit
5262439300
@ -120,7 +120,8 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||||||
private static readonly Dictionary<string, string> _mjpegCodecMap = new(StringComparer.OrdinalIgnoreCase)
|
private static readonly Dictionary<string, string> _mjpegCodecMap = new(StringComparer.OrdinalIgnoreCase)
|
||||||
{
|
{
|
||||||
{ "vaapi", _defaultMjpegEncoder + "_vaapi" },
|
{ "vaapi", _defaultMjpegEncoder + "_vaapi" },
|
||||||
{ "qsv", _defaultMjpegEncoder + "_qsv" }
|
{ "qsv", _defaultMjpegEncoder + "_qsv" },
|
||||||
|
{ "videotoolbox", _defaultMjpegEncoder + "_videotoolbox" }
|
||||||
};
|
};
|
||||||
|
|
||||||
public static readonly string[] LosslessAudioCodecs = new string[]
|
public static readonly string[] LosslessAudioCodecs = new string[]
|
||||||
|
@ -95,6 +95,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|||||||
"h264_v4l2m2m",
|
"h264_v4l2m2m",
|
||||||
"h264_videotoolbox",
|
"h264_videotoolbox",
|
||||||
"hevc_videotoolbox",
|
"hevc_videotoolbox",
|
||||||
|
"mjpeg_videotoolbox",
|
||||||
"h264_rkmpp",
|
"h264_rkmpp",
|
||||||
"hevc_rkmpp"
|
"hevc_rkmpp"
|
||||||
};
|
};
|
||||||
@ -500,6 +501,11 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|||||||
return output.Contains(keyDesc, StringComparison.Ordinal);
|
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)
|
private IEnumerable<string> GetCodecs(Codec codec)
|
||||||
{
|
{
|
||||||
string codecstr = codec == Codec.Encoder ? "encoders" : "decoders";
|
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)]
|
[GeneratedRegex("^\\s\\S{6}\\s(?<codec>[\\w|-]+)\\s+.+$", RegexOptions.Multiline)]
|
||||||
private static partial Regex CodecRegex();
|
private static partial Regex CodecRegex();
|
||||||
|
|
||||||
|
@ -74,6 +74,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|||||||
private IDictionary<int, bool> _filtersWithOption = new Dictionary<int, bool>();
|
private IDictionary<int, bool> _filtersWithOption = new Dictionary<int, bool>();
|
||||||
|
|
||||||
private bool _isPkeyPauseSupported = false;
|
private bool _isPkeyPauseSupported = false;
|
||||||
|
private bool _isLowPriorityHwDecodeSupported = false;
|
||||||
|
|
||||||
private bool _isVaapiDeviceAmd = false;
|
private bool _isVaapiDeviceAmd = false;
|
||||||
private bool _isVaapiDeviceInteliHD = false;
|
private bool _isVaapiDeviceInteliHD = false;
|
||||||
@ -194,6 +195,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|||||||
_threads = EncodingHelper.GetNumberOfThreads(null, options, null);
|
_threads = EncodingHelper.GetNumberOfThreads(null, options, null);
|
||||||
|
|
||||||
_isPkeyPauseSupported = validator.CheckSupportedRuntimeKey("p pause transcoding");
|
_isPkeyPauseSupported = validator.CheckSupportedRuntimeKey("p pause transcoding");
|
||||||
|
_isLowPriorityHwDecodeSupported = validator.CheckSupportedHwaccelFlag("low_priority");
|
||||||
|
|
||||||
// Check the Vaapi device vendor
|
// Check the Vaapi device vendor
|
||||||
if (OperatingSystem.IsLinux()
|
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
|
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)
|
if (enableKeyFrameOnlyExtraction)
|
||||||
{
|
{
|
||||||
inputArg = "-skip_frame nokey " + inputArg;
|
inputArg = "-skip_frame nokey " + inputArg;
|
||||||
@ -921,6 +929,14 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|||||||
encoderQuality = (100 - ((qualityScale - 1) * (100 / 30))) / 118;
|
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
|
// Output arguments
|
||||||
var targetDirectory = Path.Combine(_configurationManager.ApplicationPaths.TempDirectory, Guid.NewGuid().ToString("N"));
|
var targetDirectory = Path.Combine(_configurationManager.ApplicationPaths.TempDirectory, Guid.NewGuid().ToString("N"));
|
||||||
Directory.CreateDirectory(targetDirectory);
|
Directory.CreateDirectory(targetDirectory);
|
||||||
@ -929,12 +945,13 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|||||||
// Final command arguments
|
// Final command arguments
|
||||||
var args = string.Format(
|
var args = string.Format(
|
||||||
CultureInfo.InvariantCulture,
|
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,
|
inputArg,
|
||||||
filterParam,
|
filterParam,
|
||||||
outputThreads.GetValueOrDefault(_threads),
|
outputThreads.GetValueOrDefault(_threads),
|
||||||
vidEncoder,
|
vidEncoder,
|
||||||
qualityScale.HasValue ? "-qscale:v " + encoderQuality.Value.ToString(CultureInfo.InvariantCulture) + " " : string.Empty,
|
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",
|
"image2",
|
||||||
outputPath);
|
outputPath);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user