Browse Source

qsv and vaapi scaling fixes (#966)

* add qsv device option to ffmpeg profile

* fix vaapi scaling

* cleanup
pull/968/head
Jason Dove 4 years ago committed by GitHub
parent
commit
27b923b462
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      CHANGELOG.md
  2. 5
      ErsatzTV.Core/FFmpeg/FFmpegLibraryProcessService.cs
  3. 6
      ErsatzTV.FFmpeg/Filter/AvailableScaleFilters.cs
  4. 14
      ErsatzTV.FFmpeg/Filter/SetDarFilter.cs
  5. 35
      ErsatzTV.FFmpeg/Filter/Vaapi/ScaleVaapiFilter.cs
  6. 6
      ErsatzTV.FFmpeg/Option/HardwareAcceleration/AvailableHardwareAccelerationOptions.cs
  7. 20
      ErsatzTV.FFmpeg/Option/HardwareAcceleration/QsvHardwareAccelerationOption.cs
  8. 23
      ErsatzTV.FFmpeg/PipelineBuilder.cs
  9. 55
      ErsatzTV/Pages/FFmpegEditor.razor

3
CHANGELOG.md

@ -9,6 +9,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). @@ -9,6 +9,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Fix scaling logic for `Nvidia` acceleration and software mode
- Attempt to position watermarks within content (not over added black padding)
### Added
- Add `QSV Device` option to ffmpeg profile on linux
## [0.6.7-beta] - 2022-09-05
### Fixed
- When all audio streams are selected with `HLS Direct`, explicitly copy them without transcoding

5
ErsatzTV.Core/FFmpeg/FFmpegLibraryProcessService.cs

@ -623,7 +623,10 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService @@ -623,7 +623,10 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
}
private static Option<string> VaapiDeviceName(HardwareAccelerationMode accelerationMode, string vaapiDevice) =>
accelerationMode == HardwareAccelerationMode.Vaapi ? vaapiDevice : Option<string>.None;
accelerationMode == HardwareAccelerationMode.Vaapi ||
OperatingSystem.IsLinux() && accelerationMode == HardwareAccelerationMode.Qsv
? vaapiDevice
: Option<string>.None;
private static string GetVideoFormat(FFmpegPlaybackSettings playbackSettings) =>
playbackSettings.VideoFormat switch

6
ErsatzTV.FFmpeg/Filter/AvailableScaleFilters.cs

@ -28,7 +28,11 @@ public static class AvailableScaleFilters @@ -28,7 +28,11 @@ public static class AvailableScaleFilters
extraHardwareFrames,
isAnamorphicEdgeCase,
sampleAspectRatio),
HardwareAccelerationMode.Vaapi => new ScaleVaapiFilter(currentState, scaledSize, paddedSize),
HardwareAccelerationMode.Vaapi => new ScaleVaapiFilter(
currentState,
scaledSize,
paddedSize,
isAnamorphicEdgeCase),
_ => new ScaleFilter(currentState, scaledSize, paddedSize, isAnamorphicEdgeCase)
};
}

14
ErsatzTV.FFmpeg/Filter/SetDarFilter.cs

@ -1,14 +0,0 @@ @@ -1,14 +0,0 @@
namespace ErsatzTV.FFmpeg.Filter;
public class SetDarFilter : BaseFilter
{
private readonly string _displayAspectRatio;
public SetDarFilter(string displayAspectRatio)
{
_displayAspectRatio = displayAspectRatio;
}
public override string Filter => $"setdar=dar={_displayAspectRatio.Replace(':', '/')}";
public override FrameState NextState(FrameState currentState) => currentState;
}

35
ErsatzTV.FFmpeg/Filter/Vaapi/ScaleVaapiFilter.cs

@ -6,13 +6,19 @@ public class ScaleVaapiFilter : BaseFilter @@ -6,13 +6,19 @@ public class ScaleVaapiFilter : BaseFilter
{
private readonly FrameState _currentState;
private readonly FrameSize _paddedSize;
private readonly bool _isAnamorphicEdgeCase;
private readonly FrameSize _scaledSize;
public ScaleVaapiFilter(FrameState currentState, FrameSize scaledSize, FrameSize paddedSize)
public ScaleVaapiFilter(
FrameState currentState,
FrameSize scaledSize,
FrameSize paddedSize,
bool isAnamorphicEdgeCase)
{
_currentState = currentState;
_scaledSize = scaledSize;
_paddedSize = paddedSize;
_isAnamorphicEdgeCase = isAnamorphicEdgeCase;
}
public override string Filter
@ -31,14 +37,34 @@ public class ScaleVaapiFilter : BaseFilter @@ -31,14 +37,34 @@ public class ScaleVaapiFilter : BaseFilter
}
else
{
string aspectRatio = string.Empty;
if (_scaledSize != _paddedSize)
{
aspectRatio = ":force_original_aspect_ratio=decrease";
}
string squareScale = string.Empty;
string targetSize = $"{_paddedSize.Width}:{_paddedSize.Height}";
string format = string.Empty;
foreach (IPixelFormat pixelFormat in _currentState.PixelFormat)
{
format = $":format={pixelFormat.FFmpegName}";
}
string targetSize = $"{_paddedSize.Width}:{_paddedSize.Height}";
scale = $"scale_vaapi={targetSize}:force_divisible_by=2{format}";
if (_isAnamorphicEdgeCase)
{
squareScale = $"scale_vaapi=iw:sar*ih{format},setsar=1,";
}
else if (_currentState.IsAnamorphic)
{
squareScale = $"scale_vaapi=iw*sar:ih{format},setsar=1,";
}
else
{
aspectRatio += ",setsar=1";
}
scale = $"{squareScale}scale_vaapi={targetSize}:force_divisible_by=2{format}{aspectRatio}";
}
if (_currentState.FrameDataLocation == FrameDataLocation.Hardware)
@ -59,6 +85,7 @@ public class ScaleVaapiFilter : BaseFilter @@ -59,6 +85,7 @@ public class ScaleVaapiFilter : BaseFilter
{
ScaledSize = _scaledSize,
PaddedSize = _scaledSize,
FrameDataLocation = FrameDataLocation.Hardware
FrameDataLocation = FrameDataLocation.Hardware,
IsAnamorphic = false // this filter always outputs square pixels
};
}

6
ErsatzTV.FFmpeg/Option/HardwareAcceleration/AvailableHardwareAccelerationOptions.cs

@ -6,13 +6,13 @@ public static class AvailableHardwareAccelerationOptions @@ -6,13 +6,13 @@ public static class AvailableHardwareAccelerationOptions
{
public static Option<IPipelineStep> ForMode(
HardwareAccelerationMode mode,
Option<string> vaapiDevice,
Option<string> gpuDevice,
ILogger logger) =>
mode switch
{
HardwareAccelerationMode.Nvenc => new CudaHardwareAccelerationOption(),
HardwareAccelerationMode.Qsv => new QsvHardwareAccelerationOption(),
HardwareAccelerationMode.Vaapi => GetVaapiAcceleration(vaapiDevice, logger),
HardwareAccelerationMode.Qsv => new QsvHardwareAccelerationOption(gpuDevice),
HardwareAccelerationMode.Vaapi => GetVaapiAcceleration(gpuDevice, logger),
HardwareAccelerationMode.VideoToolbox => new VideoToolboxHardwareAccelerationOption(),
HardwareAccelerationMode.Amf => new AmfHardwareAccelerationOption(),
HardwareAccelerationMode.None => Option<IPipelineStep>.None,

20
ErsatzTV.FFmpeg/Option/HardwareAcceleration/QsvHardwareAccelerationOption.cs

@ -4,6 +4,13 @@ namespace ErsatzTV.FFmpeg.Option.HardwareAcceleration; @@ -4,6 +4,13 @@ namespace ErsatzTV.FFmpeg.Option.HardwareAcceleration;
public class QsvHardwareAccelerationOption : GlobalOption
{
private readonly Option<string> _qsvDevice;
public QsvHardwareAccelerationOption(Option<string> qsvDevice)
{
_qsvDevice = qsvDevice;
}
// TODO: read this from ffmpeg output
private readonly List<string> _supportedFFmpegFormats = new()
{
@ -17,7 +24,7 @@ public class QsvHardwareAccelerationOption : GlobalOption @@ -17,7 +24,7 @@ public class QsvHardwareAccelerationOption : GlobalOption
{
string[] initDevices = OperatingSystem.IsWindows()
? new[] { "-init_hw_device", "qsv=hw:hw,child_device_type=dxva2", "-filter_hw_device", "hw" }
: new[] { "-init_hw_device", "qsv=hw", "-filter_hw_device", "hw" };
: new[] { "-init_hw_device", "qsv=hw:hw,child_device_type=vaapi", "-filter_hw_device", "hw" };
var result = new List<string>
{
@ -25,6 +32,17 @@ public class QsvHardwareAccelerationOption : GlobalOption @@ -25,6 +32,17 @@ public class QsvHardwareAccelerationOption : GlobalOption
"-hwaccel_output_format", "qsv"
};
if (OperatingSystem.IsLinux())
{
foreach (string qsvDevice in _qsvDevice)
{
if (!string.IsNullOrWhiteSpace(qsvDevice))
{
result.AddRange(new[] { "-qsv_device", qsvDevice });
}
}
}
result.AddRange(initDevices);
return result;

23
ErsatzTV.FFmpeg/PipelineBuilder.cs

@ -385,13 +385,6 @@ public class PipelineBuilder @@ -385,13 +385,6 @@ public class PipelineBuilder
IPipelineFilterStep padStep = new PadFilter(currentState, desiredState.PaddedSize);
currentState = padStep.NextState(currentState);
_videoInputFile.Iter(f => f.FilterSteps.Add(padStep));
// if (videoStream.DisplayAspectRatio == desiredState.DisplayAspectRatio)
// {
// IPipelineFilterStep darStep = new SetDarFilter(desiredState.DisplayAspectRatio);
// currentState = darStep.NextState(currentState);
// _videoInputFile.Iter(f => f.FilterSteps.Add(darStep));
// }
}
}
else if (currentState.ScaledSize != desiredState.ScaledSize)
@ -415,14 +408,6 @@ public class PipelineBuilder @@ -415,14 +408,6 @@ public class PipelineBuilder
currentState = padStep.NextState(currentState);
_videoInputFile.Iter(f => f.FilterSteps.Add(padStep));
}
// if (videoStream.DisplayAspectRatio == desiredState.DisplayAspectRatio ||
// ffmpegState.EncoderHardwareAccelerationMode == HardwareAccelerationMode.Qsv)
// {
// IPipelineFilterStep darStep = new SetDarFilter(desiredState.DisplayAspectRatio);
// currentState = darStep.NextState(currentState);
// _videoInputFile.Iter(f => f.FilterSteps.Add(darStep));
// }
}
else if (currentState.PaddedSize != desiredState.PaddedSize)
{
@ -444,14 +429,6 @@ public class PipelineBuilder @@ -444,14 +429,6 @@ public class PipelineBuilder
currentState = padStep.NextState(currentState);
_videoInputFile.Iter(f => f.FilterSteps.Add(padStep));
}
// if (videoStream.DisplayAspectRatio == desiredState.DisplayAspectRatio ||
// ffmpegState.EncoderHardwareAccelerationMode == HardwareAccelerationMode.Qsv)
// {
// IPipelineFilterStep darStep = new SetDarFilter(desiredState.DisplayAspectRatio);
// currentState = darStep.NextState(currentState);
// _videoInputFile.Iter(f => f.FilterSteps.Add(darStep));
// }
}
if (hasOverlay && currentState.PixelFormat.Map(pf => pf.FFmpegName) !=

55
ErsatzTV/Pages/FFmpegEditor.razor

@ -4,6 +4,8 @@ @@ -4,6 +4,8 @@
@using ErsatzTV.Application.Resolutions
@using ErsatzTV.Core.FFmpeg
@using ErsatzTV.Application.FFmpegProfiles
@using ErsatzTV.Infrastructure.Runtime
@using System.Runtime.InteropServices
@implements IDisposable
@inject NavigationManager _navigationManager
@inject ILogger<FFmpegEditor> _logger
@ -60,25 +62,40 @@ @@ -60,25 +62,40 @@
}
</MudSelect>
</MudElement>
<MudElement HtmlTag="div" Class="mt-3">
<MudSelect Disabled="@(_model.HardwareAcceleration != HardwareAccelerationKind.Vaapi)" Label="VAAPI Driver" @bind-Value="_model.VaapiDriver" For="@(() => _model.VaapiDriver)">
@foreach (VaapiDriver driver in Enum.GetValues<VaapiDriver>())
{
<MudSelectItem Value="@driver">@driver</MudSelectItem>
}
</MudSelect>
</MudElement>
<MudElement HtmlTag="div" Class="mt-3">
<MudSelect Disabled="@(_model.HardwareAcceleration != HardwareAccelerationKind.Vaapi)" Label="VAAPI Device" @bind-Value="_model.VaapiDevice" For="@(() => _model.VaapiDevice)">
@foreach (string device in _vaapiDevices)
{
<MudSelectItem Value="@device">@device</MudSelectItem>
}
</MudSelect>
</MudElement>
<MudElement HtmlTag="div" Class="mt-3">
<MudTextField Disabled="@(_model.HardwareAcceleration != HardwareAccelerationKind.Qsv)" Label="QSV Extra Hardware Frames" @bind-Value="_model.QsvExtraHardwareFrames" For="@(() => _model.QsvExtraHardwareFrames)" />
</MudElement>
@if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
@if (_model.HardwareAcceleration == HardwareAccelerationKind.Vaapi)
{
<MudElement HtmlTag="div" Class="mt-3">
<MudSelect Disabled="@(_model.HardwareAcceleration != HardwareAccelerationKind.Vaapi)" Label="VAAPI Driver" @bind-Value="_model.VaapiDriver" For="@(() => _model.VaapiDriver)">
@foreach (VaapiDriver driver in Enum.GetValues<VaapiDriver>())
{
<MudSelectItem Value="@driver">@driver</MudSelectItem>
}
</MudSelect>
</MudElement>
}
@if (_model.HardwareAcceleration == HardwareAccelerationKind.Vaapi || _model.HardwareAcceleration == HardwareAccelerationKind.Qsv)
{
<MudElement HtmlTag="div" Class="mt-3">
<MudSelect Disabled="@(_model.HardwareAcceleration != HardwareAccelerationKind.Vaapi && _model.HardwareAcceleration != HardwareAccelerationKind.Qsv)"
Label="@(_model.HardwareAcceleration == HardwareAccelerationKind.Vaapi ? "VAAPI Device" : "QSV Device")"
@bind-Value="_model.VaapiDevice"
For="@(() => _model.VaapiDevice)">
@foreach (string device in _vaapiDevices)
{
<MudSelectItem Value="@device">@device</MudSelectItem>
}
</MudSelect>
</MudElement>
}
}
@if (_model.HardwareAcceleration == HardwareAccelerationKind.Qsv)
{
<MudElement HtmlTag="div" Class="mt-3">
<MudTextField Disabled="@(_model.HardwareAcceleration != HardwareAccelerationKind.Qsv)" Label="QSV Extra Hardware Frames" @bind-Value="_model.QsvExtraHardwareFrames" For="@(() => _model.QsvExtraHardwareFrames)"/>
</MudElement>
}
<MudElement HtmlTag="div" Class="mt-3">
<MudCheckBox Label="Normalize Frame Rate" @bind-Checked="@_model.NormalizeFramerate" For="@(() => _model.NormalizeFramerate)"/>
</MudElement>

Loading…
Cancel
Save