Browse Source

use vaapi driver settings with new transcoder logic (#635)

* add LIBVA_DRIVER_NAME env var

* add vaapi device name

* add FFREPORT env var

* fixes
pull/636/head
Jason Dove 4 years ago committed by GitHub
parent
commit
2f2d7952dd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 45
      ErsatzTV.Core/FFmpeg/FFmpegLibraryProcessService.cs
  2. 14
      ErsatzTV.FFmpeg.Tests/PipelineBuilderTests.cs
  3. 9
      ErsatzTV.FFmpeg/CommandGenerator.cs
  4. 2
      ErsatzTV.FFmpeg/Decoder/Cuvid/DecoderH264Cuvid.cs
  5. 3
      ErsatzTV.FFmpeg/Decoder/Cuvid/DecoderHevcCuvid.cs
  6. 4
      ErsatzTV.FFmpeg/Decoder/Cuvid/DecoderMpeg2Cuvid.cs
  7. 2
      ErsatzTV.FFmpeg/Decoder/Cuvid/DecoderMpeg4Cuvid.cs
  8. 3
      ErsatzTV.FFmpeg/Decoder/Cuvid/DecoderVc1Cuvid.cs
  9. 3
      ErsatzTV.FFmpeg/Decoder/Cuvid/DecoderVp9Cuvid.cs
  10. 7
      ErsatzTV.FFmpeg/Decoder/DecoderBase.cs
  11. 2
      ErsatzTV.FFmpeg/Decoder/DecoderH264.cs
  12. 2
      ErsatzTV.FFmpeg/Decoder/DecoderHevc.cs
  13. 2
      ErsatzTV.FFmpeg/Decoder/DecoderImplicit.cs
  14. 2
      ErsatzTV.FFmpeg/Decoder/DecoderMpeg1.cs
  15. 2
      ErsatzTV.FFmpeg/Decoder/DecoderMpeg2.cs
  16. 2
      ErsatzTV.FFmpeg/Decoder/DecoderMpeg4.cs
  17. 2
      ErsatzTV.FFmpeg/Decoder/DecoderMsMpeg4v2.cs
  18. 2
      ErsatzTV.FFmpeg/Decoder/DecoderMsMpeg4v3.cs
  19. 2
      ErsatzTV.FFmpeg/Decoder/DecoderVaapi.cs
  20. 2
      ErsatzTV.FFmpeg/Decoder/DecoderVc1.cs
  21. 2
      ErsatzTV.FFmpeg/Decoder/DecoderVp9.cs
  22. 2
      ErsatzTV.FFmpeg/Decoder/Qsv/DecoderH264Qsv.cs
  23. 2
      ErsatzTV.FFmpeg/Decoder/Qsv/DecoderHevcQsv.cs
  24. 2
      ErsatzTV.FFmpeg/Decoder/Qsv/DecoderMpeg2Qsv.cs
  25. 2
      ErsatzTV.FFmpeg/Decoder/Qsv/DecoderVc1Qsv.cs
  26. 2
      ErsatzTV.FFmpeg/Decoder/Qsv/DecoderVp9Qsv.cs
  27. 5
      ErsatzTV.FFmpeg/Encoder/EncoderBase.cs
  28. 3
      ErsatzTV.FFmpeg/Environment/EnvironmentVariable.cs
  29. 46
      ErsatzTV.FFmpeg/Environment/FFReportVariable.cs
  30. 22
      ErsatzTV.FFmpeg/Environment/LibvaDriverNameVariable.cs
  31. 5
      ErsatzTV.FFmpeg/Filter/BaseFilter.cs
  32. 5
      ErsatzTV.FFmpeg/Filter/ComplexFilter.cs
  33. 5
      ErsatzTV.FFmpeg/Format/ConcatInputFormat.cs
  34. 8
      ErsatzTV.FFmpeg/FrameState.cs
  35. 5
      ErsatzTV.FFmpeg/IPipelineStep.cs
  36. 6
      ErsatzTV.FFmpeg/Option/FrameRateOutputOption.cs
  37. 6
      ErsatzTV.FFmpeg/Option/GlobalOption.cs
  38. 33
      ErsatzTV.FFmpeg/Option/HardwareAcceleration/AvailableHardwareAccelerationOptions.cs
  39. 9
      ErsatzTV.FFmpeg/Option/HardwareAcceleration/VaapiHardwareAccelerationOption.cs
  40. 5
      ErsatzTV.FFmpeg/Option/InfiniteLoopInputOption.cs
  41. 5
      ErsatzTV.FFmpeg/Option/OutputOption.cs
  42. 6
      ErsatzTV.FFmpeg/Option/RealtimeInputOption.cs
  43. 5
      ErsatzTV.FFmpeg/Option/StreamSeekInputOption.cs
  44. 5
      ErsatzTV.FFmpeg/Option/TimeLimitOutputOption.cs
  45. 5
      ErsatzTV.FFmpeg/OutputFormat/OutputFormatHls.cs
  46. 6
      ErsatzTV.FFmpeg/OutputFormat/OutputFormatMpegTs.cs
  47. 49
      ErsatzTV.FFmpeg/PipelineBuilder.cs
  48. 6
      ErsatzTV.FFmpeg/Protocol/PipeProtocol.cs
  49. 3
      ErsatzTV.sln.DotSettings

45
ErsatzTV.Core/FFmpeg/FFmpegLibraryProcessService.cs

@ -2,12 +2,14 @@
using System.Diagnostics; using System.Diagnostics;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using ErsatzTV.Core.Domain; using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Domain.Filler; using ErsatzTV.Core.Domain.Filler;
using ErsatzTV.Core.Interfaces.FFmpeg; using ErsatzTV.Core.Interfaces.FFmpeg;
using ErsatzTV.FFmpeg; using ErsatzTV.FFmpeg;
using ErsatzTV.FFmpeg.Environment;
using ErsatzTV.FFmpeg.Format; using ErsatzTV.FFmpeg.Format;
using ErsatzTV.FFmpeg.OutputFormat; using ErsatzTV.FFmpeg.OutputFormat;
using LanguageExt; using LanguageExt;
@ -122,7 +124,10 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
: Option<string>.None; : Option<string>.None;
var desiredState = new FrameState( var desiredState = new FrameState(
saveReports,
hwAccel, hwAccel,
VaapiDriverName(hwAccel, vaapiDriver),
VaapiDeviceName(hwAccel, vaapiDevice),
playbackSettings.RealtimeOutput, playbackSettings.RealtimeOutput,
false, false,
playbackSettings.StreamSeek, playbackSettings.StreamSeek,
@ -170,7 +175,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
public Process ConcatChannel(string ffmpegPath, bool saveReports, Channel channel, string scheme, string host) 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 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<InputFile> var inputFiles = new List<InputFile>
{ {
@ -218,12 +223,13 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
private Process GetProcess(string ffmpegPath, IList<InputFile> inputFiles, FrameState desiredState) private Process GetProcess(string ffmpegPath, IList<InputFile> inputFiles, FrameState desiredState)
{ {
var pipelineBuilder = new PipelineBuilder(inputFiles, _logger); var pipelineBuilder = new PipelineBuilder(inputFiles, FileSystemLayout.FFmpegReportsFolder, _logger);
IList<IPipelineStep> pipelineSteps = pipelineBuilder.Build(desiredState); IList<IPipelineStep> pipelineSteps = pipelineBuilder.Build(desiredState);
_logger.LogDebug("FFmpeg pipeline {PipelineSteps}", pipelineSteps); _logger.LogDebug("FFmpeg pipeline {PipelineSteps}", pipelineSteps.Map(ps => ps.GetType().Name));
IList<EnvironmentVariable> environmentVariables = CommandGenerator.GenerateEnvironmentVariables(pipelineSteps);
IList<string> arguments = CommandGenerator.GenerateArguments(inputFiles, pipelineSteps); IList<string> arguments = CommandGenerator.GenerateArguments(inputFiles, pipelineSteps);
var startInfo = new ProcessStartInfo var startInfo = new ProcessStartInfo
@ -236,6 +242,16 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
StandardOutputEncoding = Encoding.UTF8 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) foreach (string argument in arguments)
{ {
startInfo.ArgumentList.Add(argument); startInfo.ArgumentList.Add(argument);
@ -246,4 +262,27 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
StartInfo = startInfo StartInfo = startInfo
}; };
} }
private static Option<string> 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<string>.None;
}
private static Option<string> VaapiDeviceName(HardwareAccelerationMode accelerationMode, string vaapiDevice)
{
return accelerationMode == HardwareAccelerationMode.Vaapi ? vaapiDevice : Option<string>.None;
}
} }

14
ErsatzTV.FFmpeg.Tests/PipelineBuilderTests.cs

@ -30,7 +30,10 @@ public class PipelineGeneratorTests
var inputFiles = new List<InputFile> { testFile }; var inputFiles = new List<InputFile> { testFile };
var desiredState = new FrameState( var desiredState = new FrameState(
false,
HardwareAccelerationMode.None, HardwareAccelerationMode.None,
Option<string>.None,
Option<string>.None,
true, true,
false, false,
Option<TimeSpan>.None, Option<TimeSpan>.None,
@ -60,7 +63,7 @@ public class PipelineGeneratorTests
Option<string>.None, Option<string>.None,
0); 0);
var builder = new PipelineBuilder(inputFiles, _logger); var builder = new PipelineBuilder(inputFiles, "", _logger);
IList<IPipelineStep> result = builder.Build(desiredState); IList<IPipelineStep> result = builder.Build(desiredState);
result.Should().HaveCountGreaterThan(0); result.Should().HaveCountGreaterThan(0);
@ -83,7 +86,10 @@ public class PipelineGeneratorTests
var inputFiles = new List<InputFile> { testFile }; var inputFiles = new List<InputFile> { testFile };
var desiredState = new FrameState( var desiredState = new FrameState(
false,
HardwareAccelerationMode.None, HardwareAccelerationMode.None,
Option<string>.None,
Option<string>.None,
true, true,
false, false,
Option<TimeSpan>.None, Option<TimeSpan>.None,
@ -113,7 +119,7 @@ public class PipelineGeneratorTests
Option<string>.None, Option<string>.None,
0); 0);
var builder = new PipelineBuilder(inputFiles, _logger); var builder = new PipelineBuilder(inputFiles, "", _logger);
IList<IPipelineStep> result = builder.Build(desiredState); IList<IPipelineStep> result = builder.Build(desiredState);
result.Should().HaveCountGreaterThan(0); result.Should().HaveCountGreaterThan(0);
@ -125,14 +131,14 @@ public class PipelineGeneratorTests
public void Concat_Test() public void Concat_Test()
{ {
var resolution = new FrameSize(1920, 1080); 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<InputFile> var inputFiles = new List<InputFile>
{ {
new ConcatInputFile("http://localhost:8080/ffmpeg/concat/1", resolution) new ConcatInputFile("http://localhost:8080/ffmpeg/concat/1", resolution)
}; };
var builder = new PipelineBuilder(inputFiles, _logger); var builder = new PipelineBuilder(inputFiles, "", _logger);
IList<IPipelineStep> result = builder.Build(desiredState); IList<IPipelineStep> result = builder.Build(desiredState);
result.Should().HaveCountGreaterThan(0); result.Should().HaveCountGreaterThan(0);

9
ErsatzTV.FFmpeg/CommandGenerator.cs

@ -1,7 +1,14 @@
namespace ErsatzTV.FFmpeg; using ErsatzTV.FFmpeg.Environment;
namespace ErsatzTV.FFmpeg;
public static class CommandGenerator public static class CommandGenerator
{ {
public static IList<EnvironmentVariable> GenerateEnvironmentVariables(IEnumerable<IPipelineStep> pipelineSteps)
{
return pipelineSteps.SelectMany(ps => ps.EnvironmentVariables).ToList();
}
public static IList<string> GenerateArguments( public static IList<string> GenerateArguments(
IEnumerable<InputFile> inputFiles, IEnumerable<InputFile> inputFiles,
IList<IPipelineStep> pipelineSteps) IList<IPipelineStep> pipelineSteps)

2
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) public override FrameState NextState(FrameState currentState)
{ {

3
ErsatzTV.FFmpeg/Decoder/Cuvid/DecoderHevcCuvid.cs

@ -28,7 +28,8 @@ public class DecoderHevcCuvid : DecoderBase
return result; return result;
} }
} }
public override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Hardware;
protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Hardware;
public override FrameState NextState(FrameState currentState) public override FrameState NextState(FrameState currentState)
{ {

4
ErsatzTV.FFmpeg/Decoder/Cuvid/DecoderMpeg2Cuvid.cs

@ -35,8 +35,8 @@ public class DecoderMpeg2Cuvid : DecoderBase
return result; return result;
} }
} }
public override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Hardware; protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Hardware;
public override FrameState NextState(FrameState currentState) public override FrameState NextState(FrameState currentState)
{ {

2
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) public override FrameState NextState(FrameState currentState)
{ {

3
ErsatzTV.FFmpeg/Decoder/Cuvid/DecoderVc1Cuvid.cs

@ -28,7 +28,8 @@ public class DecoderVc1Cuvid : DecoderBase
return result; return result;
} }
} }
public override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Hardware;
protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Hardware;
public override FrameState NextState(FrameState currentState) public override FrameState NextState(FrameState currentState)
{ {

3
ErsatzTV.FFmpeg/Decoder/Cuvid/DecoderVp9Cuvid.cs

@ -28,7 +28,8 @@ public class DecoderVp9Cuvid : DecoderBase
return result; return result;
} }
} }
public override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Hardware;
protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Hardware;
public override FrameState NextState(FrameState currentState) public override FrameState NextState(FrameState currentState)
{ {

7
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 class DecoderBase : IDecoder
{ {
public abstract FrameDataLocation OutputFrameDataLocation { get; } protected abstract FrameDataLocation OutputFrameDataLocation { get; }
public IList<EnvironmentVariable> EnvironmentVariables => Array.Empty<EnvironmentVariable>();
public IList<string> GlobalOptions => Array.Empty<string>(); public IList<string> GlobalOptions => Array.Empty<string>();
public virtual IList<string> InputOptions => new List<string> { "-c:v", Name }; public virtual IList<string> InputOptions => new List<string> { "-c:v", Name };
public IList<string> FilterOptions => Array.Empty<string>(); public IList<string> FilterOptions => Array.Empty<string>();

2
ErsatzTV.FFmpeg/Decoder/DecoderH264.cs

@ -3,5 +3,5 @@
public class DecoderH264 : DecoderBase public class DecoderH264 : DecoderBase
{ {
public override string Name => "h264"; public override string Name => "h264";
public override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software; protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software;
} }

2
ErsatzTV.FFmpeg/Decoder/DecoderHevc.cs

@ -3,5 +3,5 @@
public class DecoderHevc : DecoderBase public class DecoderHevc : DecoderBase
{ {
public override string Name => "hevc"; public override string Name => "hevc";
public override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software; protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software;
} }

2
ErsatzTV.FFmpeg/Decoder/DecoderImplicit.cs

@ -2,7 +2,7 @@
public class DecoderImplicit : DecoderBase public class DecoderImplicit : DecoderBase
{ {
public override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software; protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software;
public override string Name => string.Empty; public override string Name => string.Empty;
public override IList<string> InputOptions => Array.Empty<string>(); public override IList<string> InputOptions => Array.Empty<string>();
} }

2
ErsatzTV.FFmpeg/Decoder/DecoderMpeg1.cs

@ -3,5 +3,5 @@
public class DecoderMpeg1Video : DecoderBase public class DecoderMpeg1Video : DecoderBase
{ {
public override string Name => "mpeg1video"; public override string Name => "mpeg1video";
public override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software; protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software;
} }

2
ErsatzTV.FFmpeg/Decoder/DecoderMpeg2.cs

@ -3,5 +3,5 @@
public class DecoderMpeg2Video : DecoderBase public class DecoderMpeg2Video : DecoderBase
{ {
public override string Name => "mpeg2video"; public override string Name => "mpeg2video";
public override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software; protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software;
} }

2
ErsatzTV.FFmpeg/Decoder/DecoderMpeg4.cs

@ -3,5 +3,5 @@
public class DecoderMpeg4 : DecoderBase public class DecoderMpeg4 : DecoderBase
{ {
public override string Name => "mpeg4"; public override string Name => "mpeg4";
public override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software; protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software;
} }

2
ErsatzTV.FFmpeg/Decoder/DecoderMsMpeg4v2.cs

@ -3,5 +3,5 @@
public class DecoderMsMpeg4V2 : DecoderBase public class DecoderMsMpeg4V2 : DecoderBase
{ {
public override string Name => "msmpeg4v2"; public override string Name => "msmpeg4v2";
public override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software; protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software;
} }

2
ErsatzTV.FFmpeg/Decoder/DecoderMsMpeg4v3.cs

@ -3,5 +3,5 @@
public class DecoderMsMpeg4V3 : DecoderBase public class DecoderMsMpeg4V3 : DecoderBase
{ {
public override string Name => "msmpeg4v3"; public override string Name => "msmpeg4v3";
public override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software; protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software;
} }

2
ErsatzTV.FFmpeg/Decoder/DecoderVaapi.cs

@ -4,7 +4,7 @@ namespace ErsatzTV.FFmpeg.Decoder;
public class DecoderVaapi : DecoderBase public class DecoderVaapi : DecoderBase
{ {
public override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software; protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software;
public override string Name => "implicit_vaapi"; public override string Name => "implicit_vaapi";
public override IList<string> InputOptions => Array.Empty<string>(); public override IList<string> InputOptions => Array.Empty<string>();

2
ErsatzTV.FFmpeg/Decoder/DecoderVc1.cs

@ -3,5 +3,5 @@
public class DecoderVc1 : DecoderBase public class DecoderVc1 : DecoderBase
{ {
public override string Name => "vc1"; public override string Name => "vc1";
public override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software; protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software;
} }

2
ErsatzTV.FFmpeg/Decoder/DecoderVp9.cs

@ -3,5 +3,5 @@
public class DecoderVp9 : DecoderBase public class DecoderVp9 : DecoderBase
{ {
public override string Name => "vp9"; public override string Name => "vp9";
public override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software; protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software;
} }

2
ErsatzTV.FFmpeg/Decoder/Qsv/DecoderH264Qsv.cs

@ -6,7 +6,7 @@ public class DecoderH264Qsv : DecoderBase
{ {
public override string Name => "h264_qsv"; public override string Name => "h264_qsv";
public override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Hardware; protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Hardware;
public override FrameState NextState(FrameState currentState) public override FrameState NextState(FrameState currentState)
{ {

2
ErsatzTV.FFmpeg/Decoder/Qsv/DecoderHevcQsv.cs

@ -4,5 +4,5 @@ public class DecoderHevcQsv : DecoderBase
{ {
public override string Name => "hevc_qsv"; public override string Name => "hevc_qsv";
public override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Hardware; protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Hardware;
} }

2
ErsatzTV.FFmpeg/Decoder/Qsv/DecoderMpeg2Qsv.cs

@ -4,5 +4,5 @@ public class DecoderMpeg2Qsv : DecoderBase
{ {
public override string Name => "mpeg2_qsv"; public override string Name => "mpeg2_qsv";
public override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Hardware; protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Hardware;
} }

2
ErsatzTV.FFmpeg/Decoder/Qsv/DecoderVc1Qsv.cs

@ -4,5 +4,5 @@ public class DecoderVc1Qsv : DecoderBase
{ {
public override string Name => "vc1_qsv"; public override string Name => "vc1_qsv";
public override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Hardware; protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Hardware;
} }

2
ErsatzTV.FFmpeg/Decoder/Qsv/DecoderVp9Qsv.cs

@ -4,5 +4,5 @@ public class DecoderVp9Qsv : DecoderBase
{ {
public override string Name => "vp9_qsv"; public override string Name => "vp9_qsv";
public override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Hardware; protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Hardware;
} }

5
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 abstract class EncoderBase : IEncoder
{ {
public IList<EnvironmentVariable> EnvironmentVariables => Array.Empty<EnvironmentVariable>();
public IList<string> GlobalOptions => Array.Empty<string>(); public IList<string> GlobalOptions => Array.Empty<string>();
public IList<string> InputOptions => Array.Empty<string>(); public IList<string> InputOptions => Array.Empty<string>();
public IList<string> FilterOptions => Array.Empty<string>(); public IList<string> FilterOptions => Array.Empty<string>();

3
ErsatzTV.FFmpeg/Environment/EnvironmentVariable.cs

@ -0,0 +1,3 @@
namespace ErsatzTV.FFmpeg.Environment;
public record EnvironmentVariable(string Key, string Value);

46
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<InputFile> _inputFiles;
public FFReportVariable(string reportsFolder, IList<InputFile> inputFiles)
{
_reportsFolder = reportsFolder;
_inputFiles = inputFiles;
}
public IList<EnvironmentVariable> EnvironmentVariables
{
get
{
string fileName = _inputFiles.OfType<ConcatInputFile>().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<EnvironmentVariable>
{
new("FFREPORT", $"file={fileName}:level=32")
};
}
}
public IList<string> GlobalOptions => Array.Empty<string>();
public IList<string> InputOptions => Array.Empty<string>();
public IList<string> FilterOptions => Array.Empty<string>();
public IList<string> OutputOptions => Array.Empty<string>();
public FrameState NextState(FrameState currentState) => currentState with { SaveReport = true };
}

22
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<EnvironmentVariable> EnvironmentVariables => new List<EnvironmentVariable>
{
new("LIBVA_DRIVER_NAME", _driverName)
};
public IList<string> GlobalOptions => Array.Empty<string>();
public IList<string> InputOptions => Array.Empty<string>();
public IList<string> FilterOptions => Array.Empty<string>();
public IList<string> OutputOptions => Array.Empty<string>();
public FrameState NextState(FrameState currentState) => currentState with { VaapiDriver = _driverName };
}

5
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 abstract class BaseFilter : IPipelineFilterStep
{ {
public IList<EnvironmentVariable> EnvironmentVariables => Array.Empty<EnvironmentVariable>();
public virtual IList<string> GlobalOptions => Array.Empty<string>(); public virtual IList<string> GlobalOptions => Array.Empty<string>();
public virtual IList<string> InputOptions => Array.Empty<string>(); public virtual IList<string> InputOptions => Array.Empty<string>();
public virtual IList<string> FilterOptions => Array.Empty<string>(); public virtual IList<string> FilterOptions => Array.Empty<string>();

5
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 public class ComplexFilter : IPipelineStep
{ {
@ -73,6 +75,7 @@ public class ComplexFilter : IPipelineStep
return result; return result;
} }
public IList<EnvironmentVariable> EnvironmentVariables => Array.Empty<EnvironmentVariable>();
public IList<string> GlobalOptions => Array.Empty<string>(); public IList<string> GlobalOptions => Array.Empty<string>();
public IList<string> InputOptions => Array.Empty<string>(); public IList<string> InputOptions => Array.Empty<string>();
public IList<string> FilterOptions => Arguments(); public IList<string> FilterOptions => Arguments();

5
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 class ConcatInputFormat : IPipelineStep
{ {
public IList<EnvironmentVariable> EnvironmentVariables => Array.Empty<EnvironmentVariable>();
public IList<string> GlobalOptions => Array.Empty<string>(); public IList<string> GlobalOptions => Array.Empty<string>();
public IList<string> InputOptions => new List<string> public IList<string> InputOptions => new List<string>

8
ErsatzTV.FFmpeg/FrameState.cs

@ -5,7 +5,10 @@ using LanguageExt;
namespace ErsatzTV.FFmpeg; namespace ErsatzTV.FFmpeg;
public record FrameState( public record FrameState(
bool SaveReport,
HardwareAccelerationMode HardwareAccelerationMode, HardwareAccelerationMode HardwareAccelerationMode,
Option<string> VaapiDriver,
Option<string> VaapiDevice,
bool Realtime, bool Realtime,
bool InfiniteLoop, bool InfiniteLoop,
Option<TimeSpan> Start, Option<TimeSpan> Start,
@ -36,9 +39,12 @@ public record FrameState(
long PtsOffset, long PtsOffset,
FrameDataLocation FrameDataLocation = FrameDataLocation.Unknown) FrameDataLocation FrameDataLocation = FrameDataLocation.Unknown)
{ {
public static FrameState Concat(string channelName, FrameSize resolution) => public static FrameState Concat(bool saveReport, string channelName, FrameSize resolution) =>
new( new(
saveReport,
HardwareAccelerationMode.None, HardwareAccelerationMode.None,
Option<string>.None,
Option<string>.None,
true, // realtime true, // realtime
true, // infinite loop true, // infinite loop
Option<TimeSpan>.None, Option<TimeSpan>.None,

5
ErsatzTV.FFmpeg/IPipelineStep.cs

@ -1,7 +1,10 @@
namespace ErsatzTV.FFmpeg; using ErsatzTV.FFmpeg.Environment;
namespace ErsatzTV.FFmpeg;
public interface IPipelineStep public interface IPipelineStep
{ {
IList<EnvironmentVariable> EnvironmentVariables { get; }
IList<string> GlobalOptions { get; } IList<string> GlobalOptions { get; }
IList<string> InputOptions { get; } IList<string> InputOptions { get; }
IList<string> FilterOptions { get; } IList<string> FilterOptions { get; }

6
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 public class FrameRateOutputOption : IPipelineStep
{ {
@ -9,7 +11,7 @@ public class FrameRateOutputOption : IPipelineStep
_frameRate = frameRate; _frameRate = frameRate;
} }
public FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Unknown; public IList<EnvironmentVariable> EnvironmentVariables => Array.Empty<EnvironmentVariable>();
public IList<string> GlobalOptions => Array.Empty<string>(); public IList<string> GlobalOptions => Array.Empty<string>();
public IList<string> InputOptions => Array.Empty<string>(); public IList<string> InputOptions => Array.Empty<string>();
public IList<string> FilterOptions => Array.Empty<string>(); public IList<string> FilterOptions => Array.Empty<string>();

6
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 abstract class GlobalOption : IPipelineStep
{ {
public FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Unknown; public IList<EnvironmentVariable> EnvironmentVariables => Array.Empty<EnvironmentVariable>();
public abstract IList<string> GlobalOptions { get; } public abstract IList<string> GlobalOptions { get; }
public IList<string> InputOptions => Array.Empty<string>(); public IList<string> InputOptions => Array.Empty<string>();
public IList<string> FilterOptions => Array.Empty<string>(); public IList<string> FilterOptions => Array.Empty<string>();

33
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 class AvailableHardwareAccelerationOptions
{ {
public static IPipelineStep ForMode(HardwareAccelerationMode mode) public static Option<IPipelineStep> ForMode(
{ HardwareAccelerationMode mode,
return mode switch Option<string> vaapiDevice,
ILogger logger) =>
mode switch
{ {
HardwareAccelerationMode.Nvenc => new CudaHardwareAccelerationOption(), HardwareAccelerationMode.Nvenc => new CudaHardwareAccelerationOption(),
HardwareAccelerationMode.Qsv => new QsvHardwareAccelerationOption(), HardwareAccelerationMode.Qsv => new QsvHardwareAccelerationOption(),
HardwareAccelerationMode.Vaapi => new VaapiHardwareAccelerationOption(), HardwareAccelerationMode.Vaapi => GetVaapiAcceleration(vaapiDevice, logger),
HardwareAccelerationMode.VideoToolbox => new VideoToolboxHardwareAccelerationOption(), HardwareAccelerationMode.VideoToolbox => new VideoToolboxHardwareAccelerationOption(),
_ => throw new ArgumentOutOfRangeException(nameof(mode), mode, null) _ => LogUnknownMode(mode, logger)
}; };
private static Option<IPipelineStep> GetVaapiAcceleration(Option<string> 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<IPipelineStep>.None;
}
private static Option<IPipelineStep> LogUnknownMode(HardwareAccelerationMode mode, ILogger logger)
{
logger.LogWarning("Unexpected hardware acceleration mode {AccelMode}; may have playback issues", mode);
return Option<IPipelineStep>.None;
} }
} }

9
ErsatzTV.FFmpeg/Option/HardwareAcceleration/VaapiHardwareAccelerationOption.cs

@ -2,8 +2,15 @@
public class VaapiHardwareAccelerationOption : GlobalOption public class VaapiHardwareAccelerationOption : GlobalOption
{ {
private readonly string _vaapiDevice;
public VaapiHardwareAccelerationOption(string vaapiDevice)
{
_vaapiDevice = vaapiDevice;
}
public override IList<string> GlobalOptions => new List<string> public override IList<string> GlobalOptions => new List<string>
{ "-hwaccel", "vaapi", "-hwaccel_output_format", "vaapi" }; { "-hwaccel", "vaapi", "-vaapi_device", _vaapiDevice, "-hwaccel_output_format", "vaapi" };
public override FrameState NextState(FrameState currentState) => currentState with public override FrameState NextState(FrameState currentState) => currentState with
{ {

5
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 public class InfiniteLoopInputOption : IPipelineStep
{ {
@ -9,6 +11,7 @@ public class InfiniteLoopInputOption : IPipelineStep
_currentState = currentState; _currentState = currentState;
} }
public IList<EnvironmentVariable> EnvironmentVariables => Array.Empty<EnvironmentVariable>();
public IList<string> GlobalOptions => Array.Empty<string>(); public IList<string> GlobalOptions => Array.Empty<string>();
public IList<string> InputOptions => new List<string> { "-stream_loop", "-1" }; public IList<string> InputOptions => new List<string> { "-stream_loop", "-1" };
public IList<string> FilterOptions => Array.Empty<string>(); public IList<string> FilterOptions => Array.Empty<string>();

5
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 abstract class OutputOption : IPipelineStep
{ {
public FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Unknown; public FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Unknown;
public IList<EnvironmentVariable> EnvironmentVariables => Array.Empty<EnvironmentVariable>();
public IList<string> GlobalOptions => Array.Empty<string>(); public IList<string> GlobalOptions => Array.Empty<string>();
public IList<string> InputOptions => Array.Empty<string>(); public IList<string> InputOptions => Array.Empty<string>();
public IList<string> FilterOptions => Array.Empty<string>(); public IList<string> FilterOptions => Array.Empty<string>();

6
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 class RealtimeInputOption : IPipelineStep
{ {
public IList<EnvironmentVariable> EnvironmentVariables => Array.Empty<EnvironmentVariable>();
// some builds of ffmpeg seem to hang when realtime input is requested with multithreading, // some builds of ffmpeg seem to hang when realtime input is requested with multithreading,
// so we force a single thread here // so we force a single thread here
public IList<string> GlobalOptions => new List<string> { "-threads", "1" }; public IList<string> GlobalOptions => new List<string> { "-threads", "1" };

5
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 public class StreamSeekInputOption : IPipelineStep
{ {
@ -10,6 +12,7 @@ public class StreamSeekInputOption : IPipelineStep
} }
public FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Unknown; public FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Unknown;
public IList<EnvironmentVariable> EnvironmentVariables => Array.Empty<EnvironmentVariable>();
public IList<string> GlobalOptions => Array.Empty<string>(); public IList<string> GlobalOptions => Array.Empty<string>();
public IList<string> InputOptions => public IList<string> InputOptions =>
_start == TimeSpan.Zero ? Array.Empty<string>() : new List<string> { "-ss", $"{_start:c}" }; _start == TimeSpan.Zero ? Array.Empty<string>() : new List<string> { "-ss", $"{_start:c}" };

5
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 public class TimeLimitOutputOption : IPipelineStep
{ {
@ -10,6 +12,7 @@ public class TimeLimitOutputOption : IPipelineStep
} }
public FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Unknown; public FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Unknown;
public IList<EnvironmentVariable> EnvironmentVariables => Array.Empty<EnvironmentVariable>();
public IList<string> GlobalOptions => Array.Empty<string>(); public IList<string> GlobalOptions => Array.Empty<string>();
public IList<string> InputOptions => Array.Empty<string>(); public IList<string> InputOptions => Array.Empty<string>();
public IList<string> FilterOptions => Array.Empty<string>(); public IList<string> FilterOptions => Array.Empty<string>();

5
ErsatzTV.FFmpeg/OutputFormat/OutputFormatHls.cs

@ -1,4 +1,5 @@
using LanguageExt; using ErsatzTV.FFmpeg.Environment;
using LanguageExt;
namespace ErsatzTV.FFmpeg.OutputFormat; namespace ErsatzTV.FFmpeg.OutputFormat;
@ -21,7 +22,7 @@ public class OutputFormatHls : IPipelineStep
_playlistPath = playlistPath; _playlistPath = playlistPath;
} }
public FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software; public IList<EnvironmentVariable> EnvironmentVariables => Array.Empty<EnvironmentVariable>();
public IList<string> GlobalOptions => Array.Empty<string>(); public IList<string> GlobalOptions => Array.Empty<string>();
public IList<string> InputOptions => Array.Empty<string>(); public IList<string> InputOptions => Array.Empty<string>();
public IList<string> FilterOptions => Array.Empty<string>(); public IList<string> FilterOptions => Array.Empty<string>();

6
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 class OutputFormatMpegTs : IPipelineStep
{ {
public FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software; public IList<EnvironmentVariable> EnvironmentVariables => Array.Empty<EnvironmentVariable>();
public IList<string> GlobalOptions => Array.Empty<string>(); public IList<string> GlobalOptions => Array.Empty<string>();
public IList<string> InputOptions => Array.Empty<string>(); public IList<string> InputOptions => Array.Empty<string>();
public IList<string> FilterOptions => Array.Empty<string>(); public IList<string> FilterOptions => Array.Empty<string>();

49
ErsatzTV.FFmpeg/PipelineBuilder.cs

@ -1,5 +1,6 @@
using ErsatzTV.FFmpeg.Decoder; using ErsatzTV.FFmpeg.Decoder;
using ErsatzTV.FFmpeg.Encoder; using ErsatzTV.FFmpeg.Encoder;
using ErsatzTV.FFmpeg.Environment;
using ErsatzTV.FFmpeg.Filter; using ErsatzTV.FFmpeg.Filter;
using ErsatzTV.FFmpeg.Format; using ErsatzTV.FFmpeg.Format;
using ErsatzTV.FFmpeg.Option; using ErsatzTV.FFmpeg.Option;
@ -18,9 +19,10 @@ public class PipelineBuilder
private readonly List<IPipelineFilterStep> _audioFilterSteps; private readonly List<IPipelineFilterStep> _audioFilterSteps;
private readonly List<IPipelineFilterStep> _videoFilterSteps; private readonly List<IPipelineFilterStep> _videoFilterSteps;
private readonly IList<InputFile> _inputFiles; private readonly IList<InputFile> _inputFiles;
private readonly string _reportsFolder;
private readonly ILogger _logger; private readonly ILogger _logger;
public PipelineBuilder(IList<InputFile> inputFiles, ILogger logger) public PipelineBuilder(IList<InputFile> inputFiles, string reportsFolder, ILogger logger)
{ {
_pipelineSteps = new List<IPipelineStep> _pipelineSteps = new List<IPipelineStep>
{ {
@ -38,6 +40,7 @@ public class PipelineBuilder
_videoFilterSteps = new List<IPipelineFilterStep>(); _videoFilterSteps = new List<IPipelineFilterStep>();
_inputFiles = inputFiles; _inputFiles = inputFiles;
_reportsFolder = reportsFolder;
_logger = logger; _logger = logger;
} }
@ -68,7 +71,10 @@ public class PipelineBuilder
} }
var currentState = new FrameState( var currentState = new FrameState(
false, // save report
HardwareAccelerationMode.None, HardwareAccelerationMode.None,
Option<string>.None,
Option<string>.None,
false, // realtime false, // realtime
false, // infinite loop false, // infinite loop
Option<TimeSpan>.None, Option<TimeSpan>.None,
@ -98,6 +104,13 @@ public class PipelineBuilder
Option<string>.None, Option<string>.None,
0); 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) foreach (TimeSpan desiredStart in desiredState.Start)
{ {
if (currentState.Start != desiredStart) if (currentState.Start != desiredStart)
@ -129,13 +142,37 @@ public class PipelineBuilder
} }
else else
{ {
// TODO: prioritize which codecs are used (hw accel)
if (currentState.HardwareAccelerationMode != desiredState.HardwareAccelerationMode) if (currentState.HardwareAccelerationMode != desiredState.HardwareAccelerationMode)
{ {
IPipelineStep accel = Option<IPipelineStep> maybeAccel = AvailableHardwareAccelerationOptions.ForMode(
AvailableHardwareAccelerationOptions.ForMode(desiredState.HardwareAccelerationMode); desiredState.HardwareAccelerationMode,
currentState = accel.NextState(currentState); desiredState.VaapiDevice,
_pipelineSteps.Add(accel); _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)) foreach (IDecoder decoder in AvailableDecoders.ForVideoFormat(currentState, desiredState, _logger))

6
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 class PipeProtocol : IPipelineStep
{ {
public FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software; public IList<EnvironmentVariable> EnvironmentVariables => Array.Empty<EnvironmentVariable>();
public IList<string> GlobalOptions => Array.Empty<string>(); public IList<string> GlobalOptions => Array.Empty<string>();
public IList<string> InputOptions => Array.Empty<string>(); public IList<string> InputOptions => Array.Empty<string>();
public IList<string> FilterOptions => Array.Empty<string>(); public IList<string> FilterOptions => Array.Empty<string>();

3
ErsatzTV.sln.DotSettings

@ -1,6 +1,7 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> <wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeInspection/CodeAnnotations/NamespacesWithAnnotations/=ErsatzTV_002EAnnotations/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/CodeInspection/CodeAnnotations/NamespacesWithAnnotations/=ErsatzTV_002EAnnotations/@EntryIndexedValue">True</s:Boolean>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=DTO/@EntryIndexedValue">DTO</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=DTO/@EntryIndexedValue">DTO</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=FF/@EntryIndexedValue">FF</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=HDHR/@EntryIndexedValue">HDHR</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=HDHR/@EntryIndexedValue">HDHR</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=LE/@EntryIndexedValue">LE</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=LE/@EntryIndexedValue">LE</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=NV/@EntryIndexedValue">NV</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=NV/@EntryIndexedValue">NV</s:String>
@ -28,6 +29,7 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=ffconcat/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=ffconcat/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=fflags/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=fflags/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=ffprobe/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=ffprobe/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=FFREPORT/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Fmpeg/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=Fmpeg/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=fontfile/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=fontfile/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Fprobe/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=Fprobe/@EntryIndexedValue">True</s:Boolean>
@ -36,6 +38,7 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=igndts/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=igndts/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Jellyfin/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=Jellyfin/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Libavfilter/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=Libavfilter/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Libva/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=libx/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=libx/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=maxrate/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=maxrate/@EntryIndexedValue">True</s:Boolean>

Loading…
Cancel
Save