mirror of https://github.com/HandBrake/HandBrake
1199 lines
42 KiB
C#
1199 lines
42 KiB
C#
// --------------------------------------------------------------------------------------------------------------------
|
|
// <copyright file="VideoViewModel.cs" company="HandBrake Project (https://handbrake.fr)">
|
|
// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License.
|
|
// </copyright>
|
|
// <summary>
|
|
// The Video View Model
|
|
// </summary>
|
|
// --------------------------------------------------------------------------------------------------------------------
|
|
|
|
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<string, string> encoderOptions = new Dictionary<string, string>();
|
|
|
|
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<HBVideoEncoder>(HandBrakeEncoderHelpers.VideoEncoders.ToList());
|
|
|
|
this.VideoProfiles = new BindingList<VideoProfile>();
|
|
this.VideoTunes = new BindingList<VideoTune>();
|
|
this.VideoPresets = new BindingList<VideoPreset>();
|
|
this.VideoLevels = new BindingList<VideoLevel>();
|
|
}
|
|
|
|
public event EventHandler<TabStatusEventArgs> TabStatusChanged;
|
|
|
|
public IUserSettingService UserSettingService => this.userSettingService;
|
|
|
|
public EncodeTask Task { get; set; }
|
|
|
|
public IEnumerable<string> Framerates
|
|
{
|
|
get
|
|
{
|
|
List<string> framerates = new List<string> { 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<VideoColourRange> ColourRanges => new BindingList<VideoColourRange>(EnumHelper<VideoColourRange>.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<HBVideoEncoder> 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<VideoPreset> VideoPresets { get; set; }
|
|
|
|
public BindingList<VideoTune> VideoTunes { get; set; }
|
|
|
|
public BindingList<VideoProfile> VideoProfiles { get; set; }
|
|
|
|
public BindingList<VideoLevel> 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<string> tunes = new List<string>();
|
|
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<double>(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<double>(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<double>(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;
|
|
}
|
|
}
|
|
} |