Browse Source

use hardware-accelerated tonemapping with vaapi (#2028)

* add tonemap_vaapi filter

* let vaapi pipeline handle hdr content

* use tonemap_opencl with vaapi

* update changelog
pull/2029/head
Jason Dove 2 weeks ago committed by GitHub
parent
commit
dfdfa6f349
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 3
      CHANGELOG.md
  2. 1
      ErsatzTV.FFmpeg/Capabilities/FFmpegCapabilities.cs
  3. 4
      ErsatzTV.FFmpeg/Capabilities/FFmpegKnownFilter.cs
  4. 4
      ErsatzTV.FFmpeg/Capabilities/FFmpegKnownHardwareAcceleration.cs
  5. 12
      ErsatzTV.FFmpeg/Filter/Vaapi/TonemapVaapiFilter.cs
  6. 3
      ErsatzTV.FFmpeg/HardwareAccelerationMode.cs
  7. 19
      ErsatzTV.FFmpeg/Pipeline/PipelineBuilderFactory.cs
  8. 35
      ErsatzTV.FFmpeg/Pipeline/VaapiPipelineBuilder.cs
  9. 1
      ErsatzTV.sln.DotSettings

3
CHANGELOG.md

@ -23,7 +23,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- `Strict` will add nearly 24h (23:58) of unscheduled time so that it can start exactly at 6:00 AM the next day - `Strict` will add nearly 24h (23:58) of unscheduled time so that it can start exactly at 6:00 AM the next day
- `Flexible` will NOT add unscheduled time, and will schedule its item at 6:02 AM (which may also affect the scheduling of later items) - `Flexible` will NOT add unscheduled time, and will schedule its item at 6:02 AM (which may also affect the scheduling of later items)
- Add basic HDR transcoding support - Add basic HDR transcoding support
- For this initial implementation, HDR content will *always* use a software pipeline - VAAPI may use hardware-accelerated tone mapping
- In all other cases, HDR content will use a software pipeline
### Changed ### Changed
- Start to make UI minimally responsive (functional on smaller screens) - Start to make UI minimally responsive (functional on smaller screens)

1
ErsatzTV.FFmpeg/Capabilities/FFmpegCapabilities.cs

@ -40,6 +40,7 @@ public class FFmpegCapabilities : IFFmpegCapabilities
HardwareAccelerationMode.Qsv => FFmpegKnownHardwareAcceleration.Qsv, HardwareAccelerationMode.Qsv => FFmpegKnownHardwareAcceleration.Qsv,
HardwareAccelerationMode.Vaapi => FFmpegKnownHardwareAcceleration.Vaapi, HardwareAccelerationMode.Vaapi => FFmpegKnownHardwareAcceleration.Vaapi,
HardwareAccelerationMode.VideoToolbox => FFmpegKnownHardwareAcceleration.VideoToolbox, HardwareAccelerationMode.VideoToolbox => FFmpegKnownHardwareAcceleration.VideoToolbox,
HardwareAccelerationMode.OpenCL => FFmpegKnownHardwareAcceleration.OpenCL,
_ => Option<FFmpegKnownHardwareAcceleration>.None _ => Option<FFmpegKnownHardwareAcceleration>.None
}; };

4
ErsatzTV.FFmpeg/Capabilities/FFmpegKnownFilter.cs

@ -3,6 +3,7 @@ namespace ErsatzTV.FFmpeg.Capabilities;
public record FFmpegKnownFilter public record FFmpegKnownFilter
{ {
public static readonly FFmpegKnownFilter ScaleNpp = new("scale_npp"); public static readonly FFmpegKnownFilter ScaleNpp = new("scale_npp");
public static readonly FFmpegKnownFilter TonemapOpenCL = new("tonemap_opencl");
private FFmpegKnownFilter(string Name) => this.Name = Name; private FFmpegKnownFilter(string Name) => this.Name = Name;
@ -11,6 +12,7 @@ public record FFmpegKnownFilter
public static IList<string> AllFilters => public static IList<string> AllFilters =>
new[] new[]
{ {
ScaleNpp.Name ScaleNpp.Name,
TonemapOpenCL.Name,
}; };
} }

4
ErsatzTV.FFmpeg/Capabilities/FFmpegKnownHardwareAcceleration.cs

@ -7,6 +7,7 @@ public record FFmpegKnownHardwareAcceleration
public static readonly FFmpegKnownHardwareAcceleration Qsv = new("qsv"); public static readonly FFmpegKnownHardwareAcceleration Qsv = new("qsv");
public static readonly FFmpegKnownHardwareAcceleration Vaapi = new("vaapi"); public static readonly FFmpegKnownHardwareAcceleration Vaapi = new("vaapi");
public static readonly FFmpegKnownHardwareAcceleration VideoToolbox = new("videotoolbox"); public static readonly FFmpegKnownHardwareAcceleration VideoToolbox = new("videotoolbox");
public static readonly FFmpegKnownHardwareAcceleration OpenCL = new("opencl");
private FFmpegKnownHardwareAcceleration(string Name) => this.Name = Name; private FFmpegKnownHardwareAcceleration(string Name) => this.Name = Name;
@ -19,6 +20,7 @@ public record FFmpegKnownHardwareAcceleration
Cuda.Name, Cuda.Name,
Qsv.Name, Qsv.Name,
Vaapi.Name, Vaapi.Name,
VideoToolbox.Name VideoToolbox.Name,
OpenCL.Name
}; };
} }

12
ErsatzTV.FFmpeg/Filter/Vaapi/TonemapVaapiFilter.cs

@ -0,0 +1,12 @@
namespace ErsatzTV.FFmpeg.Filter.Vaapi;
public class TonemapVaapiFilter : BaseFilter
{
public override string Filter => "hwupload=derive_device=vaapi,hwmap=derive_device=opencl,tonemap_opencl,hwmap=derive_device=vaapi:reverse=1";
public override FrameState NextState(FrameState currentState) =>
currentState with
{
FrameDataLocation = FrameDataLocation.Hardware
};
}

3
ErsatzTV.FFmpeg/HardwareAccelerationMode.cs

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

19
ErsatzTV.FFmpeg/Pipeline/PipelineBuilderFactory.cs

@ -44,10 +44,10 @@ public class PipelineBuilderFactory : IPipelineBuilderFactory
return hardwareAccelerationMode switch return hardwareAccelerationMode switch
{ {
// force software pipeline when content is HDR HardwareAccelerationMode.Vaapi when capabilities is not NoHardwareCapabilities => new VaapiPipelineBuilder(
_ when isHdrContent => new SoftwarePipelineBuilder(
ffmpegCapabilities, ffmpegCapabilities,
HardwareAccelerationMode.None, capabilities,
hardwareAccelerationMode,
videoInputFile, videoInputFile,
audioInputFile, audioInputFile,
watermarkInputFile, watermarkInputFile,
@ -57,10 +57,10 @@ public class PipelineBuilderFactory : IPipelineBuilderFactory
fontsFolder, fontsFolder,
_logger), _logger),
HardwareAccelerationMode.Nvenc when capabilities is not NoHardwareCapabilities => new NvidiaPipelineBuilder( // force software pipeline when content is HDR (and not VAAPI)
_ when isHdrContent => new SoftwarePipelineBuilder(
ffmpegCapabilities, ffmpegCapabilities,
capabilities, HardwareAccelerationMode.None,
hardwareAccelerationMode,
videoInputFile, videoInputFile,
audioInputFile, audioInputFile,
watermarkInputFile, watermarkInputFile,
@ -69,7 +69,8 @@ public class PipelineBuilderFactory : IPipelineBuilderFactory
reportsFolder, reportsFolder,
fontsFolder, fontsFolder,
_logger), _logger),
HardwareAccelerationMode.Vaapi when capabilities is not NoHardwareCapabilities => new VaapiPipelineBuilder(
HardwareAccelerationMode.Nvenc when capabilities is not NoHardwareCapabilities => new NvidiaPipelineBuilder(
ffmpegCapabilities, ffmpegCapabilities,
capabilities, capabilities,
hardwareAccelerationMode, hardwareAccelerationMode,
@ -81,6 +82,7 @@ public class PipelineBuilderFactory : IPipelineBuilderFactory
reportsFolder, reportsFolder,
fontsFolder, fontsFolder,
_logger), _logger),
HardwareAccelerationMode.Qsv when capabilities is not NoHardwareCapabilities => new QsvPipelineBuilder( HardwareAccelerationMode.Qsv when capabilities is not NoHardwareCapabilities => new QsvPipelineBuilder(
ffmpegCapabilities, ffmpegCapabilities,
capabilities, capabilities,
@ -93,6 +95,7 @@ public class PipelineBuilderFactory : IPipelineBuilderFactory
reportsFolder, reportsFolder,
fontsFolder, fontsFolder,
_logger), _logger),
HardwareAccelerationMode.VideoToolbox when capabilities is not NoHardwareCapabilities => new HardwareAccelerationMode.VideoToolbox when capabilities is not NoHardwareCapabilities => new
VideoToolboxPipelineBuilder( VideoToolboxPipelineBuilder(
ffmpegCapabilities, ffmpegCapabilities,
@ -106,6 +109,7 @@ public class PipelineBuilderFactory : IPipelineBuilderFactory
reportsFolder, reportsFolder,
fontsFolder, fontsFolder,
_logger), _logger),
HardwareAccelerationMode.Amf when capabilities is not NoHardwareCapabilities => new AmfPipelineBuilder( HardwareAccelerationMode.Amf when capabilities is not NoHardwareCapabilities => new AmfPipelineBuilder(
ffmpegCapabilities, ffmpegCapabilities,
capabilities, capabilities,
@ -118,6 +122,7 @@ public class PipelineBuilderFactory : IPipelineBuilderFactory
reportsFolder, reportsFolder,
fontsFolder, fontsFolder,
_logger), _logger),
_ => new SoftwarePipelineBuilder( _ => new SoftwarePipelineBuilder(
ffmpegCapabilities, ffmpegCapabilities,
HardwareAccelerationMode.None, HardwareAccelerationMode.None,

35
ErsatzTV.FFmpeg/Pipeline/VaapiPipelineBuilder.cs

@ -17,6 +17,7 @@ namespace ErsatzTV.FFmpeg.Pipeline;
public class VaapiPipelineBuilder : SoftwarePipelineBuilder public class VaapiPipelineBuilder : SoftwarePipelineBuilder
{ {
private readonly IFFmpegCapabilities _ffmpegCapabilities;
private readonly IHardwareCapabilities _hardwareCapabilities; private readonly IHardwareCapabilities _hardwareCapabilities;
private readonly ILogger _logger; private readonly ILogger _logger;
@ -43,6 +44,7 @@ public class VaapiPipelineBuilder : SoftwarePipelineBuilder
fontsFolder, fontsFolder,
logger) logger)
{ {
_ffmpegCapabilities = ffmpegCapabilities;
_hardwareCapabilities = hardwareCapabilities; _hardwareCapabilities = hardwareCapabilities;
_logger = logger; _logger = logger;
} }
@ -168,6 +170,8 @@ public class VaapiPipelineBuilder : SoftwarePipelineBuilder
// _logger.LogDebug("After decode: {PixelFormat}", currentState.PixelFormat); // _logger.LogDebug("After decode: {PixelFormat}", currentState.PixelFormat);
currentState = SetTonemap(videoInputFile, videoStream, ffmpegState, desiredState, currentState);
currentState = SetDeinterlace(videoInputFile, context, ffmpegState, currentState); currentState = SetDeinterlace(videoInputFile, context, ffmpegState, currentState);
// _logger.LogDebug("After deinterlace: {PixelFormat}", currentState.PixelFormat); // _logger.LogDebug("After deinterlace: {PixelFormat}", currentState.PixelFormat);
@ -619,4 +623,35 @@ public class VaapiPipelineBuilder : SoftwarePipelineBuilder
return currentState; return currentState;
} }
private FrameState SetTonemap(
VideoInputFile videoInputFile,
VideoStream videoStream,
FFmpegState ffmpegState,
FrameState desiredState,
FrameState currentState)
{
if (videoStream.ColorParams.IsHdr)
{
foreach (IPixelFormat pixelFormat in desiredState.PixelFormat)
{
if (ffmpegState.DecoderHardwareAccelerationMode == HardwareAccelerationMode.Vaapi && _ffmpegCapabilities.HasFilter(FFmpegKnownFilter.TonemapOpenCL))
{
var filter = new TonemapVaapiFilter();
currentState = filter.NextState(currentState);
videoStream.ResetColorParams(ColorParams.Default);
videoInputFile.FilterSteps.Add(filter);
}
else
{
var filter = new TonemapFilter(currentState, pixelFormat);
currentState = filter.NextState(currentState);
videoStream.ResetColorParams(ColorParams.Default);
videoInputFile.FilterSteps.Add(filter);
}
}
}
return currentState;
}
} }

1
ErsatzTV.sln.DotSettings

@ -1,5 +1,6 @@
<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/=CL/@EntryIndexedValue">CL</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/=DTO/@EntryIndexedValue">DTO</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=EPG/@EntryIndexedValue">EPG</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=EPG/@EntryIndexedValue">EPG</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/=FF/@EntryIndexedValue">FF</s:String>

Loading…
Cancel
Save