From 2f2d7952dd007f05f4e286491f7f6189fccddd6b Mon Sep 17 00:00:00 2001 From: Jason Dove Date: Tue, 15 Feb 2022 08:44:49 -0600 Subject: [PATCH] use vaapi driver settings with new transcoder logic (#635) * add LIBVA_DRIVER_NAME env var * add vaapi device name * add FFREPORT env var * fixes --- .../FFmpeg/FFmpegLibraryProcessService.cs | 45 +++++++++++++++-- ErsatzTV.FFmpeg.Tests/PipelineBuilderTests.cs | 14 ++++-- ErsatzTV.FFmpeg/CommandGenerator.cs | 9 +++- .../Decoder/Cuvid/DecoderH264Cuvid.cs | 2 +- .../Decoder/Cuvid/DecoderHevcCuvid.cs | 3 +- .../Decoder/Cuvid/DecoderMpeg2Cuvid.cs | 4 +- .../Decoder/Cuvid/DecoderMpeg4Cuvid.cs | 2 +- .../Decoder/Cuvid/DecoderVc1Cuvid.cs | 3 +- .../Decoder/Cuvid/DecoderVp9Cuvid.cs | 3 +- ErsatzTV.FFmpeg/Decoder/DecoderBase.cs | 7 ++- ErsatzTV.FFmpeg/Decoder/DecoderH264.cs | 2 +- ErsatzTV.FFmpeg/Decoder/DecoderHevc.cs | 2 +- ErsatzTV.FFmpeg/Decoder/DecoderImplicit.cs | 2 +- ErsatzTV.FFmpeg/Decoder/DecoderMpeg1.cs | 2 +- ErsatzTV.FFmpeg/Decoder/DecoderMpeg2.cs | 2 +- ErsatzTV.FFmpeg/Decoder/DecoderMpeg4.cs | 2 +- ErsatzTV.FFmpeg/Decoder/DecoderMsMpeg4v2.cs | 2 +- ErsatzTV.FFmpeg/Decoder/DecoderMsMpeg4v3.cs | 2 +- ErsatzTV.FFmpeg/Decoder/DecoderVaapi.cs | 2 +- ErsatzTV.FFmpeg/Decoder/DecoderVc1.cs | 2 +- ErsatzTV.FFmpeg/Decoder/DecoderVp9.cs | 2 +- ErsatzTV.FFmpeg/Decoder/Qsv/DecoderH264Qsv.cs | 2 +- ErsatzTV.FFmpeg/Decoder/Qsv/DecoderHevcQsv.cs | 2 +- .../Decoder/Qsv/DecoderMpeg2Qsv.cs | 2 +- ErsatzTV.FFmpeg/Decoder/Qsv/DecoderVc1Qsv.cs | 2 +- ErsatzTV.FFmpeg/Decoder/Qsv/DecoderVp9Qsv.cs | 2 +- ErsatzTV.FFmpeg/Encoder/EncoderBase.cs | 5 +- .../Environment/EnvironmentVariable.cs | 3 ++ .../Environment/FFReportVariable.cs | 46 +++++++++++++++++ .../Environment/LibvaDriverNameVariable.cs | 22 +++++++++ ErsatzTV.FFmpeg/Filter/BaseFilter.cs | 5 +- ErsatzTV.FFmpeg/Filter/ComplexFilter.cs | 5 +- ErsatzTV.FFmpeg/Format/ConcatInputFormat.cs | 5 +- ErsatzTV.FFmpeg/FrameState.cs | 8 ++- ErsatzTV.FFmpeg/IPipelineStep.cs | 5 +- .../Option/FrameRateOutputOption.cs | 6 ++- ErsatzTV.FFmpeg/Option/GlobalOption.cs | 6 ++- .../AvailableHardwareAccelerationOptions.cs | 33 ++++++++++--- .../VaapiHardwareAccelerationOption.cs | 9 +++- .../Option/InfiniteLoopInputOption.cs | 5 +- ErsatzTV.FFmpeg/Option/OutputOption.cs | 5 +- ErsatzTV.FFmpeg/Option/RealtimeInputOption.cs | 6 ++- .../Option/StreamSeekInputOption.cs | 5 +- .../Option/TimeLimitOutputOption.cs | 5 +- .../OutputFormat/OutputFormatHls.cs | 5 +- .../OutputFormat/OutputFormatMpegTs.cs | 6 ++- ErsatzTV.FFmpeg/PipelineBuilder.cs | 49 ++++++++++++++++--- ErsatzTV.FFmpeg/Protocol/PipeProtocol.cs | 6 ++- ErsatzTV.sln.DotSettings | 3 ++ 49 files changed, 310 insertions(+), 67 deletions(-) create mode 100644 ErsatzTV.FFmpeg/Environment/EnvironmentVariable.cs create mode 100644 ErsatzTV.FFmpeg/Environment/FFReportVariable.cs create mode 100644 ErsatzTV.FFmpeg/Environment/LibvaDriverNameVariable.cs diff --git a/ErsatzTV.Core/FFmpeg/FFmpegLibraryProcessService.cs b/ErsatzTV.Core/FFmpeg/FFmpegLibraryProcessService.cs index 216f127a7..e74f6b286 100644 --- a/ErsatzTV.Core/FFmpeg/FFmpegLibraryProcessService.cs +++ b/ErsatzTV.Core/FFmpeg/FFmpegLibraryProcessService.cs @@ -2,12 +2,14 @@ using System.Diagnostics; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Text; using System.Threading.Tasks; using ErsatzTV.Core.Domain; using ErsatzTV.Core.Domain.Filler; using ErsatzTV.Core.Interfaces.FFmpeg; using ErsatzTV.FFmpeg; +using ErsatzTV.FFmpeg.Environment; using ErsatzTV.FFmpeg.Format; using ErsatzTV.FFmpeg.OutputFormat; using LanguageExt; @@ -122,7 +124,10 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService : Option.None; var desiredState = new FrameState( + saveReports, hwAccel, + VaapiDriverName(hwAccel, vaapiDriver), + VaapiDeviceName(hwAccel, vaapiDevice), playbackSettings.RealtimeOutput, false, playbackSettings.StreamSeek, @@ -170,7 +175,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService public Process ConcatChannel(string ffmpegPath, bool saveReports, Channel channel, string scheme, string host) { var resolution = new FrameSize(channel.FFmpegProfile.Resolution.Width, channel.FFmpegProfile.Resolution.Height); - var desiredState = FrameState.Concat(channel.Name, resolution); + var desiredState = FrameState.Concat(saveReports, channel.Name, resolution); var inputFiles = new List { @@ -218,12 +223,13 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService private Process GetProcess(string ffmpegPath, IList inputFiles, FrameState desiredState) { - var pipelineBuilder = new PipelineBuilder(inputFiles, _logger); + var pipelineBuilder = new PipelineBuilder(inputFiles, FileSystemLayout.FFmpegReportsFolder, _logger); IList pipelineSteps = pipelineBuilder.Build(desiredState); - _logger.LogDebug("FFmpeg pipeline {PipelineSteps}", pipelineSteps); + _logger.LogDebug("FFmpeg pipeline {PipelineSteps}", pipelineSteps.Map(ps => ps.GetType().Name)); + IList environmentVariables = CommandGenerator.GenerateEnvironmentVariables(pipelineSteps); IList arguments = CommandGenerator.GenerateArguments(inputFiles, pipelineSteps); var startInfo = new ProcessStartInfo @@ -236,6 +242,16 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService StandardOutputEncoding = Encoding.UTF8 }; + if (environmentVariables.Any()) + { + _logger.LogDebug("FFmpeg environment variables {EnvVars}", environmentVariables); + } + + foreach ((string key, string value) in environmentVariables) + { + startInfo.EnvironmentVariables[key] = value; + } + foreach (string argument in arguments) { startInfo.ArgumentList.Add(argument); @@ -246,4 +262,27 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService StartInfo = startInfo }; } + + private static Option VaapiDriverName(HardwareAccelerationMode accelerationMode, VaapiDriver driver) + { + if (accelerationMode == HardwareAccelerationMode.Vaapi) + { + switch (driver) + { + case VaapiDriver.i965: + return "i965"; + case VaapiDriver.iHD: + return "iHD"; + case VaapiDriver.RadeonSI: + return "radeonsi"; + } + } + + return Option.None; + } + + private static Option VaapiDeviceName(HardwareAccelerationMode accelerationMode, string vaapiDevice) + { + return accelerationMode == HardwareAccelerationMode.Vaapi ? vaapiDevice : Option.None; + } } diff --git a/ErsatzTV.FFmpeg.Tests/PipelineBuilderTests.cs b/ErsatzTV.FFmpeg.Tests/PipelineBuilderTests.cs index 45c360e75..9ae6eae0e 100644 --- a/ErsatzTV.FFmpeg.Tests/PipelineBuilderTests.cs +++ b/ErsatzTV.FFmpeg.Tests/PipelineBuilderTests.cs @@ -30,7 +30,10 @@ public class PipelineGeneratorTests var inputFiles = new List { testFile }; var desiredState = new FrameState( + false, HardwareAccelerationMode.None, + Option.None, + Option.None, true, false, Option.None, @@ -60,7 +63,7 @@ public class PipelineGeneratorTests Option.None, 0); - var builder = new PipelineBuilder(inputFiles, _logger); + var builder = new PipelineBuilder(inputFiles, "", _logger); IList result = builder.Build(desiredState); result.Should().HaveCountGreaterThan(0); @@ -83,7 +86,10 @@ public class PipelineGeneratorTests var inputFiles = new List { testFile }; var desiredState = new FrameState( + false, HardwareAccelerationMode.None, + Option.None, + Option.None, true, false, Option.None, @@ -113,7 +119,7 @@ public class PipelineGeneratorTests Option.None, 0); - var builder = new PipelineBuilder(inputFiles, _logger); + var builder = new PipelineBuilder(inputFiles, "", _logger); IList result = builder.Build(desiredState); result.Should().HaveCountGreaterThan(0); @@ -125,14 +131,14 @@ public class PipelineGeneratorTests public void Concat_Test() { var resolution = new FrameSize(1920, 1080); - var desiredState = FrameState.Concat("Some Channel", resolution); + var desiredState = FrameState.Concat(false, "Some Channel", resolution); var inputFiles = new List { new ConcatInputFile("http://localhost:8080/ffmpeg/concat/1", resolution) }; - var builder = new PipelineBuilder(inputFiles, _logger); + var builder = new PipelineBuilder(inputFiles, "", _logger); IList result = builder.Build(desiredState); result.Should().HaveCountGreaterThan(0); diff --git a/ErsatzTV.FFmpeg/CommandGenerator.cs b/ErsatzTV.FFmpeg/CommandGenerator.cs index 9a0d01600..df7f490bc 100644 --- a/ErsatzTV.FFmpeg/CommandGenerator.cs +++ b/ErsatzTV.FFmpeg/CommandGenerator.cs @@ -1,7 +1,14 @@ -namespace ErsatzTV.FFmpeg; +using ErsatzTV.FFmpeg.Environment; + +namespace ErsatzTV.FFmpeg; public static class CommandGenerator { + public static IList GenerateEnvironmentVariables(IEnumerable pipelineSteps) + { + return pipelineSteps.SelectMany(ps => ps.EnvironmentVariables).ToList(); + } + public static IList GenerateArguments( IEnumerable inputFiles, IList pipelineSteps) diff --git a/ErsatzTV.FFmpeg/Decoder/Cuvid/DecoderH264Cuvid.cs b/ErsatzTV.FFmpeg/Decoder/Cuvid/DecoderH264Cuvid.cs index b794f8ddc..ab6bb5cde 100644 --- a/ErsatzTV.FFmpeg/Decoder/Cuvid/DecoderH264Cuvid.cs +++ b/ErsatzTV.FFmpeg/Decoder/Cuvid/DecoderH264Cuvid.cs @@ -30,7 +30,7 @@ public class DecoderH264Cuvid : DecoderBase } } - public override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Hardware; + protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Hardware; public override FrameState NextState(FrameState currentState) { diff --git a/ErsatzTV.FFmpeg/Decoder/Cuvid/DecoderHevcCuvid.cs b/ErsatzTV.FFmpeg/Decoder/Cuvid/DecoderHevcCuvid.cs index 95837dc39..01d67a2b8 100644 --- a/ErsatzTV.FFmpeg/Decoder/Cuvid/DecoderHevcCuvid.cs +++ b/ErsatzTV.FFmpeg/Decoder/Cuvid/DecoderHevcCuvid.cs @@ -28,7 +28,8 @@ public class DecoderHevcCuvid : DecoderBase return result; } } - public override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Hardware; + + protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Hardware; public override FrameState NextState(FrameState currentState) { diff --git a/ErsatzTV.FFmpeg/Decoder/Cuvid/DecoderMpeg2Cuvid.cs b/ErsatzTV.FFmpeg/Decoder/Cuvid/DecoderMpeg2Cuvid.cs index 4306489d1..bbbf59626 100644 --- a/ErsatzTV.FFmpeg/Decoder/Cuvid/DecoderMpeg2Cuvid.cs +++ b/ErsatzTV.FFmpeg/Decoder/Cuvid/DecoderMpeg2Cuvid.cs @@ -35,8 +35,8 @@ public class DecoderMpeg2Cuvid : DecoderBase return result; } } - - public override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Hardware; + + protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Hardware; public override FrameState NextState(FrameState currentState) { diff --git a/ErsatzTV.FFmpeg/Decoder/Cuvid/DecoderMpeg4Cuvid.cs b/ErsatzTV.FFmpeg/Decoder/Cuvid/DecoderMpeg4Cuvid.cs index 7abfdfcd4..eb7fcb792 100644 --- a/ErsatzTV.FFmpeg/Decoder/Cuvid/DecoderMpeg4Cuvid.cs +++ b/ErsatzTV.FFmpeg/Decoder/Cuvid/DecoderMpeg4Cuvid.cs @@ -30,7 +30,7 @@ public class DecoderMpeg4Cuvid : DecoderBase } } - public override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Hardware; + protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Hardware; public override FrameState NextState(FrameState currentState) { diff --git a/ErsatzTV.FFmpeg/Decoder/Cuvid/DecoderVc1Cuvid.cs b/ErsatzTV.FFmpeg/Decoder/Cuvid/DecoderVc1Cuvid.cs index 9608e14ed..1209ce45c 100644 --- a/ErsatzTV.FFmpeg/Decoder/Cuvid/DecoderVc1Cuvid.cs +++ b/ErsatzTV.FFmpeg/Decoder/Cuvid/DecoderVc1Cuvid.cs @@ -28,7 +28,8 @@ public class DecoderVc1Cuvid : DecoderBase return result; } } - public override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Hardware; + + protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Hardware; public override FrameState NextState(FrameState currentState) { diff --git a/ErsatzTV.FFmpeg/Decoder/Cuvid/DecoderVp9Cuvid.cs b/ErsatzTV.FFmpeg/Decoder/Cuvid/DecoderVp9Cuvid.cs index fe23851f0..b6bb9a910 100644 --- a/ErsatzTV.FFmpeg/Decoder/Cuvid/DecoderVp9Cuvid.cs +++ b/ErsatzTV.FFmpeg/Decoder/Cuvid/DecoderVp9Cuvid.cs @@ -28,7 +28,8 @@ public class DecoderVp9Cuvid : DecoderBase return result; } } - public override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Hardware; + + protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Hardware; public override FrameState NextState(FrameState currentState) { diff --git a/ErsatzTV.FFmpeg/Decoder/DecoderBase.cs b/ErsatzTV.FFmpeg/Decoder/DecoderBase.cs index 4823eb259..57e0a1d52 100644 --- a/ErsatzTV.FFmpeg/Decoder/DecoderBase.cs +++ b/ErsatzTV.FFmpeg/Decoder/DecoderBase.cs @@ -1,8 +1,11 @@ -namespace ErsatzTV.FFmpeg.Decoder; +using ErsatzTV.FFmpeg.Environment; + +namespace ErsatzTV.FFmpeg.Decoder; public abstract class DecoderBase : IDecoder { - public abstract FrameDataLocation OutputFrameDataLocation { get; } + protected abstract FrameDataLocation OutputFrameDataLocation { get; } + public IList EnvironmentVariables => Array.Empty(); public IList GlobalOptions => Array.Empty(); public virtual IList InputOptions => new List { "-c:v", Name }; public IList FilterOptions => Array.Empty(); diff --git a/ErsatzTV.FFmpeg/Decoder/DecoderH264.cs b/ErsatzTV.FFmpeg/Decoder/DecoderH264.cs index b64165960..4a538b71d 100644 --- a/ErsatzTV.FFmpeg/Decoder/DecoderH264.cs +++ b/ErsatzTV.FFmpeg/Decoder/DecoderH264.cs @@ -3,5 +3,5 @@ public class DecoderH264 : DecoderBase { public override string Name => "h264"; - public override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software; + protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software; } diff --git a/ErsatzTV.FFmpeg/Decoder/DecoderHevc.cs b/ErsatzTV.FFmpeg/Decoder/DecoderHevc.cs index 5c870a799..f2e304e2d 100644 --- a/ErsatzTV.FFmpeg/Decoder/DecoderHevc.cs +++ b/ErsatzTV.FFmpeg/Decoder/DecoderHevc.cs @@ -3,5 +3,5 @@ public class DecoderHevc : DecoderBase { public override string Name => "hevc"; - public override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software; + protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software; } diff --git a/ErsatzTV.FFmpeg/Decoder/DecoderImplicit.cs b/ErsatzTV.FFmpeg/Decoder/DecoderImplicit.cs index 5c574e191..1840e4761 100644 --- a/ErsatzTV.FFmpeg/Decoder/DecoderImplicit.cs +++ b/ErsatzTV.FFmpeg/Decoder/DecoderImplicit.cs @@ -2,7 +2,7 @@ public class DecoderImplicit : DecoderBase { - public override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software; + protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software; public override string Name => string.Empty; public override IList InputOptions => Array.Empty(); } diff --git a/ErsatzTV.FFmpeg/Decoder/DecoderMpeg1.cs b/ErsatzTV.FFmpeg/Decoder/DecoderMpeg1.cs index 514c5023c..d22021334 100644 --- a/ErsatzTV.FFmpeg/Decoder/DecoderMpeg1.cs +++ b/ErsatzTV.FFmpeg/Decoder/DecoderMpeg1.cs @@ -3,5 +3,5 @@ public class DecoderMpeg1Video : DecoderBase { public override string Name => "mpeg1video"; - public override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software; + protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software; } diff --git a/ErsatzTV.FFmpeg/Decoder/DecoderMpeg2.cs b/ErsatzTV.FFmpeg/Decoder/DecoderMpeg2.cs index 33866ed18..702d201ec 100644 --- a/ErsatzTV.FFmpeg/Decoder/DecoderMpeg2.cs +++ b/ErsatzTV.FFmpeg/Decoder/DecoderMpeg2.cs @@ -3,5 +3,5 @@ public class DecoderMpeg2Video : DecoderBase { public override string Name => "mpeg2video"; - public override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software; + protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software; } diff --git a/ErsatzTV.FFmpeg/Decoder/DecoderMpeg4.cs b/ErsatzTV.FFmpeg/Decoder/DecoderMpeg4.cs index ea9006ea3..f2cb01025 100644 --- a/ErsatzTV.FFmpeg/Decoder/DecoderMpeg4.cs +++ b/ErsatzTV.FFmpeg/Decoder/DecoderMpeg4.cs @@ -3,5 +3,5 @@ public class DecoderMpeg4 : DecoderBase { public override string Name => "mpeg4"; - public override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software; + protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software; } diff --git a/ErsatzTV.FFmpeg/Decoder/DecoderMsMpeg4v2.cs b/ErsatzTV.FFmpeg/Decoder/DecoderMsMpeg4v2.cs index 6b6421575..68f63b94f 100644 --- a/ErsatzTV.FFmpeg/Decoder/DecoderMsMpeg4v2.cs +++ b/ErsatzTV.FFmpeg/Decoder/DecoderMsMpeg4v2.cs @@ -3,5 +3,5 @@ public class DecoderMsMpeg4V2 : DecoderBase { public override string Name => "msmpeg4v2"; - public override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software; + protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software; } diff --git a/ErsatzTV.FFmpeg/Decoder/DecoderMsMpeg4v3.cs b/ErsatzTV.FFmpeg/Decoder/DecoderMsMpeg4v3.cs index 3da89fbc7..2d2fe886b 100644 --- a/ErsatzTV.FFmpeg/Decoder/DecoderMsMpeg4v3.cs +++ b/ErsatzTV.FFmpeg/Decoder/DecoderMsMpeg4v3.cs @@ -3,5 +3,5 @@ public class DecoderMsMpeg4V3 : DecoderBase { public override string Name => "msmpeg4v3"; - public override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software; + protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software; } diff --git a/ErsatzTV.FFmpeg/Decoder/DecoderVaapi.cs b/ErsatzTV.FFmpeg/Decoder/DecoderVaapi.cs index 88698ed77..e38d86a4b 100644 --- a/ErsatzTV.FFmpeg/Decoder/DecoderVaapi.cs +++ b/ErsatzTV.FFmpeg/Decoder/DecoderVaapi.cs @@ -4,7 +4,7 @@ namespace ErsatzTV.FFmpeg.Decoder; public class DecoderVaapi : DecoderBase { - public override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software; + protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software; public override string Name => "implicit_vaapi"; public override IList InputOptions => Array.Empty(); diff --git a/ErsatzTV.FFmpeg/Decoder/DecoderVc1.cs b/ErsatzTV.FFmpeg/Decoder/DecoderVc1.cs index a792f22d3..0b2a9adcf 100644 --- a/ErsatzTV.FFmpeg/Decoder/DecoderVc1.cs +++ b/ErsatzTV.FFmpeg/Decoder/DecoderVc1.cs @@ -3,5 +3,5 @@ public class DecoderVc1 : DecoderBase { public override string Name => "vc1"; - public override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software; + protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software; } diff --git a/ErsatzTV.FFmpeg/Decoder/DecoderVp9.cs b/ErsatzTV.FFmpeg/Decoder/DecoderVp9.cs index fd0dad076..822fab48e 100644 --- a/ErsatzTV.FFmpeg/Decoder/DecoderVp9.cs +++ b/ErsatzTV.FFmpeg/Decoder/DecoderVp9.cs @@ -3,5 +3,5 @@ public class DecoderVp9 : DecoderBase { public override string Name => "vp9"; - public override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software; + protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software; } diff --git a/ErsatzTV.FFmpeg/Decoder/Qsv/DecoderH264Qsv.cs b/ErsatzTV.FFmpeg/Decoder/Qsv/DecoderH264Qsv.cs index 8e311d196..3666490f6 100644 --- a/ErsatzTV.FFmpeg/Decoder/Qsv/DecoderH264Qsv.cs +++ b/ErsatzTV.FFmpeg/Decoder/Qsv/DecoderH264Qsv.cs @@ -6,7 +6,7 @@ public class DecoderH264Qsv : DecoderBase { public override string Name => "h264_qsv"; - public override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Hardware; + protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Hardware; public override FrameState NextState(FrameState currentState) { diff --git a/ErsatzTV.FFmpeg/Decoder/Qsv/DecoderHevcQsv.cs b/ErsatzTV.FFmpeg/Decoder/Qsv/DecoderHevcQsv.cs index 2de3a1fbc..00be75d07 100644 --- a/ErsatzTV.FFmpeg/Decoder/Qsv/DecoderHevcQsv.cs +++ b/ErsatzTV.FFmpeg/Decoder/Qsv/DecoderHevcQsv.cs @@ -4,5 +4,5 @@ public class DecoderHevcQsv : DecoderBase { public override string Name => "hevc_qsv"; - public override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Hardware; + protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Hardware; } diff --git a/ErsatzTV.FFmpeg/Decoder/Qsv/DecoderMpeg2Qsv.cs b/ErsatzTV.FFmpeg/Decoder/Qsv/DecoderMpeg2Qsv.cs index 6b6b00fbd..b0ce834d5 100644 --- a/ErsatzTV.FFmpeg/Decoder/Qsv/DecoderMpeg2Qsv.cs +++ b/ErsatzTV.FFmpeg/Decoder/Qsv/DecoderMpeg2Qsv.cs @@ -4,5 +4,5 @@ public class DecoderMpeg2Qsv : DecoderBase { public override string Name => "mpeg2_qsv"; - public override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Hardware; + protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Hardware; } diff --git a/ErsatzTV.FFmpeg/Decoder/Qsv/DecoderVc1Qsv.cs b/ErsatzTV.FFmpeg/Decoder/Qsv/DecoderVc1Qsv.cs index caefab7e4..06c785cae 100644 --- a/ErsatzTV.FFmpeg/Decoder/Qsv/DecoderVc1Qsv.cs +++ b/ErsatzTV.FFmpeg/Decoder/Qsv/DecoderVc1Qsv.cs @@ -4,5 +4,5 @@ public class DecoderVc1Qsv : DecoderBase { public override string Name => "vc1_qsv"; - public override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Hardware; + protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Hardware; } diff --git a/ErsatzTV.FFmpeg/Decoder/Qsv/DecoderVp9Qsv.cs b/ErsatzTV.FFmpeg/Decoder/Qsv/DecoderVp9Qsv.cs index 2622a51d3..023df2853 100644 --- a/ErsatzTV.FFmpeg/Decoder/Qsv/DecoderVp9Qsv.cs +++ b/ErsatzTV.FFmpeg/Decoder/Qsv/DecoderVp9Qsv.cs @@ -4,5 +4,5 @@ public class DecoderVp9Qsv : DecoderBase { public override string Name => "vp9_qsv"; - public override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Hardware; + protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Hardware; } diff --git a/ErsatzTV.FFmpeg/Encoder/EncoderBase.cs b/ErsatzTV.FFmpeg/Encoder/EncoderBase.cs index 17e3064d2..808b0d082 100644 --- a/ErsatzTV.FFmpeg/Encoder/EncoderBase.cs +++ b/ErsatzTV.FFmpeg/Encoder/EncoderBase.cs @@ -1,7 +1,10 @@ -namespace ErsatzTV.FFmpeg.Encoder; +using ErsatzTV.FFmpeg.Environment; + +namespace ErsatzTV.FFmpeg.Encoder; public abstract class EncoderBase : IEncoder { + public IList EnvironmentVariables => Array.Empty(); public IList GlobalOptions => Array.Empty(); public IList InputOptions => Array.Empty(); public IList FilterOptions => Array.Empty(); diff --git a/ErsatzTV.FFmpeg/Environment/EnvironmentVariable.cs b/ErsatzTV.FFmpeg/Environment/EnvironmentVariable.cs new file mode 100644 index 000000000..caafc38f6 --- /dev/null +++ b/ErsatzTV.FFmpeg/Environment/EnvironmentVariable.cs @@ -0,0 +1,3 @@ +namespace ErsatzTV.FFmpeg.Environment; + +public record EnvironmentVariable(string Key, string Value); diff --git a/ErsatzTV.FFmpeg/Environment/FFReportVariable.cs b/ErsatzTV.FFmpeg/Environment/FFReportVariable.cs new file mode 100644 index 000000000..9ca2a432a --- /dev/null +++ b/ErsatzTV.FFmpeg/Environment/FFReportVariable.cs @@ -0,0 +1,46 @@ +using System.Runtime.InteropServices; + +namespace ErsatzTV.FFmpeg.Environment; + +public class FFReportVariable : IPipelineStep +{ + private readonly string _reportsFolder; + private readonly IList _inputFiles; + + public FFReportVariable(string reportsFolder, IList inputFiles) + { + _reportsFolder = reportsFolder; + _inputFiles = inputFiles; + } + + public IList EnvironmentVariables + { + get + { + string fileName = _inputFiles.OfType().Any() + ? Path.Combine(_reportsFolder, "ffmpeg-%t-concat.log") + : Path.Combine(_reportsFolder, "ffmpeg-%t-transcode.log"); + + // rework filename in a format that works on windows + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + // \ is escape, so use / for directory separators + fileName = fileName.Replace(@"\", @"/"); + + // colon after drive letter needs to be escaped + fileName = fileName.Replace(@":/", @"\:/"); + } + + return new List + { + new("FFREPORT", $"file={fileName}:level=32") + }; + } + } + + public IList GlobalOptions => Array.Empty(); + public IList InputOptions => Array.Empty(); + public IList FilterOptions => Array.Empty(); + public IList OutputOptions => Array.Empty(); + public FrameState NextState(FrameState currentState) => currentState with { SaveReport = true }; +} diff --git a/ErsatzTV.FFmpeg/Environment/LibvaDriverNameVariable.cs b/ErsatzTV.FFmpeg/Environment/LibvaDriverNameVariable.cs new file mode 100644 index 000000000..04ca2a5d2 --- /dev/null +++ b/ErsatzTV.FFmpeg/Environment/LibvaDriverNameVariable.cs @@ -0,0 +1,22 @@ +namespace ErsatzTV.FFmpeg.Environment; + +public class LibvaDriverNameVariable : IPipelineStep +{ + private readonly string _driverName; + + public LibvaDriverNameVariable(string driverName) + { + _driverName = driverName; + } + + public IList EnvironmentVariables => new List + { + new("LIBVA_DRIVER_NAME", _driverName) + }; + + public IList GlobalOptions => Array.Empty(); + public IList InputOptions => Array.Empty(); + public IList FilterOptions => Array.Empty(); + public IList OutputOptions => Array.Empty(); + public FrameState NextState(FrameState currentState) => currentState with { VaapiDriver = _driverName }; +} diff --git a/ErsatzTV.FFmpeg/Filter/BaseFilter.cs b/ErsatzTV.FFmpeg/Filter/BaseFilter.cs index 68b71d026..11954cd97 100644 --- a/ErsatzTV.FFmpeg/Filter/BaseFilter.cs +++ b/ErsatzTV.FFmpeg/Filter/BaseFilter.cs @@ -1,7 +1,10 @@ -namespace ErsatzTV.FFmpeg.Filter; +using ErsatzTV.FFmpeg.Environment; + +namespace ErsatzTV.FFmpeg.Filter; public abstract class BaseFilter : IPipelineFilterStep { + public IList EnvironmentVariables => Array.Empty(); public virtual IList GlobalOptions => Array.Empty(); public virtual IList InputOptions => Array.Empty(); public virtual IList FilterOptions => Array.Empty(); diff --git a/ErsatzTV.FFmpeg/Filter/ComplexFilter.cs b/ErsatzTV.FFmpeg/Filter/ComplexFilter.cs index ba0ff697c..2b302c03f 100644 --- a/ErsatzTV.FFmpeg/Filter/ComplexFilter.cs +++ b/ErsatzTV.FFmpeg/Filter/ComplexFilter.cs @@ -1,4 +1,6 @@ -namespace ErsatzTV.FFmpeg.Filter; +using ErsatzTV.FFmpeg.Environment; + +namespace ErsatzTV.FFmpeg.Filter; public class ComplexFilter : IPipelineStep { @@ -73,6 +75,7 @@ public class ComplexFilter : IPipelineStep return result; } + public IList EnvironmentVariables => Array.Empty(); public IList GlobalOptions => Array.Empty(); public IList InputOptions => Array.Empty(); public IList FilterOptions => Arguments(); diff --git a/ErsatzTV.FFmpeg/Format/ConcatInputFormat.cs b/ErsatzTV.FFmpeg/Format/ConcatInputFormat.cs index 434b88e31..923578c02 100644 --- a/ErsatzTV.FFmpeg/Format/ConcatInputFormat.cs +++ b/ErsatzTV.FFmpeg/Format/ConcatInputFormat.cs @@ -1,7 +1,10 @@ -namespace ErsatzTV.FFmpeg.Format; +using ErsatzTV.FFmpeg.Environment; + +namespace ErsatzTV.FFmpeg.Format; public class ConcatInputFormat : IPipelineStep { + public IList EnvironmentVariables => Array.Empty(); public IList GlobalOptions => Array.Empty(); public IList InputOptions => new List diff --git a/ErsatzTV.FFmpeg/FrameState.cs b/ErsatzTV.FFmpeg/FrameState.cs index 1a4db3547..d949430d4 100644 --- a/ErsatzTV.FFmpeg/FrameState.cs +++ b/ErsatzTV.FFmpeg/FrameState.cs @@ -5,7 +5,10 @@ using LanguageExt; namespace ErsatzTV.FFmpeg; public record FrameState( + bool SaveReport, HardwareAccelerationMode HardwareAccelerationMode, + Option VaapiDriver, + Option VaapiDevice, bool Realtime, bool InfiniteLoop, Option Start, @@ -36,9 +39,12 @@ public record FrameState( long PtsOffset, FrameDataLocation FrameDataLocation = FrameDataLocation.Unknown) { - public static FrameState Concat(string channelName, FrameSize resolution) => + public static FrameState Concat(bool saveReport, string channelName, FrameSize resolution) => new( + saveReport, HardwareAccelerationMode.None, + Option.None, + Option.None, true, // realtime true, // infinite loop Option.None, diff --git a/ErsatzTV.FFmpeg/IPipelineStep.cs b/ErsatzTV.FFmpeg/IPipelineStep.cs index c10a7495c..d87ff2a53 100644 --- a/ErsatzTV.FFmpeg/IPipelineStep.cs +++ b/ErsatzTV.FFmpeg/IPipelineStep.cs @@ -1,7 +1,10 @@ -namespace ErsatzTV.FFmpeg; +using ErsatzTV.FFmpeg.Environment; + +namespace ErsatzTV.FFmpeg; public interface IPipelineStep { + IList EnvironmentVariables { get; } IList GlobalOptions { get; } IList InputOptions { get; } IList FilterOptions { get; } diff --git a/ErsatzTV.FFmpeg/Option/FrameRateOutputOption.cs b/ErsatzTV.FFmpeg/Option/FrameRateOutputOption.cs index 7628df548..d461fcaca 100644 --- a/ErsatzTV.FFmpeg/Option/FrameRateOutputOption.cs +++ b/ErsatzTV.FFmpeg/Option/FrameRateOutputOption.cs @@ -1,4 +1,6 @@ -namespace ErsatzTV.FFmpeg.Option; +using ErsatzTV.FFmpeg.Environment; + +namespace ErsatzTV.FFmpeg.Option; public class FrameRateOutputOption : IPipelineStep { @@ -9,7 +11,7 @@ public class FrameRateOutputOption : IPipelineStep _frameRate = frameRate; } - public FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Unknown; + public IList EnvironmentVariables => Array.Empty(); public IList GlobalOptions => Array.Empty(); public IList InputOptions => Array.Empty(); public IList FilterOptions => Array.Empty(); diff --git a/ErsatzTV.FFmpeg/Option/GlobalOption.cs b/ErsatzTV.FFmpeg/Option/GlobalOption.cs index a32aed115..89d0db992 100644 --- a/ErsatzTV.FFmpeg/Option/GlobalOption.cs +++ b/ErsatzTV.FFmpeg/Option/GlobalOption.cs @@ -1,8 +1,10 @@ -namespace ErsatzTV.FFmpeg.Option; +using ErsatzTV.FFmpeg.Environment; + +namespace ErsatzTV.FFmpeg.Option; public abstract class GlobalOption : IPipelineStep { - public FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Unknown; + public IList EnvironmentVariables => Array.Empty(); public abstract IList GlobalOptions { get; } public IList InputOptions => Array.Empty(); public IList FilterOptions => Array.Empty(); diff --git a/ErsatzTV.FFmpeg/Option/HardwareAcceleration/AvailableHardwareAccelerationOptions.cs b/ErsatzTV.FFmpeg/Option/HardwareAcceleration/AvailableHardwareAccelerationOptions.cs index faf8ed5f7..19ac3a91b 100644 --- a/ErsatzTV.FFmpeg/Option/HardwareAcceleration/AvailableHardwareAccelerationOptions.cs +++ b/ErsatzTV.FFmpeg/Option/HardwareAcceleration/AvailableHardwareAccelerationOptions.cs @@ -1,16 +1,37 @@ -namespace ErsatzTV.FFmpeg.Option.HardwareAcceleration; +using LanguageExt; +using Microsoft.Extensions.Logging; + +namespace ErsatzTV.FFmpeg.Option.HardwareAcceleration; public static class AvailableHardwareAccelerationOptions { - public static IPipelineStep ForMode(HardwareAccelerationMode mode) - { - return mode switch + public static Option ForMode( + HardwareAccelerationMode mode, + Option vaapiDevice, + ILogger logger) => + mode switch { HardwareAccelerationMode.Nvenc => new CudaHardwareAccelerationOption(), HardwareAccelerationMode.Qsv => new QsvHardwareAccelerationOption(), - HardwareAccelerationMode.Vaapi => new VaapiHardwareAccelerationOption(), + HardwareAccelerationMode.Vaapi => GetVaapiAcceleration(vaapiDevice, logger), HardwareAccelerationMode.VideoToolbox => new VideoToolboxHardwareAccelerationOption(), - _ => throw new ArgumentOutOfRangeException(nameof(mode), mode, null) + _ => LogUnknownMode(mode, logger) }; + + private static Option GetVaapiAcceleration(Option vaapiDevice, ILogger logger) + { + foreach (string device in vaapiDevice) + { + return new VaapiHardwareAccelerationOption(device); + } + + logger.LogWarning("VAAPI device name is missing; falling back to software mode"); + return Option.None; + } + + private static Option LogUnknownMode(HardwareAccelerationMode mode, ILogger logger) + { + logger.LogWarning("Unexpected hardware acceleration mode {AccelMode}; may have playback issues", mode); + return Option.None; } } diff --git a/ErsatzTV.FFmpeg/Option/HardwareAcceleration/VaapiHardwareAccelerationOption.cs b/ErsatzTV.FFmpeg/Option/HardwareAcceleration/VaapiHardwareAccelerationOption.cs index 5687401f5..331ac3507 100644 --- a/ErsatzTV.FFmpeg/Option/HardwareAcceleration/VaapiHardwareAccelerationOption.cs +++ b/ErsatzTV.FFmpeg/Option/HardwareAcceleration/VaapiHardwareAccelerationOption.cs @@ -2,8 +2,15 @@ public class VaapiHardwareAccelerationOption : GlobalOption { + private readonly string _vaapiDevice; + + public VaapiHardwareAccelerationOption(string vaapiDevice) + { + _vaapiDevice = vaapiDevice; + } + public override IList GlobalOptions => new List - { "-hwaccel", "vaapi", "-hwaccel_output_format", "vaapi" }; + { "-hwaccel", "vaapi", "-vaapi_device", _vaapiDevice, "-hwaccel_output_format", "vaapi" }; public override FrameState NextState(FrameState currentState) => currentState with { diff --git a/ErsatzTV.FFmpeg/Option/InfiniteLoopInputOption.cs b/ErsatzTV.FFmpeg/Option/InfiniteLoopInputOption.cs index 714050ad8..374457ff3 100644 --- a/ErsatzTV.FFmpeg/Option/InfiniteLoopInputOption.cs +++ b/ErsatzTV.FFmpeg/Option/InfiniteLoopInputOption.cs @@ -1,4 +1,6 @@ -namespace ErsatzTV.FFmpeg.Option; +using ErsatzTV.FFmpeg.Environment; + +namespace ErsatzTV.FFmpeg.Option; public class InfiniteLoopInputOption : IPipelineStep { @@ -9,6 +11,7 @@ public class InfiniteLoopInputOption : IPipelineStep _currentState = currentState; } + public IList EnvironmentVariables => Array.Empty(); public IList GlobalOptions => Array.Empty(); public IList InputOptions => new List { "-stream_loop", "-1" }; public IList FilterOptions => Array.Empty(); diff --git a/ErsatzTV.FFmpeg/Option/OutputOption.cs b/ErsatzTV.FFmpeg/Option/OutputOption.cs index 510f71623..36fba5841 100644 --- a/ErsatzTV.FFmpeg/Option/OutputOption.cs +++ b/ErsatzTV.FFmpeg/Option/OutputOption.cs @@ -1,8 +1,11 @@ -namespace ErsatzTV.FFmpeg.Option; +using ErsatzTV.FFmpeg.Environment; + +namespace ErsatzTV.FFmpeg.Option; public abstract class OutputOption : IPipelineStep { public FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Unknown; + public IList EnvironmentVariables => Array.Empty(); public IList GlobalOptions => Array.Empty(); public IList InputOptions => Array.Empty(); public IList FilterOptions => Array.Empty(); diff --git a/ErsatzTV.FFmpeg/Option/RealtimeInputOption.cs b/ErsatzTV.FFmpeg/Option/RealtimeInputOption.cs index 280566520..d81960e99 100644 --- a/ErsatzTV.FFmpeg/Option/RealtimeInputOption.cs +++ b/ErsatzTV.FFmpeg/Option/RealtimeInputOption.cs @@ -1,7 +1,11 @@ -namespace ErsatzTV.FFmpeg.Option; +using ErsatzTV.FFmpeg.Environment; + +namespace ErsatzTV.FFmpeg.Option; public class RealtimeInputOption : IPipelineStep { + public IList EnvironmentVariables => Array.Empty(); + // some builds of ffmpeg seem to hang when realtime input is requested with multithreading, // so we force a single thread here public IList GlobalOptions => new List { "-threads", "1" }; diff --git a/ErsatzTV.FFmpeg/Option/StreamSeekInputOption.cs b/ErsatzTV.FFmpeg/Option/StreamSeekInputOption.cs index d454f0bb0..0f4053a92 100644 --- a/ErsatzTV.FFmpeg/Option/StreamSeekInputOption.cs +++ b/ErsatzTV.FFmpeg/Option/StreamSeekInputOption.cs @@ -1,4 +1,6 @@ -namespace ErsatzTV.FFmpeg.Option; +using ErsatzTV.FFmpeg.Environment; + +namespace ErsatzTV.FFmpeg.Option; public class StreamSeekInputOption : IPipelineStep { @@ -10,6 +12,7 @@ public class StreamSeekInputOption : IPipelineStep } public FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Unknown; + public IList EnvironmentVariables => Array.Empty(); public IList GlobalOptions => Array.Empty(); public IList InputOptions => _start == TimeSpan.Zero ? Array.Empty() : new List { "-ss", $"{_start:c}" }; diff --git a/ErsatzTV.FFmpeg/Option/TimeLimitOutputOption.cs b/ErsatzTV.FFmpeg/Option/TimeLimitOutputOption.cs index 889d767a5..7718bca9e 100644 --- a/ErsatzTV.FFmpeg/Option/TimeLimitOutputOption.cs +++ b/ErsatzTV.FFmpeg/Option/TimeLimitOutputOption.cs @@ -1,4 +1,6 @@ -namespace ErsatzTV.FFmpeg.Option; +using ErsatzTV.FFmpeg.Environment; + +namespace ErsatzTV.FFmpeg.Option; public class TimeLimitOutputOption : IPipelineStep { @@ -10,6 +12,7 @@ public class TimeLimitOutputOption : IPipelineStep } public FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Unknown; + public IList EnvironmentVariables => Array.Empty(); public IList GlobalOptions => Array.Empty(); public IList InputOptions => Array.Empty(); public IList FilterOptions => Array.Empty(); diff --git a/ErsatzTV.FFmpeg/OutputFormat/OutputFormatHls.cs b/ErsatzTV.FFmpeg/OutputFormat/OutputFormatHls.cs index 8206aafd1..61844f670 100644 --- a/ErsatzTV.FFmpeg/OutputFormat/OutputFormatHls.cs +++ b/ErsatzTV.FFmpeg/OutputFormat/OutputFormatHls.cs @@ -1,4 +1,5 @@ -using LanguageExt; +using ErsatzTV.FFmpeg.Environment; +using LanguageExt; namespace ErsatzTV.FFmpeg.OutputFormat; @@ -21,7 +22,7 @@ public class OutputFormatHls : IPipelineStep _playlistPath = playlistPath; } - public FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software; + public IList EnvironmentVariables => Array.Empty(); public IList GlobalOptions => Array.Empty(); public IList InputOptions => Array.Empty(); public IList FilterOptions => Array.Empty(); diff --git a/ErsatzTV.FFmpeg/OutputFormat/OutputFormatMpegTs.cs b/ErsatzTV.FFmpeg/OutputFormat/OutputFormatMpegTs.cs index 7eb8f0213..6e9687606 100644 --- a/ErsatzTV.FFmpeg/OutputFormat/OutputFormatMpegTs.cs +++ b/ErsatzTV.FFmpeg/OutputFormat/OutputFormatMpegTs.cs @@ -1,8 +1,10 @@ -namespace ErsatzTV.FFmpeg.OutputFormat; +using ErsatzTV.FFmpeg.Environment; + +namespace ErsatzTV.FFmpeg.OutputFormat; public class OutputFormatMpegTs : IPipelineStep { - public FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software; + public IList EnvironmentVariables => Array.Empty(); public IList GlobalOptions => Array.Empty(); public IList InputOptions => Array.Empty(); public IList FilterOptions => Array.Empty(); diff --git a/ErsatzTV.FFmpeg/PipelineBuilder.cs b/ErsatzTV.FFmpeg/PipelineBuilder.cs index 40313e8eb..778484b2b 100644 --- a/ErsatzTV.FFmpeg/PipelineBuilder.cs +++ b/ErsatzTV.FFmpeg/PipelineBuilder.cs @@ -1,5 +1,6 @@ using ErsatzTV.FFmpeg.Decoder; using ErsatzTV.FFmpeg.Encoder; +using ErsatzTV.FFmpeg.Environment; using ErsatzTV.FFmpeg.Filter; using ErsatzTV.FFmpeg.Format; using ErsatzTV.FFmpeg.Option; @@ -18,9 +19,10 @@ public class PipelineBuilder private readonly List _audioFilterSteps; private readonly List _videoFilterSteps; private readonly IList _inputFiles; + private readonly string _reportsFolder; private readonly ILogger _logger; - public PipelineBuilder(IList inputFiles, ILogger logger) + public PipelineBuilder(IList inputFiles, string reportsFolder, ILogger logger) { _pipelineSteps = new List { @@ -38,6 +40,7 @@ public class PipelineBuilder _videoFilterSteps = new List(); _inputFiles = inputFiles; + _reportsFolder = reportsFolder; _logger = logger; } @@ -68,7 +71,10 @@ public class PipelineBuilder } var currentState = new FrameState( + false, // save report HardwareAccelerationMode.None, + Option.None, + Option.None, false, // realtime false, // infinite loop Option.None, @@ -98,6 +104,13 @@ public class PipelineBuilder Option.None, 0); + if (desiredState.SaveReport && !currentState.SaveReport) + { + IPipelineStep step = new FFReportVariable(_reportsFolder, _inputFiles); + currentState = step.NextState(currentState); + _pipelineSteps.Add(step); + } + foreach (TimeSpan desiredStart in desiredState.Start) { if (currentState.Start != desiredStart) @@ -129,13 +142,37 @@ public class PipelineBuilder } else { - // TODO: prioritize which codecs are used (hw accel) if (currentState.HardwareAccelerationMode != desiredState.HardwareAccelerationMode) { - IPipelineStep accel = - AvailableHardwareAccelerationOptions.ForMode(desiredState.HardwareAccelerationMode); - currentState = accel.NextState(currentState); - _pipelineSteps.Add(accel); + Option maybeAccel = AvailableHardwareAccelerationOptions.ForMode( + desiredState.HardwareAccelerationMode, + desiredState.VaapiDevice, + _logger); + + if (maybeAccel.IsNone) + { + desiredState = desiredState with + { + // disable hw accel if we don't match anything + HardwareAccelerationMode = HardwareAccelerationMode.None + }; + } + + foreach (IPipelineStep accel in maybeAccel) + { + currentState = accel.NextState(currentState); + _pipelineSteps.Add(accel); + } + } + + foreach (string desiredVaapiDriver in desiredState.VaapiDriver) + { + if (currentState.VaapiDriver != desiredVaapiDriver) + { + IPipelineStep step = new LibvaDriverNameVariable(desiredVaapiDriver); + currentState = step.NextState(currentState); + _pipelineSteps.Add(step); + } } foreach (IDecoder decoder in AvailableDecoders.ForVideoFormat(currentState, desiredState, _logger)) diff --git a/ErsatzTV.FFmpeg/Protocol/PipeProtocol.cs b/ErsatzTV.FFmpeg/Protocol/PipeProtocol.cs index c43c0ca67..587e85f74 100644 --- a/ErsatzTV.FFmpeg/Protocol/PipeProtocol.cs +++ b/ErsatzTV.FFmpeg/Protocol/PipeProtocol.cs @@ -1,8 +1,10 @@ -namespace ErsatzTV.FFmpeg.Protocol; +using ErsatzTV.FFmpeg.Environment; + +namespace ErsatzTV.FFmpeg.Protocol; public class PipeProtocol : IPipelineStep { - public FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software; + public IList EnvironmentVariables => Array.Empty(); public IList GlobalOptions => Array.Empty(); public IList InputOptions => Array.Empty(); public IList FilterOptions => Array.Empty(); diff --git a/ErsatzTV.sln.DotSettings b/ErsatzTV.sln.DotSettings index 2a8ade84d..b25e4a4ac 100644 --- a/ErsatzTV.sln.DotSettings +++ b/ErsatzTV.sln.DotSettings @@ -1,6 +1,7 @@  True DTO + FF HDHR LE NV @@ -28,6 +29,7 @@ True True True + True True True True @@ -36,6 +38,7 @@ True True True + True True True