// -------------------------------------------------------------------------------------------------------------------- // // 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 System.Windows; using Caliburn.Micro; using HandBrake.Interop.Interop; using HandBrake.Interop.Interop.Interfaces.Model.Encoders; using HandBrakeWPF.EventArgs; using HandBrakeWPF.Properties; using HandBrakeWPF.Services.Interfaces; using HandBrakeWPF.Services.Presets.Model; using HandBrakeWPF.Services.Scan.Model; using HandBrakeWPF.Utilities; using HandBrakeWPF.ViewModels.Interfaces; using Clipboard = System.Windows.Clipboard; using EncodeTask = HandBrakeWPF.Services.Encode.Model.EncodeTask; using FramerateMode = HandBrakeWPF.Services.Encode.Model.Models.FramerateMode; using OutputFormat = HandBrakeWPF.Services.Encode.Model.Models.OutputFormat; using SettingChangedEventArgs = HandBrakeWPF.EventArgs.SettingChangedEventArgs; using VideoEncoder = HandBrakeWPF.Model.Video.VideoEncoder; using VideoEncodeRateType = HandBrakeWPF.Model.Video.VideoEncodeRateType; using VideoLevel = HandBrakeWPF.Services.Encode.Model.Models.Video.VideoLevel; using VideoPreset = HandBrakeWPF.Services.Encode.Model.Models.Video.VideoPreset; using VideoProfile = HandBrakeWPF.Services.Encode.Model.Models.Video.VideoProfile; using VideoTune = HandBrakeWPF.Services.Encode.Model.Models.Video.VideoTune; /// /// The Video View Model /// public class VideoViewModel : ViewModelBase, IVideoViewModel { /* * Hard Code "None" in the Models for Tune. * Test Everything */ #region Constants and Fields private const string SameAsSource = "Same as source"; private readonly IUserSettingService userSettingService; private readonly IErrorService errorService; private bool displayOptimiseOptions; private int qualityMax; private int qualityMin; private bool showPeakFramerate; private int rf; private bool displayTurboFirstPass; private int videoPresetMaxValue; private int videoPresetValue; private VideoTune videoTune; private bool fastDecode; private bool displayTuneControls; private bool displayLevelControl; private bool displayProfileControl; private Dictionary encoderOptions = new Dictionary(); #endregion #region Constructors and Destructors /// /// Initializes a new instance of the class. /// /// /// The user Setting Service. /// public VideoViewModel(IUserSettingService userSettingService, IErrorService errorService) { this.Task = new EncodeTask { VideoEncoder = VideoEncoder.X264 }; this.userSettingService = userSettingService; this.errorService = errorService; this.QualityMin = 0; this.QualityMax = 51; this.IsConstantQuantity = true; this.VideoEncoders = EnumHelper.GetEnumList(); this.VideoProfiles = new BindingList(); this.VideoTunes = new BindingList(); this.VideoPresets = new BindingList(); this.VideoLevels = new BindingList(); this.userSettingService.SettingChanged += this.UserSettingServiceSettingChanged; } #endregion public event EventHandler TabStatusChanged; #region Public Properties public IUserSettingService UserSettingService => this.userSettingService; /// /// Gets or sets the current Encode Task. /// public EncodeTask Task { get; set; } /// /// Gets Framerates. /// public IEnumerable Framerates { get { List framerates = new List { "Same as source" }; framerates.AddRange(HandBrakeEncoderHelpers.VideoFramerates.Select(item => item.Name)); return framerates; } } /// /// Gets or sets a value indicating whether IsConstantFramerate. /// public bool IsConstantFramerate { get { return 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); } } /// /// Gets or sets a value indicating whether IsConstantQuantity. /// public bool IsConstantQuantity { get { return this.Task.VideoEncodeRateType == VideoEncodeRateType.ConstantQuality; } set { if (value) { this.Task.VideoEncodeRateType = VideoEncodeRateType.ConstantQuality; this.TwoPass = false; this.TurboFirstPass = false; this.VideoBitrate = null; this.NotifyOfPropertyChange(() => this.Task); } else { this.Task.VideoEncodeRateType = VideoEncodeRateType.AverageBitrate; } this.NotifyOfPropertyChange(() => this.IsConstantQuantity); this.NotifyOfPropertyChange(() => this.IsTwoPassEnabled); this.OnTabStatusChanged(null); } } public bool IsTwoPassEnabled { get { if (this.IsConstantQuantity) { return false; } if (this.SelectedVideoEncoder == VideoEncoder.NvencH264 || this.SelectedVideoEncoder == VideoEncoder.NvencH265 || this.SelectedVideoEncoder == VideoEncoder.VceH264 || this.SelectedVideoEncoder == VideoEncoder.VceH265 || this.SelectedVideoEncoder == VideoEncoder.QuickSync || this.SelectedVideoEncoder == VideoEncoder.QuickSyncH265 || this.SelectedVideoEncoder == VideoEncoder.QuickSyncH26510b) { return false; } return true; } } /// /// Gets or sets a value indicating whether IsPeakFramerate. /// public bool IsPeakFramerate { get { return 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); } } /// /// Gets or sets a value indicating whether IsVariableFramerate. /// public bool IsVariableFramerate { get { return 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); } } /// /// Gets a value indicating whether is lossless. /// public bool IsLossless { get { return 0.0.Equals(this.DisplayRF) && (this.SelectedVideoEncoder == VideoEncoder.X264 || this.SelectedVideoEncoder == VideoEncoder.X264_10); } } /// /// Gets or sets QualityMax. /// public int QualityMax { get { return this.qualityMax; } set { if (!qualityMax.Equals(value)) { this.qualityMax = value; this.NotifyOfPropertyChange(() => this.QualityMax); } } } /// /// Gets or sets QualityMin. /// public int QualityMin { get { return this.qualityMin; } set { if (!qualityMin.Equals(value)) { this.qualityMin = value; this.NotifyOfPropertyChange(() => this.QualityMin); } } } /// /// Gets or sets RF. /// public int RF { get { return rf; } set { this.rf = value; this.SetQualitySliderBounds(); switch (this.SelectedVideoEncoder) { case VideoEncoder.FFMpeg: case VideoEncoder.FFMpeg2: this.Task.Quality = (32 - value); break; case VideoEncoder.VP8: case VideoEncoder.VP9: this.Task.Quality = (63 - value); break; case VideoEncoder.X264: case VideoEncoder.X264_10: case VideoEncoder.X265: case VideoEncoder.X265_10: case VideoEncoder.X265_12: double cqStep = userSettingService.GetUserSetting(UserSettingConstants.X264Step); double rfValue = 51.0 - (value * cqStep); rfValue = Math.Round(rfValue, 2); this.Task.Quality = rfValue; break; case VideoEncoder.QuickSync: case VideoEncoder.QuickSyncH265: case VideoEncoder.VceH264: case VideoEncoder.VceH265: case VideoEncoder.NvencH264: case VideoEncoder.NvencH265: rfValue = 51.0 - value; rfValue = Math.Round(rfValue, 0); this.Task.Quality = rfValue; break; case VideoEncoder.QuickSyncH26510b: rfValue = 63.0 - (value - 0); rfValue = Math.Round(rfValue, 0); this.Task.Quality = rfValue; break; case VideoEncoder.Theora: Task.Quality = value; break; } this.NotifyOfPropertyChange(() => this.RF); this.NotifyOfPropertyChange(() => this.DisplayRF); this.NotifyOfPropertyChange(() => this.IsLossless); this.OnTabStatusChanged(null); } } /// /// Gets or sets the Video Bitrate. /// public int? VideoBitrate { get { return this.Task.VideoBitrate; } set { if (value == this.Task.VideoBitrate) { return; } this.Task.VideoBitrate = value; this.NotifyOfPropertyChange(() => this.VideoBitrate); this.OnTabStatusChanged(null); } } /// /// Gets DisplayRF. /// public double DisplayRF { get { return Task.Quality.HasValue ? this.Task.Quality.Value : 0; } } /// /// Gets or sets a value indicating whether two pass. /// public bool TwoPass { get { return this.Task.TwoPass; } set { this.Task.TwoPass = value; this.NotifyOfPropertyChange(() => this.TwoPass); this.OnTabStatusChanged(null); } } /// /// Gets or sets a value indicating whether turbo first pass. /// public bool TurboFirstPass { get { return this.Task.TurboFirstPass; } set { this.Task.TurboFirstPass = value; this.NotifyOfPropertyChange(() => this.TurboFirstPass); this.OnTabStatusChanged(null); } } /// /// Gets the rfqp. /// public string Rfqp { get { if (this.SelectedVideoEncoder == VideoEncoder.X264 || this.SelectedVideoEncoder == VideoEncoder.X264_10 || this.SelectedVideoEncoder == VideoEncoder.X265 || this.SelectedVideoEncoder == VideoEncoder.X265_10 || this.SelectedVideoEncoder == VideoEncoder.X265_12) { return "RF"; } if (this.SelectedVideoEncoder == VideoEncoder.NvencH264 || this.SelectedVideoEncoder == VideoEncoder.NvencH265) { return string.Empty; } return "QP"; } } /// /// Gets the high quality label. /// public string HighQualityLabel { get { return this.SelectedVideoEncoder == VideoEncoder.X264 || this.SelectedVideoEncoder == VideoEncoder.X264_10 ? Resources.Video_PlaceboQuality : Resources.Video_HigherQuality; } } /// /// Gets or sets SelectedFramerate. /// 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); } } /// /// Gets or sets SelectedVideoEncoder. /// public VideoEncoder SelectedVideoEncoder { get { return 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 this.encoderOptions[EnumHelper.GetShortName(this.Task.VideoEncoder)] = this.ExtraArguments; this.Task.VideoEncoder = value; this.NotifyOfPropertyChange(() => this.SelectedVideoEncoder); this.HandleEncoderChange(this.Task.VideoEncoder); this.HandleRFChange(); this.OnTabStatusChanged(null); } } } /// /// Gets or sets a value indicating whether ShowPeakFramerate. /// public bool ShowPeakFramerate { get { return this.showPeakFramerate; } set { this.showPeakFramerate = value; this.NotifyOfPropertyChange(() => this.ShowPeakFramerate); } } /// /// Gets or sets VideoEncoders. /// public IEnumerable VideoEncoders { get; set; } /// /// Gets or sets the extra arguments. /// public string ExtraArguments { get { return this.Task.ExtraAdvancedArguments; } set { if (!Equals(this.Task.ExtraAdvancedArguments, value)) { this.Task.ExtraAdvancedArguments = value; this.NotifyOfPropertyChange(() => this.ExtraArguments); this.NotifyOfPropertyChange(() => FullOptionsTooltip); this.OnTabStatusChanged(null); } } } /// /// Gets or sets a value indicating whether to display H264 /// public bool DisplayOptimiseOptions { get { return this.displayOptimiseOptions; } set { this.displayOptimiseOptions = value; this.NotifyOfPropertyChange(() => this.DisplayOptimiseOptions); this.NotifyOfPropertyChange(() => FullOptionsTooltip); } } /// /// Gets or sets a value indicating whether display non qsv controls. /// public bool DisplayTwoPass { get { return this.SelectedVideoEncoder != VideoEncoder.QuickSync && this.SelectedVideoEncoder != VideoEncoder.QuickSyncH265 && this.SelectedVideoEncoder != VideoEncoder.QuickSyncH26510b && this.SelectedVideoEncoder != VideoEncoder.NvencH264 && this.SelectedVideoEncoder != VideoEncoder.NvencH265; } } /// /// Gets or sets a value indicating whether display tune controls. /// public bool DisplayTuneControls { get { return this.displayTuneControls; } set { if (value.Equals(this.displayTuneControls)) { return; } this.displayTuneControls = value; this.NotifyOfPropertyChange(() => this.DisplayTuneControls); } } public bool DisplayFastDecode { get; set; } /// /// Gets or sets a value indicating whether display level control. /// public bool DisplayLevelControl { get { return this.displayLevelControl; } set { if (value.Equals(this.displayLevelControl)) { return; } this.displayLevelControl = value; this.NotifyOfPropertyChange(() => this.DisplayLevelControl); } } /// /// Gets or sets a value indicating whether the profile control is displayed. /// public bool DisplayProfileControl { get { return this.displayProfileControl; } set { if (value.Equals(this.displayProfileControl)) { return; } this.displayProfileControl = value; this.NotifyOfPropertyChange(() => this.DisplayProfileControl); } } /// /// Gets or sets a value indicating whether fast decode. /// public bool FastDecode { get { return this.Task.VideoTunes.Contains(VideoTune.FastDecode); } set { this.fastDecode = value; // 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); } } /// /// Gets or sets the video preset. /// public VideoPreset VideoPreset { get { return this.Task.VideoPreset; } set { this.Task.VideoPreset = value; this.NotifyOfPropertyChange(() => this.VideoPreset); this.NotifyOfPropertyChange(() => this.FullOptionsTooltip); this.OnTabStatusChanged(null); } } /// /// Gets or sets the video preset value. /// public int VideoPresetValue { get { return this.videoPresetValue; } set { this.videoPresetValue = value; HBVideoEncoder encoder = HandBrakeEncoderHelpers.VideoEncoders.FirstOrDefault(s => s.ShortName == EnumHelper.GetShortName(this.SelectedVideoEncoder)); if (encoder != null) { string preset = value >= 0 ? encoder.Presets[value] : null; this.VideoPreset = preset != null ? new VideoPreset(preset, preset) : this.VideoPresets.FirstOrDefault(); } this.NotifyOfPropertyChange(() => this.VideoPresetValue); } } /// /// Gets or sets the video preset max value. /// public int VideoPresetMaxValue { get { return this.videoPresetMaxValue; } set { if (value == this.videoPresetMaxValue) { return; } this.videoPresetMaxValue = value; this.NotifyOfPropertyChange(() => this.VideoPresetMaxValue); } } /// /// Gets or sets the video tune. /// public VideoTune VideoTune { get { return 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 == VideoEncoder.X264 || this.SelectedVideoEncoder == VideoEncoder.X264_10) && hasFastDecode) { this.Task.VideoTunes.Add(VideoTune.FastDecode); } this.NotifyOfPropertyChange(() => this.VideoTune); this.NotifyOfPropertyChange(() => this.FullOptionsTooltip); this.OnTabStatusChanged(null); } } /// /// Gets or sets the video profile. /// public VideoProfile VideoProfile { get { return this.Task.VideoProfile; } set { this.Task.VideoProfile = value; this.NotifyOfPropertyChange(() => this.VideoProfile); this.NotifyOfPropertyChange(() => this.FullOptionsTooltip); this.OnTabStatusChanged(null); } } /// /// Gets or sets the video level. /// public VideoLevel VideoLevel { get { return this.Task.VideoLevel; } set { this.Task.VideoLevel = value; this.NotifyOfPropertyChange(() => this.VideoLevel); this.NotifyOfPropertyChange(() => this.FullOptionsTooltip); this.OnTabStatusChanged(null); } } /// /// Gets or sets the video presets. /// public BindingList VideoPresets { get; set; } /// /// Gets or sets the video tunes. /// public BindingList VideoTunes { get; set; } /// /// Gets or sets the video profiles. /// public BindingList VideoProfiles { get; set; } /// /// Gets or sets the video levels. /// public BindingList VideoLevels { get; set; } /// /// Gets the full options tooltip. /// public string FullOptionsTooltip { get { return this.SelectedVideoEncoder == VideoEncoder.X264 || this.SelectedVideoEncoder == VideoEncoder.X264_10 ? string.Format(Resources.Video_EncoderExtraArgs, this.GetActualx264Query()) : Resources.Video_EncoderExtraArgsTooltip; } } /// /// Gets or sets a value indicating whether display turbo first pass. /// public bool DisplayTurboFirstPass { get { return this.displayTurboFirstPass; } set { if (value.Equals(this.displayTurboFirstPass)) { return; } this.displayTurboFirstPass = value; this.NotifyOfPropertyChange(() => this.DisplayTurboFirstPass); } } #endregion #region Public Methods /// /// Setup this window for a new source /// /// /// The source. /// /// /// The title. /// /// /// The preset. /// /// /// The task. /// public void SetSource(Source source, Title title, Preset preset, EncodeTask task) { this.Task = task; } /// /// Setup this tab for the specified preset. /// /// /// The preset. /// /// /// The 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.TwoPass = preset.Task.TwoPass; this.TurboFirstPass = preset.Task.TurboFirstPass; this.VideoBitrate = preset.Task.VideoEncodeRateType == VideoEncodeRateType.AverageBitrate ? preset.Task.VideoBitrate : null; this.NotifyOfPropertyChange(() => this.Task); this.HandleEncoderChange(preset.Task.VideoEncoder); this.SetQualitySliderBounds(); this.SetRF(preset.Task.Quality); HBVideoEncoder encoder = HandBrakeEncoderHelpers.VideoEncoders.FirstOrDefault(s => s.ShortName == EnumHelper.GetShortName(preset.Task.VideoEncoder)); if (encoder != null) { if (preset.Task.VideoEncoder == VideoEncoder.X264 || preset.Task.VideoEncoder == VideoEncoder.X264_10 || preset.Task.VideoEncoder == VideoEncoder.X265 || preset.Task.VideoEncoder == VideoEncoder.X265_10 || preset.Task.VideoEncoder == VideoEncoder.X265_12 || preset.Task.VideoEncoder == VideoEncoder.QuickSync || preset.Task.VideoEncoder == VideoEncoder.QuickSyncH265 || preset.Task.VideoEncoder == VideoEncoder.QuickSyncH26510b || preset.Task.VideoEncoder == VideoEncoder.VceH264 || preset.Task.VideoEncoder == VideoEncoder.VceH265 || preset.Task.VideoEncoder == VideoEncoder.NvencH264 || preset.Task.VideoEncoder == VideoEncoder.NvencH265) { this.VideoLevel = preset.Task.VideoLevel != null ? preset.Task.VideoLevel.Clone() : this.VideoLevels.FirstOrDefault(); this.VideoProfile = preset.Task.VideoProfile != null ? preset.Task.VideoProfile.Clone() : this.VideoProfiles.FirstOrDefault(); this.VideoPresetValue = preset.Task.VideoPreset != null ? this.VideoPresets.IndexOf(preset.Task.VideoPreset) : 0; if (preset.Task.VideoEncoder == VideoEncoder.X265 || preset.Task.VideoEncoder == VideoEncoder.X265_10 || preset.Task.VideoEncoder == VideoEncoder.X265_12) { 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; } /// /// Update all the UI controls based on the encode task passed in. /// /// /// The task. /// 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.QualityMax); this.NotifyOfPropertyChange(() => this.QualityMin); this.NotifyOfPropertyChange(() => this.RF); this.NotifyOfPropertyChange(() => this.DisplayRF); this.NotifyOfPropertyChange(() => this.IsLossless); this.NotifyOfPropertyChange(() => this.VideoBitrate); this.NotifyOfPropertyChange(() => this.Task.Quality); this.NotifyOfPropertyChange(() => this.Task.TwoPass); this.NotifyOfPropertyChange(() => this.Task.TurboFirstPass); 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; HBVideoEncoder encoder = HandBrakeEncoderHelpers.VideoEncoders.FirstOrDefault(s => s.ShortName == EnumHelper.GetShortName(this.SelectedVideoEncoder)); if (encoder != null && this.VideoPreset != null) { int index = encoder.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.TwoPass != this.Task.TwoPass) { return false; } if (preset.Task.TurboFirstPass != this.Task.TurboFirstPass) { return false; } } else { if (preset.Task.Quality != this.Task.Quality) { return false; } } if (this.Task.VideoEncoder == VideoEncoder.X264 || this.Task.VideoEncoder == VideoEncoder.X264_10 || this.Task.VideoEncoder == VideoEncoder.X265 || this.Task.VideoEncoder == VideoEncoder.X265_10 || this.Task.VideoEncoder == VideoEncoder.X265_12 || this.Task.VideoEncoder == VideoEncoder.QuickSync || this.Task.VideoEncoder == VideoEncoder.QuickSyncH265 || this.Task.VideoEncoder == VideoEncoder.QuickSyncH26510b || this.Task.VideoEncoder == VideoEncoder.VceH264 || this.Task.VideoEncoder == VideoEncoder.VceH265 || this.Task.VideoEncoder == VideoEncoder.NvencH264 || this.Task.VideoEncoder == VideoEncoder.NvencH265) { if (!Equals(preset.Task.VideoPreset, this.Task.VideoPreset)) { return false; } 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 (!Equals(preset.Task.VideoProfile, this.Task.VideoProfile)) { return false; } if (!Equals(preset.Task.VideoLevel, this.Task.VideoLevel)) { return false; } } if (!Equals(preset.Task.ExtraAdvancedArguments, this.Task.ExtraAdvancedArguments)) { return false; } return true; } /// /// Trigger a Notify Property Changed on the Task to force various UI elements to update. /// public void RefreshTask() { this.NotifyOfPropertyChange(() => this.Task); VideoEncoder[] allowableWebmEncoders = { VideoEncoder.VP8, VideoEncoder.VP9 }; if ((Task.OutputFormat == OutputFormat.Mp4) && (this.SelectedVideoEncoder == VideoEncoder.Theora || allowableWebmEncoders.Contains(this.SelectedVideoEncoder))) { this.SelectedVideoEncoder = VideoEncoder.X264; } if ((Task.OutputFormat == OutputFormat.WebM) && !allowableWebmEncoders.Contains(this.SelectedVideoEncoder)) { this.SelectedVideoEncoder = VideoEncoder.VP8; } } /// /// The copy query. /// public void CopyQuery() { Clipboard.SetDataObject(this.SelectedVideoEncoder == VideoEncoder.X264 || this.SelectedVideoEncoder == VideoEncoder.X264_10 ? this.GetActualx264Query() : this.ExtraArguments); } #endregion protected virtual void OnTabStatusChanged(TabStatusEventArgs e) { this.TabStatusChanged?.Invoke(this, e); } /// /// Set the bounds of the Constant Quality Slider /// private void SetQualitySliderBounds() { // Note Updating bounds to the same values won't trigger an update. // The properties are smart enough to not take in equal values. switch (this.SelectedVideoEncoder) { case VideoEncoder.FFMpeg: case VideoEncoder.FFMpeg2: this.QualityMin = 1; this.QualityMax = 31; break; case VideoEncoder.QuickSync: case VideoEncoder.QuickSyncH265: case VideoEncoder.VceH264: case VideoEncoder.VceH265: case VideoEncoder.NvencH264: case VideoEncoder.NvencH265: this.QualityMin = 0; this.QualityMax = 51; break; case VideoEncoder.QuickSyncH26510b: this.QualityMin = 0; this.QualityMax = 63; break; case VideoEncoder.X264: case VideoEncoder.X264_10: case VideoEncoder.X265: case VideoEncoder.X265_10: case VideoEncoder.X265_12: this.QualityMin = 0; this.QualityMax = (int)(51 / userSettingService.GetUserSetting(UserSettingConstants.X264Step)); break; case VideoEncoder.Theora: case VideoEncoder.VP8: case VideoEncoder.VP9: this.QualityMin = 0; this.QualityMax = 63; break; } } /// /// The get actualx 264 query. /// /// /// The . /// private string GetActualx264Query() { VideoEncoder encoder = this.SelectedVideoEncoder; if (encoder != VideoEncoder.X264 && encoder != VideoEncoder.X264_10) { return string.Empty; } HBVideoEncoder hbEncoder = HandBrakeEncoderHelpers.VideoEncoders.FirstOrDefault(s => s.ShortName == EnumHelper.GetShortName(encoder)); if (hbEncoder == null || !hbEncoder.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."; } } /// /// The user setting service_ setting changed. /// /// /// The sender. /// /// /// The e. /// private void UserSettingServiceSettingChanged(object sender, SettingChangedEventArgs e) { if (e.Key == UserSettingConstants.EnableVceEncoder || e.Key == UserSettingConstants.EnableNvencEncoder || e.Key == UserSettingConstants.EnableQuickSyncEncoding) { this.NotifyOfPropertyChange(() => this.VideoEncoders); } } /// /// The set rf. /// /// /// The quality. /// private void SetRF(double? quality) { double cqStep = this.userSettingService.GetUserSetting(UserSettingConstants.X264Step); double rfValue = 0; switch (this.SelectedVideoEncoder) { case VideoEncoder.FFMpeg: case VideoEncoder.FFMpeg2: if (quality.HasValue) { int cq; int.TryParse(quality.Value.ToString(CultureInfo.InvariantCulture), out cq); this.RF = 32 - cq; } break; case VideoEncoder.VP8: case VideoEncoder.VP9: if (quality.HasValue) { int cq; int.TryParse(quality.Value.ToString(CultureInfo.InvariantCulture), out cq); this.RF = 63 - cq; } break; case VideoEncoder.X265: case VideoEncoder.X265_10: case VideoEncoder.X265_12: case VideoEncoder.X264: case VideoEncoder.X264_10: case VideoEncoder.QuickSync: case VideoEncoder.QuickSyncH265: case VideoEncoder.QuickSyncH26510b: case VideoEncoder.VceH264: case VideoEncoder.VceH265: case VideoEncoder.NvencH264: case VideoEncoder.NvencH265: if (this.SelectedVideoEncoder == VideoEncoder.QuickSync || this.SelectedVideoEncoder == VideoEncoder.QuickSyncH265 || this.SelectedVideoEncoder == VideoEncoder.QuickSyncH26510b || this.SelectedVideoEncoder == VideoEncoder.VceH264 || this.SelectedVideoEncoder == VideoEncoder.VceH265 || this.SelectedVideoEncoder == VideoEncoder.NvencH264 || this.SelectedVideoEncoder == VideoEncoder.NvencH265) { cqStep = 1; } double multiplier = 1.0 / cqStep; if (quality.HasValue) { rfValue = quality.Value * multiplier; } this.RF = this.QualityMax - (int)Math.Round(rfValue, 0); break; case VideoEncoder.Theora: if (quality.HasValue) { this.RF = (int)quality.Value; } break; } } /// /// The handle encoder change. /// /// /// The selected encoder. /// private void HandleEncoderChange(VideoEncoder selectedEncoder) { HBVideoEncoder encoder = HandBrakeEncoderHelpers.VideoEncoders.FirstOrDefault(s => s.ShortName == EnumHelper.GetShortName(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) { this.VideoTunes.Add(VideoTune.None); foreach (var item in encoder.Tunes) { if (item == VideoTune.FastDecode.ShortName && (selectedEncoder == VideoEncoder.X264 || selectedEncoder == VideoEncoder.X264_10)) { continue; } this.VideoTunes.Add(new VideoTune(item, item)); } this.FastDecode = false; this.VideoTune = VideoTune.None; } 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 = this.SelectedVideoEncoder == VideoEncoder.X264 || this.SelectedVideoEncoder == VideoEncoder.X264_10 || this.SelectedVideoEncoder == VideoEncoder.X265 || this.SelectedVideoEncoder == VideoEncoder.X265_10 || this.SelectedVideoEncoder == VideoEncoder.X265_12 || this.SelectedVideoEncoder == VideoEncoder.QuickSync || this.SelectedVideoEncoder == VideoEncoder.QuickSyncH265 || this.SelectedVideoEncoder == VideoEncoder.QuickSyncH26510b || this.SelectedVideoEncoder == VideoEncoder.VceH264 || this.SelectedVideoEncoder == VideoEncoder.VceH265 || this.SelectedVideoEncoder == VideoEncoder.NvencH264 || this.SelectedVideoEncoder == VideoEncoder.NvencH265 || this.SelectedVideoEncoder == VideoEncoder.VP8 || this.SelectedVideoEncoder == VideoEncoder.VP9; this.DisplayTurboFirstPass = selectedEncoder == VideoEncoder.X264 || selectedEncoder == VideoEncoder.X264_10 || selectedEncoder == VideoEncoder.X265 || selectedEncoder == VideoEncoder.X265_10 || selectedEncoder == VideoEncoder.X265_12; this.DisplayTuneControls = this.SelectedVideoEncoder == VideoEncoder.X264 || this.SelectedVideoEncoder == VideoEncoder.X264_10 || this.SelectedVideoEncoder == VideoEncoder.X265 || this.SelectedVideoEncoder == VideoEncoder.X265_10 || this.SelectedVideoEncoder == VideoEncoder.X265_12; this.DisplayLevelControl = this.SelectedVideoEncoder == VideoEncoder.X264 || this.SelectedVideoEncoder == VideoEncoder.X264_10 || this.SelectedVideoEncoder == VideoEncoder.X265 || this.SelectedVideoEncoder == VideoEncoder.X265_10 || this.SelectedVideoEncoder == VideoEncoder.X265_12 || this.SelectedVideoEncoder == VideoEncoder.QuickSync || this.SelectedVideoEncoder == VideoEncoder.QuickSyncH265 || this.SelectedVideoEncoder == VideoEncoder.QuickSyncH26510b || this.SelectedVideoEncoder == VideoEncoder.VceH264 || this.SelectedVideoEncoder == VideoEncoder.VceH265 || this.SelectedVideoEncoder == VideoEncoder.NvencH264 || this.SelectedVideoEncoder == VideoEncoder.NvencH265; this.DisplayFastDecode = this.SelectedVideoEncoder == VideoEncoder.X264 || this.SelectedVideoEncoder == VideoEncoder.X264_10; this.NotifyOfPropertyChange(() => this.DisplayFastDecode); if (!this.DisplayFastDecode) { this.FastDecode = false; } this.DisplayProfileControl = this.SelectedVideoEncoder == VideoEncoder.X264 || this.SelectedVideoEncoder == VideoEncoder.X264_10 || this.SelectedVideoEncoder == VideoEncoder.X265 || this.SelectedVideoEncoder == VideoEncoder.X265_10 || this.SelectedVideoEncoder == VideoEncoder.X265_12 || this.SelectedVideoEncoder == VideoEncoder.QuickSync || this.SelectedVideoEncoder == VideoEncoder.QuickSyncH265 || this.SelectedVideoEncoder == VideoEncoder.QuickSyncH26510b || this.SelectedVideoEncoder == VideoEncoder.VceH264 || this.SelectedVideoEncoder == VideoEncoder.VceH265 || this.SelectedVideoEncoder == VideoEncoder.NvencH264 || this.SelectedVideoEncoder == VideoEncoder.NvencH265; // Refresh Display this.NotifyOfPropertyChange(() => this.Rfqp); this.NotifyOfPropertyChange(() => this.HighQualityLabel); this.NotifyOfPropertyChange(() => this.IsTwoPassEnabled); this.NotifyOfPropertyChange(() => this.DisplayTwoPass); // Handle some quicksync specific options. if (selectedEncoder == VideoEncoder.QuickSync || selectedEncoder == VideoEncoder.QuickSyncH265 || selectedEncoder == VideoEncoder.QuickSyncH26510b) { this.TwoPass = false; this.TurboFirstPass = false; this.SelectedFramerate = null; } if (selectedEncoder == VideoEncoder.NvencH264 || selectedEncoder == VideoEncoder.NvencH265 || selectedEncoder == VideoEncoder.VceH264 || selectedEncoder == VideoEncoder.VceH265) { this.TwoPass = false; this.TurboFirstPass = false; } // Cleanup Extra Arguments // Load the cached arguments. Saves the user from resetting when switching encoders. string result; this.ExtraArguments = this.encoderOptions.TryGetValue(EnumHelper.GetShortName(selectedEncoder), 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(VideoEncoder selectedEncoder) { int defaultPreset = (int)Math.Round((decimal)(this.VideoPresetMaxValue / 2), 0); // Override for NVEnc if (selectedEncoder == VideoEncoder.NvencH264 || selectedEncoder == VideoEncoder.NvencH265) { // TODO -> is the RTX good enough to default to a more balanced preset? defaultPreset = this.VideoPresets.IndexOf(this.VideoPresets.FirstOrDefault(s => s.ShortName == "slow")); } // Override for QuickSync if (selectedEncoder == VideoEncoder.QuickSyncH265 || selectedEncoder == VideoEncoder.QuickSyncH26510b) { if (HandBrake.Interop.Utilities.SystemInfo.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 == VideoEncoder.QuickSync) { // h264 encoder hasn't changed much in recent years, so stick to balanced. defaultPreset = this.VideoPresets.IndexOf(this.VideoPresets.FirstOrDefault(s => s.ShortName == "balanced")); } // Override for VCE if (selectedEncoder == VideoEncoder.VceH264 || selectedEncoder == VideoEncoder.VceH265) { // TODO Would be good to have VCE version detection. defaultPreset = this.VideoPresets.IndexOf(this.VideoPresets.FirstOrDefault(s => s.ShortName == "balanced")); } return defaultPreset; } } }