Imports System.Text Imports StaxRip.UI Imports StaxRip.VideoEncoderCommandLine Public Class VvencffappEnc Inherits BasicVideoEncoder Property ParamsStore As New PrimitiveStore Sub New() Name = "vvencFFapp" Params.ApplyPresetDefaultValues() Params.ApplyPresetValues() End Sub Overloads Shared ReadOnly Property Package As Package Get Return Package.VVenCFFapp End Get End Property Private ParamsValue As VvencffappParams Property Params As VvencffappParams Get If ParamsValue Is Nothing Then ParamsValue = New VvencffappParams ParamsValue.Init(ParamsStore) End If Return ParamsValue End Get Set(value As VvencffappParams) ParamsValue = value End Set End Property Public Overrides Property OverridesTargetFileName As Boolean Get Return Params.OverrideTargetFileName.Value End Get Set(value As Boolean) If Params.OverrideTargetFileName.Value <> value Then Params.OverrideTargetFileName.Value = value Params.RaiseValueChanged(Params.OverrideTargetFileName) End If End Set End Property Public Overrides ReadOnly Property OverridingTargetFileName As String Get Dim value = Macro.ExpandParamValues(Params.TargetFileName.Value, Params.Items) value = value.Replace(Environment.NewLine, "") Return value End Get End Property Overrides ReadOnly Property Codec As String Get Return "vvc" End Get End Property Overrides ReadOnly Property OutputExt As String Get Return "vvc" End Get End Property Overrides Sub Encode() Encode("Video encoding", GetArgs(1, 0, 0, Nothing, p.Script), s.EncoderProcessPriority) If Params.Mode.Value = VvencffappRateMode.TwoPass Then Encode("Video encoding second pass", GetArgs(2, 0, 0, Nothing, p.Script), s.EncoderProcessPriority) End If End Sub Overloads Sub Encode(passName As String, commandLine As String, priority As ProcessPriorityClass) If Not CanChunkEncode() Then p.Script.Synchronize() End If Using proc As New Proc proc.Package = Package.VVenCFFapp proc.Header = passName proc.Encoding = Encoding.UTF8 proc.Priority = priority proc.FrameCount = p.Script.GetFrameCount proc.SkipString = "TId: " If commandLine.Contains("|") Then proc.File = "cmd.exe" proc.Arguments = "/S /C """ + commandLine + """" Else proc.CommandLine = commandLine End If proc.Start() End Using End Sub Overrides Property Bitrate As Integer Get Return CInt(Params.TargetBitrate.Value) End Get Set(value As Integer) Params.TargetBitrate.Value = value End Set End Property Overrides Function CanChunkEncode() As Boolean Return CInt(Params.Chunks.Value) > 1 End Function Overrides Function GetChunks() As Integer Return CInt(Params.Chunks.Value) End Function Overrides Function GetChunkEncodeActions() As List(Of Action) Dim maxFrame = If(Params.Decoder.Value = 0, p.Script.GetFrameCount(), p.SourceFrames) Dim chunkCount = CInt(Params.Chunks.Value) Dim startFrame = CInt(Params.FrameSkip.Value) Dim length = If(CInt(Params.FramesToBeEncoded.Value) > 0, CInt(Params.FramesToBeEncoded.Value), maxFrame - startFrame) Dim endFrame = Math.Min(startFrame + length - 1, maxFrame) Dim chunkLength = length \ chunkCount Dim ret As New List(Of Action) For chunk = 0 To chunkCount - 1 Dim chunkStart = startFrame + (chunk * chunkLength) Dim chunkEnd = If(chunk <> chunkCount - 1, chunkStart + (chunkLength - 1), endFrame) Dim chunkName = "" Dim passName = "" If chunkCount > 1 Then chunkName = "_chunk" & (chunk + 1) passName = " chunk " & (chunk + 1) End If If Params.Mode.Value = VvencffappRateMode.TwoPass Then ret.Add(Sub() Encode("Video encoding pass 1" + passName, GetArgs(1, chunkStart, chunkEnd, chunkName, p.Script), s.EncoderProcessPriority) Encode("Video encoding pass 2" + passName, GetArgs(2, chunkStart, chunkEnd, chunkName, p.Script), s.EncoderProcessPriority) End Sub) Else ret.Add(Sub() Encode("Video encoding" + passName, GetArgs(1, chunkStart, chunkEnd, chunkName, p.Script), s.EncoderProcessPriority)) End If Next Return ret End Function Overrides Sub RunCompCheck() If Not g.VerifyRequirements OrElse Not g.IsValidSource Then Exit Sub End If Dim newParams As New VvencffappParams Dim newStore = ObjectHelp.GetCopy(ParamsStore) newParams.Init(newStore) Dim enc As New VvencffappEnc enc.Params = newParams enc.Params.Mode.Value = VvencffappRateMode.SingleQuant enc.Params.Quant.Value = enc.Params.CompCheck.Value Dim script As New VideoScript script.Engine = p.Script.Engine script.Filters = p.Script.GetFiltersCopy Dim code As String Dim framerate = p.Script.GetFramerate() Dim totalFrames = p.Script.GetFrameCount() Dim range = framerate * p.CompCheckTestblockSeconds Dim every = (100 / p.CompCheckPercentage) * range If script.Engine = ScriptEngine.AviSynth Then code = $"SelectRangeEvery({CInt(every)},{CInt(range)})" Else code = "fpsnum = clip.fps_num" + BR code += "fpsden = clip.fps_den" + BR code += $"clip = core.std.SelectEvery(clip = clip, cycle = {CInt(every)}, offsets = range({CInt(range)}))" + BR code += "clip = core.std.AssumeFPS(clip = clip, fpsnum = fpsnum, fpsden = fpsden)" End If script.Filters.Add(New VideoFilter("aaa", "aaa", code)) script.Path = Path.Combine(p.TempDir, p.TargetFile.Base + "_CompCheck." + script.FileType) script.Synchronize() Log.WriteLine(BR + script.GetFullScript + BR) Dim commandLine = enc.Params.GetArgs(0, 0, 0, Nothing, script, Path.Combine(p.TempDir, p.TargetFile.Base + "_CompCheck." + OutputExt), True, True) Try Encode("Compressibility Check", commandLine, s.EncoderProcessPriority) Catch ex As AbortException Exit Sub Catch ex As Exception g.ShowException(ex) Exit Sub End Try Dim bits = (New FileInfo(Path.Combine(p.TempDir, p.TargetFile.Base + "_CompCheck." + OutputExt)).Length) * 8 p.Compressibility = (bits / script.GetFrameCount) / (p.TargetWidth * p.TargetHeight) OnAfterCompCheck() g.MainForm.Assistant() Log.WriteLine("Quality: " & CInt(Calc.GetPercent).ToString() + " %") Log.WriteLine("Compressibility: " + p.Compressibility.ToString("f3")) Log.Save() End Sub Overloads Function GetArgs( pass As Integer, startFrame As Integer, endFrame As Integer, chunkName As String, script As VideoScript, Optional includePaths As Boolean = True) As String Return Params.GetArgs(pass, startFrame, endFrame, chunkName, script, OutputPath.DirAndBase + OutputExtFull, includePaths, True) End Function Overrides Sub ShowConfigDialog(Optional param As CommandLineParam = Nothing) Dim newParams As New VvencffappParams Dim store = ObjectHelp.GetCopy(ParamsStore) newParams.Init(store) newParams.ApplyPresetDefaultValues() Using form As New CommandLineForm(newParams) form.HTMLHelpFunc = Function() $"

vvencFFapp Online Help

" + $"

vvencFFapp Console Help

{HelpDocument.ConvertChars(Package.VVenCFFapp.CreateHelpfile())}
" Dim a = Sub() Dim enc = ObjectHelp.GetCopy(Me) Dim params2 As New VvencffappParams Dim store2 = ObjectHelp.GetCopy(store) params2.Init(store2) enc.Params = params2 enc.ParamsStore = store2 SaveProfile(enc) End Sub form.cms.Add("Save Profile...", a, Keys.Control Or Keys.S, Symbol.Save) If Not String.IsNullOrWhiteSpace(param?.Path) Then form.SimpleUI.ShowPage(param?.Path) form.cbGoTo.Text = param.GetSwitches().FirstOrDefault() End If If form.ShowDialog() = DialogResult.OK Then AutoCompCheckValue = CInt(newParams.CompCheckAimedQuality.Value) Params = newParams ParamsStore = store OnStateChange() UpdateTargetFile() End If End Using End Sub Overrides Property QualityMode() As Boolean Get Return Params.Mode.Value = VvencffappRateMode.SingleQuant End Get Set(Value As Boolean) End Set End Property Public Overrides ReadOnly Property CommandLineParams As CommandLineParams Get Return Params End Get End Property Sub SetMuxer() Muxer = New MP4Muxer() End Sub Overrides Function CreateEditControl() As Control Return New VvencffappControl(Me) With {.Dock = DockStyle.Fill} End Function Shared Function Test() As String Dim tester As New ConsolAppTester tester.IgnoredSwitches = "" tester.UndocumentedSwitches = "" tester.Package = Package.VVenCFFapp tester.CodeFile = Path.Combine(Folder.Startup.Parent, "Encoding", "VvencffappEnc.vb") Return tester.Test End Function End Class Public Class VvencffappParams Inherits CommandLineParams Sub New() Title = "vvencFFapp Options" End Sub Public Overrides ReadOnly Property Package As Package Get Return VvencffappEnc.Package End Get End Property Property OverrideTargetFileName As New BoolParam() With { .Text = "Override Target File Name", .Init = False} Property TargetFileName As New StringParam With { .Text = "Target File Name", .Quotes = QuotesMode.Never, .TextChangedAction = Sub(text) TargetFileNamePreview.Value = Macro.ExpandParamValues(text, Items), .Init = "%source_name%_new", .InitAction = Sub(tb) tb.Edit.MultilineHeightFactor = 6 tb.Edit.TextBox.Font = FontManager.GetCodeFont() End Sub} Property TargetFileNamePreview As New StringParam With { .Text = "Preview", .Quotes = QuotesMode.Never, .InitAction = Sub(tb) tb.Edit.MultilineHeightFactor = 3 tb.Edit.TextBox.Font = FontManager.GetCodeFont() tb.Edit.TextBox.ReadOnly = True BlockValueChanged = True .Value = Macro.ExpandParamValues(TargetFileName.Value, Items) BlockValueChanged = False End Sub} Property Decoder As New OptionParam With { .Text = "Decoder", .Options = {"AviSynth/VapourSynth", "QSVEnc (Intel)", "ffmpeg (Intel)", "ffmpeg (DXVA2)"}, .Values = {"script", "qs", "ffqsv", "ffdxva"}} Property PipingToolAVS As New OptionParam With { .Name = "PipingToolAVS", .Text = "Pipe", .VisibleFunc = Function() p.Script.IsAviSynth AndAlso Decoder.Value = 0, .Options = {"Automatic", "avs2pipemod", "ffmpeg"}} Property PipingToolVS As New OptionParam With { .Name = "PipingToolVS", .Text = "Pipe", .VisibleFunc = Function() p.Script.IsVapourSynth AndAlso Decoder.Value = 0, .Options = {"Automatic", "vspipe", "ffmpeg"}} Property CompCheck As New NumParam With { .Name = "CompCheckQuant", .Text = "Comp. Check", .Value = 18, .Help = "QP value used as 100% for the compressibility check.", .Config = {1, 50}} Property CompCheckAimedQuality As New NumParam With { .Name = "CompCheckAimedQuality", .Text = "Aimed Quality", .Value = 50, .Help = "Percent value to adjusts the target file size or image size after the compressibility check accordingly.", .Config = {1, 100}} Property Chunks As New NumParam With { .Text = "Chunks", .Init = 1, .Config = {1, 32}} Property Mode As New OptionParam With { .Switches = {"--TargetBitrate", "--QP", "--Pass", "--NumPasses", "--RCStatsFile"}, .Name = "Mode", .Text = "Mode", .Options = {"Bitrate", "Quantizer", "Two Pass"}, .Value = 1} Property Quant As New NumParam With { .Switches = {"--QP", "-q"}, .Name = "Quant", .Text = "Quantizer", .DefaultValue = 32, .Value = 32, .VisibleFunc = Function() Mode.Value = VvencffappRateMode.SingleQuant, .Config = {0, 63, 1}} Property TargetBitrate As New NumParam With { .HelpSwitch = "--TargetBitrate", .Text = "Target Bitrate", .Init = 5000, .VisibleFunc = Function() Mode.Value <> VvencffappRateMode.SingleQuant, .Config = {0, 1000000, 100}} Property Preset As New OptionParam With { .Switch = "--preset", .Text = "Preset", .Options = {"None", "Faster", "Fast", "Medium", "Slow", "Slower", "Medium_LowDecEnergy", "Firstpass", "ToolTest"}, .Init = 3} Property FrameSkip As New NumParam With { .HelpSwitch = "--FrameSkip", .Text = "Frames To Be Skipped"} Property FramesToBeEncoded As New NumParam With { .HelpSwitch = "--FramesToBeEncoded", .Text = "Frames To Be Encoded"} Property Custom As New StringParam With { .Text = "Custom", .Quotes = QuotesMode.Never, .AlwaysOn = True, .InitAction = Sub(tb) tb.Edit.MultilineHeightFactor = 6 tb.Edit.TextBox.Font = FontManager.GetCodeFont() End Sub} Property CustomFirstPass As New StringParam With { .Text = "Custom" + BR + "First Pass", .Quotes = QuotesMode.Never, .VisibleFunc = Function() Mode.Value = VvencffappRateMode.TwoPass, .InitAction = Sub(tb) tb.Edit.MultilineHeightFactor = 6 tb.Edit.TextBox.Font = FontManager.GetCodeFont() End Sub} Property CustomSecondPass As New StringParam With { .Text = "Custom" + BR + "Second Pass", .Quotes = QuotesMode.Never, .VisibleFunc = Function() Mode.Value = VvencffappRateMode.TwoPass, .InitAction = Sub(tb) tb.Edit.MultilineHeightFactor = 6 tb.Edit.TextBox.Font = FontManager.GetCodeFont() End Sub} Overrides ReadOnly Property Items As List(Of CommandLineParam) Get If ItemsValue Is Nothing Then ItemsValue = New List(Of CommandLineParam) Add("Basic", Decoder, PipingToolAVS, PipingToolVS, Mode, TargetBitrate, Quant, Preset, New OptionParam() With {.Switch = "--Profile", .Name = "Profile", .Text = "Profile", .Options = {"Automatic", "main_10", "main_10_still_picture"}}, New OptionParam() With {.Switch = "--Level", .Name = "Level", .Text = "Level", .Options = {"Automatic", "1.0", "2.0", "2.1", "3.0", "3.1", "4.0", "4.1", "5.0", "5.1", "5.2", "6.0", "6.1", "6.2", "6.3", "15.5"}}, New OptionParam() With {.Switch = "--Tier", .Name = "Tier", .Text = "Tier", .Options = {"Main", "High"}}, New OptionParam() With {.Switch = "--Verbosity", .Name = "Verbosity", .Text = "Verbosity", .Options = {"0: Silent", "1: Error", "2: Warning", "3: Info", "4: Notice", "5: Verbose (Default)", "6: Debug"}, .Init = 5, .IntegerValue = True}, New OptionParam() With {.Switch = "--SIMD", .Name = "SIMD", .Text = "SIMD", .Options = {"Automatic", "SCALAR", "SSE41", "SSE42", "AVX", "AVX2", "AVX512"}} ) Add("I/O", New OptionParam() With {.Switch = "--OutputBitDepth", .Name = "OutputBitDepth", .Text = "Output Bit-Depth", .Options = {"Automatic", "8", "10", "12"}}, FrameSkip, FramesToBeEncoded ) Add("Threading", New NumParam() With {.Switch = "--Threads", .Name = "Threads", .Text = "Threads", .Config = {0, 128, 1, 0}}, New NumParam() With {.Switch = "--MaxParallelFrames", .Name = "MaxParallelFrames", .Text = "Max Parallel Frames", .Config = {-1, 128, 1, 0}, .Init = -1} ) 'Add("Slice Decision", ') Add("Rate Control", New NumParam() With {.Switch = "--LookAhead", .Name = "LookAhead", .Text = "LookAhead", .Config = {-1, 1, 1, 0}, .Init = -1} ) 'Add("Quantization", ') Add("Custom", Custom, CustomFirstPass, CustomSecondPass) Add("Other", OverrideTargetFileName, TargetFileName, TargetFileNamePreview, New LineParam(), Chunks, CompCheck, CompCheckAimedQuality ) 'ItemsValue = ItemsValue.OrderBy(Function(i) i.Weight).ToList End If Return ItemsValue End Get End Property Overrides Function GetCommandLinePreview() As String Dim ret = GetCommandLine(True, True, 1) If Mode.Value = VvencffappRateMode.TwoPass Then ret += BR2 + GetCommandLine(True, True, 2) End If Return ret End Function Public Overrides Sub ShowHelp(options As String()) Using td As New TaskDialog(Of String) td.Title = String.Join(", ", options) td.AddCommand("Online Help") td.AddCommand("Console Help") Select Case td.Show Case "Online Help" Dim ret = GetHelpOption(options) If ret <> "" Then g.ShellExecute("https://github.com/fraunhoferhhi/vvenc/wiki/Usage#how-to-use-the-full-featured-expert-mode-encoder") End If Case "Console Help" ShowConsoleHelp(Package.VVenCFFapp, options) End Select End Using End Sub Private BlockValueChanged As Boolean Protected Overrides Sub OnValueChanged(item As CommandLineParam) If BlockValueChanged Then Exit Sub If item Is Preset Then BlockValueChanged = True ApplyPresetValues() BlockValueChanged = False End If If item IsNot TargetFileName AndAlso item IsNot TargetFileNamePreview Then TargetFileName.TextChangedAction?.Invoke(TargetFileName.Value) MyBase.OnValueChanged(item) End Sub Overloads Overrides Function GetCommandLine( includePaths As Boolean, includeExecutable As Boolean, Optional pass As Integer = 1) As String Return GetArgs(pass, 0, 0, Nothing, p.Script, p.VideoEncoder.OutputPath.DirAndBase + p.VideoEncoder.OutputExtFull, includePaths, includeExecutable) End Function Overloads Function GetArgs( pass As Integer, startFrame As Integer, endFrame As Integer, chunkName As String, script As VideoScript, targetPath As String, includePaths As Boolean, includeExecutable As Boolean) As String ApplyPresetDefaultValues() Dim sb As New StringBuilder Dim pipeTool = If(p.Script.IsAviSynth, PipingToolAVS, PipingToolVS).ValueText.ToLowerInvariant() Dim isSingleChunk = endFrame = 0 If includePaths AndAlso includeExecutable Then Dim isCropped = (p.CropLeft Or p.CropTop Or p.CropRight Or p.CropBottom) <> 0 AndAlso Decoder.ValueText <> "avs" AndAlso p.Script.IsFilterActive("Crop") Dim pipeString = "" Select Case Decoder.ValueText.ToLowerInvariant() Case "script" 'Broken ANSI fallback taken from x265 If pipeTool = "automatic" OrElse (p.Script.IsAviSynth AndAlso Not TextEncoding.IsProcessUTF8 AndAlso Not TextEncoding.ArePathsSupportedByASCIIEncoding) Then pipeTool = If(p.Script.IsAviSynth, "avs2pipemod", "vspipe") End If Select Case pipeTool Case "avs2pipemod" Dim chunk = If(isSingleChunk, "", $" -trim={startFrame},{endFrame}") Dim dll = If(FrameServerHelp.IsPortable, $" -dll={Package.AviSynth.Path.Escape}", "") pipeString = Package.avs2pipemod.Path.Escape + dll + chunk + " -y4mp " + script.Path.Escape sb.Append(pipeString & " | " & Package.VVenCFFapp.Path.Escape) Case "vspipe" Dim chunk = If(isSingleChunk, "", $" --start {startFrame} --end {endFrame}") pipeString = Package.vspipe.Path.Escape + " " + script.Path.Escape + " - --container y4m" + chunk sb.Append(pipeString & " | " & Package.VVenCFFapp.Path.Escape) Case "ffmpeg" pipeString = Package.ffmpeg.Path.Escape + If(p.Script.IsVapourSynth, " -f vapoursynth", "") + " -i " + script.Path.Escape + " -f yuv4mpegpipe -strict -1 -loglevel fatal -hide_banner -" sb.Append(pipeString & " | " & Package.VVenCFFapp.Path.Escape) End Select Case "qs" Dim crop = If(isCropped, " --crop " & p.CropLeft & "," & p.CropTop & "," & p.CropRight & "," & p.CropBottom, "") pipeString = Package.QSVEncC.Path.Escape + " -o - -c raw" + crop + " -i " + p.SourceFile.Escape sb.Append(pipeString & " | " & Package.VVenCFFapp.Path.Escape) Case "ffqsv" Dim crop = If(isCropped, $" -vf ""crop={p.SourceWidth - p.CropLeft - p.CropRight}:{p.SourceHeight - p.CropTop - p.CropBottom}:{p.CropLeft}:{p.CropTop}""", "") Dim pix_fmt = If(p.SourceVideoBitDepth = 10 OrElse p.Script.Info.BitDepth >= 10, "yuv420p10le", "yuv420p") pipeString = Package.ffmpeg.Path.Escape + " -threads 1 -hwaccel qsv -i " + p.SourceFile.Escape + " -f yuv4mpegpipe -pix_fmt " + pix_fmt + " -strict -1" + crop + " -loglevel fatal -hide_banner -" sb.Append(pipeString & " | " & Package.VVenCFFapp.Path.Escape) Case "ffdxva" Dim crop = If(isCropped, $" -vf ""crop={p.SourceWidth - p.CropLeft - p.CropRight}:{p.SourceHeight - p.CropTop - p.CropBottom}:{p.CropLeft}:{p.CropTop}""", "") Dim pix_fmt = If(p.SourceVideoBitDepth = 10 OrElse p.Script.Info.BitDepth >= 10, "yuv420p10le", "yuv420p") pipeString = Package.ffmpeg.Path.Escape + " -threads 1 -hwaccel dxva2 -i " + p.SourceFile.Escape + " -f yuv4mpegpipe -pix_fmt " + pix_fmt + " -strict -1" + crop + " -loglevel fatal -hide_banner -" sb.Append(pipeString & " | " & Package.VVenCFFapp.Path.Escape) End Select End If If includePaths Then Dim input = If(Decoder.Value = 0 AndAlso pipeTool = "automatic", script.Path.Escape, " --InputFile -") If (Mode.Value = VvencffappRateMode.TwoPass AndAlso pass = 1) Then sb.Append(input) sb.Append(" --BitstreamFile NUL") Else sb.Append(input) sb.Append(" --BitstreamFile " + (targetPath.DirAndBase + chunkName + targetPath.ExtFull).Escape) End If If Decoder.Value <> 0 OrElse pipeTool <> "automatic" Then sb.Append(" --y4m") End If If Mode.Value = VvencffappRateMode.TwoPass Then sb.Append(" --RCStatsFile " + (targetPath.DirAndBase + chunkName + ".stats").Escape) End If End If If isSingleChunk Then If FrameSkip.Value > 0 AndAlso Not IsCustom(pass, "--FrameSkip") Then sb.Append($" --FrameSkip {FrameSkip.Value}") End If If FramesToBeEncoded.Value > 0 AndAlso Not IsCustom(pass, "--FramesToBeEncoded") Then Dim maxFrames = If(script.GetFrameCount > 0, script.GetFrameCount - FrameSkip.Value, FramesToBeEncoded.Value) maxFrames = If(maxFrames < 0, 0, maxFrames) sb.Append($" --FramesToBeEncoded {Math.Min(maxFrames, FramesToBeEncoded.Value)}") End If 'Else ' If includePaths Then ' sb.Append($" --FrameSkip {startFrame} --FramesToBeEncoded {endFrame - startFrame + 1}") ' End If End If If includePaths Then sb.Append($" --Size {p.TargetWidth}x{p.TargetHeight}") End If If Mode.Value = VvencffappRateMode.TwoPass Then sb.Append(" --NumPasses 2") sb.Append(" --Pass " & pass) If pass = 1 Then If CustomFirstPass.Value <> "" Then sb.Append(" " + CustomFirstPass.Value) End If Else If CustomSecondPass.Value <> "" Then sb.Append(" " + CustomSecondPass.Value) End If End If End If 'If includePaths AndAlso (MultiPassOptDistortion.Value OrElse MultiPassOptAnalysis.Value) Then ' sb.Append(" --analysis-reuse-file " + (targetPath.DirAndBase + chunkName + ".analysis").Escape) 'End If If Mode.Value = VvencffappRateMode.SingleQuant Then If Not IsCustom(pass, "--QP") AndAlso Quant.Value <> Quant.DefaultValue Then sb.Append(" --QP " + CInt(Quant.Value).ToString) End If Else If Not IsCustom(pass, "--TargetBitrate") Then sb.Append(" --TargetBitrate " & If(pass = 1, TargetBitrate.Value, p.VideoBitrate) & "k") End If End If Dim q = From i In Items Where i.GetArgs <> "" AndAlso Not IsCustom(pass, i.GetSwitches()) If q.Count > 0 Then sb.Append(" " + q.Select(Function(item) item.GetArgs).Join(" ")) End If Return Macro.Expand(sb.ToString.Trim.FixBreak.Replace(BR, " ")) End Function Function IsCustom(pass As Integer, switches As String()) As Boolean If switches Is Nothing Then Return False Return switches.Any(Function(switch) IsCustom(pass, switch)) End Function Function IsCustom(pass As Integer, switch As String) As Boolean If switch = "" Then Return False End If If Mode.Value = VvencffappRateMode.TwoPass Then If pass = 1 Then If CustomFirstPass.Value?.Contains(switch + " ") OrElse CustomFirstPass.Value?.EndsWith(switch) Then Return True End If Else If CustomSecondPass.Value?.Contains(switch + " ") OrElse CustomSecondPass.Value?.EndsWith(switch) Then Return True End If End If End If Return If(Custom.Value?.Contains(switch + " ") OrElse Custom.Value?.EndsWith(switch), False) End Function Sub ApplyPresetValues() End Sub Sub ApplyPresetDefaultValues() End Sub End Class Public Enum VvencffappRateMode SingleBitrate SingleQuant TwoPass End Enum