// -------------------------------------------------------------------------------------------------------------------- // // This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License. // // // The Video View Model // // -------------------------------------------------------------------------------------------------------------------- namespace HandBrakeWPF.ViewModels { using System; using System.Collections.Generic; using System.ComponentModel; using System.Globalization; using System.Linq; using HandBrake.App.Core.Utilities; using HandBrake.Interop.Interop; using HandBrake.Interop.Interop.Interfaces.Model; using HandBrake.Interop.Interop.Interfaces.Model.Encoders; using HandBrakeWPF.EventArgs; using HandBrakeWPF.Model; using HandBrakeWPF.Model.Video; using HandBrakeWPF.Properties; using HandBrakeWPF.Services.Interfaces; using HandBrakeWPF.Services.Presets.Model; using HandBrakeWPF.Services.Scan.Model; using HandBrakeWPF.ViewModels.Interfaces; using Clipboard = System.Windows.Clipboard; using EncodeTask = Services.Encode.Model.EncodeTask; using FramerateMode = Services.Encode.Model.Models.FramerateMode; using OutputFormat = Services.Encode.Model.Models.OutputFormat; using VideoEncodeRateType = Model.Video.VideoEncodeRateType; using VideoLevel = Services.Encode.Model.Models.Video.VideoLevel; using VideoPreset = Services.Encode.Model.Models.Video.VideoPreset; using VideoProfile = Services.Encode.Model.Models.Video.VideoProfile; using VideoTune = Services.Encode.Model.Models.Video.VideoTune; public class VideoViewModel : ViewModelBase, IVideoViewModel { private const string SameAsSource = "Same as source"; private readonly IUserSettingService userSettingService; private bool displayOptimiseOptions; private int qualityMax; private int qualityMin; private bool showPeakFramerate; private int rf; private bool displayTurboAnalysisPass; private int videoPresetMaxValue; private int videoPresetValue; private VideoTune videoTune; private bool displayTuneControls; private bool displayLevelControl; private bool displayProfileControl; private Dictionary encoderOptions = new Dictionary(); public VideoViewModel(IUserSettingService userSettingService, IErrorService errorService) { this.Task = new EncodeTask { VideoEncoder = HandBrakeEncoderHelpers.VideoEncoders.First(s => s.IsX264) }; this.userSettingService = userSettingService; this.QualityMin = 0; this.QualityMax = 51; this.IsConstantQuantity = true; this.VideoEncoders = new BindingList(HandBrakeEncoderHelpers.VideoEncoders.ToList()); this.VideoProfiles = new BindingList(); this.VideoTunes = new BindingList(); this.VideoPresets = new BindingList(); this.VideoLevels = new BindingList(); } public event EventHandler TabStatusChanged; public IUserSettingService UserSettingService => this.userSettingService; public EncodeTask Task { get; set; } public IEnumerable Framerates { get { List framerates = new List { SameAsSource }; framerates.AddRange(HandBrakeEncoderHelpers.VideoFramerates.Select(item => item.Name)); return framerates; } } public bool IsConstantFramerate { get => this.Task.FramerateMode == FramerateMode.CFR; set { if (value) { this.Task.FramerateMode = FramerateMode.CFR; this.IsVariableFramerate = false; this.IsPeakFramerate = false; } this.NotifyOfPropertyChange(() => this.IsConstantFramerate); this.OnTabStatusChanged(null); } } public bool IsConstantQuantity { get => this.Task.VideoEncodeRateType == VideoEncodeRateType.ConstantQuality; set { if (value) { this.Task.VideoEncodeRateType = VideoEncodeRateType.ConstantQuality; this.VideoBitrate = null; this.NotifyOfPropertyChange(() => this.Task); } else { this.Task.VideoEncodeRateType = VideoEncodeRateType.AverageBitrate; } this.NotifyOfPropertyChange(() => this.IsConstantQuantity); this.NotifyOfPropertyChange(() => this.IsMultiPassEnabled); this.OnTabStatusChanged(null); } } public BindingList ColourRanges => new BindingList(EnumHelper.GetEnumList().ToList()); public bool IsMultiPassEnabled { get { return this.SelectedVideoEncoder.SupportsMultiPass(this.IsConstantQuantity); } } public bool? IsQualitySupported => this.SelectedVideoEncoder?.SupportsQuality; public bool? IsQualityAdjustmentSupported => this.SelectedVideoEncoder?.SupportsQualityAdjustment; public bool? IsBitrateSupported => this.SelectedVideoEncoder?.SupportsBitrate; public bool IsPeakFramerate { get => this.Task.FramerateMode == FramerateMode.PFR; set { if (value) { this.Task.FramerateMode = FramerateMode.PFR; this.IsVariableFramerate = false; this.IsConstantFramerate = false; } this.NotifyOfPropertyChange(() => this.IsPeakFramerate); this.OnTabStatusChanged(null); } } public bool IsVariableFramerate { get => this.Task.FramerateMode == FramerateMode.VFR; set { if (value) { this.IsPeakFramerate = false; this.IsConstantFramerate = false; this.Task.FramerateMode = FramerateMode.VFR; } this.NotifyOfPropertyChange(() => this.IsVariableFramerate); this.OnTabStatusChanged(null); } } public int QualityMax { get => this.qualityMax; set { if (!qualityMax.Equals(value)) { this.qualityMax = value; this.NotifyOfPropertyChange(() => this.QualityMax); } } } public int QualityMin { get => this.qualityMin; set { if (!qualityMin.Equals(value)) { this.qualityMin = value; this.NotifyOfPropertyChange(() => this.QualityMin); } } } public int RF { get => rf; set { this.rf = value; this.SetQualitySliderBounds(); this.Task.Quality = CalculateQualityValue(value); this.NotifyOfPropertyChange(() => this.RF); this.NotifyOfPropertyChange(() => this.DisplayRF); this.OnTabStatusChanged(new TabStatusEventArgs("filters", ChangedOption.Quality)); } } public int? VideoBitrate { get => this.Task.VideoBitrate; set { if (value == this.Task.VideoBitrate) { return; } this.Task.VideoBitrate = value; this.NotifyOfPropertyChange(() => this.VideoBitrate); this.OnTabStatusChanged(new TabStatusEventArgs("filters", ChangedOption.Bitrate)); } } public double DisplayRF { get => Task.Quality.HasValue ? this.Task.Quality.Value : 0; } public bool MultiPass { get => this.Task.MultiPass; set { this.Task.MultiPass = value; this.NotifyOfPropertyChange(() => this.MultiPass); this.OnTabStatusChanged(null); } } public bool TurboAnalysisPass { get => this.Task.TurboAnalysisPass; set { this.Task.TurboAnalysisPass = value; this.NotifyOfPropertyChange(() => this.TurboAnalysisPass); this.OnTabStatusChanged(null); } } public VideoColourRange ColourRange { get => this.Task.VideoColourRange; set { this.Task.VideoColourRange = value; this.NotifyOfPropertyChange(() => this.ColourRange); this.OnTabStatusChanged(null); } } public HDRDynamicMetadata PasshtruHDRDynamicMetadata { get => this.Task.PasshtruHDRDynamicMetadata; set { this.Task.PasshtruHDRDynamicMetadata = value; this.NotifyOfPropertyChange(() => this.PasshtruHDRDynamicMetadata); this.OnTabStatusChanged(null); } } public string Rfqp { get => HandBrakeEncoderHelpers.GetVideoQualityRateControlName(this.SelectedVideoEncoder?.ShortName); } public string HighQualityLabel { get => this.SelectedVideoEncoder.IsX264 ? Resources.Video_PlaceboQuality : Resources.Video_HigherQuality; } public string SelectedFramerate { get { if (this.Task.Framerate == null) { return "Same as source"; } return this.Task.Framerate.Value.ToString(CultureInfo.InvariantCulture); } set { if (value == "Same as source" || value == null) { this.Task.Framerate = null; this.ShowPeakFramerate = false; if (this.Task.FramerateMode == FramerateMode.PFR) { this.IsVariableFramerate = true; } } else if (!string.IsNullOrEmpty(value)) { this.ShowPeakFramerate = true; if (this.Task.FramerateMode == FramerateMode.VFR) { this.IsPeakFramerate = true; } this.Task.Framerate = double.Parse(value, CultureInfo.InvariantCulture); } this.NotifyOfPropertyChange(() => this.SelectedFramerate); this.NotifyOfPropertyChange(() => this.Task); this.OnTabStatusChanged(null); } } public HBVideoEncoder SelectedVideoEncoder { get => this.Task.VideoEncoder; set { if (!object.Equals(value, this.Task.VideoEncoder)) { // Cache the current extra args. We can set them back later if the user switches back if (this.Task.VideoEncoder != null) { this.encoderOptions[this.Task.VideoEncoder.ShortName] = this.ExtraArguments; } this.Task.VideoEncoder = value; this.NotifyOfPropertyChange(() => this.SelectedVideoEncoder); this.HandleEncoderChange(this.Task.VideoEncoder); this.HandleRFChange(); this.OnTabStatusChanged(null); this.OnTabStatusChanged(new TabStatusEventArgs("filters", ChangedOption.Encoder)); this.NotifyOfPropertyChange(() => this.IsQualitySupported); this.NotifyOfPropertyChange(() => this.IsQualityAdjustmentSupported); this.NotifyOfPropertyChange(() => this.IsBitrateSupported); } } } public bool ShowPeakFramerate { get => this.showPeakFramerate; set { this.showPeakFramerate = value; this.NotifyOfPropertyChange(() => this.ShowPeakFramerate); } } public IEnumerable VideoEncoders { get; set; } public string ExtraArguments { get => this.Task.ExtraAdvancedArguments; set { if (!Equals(this.Task.ExtraAdvancedArguments, value)) { this.Task.ExtraAdvancedArguments = value; this.NotifyOfPropertyChange(() => this.ExtraArguments); this.NotifyOfPropertyChange(() => FullOptionsTooltip); this.OnTabStatusChanged(null); } } } public bool DisplayOptimiseOptions { get => this.displayOptimiseOptions; set { this.displayOptimiseOptions = value; this.NotifyOfPropertyChange(() => this.DisplayOptimiseOptions); this.NotifyOfPropertyChange(() => FullOptionsTooltip); } } public bool DisplayMultiPass => this.SelectedVideoEncoder.SupportsMultiPass(); public bool DisplayTuneControls { get => this.displayTuneControls; set { if (value.Equals(this.displayTuneControls)) { return; } this.displayTuneControls = value; this.NotifyOfPropertyChange(() => this.DisplayTuneControls); } } public bool DisplayFastDecode { get; set; } public bool DisplayLevelControl { get => this.displayLevelControl; set { if (value.Equals(this.displayLevelControl)) { return; } this.displayLevelControl = value; this.NotifyOfPropertyChange(() => this.DisplayLevelControl); } } public bool DisplayProfileControl { get => this.displayProfileControl; set { if (value.Equals(this.displayProfileControl)) { return; } this.displayProfileControl = value; this.NotifyOfPropertyChange(() => this.DisplayProfileControl); } } public bool FastDecode { get => this.Task.VideoTunes.Contains(VideoTune.FastDecode); set { // Update the encode task if (value && !this.Task.VideoTunes.Contains(VideoTune.FastDecode)) { this.Task.VideoTunes.Add(VideoTune.FastDecode); } else { this.Task.VideoTunes.Remove(VideoTune.FastDecode); } this.NotifyOfPropertyChange(() => this.FastDecode); this.NotifyOfPropertyChange(() => this.FullOptionsTooltip); this.OnTabStatusChanged(null); } } public VideoPreset VideoPreset { get => this.Task.VideoPreset; set { this.Task.VideoPreset = value; this.NotifyOfPropertyChange(() => this.VideoPreset); this.NotifyOfPropertyChange(() => this.FullOptionsTooltip); this.OnTabStatusChanged(null); } } public int VideoPresetValue { get => this.videoPresetValue; set { this.videoPresetValue = value; if (this.SelectedVideoEncoder != null && this.SelectedVideoEncoder.Presets != null) { string preset = value >= 0 ? this.SelectedVideoEncoder.Presets[value] : null; this.VideoPreset = preset != null ? new VideoPreset(preset, preset) : this.VideoPresets.FirstOrDefault(); } this.NotifyOfPropertyChange(() => this.VideoPresetValue); } } public int VideoPresetMaxValue { get => this.videoPresetMaxValue; set { if (value == this.videoPresetMaxValue) { return; } this.videoPresetMaxValue = value; this.NotifyOfPropertyChange(() => this.VideoPresetMaxValue); } } public VideoTune VideoTune { get => this.videoTune; set { if (Equals(value, this.videoTune)) { return; } this.videoTune = value; // Update the encode task. bool hasFastDecode = this.Task.VideoTunes.Contains(VideoTune.FastDecode); this.Task.VideoTunes.Clear(); if (value != null && !Equals(value, VideoTune.None)) { this.Task.VideoTunes.Add(value); } if ((this.SelectedVideoEncoder.IsX264 || this.SelectedVideoEncoder.IsSVTAV1) && hasFastDecode) { this.Task.VideoTunes.Add(VideoTune.FastDecode); } this.NotifyOfPropertyChange(() => this.VideoTune); this.NotifyOfPropertyChange(() => this.FullOptionsTooltip); this.OnTabStatusChanged(null); } } public VideoProfile VideoProfile { get => this.Task.VideoProfile; set { this.Task.VideoProfile = value; this.NotifyOfPropertyChange(() => this.VideoProfile); this.NotifyOfPropertyChange(() => this.FullOptionsTooltip); this.OnTabStatusChanged(null); } } public VideoLevel VideoLevel { get => this.Task.VideoLevel; set { this.Task.VideoLevel = value; this.NotifyOfPropertyChange(() => this.VideoLevel); this.NotifyOfPropertyChange(() => this.FullOptionsTooltip); this.OnTabStatusChanged(null); } } public BindingList VideoPresets { get; set; } public BindingList VideoTunes { get; set; } public BindingList VideoProfiles { get; set; } public BindingList VideoLevels { get; set; } public string FullOptionsTooltip { get => this.SelectedVideoEncoder != null && this.SelectedVideoEncoder.IsX264 ? string.Format(Resources.Video_EncoderExtraArgs, this.GetActualx264Query()) : Resources.Video_EncoderExtraArgsTooltip; } public bool DisplayTurboAnalysisPass { get => this.displayTurboAnalysisPass; set { if (value.Equals(this.displayTurboAnalysisPass)) { return; } this.displayTurboAnalysisPass = value; this.NotifyOfPropertyChange(() => this.DisplayTurboAnalysisPass); } } public void SetSource(Source source, Title title, Preset preset, EncodeTask task) { this.Task = task; } public void SetPreset(Preset preset, EncodeTask task) { this.Task = task; if (preset == null || preset.Task == null) { return; } this.SelectedVideoEncoder = preset.Task.VideoEncoder; this.SelectedFramerate = preset.Task.Framerate.HasValue ? preset.Task.Framerate.Value.ToString(CultureInfo.InvariantCulture) : SameAsSource; this.IsConstantQuantity = preset.Task.VideoEncodeRateType == VideoEncodeRateType.ConstantQuality; switch (preset.Task.FramerateMode) { case FramerateMode.CFR: this.IsConstantFramerate = true; break; case FramerateMode.VFR: this.IsVariableFramerate = true; this.ShowPeakFramerate = false; break; case FramerateMode.PFR: this.IsPeakFramerate = true; this.ShowPeakFramerate = true; break; } this.MultiPass = preset.Task.MultiPass; this.TurboAnalysisPass = preset.Task.TurboAnalysisPass; this.VideoBitrate = preset.Task.VideoEncodeRateType == VideoEncodeRateType.AverageBitrate ? preset.Task.VideoBitrate : null; this.ColourRange = preset.Task.VideoColourRange; this.NotifyOfPropertyChange(() => this.Task); this.HandleEncoderChange(preset.Task.VideoEncoder); this.SetQualitySliderBounds(); this.SetRF(preset.Task.Quality); HBVideoEncoder encoder = preset.Task.VideoEncoder; if (encoder != null) { this.VideoLevel = encoder.Levels?.Count > 0 ? preset.Task.VideoLevel != null ? preset.Task.VideoLevel.Clone() : this.VideoLevels.FirstOrDefault() : null; this.VideoProfile = encoder.Profiles?.Count > 0 ? preset.Task.VideoProfile != null ? preset.Task.VideoProfile.Clone() : this.VideoProfiles.FirstOrDefault() : null; this.VideoPresetValue = encoder.Presets?.Count > 0 ? preset.Task.VideoPreset != null ? this.VideoPresets.IndexOf(preset.Task.VideoPreset) : 0 : 0; if (preset.Task.VideoEncoder.IsX265) { this.FastDecode = false; this.VideoTune = (preset.Task.VideoTunes != null && preset.Task.VideoTunes.Any() ? preset.Task.VideoTunes.FirstOrDefault() : this.VideoTunes.FirstOrDefault()) ?? VideoTune.None; } else { this.FastDecode = preset.Task.VideoTunes != null && preset.Task.VideoTunes.Contains(VideoTune.FastDecode); this.VideoTune = (preset.Task.VideoTunes != null && preset.Task.VideoTunes.Any() ? preset.Task.VideoTunes.FirstOrDefault(t => !Equals(t, VideoTune.FastDecode)) : this.VideoTunes.FirstOrDefault()) ?? VideoTune.None; } } this.ExtraArguments = preset.Task.ExtraAdvancedArguments; } public void UpdateTask(EncodeTask task) { this.Task = task; this.SetQualitySliderBounds(); this.SetRF(task.Quality); this.ShowPeakFramerate = this.IsPeakFramerate; this.NotifyOfPropertyChange(() => this.IsConstantFramerate); this.NotifyOfPropertyChange(() => this.IsConstantQuantity); this.NotifyOfPropertyChange(() => this.IsPeakFramerate); this.NotifyOfPropertyChange(() => this.IsVariableFramerate); this.NotifyOfPropertyChange(() => this.SelectedVideoEncoder); this.NotifyOfPropertyChange(() => this.SelectedFramerate); this.NotifyOfPropertyChange(() => this.ColourRange); this.NotifyOfPropertyChange(() => this.QualityMax); this.NotifyOfPropertyChange(() => this.QualityMin); this.NotifyOfPropertyChange(() => this.RF); this.NotifyOfPropertyChange(() => this.DisplayRF); this.NotifyOfPropertyChange(() => this.VideoBitrate); this.NotifyOfPropertyChange(() => this.Task.Quality); this.NotifyOfPropertyChange(() => this.Task.MultiPass); this.NotifyOfPropertyChange(() => this.Task.TurboAnalysisPass); this.NotifyOfPropertyChange(() => this.VideoTune); this.NotifyOfPropertyChange(() => this.VideoProfile); this.NotifyOfPropertyChange(() => this.VideoPreset); this.NotifyOfPropertyChange(() => this.VideoLevel); this.NotifyOfPropertyChange(() => this.FastDecode); this.NotifyOfPropertyChange(() => this.ExtraArguments); this.VideoTune = (task.VideoTunes != null && task.VideoTunes.Any() ? task.VideoTunes.FirstOrDefault(t => !Equals(t, VideoTune.FastDecode)) : this.VideoTunes.FirstOrDefault()) ?? VideoTune.None; if (this.SelectedVideoEncoder != null && this.VideoPreset != null) { int index = this.SelectedVideoEncoder.Presets.IndexOf(this.VideoPreset.ShortName); this.VideoPresetValue = index; } } public bool MatchesPreset(Preset preset) { if (preset.Task.VideoEncoder != this.Task.VideoEncoder) { return false; } if (preset.Task.Framerate != this.Task.Framerate) { return false; } if (preset.Task.FramerateMode != this.Task.FramerateMode) { return false; } if (preset.Task.VideoEncodeRateType != this.Task.VideoEncodeRateType) { return false; } if (preset.Task.VideoEncodeRateType == VideoEncodeRateType.AverageBitrate) { if (preset.Task.VideoBitrate != this.Task.VideoBitrate) { return false; } if (preset.Task.MultiPass != this.Task.MultiPass) { return false; } if (preset.Task.TurboAnalysisPass != this.Task.TurboAnalysisPass) { return false; } } else { if (preset.Task.Quality != this.Task.Quality) { return false; } } if (this.SelectedVideoEncoder != null) { if (this.SelectedVideoEncoder.Presets != null && this.SelectedVideoEncoder.Presets.Any()) { if (!Equals(preset.Task.VideoPreset, this.Task.VideoPreset)) { return false; } } if (this.SelectedVideoEncoder.Tunes != null && this.SelectedVideoEncoder.Tunes.Any()) { foreach (VideoTune taskVideoTune in preset.Task.VideoTunes) { if (!this.Task.VideoTunes.Contains(taskVideoTune)) { return false; } } foreach (VideoTune tune in preset.Task.VideoTunes) { if (!this.Task.VideoTunes.Contains(tune)) { return false; } } if (preset.Task.VideoTunes.Count != this.Task.VideoTunes.Count) { return false; } } if (this.SelectedVideoEncoder.Profiles != null && this.SelectedVideoEncoder.Profiles.Any()) { if (!Equals(preset.Task.VideoProfile, this.Task.VideoProfile)) { return false; } } if (this.SelectedVideoEncoder.Levels != null && this.SelectedVideoEncoder.Levels.Any()) { if (!Equals(preset.Task.VideoLevel, this.Task.VideoLevel)) { return false; } } } if (!Equals(preset.Task.ExtraAdvancedArguments, this.Task.ExtraAdvancedArguments)) { return false; } return true; } public void RefreshTask() { if (this.SelectedVideoEncoder != null && this.Task.OutputFormat == OutputFormat.Mp4 && !this.SelectedVideoEncoder.SupportsMP4) { this.SelectedVideoEncoder = HandBrakeEncoderHelpers.VideoEncoders.First(s => s.IsX264); } if (this.SelectedVideoEncoder != null && this.Task.OutputFormat == OutputFormat.WebM && !this.SelectedVideoEncoder.SupportsWebM) { this.SelectedVideoEncoder = HandBrakeEncoderHelpers.VideoEncoders.First(s => s.IsVP9); } this.NotifyOfPropertyChange(() => this.Task); } public void CopyQuery() { Clipboard.SetDataObject(this.SelectedVideoEncoder.IsX264 ? this.GetActualx264Query() : this.ExtraArguments); } protected virtual void OnTabStatusChanged(TabStatusEventArgs e) { this.TabStatusChanged?.Invoke(this, e); } private string GetActualx264Query() { if (!this.SelectedVideoEncoder.IsX264) { return string.Empty; } if (this.SelectedVideoEncoder == null || !this.SelectedVideoEncoder.Presets.Contains(this.VideoPreset?.ShortName)) { return string.Empty; } string preset = this.VideoPreset != null ? this.VideoPreset.ShortName : string.Empty; string profile = this.VideoProfile != null ? this.VideoProfile.ShortName : string.Empty; List tunes = new List(); if (this.VideoTune != null && this.VideoTune.ShortName != "none") { tunes.Add(this.VideoTune.ShortName); } if (this.FastDecode) { tunes.Add("fastdecode"); } // Get the width or height, default if we don't have it yet so we don't crash. int width = this.Task.Width.HasValue ? this.Task.Width.Value : 720; int height = this.Task.Height.HasValue ? this.Task.Height.Value : 576; if (height == 0) { height = 576; } if (width == 0) { width = 720; } try { return HandBrakeUtils.CreateX264OptionsString( preset, tunes, this.ExtraArguments, profile, this.VideoLevel != null ? this.VideoLevel.ShortName : string.Empty, width, height); } catch (Exception) { return "Error: Libhb not loaded."; } } private void SetRF(double? quality) { if (!quality.HasValue) { return; } VideoQualityLimits limits = HandBrakeEncoderHelpers.GetVideoQualityLimits(this.SelectedVideoEncoder?.ShortName); if (limits == null) { return; } double cqStep = 1; if (limits.Granularity != 1) { cqStep = this.userSettingService.GetUserSetting(UserSettingConstants.X264Step); cqStep = 1 / cqStep; // Inverse } if (limits.Ascending) { this.RF = (int)quality.Value; // Theora } else { if (limits.Low == 0) { this.RF = (int)(limits.High * cqStep) - (int)(quality * cqStep); } else // Supporting negative ranges { if (quality >= 0) { this.RF = (int)(limits.High * cqStep) - ((int)(quality * cqStep) - (int)(limits.Low * cqStep)); } else { int augment = limits.Low >= 0 ? 0 : (int)(limits.Low * cqStep) * -1; // Handle negative ranges this.RF = (int)(limits.High * cqStep) - augment + (int)(quality * cqStep * -1); } } } } private double? CalculateQualityValue(int sliderValue) { VideoQualityLimits limits = HandBrakeEncoderHelpers.GetVideoQualityLimits(this.SelectedVideoEncoder.ShortName); if (limits == null) { return null; } double cqStep = 1; if (limits.Granularity != 1) { cqStep = this.userSettingService.GetUserSetting(UserSettingConstants.X264Step); } if (limits.Ascending) // Theora { return sliderValue; } else // x264, x265, MPEG-4, MPEG-2, AV1, QuickSync { if (limits.Low > 0) { int invCqStep = (int)(1 / cqStep); // Inverse sliderValue -= (int)(limits.Low * invCqStep); // Handles the non 0 Starting point. MPEG-4, MPEG-2 } float augment = limits.Low > 0 ? 0 : (limits.Low * -1); // Handle negative ranges if (cqStep != 1) { return Math.Round(limits.High - (sliderValue * cqStep) - augment, 2); } else { return limits.High - sliderValue - augment; } } } private void SetQualitySliderBounds() { if (this.SelectedVideoEncoder == null) { return; } VideoQualityLimits limits = HandBrakeEncoderHelpers.GetVideoQualityLimits(this.SelectedVideoEncoder.ShortName); if (limits == null) { return; } double cqStep = 1; if (limits.Granularity != 1) { cqStep = this.userSettingService.GetUserSetting(UserSettingConstants.X264Step); } if (cqStep != 1) { this.QualityMin = (int)Math.Round(limits.Low / cqStep, 0); this.QualityMax = (int)Math.Round(limits.High / cqStep, 0); } else { this.QualityMin = (int)limits.Low; this.QualityMax = (int)limits.High; } } private void HandleEncoderChange(HBVideoEncoder selectedEncoder) { HBVideoEncoder encoder = selectedEncoder; if (encoder != null) { // Setup Profile this.VideoProfiles.Clear(); if (encoder.Profiles != null) { foreach (var item in encoder.Profiles) { this.VideoProfiles.Add(new VideoProfile(item, item)); } this.VideoProfile = this.VideoProfiles.FirstOrDefault(); } else { this.VideoProfile = null; } // Setup Tune this.VideoTunes.Clear(); if (encoder.Tunes != null) { foreach (var item in encoder.Tunes) { if (item == VideoTune.None.ShortName) { this.VideoTunes.Add(VideoTune.None); } else if (item == VideoTune.FastDecode.ShortName && (this.SelectedVideoEncoder.IsX264 || this.SelectedVideoEncoder.IsSVTAV1)) { continue; } else { this.VideoTunes.Add(new VideoTune(item, item)); } } this.FastDecode = false; this.VideoTune = this.VideoTunes.First(); } else { this.FastDecode = false; this.VideoTune = VideoTune.None; } // Setup Levels this.VideoLevels.Clear(); if (encoder.Levels != null) { foreach (var item in encoder.Levels) { this.VideoLevels.Add(new VideoLevel(item, item)); } this.VideoLevel = this.VideoLevels.FirstOrDefault(); } else { this.VideoLevel = VideoLevel.Auto; } // Setup Presets. this.VideoPresets.Clear(); if (encoder.Presets != null) { foreach (var item in encoder.Presets) { this.VideoPresets.Add(new VideoPreset(item, item)); } this.VideoPresetMaxValue = encoder.Presets.Count - 1; this.VideoPresetValue = GetDefaultEncoderPreset(selectedEncoder); } else { this.VideoPreset = null; } } // Update the Quality Slider. Make sure the bounds are up to date with the users settings. this.SetQualitySliderBounds(); // Update control display this.DisplayOptimiseOptions = encoder?.Presets?.Count > 0; this.DisplayTurboAnalysisPass = this.SelectedVideoEncoder.IsX264 || this.SelectedVideoEncoder.IsX265; this.DisplayTuneControls = encoder?.Tunes?.Count > 0; this.DisplayLevelControl = encoder?.Levels?.Count > 0; this.DisplayFastDecode = this.SelectedVideoEncoder.IsX264 || this.SelectedVideoEncoder.IsSVTAV1; this.NotifyOfPropertyChange(() => this.DisplayFastDecode); if (!this.DisplayFastDecode) { this.FastDecode = false; } this.DisplayProfileControl = encoder?.Profiles?.Count > 0; // Refresh Display this.NotifyOfPropertyChange(() => this.Rfqp); this.NotifyOfPropertyChange(() => this.HighQualityLabel); this.NotifyOfPropertyChange(() => this.IsMultiPassEnabled); this.NotifyOfPropertyChange(() => this.DisplayMultiPass); if (this.SelectedVideoEncoder != null && !this.SelectedVideoEncoder.SupportsMultiPass()) { this.MultiPass = false; this.TurboAnalysisPass = false; } // Cleanup Extra Arguments // Load the cached arguments. Saves the user from resetting when switching encoders. string result; this.ExtraArguments = this.encoderOptions.TryGetValue(selectedEncoder?.ShortName, out result) ? result : string.Empty; } private void HandleRFChange() { double displayRF = this.DisplayRF; if (displayRF > this.QualityMax || displayRF < this.QualityMin) { displayRF = this.qualityMax / 2; } this.SetRF(displayRF); } private int GetDefaultEncoderPreset(HBVideoEncoder selectedEncoder) { int defaultPreset = (int)Math.Round((decimal)(this.VideoPresetMaxValue / 2), 0); // Override for NVEnc if (selectedEncoder.IsNVEnc) { defaultPreset = this.VideoPresets.IndexOf(this.VideoPresets.FirstOrDefault(s => s.ShortName == "medium")); } // Override for QuickSync if (selectedEncoder.IsQuickSyncH265) { if (HandBrakeHardwareEncoderHelper.QsvHardwareGeneration > 6) { defaultPreset = this.VideoPresets.IndexOf(this.VideoPresets.FirstOrDefault(s => s.ShortName == "speed")); // TGL } else { defaultPreset = this.VideoPresets.IndexOf(this.VideoPresets.FirstOrDefault(s => s.ShortName == "balanced")); // ICL and Earlier. } } if (selectedEncoder.IsQuickSyncAV1) { defaultPreset = this.VideoPresets.IndexOf(this.VideoPresets.FirstOrDefault(s => s.ShortName == "speed")); // Alchemist and later } if (selectedEncoder.IsQuickSync || selectedEncoder.IsVCN) { defaultPreset = this.VideoPresets.IndexOf(this.VideoPresets.FirstOrDefault(s => s.ShortName == "balanced")); } return defaultPreset; } } }