Browse Source

fix nvidia in docker; minimize nvenc sessions (#2460)

pull/2461/head
Jason Dove 3 months ago committed by GitHub
parent
commit
8b4b7cf16a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 4
      ErsatzTV.FFmpeg/Capabilities/Nvidia/CudaDevice.cs
  2. 115
      ErsatzTV.FFmpeg/Capabilities/Nvidia/CudaHelper.cs
  3. 6
      ErsatzTV.FFmpeg/Capabilities/Nvidia/NvEncSharpRedirector.cs
  4. 123
      ErsatzTV.FFmpeg/Capabilities/NvidiaHardwareCapabilities.cs

4
ErsatzTV.FFmpeg/Capabilities/Nvidia/CudaDevice.cs

@ -1,3 +1,5 @@ @@ -1,3 +1,5 @@
namespace ErsatzTV.FFmpeg.Capabilities.Nvidia;
public record CudaDevice(int Handle, string Model, Version Version);
public record CudaDevice(int Handle, string Model, Version Version, IReadOnlyList<CudaCodec> Codecs);
public record CudaCodec(string Name, Guid CodecGuid, IReadOnlyList<Guid> ProfileGuids, IReadOnlyList<int> BitDepths);

115
ErsatzTV.FFmpeg/Capabilities/Nvidia/CudaHelper.cs

@ -1,3 +1,4 @@ @@ -1,3 +1,4 @@
using System.Collections.Immutable;
using System.Globalization;
using System.Text;
using ErsatzTV.FFmpeg.Format;
@ -62,69 +63,89 @@ internal static class CudaHelper @@ -62,69 +63,89 @@ internal static class CudaHelper
foreach (var description in CuDevice.GetDescriptions())
{
var device = description.Device;
string name = device.GetName();
int nullIndex = name.IndexOf('\0');
if (nullIndex > 0)
try
{
name = name[..nullIndex];
}
var device = description.Device;
int major = device.GetAttribute(CuDeviceAttribute.ComputeCapabilityMajor);
int minor = device.GetAttribute(CuDeviceAttribute.ComputeCapabilityMinor);
string name = device.GetName();
int nullIndex = name.IndexOf('\0');
if (nullIndex > 0)
{
name = name[..nullIndex];
}
result.Add(new CudaDevice(device.Handle, name, new Version(major, minor)));
}
int major = device.GetAttribute(CuDeviceAttribute.ComputeCapabilityMajor);
int minor = device.GetAttribute(CuDeviceAttribute.ComputeCapabilityMinor);
return result;
}
using var context = device.CreateContext();
var sessionParams = new NvEncOpenEncodeSessionExParams
{
Version = LibNvEnc.NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER,
ApiVersion = LibNvEnc.NVENCAPI_VERSION,
Device = context.Handle,
DeviceType = NvEncDeviceType.Cuda
};
var codecs = new List<CudaCodec>();
var encoder = LibNvEnc.OpenEncoder(ref sessionParams);
try
{
IReadOnlyList<Guid> codecGuids = encoder.GetEncodeGuids();
foreach ((string codecName, Guid codecGuid) in AllCodecs)
{
if (codecGuids.Contains(codecGuid))
{
IReadOnlyList<Guid> codecProfileGuids = encoder.GetEncodeProfileGuids(codecGuid);
internal static string GetDeviceDetails(CudaDevice device)
{
var sb = new StringBuilder();
var bitDepths = new List<int> { 8 };
try
{
var dev = CuDevice.GetDevice(device.Handle);
using var context = dev.CreateContext();
var sessionParams = new NvEncOpenEncodeSessionExParams
{
Version = LibNvEnc.NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER,
ApiVersion = LibNvEnc.NVENCAPI_VERSION,
Device = context.Handle,
DeviceType = NvEncDeviceType.Cuda
};
var cap = new NvEncCapsParam { CapsToQuery = NvEncCaps.Support10bitEncode };
var capsVal = 0;
encoder.GetEncodeCaps(codecGuid, ref cap, ref capsVal);
if (capsVal > 0)
{
bitDepths.Add(10);
}
var encoder = LibNvEnc.OpenEncoder(ref sessionParams);
try
{
sb.AppendLine(" Encoding:");
IReadOnlyList<Guid> codecGuids = encoder.GetEncodeGuids();
foreach ((string codecName, Guid codecGuid) in AllCodecs)
{
if (codecGuids.Contains(codecGuid))
{
sb.AppendLine(CultureInfo.InvariantCulture, $" - Supports {codecName} 8-bit");
var cudaCodec = new CudaCodec(
codecName,
codecGuid,
codecProfileGuids,
bitDepths.ToImmutableList());
var cap = new NvEncCapsParam { CapsToQuery = NvEncCaps.Support10bitEncode };
var capsVal = 0;
encoder.GetEncodeCaps(codecGuid, ref cap, ref capsVal);
if (capsVal > 0)
{
sb.AppendLine(CultureInfo.InvariantCulture, $" - Supports {codecName} 10-bit");
codecs.Add(cudaCodec);
}
}
}
finally
{
encoder.DestroyEncoder();
}
result.Add(new CudaDevice(device.Handle, name, new Version(major, minor), codecs));
}
finally
catch (Exception)
{
encoder.DestroyEncoder();
// do nothing
}
}
catch (Exception)
return result;
}
internal static string GetDeviceDetails(CudaDevice device)
{
var sb = new StringBuilder();
sb.AppendLine(" Encoding:");
foreach (CudaCodec cudaCodec in device.Codecs)
{
// do nothing
sb.AppendLine(CultureInfo.InvariantCulture, $" - Supports {cudaCodec.Name} 8-bit");
if (cudaCodec.BitDepths.Contains(10))
{
sb.AppendLine(CultureInfo.InvariantCulture, $" - Supports {cudaCodec.Name} 10-bit");
}
}
return sb.ToString();

6
ErsatzTV.FFmpeg/Capabilities/Nvidia/NvEncSharpRedirector.cs

@ -15,7 +15,7 @@ public static class NvEncSharpRedirector @@ -15,7 +15,7 @@ public static class NvEncSharpRedirector
if (libraryName.Equals("nvEncodeAPI64.dll", StringComparison.OrdinalIgnoreCase))
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
return NativeLibrary.Load("libnvidia-encode.so", assembly, searchPath);
return NativeLibrary.Load("libnvidia-encode.so.1", assembly, searchPath);
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
return NativeLibrary.Load("nvEncodeAPI64.dll", assembly, searchPath);
}
@ -23,7 +23,7 @@ public static class NvEncSharpRedirector @@ -23,7 +23,7 @@ public static class NvEncSharpRedirector
if (libraryName.Equals("nvEncodeAPI.dll", StringComparison.OrdinalIgnoreCase))
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
return NativeLibrary.Load("libnvidia-encode.so", assembly, searchPath);
return NativeLibrary.Load("libnvidia-encode.so.1", assembly, searchPath);
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
return NativeLibrary.Load("nvEncodeAPI.dll", assembly, searchPath);
}
@ -31,7 +31,7 @@ public static class NvEncSharpRedirector @@ -31,7 +31,7 @@ public static class NvEncSharpRedirector
if (libraryName.Equals("nvcuda.dll", StringComparison.OrdinalIgnoreCase))
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
return NativeLibrary.Load("libcuda.so", assembly, searchPath);
return NativeLibrary.Load("libcuda.so.1", assembly, searchPath);
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
return NativeLibrary.Load("nvcuda.dll", assembly, searchPath);
}

123
ErsatzTV.FFmpeg/Capabilities/NvidiaHardwareCapabilities.cs

@ -95,92 +95,55 @@ public class NvidiaHardwareCapabilities : IHardwareCapabilities @@ -95,92 +95,55 @@ public class NvidiaHardwareCapabilities : IHardwareCapabilities
{
int bitDepth = maybePixelFormat.Map(pf => pf.BitDepth).IfNone(8);
try
_logger.LogDebug(
"Checking NvEnc {Format} / {Profile} / {BitDepth}-bit",
videoFormat,
videoProfile,
bitDepth);
var codec = _cudaDevice.Codecs.FirstOrDefault(c => c.Name.Equals(
videoFormat,
StringComparison.OrdinalIgnoreCase));
if (codec == null)
{
var dev = CuDevice.GetDevice(0);
using var context = dev.CreateContext();
var sessionParams = new NvEncOpenEncodeSessionExParams
{
Version = LibNvEnc.NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER,
ApiVersion = LibNvEnc.NVENCAPI_VERSION,
Device = context.Handle,
DeviceType = NvEncDeviceType.Cuda
};
_logger.LogWarning("NvEnc {Format} is not supported; will use software encode", videoFormat);
return FFmpegCapability.Software;
}
var encoder = LibNvEnc.OpenEncoder(ref sessionParams);
try
{
_logger.LogDebug(
"Checking NvEnc {Format} / {Profile} / {BitDepth}-bit",
videoFormat,
videoProfile,
bitDepth);
var codecGuid = videoFormat switch
{
VideoFormat.Hevc => NvEncCodecGuids.Hevc,
_ => NvEncCodecGuids.H264
};
IReadOnlyList<Guid> codecGuids = encoder.GetEncodeGuids();
if (!codecGuids.Contains(codecGuid))
{
_logger.LogWarning("NvEnc {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,
_ => NvEncProfileGuids.H264Main
};
IReadOnlyList<Guid> profileGuids = encoder.GetEncodeProfileGuids(codecGuid);
if (!profileGuids.Contains(profileGuid))
{
_logger.LogWarning(
"NvEnc {Format} / {Profile} is not supported; will use software encode",
videoFormat,
videoProfile);
return FFmpegCapability.Software;
}
if (bitDepth == 10)
{
var cap = new NvEncCapsParam { CapsToQuery = NvEncCaps.Support10bitEncode };
var capsVal = 0;
encoder.GetEncodeCaps(codecGuid, ref cap, ref capsVal);
if (capsVal == 0)
{
_logger.LogWarning(
"NvEnc {Format} / {Profile} / {BitDepth}-bit is not supported; will use software encode",
videoFormat,
videoProfile,
bitDepth);
return FFmpegCapability.Software;
}
}
return FFmpegCapability.Hardware;
}
finally
{
encoder.DestroyEncoder();
}
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,
_ => NvEncProfileGuids.H264Main
};
if (!codec.ProfileGuids.Contains(profileGuid))
{
_logger.LogWarning(
"NvEnc {Format} / {Profile} is not supported; will use software encode",
videoFormat,
videoProfile);
return FFmpegCapability.Software;
}
catch (Exception ex)
if (!codec.BitDepths.Contains(bitDepth))
{
_logger.LogWarning(ex, "Unexpected error checking NvEnc capabilities; falling back to software");
_logger.LogWarning(
"NvEnc {Format} / {Profile} / {BitDepth}-bit is not supported; will use software encode",
videoFormat,
videoProfile,
bitDepth);
return FFmpegCapability.Software;
}
return FFmpegCapability.Software;
return FFmpegCapability.Hardware;
}
public Option<RateControlMode> GetRateControlMode(string videoFormat, Option<IPixelFormat> maybePixelFormat) =>

Loading…
Cancel
Save