Browse Source

partial v4l2m2m accel support (#2416)

* start to add v4l2m2m accel

* add v4l2m2m pipeline

* add encoders

* fix decoders and encoders

* output software frames from decoders

* more buffers

* hide v4l2m2m from ui
pull/2417/head
Jason Dove 8 months ago committed by GitHub
parent
commit
307b9dadd2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 6
      ErsatzTV.Application/FFmpegProfiles/Queries/GetSupportedHardwareAccelerationKindsHandler.cs
  2. 3
      ErsatzTV.Core/Domain/HardwareAccelerationKind.cs
  3. 1
      ErsatzTV.Core/FFmpeg/FFmpegLibraryProcessService.cs
  4. 8
      ErsatzTV.FFmpeg/Capabilities/FFmpegCapabilities.cs
  5. 34
      ErsatzTV.FFmpeg/Capabilities/FFmpegKnownDecoder.cs
  6. 2
      ErsatzTV.FFmpeg/Capabilities/FFmpegKnownEncoder.cs
  7. 4
      ErsatzTV.FFmpeg/Capabilities/FFmpegKnownHardwareAcceleration.cs
  8. 1
      ErsatzTV.FFmpeg/Capabilities/HardwareCapabilitiesFactory.cs
  9. 67
      ErsatzTV.FFmpeg/Capabilities/V4l2m2mHardwareCapabilities.cs
  10. 8
      ErsatzTV.FFmpeg/Decoder/DecoderV4l2m2m.cs
  11. 19
      ErsatzTV.FFmpeg/Decoder/V4l2m2m/DecoderH264V4l2m2m.cs
  12. 27
      ErsatzTV.FFmpeg/Decoder/V4l2m2m/DecoderHevcV4l2m2m.cs
  13. 8
      ErsatzTV.FFmpeg/Decoder/V4l2m2m/DecoderMpeg2V4l2m2m.cs
  14. 8
      ErsatzTV.FFmpeg/Decoder/V4l2m2m/DecoderMpeg4V4l2m2m.cs
  15. 8
      ErsatzTV.FFmpeg/Decoder/V4l2m2m/DecoderVc1V4l2m2m.cs
  16. 27
      ErsatzTV.FFmpeg/Decoder/V4l2m2m/DecoderVp8V4l2m2m.cs
  17. 27
      ErsatzTV.FFmpeg/Decoder/V4l2m2m/DecoderVp9V4l2m2m.cs
  18. 21
      ErsatzTV.FFmpeg/Encoder/V4l2m2m/EncoderH264V4l2m2m.cs
  19. 15
      ErsatzTV.FFmpeg/Encoder/V4l2m2m/EncoderHevcV4l2m2m.cs
  20. 3
      ErsatzTV.FFmpeg/Filter/SubtitleHardwareUploadFilter.cs
  21. 3
      ErsatzTV.FFmpeg/Filter/WatermarkHardwareUploadFilter.cs
  22. 1
      ErsatzTV.FFmpeg/Format/VideoFormat.cs
  23. 3
      ErsatzTV.FFmpeg/HardwareAccelerationMode.cs
  24. 15
      ErsatzTV.FFmpeg/Pipeline/PipelineBuilderFactory.cs
  25. 158
      ErsatzTV.FFmpeg/Pipeline/V4l2m2mPipelineBuilder.cs
  26. 24
      ErsatzTV.Infrastructure/Health/Checks/HardwareAccelerationHealthCheck.cs
  27. 1
      ErsatzTV/Pages/FFmpegEditor.razor
  28. 13
      ErsatzTV/Validators/FFmpegProfileEditViewModelValidator.cs

6
ErsatzTV.Application/FFmpegProfiles/Queries/GetSupportedHardwareAccelerationKindsHandler.cs

@ -66,6 +66,12 @@ public class @@ -66,6 +66,12 @@ public class
result.Add(HardwareAccelerationKind.Amf);
}
// TODO: fix and enable V4L2 M2M
// if (ffmpegCapabilities.HasHardwareAcceleration(HardwareAccelerationMode.V4l2m2m))
// {
// result.Add(HardwareAccelerationKind.V4l2m2m);
// }
return result;
}

3
ErsatzTV.Core/Domain/HardwareAccelerationKind.cs

@ -7,5 +7,6 @@ public enum HardwareAccelerationKind @@ -7,5 +7,6 @@ public enum HardwareAccelerationKind
Nvenc = 2,
Vaapi = 3,
VideoToolbox = 4,
Amf = 5
Amf = 5,
V4l2m2m = 6
}

1
ErsatzTV.Core/FFmpeg/FFmpegLibraryProcessService.cs

@ -1128,6 +1128,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService @@ -1128,6 +1128,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
HardwareAccelerationKind.Vaapi => HardwareAccelerationMode.Vaapi,
HardwareAccelerationKind.VideoToolbox => HardwareAccelerationMode.VideoToolbox,
HardwareAccelerationKind.Amf => HardwareAccelerationMode.Amf,
HardwareAccelerationKind.V4l2m2m => HardwareAccelerationMode.V4l2m2m,
_ => HardwareAccelerationMode.None
};
}

8
ErsatzTV.FFmpeg/Capabilities/FFmpegCapabilities.cs

@ -35,6 +35,14 @@ public class FFmpegCapabilities : IFFmpegCapabilities @@ -35,6 +35,14 @@ public class FFmpegCapabilities : IFFmpegCapabilities
StringComparison.OrdinalIgnoreCase));
}
// V4l2m2m isn't a "hwaccel" in ffmpeg, so check for presence of encoders
if (hardwareAccelerationMode is HardwareAccelerationMode.V4l2m2m)
{
return _ffmpegEncoders.Any(e => e.EndsWith(
$"_{FFmpegKnownHardwareAcceleration.V4l2m2m.Name}",
StringComparison.OrdinalIgnoreCase));
}
Option<FFmpegKnownHardwareAcceleration> maybeAccelToCheck = hardwareAccelerationMode switch
{
HardwareAccelerationMode.Nvenc => FFmpegKnownHardwareAcceleration.Cuda,

34
ErsatzTV.FFmpeg/Capabilities/FFmpegKnownDecoder.cs

@ -11,14 +11,20 @@ public record FFmpegKnownDecoder @@ -11,14 +11,20 @@ public record FFmpegKnownDecoder
public static readonly FFmpegKnownDecoder Vp9Cuvid = new("vp9_cuvid");
public static readonly FFmpegKnownDecoder Libdav1d = new("libdav1d");
public static readonly FFmpegKnownDecoder Libaomav1 = new("libaom-av1");
public static readonly FFmpegKnownDecoder H264V4l2m2m = new("h264_v4l2m2m");
public static readonly FFmpegKnownDecoder HevcV4l2m2m = new("hevc_v4l2m2m");
public static readonly FFmpegKnownDecoder Mpeg2V4l2m2m = new("mpeg2_v4l2m2m");
public static readonly FFmpegKnownDecoder Mpeg4V4l2m2m = new("mpeg4_v4l2m2m");
public static readonly FFmpegKnownDecoder Vc1V4l2m2m = new("vc1_v4l2m2m");
public static readonly FFmpegKnownDecoder Vp84V4l2m2m = new("vp8_v4l2m2m");
public static readonly FFmpegKnownDecoder Vp94V4l2m2m = new("vp9_v4l2m2m");
private FFmpegKnownDecoder(string Name) => this.Name = Name;
public string Name { get; }
public static IList<string> AllDecoders =>
new[]
{
[
Av1Cuvid.Name,
H264Cuvid.Name,
HevcCuvid.Name,
@ -26,7 +32,27 @@ public record FFmpegKnownDecoder @@ -26,7 +32,27 @@ public record FFmpegKnownDecoder
Mpeg4Cuvid.Name,
Vc1Cuvid.Name,
Vp9Cuvid.Name,
Libdav1d.Name,
Libaomav1.Name
};
Libaomav1.Name,
H264V4l2m2m.Name,
HevcV4l2m2m.Name,
Mpeg2V4l2m2m.Name,
Mpeg4V4l2m2m.Name,
Vc1V4l2m2m.Name,
Vp84V4l2m2m.Name,
Vp94V4l2m2m.Name
];
public static IList<string> V4l2m2mDecoders =>
[
H264V4l2m2m.Name,
HevcV4l2m2m.Name,
Mpeg2V4l2m2m.Name,
Mpeg4V4l2m2m.Name,
Vc1V4l2m2m.Name,
Vp84V4l2m2m.Name,
Vp94V4l2m2m.Name
];
}

2
ErsatzTV.FFmpeg/Capabilities/FFmpegKnownEncoder.cs

@ -13,6 +13,8 @@ public record FFmpegKnownEncoder @@ -13,6 +13,8 @@ public record FFmpegKnownEncoder
[
"h264_amf",
"hevc_amf",
"h264_v4l2m2m",
"hevc_v4l2m2m",
"h264_videotoolbox",
"hevc_videotoolbox"
];

4
ErsatzTV.FFmpeg/Capabilities/FFmpegKnownHardwareAcceleration.cs

@ -9,6 +9,7 @@ public record FFmpegKnownHardwareAcceleration @@ -9,6 +9,7 @@ public record FFmpegKnownHardwareAcceleration
public static readonly FFmpegKnownHardwareAcceleration VideoToolbox = new("videotoolbox");
public static readonly FFmpegKnownHardwareAcceleration OpenCL = new("opencl");
public static readonly FFmpegKnownHardwareAcceleration Vulkan = new("vulkan");
public static readonly FFmpegKnownHardwareAcceleration V4l2m2m = new("v4l2m2m");
private FFmpegKnownHardwareAcceleration(string Name) => this.Name = Name;
@ -22,6 +23,7 @@ public record FFmpegKnownHardwareAcceleration @@ -22,6 +23,7 @@ public record FFmpegKnownHardwareAcceleration
Vaapi.Name,
VideoToolbox.Name,
OpenCL.Name,
Vulkan.Name
Vulkan.Name,
V4l2m2m.Name
];
}

1
ErsatzTV.FFmpeg/Capabilities/HardwareCapabilitiesFactory.cs

@ -109,6 +109,7 @@ public class HardwareCapabilitiesFactory : IHardwareCapabilitiesFactory @@ -109,6 +109,7 @@ public class HardwareCapabilitiesFactory : IHardwareCapabilitiesFactory
HardwareAccelerationMode.Vaapi => await GetVaapiCapabilities(vaapiDisplay, vaapiDriver, vaapiDevice),
HardwareAccelerationMode.VideoToolbox => new VideoToolboxHardwareCapabilities(ffmpegCapabilities, _logger),
HardwareAccelerationMode.Amf => new AmfHardwareCapabilities(),
HardwareAccelerationMode.V4l2m2m => new V4l2m2mHardwareCapabilities(ffmpegCapabilities),
_ => new DefaultHardwareCapabilities()
};
}

67
ErsatzTV.FFmpeg/Capabilities/V4l2m2mHardwareCapabilities.cs

@ -0,0 +1,67 @@ @@ -0,0 +1,67 @@
using ErsatzTV.FFmpeg.Format;
namespace ErsatzTV.FFmpeg.Capabilities;
public class V4l2m2mHardwareCapabilities(IFFmpegCapabilities ffmpegCapabilities) : IHardwareCapabilities
{
public FFmpegCapability CanDecode(
string videoFormat,
Option<string> videoProfile,
Option<IPixelFormat> maybePixelFormat,
bool isHdr)
{
int bitDepth = maybePixelFormat.Map(pf => pf.BitDepth).IfNone(8);
return (videoFormat, bitDepth) switch
{
(VideoFormat.H264, 8) => ffmpegCapabilities.HasDecoder(FFmpegKnownDecoder.H264V4l2m2m)
? FFmpegCapability.Hardware
: FFmpegCapability.Software,
(VideoFormat.Hevc, _) => ffmpegCapabilities.HasDecoder(FFmpegKnownDecoder.HevcV4l2m2m)
? FFmpegCapability.Hardware
: FFmpegCapability.Software,
(VideoFormat.Mpeg2Video, 8) => ffmpegCapabilities.HasDecoder(FFmpegKnownDecoder.Mpeg2V4l2m2m)
? FFmpegCapability.Hardware
: FFmpegCapability.Software,
(VideoFormat.Mpeg4, _) => ffmpegCapabilities.HasDecoder(FFmpegKnownDecoder.Mpeg4V4l2m2m)
? FFmpegCapability.Hardware
: FFmpegCapability.Software,
(VideoFormat.Vc1, _) => ffmpegCapabilities.HasDecoder(FFmpegKnownDecoder.Vc1V4l2m2m)
? FFmpegCapability.Hardware
: FFmpegCapability.Software,
(VideoFormat.Vp8, _) => ffmpegCapabilities.HasDecoder(FFmpegKnownDecoder.Vp84V4l2m2m)
? FFmpegCapability.Hardware
: FFmpegCapability.Software,
(VideoFormat.Vp9, _) => ffmpegCapabilities.HasDecoder(FFmpegKnownDecoder.Vp94V4l2m2m)
? FFmpegCapability.Hardware
: FFmpegCapability.Software,
_ => FFmpegCapability.Software
};
}
public FFmpegCapability CanEncode(
string videoFormat,
Option<string> videoProfile,
Option<IPixelFormat> maybePixelFormat)
{
int bitDepth = maybePixelFormat.Map(pf => pf.BitDepth).IfNone(8);
return (videoFormat, bitDepth) switch
{
// 10-bit h264 encoding is not support by any hardware
(VideoFormat.H264, 10) => FFmpegCapability.Software,
_ => FFmpegCapability.Hardware
};
}
public Option<RateControlMode> GetRateControlMode(string videoFormat, Option<IPixelFormat> maybePixelFormat) =>
Option<RateControlMode>.None;
}

8
ErsatzTV.FFmpeg/Decoder/DecoderV4l2m2m.cs

@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
namespace ErsatzTV.FFmpeg.Decoder;
public class DecoderV4l2m2m : DecoderBase
{
protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software;
public override string Name => "implicit_v4l2m2m";
public override string[] InputOptions(InputFile inputFile) => [];
}

19
ErsatzTV.FFmpeg/Decoder/V4l2m2m/DecoderH264V4l2m2m.cs

@ -0,0 +1,19 @@ @@ -0,0 +1,19 @@
using ErsatzTV.FFmpeg.Format;
namespace ErsatzTV.FFmpeg.Decoder.V4l2m2m;
public class DecoderH264V4l2m2m : DecoderBase
{
public override string Name => "h264_v4l2m2m";
protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software;
public override FrameState NextState(FrameState currentState)
{
FrameState nextState = base.NextState(currentState);
return currentState.PixelFormat.Match(
pixelFormat => nextState with { PixelFormat = new PixelFormatNv12(pixelFormat.Name) },
() => nextState);
}
}

27
ErsatzTV.FFmpeg/Decoder/V4l2m2m/DecoderHevcV4l2m2m.cs

@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
using ErsatzTV.FFmpeg.Format;
namespace ErsatzTV.FFmpeg.Decoder.V4l2m2m;
public class DecoderHevcV4l2m2m : DecoderBase
{
public override string Name => "hevc_v4l2m2m";
protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software;
public override FrameState NextState(FrameState currentState)
{
FrameState nextState = base.NextState(currentState);
return currentState.PixelFormat.Match(
pixelFormat =>
{
if (pixelFormat.BitDepth == 10)
{
return nextState with { PixelFormat = new PixelFormatP010() };
}
return nextState with { PixelFormat = new PixelFormatNv12(pixelFormat.Name) };
},
() => nextState);
}
}

8
ErsatzTV.FFmpeg/Decoder/V4l2m2m/DecoderMpeg2V4l2m2m.cs

@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
namespace ErsatzTV.FFmpeg.Decoder.V4l2m2m;
public class DecoderMpeg2V4l2m2m : DecoderBase
{
public override string Name => "mpeg2_v4l2m2m";
protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software;
}

8
ErsatzTV.FFmpeg/Decoder/V4l2m2m/DecoderMpeg4V4l2m2m.cs

@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
namespace ErsatzTV.FFmpeg.Decoder.V4l2m2m;
public class DecoderMpeg4V4l2m2m : DecoderBase
{
public override string Name => "mpeg4_v4l2m2m";
protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software;
}

8
ErsatzTV.FFmpeg/Decoder/V4l2m2m/DecoderVc1V4l2m2m.cs

@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
namespace ErsatzTV.FFmpeg.Decoder.V4l2m2m;
public class DecoderVc1V4l2m2m : DecoderBase
{
public override string Name => "vc1_v4l2m2m";
protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software;
}

27
ErsatzTV.FFmpeg/Decoder/V4l2m2m/DecoderVp8V4l2m2m.cs

@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
using ErsatzTV.FFmpeg.Format;
namespace ErsatzTV.FFmpeg.Decoder.V4l2m2m;
public class DecoderVp8V4l2m2m : DecoderBase
{
public override string Name => "vp8_v4l2m2m";
protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software;
public override FrameState NextState(FrameState currentState)
{
FrameState nextState = base.NextState(currentState);
return currentState.PixelFormat.Match(
pixelFormat =>
{
if (pixelFormat.BitDepth == 10)
{
return nextState with { PixelFormat = new PixelFormatP010() };
}
return nextState with { PixelFormat = new PixelFormatNv12(pixelFormat.Name) };
},
() => nextState);
}
}

27
ErsatzTV.FFmpeg/Decoder/V4l2m2m/DecoderVp9V4l2m2m.cs

@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
using ErsatzTV.FFmpeg.Format;
namespace ErsatzTV.FFmpeg.Decoder.V4l2m2m;
public class DecoderVp9V4l2m2m : DecoderBase
{
public override string Name => "vp9_v4l2m2m";
protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Software;
public override FrameState NextState(FrameState currentState)
{
FrameState nextState = base.NextState(currentState);
return currentState.PixelFormat.Match(
pixelFormat =>
{
if (pixelFormat.BitDepth == 10)
{
return nextState with { PixelFormat = new PixelFormatP010() };
}
return nextState with { PixelFormat = new PixelFormatNv12(pixelFormat.Name) };
},
() => nextState);
}
}

21
ErsatzTV.FFmpeg/Encoder/V4l2m2m/EncoderH264V4l2m2m.cs

@ -0,0 +1,21 @@ @@ -0,0 +1,21 @@
using ErsatzTV.FFmpeg.Format;
namespace ErsatzTV.FFmpeg.Encoder.V4l2m2m;
public class EncoderH264V4l2m2m : EncoderBase
{
public override string Name => "h264_v4l2m2m";
public override StreamKind Kind => StreamKind.Video;
public override string[] OutputOptions =>
[
"-c:v", Name,
"-num_capture_buffers", "16"
];
public override FrameState NextState(FrameState currentState) => currentState with
{
VideoFormat = VideoFormat.H264,
FrameDataLocation = FrameDataLocation.Hardware
};
}

15
ErsatzTV.FFmpeg/Encoder/V4l2m2m/EncoderHevcV4l2m2m.cs

@ -0,0 +1,15 @@ @@ -0,0 +1,15 @@
using ErsatzTV.FFmpeg.Format;
namespace ErsatzTV.FFmpeg.Encoder.V4l2m2m;
public class EncoderHevcV4l2m2m : EncoderBase
{
public override string Name => "hevc_v4l2m2m";
public override StreamKind Kind => StreamKind.Video;
public override FrameState NextState(FrameState currentState) => currentState with
{
VideoFormat = VideoFormat.Hevc,
FrameDataLocation = FrameDataLocation.Hardware
};
}

3
ErsatzTV.FFmpeg/Filter/SubtitleHardwareUploadFilter.cs

@ -28,6 +28,9 @@ public class SubtitleHardwareUploadFilter : BaseFilter @@ -28,6 +28,9 @@ public class SubtitleHardwareUploadFilter : BaseFilter
// leave amf in software since we use a software overlay filter
HardwareAccelerationMode.Amf => string.Empty,
// leave v4l2m2m in software since we use a software overlay filter
HardwareAccelerationMode.V4l2m2m => string.Empty,
_ => "hwupload"
};

3
ErsatzTV.FFmpeg/Filter/WatermarkHardwareUploadFilter.cs

@ -27,6 +27,9 @@ public class WatermarkHardwareUploadFilter : BaseFilter @@ -27,6 +27,9 @@ public class WatermarkHardwareUploadFilter : BaseFilter
// leave amf in software since we use a software overlay filter
HardwareAccelerationMode.Amf => string.Empty,
// leave v4l2m2m in software since we use a software overlay filter
HardwareAccelerationMode.V4l2m2m => string.Empty,
_ => "hwupload"
};

1
ErsatzTV.FFmpeg/Format/VideoFormat.cs

@ -10,6 +10,7 @@ public static class VideoFormat @@ -10,6 +10,7 @@ public static class VideoFormat
public const string MsMpeg4V3 = "msmpeg4v3";
public const string Vc1 = "vc1";
public const string Mpeg4 = "mpeg4";
public const string Vp8 = "vp8";
public const string Vp9 = "vp9";
public const string Av1 = "av1";
public const string MpegTs = "mpegts";

3
ErsatzTV.FFmpeg/HardwareAccelerationMode.cs

@ -9,5 +9,6 @@ public enum HardwareAccelerationMode @@ -9,5 +9,6 @@ public enum HardwareAccelerationMode
VideoToolbox = 4,
Amf = 5,
OpenCL = 6,
Vulkan = 7
Vulkan = 7,
V4l2m2m = 8
}

15
ErsatzTV.FFmpeg/Pipeline/PipelineBuilderFactory.cs

@ -116,6 +116,21 @@ public class PipelineBuilderFactory : IPipelineBuilderFactory @@ -116,6 +116,21 @@ public class PipelineBuilderFactory : IPipelineBuilderFactory
fontsFolder,
_logger),
HardwareAccelerationMode.V4l2m2m when capabilities is not NoHardwareCapabilities => new
V4l2m2mPipelineBuilder(
ffmpegCapabilities,
capabilities,
hardwareAccelerationMode,
videoInputFile,
audioInputFile,
watermarkInputFile,
subtitleInputFile,
concatInputFile,
graphicsEngineInput,
reportsFolder,
fontsFolder,
_logger),
HardwareAccelerationMode.Amf when capabilities is not NoHardwareCapabilities => new AmfPipelineBuilder(
ffmpegCapabilities,
capabilities,

158
ErsatzTV.FFmpeg/Pipeline/V4l2m2mPipelineBuilder.cs

@ -0,0 +1,158 @@ @@ -0,0 +1,158 @@
using ErsatzTV.FFmpeg.Capabilities;
using ErsatzTV.FFmpeg.Decoder;
using ErsatzTV.FFmpeg.Decoder.V4l2m2m;
using ErsatzTV.FFmpeg.Encoder;
using ErsatzTV.FFmpeg.Encoder.V4l2m2m;
using ErsatzTV.FFmpeg.Filter;
using ErsatzTV.FFmpeg.Format;
using ErsatzTV.FFmpeg.OutputFormat;
using ErsatzTV.FFmpeg.OutputOption;
using Microsoft.Extensions.Logging;
namespace ErsatzTV.FFmpeg.Pipeline;
public class V4l2m2mPipelineBuilder : SoftwarePipelineBuilder
{
private readonly IHardwareCapabilities _hardwareCapabilities;
private readonly ILogger _logger;
public V4l2m2mPipelineBuilder(
IFFmpegCapabilities ffmpegCapabilities,
IHardwareCapabilities hardwareCapabilities,
HardwareAccelerationMode hardwareAccelerationMode,
Option<VideoInputFile> videoInputFile,
Option<AudioInputFile> audioInputFile,
Option<WatermarkInputFile> watermarkInputFile,
Option<SubtitleInputFile> subtitleInputFile,
Option<ConcatInputFile> concatInputFile,
Option<GraphicsEngineInput> graphicsEngineInput,
string reportsFolder,
string fontsFolder,
ILogger logger) : base(
ffmpegCapabilities,
hardwareAccelerationMode,
videoInputFile,
audioInputFile,
watermarkInputFile,
subtitleInputFile,
concatInputFile,
graphicsEngineInput,
reportsFolder,
fontsFolder,
logger)
{
_hardwareCapabilities = hardwareCapabilities;
_logger = logger;
}
protected override FFmpegState SetAccelState(
VideoStream videoStream,
FFmpegState ffmpegState,
FrameState desiredState,
PipelineContext context,
ICollection<IPipelineStep> pipelineSteps)
{
FFmpegCapability decodeCapability = _hardwareCapabilities.CanDecode(
videoStream.Codec,
videoStream.Profile,
videoStream.PixelFormat,
videoStream.ColorParams.IsHdr);
FFmpegCapability encodeCapability = _hardwareCapabilities.CanEncode(
desiredState.VideoFormat,
desiredState.VideoProfile,
desiredState.PixelFormat);
// use software encoding (rawvideo) when piping to parent hls segmenter
if (ffmpegState.OutputFormat is OutputFormatKind.Nut)
{
encodeCapability = FFmpegCapability.Software;
}
// disable hw accel if decoder/encoder isn't supported
return ffmpegState with
{
DecoderHardwareAccelerationMode = decodeCapability == FFmpegCapability.Hardware
? HardwareAccelerationMode.V4l2m2m
: HardwareAccelerationMode.None,
EncoderHardwareAccelerationMode = encodeCapability == FFmpegCapability.Hardware
? HardwareAccelerationMode.V4l2m2m
: HardwareAccelerationMode.None
};
}
protected override Option<IDecoder> SetDecoder(
VideoInputFile videoInputFile,
VideoStream videoStream,
FFmpegState ffmpegState,
PipelineContext context)
{
Option<IDecoder> maybeDecoder = (ffmpegState.DecoderHardwareAccelerationMode, videoStream.Codec) switch
{
(HardwareAccelerationMode.V4l2m2m, VideoFormat.Hevc) => new DecoderHevcV4l2m2m(),
(HardwareAccelerationMode.V4l2m2m, VideoFormat.H264) => new DecoderH264V4l2m2m(),
(HardwareAccelerationMode.V4l2m2m, VideoFormat.Mpeg2Video) => new DecoderMpeg2V4l2m2m(),
(HardwareAccelerationMode.V4l2m2m, VideoFormat.Mpeg4) => new DecoderMpeg4V4l2m2m(),
(HardwareAccelerationMode.V4l2m2m, VideoFormat.Vc1) => new DecoderVc1V4l2m2m(),
(HardwareAccelerationMode.V4l2m2m, VideoFormat.Vp8) => new DecoderVp8V4l2m2m(),
(HardwareAccelerationMode.V4l2m2m, VideoFormat.Vp9) => new DecoderVp9V4l2m2m(),
_ => GetSoftwareDecoder(videoStream)
};
foreach (IDecoder decoder in maybeDecoder)
{
videoInputFile.AddOption(decoder);
return Some(decoder);
}
return None;
}
protected override Option<IEncoder> GetEncoder(
FFmpegState ffmpegState,
FrameState currentState,
FrameState desiredState) =>
(ffmpegState.EncoderHardwareAccelerationMode, desiredState.VideoFormat) switch
{
(HardwareAccelerationMode.V4l2m2m, VideoFormat.Hevc) =>
new EncoderHevcV4l2m2m(),
(HardwareAccelerationMode.V4l2m2m, VideoFormat.H264) =>
new EncoderH264V4l2m2m(),
_ => GetSoftwareEncoder(ffmpegState, currentState, desiredState)
};
protected override List<IPipelineFilterStep> SetPixelFormat(
VideoStream videoStream,
Option<IPixelFormat> desiredPixelFormat,
FrameState currentState,
ICollection<IPipelineStep> pipelineSteps)
{
var result = new List<IPipelineFilterStep>();
foreach (IPixelFormat pixelFormat in desiredPixelFormat)
{
if (!videoStream.ColorParams.IsBt709)
{
// _logger.LogDebug("Adding colorspace filter");
var colorspace = new ColorspaceFilter(currentState, videoStream, pixelFormat);
currentState = colorspace.NextState(currentState);
result.Add(colorspace);
}
if (currentState.PixelFormat.Map(f => f.FFmpegName) != pixelFormat.FFmpegName)
{
_logger.LogDebug(
"Format {A} doesn't equal {B}",
currentState.PixelFormat.Map(f => f.FFmpegName),
pixelFormat.FFmpegName);
//result.Add(new PixelFormatFilter(pixelFormat));
}
pipelineSteps.Add(new PixelFormatOutputOption(pixelFormat));
}
return result;
}
}

24
ErsatzTV.Infrastructure/Health/Checks/HardwareAccelerationHealthCheck.cs

@ -132,19 +132,23 @@ public class HardwareAccelerationHealthCheck : BaseHealthCheck, IHardwareAcceler @@ -132,19 +132,23 @@ public class HardwareAccelerationHealthCheck : BaseHealthCheck, IHardwareAcceler
}
}
if (_runtimeInfo.IsOSPlatform(OSPlatform.Windows))
// not real ffmpeg hwaccels, but have hw encoders that we can use
string output2 = await GetProcessOutput(
ffmpegPath,
FFmpegEncodersArguments,
cancellationToken);
foreach (string encoder in output2.Split("\n").Map(s => s.Trim()))
{
string output2 = await GetProcessOutput(
ffmpegPath,
FFmpegEncodersArguments,
cancellationToken);
foreach (string method in output2.Split("\n").Map(s => s.Trim()))
if (_runtimeInfo.IsOSPlatform(OSPlatform.Windows) && encoder.Contains("_amf "))
{
if (method.Contains("_amf "))
{
result.Add(HardwareAccelerationKind.Amf);
}
result.Add(HardwareAccelerationKind.Amf);
}
// TODO: fix and enable V4L2 M2M
// else if (_runtimeInfo.IsOSPlatform(OSPlatform.Linux) && encoder.Contains("_v4l2m2m "))
// {
// result.Add(HardwareAccelerationKind.V4l2m2m);
// }
}
return result.ToList();

1
ErsatzTV/Pages/FFmpegEditor.razor

@ -430,6 +430,7 @@ @@ -430,6 +430,7 @@
HardwareAccelerationKind.Qsv => HardwareAccelerationMode.Qsv,
HardwareAccelerationKind.Vaapi => HardwareAccelerationMode.Vaapi,
HardwareAccelerationKind.VideoToolbox => HardwareAccelerationMode.VideoToolbox,
HardwareAccelerationKind.V4l2m2m => HardwareAccelerationMode.V4l2m2m,
_ => HardwareAccelerationMode.None
};

13
ErsatzTV/Validators/FFmpegProfileEditViewModelValidator.cs

@ -40,6 +40,11 @@ public class FFmpegProfileEditViewModelValidator : AbstractValidator<FFmpegProfi @@ -40,6 +40,11 @@ public class FFmpegProfileEditViewModelValidator : AbstractValidator<FFmpegProfi
FFmpegProfileVideoFormat.Hevc
];
private static readonly List<FFmpegProfileVideoFormat> V4l2m2mFormats =
[
FFmpegProfileVideoFormat.H264,
FFmpegProfileVideoFormat.Hevc
];
public FFmpegProfileEditViewModelValidator()
{
RuleFor(x => x.Name).NotEmpty();
@ -93,6 +98,14 @@ public class FFmpegProfileEditViewModelValidator : AbstractValidator<FFmpegProfi @@ -93,6 +98,14 @@ public class FFmpegProfileEditViewModelValidator : AbstractValidator<FFmpegProfi
.WithMessage("Amf supports formats (h264, hevc)");
});
When(
x => x.HardwareAcceleration == HardwareAccelerationKind.V4l2m2m,
() =>
{
RuleFor(x => x.VideoFormat).Must(c => V4l2m2mFormats.Contains(c))
.WithMessage("V4L2 M2M supports formats (h264, hevc)");
});
When(
x => x.VideoFormat == FFmpegProfileVideoFormat.Mpeg2Video,
() => RuleFor(x => x.BitDepth)

Loading…
Cancel
Save