Merge pull request #6327 from nyanmisaka/tonemap-overlay
This commit is contained in:
commit
e83d7a8667
|
@ -37,6 +37,8 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
"ConstrainedHigh"
|
||||
};
|
||||
|
||||
private static readonly Version minVersionForCudaOverlay = new Version(4, 4);
|
||||
|
||||
public EncodingHelper(
|
||||
IMediaEncoder mediaEncoder,
|
||||
ISubtitleEncoder subtitleEncoder)
|
||||
|
@ -106,17 +108,41 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
private bool IsCudaSupported()
|
||||
{
|
||||
return _mediaEncoder.SupportsHwaccel("cuda")
|
||||
&& _mediaEncoder.SupportsFilter("scale_cuda", null)
|
||||
&& _mediaEncoder.SupportsFilter("yadif_cuda", null);
|
||||
&& _mediaEncoder.SupportsFilter("scale_cuda")
|
||||
&& _mediaEncoder.SupportsFilter("yadif_cuda")
|
||||
&& _mediaEncoder.SupportsFilter("hwupload_cuda");
|
||||
}
|
||||
|
||||
private bool IsTonemappingSupported(EncodingJobInfo state, EncodingOptions options)
|
||||
private bool IsOpenclTonemappingSupported(EncodingJobInfo state, EncodingOptions options)
|
||||
{
|
||||
var videoStream = state.VideoStream;
|
||||
return IsColorDepth10(state)
|
||||
if (videoStream == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return options.EnableTonemapping
|
||||
&& (string.Equals(videoStream.ColorTransfer, "smpte2084", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(videoStream.ColorTransfer, "arib-std-b67", StringComparison.OrdinalIgnoreCase))
|
||||
&& IsColorDepth10(state)
|
||||
&& _mediaEncoder.SupportsHwaccel("opencl")
|
||||
&& options.EnableTonemapping
|
||||
&& string.Equals(videoStream.VideoRange, "HDR", StringComparison.OrdinalIgnoreCase);
|
||||
&& _mediaEncoder.SupportsFilter("tonemap_opencl");
|
||||
}
|
||||
|
||||
private bool IsCudaTonemappingSupported(EncodingJobInfo state, EncodingOptions options)
|
||||
{
|
||||
var videoStream = state.VideoStream;
|
||||
if (videoStream == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return options.EnableTonemapping
|
||||
&& (string.Equals(videoStream.ColorTransfer, "smpte2084", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(videoStream.ColorTransfer, "arib-std-b67", StringComparison.OrdinalIgnoreCase))
|
||||
&& IsColorDepth10(state)
|
||||
&& _mediaEncoder.SupportsHwaccel("cuda")
|
||||
&& _mediaEncoder.SupportsFilterWithOption(FilterOptionType.TonemapCudaName);
|
||||
}
|
||||
|
||||
private bool IsVppTonemappingSupported(EncodingJobInfo state, EncodingOptions options)
|
||||
|
@ -132,23 +158,25 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
if (string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// Limited to HEVC for now since the filter doesn't accept master data from VP9.
|
||||
return IsColorDepth10(state)
|
||||
return options.EnableVppTonemapping
|
||||
&& string.Equals(videoStream.ColorTransfer, "smpte2084", StringComparison.OrdinalIgnoreCase)
|
||||
&& IsColorDepth10(state)
|
||||
&& string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase)
|
||||
&& _mediaEncoder.SupportsHwaccel("vaapi")
|
||||
&& options.EnableVppTonemapping
|
||||
&& string.Equals(videoStream.ColorTransfer, "smpte2084", StringComparison.OrdinalIgnoreCase);
|
||||
&& _mediaEncoder.SupportsFilter("tonemap_vaapi");
|
||||
}
|
||||
|
||||
// Hybrid VPP tonemapping for QSV with VAAPI
|
||||
if (OperatingSystem.IsLinux() && string.Equals(options.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// Limited to HEVC for now since the filter doesn't accept master data from VP9.
|
||||
return IsColorDepth10(state)
|
||||
return options.EnableVppTonemapping
|
||||
&& string.Equals(videoStream.ColorTransfer, "smpte2084", StringComparison.OrdinalIgnoreCase)
|
||||
&& IsColorDepth10(state)
|
||||
&& string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase)
|
||||
&& _mediaEncoder.SupportsHwaccel("vaapi")
|
||||
&& _mediaEncoder.SupportsHwaccel("qsv")
|
||||
&& options.EnableVppTonemapping
|
||||
&& string.Equals(videoStream.ColorTransfer, "smpte2084", StringComparison.OrdinalIgnoreCase);
|
||||
&& _mediaEncoder.SupportsFilter("tonemap_vaapi")
|
||||
&& _mediaEncoder.SupportsHwaccel("qsv");
|
||||
}
|
||||
|
||||
// Native VPP tonemapping may come to QSV in the future.
|
||||
|
@ -497,13 +525,16 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
/// Gets the input argument.
|
||||
/// </summary>
|
||||
/// <param name="state">Encoding state.</param>
|
||||
/// <param name="encodingOptions">Encoding options.</param>
|
||||
/// <param name="options">Encoding options.</param>
|
||||
/// <returns>Input arguments.</returns>
|
||||
public string GetInputArgument(EncodingJobInfo state, EncodingOptions encodingOptions)
|
||||
public string GetInputArgument(EncodingJobInfo state, EncodingOptions options)
|
||||
{
|
||||
var arg = new StringBuilder();
|
||||
var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, encodingOptions) ?? string.Empty;
|
||||
var outputVideoCodec = GetVideoEncoder(state, encodingOptions) ?? string.Empty;
|
||||
var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, options) ?? string.Empty;
|
||||
var outputVideoCodec = GetVideoEncoder(state, options) ?? string.Empty;
|
||||
var isWindows = OperatingSystem.IsWindows();
|
||||
var isLinux = OperatingSystem.IsLinux();
|
||||
var isMacOS = OperatingSystem.IsMacOS();
|
||||
var isSwDecoder = string.IsNullOrEmpty(videoDecoder);
|
||||
var isD3d11vaDecoder = videoDecoder.IndexOf("d3d11va", StringComparison.OrdinalIgnoreCase) != -1;
|
||||
var isVaapiDecoder = videoDecoder.IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1;
|
||||
|
@ -512,26 +543,24 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
var isQsvEncoder = outputVideoCodec.IndexOf("qsv", StringComparison.OrdinalIgnoreCase) != -1;
|
||||
var isNvdecDecoder = videoDecoder.Contains("cuda", StringComparison.OrdinalIgnoreCase);
|
||||
var isCuvidHevcDecoder = videoDecoder.Contains("hevc_cuvid", StringComparison.OrdinalIgnoreCase);
|
||||
var isWindows = OperatingSystem.IsWindows();
|
||||
var isLinux = OperatingSystem.IsLinux();
|
||||
var isMacOS = OperatingSystem.IsMacOS();
|
||||
var isTonemappingSupported = IsTonemappingSupported(state, encodingOptions);
|
||||
var isVppTonemappingSupported = IsVppTonemappingSupported(state, encodingOptions);
|
||||
var isCuvidVp9Decoder = videoDecoder.Contains("vp9_cuvid", StringComparison.OrdinalIgnoreCase);
|
||||
var isOpenclTonemappingSupported = IsOpenclTonemappingSupported(state, options);
|
||||
var isVppTonemappingSupported = IsVppTonemappingSupported(state, options);
|
||||
var isCudaTonemappingSupported = IsCudaTonemappingSupported(state, options);
|
||||
|
||||
if (!IsCopyCodec(outputVideoCodec))
|
||||
{
|
||||
if (state.IsVideoRequest
|
||||
&& _mediaEncoder.SupportsHwaccel("vaapi")
|
||||
&& string.Equals(encodingOptions.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase))
|
||||
&& string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (isVaapiDecoder)
|
||||
{
|
||||
if (isTonemappingSupported && !isVppTonemappingSupported)
|
||||
if (isOpenclTonemappingSupported && !isVppTonemappingSupported)
|
||||
{
|
||||
arg.Append("-init_hw_device vaapi=va:")
|
||||
.Append(encodingOptions.VaapiDevice)
|
||||
.Append(' ')
|
||||
.Append("-init_hw_device opencl=ocl@va ")
|
||||
.Append(options.VaapiDevice)
|
||||
.Append(" -init_hw_device opencl=ocl@va ")
|
||||
.Append("-hwaccel_device va ")
|
||||
.Append("-hwaccel_output_format vaapi ")
|
||||
.Append("-filter_hw_device ocl ");
|
||||
|
@ -540,14 +569,14 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
{
|
||||
arg.Append("-hwaccel_output_format vaapi ")
|
||||
.Append("-vaapi_device ")
|
||||
.Append(encodingOptions.VaapiDevice)
|
||||
.Append(options.VaapiDevice)
|
||||
.Append(' ');
|
||||
}
|
||||
}
|
||||
else if (!isVaapiDecoder && isVaapiEncoder)
|
||||
{
|
||||
arg.Append("-vaapi_device ")
|
||||
.Append(encodingOptions.VaapiDevice)
|
||||
.Append(options.VaapiDevice)
|
||||
.Append(' ');
|
||||
}
|
||||
|
||||
|
@ -555,7 +584,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
}
|
||||
|
||||
if (state.IsVideoRequest
|
||||
&& string.Equals(encodingOptions.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase))
|
||||
&& string.Equals(options.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
|
||||
|
||||
|
@ -591,9 +620,8 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
else if (isVaapiDecoder && isVppTonemappingSupported)
|
||||
{
|
||||
arg.Append("-init_hw_device vaapi=va:")
|
||||
.Append(encodingOptions.VaapiDevice)
|
||||
.Append(' ')
|
||||
.Append("-init_hw_device qsv@va ")
|
||||
.Append(options.VaapiDevice)
|
||||
.Append(" -init_hw_device qsv@va ")
|
||||
.Append("-hwaccel_output_format vaapi ");
|
||||
}
|
||||
|
||||
|
@ -602,7 +630,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
}
|
||||
|
||||
if (state.IsVideoRequest
|
||||
&& string.Equals(encodingOptions.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase)
|
||||
&& string.Equals(options.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase)
|
||||
&& isNvdecDecoder)
|
||||
{
|
||||
// Fix for 'No decoder surfaces left' error. https://trac.ffmpeg.org/ticket/7562
|
||||
|
@ -610,22 +638,31 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
}
|
||||
|
||||
if (state.IsVideoRequest
|
||||
&& ((string.Equals(encodingOptions.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase)
|
||||
&& (isNvdecDecoder || isCuvidHevcDecoder || isSwDecoder))
|
||||
|| (string.Equals(encodingOptions.HardwareAccelerationType, "amf", StringComparison.OrdinalIgnoreCase)
|
||||
&& (isD3d11vaDecoder || isSwDecoder))))
|
||||
&& ((string.Equals(options.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase)
|
||||
&& (isNvdecDecoder || isCuvidHevcDecoder || isCuvidVp9Decoder || isSwDecoder))))
|
||||
{
|
||||
if (isTonemappingSupported)
|
||||
if (!isCudaTonemappingSupported && isOpenclTonemappingSupported)
|
||||
{
|
||||
arg.Append("-init_hw_device opencl=ocl:")
|
||||
.Append(encodingOptions.OpenclDevice)
|
||||
.Append(' ')
|
||||
.Append("-filter_hw_device ocl ");
|
||||
.Append(options.OpenclDevice)
|
||||
.Append(" -filter_hw_device ocl ");
|
||||
}
|
||||
}
|
||||
|
||||
if (state.IsVideoRequest
|
||||
&& string.Equals(encodingOptions.HardwareAccelerationType, "videotoolbox", StringComparison.OrdinalIgnoreCase))
|
||||
&& string.Equals(options.HardwareAccelerationType, "amf", StringComparison.OrdinalIgnoreCase)
|
||||
&& (isD3d11vaDecoder || isSwDecoder))
|
||||
{
|
||||
if (isOpenclTonemappingSupported)
|
||||
{
|
||||
arg.Append("-init_hw_device opencl=ocl:")
|
||||
.Append(options.OpenclDevice)
|
||||
.Append(" -filter_hw_device ocl ");
|
||||
}
|
||||
}
|
||||
|
||||
if (state.IsVideoRequest
|
||||
&& string.Equals(options.HardwareAccelerationType, "videotoolbox", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
arg.Append("-hwaccel videotoolbox ");
|
||||
}
|
||||
|
@ -2010,14 +2047,18 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
var isQsvHevcEncoder = outputVideoCodec.Contains("hevc_qsv", StringComparison.OrdinalIgnoreCase);
|
||||
var isNvdecDecoder = videoDecoder.Contains("cuda", StringComparison.OrdinalIgnoreCase);
|
||||
var isNvencEncoder = outputVideoCodec.Contains("nvenc", StringComparison.OrdinalIgnoreCase);
|
||||
var isTonemappingSupported = IsTonemappingSupported(state, options);
|
||||
var isVppTonemappingSupported = IsVppTonemappingSupported(state, options);
|
||||
var isTonemappingSupportedOnVaapi = string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase) && isVaapiDecoder && (isVaapiH264Encoder || isVaapiHevcEncoder);
|
||||
var isTonemappingSupportedOnQsv = string.Equals(options.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase) && isVaapiDecoder && (isQsvH264Encoder || isQsvHevcEncoder);
|
||||
var isOpenclTonemappingSupported = IsOpenclTonemappingSupported(state, options);
|
||||
var isVppTonemappingSupported = IsVppTonemappingSupported(state, options);
|
||||
|
||||
var mediaEncoderVersion = _mediaEncoder.GetMediaEncoderVersion();
|
||||
var isCudaOverlaySupported = _mediaEncoder.SupportsFilter("overlay_cuda") && mediaEncoderVersion != null && mediaEncoderVersion >= minVersionForCudaOverlay;
|
||||
var isCudaFormatConversionSupported = _mediaEncoder.SupportsFilterWithOption(FilterOptionType.ScaleCudaFormat);
|
||||
|
||||
// Tonemapping and burn-in graphical subtitles requires overlay_vaapi.
|
||||
// But it's still in ffmpeg mailing list. Disable it for now.
|
||||
if (isTonemappingSupportedOnVaapi && isTonemappingSupported && !isVppTonemappingSupported)
|
||||
if (isTonemappingSupportedOnVaapi && isOpenclTonemappingSupported && !isVppTonemappingSupported)
|
||||
{
|
||||
return GetOutputSizeParam(state, options, outputVideoCodec);
|
||||
}
|
||||
|
@ -2043,13 +2084,22 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
if (!string.IsNullOrEmpty(videoSizeParam)
|
||||
&& !(isTonemappingSupportedOnQsv && isVppTonemappingSupported))
|
||||
{
|
||||
// For QSV, feed it into hardware encoder now
|
||||
// upload graphical subtitle to QSV
|
||||
if (isLinux && (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(outputVideoCodec, "hevc_qsv", StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
videoSizeParam += ",hwupload=extra_hw_frames=64";
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(videoSizeParam))
|
||||
{
|
||||
// upload graphical subtitle to cuda
|
||||
if (isNvdecDecoder && isNvencEncoder && isCudaOverlaySupported && isCudaFormatConversionSupported)
|
||||
{
|
||||
videoSizeParam += ",hwupload_cuda";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var mapPrefix = state.SubtitleStream.IsExternal ?
|
||||
|
@ -2062,9 +2112,9 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
|
||||
// Setup default filtergraph utilizing FFMpeg overlay() and FFMpeg scale() (see the return of this function for index reference)
|
||||
// Always put the scaler before the overlay for better performance
|
||||
var retStr = !outputSizeParam.IsEmpty
|
||||
? " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay\""
|
||||
: " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay\"";
|
||||
var retStr = outputSizeParam.IsEmpty
|
||||
? " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay\""
|
||||
: " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay\"";
|
||||
|
||||
// When the input may or may not be hardware VAAPI decodable
|
||||
if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase)
|
||||
|
@ -2075,9 +2125,9 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
[sub]: SW scaling subtitle to FixedOutputSize
|
||||
[base][sub]: SW overlay
|
||||
*/
|
||||
retStr = !outputSizeParam.IsEmpty
|
||||
? " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3},hwdownload[base];[base][sub]overlay,format=nv12,hwupload\""
|
||||
: " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]hwdownload[base];[base][sub]overlay,format=nv12,hwupload\"";
|
||||
retStr = outputSizeParam.IsEmpty
|
||||
? " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]hwdownload[base];[base][sub]overlay,format=nv12,hwupload\""
|
||||
: " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3},hwdownload[base];[base][sub]overlay,format=nv12,hwupload\"";
|
||||
}
|
||||
|
||||
// If we're hardware VAAPI decoding and software encoding, download frames from the decoder first
|
||||
|
@ -2090,9 +2140,9 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
[sub]: SW scaling subtitle to FixedOutputSize
|
||||
[base][sub]: SW overlay
|
||||
*/
|
||||
retStr = !outputSizeParam.IsEmpty
|
||||
? " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay\""
|
||||
: " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay\"";
|
||||
retStr = outputSizeParam.IsEmpty
|
||||
? " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay\""
|
||||
: " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay\"";
|
||||
}
|
||||
else if (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(outputVideoCodec, "hevc_qsv", StringComparison.OrdinalIgnoreCase))
|
||||
|
@ -2109,16 +2159,25 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
}
|
||||
else if (isLinux)
|
||||
{
|
||||
retStr = !outputSizeParam.IsEmpty
|
||||
? " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay_qsv\""
|
||||
: " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay_qsv\"";
|
||||
retStr = outputSizeParam.IsEmpty
|
||||
? " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay_qsv\""
|
||||
: " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay_qsv\"";
|
||||
}
|
||||
}
|
||||
else if (isNvdecDecoder && isNvencEncoder)
|
||||
{
|
||||
retStr = !outputSizeParam.IsEmpty
|
||||
? " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay,format=nv12|yuv420p,hwupload_cuda\""
|
||||
: " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay,format=nv12|yuv420p,hwupload_cuda\"";
|
||||
if (isCudaOverlaySupported && isCudaFormatConversionSupported)
|
||||
{
|
||||
retStr = outputSizeParam.IsEmpty
|
||||
? " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]scale_cuda=format=yuv420p[base];[base][sub]overlay_cuda\""
|
||||
: " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay_cuda\"";
|
||||
}
|
||||
else
|
||||
{
|
||||
retStr = outputSizeParam.IsEmpty
|
||||
? " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay,format=nv12|yuv420p,hwupload_cuda\""
|
||||
: " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay,format=nv12|yuv420p,hwupload_cuda\"";
|
||||
}
|
||||
}
|
||||
|
||||
return string.Format(
|
||||
|
@ -2215,11 +2274,11 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
var isVaapiHevcEncoder = videoEncoder.Contains("hevc_vaapi", StringComparison.OrdinalIgnoreCase);
|
||||
var isQsvH264Encoder = videoEncoder.Contains("h264_qsv", StringComparison.OrdinalIgnoreCase);
|
||||
var isQsvHevcEncoder = videoEncoder.Contains("hevc_qsv", StringComparison.OrdinalIgnoreCase);
|
||||
var isTonemappingSupported = IsTonemappingSupported(state, options);
|
||||
var isOpenclTonemappingSupported = IsOpenclTonemappingSupported(state, options);
|
||||
var isVppTonemappingSupported = IsVppTonemappingSupported(state, options);
|
||||
var isTonemappingSupportedOnVaapi = string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase) && isVaapiDecoder && (isVaapiH264Encoder || isVaapiHevcEncoder);
|
||||
var isTonemappingSupportedOnQsv = string.Equals(options.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase) && isVaapiDecoder && (isQsvH264Encoder || isQsvHevcEncoder);
|
||||
var isP010PixFmtRequired = (isTonemappingSupportedOnVaapi && (isTonemappingSupported || isVppTonemappingSupported))
|
||||
var isP010PixFmtRequired = (isTonemappingSupportedOnVaapi && (isOpenclTonemappingSupported || isVppTonemappingSupported))
|
||||
|| (isTonemappingSupportedOnQsv && isVppTonemappingSupported);
|
||||
|
||||
var outputPixFmt = "format=nv12";
|
||||
|
@ -2270,15 +2329,23 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
var outputWidth = width.Value;
|
||||
var outputHeight = height.Value;
|
||||
|
||||
var isTonemappingSupported = IsTonemappingSupported(state, options);
|
||||
var isNvencEncoder = videoEncoder.Contains("nvenc", StringComparison.OrdinalIgnoreCase);
|
||||
var isOpenclTonemappingSupported = IsOpenclTonemappingSupported(state, options);
|
||||
var isCudaTonemappingSupported = IsCudaTonemappingSupported(state, options);
|
||||
var isTonemappingSupportedOnNvenc = string.Equals(options.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase);
|
||||
var isCudaFormatConversionSupported = _mediaEncoder.SupportsFilter("scale_cuda", "Output format (default \"same\")");
|
||||
var mediaEncoderVersion = _mediaEncoder.GetMediaEncoderVersion();
|
||||
var isCudaOverlaySupported = _mediaEncoder.SupportsFilter("overlay_cuda") && mediaEncoderVersion != null && mediaEncoderVersion >= minVersionForCudaOverlay;
|
||||
var isCudaFormatConversionSupported = _mediaEncoder.SupportsFilterWithOption(FilterOptionType.ScaleCudaFormat);
|
||||
var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
|
||||
|
||||
var outputPixFmt = string.Empty;
|
||||
if (isCudaFormatConversionSupported)
|
||||
{
|
||||
outputPixFmt = "format=nv12";
|
||||
if (isTonemappingSupported && isTonemappingSupportedOnNvenc)
|
||||
outputPixFmt = (hasGraphicalSubs && isCudaOverlaySupported && isNvencEncoder)
|
||||
? "format=yuv420p"
|
||||
: "format=nv12";
|
||||
if ((isOpenclTonemappingSupported || isCudaTonemappingSupported)
|
||||
&& isTonemappingSupportedOnNvenc)
|
||||
{
|
||||
outputPixFmt = "format=p010";
|
||||
}
|
||||
|
@ -2556,16 +2623,21 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
var isNvencEncoder = outputVideoCodec.Contains("nvenc", StringComparison.OrdinalIgnoreCase);
|
||||
var isCuvidH264Decoder = videoDecoder.Contains("h264_cuvid", StringComparison.OrdinalIgnoreCase);
|
||||
var isCuvidHevcDecoder = videoDecoder.Contains("hevc_cuvid", StringComparison.OrdinalIgnoreCase);
|
||||
var isCuvidVp9Decoder = videoDecoder.Contains("vp9_cuvid", StringComparison.OrdinalIgnoreCase);
|
||||
var isLibX264Encoder = outputVideoCodec.IndexOf("libx264", StringComparison.OrdinalIgnoreCase) != -1;
|
||||
var isLibX265Encoder = outputVideoCodec.IndexOf("libx265", StringComparison.OrdinalIgnoreCase) != -1;
|
||||
var isLinux = OperatingSystem.IsLinux();
|
||||
var isColorDepth10 = IsColorDepth10(state);
|
||||
var isTonemappingSupported = IsTonemappingSupported(state, options);
|
||||
var isVppTonemappingSupported = IsVppTonemappingSupported(state, options);
|
||||
var isTonemappingSupportedOnNvenc = string.Equals(options.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase) && (isNvdecDecoder || isCuvidHevcDecoder || isSwDecoder);
|
||||
|
||||
var isTonemappingSupportedOnNvenc = string.Equals(options.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase) && (isNvdecDecoder || isCuvidHevcDecoder || isCuvidVp9Decoder || isSwDecoder);
|
||||
var isTonemappingSupportedOnAmf = string.Equals(options.HardwareAccelerationType, "amf", StringComparison.OrdinalIgnoreCase) && (isD3d11vaDecoder || isSwDecoder);
|
||||
var isTonemappingSupportedOnVaapi = string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase) && isVaapiDecoder && (isVaapiH264Encoder || isVaapiHevcEncoder);
|
||||
var isTonemappingSupportedOnQsv = string.Equals(options.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase) && isVaapiDecoder && (isQsvH264Encoder || isQsvHevcEncoder);
|
||||
var isOpenclTonemappingSupported = IsOpenclTonemappingSupported(state, options);
|
||||
var isVppTonemappingSupported = IsVppTonemappingSupported(state, options);
|
||||
var isCudaTonemappingSupported = IsCudaTonemappingSupported(state, options);
|
||||
var mediaEncoderVersion = _mediaEncoder.GetMediaEncoderVersion();
|
||||
var isCudaOverlaySupported = _mediaEncoder.SupportsFilter("overlay_cuda") && mediaEncoderVersion != null && mediaEncoderVersion >= minVersionForCudaOverlay;
|
||||
|
||||
var hasSubs = state.SubtitleStream != null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
|
||||
var hasTextSubs = state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
|
||||
|
@ -2577,19 +2649,25 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
var isScalingInAdvance = false;
|
||||
var isCudaDeintInAdvance = false;
|
||||
var isHwuploadCudaRequired = false;
|
||||
var isNoTonemapFilterApplied = true;
|
||||
var isDeinterlaceH264 = state.DeInterlace("h264", true) || state.DeInterlace("avc", true);
|
||||
var isDeinterlaceHevc = state.DeInterlace("h265", true) || state.DeInterlace("hevc", true);
|
||||
|
||||
// Add OpenCL tonemapping filter for NVENC/AMF/VAAPI.
|
||||
if (isTonemappingSupportedOnNvenc || isTonemappingSupportedOnAmf || (isTonemappingSupportedOnVaapi && !isVppTonemappingSupported))
|
||||
if ((isTonemappingSupportedOnNvenc && !isCudaTonemappingSupported) || isTonemappingSupportedOnAmf || (isTonemappingSupportedOnVaapi && !isVppTonemappingSupported))
|
||||
{
|
||||
// Currently only with the use of NVENC decoder can we get a decent performance.
|
||||
// Currently only the HEVC/H265 format is supported with NVDEC decoder.
|
||||
// NVIDIA Pascal and Turing or higher are recommended.
|
||||
// AMD Polaris and Vega or higher are recommended.
|
||||
// Intel Kaby Lake or newer is required.
|
||||
if (isTonemappingSupported)
|
||||
if (isOpenclTonemappingSupported)
|
||||
{
|
||||
isNoTonemapFilterApplied = false;
|
||||
var inputHdrParams = GetInputHdrParams(videoStream.ColorTransfer);
|
||||
if (!string.IsNullOrEmpty(inputHdrParams))
|
||||
{
|
||||
filters.Add(inputHdrParams);
|
||||
}
|
||||
|
||||
var parameters = "tonemap_opencl=format=nv12:primaries=bt709:transfer=bt709:matrix=bt709:tonemap={0}:desat={1}:threshold={2}:peak={3}";
|
||||
|
||||
if (options.TonemappingParam != 0)
|
||||
|
@ -2661,7 +2739,11 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
filters.Add("hwdownload,format=p010");
|
||||
}
|
||||
|
||||
if (isNvdecDecoder || isCuvidHevcDecoder || isSwDecoder || isD3d11vaDecoder)
|
||||
if (isNvdecDecoder
|
||||
|| isCuvidHevcDecoder
|
||||
|| isCuvidVp9Decoder
|
||||
|| isSwDecoder
|
||||
|| isD3d11vaDecoder)
|
||||
{
|
||||
// Upload the HDR10 or HLG data to the OpenCL device,
|
||||
// use tonemap_opencl filter for tone mapping,
|
||||
|
@ -2669,6 +2751,14 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
filters.Add("hwupload");
|
||||
}
|
||||
|
||||
// Fallback to hable if bt2390 is chosen but not supported in tonemap_opencl.
|
||||
var isBt2390SupportedInOpenclTonemap = _mediaEncoder.SupportsFilterWithOption(FilterOptionType.TonemapOpenclBt2390);
|
||||
if (string.Equals(options.TonemappingAlgorithm, "bt2390", StringComparison.OrdinalIgnoreCase)
|
||||
&& !isBt2390SupportedInOpenclTonemap)
|
||||
{
|
||||
options.TonemappingAlgorithm = "hable";
|
||||
}
|
||||
|
||||
filters.Add(
|
||||
string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
|
@ -2680,7 +2770,11 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
options.TonemappingParam,
|
||||
options.TonemappingRange));
|
||||
|
||||
if (isNvdecDecoder || isCuvidHevcDecoder || isSwDecoder || isD3d11vaDecoder)
|
||||
if (isNvdecDecoder
|
||||
|| isCuvidHevcDecoder
|
||||
|| isCuvidVp9Decoder
|
||||
|| isSwDecoder
|
||||
|| isD3d11vaDecoder)
|
||||
{
|
||||
filters.Add("hwdownload");
|
||||
filters.Add("format=nv12");
|
||||
|
@ -2696,12 +2790,18 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
// Reverse the data route from opencl to vaapi.
|
||||
filters.Add("hwmap=derive_device=vaapi:reverse=1");
|
||||
}
|
||||
|
||||
var outputSdrParams = GetOutputSdrParams(options.TonemappingRange);
|
||||
if (!string.IsNullOrEmpty(outputSdrParams))
|
||||
{
|
||||
filters.Add(outputSdrParams);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// When the input may or may not be hardware VAAPI decodable.
|
||||
if ((isVaapiH264Encoder || isVaapiHevcEncoder)
|
||||
&& !(isTonemappingSupportedOnVaapi && (isTonemappingSupported || isVppTonemappingSupported)))
|
||||
&& !(isTonemappingSupportedOnVaapi && (isOpenclTonemappingSupported || isVppTonemappingSupported)))
|
||||
{
|
||||
filters.Add("format=nv12|vaapi");
|
||||
filters.Add("hwupload");
|
||||
|
@ -2809,6 +2909,61 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
request.MaxHeight));
|
||||
}
|
||||
|
||||
// Add Cuda tonemapping filter.
|
||||
if (isNvdecDecoder && isCudaTonemappingSupported)
|
||||
{
|
||||
isNoTonemapFilterApplied = false;
|
||||
var inputHdrParams = GetInputHdrParams(videoStream.ColorTransfer);
|
||||
if (!string.IsNullOrEmpty(inputHdrParams))
|
||||
{
|
||||
filters.Add(inputHdrParams);
|
||||
}
|
||||
|
||||
var parameters = (hasGraphicalSubs && isCudaOverlaySupported && isNvencEncoder)
|
||||
? "tonemap_cuda=format=yuv420p:primaries=bt709:transfer=bt709:matrix=bt709:tonemap={0}:peak={1}:desat={2}"
|
||||
: "tonemap_cuda=format=nv12:primaries=bt709:transfer=bt709:matrix=bt709:tonemap={0}:peak={1}:desat={2}";
|
||||
|
||||
if (options.TonemappingParam != 0)
|
||||
{
|
||||
parameters += ":param={3}";
|
||||
}
|
||||
|
||||
if (!string.Equals(options.TonemappingRange, "auto", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
parameters += ":range={4}";
|
||||
}
|
||||
|
||||
filters.Add(
|
||||
string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
parameters,
|
||||
options.TonemappingAlgorithm,
|
||||
options.TonemappingPeak,
|
||||
options.TonemappingDesat,
|
||||
options.TonemappingParam,
|
||||
options.TonemappingRange));
|
||||
|
||||
if (isLibX264Encoder
|
||||
|| isLibX265Encoder
|
||||
|| hasTextSubs
|
||||
|| (hasGraphicalSubs && !isCudaOverlaySupported && isNvencEncoder))
|
||||
{
|
||||
if (isNvencEncoder)
|
||||
{
|
||||
isHwuploadCudaRequired = true;
|
||||
}
|
||||
|
||||
filters.Add("hwdownload");
|
||||
filters.Add("format=nv12");
|
||||
}
|
||||
|
||||
var outputSdrParams = GetOutputSdrParams(options.TonemappingRange);
|
||||
if (!string.IsNullOrEmpty(outputSdrParams))
|
||||
{
|
||||
filters.Add(outputSdrParams);
|
||||
}
|
||||
}
|
||||
|
||||
// Add VPP tonemapping filter for VAAPI.
|
||||
// Full hardware based video post processing, faster than OpenCL but lacks fine tuning options.
|
||||
if ((isTonemappingSupportedOnVaapi || isTonemappingSupportedOnQsv)
|
||||
|
@ -2818,10 +2973,10 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
}
|
||||
|
||||
// Another case is when using Nvenc decoder.
|
||||
if (isNvdecDecoder && !isTonemappingSupported)
|
||||
if (isNvdecDecoder && !isOpenclTonemappingSupported && !isCudaTonemappingSupported)
|
||||
{
|
||||
var codec = videoStream.Codec;
|
||||
var isCudaFormatConversionSupported = _mediaEncoder.SupportsFilter("scale_cuda", "Output format (default \"same\")");
|
||||
var isCudaFormatConversionSupported = _mediaEncoder.SupportsFilterWithOption(FilterOptionType.ScaleCudaFormat);
|
||||
|
||||
// Assert 10-bit hardware decodable
|
||||
if (isColorDepth10 && (string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase)
|
||||
|
@ -2830,7 +2985,10 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
{
|
||||
if (isCudaFormatConversionSupported)
|
||||
{
|
||||
if (isLibX264Encoder || isLibX265Encoder || hasSubs)
|
||||
if (isLibX264Encoder
|
||||
|| isLibX265Encoder
|
||||
|| hasTextSubs
|
||||
|| (hasGraphicalSubs && !isCudaOverlaySupported && isNvencEncoder))
|
||||
{
|
||||
if (isNvencEncoder)
|
||||
{
|
||||
|
@ -2857,7 +3015,11 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
}
|
||||
|
||||
// Assert 8-bit hardware decodable
|
||||
else if (!isColorDepth10 && (isLibX264Encoder || isLibX265Encoder || hasSubs))
|
||||
else if (!isColorDepth10
|
||||
&& (isLibX264Encoder
|
||||
|| isLibX265Encoder
|
||||
|| hasTextSubs
|
||||
|| (hasGraphicalSubs && !isCudaOverlaySupported && isNvencEncoder)))
|
||||
{
|
||||
if (isNvencEncoder)
|
||||
{
|
||||
|
@ -2878,7 +3040,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
{
|
||||
// Convert hw context from ocl to va.
|
||||
// For tonemapping and text subs burn-in.
|
||||
if (isTonemappingSupportedOnVaapi && isTonemappingSupported && !isVppTonemappingSupported)
|
||||
if (isTonemappingSupportedOnVaapi && isOpenclTonemappingSupported && !isVppTonemappingSupported)
|
||||
{
|
||||
filters.Add("scale_vaapi");
|
||||
}
|
||||
|
@ -2924,6 +3086,17 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
filters.Add("hwupload_cuda");
|
||||
}
|
||||
|
||||
// If no tonemap filter is applied,
|
||||
// tag the video range as SDR to prevent the encoder from encoding HDR video.
|
||||
if (isNoTonemapFilterApplied)
|
||||
{
|
||||
var outputSdrParams = GetOutputSdrParams(null);
|
||||
if (!string.IsNullOrEmpty(outputSdrParams))
|
||||
{
|
||||
filters.Add(outputSdrParams);
|
||||
}
|
||||
}
|
||||
|
||||
var output = string.Empty;
|
||||
if (filters.Count > 0)
|
||||
{
|
||||
|
@ -2936,6 +3109,36 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
return output;
|
||||
}
|
||||
|
||||
public static string GetInputHdrParams(string colorTransfer)
|
||||
{
|
||||
if (string.Equals(colorTransfer, "arib-std-b67", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// HLG
|
||||
return "setparams=color_primaries=bt2020:color_trc=arib-std-b67:colorspace=bt2020nc";
|
||||
}
|
||||
else
|
||||
{
|
||||
// HDR10
|
||||
return "setparams=color_primaries=bt2020:color_trc=smpte2084:colorspace=bt2020nc";
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetOutputSdrParams(string tonemappingRange)
|
||||
{
|
||||
// SDR
|
||||
if (string.Equals(tonemappingRange, "tv", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return "setparams=color_primaries=bt709:color_trc=bt709:colorspace=bt709:range=tv";
|
||||
}
|
||||
|
||||
if (string.Equals(tonemappingRange, "pc", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return "setparams=color_primaries=bt709:color_trc=bt709:colorspace=bt709:range=pc";
|
||||
}
|
||||
|
||||
return "setparams=color_primaries=bt709:color_trc=bt709:colorspace=bt709";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of threads.
|
||||
/// </summary>
|
||||
|
@ -3406,8 +3609,13 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
if (string.Equals(encodingOptions.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase)
|
||||
&& IsVppTonemappingSupported(state, encodingOptions))
|
||||
{
|
||||
// Since tonemap_vaapi only support HEVC for now, no need to check the codec again.
|
||||
return GetHwaccelType(state, encodingOptions, "hevc", isColorDepth10);
|
||||
var outputVideoCodec = GetVideoEncoder(state, encodingOptions) ?? string.Empty;
|
||||
var isQsvEncoder = outputVideoCodec.Contains("qsv", StringComparison.OrdinalIgnoreCase);
|
||||
if (isQsvEncoder)
|
||||
{
|
||||
// Since tonemap_vaapi only support HEVC for now, no need to check the codec again.
|
||||
return GetHwaccelType(state, encodingOptions, "hevc", isColorDepth10);
|
||||
}
|
||||
}
|
||||
|
||||
if (string.Equals(encodingOptions.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase))
|
||||
|
@ -3940,6 +4148,11 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
|
||||
if (videoStream != null)
|
||||
{
|
||||
if (videoStream.BitDepth.HasValue)
|
||||
{
|
||||
return videoStream.BitDepth.Value == 10;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(videoStream.PixelFormat))
|
||||
{
|
||||
result = videoStream.PixelFormat.Contains("p10", StringComparison.OrdinalIgnoreCase);
|
||||
|
@ -3959,12 +4172,6 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
result = (videoStream.BitDepth ?? 8) == 10;
|
||||
if (result)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
23
MediaBrowser.Controller/MediaEncoding/FilterOptionType.cs
Normal file
23
MediaBrowser.Controller/MediaEncoding/FilterOptionType.cs
Normal file
|
@ -0,0 +1,23 @@
|
|||
namespace MediaBrowser.Controller.MediaEncoding
|
||||
{
|
||||
/// <summary>
|
||||
/// Enum FilterOptionType.
|
||||
/// </summary>
|
||||
public enum FilterOptionType
|
||||
{
|
||||
/// <summary>
|
||||
/// The scale_cuda_format.
|
||||
/// </summary>
|
||||
ScaleCudaFormat = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The tonemap_cuda_name.
|
||||
/// </summary>
|
||||
TonemapCudaName = 1,
|
||||
|
||||
/// <summary>
|
||||
/// The tonemap_opencl_bt2390.
|
||||
/// </summary>
|
||||
TonemapOpenclBt2390 = 2
|
||||
}
|
||||
}
|
|
@ -49,9 +49,21 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
/// Whether given filter is supported.
|
||||
/// </summary>
|
||||
/// <param name="filter">The filter.</param>
|
||||
/// <returns><c>true</c> if the filter is supported, <c>false</c> otherwise.</returns>
|
||||
bool SupportsFilter(string filter);
|
||||
|
||||
/// <summary>
|
||||
/// Whether filter is supported with the given option.
|
||||
/// </summary>
|
||||
/// <param name="option">The option.</param>
|
||||
/// <returns><c>true</c> if the filter is supported, <c>false</c> otherwise.</returns>
|
||||
bool SupportsFilter(string filter, string option);
|
||||
bool SupportsFilterWithOption(FilterOptionType option);
|
||||
|
||||
/// <summary>
|
||||
/// Get the version of media encoder.
|
||||
/// </summary>
|
||||
/// <returns>The version of media encoder.</returns>
|
||||
Version GetMediaEncoderVersion();
|
||||
|
||||
/// <summary>
|
||||
/// Extracts the audio image.
|
||||
|
|
|
@ -87,6 +87,24 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|||
"hevc_videotoolbox"
|
||||
};
|
||||
|
||||
private static readonly string[] _requiredFilters = new[]
|
||||
{
|
||||
"scale_cuda",
|
||||
"yadif_cuda",
|
||||
"hwupload_cuda",
|
||||
"overlay_cuda",
|
||||
"tonemap_cuda",
|
||||
"tonemap_opencl",
|
||||
"tonemap_vaapi",
|
||||
};
|
||||
|
||||
private static readonly IReadOnlyDictionary<int, string[]> _filterOptionsDict = new Dictionary<int, string[]>
|
||||
{
|
||||
{ 0, new string[] { "scale_cuda", "Output format (default \"same\")" } },
|
||||
{ 1, new string[] { "tonemap_cuda", "GPU accelerated HDR to SDR tonemapping" } },
|
||||
{ 2, new string[] { "tonemap_opencl", "bt2390" } }
|
||||
};
|
||||
|
||||
// These are the library versions that corresponds to our minimum ffmpeg version 4.x according to the version table below
|
||||
private static readonly IReadOnlyDictionary<string, Version> _ffmpegMinimumLibraryVersions = new Dictionary<string, Version>
|
||||
{
|
||||
|
@ -154,7 +172,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|||
}
|
||||
|
||||
// Work out what the version under test is
|
||||
var version = GetFFmpegVersion(versionOutput);
|
||||
var version = GetFFmpegVersionInternal(versionOutput);
|
||||
|
||||
_logger.LogInformation("Found ffmpeg version {Version}", version != null ? version.ToString() : "unknown");
|
||||
|
||||
|
@ -198,6 +216,34 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|||
|
||||
public IEnumerable<string> GetHwaccels() => GetHwaccelTypes();
|
||||
|
||||
public IEnumerable<string> GetFilters() => GetFFmpegFilters();
|
||||
|
||||
public IDictionary<int, bool> GetFiltersWithOption() => GetFFmpegFiltersWithOption();
|
||||
|
||||
public Version? GetFFmpegVersion()
|
||||
{
|
||||
string output;
|
||||
try
|
||||
{
|
||||
output = GetProcessOutput(_encoderPath, "-version");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error validating encoder");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(output))
|
||||
{
|
||||
_logger.LogError("FFmpeg validation: The process returned no result");
|
||||
return null;
|
||||
}
|
||||
|
||||
_logger.LogDebug("ffmpeg output: {Output}", output);
|
||||
|
||||
return GetFFmpegVersionInternal(output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Using the output from "ffmpeg -version" work out the FFmpeg version.
|
||||
/// For pre-built binaries the first line should contain a string like "ffmpeg version x.y", which is easy
|
||||
|
@ -206,7 +252,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|||
/// </summary>
|
||||
/// <param name="output">The output from "ffmpeg -version".</param>
|
||||
/// <returns>The FFmpeg version.</returns>
|
||||
internal Version? GetFFmpegVersion(string output)
|
||||
internal Version? GetFFmpegVersionInternal(string output)
|
||||
{
|
||||
// For pre-built binaries the FFmpeg version should be mentioned at the very start of the output
|
||||
var match = Regex.Match(output, @"^ffmpeg version n?((?:[0-9]+\.?)+)");
|
||||
|
@ -295,9 +341,9 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|||
return found;
|
||||
}
|
||||
|
||||
public bool CheckFilter(string filter, string option)
|
||||
public bool CheckFilterWithOption(string filter, string option)
|
||||
{
|
||||
if (string.IsNullOrEmpty(filter))
|
||||
if (string.IsNullOrEmpty(filter) || string.IsNullOrEmpty(option))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -315,11 +361,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|||
|
||||
if (output.Contains("Filter " + filter, StringComparison.Ordinal))
|
||||
{
|
||||
if (string.IsNullOrEmpty(option))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return output.Contains(option, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
|
@ -360,6 +401,49 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|||
return found;
|
||||
}
|
||||
|
||||
private IEnumerable<string> GetFFmpegFilters()
|
||||
{
|
||||
string output;
|
||||
try
|
||||
{
|
||||
output = GetProcessOutput(_encoderPath, "-filters");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error detecting available filters");
|
||||
return Enumerable.Empty<string>();
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(output))
|
||||
{
|
||||
return Enumerable.Empty<string>();
|
||||
}
|
||||
|
||||
var found = Regex
|
||||
.Matches(output, @"^\s\S{3}\s(?<filter>[\w|-]+)\s+.+$", RegexOptions.Multiline)
|
||||
.Cast<Match>()
|
||||
.Select(x => x.Groups["filter"].Value)
|
||||
.Where(x => _requiredFilters.Contains(x));
|
||||
|
||||
_logger.LogInformation("Available filters: {Filters}", found);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
private IDictionary<int, bool> GetFFmpegFiltersWithOption()
|
||||
{
|
||||
IDictionary<int, bool> dict = new Dictionary<int, bool>();
|
||||
for (int i = 0; i < _filterOptionsDict.Count; i++)
|
||||
{
|
||||
if (_filterOptionsDict.TryGetValue(i, out var val) && val.Length == 2)
|
||||
{
|
||||
dict.Add(i, CheckFilterWithOption(val[0], val[1]));
|
||||
}
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
private string GetProcessOutput(string path, string arguments)
|
||||
{
|
||||
using (var process = new Process()
|
||||
|
|
|
@ -65,7 +65,10 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|||
private List<string> _encoders = new List<string>();
|
||||
private List<string> _decoders = new List<string>();
|
||||
private List<string> _hwaccels = new List<string>();
|
||||
private List<string> _filters = new List<string>();
|
||||
private IDictionary<int, bool> _filtersWithOption = new Dictionary<int, bool>();
|
||||
|
||||
private Version _ffmpegVersion = null;
|
||||
private string _ffmpegPath = string.Empty;
|
||||
private string _ffprobePath;
|
||||
private int _threads;
|
||||
|
@ -129,7 +132,11 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|||
|
||||
SetAvailableDecoders(validator.GetDecoders());
|
||||
SetAvailableEncoders(validator.GetEncoders());
|
||||
SetAvailableFilters(validator.GetFilters());
|
||||
SetAvailableFiltersWithOption(validator.GetFiltersWithOption());
|
||||
SetAvailableHwaccels(validator.GetHwaccels());
|
||||
SetMediaEncoderVersion(validator);
|
||||
|
||||
_threads = EncodingHelper.GetNumberOfThreads(null, _configurationManager.GetEncodingOptions(), null);
|
||||
}
|
||||
|
||||
|
@ -235,6 +242,21 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|||
_hwaccels = list.ToList();
|
||||
}
|
||||
|
||||
public void SetAvailableFilters(IEnumerable<string> list)
|
||||
{
|
||||
_filters = list.ToList();
|
||||
}
|
||||
|
||||
public void SetAvailableFiltersWithOption(IDictionary<int, bool> dict)
|
||||
{
|
||||
_filtersWithOption = dict;
|
||||
}
|
||||
|
||||
public void SetMediaEncoderVersion(EncoderValidator validator)
|
||||
{
|
||||
_ffmpegVersion = validator.GetFFmpegVersion();
|
||||
}
|
||||
|
||||
public bool SupportsEncoder(string encoder)
|
||||
{
|
||||
return _encoders.Contains(encoder, StringComparer.OrdinalIgnoreCase);
|
||||
|
@ -250,17 +272,26 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|||
return _hwaccels.Contains(hwaccel, StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public bool SupportsFilter(string filter, string option)
|
||||
public bool SupportsFilter(string filter)
|
||||
{
|
||||
if (_ffmpegPath != null)
|
||||
return _filters.Contains(filter, StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public bool SupportsFilterWithOption(FilterOptionType option)
|
||||
{
|
||||
if (_filtersWithOption.TryGetValue((int)option, out var val))
|
||||
{
|
||||
var validator = new EncoderValidator(_logger, _ffmpegPath);
|
||||
return validator.CheckFilter(filter, option);
|
||||
return val;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public Version GetMediaEncoderVersion()
|
||||
{
|
||||
return _ffmpegVersion;
|
||||
}
|
||||
|
||||
public bool CanEncodeToAudioCodec(string codec)
|
||||
{
|
||||
if (string.Equals(codec, "opus", StringComparison.OrdinalIgnoreCase))
|
||||
|
|
|
@ -740,6 +740,23 @@ namespace MediaBrowser.MediaEncoding.Probing
|
|||
stream.BitDepth = streamInfo.BitsPerRawSample;
|
||||
}
|
||||
|
||||
if (!stream.BitDepth.HasValue)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(streamInfo.PixelFormat)
|
||||
&& streamInfo.PixelFormat.Contains("p10", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
stream.BitDepth = 10;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(streamInfo.Profile)
|
||||
&& (streamInfo.Profile.Contains("Main 10", StringComparison.OrdinalIgnoreCase)
|
||||
|| streamInfo.Profile.Contains("High 10", StringComparison.OrdinalIgnoreCase)
|
||||
|| streamInfo.Profile.Contains("Profile 2", StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
stream.BitDepth = 10;
|
||||
}
|
||||
}
|
||||
|
||||
// stream.IsAnamorphic = string.Equals(streamInfo.sample_aspect_ratio, "0:1", StringComparison.OrdinalIgnoreCase) ||
|
||||
// string.Equals(stream.AspectRatio, "2.35:1", StringComparison.OrdinalIgnoreCase) ||
|
||||
// string.Equals(stream.AspectRatio, "2.40:1", StringComparison.OrdinalIgnoreCase);
|
||||
|
|
|
@ -15,10 +15,12 @@ namespace Jellyfin.MediaEncoding.Tests
|
|||
[ClassData(typeof(GetFFmpegVersionTestData))]
|
||||
public void GetFFmpegVersionTest(string versionOutput, Version? version)
|
||||
{
|
||||
Assert.Equal(version, _encoderValidator.GetFFmpegVersion(versionOutput));
|
||||
Assert.Equal(version, _encoderValidator.GetFFmpegVersionInternal(versionOutput));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(EncoderValidatorTestsData.FFmpegV44Output, true)]
|
||||
[InlineData(EncoderValidatorTestsData.FFmpegV432Output, true)]
|
||||
[InlineData(EncoderValidatorTestsData.FFmpegV431Output, true)]
|
||||
[InlineData(EncoderValidatorTestsData.FFmpegV43Output, true)]
|
||||
[InlineData(EncoderValidatorTestsData.FFmpegV421Output, true)]
|
||||
|
@ -36,6 +38,8 @@ namespace Jellyfin.MediaEncoding.Tests
|
|||
{
|
||||
public IEnumerator<object?[]> GetEnumerator()
|
||||
{
|
||||
yield return new object?[] { EncoderValidatorTestsData.FFmpegV44Output, new Version(4, 4) };
|
||||
yield return new object?[] { EncoderValidatorTestsData.FFmpegV432Output, new Version(4, 3, 2) };
|
||||
yield return new object?[] { EncoderValidatorTestsData.FFmpegV431Output, new Version(4, 3, 1) };
|
||||
yield return new object?[] { EncoderValidatorTestsData.FFmpegV43Output, new Version(4, 3) };
|
||||
yield return new object?[] { EncoderValidatorTestsData.FFmpegV421Output, new Version(4, 2, 1) };
|
||||
|
|
|
@ -2,6 +2,30 @@ namespace Jellyfin.MediaEncoding.Tests
|
|||
{
|
||||
internal static class EncoderValidatorTestsData
|
||||
{
|
||||
public const string FFmpegV44Output = @"ffmpeg version 4.4-Jellyfin Copyright (c) 2000-2021 the FFmpeg developers
|
||||
built with gcc 10.3.0 (Rev5, Built by MSYS2 project)
|
||||
configuration: --disable-static --enable-shared --extra-version=Jellyfin --disable-ffplay --disable-debug --enable-gpl --enable-version3 --enable-bzlib --enable-iconv --enable-lzma --enable-zlib --enable-sdl2 --enable-fontconfig --enable-gmp --enable-libass --enable-libzimg --enable-libbluray --enable-libfreetype --enable-libmp3lame --enable-libopus --enable-libtheora --enable-libvorbis --enable-libwebp --enable-libvpx --enable-libx264 --enable-libx265 --enable-libdav1d --enable-opencl --enable-dxva2 --enable-d3d11va --enable-amf --enable-libmfx --enable-cuda --enable-cuda-llvm --enable-cuvid --enable-nvenc --enable-nvdec --enable-ffnvcodec --enable-gnutls
|
||||
libavutil 56. 70.100 / 56. 70.100
|
||||
libavcodec 58.134.100 / 58.134.100
|
||||
libavformat 58. 76.100 / 58. 76.100
|
||||
libavdevice 58. 13.100 / 58. 13.100
|
||||
libavfilter 7.110.100 / 7.110.100
|
||||
libswscale 5. 9.100 / 5. 9.100
|
||||
libswresample 3. 9.100 / 3. 9.100
|
||||
libpostproc 55. 9.100 / 55. 9.100";
|
||||
|
||||
public const string FFmpegV432Output = @"ffmpeg version n4.3.2-Jellyfin Copyright (c) 2000-2021 the FFmpeg developers
|
||||
built with gcc 10.2.0 (Rev9, Built by MSYS2 project)
|
||||
configuration: --disable-static --enable-shared --cc='ccache gcc' --cxx='ccache g++' --extra-version=Jellyfin --disable-ffplay --disable-debug --enable-lto --enable-gpl --enable-version3 --enable-bzlib --enable-iconv --enable-lzma --enable-zlib --enable-sdl2 --enable-fontconfig --enable-gmp --enable-libass --enable-libzimg --enable-libbluray --enable-libfreetype --enable-libmp3lame --enable-libopus --enable-libtheora --enable-libvorbis --enable-libwebp --enable-libvpx --enable-libx264 --enable-libx265 --enable-libdav1d --enable-opencl --enable-dxva2 --enable-d3d11va --enable-amf --enable-libmfx --enable-cuda --enable-cuda-llvm --enable-cuvid --enable-nvenc --enable-nvdec --enable-ffnvcodec --enable-gnutls
|
||||
libavutil 56. 51.100 / 56. 51.100
|
||||
libavcodec 58. 91.100 / 58. 91.100
|
||||
libavformat 58. 45.100 / 58. 45.100
|
||||
libavdevice 58. 10.100 / 58. 10.100
|
||||
libavfilter 7. 85.100 / 7. 85.100
|
||||
libswscale 5. 7.100 / 5. 7.100
|
||||
libswresample 3. 7.100 / 3. 7.100
|
||||
libpostproc 55. 7.100 / 55. 7.100";
|
||||
|
||||
public const string FFmpegV431Output = @"ffmpeg version n4.3.1 Copyright (c) 2000-2020 the FFmpeg developers
|
||||
built with gcc 10.1.0 (GCC)
|
||||
configuration: --prefix=/usr --disable-debug --disable-static --disable-stripping --enable-avisynth --enable-fontconfig --enable-gmp --enable-gnutls --enable-gpl --enable-ladspa --enable-libaom --enable-libass --enable-libbluray --enable-libdav1d --enable-libdrm --enable-libfreetype --enable-libfribidi --enable-libgsm --enable-libiec61883 --enable-libjack --enable-libmfx --enable-libmodplug --enable-libmp3lame --enable-libopencore_amrnb --enable-libopencore_amrwb --enable-libopenjpeg --enable-libopus --enable-libpulse --enable-librav1e --enable-libsoxr --enable-libspeex --enable-libsrt --enable-libssh --enable-libtheora --enable-libv4l2 --enable-libvidstab --enable-libvmaf --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxcb --enable-libxml2 --enable-libxvid --enable-nvdec --enable-nvenc --enable-omx --enable-shared --enable-version3
|
||||
|
|
Loading…
Reference in New Issue
Block a user