Browse Source

nvidia and software mode scaling improvements (#965)

* convert to square pixels before software scaling

* convert to square pixels in nvidia scale filter

* more scaling fixes; position watermark within padded content

* fix image subtitle scaling

* fix qsv scaling

* update dependencies
pull/968/head
Jason Dove 3 years ago committed by GitHub
parent
commit
357dfee050
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      CHANGELOG.md
  2. 4
      ErsatzTV.Core.Tests/ErsatzTV.Core.Tests.csproj
  3. 4
      ErsatzTV.Core/ErsatzTV.Core.csproj
  4. 13
      ErsatzTV.Core/FFmpeg/FFmpegLibraryProcessService.cs
  5. 20
      ErsatzTV.FFmpeg.Tests/PipelineBuilderTests.cs
  6. 2
      ErsatzTV.FFmpeg/ErsatzTV.FFmpeg.csproj
  7. 18
      ErsatzTV.FFmpeg/Filter/AvailableScaleFilters.cs
  8. 4
      ErsatzTV.FFmpeg/Filter/AvailableSubtitleOverlayFilters.cs
  9. 14
      ErsatzTV.FFmpeg/Filter/AvailableWatermarkOverlayFilters.cs
  10. 98
      ErsatzTV.FFmpeg/Filter/ComplexFilter.cs
  11. 2
      ErsatzTV.FFmpeg/Filter/Cuda/OverlaySubtitleCudaFilter.cs
  12. 11
      ErsatzTV.FFmpeg/Filter/Cuda/OverlayWatermarkCudaFilter.cs
  13. 33
      ErsatzTV.FFmpeg/Filter/Cuda/ScaleCudaFilter.cs
  14. 2
      ErsatzTV.FFmpeg/Filter/Cuda/SubtitleScaleNppFilter.cs
  15. 2
      ErsatzTV.FFmpeg/Filter/OverlaySubtitleFilter.cs
  16. 29
      ErsatzTV.FFmpeg/Filter/OverlayWatermarkFilter.cs
  17. 6
      ErsatzTV.FFmpeg/Filter/Qsv/OverlaySubtitleQsvFilter.cs
  18. 11
      ErsatzTV.FFmpeg/Filter/Qsv/OverlayWatermarkQsvFilter.cs
  19. 42
      ErsatzTV.FFmpeg/Filter/Qsv/ScaleQsvFilter.cs
  20. 2
      ErsatzTV.FFmpeg/Filter/Qsv/SubtitleScaleQsvFilter.cs
  21. 33
      ErsatzTV.FFmpeg/Filter/ScaleFilter.cs
  22. 4
      ErsatzTV.FFmpeg/Filter/ScaleImageFilter.cs
  23. 7
      ErsatzTV.FFmpeg/FrameState.cs
  24. 1
      ErsatzTV.FFmpeg/InputFile.cs
  25. 94
      ErsatzTV.FFmpeg/MediaStream.cs
  26. 68
      ErsatzTV.FFmpeg/PipelineBuilder.cs
  27. 6
      ErsatzTV.Infrastructure/ErsatzTV.Infrastructure.csproj
  28. 14
      ErsatzTV/ErsatzTV.csproj
  29. 2
      ErsatzTV/Program.cs

2
CHANGELOG.md

@ -6,6 +6,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). @@ -6,6 +6,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [Unreleased]
### Fixed
- Fix typo introduced in `0.6.7-beta` that stopped QSV HEVC encoder from working
- Fix scaling logic for `Nvidia` acceleration and software mode
- Attempt to position watermarks within content (not over added black padding)
## [0.6.7-beta] - 2022-09-05
### Fixed

4
ErsatzTV.Core.Tests/ErsatzTV.Core.Tests.csproj

@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.2" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="6.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.1" />
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.3.44">
@ -24,7 +24,7 @@ @@ -24,7 +24,7 @@
<PackageReference Include="Moq" Version="4.18.2" />
<PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
<PackageReference Include="Serilog" Version="2.11.0" />
<PackageReference Include="Serilog" Version="2.12.0" />
<PackageReference Include="Serilog.Extensions.Logging" Version="3.1.0" />
<PackageReference Include="Serilog.Sinks.Debug" Version="2.0.0" />
</ItemGroup>

4
ErsatzTV.Core/ErsatzTV.Core.csproj

@ -16,14 +16,14 @@ @@ -16,14 +16,14 @@
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.2" />
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="2.2.1" />
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.3.44">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Serilog" Version="2.11.0" />
<PackageReference Include="Serilog" Version="2.12.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />
</ItemGroup>

13
ErsatzTV.Core/FFmpeg/FFmpegLibraryProcessService.cs

@ -147,6 +147,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService @@ -147,6 +147,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
videoStream.Codec,
AvailablePixelFormats.ForPixelFormat(videoStream.PixelFormat, _logger),
new FrameSize(videoVersion.Width, videoVersion.Height),
videoVersion.SampleAspectRatio,
videoVersion.DisplayAspectRatio,
videoVersion.RFrameRate,
videoPath != audioPath); // still image when paths are different
@ -214,10 +215,10 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService @@ -214,10 +215,10 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
false, // TODO: fallback filler needs to loop
videoFormat,
desiredPixelFormat,
await playbackSettings.ScaledSize.Map(ss => new FrameSize(ss.Width, ss.Height))
.IfNoneAsync(new FrameSize(videoVersion.Width, videoVersion.Height)),
ffmpegVideoStream.SquarePixelFrameSize(
new FrameSize(channel.FFmpegProfile.Resolution.Width, channel.FFmpegProfile.Resolution.Height)),
new FrameSize(channel.FFmpegProfile.Resolution.Width, channel.FFmpegProfile.Resolution.Height),
channel.FFmpegProfile.Resolution.Width == 640 ? "4:3" : "16:9",
false,
playbackSettings.FrameRate,
playbackSettings.VideoBitrate,
playbackSettings.VideoBufferSize,
@ -314,7 +315,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService @@ -314,7 +315,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
new PixelFormatYuv420P(),
new FrameSize(desiredResolution.Width, desiredResolution.Height),
new FrameSize(desiredResolution.Width, desiredResolution.Height),
desiredResolution.Width == 640 ? "4:3" : "16:9",
false,
playbackSettings.FrameRate,
playbackSettings.VideoBitrate,
playbackSettings.VideoBufferSize,
@ -342,6 +343,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService @@ -342,6 +343,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
VideoFormat.GeneratedImage,
new PixelFormatUnknown(), // leave this unknown so we convert to desired yuv420p
new FrameSize(videoVersion.Width, videoVersion.Height),
videoVersion.SampleAspectRatio,
videoVersion.DisplayAspectRatio,
None,
true);
@ -434,7 +436,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService @@ -434,7 +436,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
{
var videoInputFile = new VideoInputFile(
inputFile,
new List<VideoStream> { new(0, string.Empty, None, FrameSize.Unknown, string.Empty, None, true) });
new List<VideoStream> { new(0, string.Empty, None, FrameSize.Unknown, string.Empty, string.Empty, None, true) });
var pipelineBuilder = new PipelineBuilder(
_runtimeInfo,
@ -516,6 +518,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService @@ -516,6 +518,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
new PixelFormatUnknown(),
new FrameSize(1, 1),
string.Empty,
string.Empty,
Option<string>.None,
!options.IsAnimated)
},

20
ErsatzTV.FFmpeg.Tests/PipelineBuilderTests.cs

@ -26,7 +26,7 @@ public class PipelineGeneratorTests @@ -26,7 +26,7 @@ public class PipelineGeneratorTests
var videoInputFile = new VideoInputFile(
"/tmp/whatever.mkv",
new List<VideoStream>
{ new(0, VideoFormat.H264, new PixelFormatYuv420P(), new FrameSize(1920, 1080), "16:9", "24", false) });
{ new(0, VideoFormat.H264, new PixelFormatYuv420P(), new FrameSize(1920, 1080), "1:1", "16:9", "24", false) });
var audioInputFile = new AudioInputFile(
"/tmp/whatever.mkv",
@ -47,7 +47,7 @@ public class PipelineGeneratorTests @@ -47,7 +47,7 @@ public class PipelineGeneratorTests
new PixelFormatYuv420P(),
new FrameSize(1920, 1080),
new FrameSize(1920, 1080),
"16:9",
false,
Option<int>.None,
2000,
4000,
@ -99,7 +99,7 @@ public class PipelineGeneratorTests @@ -99,7 +99,7 @@ public class PipelineGeneratorTests
var videoInputFile = new VideoInputFile(
"/tmp/whatever.mkv",
new List<VideoStream>
{ new(0, VideoFormat.H264, new PixelFormatYuv420P(), new FrameSize(1920, 1080), "16:9", "24", false) });
{ new(0, VideoFormat.H264, new PixelFormatYuv420P(), new FrameSize(1920, 1080), "1:1", "16:9", "24", false) });
var audioInputFile = new AudioInputFile(
"/tmp/whatever.mkv",
@ -120,7 +120,7 @@ public class PipelineGeneratorTests @@ -120,7 +120,7 @@ public class PipelineGeneratorTests
new PixelFormatYuv420P(),
new FrameSize(1920, 1080),
new FrameSize(1920, 1080),
"16:9",
false,
Option<int>.None,
2000,
4000,
@ -198,7 +198,7 @@ public class PipelineGeneratorTests @@ -198,7 +198,7 @@ public class PipelineGeneratorTests
var videoInputFile = new VideoInputFile(
"/tmp/whatever.mkv",
new List<VideoStream>
{ new(0, VideoFormat.H264, new PixelFormatYuv420P(), new FrameSize(1920, 1080), "16:9", "24", false) });
{ new(0, VideoFormat.H264, new PixelFormatYuv420P(), new FrameSize(1920, 1080), "1:1", "16:9", "24", false) });
var audioInputFile = new AudioInputFile(
"/tmp/whatever.mkv",
@ -219,7 +219,7 @@ public class PipelineGeneratorTests @@ -219,7 +219,7 @@ public class PipelineGeneratorTests
new PixelFormatYuv420P(),
new FrameSize(1920, 1080),
new FrameSize(1920, 1080),
"16:9",
false,
Option<int>.None,
2000,
4000,
@ -273,7 +273,7 @@ public class PipelineGeneratorTests @@ -273,7 +273,7 @@ public class PipelineGeneratorTests
var videoInputFile = new VideoInputFile(
"/tmp/whatever.mkv",
new List<VideoStream>
{ new(0, VideoFormat.H264, new PixelFormatYuv420P(), new FrameSize(1920, 1080), "16:9", "24", false) });
{ new(0, VideoFormat.H264, new PixelFormatYuv420P(), new FrameSize(1920, 1080), "1:1", "16:9", "24", false) });
Option<AudioInputFile> audioInputFile = Option<AudioInputFile>.None;
@ -284,7 +284,7 @@ public class PipelineGeneratorTests @@ -284,7 +284,7 @@ public class PipelineGeneratorTests
new PixelFormatYuv420P(),
new FrameSize(1920, 1080),
new FrameSize(1920, 1080),
"16:9",
false,
Option<int>.None,
2000,
4000,
@ -341,7 +341,7 @@ public class PipelineGeneratorTests @@ -341,7 +341,7 @@ public class PipelineGeneratorTests
"/test/input/file.png",
new List<VideoStream>
{
new(0, string.Empty, Option<IPixelFormat>.None, FrameSize.Unknown, string.Empty, Option<string>.None, true)
new(0, string.Empty, Option<IPixelFormat>.None, FrameSize.Unknown, string.Empty, string.Empty, Option<string>.None, true)
});
var pipelineBuilder = new PipelineBuilder(
@ -360,7 +360,7 @@ public class PipelineGeneratorTests @@ -360,7 +360,7 @@ public class PipelineGeneratorTests
string command = PrintCommand(videoInputFile, None, None, None, result);
command.Should().Be(
"-nostdin -hide_banner -nostats -loglevel error -i /test/input/file.png -vf scale=-1:200 /test/output/file.jpg");
"-nostdin -hide_banner -nostats -loglevel error -i /test/input/file.png -vf scale=-1:200:force_original_aspect_ratio=decrease /test/output/file.jpg");
}
private static string PrintCommand(

2
ErsatzTV.FFmpeg/ErsatzTV.FFmpeg.csproj

@ -10,7 +10,7 @@ @@ -10,7 +10,7 @@
<PackageReference Include="CliWrap" Version="3.5.0" />
<PackageReference Include="LanguageExt.Core" Version="4.2.9" />
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.2" />
</ItemGroup>
<ItemGroup>

18
ErsatzTV.FFmpeg/Filter/AvailableScaleFilters.cs

@ -8,19 +8,27 @@ namespace ErsatzTV.FFmpeg.Filter; @@ -8,19 +8,27 @@ namespace ErsatzTV.FFmpeg.Filter;
public static class AvailableScaleFilters
{
public static IPipelineFilterStep ForAcceleration(
IRuntimeInfo runtimeInfo,
IRuntimeInfo _,
HardwareAccelerationMode accelMode,
FrameState currentState,
FrameSize scaledSize,
FrameSize paddedSize,
int extraHardwareFrames) =>
int extraHardwareFrames,
bool isAnamorphicEdgeCase,
string sampleAspectRatio) =>
accelMode switch
{
HardwareAccelerationMode.Nvenc => new ScaleCudaFilter(currentState, scaledSize, paddedSize),
HardwareAccelerationMode.Nvenc =>
new ScaleCudaFilter(currentState, scaledSize, paddedSize, isAnamorphicEdgeCase),
HardwareAccelerationMode.Qsv when currentState.FrameDataLocation == FrameDataLocation.Hardware ||
scaledSize == paddedSize =>
new ScaleQsvFilter(runtimeInfo, currentState, scaledSize, paddedSize, extraHardwareFrames),
new ScaleQsvFilter(
currentState,
scaledSize,
extraHardwareFrames,
isAnamorphicEdgeCase,
sampleAspectRatio),
HardwareAccelerationMode.Vaapi => new ScaleVaapiFilter(currentState, scaledSize, paddedSize),
_ => new ScaleFilter(currentState, scaledSize, paddedSize)
_ => new ScaleFilter(currentState, scaledSize, paddedSize, isAnamorphicEdgeCase)
};
}

4
ErsatzTV.FFmpeg/Filter/AvailableSubtitleOverlayFilters.cs

@ -5,11 +5,11 @@ namespace ErsatzTV.FFmpeg.Filter; @@ -5,11 +5,11 @@ namespace ErsatzTV.FFmpeg.Filter;
public static class AvailableSubtitleOverlayFilters
{
public static IPipelineFilterStep ForAcceleration(HardwareAccelerationMode accelMode, FrameState currentState) =>
public static IPipelineFilterStep ForAcceleration(HardwareAccelerationMode accelMode) =>
accelMode switch
{
HardwareAccelerationMode.Nvenc => new OverlaySubtitleCudaFilter(),
HardwareAccelerationMode.Qsv => new OverlaySubtitleQsvFilter(currentState),
HardwareAccelerationMode.Qsv => new OverlaySubtitleQsvFilter(),
_ => new OverlaySubtitleFilter()
};
}

14
ErsatzTV.FFmpeg/Filter/AvailableWatermarkOverlayFilters.cs

@ -1,6 +1,7 @@ @@ -1,6 +1,7 @@
using ErsatzTV.FFmpeg.Filter.Cuda;
using ErsatzTV.FFmpeg.Filter.Qsv;
using ErsatzTV.FFmpeg.State;
using Microsoft.Extensions.Logging;
namespace ErsatzTV.FFmpeg.Filter;
@ -8,13 +9,16 @@ public static class AvailableWatermarkOverlayFilters @@ -8,13 +9,16 @@ public static class AvailableWatermarkOverlayFilters
{
public static IPipelineFilterStep ForAcceleration(
HardwareAccelerationMode accelMode,
FrameState currentState,
WatermarkState watermarkState,
FrameSize resolution) =>
FrameSize resolution,
FrameSize squarePixelFrameSize,
ILogger logger) =>
accelMode switch
{
HardwareAccelerationMode.Nvenc => new OverlayWatermarkCudaFilter(currentState, watermarkState, resolution),
HardwareAccelerationMode.Qsv => new OverlayWatermarkQsvFilter(currentState, watermarkState, resolution),
_ => new OverlayWatermarkFilter(currentState, watermarkState, resolution)
HardwareAccelerationMode.Nvenc =>
new OverlayWatermarkCudaFilter(watermarkState, resolution, squarePixelFrameSize, logger),
HardwareAccelerationMode.Qsv =>
new OverlayWatermarkQsvFilter(watermarkState, resolution, squarePixelFrameSize, logger),
_ => new OverlayWatermarkFilter(watermarkState, resolution, squarePixelFrameSize, logger)
};
}

98
ErsatzTV.FFmpeg/Filter/ComplexFilter.cs

@ -1,5 +1,6 @@ @@ -1,5 +1,6 @@
using ErsatzTV.FFmpeg.Environment;
using ErsatzTV.FFmpeg.Format;
using Microsoft.Extensions.Logging;
namespace ErsatzTV.FFmpeg.Filter;
@ -8,6 +9,7 @@ public class ComplexFilter : IPipelineStep @@ -8,6 +9,7 @@ public class ComplexFilter : IPipelineStep
private readonly FrameState _currentState;
private readonly FFmpegState _ffmpegState;
private readonly string _fontsDir;
private readonly ILogger _logger;
private readonly Option<AudioInputFile> _maybeAudioInputFile;
private readonly Option<SubtitleInputFile> _maybeSubtitleInputFile;
private readonly Option<VideoInputFile> _maybeVideoInputFile;
@ -22,7 +24,8 @@ public class ComplexFilter : IPipelineStep @@ -22,7 +24,8 @@ public class ComplexFilter : IPipelineStep
Option<WatermarkInputFile> maybeWatermarkInputFile,
Option<SubtitleInputFile> maybeSubtitleInputFile,
FrameSize resolution,
string fontsDir)
string fontsDir,
ILogger logger)
{
_currentState = currentState;
_ffmpegState = ffmpegState;
@ -32,6 +35,7 @@ public class ComplexFilter : IPipelineStep @@ -32,6 +35,7 @@ public class ComplexFilter : IPipelineStep
_maybeSubtitleInputFile = maybeSubtitleInputFile;
_resolution = resolution;
_fontsDir = fontsDir;
_logger = logger;
}
public IList<EnvironmentVariable> EnvironmentVariables => Array.Empty<EnvironmentVariable>();
@ -147,48 +151,56 @@ public class ComplexFilter : IPipelineStep @@ -147,48 +151,56 @@ public class ComplexFilter : IPipelineStep
watermarkLabel = $"[{watermarkLabel}]";
}
IPipelineFilterStep overlayFilter = AvailableWatermarkOverlayFilters.ForAcceleration(
_ffmpegState.EncoderHardwareAccelerationMode,
_currentState,
watermarkInputFile.DesiredState,
_resolution);
if (overlayFilter.Filter != string.Empty)
foreach (VideoInputFile videoInputFile in _maybeVideoInputFile)
{
string tempVideoLabel = string.IsNullOrWhiteSpace(videoFilterComplex)
? $"[{videoLabel}]"
: videoLabel;
// vaapi uses software overlay and needs to upload
// videotoolbox seems to require a hwupload for hevc
// also wait to upload if a subtitle overlay is coming
string uploadDownloadFilter = string.Empty;
if (_maybeSubtitleInputFile.IsNone &&
(_ffmpegState.EncoderHardwareAccelerationMode == HardwareAccelerationMode.Vaapi ||
_ffmpegState.EncoderHardwareAccelerationMode == HardwareAccelerationMode.VideoToolbox &&
_currentState.VideoFormat == VideoFormat.Hevc))
foreach (VideoStream stream in videoInputFile.VideoStreams)
{
uploadDownloadFilter = new HardwareUploadFilter(_ffmpegState).Filter;
}
if (_maybeSubtitleInputFile.Map(s => !s.IsImageBased).IfNone(false) &&
_ffmpegState.EncoderHardwareAccelerationMode != HardwareAccelerationMode.Vaapi &&
_ffmpegState.EncoderHardwareAccelerationMode != HardwareAccelerationMode.VideoToolbox &&
_ffmpegState.EncoderHardwareAccelerationMode != HardwareAccelerationMode.Amf)
{
uploadDownloadFilter = new HardwareDownloadFilter(_currentState).Filter;
}
if (!string.IsNullOrWhiteSpace(uploadDownloadFilter))
{
uploadDownloadFilter = "," + uploadDownloadFilter;
IPipelineFilterStep overlayFilter = AvailableWatermarkOverlayFilters.ForAcceleration(
_ffmpegState.EncoderHardwareAccelerationMode,
watermarkInputFile.DesiredState,
_resolution,
stream.SquarePixelFrameSize(_resolution),
_logger);
if (overlayFilter.Filter != string.Empty)
{
string tempVideoLabel = string.IsNullOrWhiteSpace(videoFilterComplex)
? $"[{videoLabel}]"
: videoLabel;
// vaapi uses software overlay and needs to upload
// videotoolbox seems to require a hwupload for hevc
// also wait to upload if a subtitle overlay is coming
string uploadDownloadFilter = string.Empty;
if (_maybeSubtitleInputFile.IsNone &&
(_ffmpegState.EncoderHardwareAccelerationMode == HardwareAccelerationMode.Vaapi ||
_ffmpegState.EncoderHardwareAccelerationMode ==
HardwareAccelerationMode.VideoToolbox &&
_currentState.VideoFormat == VideoFormat.Hevc))
{
uploadDownloadFilter = new HardwareUploadFilter(_ffmpegState).Filter;
}
if (_maybeSubtitleInputFile.Map(s => !s.IsImageBased).IfNone(false) &&
_ffmpegState.EncoderHardwareAccelerationMode != HardwareAccelerationMode.Vaapi &&
_ffmpegState.EncoderHardwareAccelerationMode != HardwareAccelerationMode.VideoToolbox &&
_ffmpegState.EncoderHardwareAccelerationMode != HardwareAccelerationMode.Amf)
{
uploadDownloadFilter = new HardwareDownloadFilter(_currentState).Filter;
}
if (!string.IsNullOrWhiteSpace(uploadDownloadFilter))
{
uploadDownloadFilter = "," + uploadDownloadFilter;
}
watermarkOverlayFilterComplex =
$"{tempVideoLabel}{watermarkLabel}{overlayFilter.Filter}{uploadDownloadFilter}[vf]";
// change the mapped label
videoLabel = "[vf]";
}
}
watermarkOverlayFilterComplex =
$"{tempVideoLabel}{watermarkLabel}{overlayFilter.Filter}{uploadDownloadFilter}[vf]";
// change the mapped label
videoLabel = "[vf]";
}
}
}
@ -216,10 +228,8 @@ public class ComplexFilter : IPipelineStep @@ -216,10 +228,8 @@ public class ComplexFilter : IPipelineStep
string filter;
if (subtitleInputFile.IsImageBased)
{
IPipelineFilterStep overlayFilter =
AvailableSubtitleOverlayFilters.ForAcceleration(
_ffmpegState.EncoderHardwareAccelerationMode,
_currentState);
IPipelineFilterStep overlayFilter = AvailableSubtitleOverlayFilters.ForAcceleration(
_ffmpegState.EncoderHardwareAccelerationMode);
filter = overlayFilter.Filter;
}
else

2
ErsatzTV.FFmpeg/Filter/Cuda/OverlaySubtitleCudaFilter.cs

@ -2,6 +2,6 @@ @@ -2,6 +2,6 @@
public class OverlaySubtitleCudaFilter : BaseFilter
{
public override string Filter => "overlay_cuda";
public override string Filter => "overlay_cuda=x=(W-w)/2:y=(H-h)/2";
public override FrameState NextState(FrameState currentState) => currentState;
}

11
ErsatzTV.FFmpeg/Filter/Cuda/OverlayWatermarkCudaFilter.cs

@ -1,16 +1,19 @@ @@ -1,16 +1,19 @@
using ErsatzTV.FFmpeg.State;
using Microsoft.Extensions.Logging;
namespace ErsatzTV.FFmpeg.Filter.Cuda;
public class OverlayWatermarkCudaFilter : OverlayWatermarkFilter
{
public OverlayWatermarkCudaFilter(
FrameState currentState,
WatermarkState watermarkState,
FrameSize resolution) : base(
currentState,
FrameSize resolution,
FrameSize squarePixelFrameSize,
ILogger logger) : base(
watermarkState,
resolution)
resolution,
squarePixelFrameSize,
logger)
{
}

33
ErsatzTV.FFmpeg/Filter/Cuda/ScaleCudaFilter.cs

@ -6,13 +6,19 @@ public class ScaleCudaFilter : BaseFilter @@ -6,13 +6,19 @@ public class ScaleCudaFilter : BaseFilter
{
private readonly FrameState _currentState;
private readonly FrameSize _paddedSize;
private readonly bool _isAnamorphicEdgeCase;
private readonly FrameSize _scaledSize;
public ScaleCudaFilter(FrameState currentState, FrameSize scaledSize, FrameSize paddedSize)
public ScaleCudaFilter(
FrameState currentState,
FrameSize scaledSize,
FrameSize paddedSize,
bool isAnamorphicEdgeCase)
{
_currentState = currentState;
_scaledSize = scaledSize;
_paddedSize = paddedSize;
_isAnamorphicEdgeCase = isAnamorphicEdgeCase;
}
public override string Filter
@ -30,6 +36,13 @@ public class ScaleCudaFilter : BaseFilter @@ -30,6 +36,13 @@ public class ScaleCudaFilter : 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)
@ -37,7 +50,20 @@ public class ScaleCudaFilter : BaseFilter @@ -37,7 +50,20 @@ public class ScaleCudaFilter : BaseFilter
format = $":format={pixelFormat.FFmpegName}";
}
scale = $"scale_cuda={targetSize}{format}";
if (_isAnamorphicEdgeCase)
{
squareScale = $"scale_cuda=iw:sar*ih{format},setsar=1,";
}
else if (_currentState.IsAnamorphic)
{
squareScale = $"scale_cuda=iw*sar:ih{format},setsar=1,";
}
else
{
aspectRatio += ",setsar=1";
}
scale = $"{squareScale}scale_cuda={targetSize}{format}{aspectRatio}";
}
// TODO: this might not always upload to hardware, so NextState could be inaccurate
@ -56,6 +82,7 @@ public class ScaleCudaFilter : BaseFilter @@ -56,6 +82,7 @@ public class ScaleCudaFilter : BaseFilter
{
ScaledSize = _scaledSize,
PaddedSize = _scaledSize,
FrameDataLocation = FrameDataLocation.Hardware
FrameDataLocation = FrameDataLocation.Hardware,
IsAnamorphic = false // this filter always outputs square pixels
};
}

2
ErsatzTV.FFmpeg/Filter/Cuda/SubtitleScaleNppFilter.cs

@ -29,7 +29,7 @@ public class SubtitleScaleNppFilter : BaseFilter @@ -29,7 +29,7 @@ public class SubtitleScaleNppFilter : BaseFilter
format = $":format={pixelFormat.FFmpegName}";
}
scale = $"scale_npp={targetSize}{format}";
scale = $"scale_npp={targetSize}{format}:force_original_aspect_ratio=1";
}
return scale;

2
ErsatzTV.FFmpeg/Filter/OverlaySubtitleFilter.cs

@ -2,6 +2,6 @@ @@ -2,6 +2,6 @@
public class OverlaySubtitleFilter : BaseFilter
{
public override string Filter => "overlay";
public override string Filter => "overlay=x=(W-w)/2:y=(H-h)/2";
public override FrameState NextState(FrameState currentState) => currentState;
}

29
ErsatzTV.FFmpeg/Filter/OverlayWatermarkFilter.cs

@ -1,18 +1,25 @@ @@ -1,18 +1,25 @@
using ErsatzTV.FFmpeg.State;
using Microsoft.Extensions.Logging;
namespace ErsatzTV.FFmpeg.Filter;
public class OverlayWatermarkFilter : BaseFilter
{
private readonly FrameState _currentState;
private readonly FrameSize _resolution;
private readonly FrameSize _squarePixelFrameSize;
private readonly ILogger _logger;
private readonly WatermarkState _watermarkState;
public OverlayWatermarkFilter(FrameState currentState, WatermarkState watermarkState, FrameSize resolution)
public OverlayWatermarkFilter(
WatermarkState watermarkState,
FrameSize resolution,
FrameSize squarePixelFrameSize,
ILogger logger)
{
_currentState = currentState;
_watermarkState = watermarkState;
_resolution = resolution;
_squarePixelFrameSize = squarePixelFrameSize;
_logger = logger;
}
public override string Filter => $"overlay={Position}";
@ -21,8 +28,20 @@ public class OverlayWatermarkFilter : BaseFilter @@ -21,8 +28,20 @@ public class OverlayWatermarkFilter : BaseFilter
{
get
{
double horizontalMargin = Math.Round(_watermarkState.HorizontalMarginPercent / 100.0 * _resolution.Width);
double verticalMargin = Math.Round(_watermarkState.VerticalMarginPercent / 100.0 * _resolution.Height);
int horizontalPadding = _resolution.Width - _squarePixelFrameSize.Width;
int verticalPadding = _resolution.Height - _squarePixelFrameSize.Height;
_logger.LogDebug(
$"Resolution: {_resolution.Width}x{_resolution.Height}");
_logger.LogDebug(
$"Square Pix: {_squarePixelFrameSize.Width}x{_squarePixelFrameSize.Height}");
double horizontalMargin = Math.Round(
_watermarkState.HorizontalMarginPercent / 100.0 * _squarePixelFrameSize.Width
+ horizontalPadding / 2.0);
double verticalMargin = Math.Round(
_watermarkState.VerticalMarginPercent / 100.0 * _squarePixelFrameSize.Height
+ verticalPadding / 2.0);
return _watermarkState.Location switch
{

6
ErsatzTV.FFmpeg/Filter/Qsv/OverlaySubtitleQsvFilter.cs

@ -2,12 +2,8 @@ @@ -2,12 +2,8 @@
public class OverlaySubtitleQsvFilter : BaseFilter
{
private readonly FrameState _currentState;
public OverlaySubtitleQsvFilter(FrameState currentState) => _currentState = currentState;
public override string Filter =>
$"overlay_qsv=eof_action=endall:shortest=1:repeatlast=0:w={_currentState.PaddedSize.Width}:h={_currentState.PaddedSize.Height}";
$"overlay_qsv=eof_action=endall:shortest=1:repeatlast=0:x=(W-w)/2:y=(H-h)/2";
public override FrameState NextState(FrameState currentState) => currentState;
}

11
ErsatzTV.FFmpeg/Filter/Qsv/OverlayWatermarkQsvFilter.cs

@ -1,16 +1,19 @@ @@ -1,16 +1,19 @@
using ErsatzTV.FFmpeg.State;
using Microsoft.Extensions.Logging;
namespace ErsatzTV.FFmpeg.Filter.Qsv;
public class OverlayWatermarkQsvFilter : OverlayWatermarkFilter
{
public OverlayWatermarkQsvFilter(
FrameState currentState,
WatermarkState watermarkState,
FrameSize resolution) : base(
currentState,
FrameSize resolution,
FrameSize squarePixelFrameSize,
ILogger logger) : base(
watermarkState,
resolution)
resolution,
squarePixelFrameSize,
logger)
{
}

42
ErsatzTV.FFmpeg/Filter/Qsv/ScaleQsvFilter.cs

@ -1,29 +1,27 @@ @@ -1,29 +1,27 @@
using System.Runtime.InteropServices;
using ErsatzTV.FFmpeg.Format;
using ErsatzTV.FFmpeg.Runtime;
using ErsatzTV.FFmpeg.Format;
namespace ErsatzTV.FFmpeg.Filter.Qsv;
public class ScaleQsvFilter : BaseFilter
{
private readonly IRuntimeInfo _runtimeInfo;
private readonly FrameState _currentState;
private readonly FrameSize _scaledSize;
private readonly FrameSize _paddedSize;
private readonly int _extraHardwareFrames;
private readonly bool _isAnamorphicEdgeCase;
private readonly string _sampleAspectRatio;
public ScaleQsvFilter(
IRuntimeInfo runtimeInfo,
FrameState currentState,
FrameSize scaledSize,
FrameSize paddedSize,
int extraHardwareFrames)
int extraHardwareFrames,
bool isAnamorphicEdgeCase,
string sampleAspectRatio)
{
_runtimeInfo = runtimeInfo;
_currentState = currentState;
_scaledSize = scaledSize;
_paddedSize = paddedSize;
_extraHardwareFrames = extraHardwareFrames;
_isAnamorphicEdgeCase = isAnamorphicEdgeCase;
_sampleAspectRatio = sampleAspectRatio;
}
public override string Filter
@ -44,16 +42,29 @@ public class ScaleQsvFilter : BaseFilter @@ -44,16 +42,29 @@ public class ScaleQsvFilter : BaseFilter
}
else
{
string squareScale = string.Empty;
string targetSize = $"w={_scaledSize.Width}:h={_scaledSize.Height}";
string format = string.Empty;
foreach (IPixelFormat pixelFormat in _currentState.PixelFormat)
{
format = $":format={pixelFormat.FFmpegName}";
}
string targetSize = _runtimeInfo.IsOSPlatform(OSPlatform.Windows)
? $"w={_paddedSize.Width}:h={_paddedSize.Height}"
: $"w={_scaledSize.Width}:h={_scaledSize.Height}";
scale = $"vpp_qsv={targetSize}{format}";
string sar = _sampleAspectRatio.Replace(':', '/');
if (_isAnamorphicEdgeCase)
{
squareScale = $"vpp_qsv=w=iw:h={sar}*ih{format},setsar=1,";
}
else if (_currentState.IsAnamorphic)
{
squareScale = $"vpp_qsv=w=iw*{sar}:h=ih{format},setsar=1,";
}
else
{
format += ",setsar=1";
}
scale = $"{squareScale}vpp_qsv={targetSize}{format}";
}
if (_currentState.FrameDataLocation == FrameDataLocation.Hardware)
@ -75,6 +86,7 @@ public class ScaleQsvFilter : BaseFilter @@ -75,6 +86,7 @@ public class ScaleQsvFilter : BaseFilter
{
ScaledSize = _scaledSize,
PaddedSize = _scaledSize,
FrameDataLocation = FrameDataLocation.Hardware
FrameDataLocation = FrameDataLocation.Hardware,
IsAnamorphic = false // this filter always outputs square pixels
};
}

2
ErsatzTV.FFmpeg/Filter/Qsv/SubtitleScaleQsvFilter.cs

@ -31,7 +31,7 @@ public class SubtitleScaleQsvFilter : BaseFilter @@ -31,7 +31,7 @@ public class SubtitleScaleQsvFilter : BaseFilter
string targetSize = $"w={_paddedSize.Width}:h={_paddedSize.Height}";
// use software scaling
scale = $"scale={targetSize}";
scale = $"scale={targetSize}:force_original_aspect_ratio=decrease";
}
string initialPixelFormat = _currentState.PixelFormat.Match(pf => pf.FFmpegName, FFmpegFormat.NV12);

33
ErsatzTV.FFmpeg/Filter/ScaleFilter.cs

@ -6,13 +6,15 @@ public class ScaleFilter : BaseFilter @@ -6,13 +6,15 @@ public class ScaleFilter : BaseFilter
{
private readonly FrameState _currentState;
private readonly FrameSize _paddedSize;
private readonly bool _isAnamorphicEdgeCase;
private readonly FrameSize _scaledSize;
public ScaleFilter(FrameState currentState, FrameSize scaledSize, FrameSize paddedSize)
public ScaleFilter(FrameState currentState, FrameSize scaledSize, FrameSize paddedSize, bool isAnamorphicEdgeCase)
{
_currentState = currentState;
_scaledSize = scaledSize;
_paddedSize = paddedSize;
_isAnamorphicEdgeCase = isAnamorphicEdgeCase;
}
public override string Filter
@ -25,8 +27,19 @@ public class ScaleFilter : BaseFilter @@ -25,8 +27,19 @@ public class ScaleFilter : BaseFilter
aspectRatio = ":force_original_aspect_ratio=decrease";
}
string scale =
$"scale={_paddedSize.Width}:{_paddedSize.Height}:flags=fast_bilinear{aspectRatio}";
string scale;
if (_isAnamorphicEdgeCase)
{
scale = $"scale=iw:sar*ih,setsar=1,scale={_paddedSize.Width}:{_paddedSize.Height}:flags=fast_bilinear{aspectRatio}";
}
else if (_currentState.IsAnamorphic)
{
scale = $"scale=iw*sar:ih,setsar=1,scale={_paddedSize.Width}:{_paddedSize.Height}:flags=fast_bilinear{aspectRatio}";
}
else
{
scale = $"scale={_paddedSize.Width}:{_paddedSize.Height}:flags=fast_bilinear{aspectRatio},setsar=1";
}
string hwdownload = string.Empty;
if (_currentState.FrameDataLocation == FrameDataLocation.Hardware)
@ -45,10 +58,12 @@ public class ScaleFilter : BaseFilter @@ -45,10 +58,12 @@ public class ScaleFilter : BaseFilter
}
}
public override FrameState NextState(FrameState currentState) => currentState with
{
ScaledSize = _scaledSize,
PaddedSize = _scaledSize,
FrameDataLocation = FrameDataLocation.Software
};
public override FrameState NextState(FrameState currentState) =>
currentState with
{
ScaledSize = _scaledSize,
PaddedSize = _scaledSize,
FrameDataLocation = FrameDataLocation.Software,
IsAnamorphic = false // this filter always outputs square pixels
};
}

4
ErsatzTV.FFmpeg/Filter/ScaleImageFilter.cs

@ -6,9 +6,7 @@ public class ScaleImageFilter : BaseFilter @@ -6,9 +6,7 @@ public class ScaleImageFilter : BaseFilter
public ScaleImageFilter(FrameSize scaledSize) => _scaledSize = scaledSize;
public override string Filter => $"scale={_scaledSize.Width}:{_scaledSize.Height}";
// public override IList<string> OutputOptions => new List<string> { "-q:v", "2" };
public override string Filter => $"scale={_scaledSize.Width}:{_scaledSize.Height}:force_original_aspect_ratio=decrease";
public override FrameState NextState(FrameState currentState) => currentState with
{

7
ErsatzTV.FFmpeg/FrameState.cs

@ -9,10 +9,13 @@ public record FrameState( @@ -9,10 +9,13 @@ public record FrameState(
Option<IPixelFormat> PixelFormat,
FrameSize ScaledSize,
FrameSize PaddedSize,
string DisplayAspectRatio,
bool IsAnamorphic,
Option<int> FrameRate,
Option<int> VideoBitrate,
Option<int> VideoBufferSize,
Option<int> VideoTrackTimeScale,
bool Deinterlaced,
FrameDataLocation FrameDataLocation = FrameDataLocation.Unknown);
FrameDataLocation FrameDataLocation = FrameDataLocation.Unknown)
{
public string FFmpegAspectRatio => PaddedSize.Width == 640 ? "4/3" : "16/9";
}

1
ErsatzTV.FFmpeg/InputFile.cs

@ -20,6 +20,7 @@ public record ConcatInputFile(string Url, FrameSize Resolution) : InputFile( @@ -20,6 +20,7 @@ public record ConcatInputFile(string Url, FrameSize Resolution) : InputFile(
Option<IPixelFormat>.None,
Resolution,
string.Empty,
string.Empty,
Option<string>.None,
false)
})

94
ErsatzTV.FFmpeg/MediaStream.cs

@ -1,4 +1,5 @@ @@ -1,4 +1,5 @@
using ErsatzTV.FFmpeg.Format;
using System.ComponentModel.DataAnnotations;
using ErsatzTV.FFmpeg.Format;
namespace ErsatzTV.FFmpeg;
@ -14,9 +15,98 @@ public record VideoStream( @@ -14,9 +15,98 @@ public record VideoStream(
string Codec,
Option<IPixelFormat> PixelFormat,
FrameSize FrameSize,
string SampleAspectRatio,
string DisplayAspectRatio,
Option<string> FrameRate,
bool StillImage) : MediaStream(
Index,
Codec,
StreamKind.Video);
StreamKind.Video)
{
public bool IsAnamorphic
{
get
{
// square pixels
if (SampleAspectRatio == "1:1")
{
return false;
}
// 0:1 is "unspecified", so anything other than that will be non-square/anamorphic
if (SampleAspectRatio != "0:1")
{
return true;
}
// SAR 0:1 && DAR 0:1 (both unspecified) means square
if (DisplayAspectRatio == "0:1")
{
return false;
}
// DAR == W:H is square
return DisplayAspectRatio != $"{FrameSize.Width}:{FrameSize.Height}";
}
}
// TODO: figure out what's really causing this
public bool IsAnamorphicEdgeCase
{
get
{
try
{
string[] split = SampleAspectRatio.Split(':');
var num = double.Parse(split[0]);
var den = double.Parse(split[1]);
if (num <= den)
{
return false;
}
double sar = num / den;
double res = (double)FrameSize.Width / FrameSize.Height;
return res - sar > 0.01;
}
catch
{
return false;
}
}
}
public FrameSize SquarePixelFrameSize(FrameSize resolution)
{
int width = FrameSize.Width;
int height = FrameSize.Height;
if (IsAnamorphic)
{
string[] split = SampleAspectRatio.Split(':');
var num = double.Parse(split[0]);
var den = double.Parse(split[1]);
bool edgeCase = IsAnamorphicEdgeCase;
width = edgeCase
? FrameSize.Width
: (int)Math.Floor(FrameSize.Width * num / den);
height = edgeCase
? (int)Math.Floor(FrameSize.Height * num / den)
: FrameSize.Height;
}
double widthPercent = (double)resolution.Width / width;
double heightPercent = (double)resolution.Height / height;
double minPercent = Math.Min(widthPercent, heightPercent);
var result = new FrameSize(
(int)Math.Floor(width * minPercent),
(int)Math.Floor(height * minPercent));
return result;
}
}

68
ErsatzTV.FFmpeg/PipelineBuilder.cs

@ -192,7 +192,7 @@ public class PipelineBuilder @@ -192,7 +192,7 @@ public class PipelineBuilder
videoStream.PixelFormat,
videoStream.FrameSize,
videoStream.FrameSize,
videoStream.DisplayAspectRatio,
videoStream.IsAnamorphic,
initialFrameRate,
Option<int>.None,
Option<int>.None,
@ -376,7 +376,8 @@ public class PipelineBuilder @@ -376,7 +376,8 @@ public class PipelineBuilder
IPipelineFilterStep scaleStep = new ScaleFilter(
currentState,
desiredState.ScaledSize,
desiredState.PaddedSize);
desiredState.PaddedSize,
videoStream.IsAnamorphicEdgeCase);
currentState = scaleStep.NextState(currentState);
_videoInputFile.Iter(f => f.FilterSteps.Add(scaleStep));
@ -385,12 +386,12 @@ public class PipelineBuilder @@ -385,12 +386,12 @@ public class PipelineBuilder
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));
}
// 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)
@ -401,7 +402,9 @@ public class PipelineBuilder @@ -401,7 +402,9 @@ public class PipelineBuilder
currentState,
desiredState.ScaledSize,
desiredState.PaddedSize,
ffmpegState.QsvExtraHardwareFrames);
ffmpegState.QsvExtraHardwareFrames,
videoStream.IsAnamorphicEdgeCase,
videoStream.SampleAspectRatio);
currentState = scaleFilter.NextState(currentState);
_videoInputFile.Iter(f => f.FilterSteps.Add(scaleFilter));
@ -413,13 +416,13 @@ public class PipelineBuilder @@ -413,13 +416,13 @@ public class PipelineBuilder
_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 (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)
{
@ -429,7 +432,9 @@ public class PipelineBuilder @@ -429,7 +432,9 @@ public class PipelineBuilder
currentState,
desiredState.ScaledSize,
desiredState.PaddedSize,
ffmpegState.QsvExtraHardwareFrames);
ffmpegState.QsvExtraHardwareFrames,
videoStream.IsAnamorphicEdgeCase,
videoStream.SampleAspectRatio);
currentState = scaleFilter.NextState(currentState);
_videoInputFile.Iter(f => f.FilterSteps.Add(scaleFilter));
@ -440,13 +445,13 @@ public class PipelineBuilder @@ -440,13 +445,13 @@ public class PipelineBuilder
_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 (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) !=
@ -485,7 +490,9 @@ public class PipelineBuilder @@ -485,7 +490,9 @@ public class PipelineBuilder
currentState,
desiredState.ScaledSize,
desiredState.PaddedSize,
ffmpegState.QsvExtraHardwareFrames);
ffmpegState.QsvExtraHardwareFrames,
videoStream.IsAnamorphicEdgeCase,
videoStream.SampleAspectRatio);
currentState = scaleFilter.NextState(currentState);
_videoInputFile.Iter(f => f.FilterSteps.Add(scaleFilter));
}
@ -516,7 +523,9 @@ public class PipelineBuilder @@ -516,7 +523,9 @@ public class PipelineBuilder
currentState,
desiredState.ScaledSize,
desiredState.PaddedSize,
ffmpegState.QsvExtraHardwareFrames);
ffmpegState.QsvExtraHardwareFrames,
videoStream.IsAnamorphicEdgeCase,
videoStream.SampleAspectRatio);
currentState = scaleFilter.NextState(currentState);
videoInputFile.FilterSteps.Add(scaleFilter);
}
@ -670,7 +679,9 @@ public class PipelineBuilder @@ -670,7 +679,9 @@ public class PipelineBuilder
currentState,
desiredState.ScaledSize,
desiredState.PaddedSize,
ffmpegState.QsvExtraHardwareFrames);
ffmpegState.QsvExtraHardwareFrames,
videoStream.IsAnamorphicEdgeCase,
videoStream.SampleAspectRatio);
currentState = scaleFilter.NextState(currentState);
_videoInputFile.Iter(f => f.FilterSteps.Add(scaleFilter));
}
@ -800,7 +811,8 @@ public class PipelineBuilder @@ -800,7 +811,8 @@ public class PipelineBuilder
_watermarkInputFile,
_subtitleInputFile,
currentState.PaddedSize,
_fontsFolder);
_fontsFolder,
_logger);
_pipelineSteps.Add(complexFilter);
}

6
ErsatzTV.Infrastructure/ErsatzTV.Infrastructure.csproj

@ -14,12 +14,12 @@ @@ -14,12 +14,12 @@
<PackageReference Include="Lucene.Net" Version="4.8.0-beta00016" />
<PackageReference Include="Lucene.Net.Analysis.Common" Version="4.8.0-beta00016" />
<PackageReference Include="Lucene.Net.QueryParser" Version="4.8.0-beta00016" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.8">
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.9">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.9" />
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.3.44">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

14
ErsatzTV/ErsatzTV.csproj

@ -55,16 +55,16 @@ @@ -55,16 +55,16 @@
<ItemGroup>
<PackageReference Include="Bugsnag.AspNet.Core" Version="3.1.0" />
<PackageReference Include="FluentValidation" Version="11.2.1" />
<PackageReference Include="FluentValidation" Version="11.2.2" />
<PackageReference Include="FluentValidation.AspNetCore" Version="11.2.2" />
<PackageReference Include="HtmlSanitizer" Version="7.1.542" />
<PackageReference Include="LanguageExt.Core" Version="4.2.9" />
<PackageReference Include="Markdig" Version="0.30.3" />
<PackageReference Include="MediatR.Courier.DependencyInjection" Version="5.0.0" />
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="10.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.8" />
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="6.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.8">
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.9" />
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="6.0.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.9">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
@ -73,12 +73,12 @@ @@ -73,12 +73,12 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="MudBlazor" Version="6.0.15" />
<PackageReference Include="NaturalSort.Extension" Version="3.2.0" />
<PackageReference Include="NaturalSort.Extension" Version="4.0.0" />
<PackageReference Include="PPioli.FluentValidation.Blazor" Version="11.1.0" />
<PackageReference Include="Refit.HttpClientFactory" Version="6.3.2" />
<PackageReference Include="Serilog" Version="2.11.0" />
<PackageReference Include="Serilog" Version="2.12.0" />
<PackageReference Include="Serilog.AspNetCore" Version="6.0.1" />
<PackageReference Include="Serilog.Settings.Configuration" Version="3.3.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="3.4.0" />
<PackageReference Include="Serilog.Sinks.SQLite" Version="6.0.1" />
<PackageReference Include="System.IO.FileSystem.Primitives" Version="4.3.0" />
<PackageReference Include="System.Text.Encoding.Extensions" Version="4.3.0" />

2
ErsatzTV/Program.cs

@ -64,7 +64,7 @@ public class Program @@ -64,7 +64,7 @@ public class Program
}
finally
{
Log.CloseAndFlush();
await Log.CloseAndFlushAsync();
}
}

Loading…
Cancel
Save