mirror of https://github.com/ErsatzTV/ErsatzTV.git
16 changed files with 469 additions and 34 deletions
@ -0,0 +1,204 @@
@@ -0,0 +1,204 @@
|
||||
using System.Collections.Generic; |
||||
using System.Linq; |
||||
using ErsatzTV.FFmpeg.Capabilities.Vaapi; |
||||
using FluentAssertions; |
||||
using NUnit.Framework; |
||||
|
||||
namespace ErsatzTV.FFmpeg.Tests.Capabilities.Vaapi; |
||||
|
||||
[TestFixture] |
||||
public class VaapiCapabilityParserTests |
||||
{ |
||||
private const string BriefOutput = @"Trying display: wayland
|
||||
vainfo: VA-API version: 1.18 (libva 2.18.2) |
||||
vainfo: Driver version: Mesa Gallium driver 23.1.2 for AMD Radeon RX 6750 XT (navi22, LLVM 15.0.7, DRM 3.52, 6.3.8-arch1-1) |
||||
vainfo: Supported profile and entrypoints |
||||
VAProfileMPEG2Simple : VAEntrypointVLD |
||||
VAProfileMPEG2Main : VAEntrypointVLD |
||||
VAProfileVC1Simple : VAEntrypointVLD |
||||
VAProfileVC1Main : VAEntrypointVLD |
||||
VAProfileVC1Advanced : VAEntrypointVLD |
||||
VAProfileH264ConstrainedBaseline: VAEntrypointVLD |
||||
VAProfileH264ConstrainedBaseline: VAEntrypointEncSlice |
||||
VAProfileH264Main : VAEntrypointVLD |
||||
VAProfileH264Main : VAEntrypointEncSlice |
||||
VAProfileH264High : VAEntrypointVLD |
||||
VAProfileH264High : VAEntrypointEncSlice |
||||
VAProfileHEVCMain : VAEntrypointVLD |
||||
VAProfileHEVCMain : VAEntrypointEncSlice |
||||
VAProfileHEVCMain10 : VAEntrypointVLD |
||||
VAProfileHEVCMain10 : VAEntrypointEncSlice |
||||
VAProfileJPEGBaseline : VAEntrypointVLD |
||||
VAProfileVP9Profile0 : VAEntrypointVLD |
||||
VAProfileVP9Profile2 : VAEntrypointVLD |
||||
VAProfileAV1Profile0 : VAEntrypointVLD |
||||
VAProfileNone : VAEntrypointVideoProc";
|
||||
|
||||
private const string FullOutput = @"Trying display: wayland
|
||||
vainfo: VA-API version: 1.18 (libva 2.18.2) |
||||
vainfo: Driver version: Mesa Gallium driver 23.1.2 for AMD Radeon RX 6750 XT (navi22, LLVM 15.0.7, DRM 3.52, 6.3.8-arch1-1) |
||||
vainfo: Supported config attributes per profile/entrypoint pair |
||||
VAProfileMPEG2Simple/VAEntrypointVLD |
||||
VAConfigAttribRTFormat : VA_RT_FORMAT_YUV420 |
||||
|
||||
VAProfileMPEG2Main/VAEntrypointVLD |
||||
VAConfigAttribRTFormat : VA_RT_FORMAT_YUV420 |
||||
|
||||
VAProfileVC1Simple/VAEntrypointVLD |
||||
VAConfigAttribRTFormat : VA_RT_FORMAT_YUV420 |
||||
|
||||
VAProfileVC1Main/VAEntrypointVLD |
||||
VAConfigAttribRTFormat : VA_RT_FORMAT_YUV420 |
||||
|
||||
VAProfileVC1Advanced/VAEntrypointVLD |
||||
VAConfigAttribRTFormat : VA_RT_FORMAT_YUV420 |
||||
|
||||
VAProfileH264ConstrainedBaseline/VAEntrypointVLD |
||||
VAConfigAttribRTFormat : VA_RT_FORMAT_YUV420 |
||||
|
||||
VAProfileH264ConstrainedBaseline/VAEntrypointEncSlice |
||||
VAConfigAttribRTFormat : VA_RT_FORMAT_YUV420 |
||||
VAConfigAttribRateControl : VA_RC_CBR |
||||
VA_RC_VBR |
||||
VA_RC_CQP |
||||
VAConfigAttribEncPackedHeaders : VA_ENC_PACKED_HEADER_NONE |
||||
VAConfigAttribEncMaxRefFrames : l0=1 |
||||
l1=0 |
||||
VAConfigAttribEncMaxSlices : 128 |
||||
VAConfigAttribEncSliceStructure : VA_ENC_SLICE_STRUCTURE_POWER_OF_TWO_ROWS |
||||
VA_ENC_SLICE_STRUCTURE_EQUAL_ROWS |
||||
VAConfigAttribEncQualityRange : number of supported quality levels is 32 |
||||
VAConfigAttribEncRateControlExt : max_num_temporal_layers_minus1=3 temporal_layer_bitrate_control_flag=1 |
||||
VAConfigAttribMaxFrameSize : max_frame_size=1 |
||||
multiple_pass=0 |
||||
|
||||
VAProfileH264Main/VAEntrypointVLD |
||||
VAConfigAttribRTFormat : VA_RT_FORMAT_YUV420 |
||||
|
||||
VAProfileH264Main/VAEntrypointEncSlice |
||||
VAConfigAttribRTFormat : VA_RT_FORMAT_YUV420 |
||||
VAConfigAttribRateControl : VA_RC_CBR |
||||
VA_RC_VBR |
||||
VA_RC_CQP |
||||
VAConfigAttribEncPackedHeaders : VA_ENC_PACKED_HEADER_NONE |
||||
VAConfigAttribEncMaxRefFrames : l0=1 |
||||
l1=0 |
||||
VAConfigAttribEncMaxSlices : 128 |
||||
VAConfigAttribEncSliceStructure : VA_ENC_SLICE_STRUCTURE_POWER_OF_TWO_ROWS |
||||
VA_ENC_SLICE_STRUCTURE_EQUAL_ROWS |
||||
VAConfigAttribEncQualityRange : number of supported quality levels is 32 |
||||
VAConfigAttribEncRateControlExt : max_num_temporal_layers_minus1=3 temporal_layer_bitrate_control_flag=1 |
||||
VAConfigAttribMaxFrameSize : max_frame_size=1 |
||||
multiple_pass=0 |
||||
|
||||
VAProfileH264High/VAEntrypointVLD |
||||
VAConfigAttribRTFormat : VA_RT_FORMAT_YUV420 |
||||
|
||||
VAProfileH264High/VAEntrypointEncSlice |
||||
VAConfigAttribRTFormat : VA_RT_FORMAT_YUV420 |
||||
VA_RT_FORMAT_YUV420_10 |
||||
VA_RT_FORMAT_YUV420_10BPP |
||||
VAConfigAttribRateControl : VA_RC_CBR |
||||
VA_RC_VBR |
||||
VA_RC_CQP |
||||
VAConfigAttribEncPackedHeaders : VA_ENC_PACKED_HEADER_NONE |
||||
VAConfigAttribEncMaxRefFrames : l0=1 |
||||
l1=0 |
||||
VAConfigAttribEncMaxSlices : 128 |
||||
VAConfigAttribEncSliceStructure : VA_ENC_SLICE_STRUCTURE_POWER_OF_TWO_ROWS |
||||
VA_ENC_SLICE_STRUCTURE_EQUAL_ROWS |
||||
VAConfigAttribEncQualityRange : number of supported quality levels is 32 |
||||
VAConfigAttribEncRateControlExt : max_num_temporal_layers_minus1=3 temporal_layer_bitrate_control_flag=1 |
||||
VAConfigAttribMaxFrameSize : max_frame_size=1 |
||||
multiple_pass=0 |
||||
|
||||
VAProfileHEVCMain/VAEntrypointVLD |
||||
VAConfigAttribRTFormat : VA_RT_FORMAT_YUV420 |
||||
|
||||
VAProfileHEVCMain/VAEntrypointEncSlice |
||||
VAConfigAttribRTFormat : VA_RT_FORMAT_YUV420 |
||||
VAConfigAttribRateControl : VA_RC_CBR |
||||
VA_RC_VBR |
||||
VA_RC_CQP |
||||
VAConfigAttribEncPackedHeaders : VA_ENC_PACKED_HEADER_SEQUENCE |
||||
VAConfigAttribEncMaxRefFrames : l0=1 |
||||
l1=0 |
||||
VAConfigAttribEncMaxSlices : 128 |
||||
VAConfigAttribEncSliceStructure : VA_ENC_SLICE_STRUCTURE_POWER_OF_TWO_ROWS |
||||
VA_ENC_SLICE_STRUCTURE_EQUAL_ROWS |
||||
VAConfigAttribEncQualityRange : number of supported quality levels is 32 |
||||
VAConfigAttribMaxFrameSize : max_frame_size=1 |
||||
multiple_pass=0 |
||||
|
||||
VAProfileHEVCMain10/VAEntrypointVLD |
||||
VAConfigAttribRTFormat : VA_RT_FORMAT_YUV420 |
||||
VA_RT_FORMAT_YUV420_10 |
||||
VA_RT_FORMAT_YUV420_10BPP |
||||
|
||||
VAProfileHEVCMain10/VAEntrypointEncSlice |
||||
VAConfigAttribRTFormat : VA_RT_FORMAT_YUV420 |
||||
VA_RT_FORMAT_YUV420_10 |
||||
VA_RT_FORMAT_YUV420_10BPP |
||||
VAConfigAttribRateControl : VA_RC_CBR |
||||
VA_RC_VBR |
||||
VA_RC_CQP |
||||
VAConfigAttribEncPackedHeaders : VA_ENC_PACKED_HEADER_SEQUENCE |
||||
VAConfigAttribEncMaxRefFrames : l0=1 |
||||
l1=0 |
||||
VAConfigAttribEncMaxSlices : 128 |
||||
VAConfigAttribEncSliceStructure : VA_ENC_SLICE_STRUCTURE_POWER_OF_TWO_ROWS |
||||
VA_ENC_SLICE_STRUCTURE_EQUAL_ROWS |
||||
VAConfigAttribEncQualityRange : number of supported quality levels is 32 |
||||
VAConfigAttribMaxFrameSize : max_frame_size=1 |
||||
multiple_pass=0 |
||||
|
||||
VAProfileJPEGBaseline/VAEntrypointVLD |
||||
VAConfigAttribRTFormat : VA_RT_FORMAT_YUV420 |
||||
VA_RT_FORMAT_YUV422 |
||||
VA_RT_FORMAT_YUV444 |
||||
VA_RT_FORMAT_YUV400 |
||||
|
||||
VAProfileVP9Profile0/VAEntrypointVLD |
||||
VAConfigAttribRTFormat : VA_RT_FORMAT_YUV420 |
||||
|
||||
VAProfileVP9Profile2/VAEntrypointVLD |
||||
VAConfigAttribRTFormat : VA_RT_FORMAT_YUV420_10 |
||||
VA_RT_FORMAT_YUV420_10BPP |
||||
|
||||
VAProfileAV1Profile0/VAEntrypointVLD |
||||
VAConfigAttribRTFormat : VA_RT_FORMAT_YUV420 |
||||
VA_RT_FORMAT_YUV420_10 |
||||
VA_RT_FORMAT_YUV420_10BPP |
||||
|
||||
VAProfileNone/VAEntrypointVideoProc |
||||
VAConfigAttribRTFormat : VA_RT_FORMAT_YUV420 |
||||
VA_RT_FORMAT_YUV422 |
||||
VA_RT_FORMAT_YUV444 |
||||
VA_RT_FORMAT_YUV400 |
||||
VA_RT_FORMAT_YUV420_10 |
||||
VA_RT_FORMAT_RGB32 |
||||
VA_RT_FORMAT_YUV420_10BPP";
|
||||
|
||||
[Test] |
||||
public void ShouldParseEntrypoints() |
||||
{ |
||||
List<VaapiProfileEntrypoint> brief = VaapiCapabilityParser.Parse(BriefOutput); |
||||
List<VaapiProfileEntrypoint> full = VaapiCapabilityParser.ParseFull(FullOutput); |
||||
|
||||
brief.Count.Should().Be(20); |
||||
full.Count.Should().Be(20); |
||||
} |
||||
|
||||
[Test] |
||||
public void Full_ShouldParseRateControlModes() |
||||
{ |
||||
List<VaapiProfileEntrypoint> full = VaapiCapabilityParser.ParseFull(FullOutput); |
||||
|
||||
full.Count.Should().Be(20); |
||||
full.Count(e => e.VaapiEntrypoint.StartsWith("VAEntrypointEnc")).Should().BeGreaterThan(0); |
||||
foreach (VaapiProfileEntrypoint entrypoint in full.Where(e => e.VaapiEntrypoint.StartsWith("VAEntrypointEnc"))) |
||||
{ |
||||
entrypoint.RateControlModes.Count.Should().BeGreaterThan(0); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,68 @@
@@ -0,0 +1,68 @@
|
||||
using System.Text.RegularExpressions; |
||||
|
||||
namespace ErsatzTV.FFmpeg.Capabilities.Vaapi; |
||||
|
||||
public static class VaapiCapabilityParser |
||||
{ |
||||
public static List<VaapiProfileEntrypoint> Parse(string output) |
||||
{ |
||||
var profileEntrypoints = new List<VaapiProfileEntrypoint>(); |
||||
|
||||
foreach (string line in string.Join("", output).Split("\n")) |
||||
{ |
||||
const string PROFILE_ENTRYPOINT_PATTERN = @"(VAProfile\w*).*(VAEntrypoint\w*)"; |
||||
Match match = Regex.Match(line, PROFILE_ENTRYPOINT_PATTERN); |
||||
if (match.Success) |
||||
{ |
||||
profileEntrypoints.Add( |
||||
new VaapiProfileEntrypoint( |
||||
match.Groups[1].Value.Trim(), |
||||
match.Groups[2].Value.Trim())); |
||||
} |
||||
} |
||||
|
||||
return profileEntrypoints; |
||||
} |
||||
|
||||
public static List<VaapiProfileEntrypoint> ParseFull(string output) |
||||
{ |
||||
var profileEntrypoints = new List<VaapiProfileEntrypoint>(); |
||||
var profile = new VaapiProfileEntrypoint(string.Empty, string.Empty); |
||||
string[] allLines = string.Join("", output).Split("\n"); |
||||
|
||||
for (var i = 0; i < allLines.Length; i++) |
||||
{ |
||||
string line = allLines[i]; |
||||
const string PROFILE_ENTRYPOINT_PATTERN = @"(VAProfile\w*).*(VAEntrypoint\w*)"; |
||||
const string PROFILE_RATE_CONTROL_PATTERN = @".*VA_RC_(\w*).*"; |
||||
Match match = Regex.Match(line, PROFILE_ENTRYPOINT_PATTERN); |
||||
if (match.Success) |
||||
{ |
||||
profile = new VaapiProfileEntrypoint(match.Groups[1].Value.Trim(), match.Groups[2].Value.Trim()); |
||||
profileEntrypoints.Add(profile); |
||||
} |
||||
else |
||||
{ |
||||
// check for rate control
|
||||
match = Regex.Match(line, PROFILE_RATE_CONTROL_PATTERN); |
||||
if (match.Success) |
||||
{ |
||||
switch (match.Groups[1].Value.Trim().ToLowerInvariant()) |
||||
{ |
||||
case "cqp": |
||||
profile.AddRateControlMode(RateControlMode.CQP); |
||||
break; |
||||
case "vbr": |
||||
profile.AddRateControlMode(RateControlMode.VBR); |
||||
break; |
||||
case "cbr": |
||||
profile.AddRateControlMode(RateControlMode.CBR); |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
return profileEntrypoints; |
||||
} |
||||
} |
||||
@ -1,3 +1,13 @@
@@ -1,3 +1,13 @@
|
||||
namespace ErsatzTV.FFmpeg.Capabilities.Vaapi; |
||||
|
||||
public record VaapiProfileEntrypoint(string VaapiProfile, string VaapiEntrypoint); |
||||
public record VaapiProfileEntrypoint(string VaapiProfile, string VaapiEntrypoint) |
||||
{ |
||||
private readonly System.Collections.Generic.HashSet<RateControlMode> _rateControlModes = new(); |
||||
|
||||
public IReadOnlyCollection<RateControlMode> RateControlModes => _rateControlModes; |
||||
|
||||
public bool AddRateControlMode(RateControlMode mode) |
||||
{ |
||||
return _rateControlModes.Add(mode); |
||||
} |
||||
} |
||||
|
||||
Loading…
Reference in new issue