// -------------------------------------------------------------------------------------------------------------------- // // 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 Caliburn.Micro; using HandBrake.ApplicationServices.Model; using HandBrake.ApplicationServices.Model.Encoding; using HandBrake.ApplicationServices.Parsing; using HandBrake.ApplicationServices.Services.Interfaces; using HandBrake.ApplicationServices.Utilities; using HandBrake.Interop; using HandBrake.Interop.Model.Encoding; using HandBrake.Interop.Model.Encoding.x264; using HandBrakeWPF.Commands.Interfaces; using HandBrakeWPF.Model; using HandBrakeWPF.Properties; using HandBrakeWPF.ViewModels.Interfaces; /// /// The Video View Model /// public class VideoViewModel : ViewModelBase, IVideoViewModel { #region Constants and Fields /// /// Same as source constant. /// private const string SameAsSource = "Same as source"; /// /// The possible h264 levels. /// private static readonly List Levels = new List { "Auto", "1.0", "1b", "1.1", "1.2", "1.3", "2.0", "2.1", "2.2", "3.0", "3.1", "3.2", "4.0", "4.1", "4.2", "5.0", "5.1", "5.2"}; /// /// Backing field for the user setting service. /// private readonly IUserSettingService userSettingService; /// /// The advanced encoder options command /// private readonly IAdvancedEncoderOptionsCommand advancedEncoderOptionsCommand; /// /// Backing field used to display / hide the x264 options /// private bool displayX264Options; /// /// The quality max. /// private int qualityMax; /// /// The quality min. /// private int qualityMin; /// /// The show peak framerate. /// private bool showPeakFramerate; /// /// The show peak framerate. /// private int rf; /// /// The x264 preset value. /// private int x264PresetValue; /// /// The extra arguments. /// private string extraArguments; /// /// The can clear tracker. /// private bool canClear; /// /// The use advanced tab. /// private bool useAdvancedTab; #endregion #region Constructors and Destructors /// /// Initializes a new instance of the class. /// /// /// The user Setting Service. /// /// /// The advanced Encoder Options Command. /// public VideoViewModel(IUserSettingService userSettingService, IAdvancedEncoderOptionsCommand advancedEncoderOptionsCommand) { this.Task = new EncodeTask { VideoEncoder = VideoEncoder.X264 }; this.userSettingService = userSettingService; this.advancedEncoderOptionsCommand = advancedEncoderOptionsCommand; this.QualityMin = 0; this.QualityMax = 51; this.IsConstantQuantity = true; this.VideoEncoders = EnumHelper.GetEnumList(); X264Presets = new BindingList(EnumHelper.GetEnumList().ToList()); H264Profiles = EnumHelper.GetEnumList(); X264Tunes = EnumHelper.GetEnumList().Where(t => t != x264Tune.Fastdecode); this.H264Levels = Levels; this.userSettingService.SettingChanged += this.UserSettingServiceSettingChanged; } #endregion #region Public Properties /// /// Gets or sets the current Encode Task. /// public EncodeTask Task { get; set; } /// /// Gets a value indicating whether show advanced tab. /// public bool ShowAdvancedTab { get { bool showAdvTabSetting = this.userSettingService.GetUserSetting(UserSettingConstants.ShowAdvancedTab); if (!showAdvTabSetting) { this.UseAdvancedTab = false; } return showAdvTabSetting; } } /// /// Gets or sets a value indicating whether use video tab. /// public bool UseAdvancedTab { get { return this.useAdvancedTab; } set { if (!object.Equals(value, this.useAdvancedTab)) { this.useAdvancedTab = value; this.Task.ShowAdvancedTab = value; this.NotifyOfPropertyChange(() => this.UseAdvancedTab); } } } /// /// Gets Framerates. /// public IEnumerable Framerates { get { return new List { "Same as source", "5", "10", "12", "15", "23.976", "24", "25", "29.97", "30", "50", "59.94", "60" }; } } /// /// 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); } } /// /// 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.Task.TwoPass = false; this.Task.TurboFirstPass = false; this.Task.VideoBitrate = null; this.NotifyOfPropertyChange(() => this.Task); } else { this.Task.VideoEncodeRateType = VideoEncodeRateType.AverageBitrate; } this.NotifyOfPropertyChange(() => this.IsConstantQuantity); } } /// /// 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); } } /// /// 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); } } /// /// Gets a value indicating whether is lossless. /// public bool IsLossless { get { return 0.0.Equals(this.DisplayRF); } } /// /// 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; double cqStep = userSettingService.GetUserSetting(UserSettingConstants.X264Step); this.SetQualitySliderBounds(); switch (this.SelectedVideoEncoder) { case VideoEncoder.FFMpeg: case VideoEncoder.FFMpeg2: this.Task.Quality = (32 - value); break; case VideoEncoder.X264: double rfValue = 51.0 - value * cqStep; rfValue = Math.Round(rfValue, 2); this.Task.Quality = rfValue; break; case VideoEncoder.Theora: Task.Quality = value; break; } this.NotifyOfPropertyChange(() => this.RF); this.NotifyOfPropertyChange(() => this.DisplayRF); this.NotifyOfPropertyChange(() => this.IsLossless); } } /// /// Gets DisplayRF. /// public double DisplayRF { get { return Task.Quality.HasValue ? this.Task.Quality.Value : 0; } } /// /// Gets the rfqp. /// public string Rfqp { get { return this.SelectedVideoEncoder == VideoEncoder.X264 ? "RF" : "QP"; } } /// /// 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") { 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); } } /// /// Gets or sets SelectedVideoEncoder. /// public VideoEncoder SelectedVideoEncoder { get { return this.Task.VideoEncoder; } set { this.Task.VideoEncoder = value; this.NotifyOfPropertyChange(() => this.SelectedVideoEncoder); // Tell the Advanced Panel off the change IAdvancedViewModel advancedViewModel = IoC.Get(); advancedViewModel.SetEncoder(this.Task.VideoEncoder); // Update the Quality Slider. Make sure the bounds are up to date with the users settings. this.SetQualitySliderBounds(); // Hide the x264 controls when not needed. this.DisplayX264Options = value == VideoEncoder.X264; this.NotifyOfPropertyChange(() => this.Rfqp); } } /// /// 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 (!object.Equals(this.Task.AdvancedEncoderOptions, value)) { this.Task.ExtraAdvancedArguments = value; this.NotifyOfPropertyChange(() => this.ExtraArguments); this.NotifyOfPropertyChange(() => FullOptionsTooltip); } } } /// /// Gets or sets a value indicating whether display x 264 options. /// public bool DisplayX264Options { get { return this.displayX264Options; } set { this.displayX264Options = value; this.NotifyOfPropertyChange(() => this.DisplayX264Options); this.NotifyOfPropertyChange(() => FullOptionsTooltip); } } /// /// Gets or sets the x 264 preset value. /// public int X264PresetValue { get { return this.x264PresetValue; } set { if (!object.Equals(this.X264PresetValue, value)) { this.x264PresetValue = value; this.X264Preset = this.X264Presets[value]; this.NotifyOfPropertyChange(() => this.x264PresetValue); this.NotifyOfPropertyChange(() => FullOptionsTooltip); } } } /// /// Gets or sets X264Preset. /// public x264Preset X264Preset { get { return this.Task.X264Preset; } set { if (!object.Equals(this.X264Preset, value)) { this.Task.X264Preset = value; this.NotifyOfPropertyChange(() => this.X264Preset); ResetAdvancedTab(); this.NotifyOfPropertyChange(() => FullOptionsTooltip); } } } /// /// Gets or sets H264Profile. /// public x264Profile H264Profile { get { return this.Task.H264Profile; } set { if (!object.Equals(this.H264Profile, value)) { this.Task.H264Profile = value; this.NotifyOfPropertyChange(() => this.H264Profile); ResetAdvancedTab(); this.NotifyOfPropertyChange(() => FullOptionsTooltip); } } } /// /// Gets or sets H264Profile. /// public string H264Level { get { return this.Task.H264Level; } set { if (!object.Equals(this.H264Level, value)) { this.Task.H264Level = value; this.NotifyOfPropertyChange(() => this.H264Level); ResetAdvancedTab(); this.NotifyOfPropertyChange(() => FullOptionsTooltip); } } } /// /// Gets or sets X264Tune. /// public x264Tune X264Tune { get { return this.Task.X264Tune; } set { if (!object.Equals(this.X264Tune, value)) { this.Task.X264Tune = value; this.NotifyOfPropertyChange(() => this.X264Tune); ResetAdvancedTab(); this.NotifyOfPropertyChange(() => FullOptionsTooltip); } } } /// /// Gets or sets a value indicating whether fast decode. /// public bool FastDecode { get { return this.Task.FastDecode; } set { if (!object.Equals(this.FastDecode, value)) { this.Task.FastDecode = value; this.NotifyOfPropertyChange(() => this.FastDecode); ResetAdvancedTab(); this.NotifyOfPropertyChange(() => FullOptionsTooltip); } } } /// /// Gets or sets X264Presets. /// public BindingList X264Presets { get; set; } /// /// Gets or sets X264Profiles. /// public IEnumerable H264Profiles { get; set; } /// /// Gets or sets X264Tunes. /// public IEnumerable X264Tunes { get; set; } /// /// Gets or sets the x 264 levels. /// public IEnumerable H264Levels { get; set; } /// /// Gets the full options tooltip. /// public string FullOptionsTooltip { get { return string.Format(Resources.Video_x264ExtraArgs, this.GetActualx264Query()); // "You can provide additional arguments using the standard x264 format"; } } #endregion #region Public Methods /// /// Setup this window for a new source /// /// /// The title. /// /// /// The preset. /// /// /// The task. /// public void SetSource(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; } double cqStep = userSettingService.GetUserSetting(UserSettingConstants.X264Step); double rfValue = 0; this.SetQualitySliderBounds(); switch (this.SelectedVideoEncoder) { case VideoEncoder.FFMpeg: case VideoEncoder.FFMpeg2: if (preset.Task.Quality.HasValue) { int cq; int.TryParse(preset.Task.Quality.Value.ToString(CultureInfo.InvariantCulture), out cq); this.RF = 32 - cq; } break; case VideoEncoder.X264: double multiplier = 1.0 / cqStep; if (preset.Task.Quality.HasValue) { rfValue = preset.Task.Quality.Value * multiplier; } this.RF = this.QualityMax - (int)Math.Round(rfValue, 0); break; case VideoEncoder.Theora: if (preset.Task.Quality.HasValue) { this.RF = (int)preset.Task.Quality.Value; } break; } this.Task.TwoPass = preset.Task.TwoPass; this.Task.TurboFirstPass = preset.Task.TurboFirstPass; this.Task.VideoBitrate = preset.Task.VideoBitrate; this.NotifyOfPropertyChange(() => this.Task); if (preset.Task != null) { this.SetEncoder(preset.Task.VideoEncoder); this.X264PresetValue = preset.Task.VideoEncoder == VideoEncoder.X264 ? (int)preset.Task.X264Preset : (int)x264Preset.Medium; this.H264Profile = preset.Task.VideoEncoder == VideoEncoder.X264 ? preset.Task.H264Profile : x264Profile.None; this.X264Tune = preset.Task.VideoEncoder == VideoEncoder.X264 ? preset.Task.X264Tune : x264Tune.None; this.H264Level = preset.Task.VideoEncoder == VideoEncoder.X264 ? preset.Task.H264Level : "Auto"; this.FastDecode = preset.Task.VideoEncoder == VideoEncoder.X264 && preset.Task.FastDecode; this.ExtraArguments = preset.Task.ExtraAdvancedArguments; this.UseAdvancedTab = !string.IsNullOrEmpty(preset.Task.AdvancedEncoderOptions) && this.ShowAdvancedTab; } } /// /// Update all the UI controls based on the encode task passed in. /// /// /// The task. /// public void UpdateTask(EncodeTask task) { this.Task = task; 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.RF); this.NotifyOfPropertyChange(() => this.DisplayRF); this.NotifyOfPropertyChange(() => this.Task.VideoBitrate); this.NotifyOfPropertyChange(() => this.Task.TwoPass); this.NotifyOfPropertyChange(() => this.Task.TurboFirstPass); this.NotifyOfPropertyChange(() => this.X264Tune); this.NotifyOfPropertyChange(() => this.X264Preset); this.NotifyOfPropertyChange(() => this.H264Level); this.NotifyOfPropertyChange(() => this.H264Profile); this.NotifyOfPropertyChange(() => this.FastDecode); this.NotifyOfPropertyChange(() => this.ExtraArguments); } /// /// Set the currently selected encoder. /// /// /// The Video Encoder. /// public void SetEncoder(VideoEncoder encoder) { this.DisplayX264Options = encoder == VideoEncoder.X264; } /// /// Trigger a Notify Property Changed on the Task to force various UI elements to update. /// public void RefreshTask() { this.NotifyOfPropertyChange(() => this.Task); if (Task.OutputFormat == OutputFormat.Mp4 && this.SelectedVideoEncoder == VideoEncoder.Theora) { this.SelectedVideoEncoder = VideoEncoder.X264; } } /// /// Clear advanced settings. /// public void ClearAdvancedSettings() { this.canClear = false; this.X264PresetValue = 5; this.X264Tune = x264Tune.None; this.H264Profile = x264Profile.None; this.FastDecode = false; this.H264Level = "Auto"; this.ExtraArguments = string.Empty; this.canClear = true; } #endregion /// /// 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.X264: this.QualityMin = 0; this.QualityMax = (int)(51 / userSettingService.GetUserSetting(UserSettingConstants.X264Step)); break; case VideoEncoder.Theora: this.QualityMin = 0; this.QualityMax = 63; break; } } /// /// Reset advanced tab. /// private void ResetAdvancedTab() { if (canClear) { this.advancedEncoderOptionsCommand.ExecuteClearAdvanced(); } } /// /// The get actualx 264 query. /// /// /// The . /// private string GetActualx264Query() { if (this.userSettingService.GetUserSetting(UserSettingConstants.DisableLibHbFeatures)) { return string.Empty; // Feature is disabled. } string preset = EnumHelper.GetDisplay(this.X264Preset).ToLower().Replace(" ", string.Empty); string profile = EnumHelper.GetDisplay(this.H264Profile).ToLower(); List tunes = new List(); if (X264Tune != x264Tune.None) { tunes.Add(this.X264Tune.ToString().ToLower().Replace(" ", string.Empty)); // TODO tidy this sillyness up. } 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; } // TODO figure out what is wrong with this?? return HandBrakeUtils.CreateX264OptionsString(preset, tunes, this.ExtraArguments, profile, this.H264Level, width, height); } /// /// The user setting service_ setting changed. /// /// /// The sender. /// /// /// The e. /// private void UserSettingServiceSettingChanged(object sender, HandBrake.ApplicationServices.EventArgs.SettingChangedEventArgs e) { if (e.Key == UserSettingConstants.ShowAdvancedTab) { this.NotifyOfPropertyChange(() => this.ShowAdvancedTab); } } } }