releases/tests/Paca.Tests/Core/GpuHardwareAccelerationProfilerTests.cs

176 lines
7.9 KiB
C#

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Paca.Core.Media;
using Paca.Core.Platform;
using Xunit;
namespace Paca.Tests.Core;
public class GpuHardwareAccelerationProfilerTests
{
private sealed class MockProbeProcessRunner : IProcessRunner
{
private readonly Func<ProcessSpec, ProcessResult> _handler;
public MockProbeProcessRunner(Func<ProcessSpec, ProcessResult> handler)
{
_handler = handler;
}
public Task<ProcessResult> RunAsync(ProcessSpec spec, Action<string, string>? onLine, CancellationToken ct)
{
return Task.FromResult(_handler(spec));
}
}
private const string FullRtx4090EncodersOutput =
"Encoders:\n" +
" V..... h264_nvenc NVIDIA NVENC H.264 encoder (codec h264)\n" +
" V..... hevc_nvenc NVIDIA NVENC hevc encoder (codec hevc)\n" +
" V..... av1_nvenc NVIDIA NVENC av1 encoder (codec av1)\n" +
" V..... libx264 libx264 H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10 (codec h264)\n" +
" V..... libx265 libx265 H.265 / HEVC (codec hevc)\n" +
" V..... libsvtav1 SVT-AV1(Scalable Video Technology for AV1) encoder (codec av1)\n";
private const string IntelArcEncodersOutput =
"Encoders:\n" +
" V..... h264_qsv H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10 (Intel Quick Sync Video acceleration) (codec h264)\n" +
" V..... hevc_qsv HEVC (Intel Quick Sync Video acceleration) (codec hevc)\n" +
" V..... av1_qsv AV1 (Intel Quick Sync Video acceleration) (codec av1)\n" +
" V..... libx264 libx264 H.264 (codec h264)\n";
private const string AmdRadeon7000EncodersOutput =
"Encoders:\n" +
" V..... h264_amf AMD AMF H.264 Encoder (codec h264)\n" +
" V..... hevc_amf AMD AMF HEVC Encoder (codec hevc)\n" +
" V..... av1_amf AMD AMF AV1 Encoder (codec av1)\n" +
" V..... libx264 libx264 H.264 (codec h264)\n";
[Fact]
public void GpuCapabilities_NVENC_H264_HEVC_AV1_전부_파싱_검증()
{
var caps = GpuHardwareAccelerationProfiler.ParseCapabilities(FullRtx4090EncodersOutput);
Assert.True(caps.Supports(GpuEncoderBackend.NvidiaNvenc, VideoCodecTarget.H264));
Assert.True(caps.Supports(GpuEncoderBackend.NvidiaNvenc, VideoCodecTarget.HEVC_H265));
Assert.True(caps.Supports(GpuEncoderBackend.NvidiaNvenc, VideoCodecTarget.AV1));
Assert.False(caps.Supports(GpuEncoderBackend.IntelQsv, VideoCodecTarget.AV1));
}
[Fact]
public void GpuCapabilities_IntelQSV_AMD_AMF_AV1_파싱_검증()
{
var intelCaps = GpuHardwareAccelerationProfiler.ParseCapabilities(IntelArcEncodersOutput);
Assert.True(intelCaps.Supports(GpuEncoderBackend.IntelQsv, VideoCodecTarget.AV1));
Assert.True(intelCaps.Supports(GpuEncoderBackend.IntelQsv, VideoCodecTarget.HEVC_H265));
Assert.False(intelCaps.Supports(GpuEncoderBackend.NvidiaNvenc, VideoCodecTarget.AV1));
var amdCaps = GpuHardwareAccelerationProfiler.ParseCapabilities(AmdRadeon7000EncodersOutput);
Assert.True(amdCaps.Supports(GpuEncoderBackend.AmdAmf, VideoCodecTarget.AV1));
Assert.True(amdCaps.Supports(GpuEncoderBackend.AmdAmf, VideoCodecTarget.HEVC_H265));
Assert.False(amdCaps.Supports(GpuEncoderBackend.IntelQsv, VideoCodecTarget.AV1));
}
[Fact]
public void GpuCapabilities_최적_인코더_자동선택_GPU_우선_및_CPU_폴백()
{
var rtxCaps = GpuHardwareAccelerationProfiler.ParseCapabilities(FullRtx4090EncodersOutput);
Assert.Equal("av1_nvenc", rtxCaps.GetOptimalEncoder(VideoCodecTarget.AV1));
Assert.Equal("hevc_nvenc", rtxCaps.GetOptimalEncoder(VideoCodecTarget.HEVC_H265));
Assert.Equal("h264_nvenc", rtxCaps.GetOptimalEncoder(VideoCodecTarget.H264));
var cpuOnlyCaps = GpuHardwareAccelerationProfiler.ParseCapabilities(
"Encoders:\n V..... libx264\n V..... libx265\n V..... libsvtav1\n");
Assert.Equal("libsvtav1", cpuOnlyCaps.GetOptimalEncoder(VideoCodecTarget.AV1));
Assert.Equal("libx265", cpuOnlyCaps.GetOptimalEncoder(VideoCodecTarget.HEVC_H265));
Assert.Equal("libx264", cpuOnlyCaps.GetOptimalEncoder(VideoCodecTarget.H264));
}
[Fact]
public async Task GpuProfiler_실제_런타임_프로빙_미지원시_폴백_검증()
{
var runner = new MockProbeProcessRunner(spec =>
{
if (spec.Args.Contains("-encoders"))
{
return new ProcessResult(0, FullRtx4090EncodersOutput, "");
}
if (spec.Args.Contains("av1_nvenc"))
{
return new ProcessResult(1, "", "[av1_nvenc @ 000001] Driver does not support the required nvenc features\nError initializing output stream");
}
if (spec.Args.Contains("hevc_nvenc") || spec.Args.Contains("h264_nvenc"))
{
return new ProcessResult(0, "frame= 1 fps=0.0 q=0.0 Lsize=N/A video:0KiB", "");
}
return new ProcessResult(0, "", "");
});
var profiler = new GpuHardwareAccelerationProfiler(runner);
var report = await profiler.ProfileAllCodecsAsync("ffmpeg.exe", CancellationToken.None);
Assert.True(report.IsProbeVerified);
Assert.Equal("libsvtav1", report.GetBestEncoder(VideoCodecTarget.AV1));
Assert.Equal("hevc_nvenc", report.GetBestEncoder(VideoCodecTarget.HEVC_H265));
Assert.Equal("h264_nvenc", report.GetBestEncoder(VideoCodecTarget.H264));
}
[Fact]
public void GpuProfiler_인코더별_최적_인수_생성_검증()
{
var nvencAv1Args = GpuHardwareAccelerationProfiler.BuildEncodingArgs("av1_nvenc", crfOrQuality: 24, preset: "p4");
Assert.Contains("-c:v", nvencAv1Args);
Assert.Contains("av1_nvenc", nvencAv1Args);
Assert.Contains("-cq", nvencAv1Args);
Assert.Contains("24", nvencAv1Args);
Assert.Contains("-preset", nvencAv1Args);
Assert.Contains("p4", nvencAv1Args);
var qsvAv1Args = GpuHardwareAccelerationProfiler.BuildEncodingArgs("av1_qsv", crfOrQuality: 26);
Assert.Contains("av1_qsv", qsvAv1Args);
Assert.Contains("-global_quality", qsvAv1Args);
Assert.Contains("26", qsvAv1Args);
var svtAv1Args = GpuHardwareAccelerationProfiler.BuildEncodingArgs("libsvtav1", crfOrQuality: 28, preset: "6");
Assert.Contains("libsvtav1", svtAv1Args);
Assert.Contains("-crf", svtAv1Args);
Assert.Contains("28", svtAv1Args);
}
[Fact]
public void HardwareEncoderDetector_기존_하위호환_및_AV1_확장_검증()
{
var sample = FullRtx4090EncodersOutput;
var caps = HardwareEncoderDetector.ParseCapabilities(sample);
Assert.True(caps.HasNvidiaNvenc);
Assert.True(caps.HasNvencAv1);
Assert.True(caps.HasNvencHevc);
var runner = new MockProbeProcessRunner(_ => new ProcessResult(0, sample, ""));
var detector = new HardwareEncoderDetector(runner);
Assert.Equal("av1_nvenc", detector.GetBestAv1Encoder(caps));
Assert.Equal("hevc_nvenc", detector.GetBestHevcEncoder(caps));
Assert.Equal("h264_nvenc", detector.GetBestH264Encoder(caps));
}
[Fact]
public void HardwareTranscoderStudio_Auto_Backend와_Capabilities_결합시_GPU_인코더_자동선택()
{
var caps = GpuHardwareAccelerationProfiler.ParseCapabilities(FullRtx4090EncodersOutput);
var spec = new TranscodePresetSpec
{
Backend = HardwareEncoderBackend.Auto,
Codec = VideoCodecTarget.AV1
};
var args = HardwareTranscoderStudio.BuildFfmpegTranscodeCommand("in.mp4", "out.mp4", spec, caps);
Assert.Contains("av1_nvenc", args);
}
}