diff --git a/Application/FileConverter/ConversionJobs/ConversionJob_FFMPEG.Converters.cs b/Application/FileConverter/ConversionJobs/ConversionJob_FFMPEG.Converters.cs index 35d46b16..279c90ed 100644 --- a/Application/FileConverter/ConversionJobs/ConversionJob_FFMPEG.Converters.cs +++ b/Application/FileConverter/ConversionJobs/ConversionJob_FFMPEG.Converters.cs @@ -45,14 +45,14 @@ private static string ComputeTransformArgs(ConversionPreset conversionPreset, He if (conversionPreset.OutputType == OutputType.Mkv || conversionPreset.OutputType == OutputType.Mp4) { // This presets use h264 codec, the size of the video need to be divisible by 2. - switch (hwAccel) - { - case Helpers.HardwareAccelerationMode.CUDA: - scaleArgs = string.Format("scale_cuda=trunc(iw*{0}/2)*2:trunc(ih*{0}/2)*2:format=yuv420p", scaleFactor.ToString("#.##", CultureInfo.InvariantCulture)); - break; - default: - scaleArgs = string.Format("scale=trunc(iw*{0}/2)*2:trunc(ih*{0}/2)*2", scaleFactor.ToString("#.##", CultureInfo.InvariantCulture)); - break; + switch (hwAccel) + { + case Helpers.HardwareAccelerationMode.CUDA: + scaleArgs = string.Format("scale_cuda=trunc(iw*{0}/2)*2:trunc(ih*{0}/2)*2:format=yuv420p", scaleFactor.ToString("#.##", CultureInfo.InvariantCulture)); + break; + default: + scaleArgs = string.Format("scale=trunc(iw*{0}/2)*2:trunc(ih*{0}/2)*2", scaleFactor.ToString("#.##", CultureInfo.InvariantCulture)); + break; } } else if (Math.Abs(scaleFactor - 1f) >= 0.005f) @@ -104,10 +104,12 @@ private static string ComputeTransformArgs(ConversionPreset conversionPreset, He transformArgs += rotationArgs; } - if (hwAccel == Helpers.HardwareAccelerationMode.Off && (conversionPreset.OutputType == OutputType.Mkv || conversionPreset.OutputType == OutputType.Mp4)) + if (hwAccel != Helpers.HardwareAccelerationMode.CUDA && (conversionPreset.OutputType == OutputType.Mkv || conversionPreset.OutputType == OutputType.Mp4)) { + // For H.264 in MP4/MKV, force yuv420p for broad player compatibility: // http://trac.ffmpeg.org/wiki/Encode/H.264#Encodingfordumbplayers - // YUV planar color space with 4:2:0 chroma subsampling + // - Software encoding and non-CUDA hardware (e.g., AMF) come through this path. + // - CUDA is excluded here because the scale_cuda path above already sets format=yuv420p. transformArgs += (transformArgs.Length > 0 ? "," : string.Empty) + "format=yuv420p"; //// TODO: maybe there should be an option for this on the settings? } @@ -324,7 +326,71 @@ private string H264EncodingSpeedToPreset(VideoEncodingSpeed encodingSpeed) return "veryslow"; } - throw new Exception("Unknown H264 encoding speed."); + throw new ArgumentOutOfRangeException(nameof(encodingSpeed), encodingSpeed, "Unknown H264 encoding speed."); + } + + /// + /// Convert video encoding speed to NVENC preset. + /// + /// The encoding speed. + /// The NVENC preset. + private string H264EncodingSpeedToNVENCPreset(VideoEncodingSpeed encodingSpeed) + { + switch (encodingSpeed) + { + case VideoEncodingSpeed.UltraFast: + return "p1"; + + case VideoEncodingSpeed.SuperFast: + return "p2"; + + case VideoEncodingSpeed.VeryFast: + return "p3"; + + case VideoEncodingSpeed.Faster: + case VideoEncodingSpeed.Fast: + case VideoEncodingSpeed.Medium: + return "p4"; + + case VideoEncodingSpeed.Slow: + return "p5"; + + case VideoEncodingSpeed.Slower: + return "p6"; + + case VideoEncodingSpeed.VerySlow: + return "p7"; + } + + throw new ArgumentOutOfRangeException(nameof(encodingSpeed), encodingSpeed, "Unknown H264 encoding speed."); + } + + /// + /// Convert video encoding speed to AMF quality mode. + /// + /// The encoding speed. + /// The AMF quality mode. + private string H264EncodingSpeedToAMFQuality(VideoEncodingSpeed encodingSpeed) + { + switch (encodingSpeed) + { + case VideoEncodingSpeed.UltraFast: + case VideoEncodingSpeed.SuperFast: + case VideoEncodingSpeed.VeryFast: + case VideoEncodingSpeed.Faster: + case VideoEncodingSpeed.Fast: + return "speed"; + + case VideoEncodingSpeed.Medium: + case VideoEncodingSpeed.Slow: + return "balanced"; + + case VideoEncodingSpeed.Slower: + case VideoEncodingSpeed.VerySlow: + return "quality"; + } + + throw new ArgumentOutOfRangeException(nameof(encodingSpeed), encodingSpeed, "Unknown H264 encoding speed."); } /// diff --git a/Application/FileConverter/ConversionJobs/ConversionJob_FFMPEG.cs b/Application/FileConverter/ConversionJobs/ConversionJob_FFMPEG.cs index 99c9e93a..74214086 100644 --- a/Application/FileConverter/ConversionJobs/ConversionJob_FFMPEG.cs +++ b/Application/FileConverter/ConversionJobs/ConversionJob_FFMPEG.cs @@ -7,11 +7,11 @@ namespace FileConverter.ConversionJobs using System.Diagnostics; using System.Globalization; using System.IO; - using System.Text.RegularExpressions; - using CommunityToolkit.Mvvm.DependencyInjection; - using FileConverter.Controls; - using FileConverter.Services; - + using System.Text.RegularExpressions; + using CommunityToolkit.Mvvm.DependencyInjection; + using FileConverter.Controls; + using FileConverter.Services; + public partial class ConversionJob_FFMPEG : ConversionJob { private readonly Regex durationRegex = new Regex(@"Duration:\s*([0-9][0-9]):([0-9][0-9]):([0-9][0-9])\.([0-9][0-9]),.*bitrate:\s*([0-9]+) kb\/s"); @@ -269,20 +269,43 @@ protected virtual void FillFFMpegArgumentsList() audioArgs = $"-c:a aac -qscale:a {this.AACBitrateToQualityIndex(audioEncodingBitrate)}"; } - string videoCodec = "libx264"; - string hwAccelArg = ""; - if (hwAccel == Helpers.HardwareAccelerationMode.CUDA) - { - videoCodec = "h264_nvenc"; - hwAccelArg = "-hwaccel cuda -hwaccel_output_format cuda"; - } - + string videoCodec = "libx264"; + string videoCodecArgs = string.Format( + "-preset {0} -crf {1}", + this.H264EncodingSpeedToPreset(videoEncodingSpeed), + this.H264QualityToCRF(videoEncodingQuality)); + string hwAccelArg = string.Empty; + + switch (hwAccel) + { + case Helpers.HardwareAccelerationMode.CUDA: + videoCodec = "h264_nvenc"; + int nvencQP = this.H264QualityToCRF(videoEncodingQuality); + videoCodecArgs = string.Format( + "-preset {0} -rc constqp -qp {1}", + this.H264EncodingSpeedToNVENCPreset(videoEncodingSpeed), + nvencQP); + + hwAccelArg = "-hwaccel cuda -hwaccel_output_format cuda"; + break; + + case Helpers.HardwareAccelerationMode.AMF: + int amfQP = this.H264QualityToCRF(videoEncodingQuality); + int amfBFrameQP = Math.Min(51, amfQP + 2); + videoCodec = "h264_amf"; + videoCodecArgs = string.Format( + "-usage transcoding -quality {0} -qp_i {1} -qp_p {1} -qp_b {2}", + this.H264EncodingSpeedToAMFQuality(videoEncodingSpeed), + amfQP, + amfBFrameQP); + break; + } + string encoderArgs = string.Format( - "-c:v {0} -preset {1} -crf {2} {3} {4}", - videoCodec, - this.H264EncodingSpeedToPreset(videoEncodingSpeed), - this.H264QualityToCRF(videoEncodingQuality), - audioArgs, + "-c:v {0} {1} {2} {3}", + videoCodec, + videoCodecArgs, + audioArgs, videoFilteringArgs); string arguments = string.Format("-n -stats {3} -i \"{0}\" {2} \"{1}\"", this.InputFilePath, this.OutputFilePath, encoderArgs, hwAccelArg); diff --git a/Application/FileConverter/Helpers.cs b/Application/FileConverter/Helpers.cs index 6387f336..1112e10f 100644 --- a/Application/FileConverter/Helpers.cs +++ b/Application/FileConverter/Helpers.cs @@ -352,10 +352,11 @@ public static class InputCategoryNames public const string Misc = "Misc"; } - public enum HardwareAccelerationMode - { - Off, - CUDA - } - } -} \ No newline at end of file + public enum HardwareAccelerationMode + { + Off, + CUDA, + AMF + } + } +} diff --git a/Application/FileConverter/ValueConverters/HardwareAccelerationModeToString.cs b/Application/FileConverter/ValueConverters/HardwareAccelerationModeToString.cs index e2742ba7..970d80c7 100644 --- a/Application/FileConverter/ValueConverters/HardwareAccelerationModeToString.cs +++ b/Application/FileConverter/ValueConverters/HardwareAccelerationModeToString.cs @@ -10,13 +10,15 @@ public class HardwareAccelerationModeToString : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - switch (value) { - case Helpers.HardwareAccelerationMode.Off: - return Properties.Resources.HardwareAccelerationModeOffName; - case Helpers.HardwareAccelerationMode.CUDA: - return Properties.Resources.HardwareAccelerationModeCUDAName; + switch (value) { + case Helpers.HardwareAccelerationMode.Off: + return Properties.Resources.HardwareAccelerationModeOffName; + case Helpers.HardwareAccelerationMode.CUDA: + return Properties.Resources.HardwareAccelerationModeCUDAName; + case Helpers.HardwareAccelerationMode.AMF: + return Properties.Resources.HardwareAccelerationModeAMFName; } - return "Unknown"; + return value?.ToString() ?? string.Empty; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) @@ -24,4 +26,4 @@ public object ConvertBack(object value, Type targetType, object parameter, Cultu throw new NotImplementedException(); } } -} \ No newline at end of file +} diff --git a/Application/FileConverter/ViewModels/SettingsViewModel.cs b/Application/FileConverter/ViewModels/SettingsViewModel.cs index 0b201e5d..15d33c87 100644 --- a/Application/FileConverter/ViewModels/SettingsViewModel.cs +++ b/Application/FileConverter/ViewModels/SettingsViewModel.cs @@ -47,7 +47,7 @@ public class SettingsViewModel : ObservableRecipient, IDataErrorInfo private ListCollectionView outputTypes; private CultureInfo[] supportedCultures; - private Helpers.HardwareAccelerationMode[] hardwareAccelerationModes = { Helpers.HardwareAccelerationMode.Off, Helpers.HardwareAccelerationMode.CUDA }; + private Helpers.HardwareAccelerationMode[] hardwareAccelerationModes = { Helpers.HardwareAccelerationMode.Off, Helpers.HardwareAccelerationMode.CUDA, Helpers.HardwareAccelerationMode.AMF }; public event Action OnPresetCreated; public event Action OnFolderCreated;