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
This commit is contained in:
Jason Dove
2022-09-18 11:04:02 -05:00
committed by GitHub
parent 7f4004c228
commit 357dfee050
29 changed files with 377 additions and 171 deletions
+2
View File
@@ -6,6 +6,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [Unreleased] ## [Unreleased]
### Fixed ### Fixed
- Fix typo introduced in `0.6.7-beta` that stopped QSV HEVC encoder from working - 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 ## [0.6.7-beta] - 2022-09-05
### Fixed ### Fixed
@@ -14,7 +14,7 @@
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="6.0.1" /> <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" 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.Extensions.Logging.Debug" Version="6.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.1" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.1" />
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.3.44"> <PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.3.44">
@@ -24,7 +24,7 @@
<PackageReference Include="Moq" Version="4.18.2" /> <PackageReference Include="Moq" Version="4.18.2" />
<PackageReference Include="NUnit" Version="3.13.3" /> <PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" /> <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.Extensions.Logging" Version="3.1.0" />
<PackageReference Include="Serilog.Sinks.Debug" Version="2.0.0" /> <PackageReference Include="Serilog.Sinks.Debug" Version="2.0.0" />
</ItemGroup> </ItemGroup>
+2 -2
View File
@@ -16,14 +16,14 @@
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="6.0.0" /> <PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.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.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.IO.RecyclableMemoryStream" Version="2.2.1" />
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.3.44"> <PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.3.44">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> <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" /> <PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />
</ItemGroup> </ItemGroup>
@@ -147,6 +147,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
videoStream.Codec, videoStream.Codec,
AvailablePixelFormats.ForPixelFormat(videoStream.PixelFormat, _logger), AvailablePixelFormats.ForPixelFormat(videoStream.PixelFormat, _logger),
new FrameSize(videoVersion.Width, videoVersion.Height), new FrameSize(videoVersion.Width, videoVersion.Height),
videoVersion.SampleAspectRatio,
videoVersion.DisplayAspectRatio, videoVersion.DisplayAspectRatio,
videoVersion.RFrameRate, videoVersion.RFrameRate,
videoPath != audioPath); // still image when paths are different videoPath != audioPath); // still image when paths are different
@@ -214,10 +215,10 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
false, // TODO: fallback filler needs to loop false, // TODO: fallback filler needs to loop
videoFormat, videoFormat,
desiredPixelFormat, desiredPixelFormat,
await playbackSettings.ScaledSize.Map(ss => new FrameSize(ss.Width, ss.Height)) ffmpegVideoStream.SquarePixelFrameSize(
.IfNoneAsync(new FrameSize(videoVersion.Width, videoVersion.Height)), new FrameSize(channel.FFmpegProfile.Resolution.Width, channel.FFmpegProfile.Resolution.Height)),
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.FrameRate,
playbackSettings.VideoBitrate, playbackSettings.VideoBitrate,
playbackSettings.VideoBufferSize, playbackSettings.VideoBufferSize,
@@ -314,7 +315,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
new PixelFormatYuv420P(), new PixelFormatYuv420P(),
new FrameSize(desiredResolution.Width, desiredResolution.Height), new FrameSize(desiredResolution.Width, desiredResolution.Height),
new FrameSize(desiredResolution.Width, desiredResolution.Height), new FrameSize(desiredResolution.Width, desiredResolution.Height),
desiredResolution.Width == 640 ? "4:3" : "16:9", false,
playbackSettings.FrameRate, playbackSettings.FrameRate,
playbackSettings.VideoBitrate, playbackSettings.VideoBitrate,
playbackSettings.VideoBufferSize, playbackSettings.VideoBufferSize,
@@ -342,6 +343,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
VideoFormat.GeneratedImage, VideoFormat.GeneratedImage,
new PixelFormatUnknown(), // leave this unknown so we convert to desired yuv420p new PixelFormatUnknown(), // leave this unknown so we convert to desired yuv420p
new FrameSize(videoVersion.Width, videoVersion.Height), new FrameSize(videoVersion.Width, videoVersion.Height),
videoVersion.SampleAspectRatio,
videoVersion.DisplayAspectRatio, videoVersion.DisplayAspectRatio,
None, None,
true); true);
@@ -434,7 +436,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
{ {
var videoInputFile = new VideoInputFile( var videoInputFile = new VideoInputFile(
inputFile, 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( var pipelineBuilder = new PipelineBuilder(
_runtimeInfo, _runtimeInfo,
@@ -516,6 +518,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
new PixelFormatUnknown(), new PixelFormatUnknown(),
new FrameSize(1, 1), new FrameSize(1, 1),
string.Empty, string.Empty,
string.Empty,
Option<string>.None, Option<string>.None,
!options.IsAnimated) !options.IsAnimated)
}, },
+10 -10
View File
@@ -26,7 +26,7 @@ public class PipelineGeneratorTests
var videoInputFile = new VideoInputFile( var videoInputFile = new VideoInputFile(
"/tmp/whatever.mkv", "/tmp/whatever.mkv",
new List<VideoStream> 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( var audioInputFile = new AudioInputFile(
"/tmp/whatever.mkv", "/tmp/whatever.mkv",
@@ -47,7 +47,7 @@ public class PipelineGeneratorTests
new PixelFormatYuv420P(), new PixelFormatYuv420P(),
new FrameSize(1920, 1080), new FrameSize(1920, 1080),
new FrameSize(1920, 1080), new FrameSize(1920, 1080),
"16:9", false,
Option<int>.None, Option<int>.None,
2000, 2000,
4000, 4000,
@@ -99,7 +99,7 @@ public class PipelineGeneratorTests
var videoInputFile = new VideoInputFile( var videoInputFile = new VideoInputFile(
"/tmp/whatever.mkv", "/tmp/whatever.mkv",
new List<VideoStream> 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( var audioInputFile = new AudioInputFile(
"/tmp/whatever.mkv", "/tmp/whatever.mkv",
@@ -120,7 +120,7 @@ public class PipelineGeneratorTests
new PixelFormatYuv420P(), new PixelFormatYuv420P(),
new FrameSize(1920, 1080), new FrameSize(1920, 1080),
new FrameSize(1920, 1080), new FrameSize(1920, 1080),
"16:9", false,
Option<int>.None, Option<int>.None,
2000, 2000,
4000, 4000,
@@ -198,7 +198,7 @@ public class PipelineGeneratorTests
var videoInputFile = new VideoInputFile( var videoInputFile = new VideoInputFile(
"/tmp/whatever.mkv", "/tmp/whatever.mkv",
new List<VideoStream> 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( var audioInputFile = new AudioInputFile(
"/tmp/whatever.mkv", "/tmp/whatever.mkv",
@@ -219,7 +219,7 @@ public class PipelineGeneratorTests
new PixelFormatYuv420P(), new PixelFormatYuv420P(),
new FrameSize(1920, 1080), new FrameSize(1920, 1080),
new FrameSize(1920, 1080), new FrameSize(1920, 1080),
"16:9", false,
Option<int>.None, Option<int>.None,
2000, 2000,
4000, 4000,
@@ -273,7 +273,7 @@ public class PipelineGeneratorTests
var videoInputFile = new VideoInputFile( var videoInputFile = new VideoInputFile(
"/tmp/whatever.mkv", "/tmp/whatever.mkv",
new List<VideoStream> 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; Option<AudioInputFile> audioInputFile = Option<AudioInputFile>.None;
@@ -284,7 +284,7 @@ public class PipelineGeneratorTests
new PixelFormatYuv420P(), new PixelFormatYuv420P(),
new FrameSize(1920, 1080), new FrameSize(1920, 1080),
new FrameSize(1920, 1080), new FrameSize(1920, 1080),
"16:9", false,
Option<int>.None, Option<int>.None,
2000, 2000,
4000, 4000,
@@ -341,7 +341,7 @@ public class PipelineGeneratorTests
"/test/input/file.png", "/test/input/file.png",
new List<VideoStream> 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( var pipelineBuilder = new PipelineBuilder(
@@ -360,7 +360,7 @@ public class PipelineGeneratorTests
string command = PrintCommand(videoInputFile, None, None, None, result); string command = PrintCommand(videoInputFile, None, None, None, result);
command.Should().Be( 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( private static string PrintCommand(
+1 -1
View File
@@ -10,7 +10,7 @@
<PackageReference Include="CliWrap" Version="3.5.0" /> <PackageReference Include="CliWrap" Version="3.5.0" />
<PackageReference Include="LanguageExt.Core" Version="4.2.9" /> <PackageReference Include="LanguageExt.Core" Version="4.2.9" />
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="6.0.0" /> <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>
<ItemGroup> <ItemGroup>
@@ -8,19 +8,27 @@ namespace ErsatzTV.FFmpeg.Filter;
public static class AvailableScaleFilters public static class AvailableScaleFilters
{ {
public static IPipelineFilterStep ForAcceleration( public static IPipelineFilterStep ForAcceleration(
IRuntimeInfo runtimeInfo, IRuntimeInfo _,
HardwareAccelerationMode accelMode, HardwareAccelerationMode accelMode,
FrameState currentState, FrameState currentState,
FrameSize scaledSize, FrameSize scaledSize,
FrameSize paddedSize, FrameSize paddedSize,
int extraHardwareFrames) => int extraHardwareFrames,
bool isAnamorphicEdgeCase,
string sampleAspectRatio) =>
accelMode switch accelMode switch
{ {
HardwareAccelerationMode.Nvenc => new ScaleCudaFilter(currentState, scaledSize, paddedSize), HardwareAccelerationMode.Nvenc =>
new ScaleCudaFilter(currentState, scaledSize, paddedSize, isAnamorphicEdgeCase),
HardwareAccelerationMode.Qsv when currentState.FrameDataLocation == FrameDataLocation.Hardware || HardwareAccelerationMode.Qsv when currentState.FrameDataLocation == FrameDataLocation.Hardware ||
scaledSize == paddedSize => scaledSize == paddedSize =>
new ScaleQsvFilter(runtimeInfo, currentState, scaledSize, paddedSize, extraHardwareFrames), new ScaleQsvFilter(
currentState,
scaledSize,
extraHardwareFrames,
isAnamorphicEdgeCase,
sampleAspectRatio),
HardwareAccelerationMode.Vaapi => new ScaleVaapiFilter(currentState, scaledSize, paddedSize), HardwareAccelerationMode.Vaapi => new ScaleVaapiFilter(currentState, scaledSize, paddedSize),
_ => new ScaleFilter(currentState, scaledSize, paddedSize) _ => new ScaleFilter(currentState, scaledSize, paddedSize, isAnamorphicEdgeCase)
}; };
} }
@@ -5,11 +5,11 @@ namespace ErsatzTV.FFmpeg.Filter;
public static class AvailableSubtitleOverlayFilters public static class AvailableSubtitleOverlayFilters
{ {
public static IPipelineFilterStep ForAcceleration(HardwareAccelerationMode accelMode, FrameState currentState) => public static IPipelineFilterStep ForAcceleration(HardwareAccelerationMode accelMode) =>
accelMode switch accelMode switch
{ {
HardwareAccelerationMode.Nvenc => new OverlaySubtitleCudaFilter(), HardwareAccelerationMode.Nvenc => new OverlaySubtitleCudaFilter(),
HardwareAccelerationMode.Qsv => new OverlaySubtitleQsvFilter(currentState), HardwareAccelerationMode.Qsv => new OverlaySubtitleQsvFilter(),
_ => new OverlaySubtitleFilter() _ => new OverlaySubtitleFilter()
}; };
} }
@@ -1,6 +1,7 @@
using ErsatzTV.FFmpeg.Filter.Cuda; using ErsatzTV.FFmpeg.Filter.Cuda;
using ErsatzTV.FFmpeg.Filter.Qsv; using ErsatzTV.FFmpeg.Filter.Qsv;
using ErsatzTV.FFmpeg.State; using ErsatzTV.FFmpeg.State;
using Microsoft.Extensions.Logging;
namespace ErsatzTV.FFmpeg.Filter; namespace ErsatzTV.FFmpeg.Filter;
@@ -8,13 +9,16 @@ public static class AvailableWatermarkOverlayFilters
{ {
public static IPipelineFilterStep ForAcceleration( public static IPipelineFilterStep ForAcceleration(
HardwareAccelerationMode accelMode, HardwareAccelerationMode accelMode,
FrameState currentState,
WatermarkState watermarkState, WatermarkState watermarkState,
FrameSize resolution) => FrameSize resolution,
FrameSize squarePixelFrameSize,
ILogger logger) =>
accelMode switch accelMode switch
{ {
HardwareAccelerationMode.Nvenc => new OverlayWatermarkCudaFilter(currentState, watermarkState, resolution), HardwareAccelerationMode.Nvenc =>
HardwareAccelerationMode.Qsv => new OverlayWatermarkQsvFilter(currentState, watermarkState, resolution), new OverlayWatermarkCudaFilter(watermarkState, resolution, squarePixelFrameSize, logger),
_ => new OverlayWatermarkFilter(currentState, watermarkState, resolution) HardwareAccelerationMode.Qsv =>
new OverlayWatermarkQsvFilter(watermarkState, resolution, squarePixelFrameSize, logger),
_ => new OverlayWatermarkFilter(watermarkState, resolution, squarePixelFrameSize, logger)
}; };
} }
+54 -44
View File
@@ -1,5 +1,6 @@
using ErsatzTV.FFmpeg.Environment; using ErsatzTV.FFmpeg.Environment;
using ErsatzTV.FFmpeg.Format; using ErsatzTV.FFmpeg.Format;
using Microsoft.Extensions.Logging;
namespace ErsatzTV.FFmpeg.Filter; namespace ErsatzTV.FFmpeg.Filter;
@@ -8,6 +9,7 @@ public class ComplexFilter : IPipelineStep
private readonly FrameState _currentState; private readonly FrameState _currentState;
private readonly FFmpegState _ffmpegState; private readonly FFmpegState _ffmpegState;
private readonly string _fontsDir; private readonly string _fontsDir;
private readonly ILogger _logger;
private readonly Option<AudioInputFile> _maybeAudioInputFile; private readonly Option<AudioInputFile> _maybeAudioInputFile;
private readonly Option<SubtitleInputFile> _maybeSubtitleInputFile; private readonly Option<SubtitleInputFile> _maybeSubtitleInputFile;
private readonly Option<VideoInputFile> _maybeVideoInputFile; private readonly Option<VideoInputFile> _maybeVideoInputFile;
@@ -22,7 +24,8 @@ public class ComplexFilter : IPipelineStep
Option<WatermarkInputFile> maybeWatermarkInputFile, Option<WatermarkInputFile> maybeWatermarkInputFile,
Option<SubtitleInputFile> maybeSubtitleInputFile, Option<SubtitleInputFile> maybeSubtitleInputFile,
FrameSize resolution, FrameSize resolution,
string fontsDir) string fontsDir,
ILogger logger)
{ {
_currentState = currentState; _currentState = currentState;
_ffmpegState = ffmpegState; _ffmpegState = ffmpegState;
@@ -32,6 +35,7 @@ public class ComplexFilter : IPipelineStep
_maybeSubtitleInputFile = maybeSubtitleInputFile; _maybeSubtitleInputFile = maybeSubtitleInputFile;
_resolution = resolution; _resolution = resolution;
_fontsDir = fontsDir; _fontsDir = fontsDir;
_logger = logger;
} }
public IList<EnvironmentVariable> EnvironmentVariables => Array.Empty<EnvironmentVariable>(); public IList<EnvironmentVariable> EnvironmentVariables => Array.Empty<EnvironmentVariable>();
@@ -147,48 +151,56 @@ public class ComplexFilter : IPipelineStep
watermarkLabel = $"[{watermarkLabel}]"; watermarkLabel = $"[{watermarkLabel}]";
} }
IPipelineFilterStep overlayFilter = AvailableWatermarkOverlayFilters.ForAcceleration( foreach (VideoInputFile videoInputFile in _maybeVideoInputFile)
_ffmpegState.EncoderHardwareAccelerationMode,
_currentState,
watermarkInputFile.DesiredState,
_resolution);
if (overlayFilter.Filter != string.Empty)
{ {
string tempVideoLabel = string.IsNullOrWhiteSpace(videoFilterComplex) foreach (VideoStream stream in videoInputFile.VideoStreams)
? $"[{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; 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]";
}
} }
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]";
} }
} }
} }
@@ -216,10 +228,8 @@ public class ComplexFilter : IPipelineStep
string filter; string filter;
if (subtitleInputFile.IsImageBased) if (subtitleInputFile.IsImageBased)
{ {
IPipelineFilterStep overlayFilter = IPipelineFilterStep overlayFilter = AvailableSubtitleOverlayFilters.ForAcceleration(
AvailableSubtitleOverlayFilters.ForAcceleration( _ffmpegState.EncoderHardwareAccelerationMode);
_ffmpegState.EncoderHardwareAccelerationMode,
_currentState);
filter = overlayFilter.Filter; filter = overlayFilter.Filter;
} }
else else
@@ -2,6 +2,6 @@
public class OverlaySubtitleCudaFilter : BaseFilter 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; public override FrameState NextState(FrameState currentState) => currentState;
} }
@@ -1,16 +1,19 @@
using ErsatzTV.FFmpeg.State; using ErsatzTV.FFmpeg.State;
using Microsoft.Extensions.Logging;
namespace ErsatzTV.FFmpeg.Filter.Cuda; namespace ErsatzTV.FFmpeg.Filter.Cuda;
public class OverlayWatermarkCudaFilter : OverlayWatermarkFilter public class OverlayWatermarkCudaFilter : OverlayWatermarkFilter
{ {
public OverlayWatermarkCudaFilter( public OverlayWatermarkCudaFilter(
FrameState currentState,
WatermarkState watermarkState, WatermarkState watermarkState,
FrameSize resolution) : base( FrameSize resolution,
currentState, FrameSize squarePixelFrameSize,
ILogger logger) : base(
watermarkState, watermarkState,
resolution) resolution,
squarePixelFrameSize,
logger)
{ {
} }
+30 -3
View File
@@ -6,13 +6,19 @@ public class ScaleCudaFilter : BaseFilter
{ {
private readonly FrameState _currentState; private readonly FrameState _currentState;
private readonly FrameSize _paddedSize; private readonly FrameSize _paddedSize;
private readonly bool _isAnamorphicEdgeCase;
private readonly FrameSize _scaledSize; private readonly FrameSize _scaledSize;
public ScaleCudaFilter(FrameState currentState, FrameSize scaledSize, FrameSize paddedSize) public ScaleCudaFilter(
FrameState currentState,
FrameSize scaledSize,
FrameSize paddedSize,
bool isAnamorphicEdgeCase)
{ {
_currentState = currentState; _currentState = currentState;
_scaledSize = scaledSize; _scaledSize = scaledSize;
_paddedSize = paddedSize; _paddedSize = paddedSize;
_isAnamorphicEdgeCase = isAnamorphicEdgeCase;
} }
public override string Filter public override string Filter
@@ -30,6 +36,13 @@ public class ScaleCudaFilter : BaseFilter
} }
else 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 targetSize = $"{_paddedSize.Width}:{_paddedSize.Height}";
string format = string.Empty; string format = string.Empty;
foreach (IPixelFormat pixelFormat in _currentState.PixelFormat) foreach (IPixelFormat pixelFormat in _currentState.PixelFormat)
@@ -37,7 +50,20 @@ public class ScaleCudaFilter : BaseFilter
format = $":format={pixelFormat.FFmpegName}"; 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 // TODO: this might not always upload to hardware, so NextState could be inaccurate
@@ -56,6 +82,7 @@ public class ScaleCudaFilter : BaseFilter
{ {
ScaledSize = _scaledSize, ScaledSize = _scaledSize,
PaddedSize = _scaledSize, PaddedSize = _scaledSize,
FrameDataLocation = FrameDataLocation.Hardware FrameDataLocation = FrameDataLocation.Hardware,
IsAnamorphic = false // this filter always outputs square pixels
}; };
} }
@@ -29,7 +29,7 @@ public class SubtitleScaleNppFilter : BaseFilter
format = $":format={pixelFormat.FFmpegName}"; format = $":format={pixelFormat.FFmpegName}";
} }
scale = $"scale_npp={targetSize}{format}"; scale = $"scale_npp={targetSize}{format}:force_original_aspect_ratio=1";
} }
return scale; return scale;
@@ -2,6 +2,6 @@
public class OverlaySubtitleFilter : BaseFilter 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; public override FrameState NextState(FrameState currentState) => currentState;
} }
@@ -1,18 +1,25 @@
using ErsatzTV.FFmpeg.State; using ErsatzTV.FFmpeg.State;
using Microsoft.Extensions.Logging;
namespace ErsatzTV.FFmpeg.Filter; namespace ErsatzTV.FFmpeg.Filter;
public class OverlayWatermarkFilter : BaseFilter public class OverlayWatermarkFilter : BaseFilter
{ {
private readonly FrameState _currentState;
private readonly FrameSize _resolution; private readonly FrameSize _resolution;
private readonly FrameSize _squarePixelFrameSize;
private readonly ILogger _logger;
private readonly WatermarkState _watermarkState; 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; _watermarkState = watermarkState;
_resolution = resolution; _resolution = resolution;
_squarePixelFrameSize = squarePixelFrameSize;
_logger = logger;
} }
public override string Filter => $"overlay={Position}"; public override string Filter => $"overlay={Position}";
@@ -21,8 +28,20 @@ public class OverlayWatermarkFilter : BaseFilter
{ {
get get
{ {
double horizontalMargin = Math.Round(_watermarkState.HorizontalMarginPercent / 100.0 * _resolution.Width); int horizontalPadding = _resolution.Width - _squarePixelFrameSize.Width;
double verticalMargin = Math.Round(_watermarkState.VerticalMarginPercent / 100.0 * _resolution.Height); 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 return _watermarkState.Location switch
{ {
@@ -2,12 +2,8 @@
public class OverlaySubtitleQsvFilter : BaseFilter public class OverlaySubtitleQsvFilter : BaseFilter
{ {
private readonly FrameState _currentState;
public OverlaySubtitleQsvFilter(FrameState currentState) => _currentState = currentState;
public override string Filter => 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; public override FrameState NextState(FrameState currentState) => currentState;
} }
@@ -1,16 +1,19 @@
using ErsatzTV.FFmpeg.State; using ErsatzTV.FFmpeg.State;
using Microsoft.Extensions.Logging;
namespace ErsatzTV.FFmpeg.Filter.Qsv; namespace ErsatzTV.FFmpeg.Filter.Qsv;
public class OverlayWatermarkQsvFilter : OverlayWatermarkFilter public class OverlayWatermarkQsvFilter : OverlayWatermarkFilter
{ {
public OverlayWatermarkQsvFilter( public OverlayWatermarkQsvFilter(
FrameState currentState,
WatermarkState watermarkState, WatermarkState watermarkState,
FrameSize resolution) : base( FrameSize resolution,
currentState, FrameSize squarePixelFrameSize,
ILogger logger) : base(
watermarkState, watermarkState,
resolution) resolution,
squarePixelFrameSize,
logger)
{ {
} }
+27 -15
View File
@@ -1,29 +1,27 @@
using System.Runtime.InteropServices; using ErsatzTV.FFmpeg.Format;
using ErsatzTV.FFmpeg.Format;
using ErsatzTV.FFmpeg.Runtime;
namespace ErsatzTV.FFmpeg.Filter.Qsv; namespace ErsatzTV.FFmpeg.Filter.Qsv;
public class ScaleQsvFilter : BaseFilter public class ScaleQsvFilter : BaseFilter
{ {
private readonly IRuntimeInfo _runtimeInfo;
private readonly FrameState _currentState; private readonly FrameState _currentState;
private readonly FrameSize _scaledSize; private readonly FrameSize _scaledSize;
private readonly FrameSize _paddedSize;
private readonly int _extraHardwareFrames; private readonly int _extraHardwareFrames;
private readonly bool _isAnamorphicEdgeCase;
private readonly string _sampleAspectRatio;
public ScaleQsvFilter( public ScaleQsvFilter(
IRuntimeInfo runtimeInfo,
FrameState currentState, FrameState currentState,
FrameSize scaledSize, FrameSize scaledSize,
FrameSize paddedSize, int extraHardwareFrames,
int extraHardwareFrames) bool isAnamorphicEdgeCase,
string sampleAspectRatio)
{ {
_runtimeInfo = runtimeInfo;
_currentState = currentState; _currentState = currentState;
_scaledSize = scaledSize; _scaledSize = scaledSize;
_paddedSize = paddedSize;
_extraHardwareFrames = extraHardwareFrames; _extraHardwareFrames = extraHardwareFrames;
_isAnamorphicEdgeCase = isAnamorphicEdgeCase;
_sampleAspectRatio = sampleAspectRatio;
} }
public override string Filter public override string Filter
@@ -44,16 +42,29 @@ public class ScaleQsvFilter : BaseFilter
} }
else else
{ {
string squareScale = string.Empty;
string targetSize = $"w={_scaledSize.Width}:h={_scaledSize.Height}";
string format = string.Empty; string format = string.Empty;
foreach (IPixelFormat pixelFormat in _currentState.PixelFormat) foreach (IPixelFormat pixelFormat in _currentState.PixelFormat)
{ {
format = $":format={pixelFormat.FFmpegName}"; format = $":format={pixelFormat.FFmpegName}";
} }
string targetSize = _runtimeInfo.IsOSPlatform(OSPlatform.Windows) string sar = _sampleAspectRatio.Replace(':', '/');
? $"w={_paddedSize.Width}:h={_paddedSize.Height}" if (_isAnamorphicEdgeCase)
: $"w={_scaledSize.Width}:h={_scaledSize.Height}"; {
scale = $"vpp_qsv={targetSize}{format}"; 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) if (_currentState.FrameDataLocation == FrameDataLocation.Hardware)
@@ -75,6 +86,7 @@ public class ScaleQsvFilter : BaseFilter
{ {
ScaledSize = _scaledSize, ScaledSize = _scaledSize,
PaddedSize = _scaledSize, PaddedSize = _scaledSize,
FrameDataLocation = FrameDataLocation.Hardware FrameDataLocation = FrameDataLocation.Hardware,
IsAnamorphic = false // this filter always outputs square pixels
}; };
} }
@@ -31,7 +31,7 @@ public class SubtitleScaleQsvFilter : BaseFilter
string targetSize = $"w={_paddedSize.Width}:h={_paddedSize.Height}"; string targetSize = $"w={_paddedSize.Width}:h={_paddedSize.Height}";
// use software scaling // use software scaling
scale = $"scale={targetSize}"; scale = $"scale={targetSize}:force_original_aspect_ratio=decrease";
} }
string initialPixelFormat = _currentState.PixelFormat.Match(pf => pf.FFmpegName, FFmpegFormat.NV12); string initialPixelFormat = _currentState.PixelFormat.Match(pf => pf.FFmpegName, FFmpegFormat.NV12);
+24 -9
View File
@@ -6,13 +6,15 @@ public class ScaleFilter : BaseFilter
{ {
private readonly FrameState _currentState; private readonly FrameState _currentState;
private readonly FrameSize _paddedSize; private readonly FrameSize _paddedSize;
private readonly bool _isAnamorphicEdgeCase;
private readonly FrameSize _scaledSize; private readonly FrameSize _scaledSize;
public ScaleFilter(FrameState currentState, FrameSize scaledSize, FrameSize paddedSize) public ScaleFilter(FrameState currentState, FrameSize scaledSize, FrameSize paddedSize, bool isAnamorphicEdgeCase)
{ {
_currentState = currentState; _currentState = currentState;
_scaledSize = scaledSize; _scaledSize = scaledSize;
_paddedSize = paddedSize; _paddedSize = paddedSize;
_isAnamorphicEdgeCase = isAnamorphicEdgeCase;
} }
public override string Filter public override string Filter
@@ -25,8 +27,19 @@ public class ScaleFilter : BaseFilter
aspectRatio = ":force_original_aspect_ratio=decrease"; aspectRatio = ":force_original_aspect_ratio=decrease";
} }
string scale = string scale;
$"scale={_paddedSize.Width}:{_paddedSize.Height}:flags=fast_bilinear{aspectRatio}"; 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; string hwdownload = string.Empty;
if (_currentState.FrameDataLocation == FrameDataLocation.Hardware) if (_currentState.FrameDataLocation == FrameDataLocation.Hardware)
@@ -45,10 +58,12 @@ public class ScaleFilter : BaseFilter
} }
} }
public override FrameState NextState(FrameState currentState) => currentState with public override FrameState NextState(FrameState currentState) =>
{ currentState with
ScaledSize = _scaledSize, {
PaddedSize = _scaledSize, ScaledSize = _scaledSize,
FrameDataLocation = FrameDataLocation.Software PaddedSize = _scaledSize,
}; FrameDataLocation = FrameDataLocation.Software,
IsAnamorphic = false // this filter always outputs square pixels
};
} }
+1 -3
View File
@@ -6,9 +6,7 @@ public class ScaleImageFilter : BaseFilter
public ScaleImageFilter(FrameSize scaledSize) => _scaledSize = scaledSize; public ScaleImageFilter(FrameSize scaledSize) => _scaledSize = scaledSize;
public override string Filter => $"scale={_scaledSize.Width}:{_scaledSize.Height}"; public override string Filter => $"scale={_scaledSize.Width}:{_scaledSize.Height}:force_original_aspect_ratio=decrease";
// public override IList<string> OutputOptions => new List<string> { "-q:v", "2" };
public override FrameState NextState(FrameState currentState) => currentState with public override FrameState NextState(FrameState currentState) => currentState with
{ {
+5 -2
View File
@@ -9,10 +9,13 @@ public record FrameState(
Option<IPixelFormat> PixelFormat, Option<IPixelFormat> PixelFormat,
FrameSize ScaledSize, FrameSize ScaledSize,
FrameSize PaddedSize, FrameSize PaddedSize,
string DisplayAspectRatio, bool IsAnamorphic,
Option<int> FrameRate, Option<int> FrameRate,
Option<int> VideoBitrate, Option<int> VideoBitrate,
Option<int> VideoBufferSize, Option<int> VideoBufferSize,
Option<int> VideoTrackTimeScale, Option<int> VideoTrackTimeScale,
bool Deinterlaced, bool Deinterlaced,
FrameDataLocation FrameDataLocation = FrameDataLocation.Unknown); FrameDataLocation FrameDataLocation = FrameDataLocation.Unknown)
{
public string FFmpegAspectRatio => PaddedSize.Width == 640 ? "4/3" : "16/9";
}
+1
View File
@@ -20,6 +20,7 @@ public record ConcatInputFile(string Url, FrameSize Resolution) : InputFile(
Option<IPixelFormat>.None, Option<IPixelFormat>.None,
Resolution, Resolution,
string.Empty, string.Empty,
string.Empty,
Option<string>.None, Option<string>.None,
false) false)
}) })
+92 -2
View File
@@ -1,4 +1,5 @@
using ErsatzTV.FFmpeg.Format; using System.ComponentModel.DataAnnotations;
using ErsatzTV.FFmpeg.Format;
namespace ErsatzTV.FFmpeg; namespace ErsatzTV.FFmpeg;
@@ -14,9 +15,98 @@ public record VideoStream(
string Codec, string Codec,
Option<IPixelFormat> PixelFormat, Option<IPixelFormat> PixelFormat,
FrameSize FrameSize, FrameSize FrameSize,
string SampleAspectRatio,
string DisplayAspectRatio, string DisplayAspectRatio,
Option<string> FrameRate, Option<string> FrameRate,
bool StillImage) : MediaStream( bool StillImage) : MediaStream(
Index, Index,
Codec, 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;
}
}
+40 -28
View File
@@ -192,7 +192,7 @@ public class PipelineBuilder
videoStream.PixelFormat, videoStream.PixelFormat,
videoStream.FrameSize, videoStream.FrameSize,
videoStream.FrameSize, videoStream.FrameSize,
videoStream.DisplayAspectRatio, videoStream.IsAnamorphic,
initialFrameRate, initialFrameRate,
Option<int>.None, Option<int>.None,
Option<int>.None, Option<int>.None,
@@ -376,7 +376,8 @@ public class PipelineBuilder
IPipelineFilterStep scaleStep = new ScaleFilter( IPipelineFilterStep scaleStep = new ScaleFilter(
currentState, currentState,
desiredState.ScaledSize, desiredState.ScaledSize,
desiredState.PaddedSize); desiredState.PaddedSize,
videoStream.IsAnamorphicEdgeCase);
currentState = scaleStep.NextState(currentState); currentState = scaleStep.NextState(currentState);
_videoInputFile.Iter(f => f.FilterSteps.Add(scaleStep)); _videoInputFile.Iter(f => f.FilterSteps.Add(scaleStep));
@@ -385,12 +386,12 @@ public class PipelineBuilder
currentState = padStep.NextState(currentState); currentState = padStep.NextState(currentState);
_videoInputFile.Iter(f => f.FilterSteps.Add(padStep)); _videoInputFile.Iter(f => f.FilterSteps.Add(padStep));
if (videoStream.DisplayAspectRatio == desiredState.DisplayAspectRatio) // if (videoStream.DisplayAspectRatio == desiredState.DisplayAspectRatio)
{ // {
IPipelineFilterStep darStep = new SetDarFilter(desiredState.DisplayAspectRatio); // IPipelineFilterStep darStep = new SetDarFilter(desiredState.DisplayAspectRatio);
currentState = darStep.NextState(currentState); // currentState = darStep.NextState(currentState);
_videoInputFile.Iter(f => f.FilterSteps.Add(darStep)); // _videoInputFile.Iter(f => f.FilterSteps.Add(darStep));
} // }
} }
} }
else if (currentState.ScaledSize != desiredState.ScaledSize) else if (currentState.ScaledSize != desiredState.ScaledSize)
@@ -401,7 +402,9 @@ public class PipelineBuilder
currentState, currentState,
desiredState.ScaledSize, desiredState.ScaledSize,
desiredState.PaddedSize, desiredState.PaddedSize,
ffmpegState.QsvExtraHardwareFrames); ffmpegState.QsvExtraHardwareFrames,
videoStream.IsAnamorphicEdgeCase,
videoStream.SampleAspectRatio);
currentState = scaleFilter.NextState(currentState); currentState = scaleFilter.NextState(currentState);
_videoInputFile.Iter(f => f.FilterSteps.Add(scaleFilter)); _videoInputFile.Iter(f => f.FilterSteps.Add(scaleFilter));
@@ -413,13 +416,13 @@ public class PipelineBuilder
_videoInputFile.Iter(f => f.FilterSteps.Add(padStep)); _videoInputFile.Iter(f => f.FilterSteps.Add(padStep));
} }
if (videoStream.DisplayAspectRatio == desiredState.DisplayAspectRatio || // if (videoStream.DisplayAspectRatio == desiredState.DisplayAspectRatio ||
ffmpegState.EncoderHardwareAccelerationMode == HardwareAccelerationMode.Qsv) // ffmpegState.EncoderHardwareAccelerationMode == HardwareAccelerationMode.Qsv)
{ // {
IPipelineFilterStep darStep = new SetDarFilter(desiredState.DisplayAspectRatio); // IPipelineFilterStep darStep = new SetDarFilter(desiredState.DisplayAspectRatio);
currentState = darStep.NextState(currentState); // currentState = darStep.NextState(currentState);
_videoInputFile.Iter(f => f.FilterSteps.Add(darStep)); // _videoInputFile.Iter(f => f.FilterSteps.Add(darStep));
} // }
} }
else if (currentState.PaddedSize != desiredState.PaddedSize) else if (currentState.PaddedSize != desiredState.PaddedSize)
{ {
@@ -429,7 +432,9 @@ public class PipelineBuilder
currentState, currentState,
desiredState.ScaledSize, desiredState.ScaledSize,
desiredState.PaddedSize, desiredState.PaddedSize,
ffmpegState.QsvExtraHardwareFrames); ffmpegState.QsvExtraHardwareFrames,
videoStream.IsAnamorphicEdgeCase,
videoStream.SampleAspectRatio);
currentState = scaleFilter.NextState(currentState); currentState = scaleFilter.NextState(currentState);
_videoInputFile.Iter(f => f.FilterSteps.Add(scaleFilter)); _videoInputFile.Iter(f => f.FilterSteps.Add(scaleFilter));
@@ -440,13 +445,13 @@ public class PipelineBuilder
_videoInputFile.Iter(f => f.FilterSteps.Add(padStep)); _videoInputFile.Iter(f => f.FilterSteps.Add(padStep));
} }
if (videoStream.DisplayAspectRatio == desiredState.DisplayAspectRatio || // if (videoStream.DisplayAspectRatio == desiredState.DisplayAspectRatio ||
ffmpegState.EncoderHardwareAccelerationMode == HardwareAccelerationMode.Qsv) // ffmpegState.EncoderHardwareAccelerationMode == HardwareAccelerationMode.Qsv)
{ // {
IPipelineFilterStep darStep = new SetDarFilter(desiredState.DisplayAspectRatio); // IPipelineFilterStep darStep = new SetDarFilter(desiredState.DisplayAspectRatio);
currentState = darStep.NextState(currentState); // currentState = darStep.NextState(currentState);
_videoInputFile.Iter(f => f.FilterSteps.Add(darStep)); // _videoInputFile.Iter(f => f.FilterSteps.Add(darStep));
} // }
} }
if (hasOverlay && currentState.PixelFormat.Map(pf => pf.FFmpegName) != if (hasOverlay && currentState.PixelFormat.Map(pf => pf.FFmpegName) !=
@@ -485,7 +490,9 @@ public class PipelineBuilder
currentState, currentState,
desiredState.ScaledSize, desiredState.ScaledSize,
desiredState.PaddedSize, desiredState.PaddedSize,
ffmpegState.QsvExtraHardwareFrames); ffmpegState.QsvExtraHardwareFrames,
videoStream.IsAnamorphicEdgeCase,
videoStream.SampleAspectRatio);
currentState = scaleFilter.NextState(currentState); currentState = scaleFilter.NextState(currentState);
_videoInputFile.Iter(f => f.FilterSteps.Add(scaleFilter)); _videoInputFile.Iter(f => f.FilterSteps.Add(scaleFilter));
} }
@@ -516,7 +523,9 @@ public class PipelineBuilder
currentState, currentState,
desiredState.ScaledSize, desiredState.ScaledSize,
desiredState.PaddedSize, desiredState.PaddedSize,
ffmpegState.QsvExtraHardwareFrames); ffmpegState.QsvExtraHardwareFrames,
videoStream.IsAnamorphicEdgeCase,
videoStream.SampleAspectRatio);
currentState = scaleFilter.NextState(currentState); currentState = scaleFilter.NextState(currentState);
videoInputFile.FilterSteps.Add(scaleFilter); videoInputFile.FilterSteps.Add(scaleFilter);
} }
@@ -670,7 +679,9 @@ public class PipelineBuilder
currentState, currentState,
desiredState.ScaledSize, desiredState.ScaledSize,
desiredState.PaddedSize, desiredState.PaddedSize,
ffmpegState.QsvExtraHardwareFrames); ffmpegState.QsvExtraHardwareFrames,
videoStream.IsAnamorphicEdgeCase,
videoStream.SampleAspectRatio);
currentState = scaleFilter.NextState(currentState); currentState = scaleFilter.NextState(currentState);
_videoInputFile.Iter(f => f.FilterSteps.Add(scaleFilter)); _videoInputFile.Iter(f => f.FilterSteps.Add(scaleFilter));
} }
@@ -800,7 +811,8 @@ public class PipelineBuilder
_watermarkInputFile, _watermarkInputFile,
_subtitleInputFile, _subtitleInputFile,
currentState.PaddedSize, currentState.PaddedSize,
_fontsFolder); _fontsFolder,
_logger);
_pipelineSteps.Add(complexFilter); _pipelineSteps.Add(complexFilter);
} }
@@ -14,12 +14,12 @@
<PackageReference Include="Lucene.Net" Version="4.8.0-beta00016" /> <PackageReference Include="Lucene.Net" Version="4.8.0-beta00016" />
<PackageReference Include="Lucene.Net.Analysis.Common" 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="Lucene.Net.QueryParser" Version="4.8.0-beta00016" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.8" /> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.8"> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.9">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </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"> <PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.3.44">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
+7 -7
View File
@@ -55,16 +55,16 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Bugsnag.AspNet.Core" Version="3.1.0" /> <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="FluentValidation.AspNetCore" Version="11.2.2" />
<PackageReference Include="HtmlSanitizer" Version="7.1.542" /> <PackageReference Include="HtmlSanitizer" Version="7.1.542" />
<PackageReference Include="LanguageExt.Core" Version="4.2.9" /> <PackageReference Include="LanguageExt.Core" Version="4.2.9" />
<PackageReference Include="Markdig" Version="0.30.3" /> <PackageReference Include="Markdig" Version="0.30.3" />
<PackageReference Include="MediatR.Courier.DependencyInjection" Version="5.0.0" /> <PackageReference Include="MediatR.Courier.DependencyInjection" Version="5.0.0" />
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="10.0.1" /> <PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="10.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.8" /> <PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.9" />
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="6.0.8" /> <PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="6.0.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.8"> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.9">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
@@ -73,12 +73,12 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="MudBlazor" Version="6.0.15" /> <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="PPioli.FluentValidation.Blazor" Version="11.1.0" />
<PackageReference Include="Refit.HttpClientFactory" Version="6.3.2" /> <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.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="Serilog.Sinks.SQLite" Version="6.0.1" />
<PackageReference Include="System.IO.FileSystem.Primitives" Version="4.3.0" /> <PackageReference Include="System.IO.FileSystem.Primitives" Version="4.3.0" />
<PackageReference Include="System.Text.Encoding.Extensions" Version="4.3.0" /> <PackageReference Include="System.Text.Encoding.Extensions" Version="4.3.0" />
+1 -1
View File
@@ -64,7 +64,7 @@ public class Program
} }
finally finally
{ {
Log.CloseAndFlush(); await Log.CloseAndFlushAsync();
} }
} }