diff options
Diffstat (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs')
| -rw-r--r-- | MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 182 |
1 files changed, 24 insertions, 158 deletions
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 2c3d44bf8..12d15d9fd 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -64,6 +64,7 @@ namespace MediaBrowser.Controller.MediaEncoding private readonly Version _minFFmpegSvtAv1Params = new Version(5, 1); private readonly Version _minFFmpegVaapiH26xEncA53CcSei = new Version(6, 0); private readonly Version _minFFmpegReadrateOption = new Version(5, 0); + private readonly Version _minFFmpegWorkingVtHwSurface = new Version(7, 0, 1); private static readonly Regex _validationRegex = new(ValidationRegex, RegexOptions.Compiled); @@ -106,7 +107,6 @@ namespace MediaBrowser.Controller.MediaEncoding // AAC, FLAC, ALAC, libopus, libvorbis encoders all support at least 8 channels private static readonly Dictionary<string, int> _audioTranscodeChannelLookup = new(StringComparer.OrdinalIgnoreCase) { - { "wmav2", 2 }, { "libmp3lame", 2 }, { "libfdk_aac", 6 }, { "ac3", 6 }, @@ -399,27 +399,6 @@ namespace MediaBrowser.Controller.MediaEncoding return GetMjpegEncoder(state, encodingOptions); } - if (string.Equals(codec, "vp8", StringComparison.OrdinalIgnoreCase) - || string.Equals(codec, "vpx", StringComparison.OrdinalIgnoreCase)) - { - return "libvpx"; - } - - if (string.Equals(codec, "vp9", StringComparison.OrdinalIgnoreCase)) - { - return "libvpx-vp9"; - } - - if (string.Equals(codec, "wmv", StringComparison.OrdinalIgnoreCase)) - { - return "wmv2"; - } - - if (string.Equals(codec, "theora", StringComparison.OrdinalIgnoreCase)) - { - return "libtheora"; - } - if (_validationRegex.IsMatch(codec)) { return codec.ToLowerInvariant(); @@ -739,11 +718,6 @@ namespace MediaBrowser.Controller.MediaEncoding return "libvorbis"; } - if (string.Equals(codec, "wma", StringComparison.OrdinalIgnoreCase)) - { - return "wmav2"; - } - if (string.Equals(codec, "opus", StringComparison.OrdinalIgnoreCase)) { return "libopus"; @@ -1373,20 +1347,6 @@ namespace MediaBrowser.Controller.MediaEncoding // Currently use the same buffer size for all encoders int bufsize = bitrate * 2; - if (string.Equals(videoCodec, "libvpx", StringComparison.OrdinalIgnoreCase) - || string.Equals(videoCodec, "libvpx-vp9", StringComparison.OrdinalIgnoreCase)) - { - // When crf is used with vpx, b:v becomes a max rate - // https://trac.ffmpeg.org/wiki/Encode/VP8 - // https://trac.ffmpeg.org/wiki/Encode/VP9 - return FormattableString.Invariant($" -maxrate:v {bitrate} -bufsize:v {bufsize} -b:v {bitrate}"); - } - - if (string.Equals(videoCodec, "msmpeg4", StringComparison.OrdinalIgnoreCase)) - { - return FormattableString.Invariant($" -b:v {bitrate}"); - } - if (string.Equals(videoCodec, "libsvtav1", StringComparison.OrdinalIgnoreCase)) { return FormattableString.Invariant($" -b:v {bitrate} -bufsize {bufsize}"); @@ -1922,93 +1882,6 @@ namespace MediaBrowser.Controller.MediaEncoding break; } } - else if (string.Equals(videoEncoder, "libvpx", StringComparison.OrdinalIgnoreCase)) // vp8 - { - // Values 0-3, 0 being highest quality but slower - var profileScore = 0; - - var qmin = "0"; - var qmax = "50"; - var crf = "10"; - - if (isVc1) - { - profileScore++; - } - - // Max of 2 - profileScore = Math.Min(profileScore, 2); - - // http://www.webmproject.org/docs/encoder-parameters/ - param += string.Format( - CultureInfo.InvariantCulture, - " -speed 16 -quality good -profile:v {0} -slices 8 -crf {1} -qmin {2} -qmax {3}", - profileScore.ToString(CultureInfo.InvariantCulture), - crf, - qmin, - qmax); - } - else if (string.Equals(videoEncoder, "libvpx-vp9", StringComparison.OrdinalIgnoreCase)) // vp9 - { - // When `-deadline` is set to `good` or `best`, `-cpu-used` ranges from 0-5. - // When `-deadline` is set to `realtime`, `-cpu-used` ranges from 0-15. - // Resources: - // * https://trac.ffmpeg.org/wiki/Encode/VP9 - // * https://superuser.com/questions/1586934 - // * https://developers.google.com/media/vp9 - param += encodingOptions.EncoderPreset switch - { - "veryslow" => " -deadline best -cpu-used 0", - "slower" => " -deadline best -cpu-used 2", - "slow" => " -deadline best -cpu-used 3", - "medium" => " -deadline good -cpu-used 0", - "fast" => " -deadline good -cpu-used 1", - "faster" => " -deadline good -cpu-used 2", - "veryfast" => " -deadline good -cpu-used 3", - "superfast" => " -deadline good -cpu-used 4", - "ultrafast" => " -deadline good -cpu-used 5", - _ => " -deadline good -cpu-used 1" - }; - - // TODO: until VP9 gets its own CRF setting, base CRF on H.265. - int h265Crf = encodingOptions.H265Crf; - int defaultVp9Crf = 31; - if (h265Crf >= 0 && h265Crf <= 51) - { - // This conversion factor is chosen to match the default CRF for H.265 to the - // recommended 1080p CRF from Google. The factor also maps the logarithmic CRF - // scale of x265 [0, 51] to that of VP9 [0, 63] relatively well. - - // Resources: - // * https://developers.google.com/media/vp9/settings/vod - const float H265ToVp9CrfConversionFactor = 1.12F; - - var vp9Crf = Convert.ToInt32(h265Crf * H265ToVp9CrfConversionFactor); - - // Encoder allows for CRF values in the range [0, 63]. - vp9Crf = Math.Clamp(vp9Crf, 0, 63); - - param += FormattableString.Invariant($" -crf {vp9Crf}"); - } - else - { - param += FormattableString.Invariant($" -crf {defaultVp9Crf}"); - } - - param += " -row-mt 1 -profile 1"; - } - else if (string.Equals(videoEncoder, "mpeg4", StringComparison.OrdinalIgnoreCase)) - { - param += " -mbd rd -flags +mv4+aic -trellis 2 -cmp 2 -subcmp 2 -bf 2"; - } - else if (string.Equals(videoEncoder, "wmv2", StringComparison.OrdinalIgnoreCase)) // asf/wmv - { - param += " -qmin 2"; - } - else if (string.Equals(videoEncoder, "msmpeg4", StringComparison.OrdinalIgnoreCase)) - { - param += " -mbd 2"; - } param += GetVideoBitrateParam(state, videoEncoder); @@ -5166,12 +5039,14 @@ namespace MediaBrowser.Controller.MediaEncoding var threeDFormat = state.MediaSource.Video3DFormat; var isVtEncoder = vidEncoder.Contains("videotoolbox", StringComparison.OrdinalIgnoreCase); + var isVtDecoder = vidDecoder.Contains("videotoolbox", StringComparison.OrdinalIgnoreCase); var doDeintH264 = state.DeInterlace("h264", true) || state.DeInterlace("avc", true); var doDeintHevc = state.DeInterlace("h265", true) || state.DeInterlace("hevc", true); var doDeintH2645 = doDeintH264 || doDeintHevc; var doVtTonemap = IsVideoToolboxTonemapAvailable(state, options); var doMetalTonemap = !doVtTonemap && IsHwTonemapAvailable(state, options); + var usingHwSurface = isVtDecoder && (_mediaEncoder.EncoderVersion >= _minFFmpegWorkingVtHwSurface); var scaleFormat = string.Empty; // Use P010 for Metal tone mapping, otherwise force an 8bit output. @@ -5259,23 +5134,25 @@ namespace MediaBrowser.Controller.MediaEncoding subFilters.Add(subTextSubtitlesFilter); } - subFilters.Add("hwupload=derive_device=videotoolbox"); + subFilters.Add("hwupload"); overlayFilters.Add("overlay_videotoolbox=eof_action=pass:repeatlast=0"); } + if (usingHwSurface) + { + return (mainFilters, subFilters, overlayFilters); + } + + // For old jellyfin-ffmpeg that has broken hwsurface, add a hwupload var needFiltering = mainFilters.Any(f => !string.IsNullOrEmpty(f)) || subFilters.Any(f => !string.IsNullOrEmpty(f)) || overlayFilters.Any(f => !string.IsNullOrEmpty(f)); - - // This is a workaround for ffmpeg's hwupload implementation - // For VideoToolbox encoders, a hwupload without a valid filter actually consuming its frame - // will cause the encoder to produce incorrect frames. if (needFiltering) { // INPUT videotoolbox/memory surface(vram/uma) // this will pass-through automatically if in/out format matches. + mainFilters.Insert(0, "hwupload"); mainFilters.Insert(0, "format=nv12|p010le|videotoolbox_vld"); - mainFilters.Insert(0, "hwupload=derive_device=videotoolbox"); } return (mainFilters, subFilters, overlayFilters); @@ -6283,36 +6160,34 @@ namespace MediaBrowser.Controller.MediaEncoding || string.Equals("yuvj420p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase); var is8_10bitSwFormatsVt = is8bitSwFormatsVt || string.Equals("yuv420p10le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase); - // VideoToolbox's Hardware surface in ffmpeg is not only slower than hwupload, but also breaks HDR in many cases. - // For example: https://trac.ffmpeg.org/ticket/10884 - // Disable it for now. - const bool UseHwSurface = false; + // The related patches make videotoolbox hardware surface working is only available in jellyfin-ffmpeg 7.0.1 at the moment. + bool useHwSurface = (_mediaEncoder.EncoderVersion >= _minFFmpegWorkingVtHwSurface) && IsVideoToolboxFullSupported(); if (is8bitSwFormatsVt) { - if (string.Equals("avc", videoStream.Codec, StringComparison.OrdinalIgnoreCase) - || string.Equals("h264", videoStream.Codec, StringComparison.OrdinalIgnoreCase)) - { - return GetHwaccelType(state, options, "h264", bitDepth, UseHwSurface); - } - if (string.Equals("vp8", videoStream.Codec, StringComparison.OrdinalIgnoreCase)) { - return GetHwaccelType(state, options, "vp8", bitDepth, UseHwSurface); + return GetHwaccelType(state, options, "vp8", bitDepth, useHwSurface); } } if (is8_10bitSwFormatsVt) { + if (string.Equals("avc", videoStream.Codec, StringComparison.OrdinalIgnoreCase) + || string.Equals("h264", videoStream.Codec, StringComparison.OrdinalIgnoreCase)) + { + return GetHwaccelType(state, options, "h264", bitDepth, useHwSurface); + } + if (string.Equals("hevc", videoStream.Codec, StringComparison.OrdinalIgnoreCase) || string.Equals("h265", videoStream.Codec, StringComparison.OrdinalIgnoreCase)) { - return GetHwaccelType(state, options, "hevc", bitDepth, UseHwSurface); + return GetHwaccelType(state, options, "hevc", bitDepth, useHwSurface); } if (string.Equals("vp9", videoStream.Codec, StringComparison.OrdinalIgnoreCase)) { - return GetHwaccelType(state, options, "vp9", bitDepth, UseHwSurface); + return GetHwaccelType(state, options, "vp9", bitDepth, useHwSurface); } } @@ -6425,24 +6300,15 @@ namespace MediaBrowser.Controller.MediaEncoding #nullable enable public static int GetNumberOfThreads(EncodingJobInfo? state, EncodingOptions encodingOptions, string? outputVideoCodec) { - // VP8 and VP9 encoders must have their thread counts set. - bool mustSetThreadCount = string.Equals(outputVideoCodec, "libvpx", StringComparison.OrdinalIgnoreCase) - || string.Equals(outputVideoCodec, "libvpx-vp9", StringComparison.OrdinalIgnoreCase); - var threads = state?.BaseRequest.CpuCoreLimit ?? encodingOptions.EncodingThreadCount; if (threads <= 0) { // Automatically set thread count - return mustSetThreadCount ? Math.Max(Environment.ProcessorCount - 1, 1) : 0; - } - - if (threads >= Environment.ProcessorCount) - { - return Environment.ProcessorCount; + return 0; } - return threads; + return Math.Min(threads, Environment.ProcessorCount); } #nullable disable |
