using ErsatzTV.FFmpeg.Encoder; using ErsatzTV.FFmpeg.Environment; using ErsatzTV.FFmpeg.Filter; using ErsatzTV.FFmpeg.GlobalOption; using ErsatzTV.FFmpeg.InputOption; namespace ErsatzTV.FFmpeg; public static class CommandGenerator { public static IList GenerateEnvironmentVariables(IEnumerable pipelineSteps) => pipelineSteps.SelectMany(ps => ps.EnvironmentVariables).ToList(); public static IList GenerateArguments( Option maybeVideoInputFile, Option maybeAudioInputFile, Option maybeWatermarkInputFile, Option maybeConcatInputFile, IList pipelineSteps, bool isIntelVaapiOrQsv) { var arguments = new List(); foreach (IPipelineStep step in pipelineSteps) { arguments.AddRange(step.GlobalOptions); } var includedPaths = new System.Collections.Generic.HashSet(); foreach (VideoInputFile videoInputFile in maybeVideoInputFile) { includedPaths.Add(videoInputFile.Path); foreach (IInputOption step in videoInputFile.InputOptions) { arguments.AddRange(step.InputOptions(videoInputFile)); } arguments.AddRange(new[] { "-i", videoInputFile.Path }); } foreach (AudioInputFile audioInputFile in maybeAudioInputFile) { if (!includedPaths.Contains(audioInputFile.Path) || isIntelVaapiOrQsv) { includedPaths.Add(audioInputFile.Path); foreach (IInputOption step in audioInputFile.InputOptions) { arguments.AddRange(step.InputOptions(audioInputFile)); } arguments.AddRange(new[] { "-i", audioInputFile.Path }); } } foreach (WatermarkInputFile watermarkInputFile in maybeWatermarkInputFile) { if (!includedPaths.Contains(watermarkInputFile.Path)) { includedPaths.Add(watermarkInputFile.Path); foreach (IInputOption step in watermarkInputFile.InputOptions) { arguments.AddRange(step.InputOptions(watermarkInputFile)); } arguments.AddRange(new[] { "-i", watermarkInputFile.Path }); } } foreach (ConcatInputFile concatInputFile in maybeConcatInputFile) { foreach (IInputOption step in concatInputFile.InputOptions) { arguments.AddRange(step.InputOptions(concatInputFile)); } arguments.AddRange(new[] { "-i", concatInputFile.Path }); } foreach (IPipelineStep step in pipelineSteps) { arguments.AddRange(step.FilterOptions); } // rearrange complex filter output options directly after video encoder var sortedSteps = pipelineSteps.Filter(s => s is not StreamSeekFilterOption && s is not ComplexFilter).ToList(); Option maybeComplex = pipelineSteps.Find(s => s is ComplexFilter); foreach (IPipelineStep complex in maybeComplex) { int encoderIndex = sortedSteps.FindIndex(s => s is EncoderBase { Kind: StreamKind.Video }); sortedSteps.Insert(encoderIndex + 1, complex); } foreach (IPipelineStep step in sortedSteps) { arguments.AddRange(step.OutputOptions); } return arguments; } }