Update VideoToolbox pipeline for jellyfin-ffmpeg7 (#12380)

This commit is contained in:
gnattu 2024-08-05 10:37:40 +08:00 committed by GitHub
parent bbf3a2138b
commit 7ea91dfcc4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -64,6 +64,7 @@ namespace MediaBrowser.Controller.MediaEncoding
private readonly Version _minFFmpegSvtAv1Params = new Version(5, 1); private readonly Version _minFFmpegSvtAv1Params = new Version(5, 1);
private readonly Version _minFFmpegVaapiH26xEncA53CcSei = new Version(6, 0); private readonly Version _minFFmpegVaapiH26xEncA53CcSei = new Version(6, 0);
private readonly Version _minFFmpegReadrateOption = new Version(5, 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); private static readonly Regex _validationRegex = new(ValidationRegex, RegexOptions.Compiled);
@ -5166,12 +5167,14 @@ namespace MediaBrowser.Controller.MediaEncoding
var threeDFormat = state.MediaSource.Video3DFormat; var threeDFormat = state.MediaSource.Video3DFormat;
var isVtEncoder = vidEncoder.Contains("videotoolbox", StringComparison.OrdinalIgnoreCase); 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 doDeintH264 = state.DeInterlace("h264", true) || state.DeInterlace("avc", true);
var doDeintHevc = state.DeInterlace("h265", true) || state.DeInterlace("hevc", true); var doDeintHevc = state.DeInterlace("h265", true) || state.DeInterlace("hevc", true);
var doDeintH2645 = doDeintH264 || doDeintHevc; var doDeintH2645 = doDeintH264 || doDeintHevc;
var doVtTonemap = IsVideoToolboxTonemapAvailable(state, options); var doVtTonemap = IsVideoToolboxTonemapAvailable(state, options);
var doMetalTonemap = !doVtTonemap && IsHwTonemapAvailable(state, options); var doMetalTonemap = !doVtTonemap && IsHwTonemapAvailable(state, options);
var usingHwSurface = isVtDecoder && (_mediaEncoder.EncoderVersion >= _minFFmpegWorkingVtHwSurface);
var scaleFormat = string.Empty; var scaleFormat = string.Empty;
// Use P010 for Metal tone mapping, otherwise force an 8bit output. // Use P010 for Metal tone mapping, otherwise force an 8bit output.
@ -5259,23 +5262,25 @@ namespace MediaBrowser.Controller.MediaEncoding
subFilters.Add(subTextSubtitlesFilter); subFilters.Add(subTextSubtitlesFilter);
} }
subFilters.Add("hwupload=derive_device=videotoolbox"); subFilters.Add("hwupload");
overlayFilters.Add("overlay_videotoolbox=eof_action=pass:repeatlast=0"); 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)) || var needFiltering = mainFilters.Any(f => !string.IsNullOrEmpty(f)) ||
subFilters.Any(f => !string.IsNullOrEmpty(f)) || subFilters.Any(f => !string.IsNullOrEmpty(f)) ||
overlayFilters.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) if (needFiltering)
{ {
// INPUT videotoolbox/memory surface(vram/uma) // INPUT videotoolbox/memory surface(vram/uma)
// this will pass-through automatically if in/out format matches. // this will pass-through automatically if in/out format matches.
mainFilters.Insert(0, "format=nv12|p010le|videotoolbox_vld"); mainFilters.Insert(0, "format=nv12|p010le|videotoolbox_vld");
mainFilters.Insert(0, "hwupload=derive_device=videotoolbox"); mainFilters.Insert(0, "hwupload");
} }
return (mainFilters, subFilters, overlayFilters); return (mainFilters, subFilters, overlayFilters);
@ -6283,22 +6288,20 @@ namespace MediaBrowser.Controller.MediaEncoding
|| string.Equals("yuvj420p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase); || string.Equals("yuvj420p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase);
var is8_10bitSwFormatsVt = is8bitSwFormatsVt || string.Equals("yuv420p10le", 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. // The related patches make videotoolbox hardware surface working is only available in jellyfin-ffmpeg 7.0.1 at the moment.
// For example: https://trac.ffmpeg.org/ticket/10884 bool useHwSurface = (_mediaEncoder.EncoderVersion >= _minFFmpegWorkingVtHwSurface) && IsVideoToolboxFullSupported();
// Disable it for now.
const bool UseHwSurface = false;
if (is8bitSwFormatsVt) if (is8bitSwFormatsVt)
{ {
if (string.Equals("avc", videoStream.Codec, StringComparison.OrdinalIgnoreCase) if (string.Equals("avc", videoStream.Codec, StringComparison.OrdinalIgnoreCase)
|| string.Equals("h264", videoStream.Codec, StringComparison.OrdinalIgnoreCase)) || string.Equals("h264", videoStream.Codec, StringComparison.OrdinalIgnoreCase))
{ {
return GetHwaccelType(state, options, "h264", bitDepth, UseHwSurface); return GetHwaccelType(state, options, "h264", bitDepth, useHwSurface);
} }
if (string.Equals("vp8", videoStream.Codec, StringComparison.OrdinalIgnoreCase)) if (string.Equals("vp8", videoStream.Codec, StringComparison.OrdinalIgnoreCase))
{ {
return GetHwaccelType(state, options, "vp8", bitDepth, UseHwSurface); return GetHwaccelType(state, options, "vp8", bitDepth, useHwSurface);
} }
} }
@ -6307,12 +6310,12 @@ namespace MediaBrowser.Controller.MediaEncoding
if (string.Equals("hevc", videoStream.Codec, StringComparison.OrdinalIgnoreCase) if (string.Equals("hevc", videoStream.Codec, StringComparison.OrdinalIgnoreCase)
|| string.Equals("h265", 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)) if (string.Equals("vp9", videoStream.Codec, StringComparison.OrdinalIgnoreCase))
{ {
return GetHwaccelType(state, options, "vp9", bitDepth, UseHwSurface); return GetHwaccelType(state, options, "vp9", bitDepth, useHwSurface);
} }
} }