using ErsatzTV.Core.Domain; using ErsatzTV.FFmpeg.Format; using ErsatzTV.ViewModels; using FluentValidation; using FluentValidation.Results; namespace ErsatzTV.Validators; public class FFmpegProfileEditViewModelValidator : AbstractValidator { private static readonly List QsvFormats = [ FFmpegProfileVideoFormat.H264, FFmpegProfileVideoFormat.Hevc, FFmpegProfileVideoFormat.Mpeg2Video, FFmpegProfileVideoFormat.Av1 ]; private static readonly List NvencFormats = [ FFmpegProfileVideoFormat.H264, FFmpegProfileVideoFormat.Hevc, FFmpegProfileVideoFormat.Av1 ]; private static readonly List VaapiFormats = [ FFmpegProfileVideoFormat.H264, FFmpegProfileVideoFormat.Hevc, FFmpegProfileVideoFormat.Mpeg2Video, FFmpegProfileVideoFormat.Av1 ]; private static readonly List VideoToolboxFormats = [ FFmpegProfileVideoFormat.H264, FFmpegProfileVideoFormat.Hevc ]; private static readonly List AmfFormats = [ FFmpegProfileVideoFormat.H264, FFmpegProfileVideoFormat.Hevc ]; private static readonly List V4l2m2mFormats = [ FFmpegProfileVideoFormat.H264, FFmpegProfileVideoFormat.Hevc ]; private static readonly List RkmppFormats = [ FFmpegProfileVideoFormat.H264, FFmpegProfileVideoFormat.Hevc ]; public FFmpegProfileEditViewModelValidator() { RuleFor(x => x.Name).NotEmpty(); RuleFor(x => x.ThreadCount).GreaterThanOrEqualTo(0); RuleFor(x => x.VideoFormat).NotEqual(FFmpegProfileVideoFormat.None); RuleFor(x => x.VideoBitrate).GreaterThan(0); RuleFor(x => x.VideoBufferSize).GreaterThan(0); RuleFor(x => x.AudioFormat).NotEqual(FFmpegProfileAudioFormat.None); RuleFor(x => x.AudioBitrate).GreaterThan(0); RuleFor(x => x.AudioChannels).GreaterThan(0); When( x => x.HardwareAcceleration == HardwareAccelerationKind.Qsv, () => { RuleFor(x => x.VideoFormat).Must(c => QsvFormats.Contains(c)) .WithMessage("QSV supports formats (h264, hevc, mpeg2video)"); }); When( x => x.HardwareAcceleration == HardwareAccelerationKind.Nvenc, () => { RuleFor(x => x.VideoFormat).Must(c => NvencFormats.Contains(c)) .WithMessage("NVENC supports formats (h264, hevc, av1)"); }); When( x => x.HardwareAcceleration == HardwareAccelerationKind.Vaapi, () => { RuleFor(x => x.VideoFormat).Must(c => VaapiFormats.Contains(c)) .WithMessage("VAAPI supports formats (h264, hevc, av1, mpeg2video)"); }); When( x => x.HardwareAcceleration == HardwareAccelerationKind.VideoToolbox, () => { RuleFor(x => x.VideoFormat).Must(c => VideoToolboxFormats.Contains(c)) .WithMessage("VideoToolbox supports formats (h264, hevc)"); }); When( x => x.HardwareAcceleration == HardwareAccelerationKind.Amf, () => { RuleFor(x => x.VideoFormat).Must(c => AmfFormats.Contains(c)) .WithMessage("Amf supports formats (h264, hevc)"); }); When( x => x.HardwareAcceleration == HardwareAccelerationKind.V4l2m2m, () => { RuleFor(x => x.VideoFormat).Must(c => V4l2m2mFormats.Contains(c)) .WithMessage("V4L2 M2M supports formats (h264, hevc)"); }); When( x => x.HardwareAcceleration == HardwareAccelerationKind.Rkmpp, () => { RuleFor(x => x.VideoFormat).Must(c => RkmppFormats.Contains(c)) .WithMessage("Rkmpp supports formats (h264, hevc)"); }); When( x => x.VideoFormat == FFmpegProfileVideoFormat.Mpeg2Video, () => RuleFor(x => x.BitDepth) .Must(bd => bd is FFmpegProfileBitDepth.EightBit) .WithMessage("Mpeg2Video does not support 10-bit content")); When( x => x.HardwareAcceleration != HardwareAccelerationKind.Nvenc && x.VideoFormat == FFmpegProfileVideoFormat.H264 && x.BitDepth == FFmpegProfileBitDepth.TenBit, () => RuleFor(x => x.VideoProfile) .Must(vp => vp == VideoProfile.High10) .WithMessage("VideoProfile must be high10 with 10-bit h264")); When( x => x.HardwareAcceleration == HardwareAccelerationKind.Nvenc && x.VideoFormat == FFmpegProfileVideoFormat.H264 && x.BitDepth == FFmpegProfileBitDepth.TenBit, () => RuleFor(x => x.VideoProfile) .Must(vp => vp == VideoProfile.High444p) .WithMessage("VideoProfile must be high444p with NVIDIA 10-bit h264")); When( x => x.VideoFormat == FFmpegProfileVideoFormat.H264 && x.BitDepth == FFmpegProfileBitDepth.EightBit, () => RuleFor(x => x.VideoProfile) .Must(vp => vp != VideoProfile.High10) .WithMessage("VideoProfile cannot be high10 with 8-bit h264")); } public Func>> ValidateValue => async (model, propertyName) => { ValidationResult result = await ValidateAsync( ValidationContext.CreateWithOptions( (FFmpegProfileEditViewModel)model, x => x.IncludeProperties(propertyName))); if (result.IsValid) { return []; } return result.Errors.Select(e => e.ErrorMessage); }; }