mirror of https://github.com/ErsatzTV/ErsatzTV.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
153 lines
5.6 KiB
153 lines
5.6 KiB
using ErsatzTV.FFmpeg.Capabilities.Nvidia; |
|
using ErsatzTV.FFmpeg.Format; |
|
using Lennox.NvEncSharp; |
|
using Microsoft.Extensions.Logging; |
|
|
|
namespace ErsatzTV.FFmpeg.Capabilities; |
|
|
|
public class NvidiaHardwareCapabilities(CudaDevice cudaDevice, IFFmpegCapabilities ffmpegCapabilities, ILogger logger) |
|
: IHardwareCapabilities |
|
{ |
|
public bool HevcBFrames(int bitDepth) => |
|
cudaDevice.Encoders.Any(e => e.CodecGuid == NvEncCodecGuids.Hevc && e.BFrames); |
|
|
|
public FFmpegCapability CanDecode( |
|
string videoFormat, |
|
Option<string> videoProfile, |
|
Option<IPixelFormat> maybePixelFormat, |
|
ColorParams colorParams) |
|
{ |
|
int bitDepth = maybePixelFormat.Map(pf => pf.BitDepth).IfNone(8); |
|
|
|
logger.LogDebug( |
|
"Checking NVIDIA decode {Format} / {Profile} / {BitDepth}-bit", |
|
videoFormat, |
|
videoProfile, |
|
bitDepth); |
|
|
|
var isHardware = false; |
|
|
|
CuVideoCodec? codecType = videoFormat switch |
|
{ |
|
VideoFormat.Mpeg2Video => CuVideoCodec.MPEG2, |
|
VideoFormat.Mpeg4 => CuVideoCodec.MPEG4, |
|
VideoFormat.Vc1 => CuVideoCodec.VC1, |
|
VideoFormat.H264 => CuVideoCodec.H264, |
|
VideoFormat.Hevc => CuVideoCodec.HEVC, |
|
VideoFormat.Vp8 => CuVideoCodec.VP8, |
|
VideoFormat.Vp9 => CuVideoCodec.VP9, |
|
VideoFormat.Av1 => (CuVideoCodec)11, // confirmed in dynlink_cuviddec.h |
|
_ => null |
|
}; |
|
|
|
if (codecType.HasValue) |
|
{ |
|
isHardware = cudaDevice.Decoders.Any(d => d.VideoCodec == codecType.Value && d.BitDepth == bitDepth); |
|
if (!isHardware) |
|
{ |
|
logger.LogWarning( |
|
"NVIDIA decode {Format} / {BitDepth} is not supported; will use software decode", |
|
videoFormat, |
|
bitDepth); |
|
} |
|
} |
|
|
|
if (colorParams.IsBt2020Ten) |
|
{ |
|
isHardware = false; |
|
} |
|
|
|
if (isHardware) |
|
{ |
|
return videoFormat switch |
|
{ |
|
VideoFormat.Mpeg2Video => CheckHardwareCodec( |
|
FFmpegKnownDecoder.Mpeg2Cuvid, |
|
ffmpegCapabilities.HasDecoder), |
|
VideoFormat.Mpeg4 => CheckHardwareCodec(FFmpegKnownDecoder.Mpeg4Cuvid, ffmpegCapabilities.HasDecoder), |
|
VideoFormat.Vc1 => CheckHardwareCodec(FFmpegKnownDecoder.Vc1Cuvid, ffmpegCapabilities.HasDecoder), |
|
VideoFormat.H264 => CheckHardwareCodec(FFmpegKnownDecoder.H264Cuvid, ffmpegCapabilities.HasDecoder), |
|
VideoFormat.Hevc => CheckHardwareCodec(FFmpegKnownDecoder.HevcCuvid, ffmpegCapabilities.HasDecoder), |
|
VideoFormat.Vp9 => CheckHardwareCodec(FFmpegKnownDecoder.Vp9Cuvid, ffmpegCapabilities.HasDecoder), |
|
VideoFormat.Av1 => CheckHardwareCodec(FFmpegKnownDecoder.Av1Cuvid, ffmpegCapabilities.HasDecoder), |
|
_ => FFmpegCapability.Software |
|
}; |
|
} |
|
|
|
return FFmpegCapability.Software; |
|
} |
|
|
|
public FFmpegCapability CanEncode( |
|
string videoFormat, |
|
Option<string> videoProfile, |
|
Option<IPixelFormat> maybePixelFormat) |
|
{ |
|
int bitDepth = maybePixelFormat.Map(pf => pf.BitDepth).IfNone(8); |
|
|
|
logger.LogDebug( |
|
"Checking NVIDIA encode {Format} / {Profile} / {BitDepth}-bit", |
|
videoFormat, |
|
videoProfile, |
|
bitDepth); |
|
|
|
var codec = cudaDevice.Encoders.FirstOrDefault(c => c.Name.Equals( |
|
videoFormat, |
|
StringComparison.OrdinalIgnoreCase)); |
|
|
|
if (codec == null) |
|
{ |
|
logger.LogWarning("NVIDIA encode {Format} is not supported; will use software encode", videoFormat); |
|
return FFmpegCapability.Software; |
|
} |
|
|
|
var profileGuid = (videoFormat, videoProfile.IfNone(string.Empty), bitDepth) switch |
|
{ |
|
(VideoFormat.Hevc, _, 8) => NvEncProfileGuids.HevcMain, |
|
(VideoFormat.Hevc, _, 10) => NvEncProfileGuids.HevcMain10, |
|
|
|
(VideoFormat.H264, _, 10) => NvEncProfileGuids.H264High444, |
|
(VideoFormat.H264, VideoProfile.High, _) => NvEncProfileGuids.H264High, |
|
// high10 is for libx264, nvenc needs high444 |
|
(VideoFormat.H264, VideoProfile.High10, _) => NvEncProfileGuids.H264High444, |
|
|
|
(VideoFormat.Av1, _, _) => CudaHelper.Av1ProfileGuid, |
|
|
|
_ => NvEncProfileGuids.H264Main |
|
}; |
|
|
|
if (!codec.ProfileGuids.Contains(profileGuid)) |
|
{ |
|
logger.LogWarning( |
|
"NVIDIA encode {Format} / {Profile} is not supported; will use software encode", |
|
videoFormat, |
|
videoProfile); |
|
return FFmpegCapability.Software; |
|
} |
|
|
|
if (!codec.BitDepths.Contains(bitDepth)) |
|
{ |
|
logger.LogWarning( |
|
"NVIDIA encode {Format} / {Profile} / {BitDepth}-bit is not supported; will use software encode", |
|
videoFormat, |
|
videoProfile, |
|
bitDepth); |
|
return FFmpegCapability.Software; |
|
} |
|
|
|
return FFmpegCapability.Hardware; |
|
} |
|
|
|
public Option<RateControlMode> GetRateControlMode(string videoFormat, Option<IPixelFormat> maybePixelFormat) => |
|
Option<RateControlMode>.None; |
|
|
|
private FFmpegCapability CheckHardwareCodec(FFmpegKnownDecoder codec, Func<FFmpegKnownDecoder, bool> check) |
|
{ |
|
if (check(codec)) |
|
{ |
|
return FFmpegCapability.Hardware; |
|
} |
|
|
|
logger.LogWarning("FFmpeg does not contain codec {Codec}; will fall back to software codec", codec.Name); |
|
return FFmpegCapability.Software; |
|
} |
|
}
|
|
|