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:
@@ -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>
|
||||||
|
|||||||
@@ -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)
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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";
|
||||||
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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
@@ -64,7 +64,7 @@ public class Program
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
Log.CloseAndFlush();
|
await Log.CloseAndFlushAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user