Merge pull request #2809 from nyanmisaka/hwaccel

Add more separate hardware decoding toggles, support videotoolbox
pull/3341/head
Anthony Lavado 4 years ago committed by GitHub
commit 25f8e596cb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -74,7 +74,8 @@ namespace MediaBrowser.Controller.MediaEncoding
{"omx", hwEncoder + "_omx"}, {"omx", hwEncoder + "_omx"},
{hwEncoder + "_v4l2m2m", hwEncoder + "_v4l2m2m"}, {hwEncoder + "_v4l2m2m", hwEncoder + "_v4l2m2m"},
{"mediacodec", hwEncoder + "_mediacodec"}, {"mediacodec", hwEncoder + "_mediacodec"},
{"vaapi", hwEncoder + "_vaapi"} {"vaapi", hwEncoder + "_vaapi"},
{"videotoolbox", hwEncoder + "_videotoolbox"}
}; };
if (!string.IsNullOrEmpty(hwType) if (!string.IsNullOrEmpty(hwType)
@ -104,7 +105,8 @@ namespace MediaBrowser.Controller.MediaEncoding
return false; return false;
} }
return true; return _mediaEncoder.SupportsHwaccel("vaapi");
} }
/// <summary> /// <summary>
@ -445,31 +447,41 @@ namespace MediaBrowser.Controller.MediaEncoding
public string GetInputArgument(EncodingJobInfo state, EncodingOptions encodingOptions) public string GetInputArgument(EncodingJobInfo state, EncodingOptions encodingOptions)
{ {
var arg = new StringBuilder(); var arg = new StringBuilder();
var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, encodingOptions) ?? string.Empty;
var outputVideoCodec = GetVideoEncoder(state, encodingOptions) ?? string.Empty;
bool isVaapiDecoder = videoDecoder.IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1;
bool isVaapiEncoder = outputVideoCodec.IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1;
bool isQsvDecoder = videoDecoder.IndexOf("qsv", StringComparison.OrdinalIgnoreCase) != -1;
bool isQsvEncoder = outputVideoCodec.IndexOf("qsv", StringComparison.OrdinalIgnoreCase) != -1;
if (state.IsVideoRequest if (state.IsVideoRequest
&& string.Equals(encodingOptions.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase)) && string.Equals(encodingOptions.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase))
{ {
arg.Append("-hwaccel vaapi -hwaccel_output_format vaapi") if (isVaapiDecoder)
.Append(" -vaapi_device ") {
.Append(encodingOptions.VaapiDevice) arg.Append("-hwaccel_output_format vaapi ")
.Append(' '); .Append("-vaapi_device ")
.Append(encodingOptions.VaapiDevice)
.Append(" ");
}
else if (!isVaapiDecoder && isVaapiEncoder)
{
arg.Append("-vaapi_device ")
.Append(encodingOptions.VaapiDevice)
.Append(" ");
}
} }
if (state.IsVideoRequest if (state.IsVideoRequest
&& string.Equals(encodingOptions.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase)) && string.Equals(encodingOptions.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase))
{ {
var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, encodingOptions);
var outputVideoCodec = GetVideoEncoder(state, encodingOptions);
var hasTextSubs = state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode; var hasTextSubs = state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
if (!hasTextSubs) if (!hasTextSubs)
{ {
// While using QSV encoder if (isQsvEncoder)
if ((outputVideoCodec ?? string.Empty).IndexOf("qsv", StringComparison.OrdinalIgnoreCase) != -1)
{ {
// While using QSV decoder if (isQsvDecoder)
if ((videoDecoder ?? string.Empty).IndexOf("qsv", StringComparison.OrdinalIgnoreCase) != -1)
{ {
arg.Append("-hwaccel qsv "); arg.Append("-hwaccel qsv ");
} }
@ -527,6 +539,8 @@ namespace MediaBrowser.Controller.MediaEncoding
|| codec.IndexOf("hevc", StringComparison.OrdinalIgnoreCase) != -1; || codec.IndexOf("hevc", StringComparison.OrdinalIgnoreCase) != -1;
} }
// TODO This is auto inserted into the mpegts mux so it might not be needed
// https://www.ffmpeg.org/ffmpeg-bitstream-filters.html#h264_005fmp4toannexb
public string GetBitStreamArgs(MediaStream stream) public string GetBitStreamArgs(MediaStream stream)
{ {
if (IsH264(stream)) if (IsH264(stream))
@ -551,8 +565,8 @@ namespace MediaBrowser.Controller.MediaEncoding
{ {
if (string.Equals(videoCodec, "libvpx", StringComparison.OrdinalIgnoreCase)) if (string.Equals(videoCodec, "libvpx", StringComparison.OrdinalIgnoreCase))
{ {
// With vpx when crf is used, b:v becomes a max rate // When crf is used with vpx, b:v becomes a max rate
// https://trac.ffmpeg.org/wiki/vpxEncodingGuide. // https://trac.ffmpeg.org/wiki/Encode/VP9
return string.Format( return string.Format(
CultureInfo.InvariantCulture, CultureInfo.InvariantCulture,
" -maxrate:v {0} -bufsize:v {1} -b:v {0}", " -maxrate:v {0} -bufsize:v {1} -b:v {0}",
@ -1525,8 +1539,9 @@ namespace MediaBrowser.Controller.MediaEncoding
EncodingOptions options, EncodingOptions options,
string outputVideoCodec) string outputVideoCodec)
{ {
var outputSizeParam = string.Empty; outputVideoCodec ??= string.Empty;
var outputSizeParam = string.Empty;
var request = state.BaseRequest; var request = state.BaseRequest;
// Add resolution params, if specified // Add resolution params, if specified
@ -1569,16 +1584,14 @@ namespace MediaBrowser.Controller.MediaEncoding
} }
var videoSizeParam = string.Empty; var videoSizeParam = string.Empty;
var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, options); var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, options) ?? string.Empty;
// Setup subtitle scaling // Setup subtitle scaling
if (state.VideoStream != null && state.VideoStream.Width.HasValue && state.VideoStream.Height.HasValue) if (state.VideoStream != null && state.VideoStream.Width.HasValue && state.VideoStream.Height.HasValue)
{ {
// force_original_aspect_ratio=decrease
// Enable decreasing output video width or height if necessary to keep the original aspect ratio
videoSizeParam = string.Format( videoSizeParam = string.Format(
CultureInfo.InvariantCulture, CultureInfo.InvariantCulture,
"scale={0}:{1}:force_original_aspect_ratio=decrease", "scale={0}:{1}",
state.VideoStream.Width.Value, state.VideoStream.Width.Value,
state.VideoStream.Height.Value); state.VideoStream.Height.Value);
@ -1591,8 +1604,10 @@ namespace MediaBrowser.Controller.MediaEncoding
// For VAAPI and CUVID decoder // For VAAPI and CUVID decoder
// these encoders cannot automatically adjust the size of graphical subtitles to fit the output video, // these encoders cannot automatically adjust the size of graphical subtitles to fit the output video,
// thus needs to be manually adjusted. // thus needs to be manually adjusted.
if ((IsVaapiSupported(state) && string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase)) if (videoDecoder.IndexOf("cuvid", StringComparison.OrdinalIgnoreCase) != -1
|| (videoDecoder ?? string.Empty).IndexOf("cuvid", StringComparison.OrdinalIgnoreCase) != -1) || (IsVaapiSupported(state) && string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase)
&& (videoDecoder.IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1
|| outputVideoCodec.IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1)))
{ {
var videoStream = state.VideoStream; var videoStream = state.VideoStream;
var inputWidth = videoStream?.Width; var inputWidth = videoStream?.Width;
@ -1603,7 +1618,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{ {
videoSizeParam = string.Format( videoSizeParam = string.Format(
CultureInfo.InvariantCulture, CultureInfo.InvariantCulture,
"scale={0}:{1}:force_original_aspect_ratio=decrease", "scale={0}:{1}",
width.Value, width.Value,
height.Value); height.Value);
} }
@ -1634,7 +1649,7 @@ namespace MediaBrowser.Controller.MediaEncoding
} }
// If we're hardware VAAPI decoding and software encoding, download frames from the decoder first // If we're hardware VAAPI decoding and software encoding, download frames from the decoder first
else if (IsVaapiSupported(state) && string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase) else if (IsVaapiSupported(state) && videoDecoder.IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1
&& string.Equals(outputVideoCodec, "libx264", StringComparison.OrdinalIgnoreCase)) && string.Equals(outputVideoCodec, "libx264", StringComparison.OrdinalIgnoreCase))
{ {
/* /*
@ -1652,7 +1667,7 @@ namespace MediaBrowser.Controller.MediaEncoding
For software decoding and hardware encoding option, frames must be hwuploaded into hardware For software decoding and hardware encoding option, frames must be hwuploaded into hardware
with fixed frame size. with fixed frame size.
*/ */
if (!string.IsNullOrEmpty(videoDecoder) && videoDecoder.Contains("qsv", StringComparison.OrdinalIgnoreCase)) if (videoDecoder.IndexOf("qsv", StringComparison.OrdinalIgnoreCase) != -1)
{ {
retStr = " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay_qsv=x=(W-w)/2:y=(H-h)/2{3}\""; retStr = " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay_qsv=x=(W-w)/2:y=(H-h)/2{3}\"";
} }
@ -1975,7 +1990,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var videoStream = state.VideoStream; var videoStream = state.VideoStream;
var filters = new List<string>(); var filters = new List<string>();
var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, options); var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, options) ?? string.Empty;
var inputWidth = videoStream?.Width; var inputWidth = videoStream?.Width;
var inputHeight = videoStream?.Height; var inputHeight = videoStream?.Height;
var threeDFormat = state.MediaSource.Video3DFormat; var threeDFormat = state.MediaSource.Video3DFormat;
@ -2000,7 +2015,7 @@ namespace MediaBrowser.Controller.MediaEncoding
} }
// If we're hardware VAAPI decoding and software encoding, download frames from the decoder first // If we're hardware VAAPI decoding and software encoding, download frames from the decoder first
else if (IsVaapiSupported(state) && string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase) else if (videoDecoder.IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1
&& string.Equals(outputVideoCodec, "libx264", StringComparison.OrdinalIgnoreCase)) && string.Equals(outputVideoCodec, "libx264", StringComparison.OrdinalIgnoreCase))
{ {
var codec = videoStream.Codec.ToLowerInvariant(); var codec = videoStream.Codec.ToLowerInvariant();
@ -2509,16 +2524,17 @@ namespace MediaBrowser.Controller.MediaEncoding
/// </summary> /// </summary>
protected string GetHardwareAcceleratedVideoDecoder(EncodingJobInfo state, EncodingOptions encodingOptions) protected string GetHardwareAcceleratedVideoDecoder(EncodingJobInfo state, EncodingOptions encodingOptions)
{ {
var videoType = state.MediaSource.VideoType ?? VideoType.VideoFile;
var videoStream = state.VideoStream;
var isColorDepth10 = !string.IsNullOrEmpty(videoStream.Profile) && (videoStream.Profile.Contains("Main 10", StringComparison.OrdinalIgnoreCase)
|| videoStream.Profile.Contains("High 10", StringComparison.OrdinalIgnoreCase));
if (EncodingHelper.IsCopyCodec(state.OutputVideoCodec)) if (EncodingHelper.IsCopyCodec(state.OutputVideoCodec))
{ {
return null; return null;
} }
return GetHardwareAcceleratedVideoDecoder(state.MediaSource.VideoType ?? VideoType.VideoFile, state.VideoStream, encodingOptions);
}
public string GetHardwareAcceleratedVideoDecoder(VideoType videoType, MediaStream videoStream, EncodingOptions encodingOptions)
{
// Only use alternative encoders for video files. // Only use alternative encoders for video files.
// When using concat with folder rips, if the mfx session fails to initialize, ffmpeg will be stuck retrying and will not exit gracefully // When using concat with folder rips, if the mfx session fails to initialize, ffmpeg will be stuck retrying and will not exit gracefully
// Since transcoding of folder rips is expiremental anyway, it's not worth adding additional variables such as this. // Since transcoding of folder rips is expiremental anyway, it's not worth adding additional variables such as this.
@ -2531,6 +2547,14 @@ namespace MediaBrowser.Controller.MediaEncoding
&& !string.IsNullOrEmpty(videoStream.Codec) && !string.IsNullOrEmpty(videoStream.Codec)
&& !string.IsNullOrEmpty(encodingOptions.HardwareAccelerationType)) && !string.IsNullOrEmpty(encodingOptions.HardwareAccelerationType))
{ {
// Only hevc and vp9 formats have 10-bit hardware decoder support now.
if (isColorDepth10 && !(string.Equals(videoStream.Codec, "hevc", StringComparison.OrdinalIgnoreCase)
|| string.Equals(videoStream.Codec, "h265", StringComparison.OrdinalIgnoreCase)
|| string.Equals(videoStream.Codec, "vp9", StringComparison.OrdinalIgnoreCase)))
{
return null;
}
if (string.Equals(encodingOptions.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase)) if (string.Equals(encodingOptions.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase))
{ {
switch (videoStream.Codec.ToLowerInvariant()) switch (videoStream.Codec.ToLowerInvariant())
@ -2552,8 +2576,8 @@ namespace MediaBrowser.Controller.MediaEncoding
case "h265": case "h265":
if (_mediaEncoder.SupportsDecoder("hevc_qsv") && encodingOptions.HardwareDecodingCodecs.Contains("hevc", StringComparer.OrdinalIgnoreCase)) if (_mediaEncoder.SupportsDecoder("hevc_qsv") && encodingOptions.HardwareDecodingCodecs.Contains("hevc", StringComparer.OrdinalIgnoreCase))
{ {
// return "-c:v hevc_qsv -load_plugin hevc_hw "; return (isColorDepth10 &&
return "-c:v hevc_qsv"; !encodingOptions.EnableDecodingColorDepth10Hevc) ? null : "-c:v hevc_qsv";
} }
break; break;
case "mpeg2video": case "mpeg2video":
@ -2568,6 +2592,19 @@ namespace MediaBrowser.Controller.MediaEncoding
return "-c:v vc1_qsv"; return "-c:v vc1_qsv";
} }
break; break;
case "vp8":
if (_mediaEncoder.SupportsDecoder("vp8_qsv") && encodingOptions.HardwareDecodingCodecs.Contains("vp8", StringComparer.OrdinalIgnoreCase))
{
return "-c:v vp8_qsv";
}
break;
case "vp9":
if (_mediaEncoder.SupportsDecoder("vp9_qsv") && encodingOptions.HardwareDecodingCodecs.Contains("vp9", StringComparer.OrdinalIgnoreCase))
{
return (isColorDepth10 &&
!encodingOptions.EnableDecodingColorDepth10Vp9) ? null : "-c:v vp9_qsv";
}
break;
} }
} }
else if (string.Equals(encodingOptions.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase)) else if (string.Equals(encodingOptions.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase))
@ -2578,12 +2615,6 @@ namespace MediaBrowser.Controller.MediaEncoding
case "h264": case "h264":
if (_mediaEncoder.SupportsDecoder("h264_cuvid") && encodingOptions.HardwareDecodingCodecs.Contains("h264", StringComparer.OrdinalIgnoreCase)) if (_mediaEncoder.SupportsDecoder("h264_cuvid") && encodingOptions.HardwareDecodingCodecs.Contains("h264", StringComparer.OrdinalIgnoreCase))
{ {
// cuvid decoder does not support 10-bit input
if ((videoStream.BitDepth ?? 8) > 8)
{
encodingOptions.HardwareDecodingCodecs = Array.Empty<string>();
return null;
}
return "-c:v h264_cuvid"; return "-c:v h264_cuvid";
} }
break; break;
@ -2591,7 +2622,8 @@ namespace MediaBrowser.Controller.MediaEncoding
case "h265": case "h265":
if (_mediaEncoder.SupportsDecoder("hevc_cuvid") && encodingOptions.HardwareDecodingCodecs.Contains("hevc", StringComparer.OrdinalIgnoreCase)) if (_mediaEncoder.SupportsDecoder("hevc_cuvid") && encodingOptions.HardwareDecodingCodecs.Contains("hevc", StringComparer.OrdinalIgnoreCase))
{ {
return "-c:v hevc_cuvid"; return (isColorDepth10 &&
!encodingOptions.EnableDecodingColorDepth10Hevc) ? null : "-c:v hevc_cuvid";
} }
break; break;
case "mpeg2video": case "mpeg2video":
@ -2612,6 +2644,19 @@ namespace MediaBrowser.Controller.MediaEncoding
return "-c:v mpeg4_cuvid"; return "-c:v mpeg4_cuvid";
} }
break; break;
case "vp8":
if (_mediaEncoder.SupportsDecoder("vp8_cuvid") && encodingOptions.HardwareDecodingCodecs.Contains("vp8", StringComparer.OrdinalIgnoreCase))
{
return "-c:v vp8_cuvid";
}
break;
case "vp9":
if (_mediaEncoder.SupportsDecoder("vp9_cuvid") && encodingOptions.HardwareDecodingCodecs.Contains("vp9", StringComparer.OrdinalIgnoreCase))
{
return (isColorDepth10 &&
!encodingOptions.EnableDecodingColorDepth10Vp9) ? null : "-c:v vp9_cuvid";
}
break;
} }
} }
else if (string.Equals(encodingOptions.HardwareAccelerationType, "mediacodec", StringComparison.OrdinalIgnoreCase)) else if (string.Equals(encodingOptions.HardwareAccelerationType, "mediacodec", StringComparison.OrdinalIgnoreCase))
@ -2629,7 +2674,8 @@ namespace MediaBrowser.Controller.MediaEncoding
case "h265": case "h265":
if (_mediaEncoder.SupportsDecoder("hevc_mediacodec") && encodingOptions.HardwareDecodingCodecs.Contains("hevc", StringComparer.OrdinalIgnoreCase)) if (_mediaEncoder.SupportsDecoder("hevc_mediacodec") && encodingOptions.HardwareDecodingCodecs.Contains("hevc", StringComparer.OrdinalIgnoreCase))
{ {
return "-c:v hevc_mediacodec"; return (isColorDepth10 &&
!encodingOptions.EnableDecodingColorDepth10Hevc) ? null : "-c:v hevc_mediacodec";
} }
break; break;
case "mpeg2video": case "mpeg2video":
@ -2653,7 +2699,8 @@ namespace MediaBrowser.Controller.MediaEncoding
case "vp9": case "vp9":
if (_mediaEncoder.SupportsDecoder("vp9_mediacodec") && encodingOptions.HardwareDecodingCodecs.Contains("vp9", StringComparer.OrdinalIgnoreCase)) if (_mediaEncoder.SupportsDecoder("vp9_mediacodec") && encodingOptions.HardwareDecodingCodecs.Contains("vp9", StringComparer.OrdinalIgnoreCase))
{ {
return "-c:v vp9_mediacodec"; return (isColorDepth10 &&
!encodingOptions.EnableDecodingColorDepth10Vp9) ? null : "-c:v vp9_mediacodec";
} }
break; break;
} }
@ -2691,27 +2738,148 @@ namespace MediaBrowser.Controller.MediaEncoding
} }
else if (string.Equals(encodingOptions.HardwareAccelerationType, "amf", StringComparison.OrdinalIgnoreCase)) else if (string.Equals(encodingOptions.HardwareAccelerationType, "amf", StringComparison.OrdinalIgnoreCase))
{ {
if (Environment.OSVersion.Platform == PlatformID.Win32NT) switch (videoStream.Codec.ToLowerInvariant())
{ {
if (Environment.OSVersion.Version.Major > 6 || (Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Minor > 1)) case "avc":
return "-hwaccel d3d11va"; case "h264":
else return GetHwaccelType(state, encodingOptions, "h264");
return "-hwaccel dxva2"; case "hevc":
case "h265":
return (isColorDepth10 &&
!encodingOptions.EnableDecodingColorDepth10Hevc) ? null : GetHwaccelType(state, encodingOptions, "hevc");
case "mpeg2video":
return GetHwaccelType(state, encodingOptions, "mpeg2video");
case "vc1":
return GetHwaccelType(state, encodingOptions, "vc1");
case "mpeg4":
return GetHwaccelType(state, encodingOptions, "mpeg4");
case "vp9":
return (isColorDepth10 &&
!encodingOptions.EnableDecodingColorDepth10Vp9) ? null : GetHwaccelType(state, encodingOptions, "vp9");
} }
else }
else if (string.Equals(encodingOptions.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase))
{
switch (videoStream.Codec.ToLowerInvariant())
{
case "avc":
case "h264":
return GetHwaccelType(state, encodingOptions, "h264");
case "hevc":
case "h265":
return (isColorDepth10 &&
!encodingOptions.EnableDecodingColorDepth10Hevc) ? null : GetHwaccelType(state, encodingOptions, "hevc");
case "mpeg2video":
return GetHwaccelType(state, encodingOptions, "mpeg2video");
case "vc1":
return GetHwaccelType(state, encodingOptions, "vc1");
case "vp8":
return GetHwaccelType(state, encodingOptions, "vp8");
case "vp9":
return (isColorDepth10 &&
!encodingOptions.EnableDecodingColorDepth10Vp9) ? null : GetHwaccelType(state, encodingOptions, "vp9");
}
}
else if (string.Equals(encodingOptions.HardwareAccelerationType, "videotoolbox", StringComparison.OrdinalIgnoreCase))
{
switch (videoStream.Codec.ToLowerInvariant())
{ {
return "-hwaccel vaapi"; case "avc":
case "h264":
if (_mediaEncoder.SupportsDecoder("h264_opencl") && encodingOptions.HardwareDecodingCodecs.Contains("h264", StringComparer.OrdinalIgnoreCase))
{
return "-c:v h264_opencl";
}
break;
case "hevc":
case "h265":
if (_mediaEncoder.SupportsDecoder("hevc_opencl") && encodingOptions.HardwareDecodingCodecs.Contains("h264", StringComparer.OrdinalIgnoreCase))
{
return (isColorDepth10 &&
!encodingOptions.EnableDecodingColorDepth10Hevc) ? null : "-c:v hevc_opencl";
}
break;
case "mpeg2video":
if (_mediaEncoder.SupportsDecoder("mpeg2_opencl") && encodingOptions.HardwareDecodingCodecs.Contains("mpeg2video", StringComparer.OrdinalIgnoreCase))
{
return "-c:v mpeg2_opencl";
}
break;
case "mpeg4":
if (_mediaEncoder.SupportsDecoder("mpeg4_opencl") && encodingOptions.HardwareDecodingCodecs.Contains("mpeg4", StringComparer.OrdinalIgnoreCase))
{
return "-c:v mpeg4_opencl";
}
break;
case "vc1":
if (_mediaEncoder.SupportsDecoder("vc1_opencl") && encodingOptions.HardwareDecodingCodecs.Contains("vc1", StringComparer.OrdinalIgnoreCase))
{
return "-c:v vc1_opencl";
}
break;
case "vp8":
if (_mediaEncoder.SupportsDecoder("vp8_opencl") && encodingOptions.HardwareDecodingCodecs.Contains("vc1", StringComparer.OrdinalIgnoreCase))
{
return "-c:v vp8_opencl";
}
break;
case "vp9":
if (_mediaEncoder.SupportsDecoder("vp9_opencl") && encodingOptions.HardwareDecodingCodecs.Contains("vc1", StringComparer.OrdinalIgnoreCase))
{
return (isColorDepth10 &&
!encodingOptions.EnableDecodingColorDepth10Vp9) ? null : "-c:v vp9_opencl";
}
break;
} }
} }
} }
var whichCodec = videoStream.Codec.ToLowerInvariant();
switch (whichCodec)
{
case "avc":
whichCodec = "h264";
break;
case "h265":
whichCodec = "hevc";
break;
}
// Avoid a second attempt if no hardware acceleration is being used // Avoid a second attempt if no hardware acceleration is being used
encodingOptions.HardwareDecodingCodecs = Array.Empty<string>(); encodingOptions.HardwareDecodingCodecs = encodingOptions.HardwareDecodingCodecs.Where(val => val != whichCodec).ToArray();
// leave blank so ffmpeg will decide // leave blank so ffmpeg will decide
return null; return null;
} }
/// <summary>
/// Gets a hwaccel type to use as a hardware decoder(dxva/vaapi) depending on the system
/// </summary>
public string GetHwaccelType(EncodingJobInfo state, EncodingOptions options, string videoCodec)
{
var isWindows = Environment.OSVersion.Platform == PlatformID.Win32NT;
var isWindows8orLater = Environment.OSVersion.Version.Major > 6 || (Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Minor > 1);
var isDxvaSupported = _mediaEncoder.SupportsHwaccel("dxva2") || _mediaEncoder.SupportsHwaccel("d3d11va");
if ((isDxvaSupported || IsVaapiSupported(state)) && options.HardwareDecodingCodecs.Contains(videoCodec, StringComparer.OrdinalIgnoreCase))
{
if (!isWindows)
{
return "-hwaccel vaapi";
}
else if (isWindows8orLater)
{
return "-hwaccel d3d11va";
}
else
{
return "-hwaccel dxva2";
}
}
return null;
}
public string GetSubtitleEmbedArguments(EncodingJobInfo state) public string GetSubtitleEmbedArguments(EncodingJobInfo state)
{ {
if (state.SubtitleStream == null || state.SubtitleDeliveryMethod != SubtitleDeliveryMethod.Embed) if (state.SubtitleStream == null || state.SubtitleDeliveryMethod != SubtitleDeliveryMethod.Embed)

@ -27,12 +27,26 @@ namespace MediaBrowser.Controller.MediaEncoding
string EncoderPath { get; } string EncoderPath { get; }
/// <summary> /// <summary>
/// Supportses the decoder. /// Whether given encoder codec is supported.
/// </summary>
/// <param name="encoder">The encoder.</param>
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
bool SupportsEncoder(string encoder);
/// <summary>
/// Whether given decoder codec is supported.
/// </summary> /// </summary>
/// <param name="decoder">The decoder.</param> /// <param name="decoder">The decoder.</param>
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns> /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
bool SupportsDecoder(string decoder); bool SupportsDecoder(string decoder);
/// <summary>
/// Whether given hardware acceleration type is supported.
/// </summary>
/// <param name="hwaccel">The hwaccel.</param>
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
bool SupportsHwaccel(string hwaccel);
/// <summary> /// <summary>
/// Extracts the audio image. /// Extracts the audio image.
/// </summary> /// </summary>
@ -98,7 +112,6 @@ namespace MediaBrowser.Controller.MediaEncoding
void SetFFmpegPath(); void SetFFmpegPath();
void UpdateEncoderPath(string path, string pathType); void UpdateEncoderPath(string path, string pathType);
bool SupportsEncoder(string encoder);
IEnumerable<string> GetPrimaryPlaylistVobFiles(string path, IIsoMount isoMount, uint? titleNumber); IEnumerable<string> GetPrimaryPlaylistVobFiles(string path, IIsoMount isoMount, uint? titleNumber);
} }

@ -14,23 +14,45 @@ namespace MediaBrowser.MediaEncoding.Encoder
private static readonly string[] requiredDecoders = new[] private static readonly string[] requiredDecoders = new[]
{ {
"h264",
"hevc",
"mpeg2video", "mpeg2video",
"mpeg4",
"msmpeg4",
"dts",
"ac3",
"aac",
"mp3",
"h264_qsv", "h264_qsv",
"hevc_qsv", "hevc_qsv",
"mpeg2_qsv", "mpeg2_qsv",
"mpeg2_mmal",
"mpeg4_mmal",
"vc1_qsv", "vc1_qsv",
"vc1_mmal", "vp8_qsv",
"vp9_qsv",
"h264_cuvid", "h264_cuvid",
"hevc_cuvid", "hevc_cuvid",
"dts", "mpeg2_cuvid",
"ac3", "vc1_cuvid",
"aac", "mpeg4_cuvid",
"mp3", "vp8_cuvid",
"h264", "vp9_cuvid",
"h264_mmal", "h264_mmal",
"hevc" "mpeg2_mmal",
"mpeg4_mmal",
"vc1_mmal",
"h264_mediacodec",
"hevc_mediacodec",
"mpeg2_mediacodec",
"mpeg4_mediacodec",
"vp8_mediacodec",
"vp9_mediacodec",
"h264_opencl",
"hevc_opencl",
"mpeg2_opencl",
"mpeg4_opencl",
"vp8_opencl",
"vp9_opencl",
"vc1_opencl"
}; };
private static readonly string[] requiredEncoders = new[] private static readonly string[] requiredEncoders = new[]
@ -43,22 +65,24 @@ namespace MediaBrowser.MediaEncoding.Encoder
"libvpx-vp9", "libvpx-vp9",
"aac", "aac",
"libfdk_aac", "libfdk_aac",
"ac3",
"libmp3lame", "libmp3lame",
"libopus", "libopus",
"libvorbis", "libvorbis",
"srt", "srt",
"h264_nvenc", "h264_amf",
"hevc_nvenc", "hevc_amf",
"h264_qsv", "h264_qsv",
"hevc_qsv", "hevc_qsv",
"h264_omx", "h264_nvenc",
"hevc_omx", "hevc_nvenc",
"h264_vaapi", "h264_vaapi",
"hevc_vaapi", "hevc_vaapi",
"h264_omx",
"hevc_omx",
"h264_v4l2m2m", "h264_v4l2m2m",
"ac3", "h264_videotoolbox",
"h264_amf", "hevc_videotoolbox"
"hevc_amf"
}; };
// Try and use the individual library versions to determine a FFmpeg version // Try and use the individual library versions to determine a FFmpeg version
@ -159,6 +183,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
public IEnumerable<string> GetEncoders() => GetCodecs(Codec.Encoder); public IEnumerable<string> GetEncoders() => GetCodecs(Codec.Encoder);
public IEnumerable<string> GetHwaccels() => GetHwaccelTypes();
/// <summary> /// <summary>
/// Using the output from "ffmpeg -version" work out the FFmpeg version. /// 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 /// For pre-built binaries the first line should contain a string like "ffmpeg version x.y", which is easy
@ -218,6 +244,29 @@ namespace MediaBrowser.MediaEncoding.Encoder
Decoder Decoder
} }
private IEnumerable<string> GetHwaccelTypes()
{
string output = null;
try
{
output = GetProcessOutput(_encoderPath, "-hwaccels");
}
catch (Exception ex)
{
_logger.LogError(ex, "Error detecting available hwaccel types");
}
if (string.IsNullOrWhiteSpace(output))
{
return Enumerable.Empty<string>();
}
var found = output.Split(new char[] {'\r','\n'}, StringSplitOptions.RemoveEmptyEntries).Skip(1).Distinct().ToList();
_logger.LogInformation("Available hwaccel types: {Types}", found);
return found;
}
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";

@ -111,6 +111,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
SetAvailableDecoders(validator.GetDecoders()); SetAvailableDecoders(validator.GetDecoders());
SetAvailableEncoders(validator.GetEncoders()); SetAvailableEncoders(validator.GetEncoders());
SetAvailableHwaccels(validator.GetHwaccels());
} }
_logger.LogInformation("FFmpeg: {EncoderLocation}: {FfmpegPath}", EncoderLocation, _ffmpegPath ?? string.Empty); _logger.LogInformation("FFmpeg: {EncoderLocation}: {FfmpegPath}", EncoderLocation, _ffmpegPath ?? string.Empty);
@ -257,6 +258,13 @@ namespace MediaBrowser.MediaEncoding.Encoder
// _logger.Info("Supported decoders: {0}", string.Join(",", list.ToArray())); // _logger.Info("Supported decoders: {0}", string.Join(",", list.ToArray()));
} }
private List<string> _hwaccels = new List<string>();
public void SetAvailableHwaccels(IEnumerable<string> list)
{
_hwaccels = list.ToList();
//_logger.Info("Supported hwaccels: {0}", string.Join(",", list.ToArray()));
}
public bool SupportsEncoder(string encoder) public bool SupportsEncoder(string encoder)
{ {
return _encoders.Contains(encoder, StringComparer.OrdinalIgnoreCase); return _encoders.Contains(encoder, StringComparer.OrdinalIgnoreCase);
@ -267,6 +275,11 @@ namespace MediaBrowser.MediaEncoding.Encoder
return _decoders.Contains(decoder, StringComparer.OrdinalIgnoreCase); return _decoders.Contains(decoder, StringComparer.OrdinalIgnoreCase);
} }
public bool SupportsHwaccel(string hwaccel)
{
return _hwaccels.Contains(hwaccel, StringComparer.OrdinalIgnoreCase);
}
public bool CanEncodeToAudioCodec(string codec) public bool CanEncodeToAudioCodec(string codec)
{ {
if (string.Equals(codec, "opus", StringComparison.OrdinalIgnoreCase)) if (string.Equals(codec, "opus", StringComparison.OrdinalIgnoreCase))

@ -736,7 +736,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
var charset = CharsetDetector.DetectFromStream(stream).Detected?.EncodingName; var charset = CharsetDetector.DetectFromStream(stream).Detected?.EncodingName;
// UTF16 is automatically converted to UTF8 by FFmpeg, do not specify a character encoding // UTF16 is automatically converted to UTF8 by FFmpeg, do not specify a character encoding
if ((path.EndsWith(".ass") || path.EndsWith(".ssa")) if ((path.EndsWith(".ass") || path.EndsWith(".ssa") || path.EndsWith(".srt"))
&& (string.Equals(charset, "utf-16le", StringComparison.OrdinalIgnoreCase) && (string.Equals(charset, "utf-16le", StringComparison.OrdinalIgnoreCase)
|| string.Equals(charset, "utf-16be", StringComparison.OrdinalIgnoreCase))) || string.Equals(charset, "utf-16be", StringComparison.OrdinalIgnoreCase)))
{ {

@ -36,7 +36,8 @@ namespace MediaBrowser.Model.Configuration
public string EncoderPreset { get; set; } public string EncoderPreset { get; set; }
public string DeinterlaceMethod { get; set; } public string DeinterlaceMethod { get; set; }
public bool EnableDecodingColorDepth10Hevc { get; set; }
public bool EnableDecodingColorDepth10Vp9 { get; set; }
public bool EnableHardwareEncoding { get; set; } public bool EnableHardwareEncoding { get; set; }
public bool EnableSubtitleExtraction { get; set; } public bool EnableSubtitleExtraction { get; set; }
@ -54,6 +55,8 @@ namespace MediaBrowser.Model.Configuration
H264Crf = 23; H264Crf = 23;
H265Crf = 28; H265Crf = 28;
DeinterlaceMethod = "yadif"; DeinterlaceMethod = "yadif";
EnableDecodingColorDepth10Hevc = true;
EnableDecodingColorDepth10Vp9 = true;
EnableHardwareEncoding = true; EnableHardwareEncoding = true;
EnableSubtitleExtraction = true; EnableSubtitleExtraction = true;
HardwareDecodingCodecs = new string[] { "h264", "vc1" }; HardwareDecodingCodecs = new string[] { "h264", "vc1" };

Loading…
Cancel
Save