fix image subtitle scaling (#949)
* properly scale image-based subtitles for nvidia and software * fix vaapi image subtitle scaling * fix qsv image subtitle scaling * update changelog
This commit is contained in:
@@ -11,6 +11,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
- `VAAPI`: Downgrade libva from 2.15 to 2.14
|
||||
- Fix bug with XMLTV that caused some filler to display with primary content details
|
||||
- Multiple fixes for content scaling with `Nvidia`, `Qsv` and `Vaapi` accelerations
|
||||
- Properly scale image-based subtitles
|
||||
|
||||
### Added
|
||||
- Add `Preferred Audio Title` feature
|
||||
|
||||
@@ -198,11 +198,11 @@ public class TranscodingTests
|
||||
[ValueSource(typeof(TestData), nameof(TestData.VideoFormats))]
|
||||
FFmpegProfileVideoFormat profileVideoFormat,
|
||||
// [ValueSource(typeof(TestData), nameof(TestData.NoAcceleration))] HardwareAccelerationKind profileAcceleration)
|
||||
// [ValueSource(typeof(TestData), nameof(TestData.NvidiaAcceleration))] HardwareAccelerationKind profileAcceleration)
|
||||
[ValueSource(typeof(TestData), nameof(TestData.NvidiaAcceleration))] HardwareAccelerationKind profileAcceleration)
|
||||
// [ValueSource(typeof(TestData), nameof(TestData.VaapiAcceleration))] HardwareAccelerationKind profileAcceleration)
|
||||
// [ValueSource(typeof(TestData), nameof(TestData.QsvAcceleration))] HardwareAccelerationKind profileAcceleration)
|
||||
// [ValueSource(typeof(TestData), nameof(TestData.VideoToolboxAcceleration))] HardwareAccelerationKind profileAcceleration)
|
||||
[ValueSource(typeof(TestData), nameof(TestData.AmfAcceleration))] HardwareAccelerationKind profileAcceleration)
|
||||
// [ValueSource(typeof(TestData), nameof(TestData.AmfAcceleration))] HardwareAccelerationKind profileAcceleration)
|
||||
{
|
||||
if (inputFormat.Encoder is "mpeg1video" or "msmpeg4v2" or "msmpeg4v3")
|
||||
{
|
||||
|
||||
@@ -239,7 +239,8 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
|
||||
hlsPlaylistPath,
|
||||
hlsSegmentTemplate,
|
||||
ptsOffset,
|
||||
playbackSettings.ThreadCount);
|
||||
playbackSettings.ThreadCount,
|
||||
128);
|
||||
|
||||
_logger.LogDebug("FFmpeg desired state {FrameState}", desiredState);
|
||||
|
||||
@@ -363,7 +364,8 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
|
||||
hlsPlaylistPath,
|
||||
hlsSegmentTemplate,
|
||||
ptsOffset,
|
||||
Option<int>.None);
|
||||
Option<int>.None,
|
||||
128);
|
||||
|
||||
var ffmpegSubtitleStream = new ErsatzTV.FFmpeg.MediaStream(0, "ass", StreamKind.Video);
|
||||
|
||||
|
||||
@@ -70,6 +70,7 @@ public class PipelineGeneratorTests
|
||||
Option<string>.None,
|
||||
Option<string>.None,
|
||||
0,
|
||||
Option<int>.None,
|
||||
Option<int>.None);
|
||||
|
||||
var builder = new PipelineBuilder(
|
||||
@@ -142,6 +143,7 @@ public class PipelineGeneratorTests
|
||||
Option<string>.None,
|
||||
Option<string>.None,
|
||||
0,
|
||||
Option<int>.None,
|
||||
Option<int>.None);
|
||||
|
||||
var builder = new PipelineBuilder(
|
||||
@@ -240,6 +242,7 @@ public class PipelineGeneratorTests
|
||||
Option<string>.None,
|
||||
Option<string>.None,
|
||||
0,
|
||||
Option<int>.None,
|
||||
Option<int>.None);
|
||||
|
||||
var builder = new PipelineBuilder(
|
||||
@@ -304,6 +307,7 @@ public class PipelineGeneratorTests
|
||||
Option<string>.None,
|
||||
Option<string>.None,
|
||||
0,
|
||||
Option<int>.None,
|
||||
Option<int>.None);
|
||||
|
||||
var builder = new PipelineBuilder(
|
||||
|
||||
@@ -43,14 +43,16 @@ public static class AvailableEncoders
|
||||
new EncoderHevcQsv(
|
||||
currentState,
|
||||
maybeWatermarkInputFile,
|
||||
maybeSubtitleInputFile),
|
||||
maybeSubtitleInputFile,
|
||||
ffmpegState.QsvExtraHardwareFrames),
|
||||
(HardwareAccelerationMode.Qsv, VideoFormat.H264) when hardwareCapabilities.CanEncode(
|
||||
VideoFormat.H264,
|
||||
desiredState.PixelFormat) =>
|
||||
new EncoderH264Qsv(
|
||||
currentState,
|
||||
maybeWatermarkInputFile,
|
||||
maybeSubtitleInputFile),
|
||||
maybeSubtitleInputFile,
|
||||
ffmpegState.QsvExtraHardwareFrames),
|
||||
|
||||
(HardwareAccelerationMode.Vaapi, VideoFormat.Hevc) when hardwareCapabilities.CanEncode(
|
||||
VideoFormat.Hevc,
|
||||
|
||||
@@ -6,21 +6,26 @@ public class EncoderH264Qsv : EncoderBase
|
||||
{
|
||||
private readonly FrameState _currentState;
|
||||
private readonly Option<SubtitleInputFile> _maybeSubtitleInputFile;
|
||||
private readonly int _extraHardwareFrames;
|
||||
private readonly Option<WatermarkInputFile> _maybeWatermarkInputFile;
|
||||
|
||||
public EncoderH264Qsv(
|
||||
FrameState currentState,
|
||||
Option<WatermarkInputFile> maybeWatermarkInputFile,
|
||||
Option<SubtitleInputFile> maybeSubtitleInputFile)
|
||||
Option<SubtitleInputFile> maybeSubtitleInputFile,
|
||||
int extraHardwareFrames)
|
||||
{
|
||||
_currentState = currentState;
|
||||
_maybeWatermarkInputFile = maybeWatermarkInputFile;
|
||||
_maybeSubtitleInputFile = maybeSubtitleInputFile;
|
||||
_extraHardwareFrames = extraHardwareFrames;
|
||||
}
|
||||
|
||||
public override string Name => "h264_qsv";
|
||||
public override StreamKind Kind => StreamKind.Video;
|
||||
public override IList<string> OutputOptions => new[] { "-c:v", "h264_qsv", "-low_power", "0" };
|
||||
|
||||
public override IList<string> OutputOptions =>
|
||||
new[] { "-c:v", "h264_qsv", "-low_power", "0", "-look_ahead", "0" };
|
||||
|
||||
// need to upload if we're still in software and a watermark is used
|
||||
public override string Filter
|
||||
@@ -37,11 +42,11 @@ public class EncoderH264Qsv : EncoderBase
|
||||
// pixel format should already be converted to a supported format by QsvHardwareAccelerationOption
|
||||
foreach (IPixelFormat pixelFormat in _currentState.PixelFormat)
|
||||
{
|
||||
return $"format={pixelFormat.FFmpegName},hwupload=extra_hw_frames=64";
|
||||
return $"format={pixelFormat.FFmpegName},hwupload=extra_hw_frames={_extraHardwareFrames}";
|
||||
}
|
||||
|
||||
// default to nv12
|
||||
return "format=nv12,hwupload=extra_hw_frames=64";
|
||||
return $"format=nv12,hwupload=extra_hw_frames={_extraHardwareFrames}";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,21 +6,26 @@ public class EncoderHevcQsv : EncoderBase
|
||||
{
|
||||
private readonly FrameState _currentState;
|
||||
private readonly Option<SubtitleInputFile> _maybeSubtitleInputFile;
|
||||
private readonly int _extraHardwareFrames;
|
||||
private readonly Option<WatermarkInputFile> _maybeWatermarkInputFile;
|
||||
|
||||
public EncoderHevcQsv(
|
||||
FrameState currentState,
|
||||
Option<WatermarkInputFile> maybeWatermarkInputFile,
|
||||
Option<SubtitleInputFile> maybeSubtitleInputFile)
|
||||
Option<SubtitleInputFile> maybeSubtitleInputFile,
|
||||
int extraHardwareFrames)
|
||||
{
|
||||
_currentState = currentState;
|
||||
_maybeWatermarkInputFile = maybeWatermarkInputFile;
|
||||
_maybeSubtitleInputFile = maybeSubtitleInputFile;
|
||||
_extraHardwareFrames = extraHardwareFrames;
|
||||
}
|
||||
|
||||
public override string Name => "hevc_qsv";
|
||||
public override StreamKind Kind => StreamKind.Video;
|
||||
public override IList<string> OutputOptions => new[] { "-c:v", "hevc_qsv", "-low_power", "0" };
|
||||
|
||||
public override IList<string> OutputOptions =>
|
||||
new[] { "-c:v", "hevc_qsv", "-low_power", "0", "-look_head", "0" };
|
||||
|
||||
// need to upload if we're still in software and a watermark is used
|
||||
public override string Filter
|
||||
@@ -37,11 +42,11 @@ public class EncoderHevcQsv : EncoderBase
|
||||
// pixel format should already be converted to a supported format by QsvHardwareAccelerationOption
|
||||
foreach (IPixelFormat pixelFormat in _currentState.PixelFormat)
|
||||
{
|
||||
return $"format={pixelFormat.FFmpegName},hwupload=extra_hw_frames=64";
|
||||
return $"format={pixelFormat.FFmpegName},hwupload=extra_hw_frames={_extraHardwareFrames}";
|
||||
}
|
||||
|
||||
// default to nv12
|
||||
return "format=nv12,hwupload=extra_hw_frames=64";
|
||||
return $"format=nv12,hwupload=extra_hw_frames={_extraHardwareFrames}";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,8 +18,11 @@ public record FFmpegState(
|
||||
Option<string> HlsPlaylistPath,
|
||||
Option<string> HlsSegmentTemplate,
|
||||
long PtsOffset,
|
||||
Option<int> ThreadCount)
|
||||
Option<int> ThreadCount,
|
||||
Option<int> MaybeQsvExtraHardwareFrames)
|
||||
{
|
||||
public int QsvExtraHardwareFrames => MaybeQsvExtraHardwareFrames.IfNone(64);
|
||||
|
||||
public static FFmpegState Concat(bool saveReport, string channelName) =>
|
||||
new(
|
||||
saveReport,
|
||||
@@ -37,5 +40,6 @@ public record FFmpegState(
|
||||
Option<string>.None,
|
||||
Option<string>.None,
|
||||
0,
|
||||
Option<int>.None,
|
||||
Option<int>.None);
|
||||
}
|
||||
|
||||
@@ -12,13 +12,14 @@ public static class AvailableScaleFilters
|
||||
HardwareAccelerationMode accelMode,
|
||||
FrameState currentState,
|
||||
FrameSize scaledSize,
|
||||
FrameSize paddedSize) =>
|
||||
FrameSize paddedSize,
|
||||
int extraHardwareFrames) =>
|
||||
accelMode switch
|
||||
{
|
||||
HardwareAccelerationMode.Nvenc => new ScaleCudaFilter(currentState, scaledSize, paddedSize),
|
||||
HardwareAccelerationMode.Qsv when currentState.FrameDataLocation == FrameDataLocation.Hardware ||
|
||||
scaledSize == paddedSize =>
|
||||
new ScaleQsvFilter(runtimeInfo, currentState, scaledSize, paddedSize),
|
||||
new ScaleQsvFilter(runtimeInfo, currentState, scaledSize, paddedSize, extraHardwareFrames),
|
||||
HardwareAccelerationMode.Vaapi => new ScaleVaapiFilter(currentState, scaledSize, paddedSize),
|
||||
_ => new ScaleFilter(currentState, scaledSize, paddedSize)
|
||||
};
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
using ErsatzTV.FFmpeg.Filter.Cuda;
|
||||
using ErsatzTV.FFmpeg.Filter.Qsv;
|
||||
|
||||
namespace ErsatzTV.FFmpeg.Filter;
|
||||
|
||||
public static class AvailableSubtitleScaleFilters
|
||||
{
|
||||
public static IPipelineFilterStep ForAcceleration(
|
||||
HardwareAccelerationMode accelMode,
|
||||
FrameState currentState,
|
||||
FrameSize scaledSize,
|
||||
FrameSize paddedSize,
|
||||
int extraHardwareFrames) =>
|
||||
accelMode switch
|
||||
{
|
||||
HardwareAccelerationMode.Nvenc => new SubtitleScaleNppFilter(currentState, scaledSize, paddedSize),
|
||||
HardwareAccelerationMode.Qsv => new SubtitleScaleQsvFilter(
|
||||
currentState,
|
||||
scaledSize,
|
||||
paddedSize,
|
||||
extraHardwareFrames),
|
||||
_ => new ScaleImageFilter(paddedSize)
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using ErsatzTV.FFmpeg.Format;
|
||||
|
||||
namespace ErsatzTV.FFmpeg.Filter.Cuda;
|
||||
|
||||
public class SubtitleScaleNppFilter : BaseFilter
|
||||
{
|
||||
private readonly FrameState _currentState;
|
||||
private readonly FrameSize _paddedSize;
|
||||
private readonly FrameSize _scaledSize;
|
||||
|
||||
public SubtitleScaleNppFilter(FrameState currentState, FrameSize scaledSize, FrameSize paddedSize)
|
||||
{
|
||||
_currentState = currentState;
|
||||
_scaledSize = scaledSize;
|
||||
_paddedSize = paddedSize;
|
||||
}
|
||||
|
||||
public override string Filter
|
||||
{
|
||||
get
|
||||
{
|
||||
string scale = string.Empty;
|
||||
if (_currentState.ScaledSize != _scaledSize)
|
||||
{
|
||||
string targetSize = $"{_paddedSize.Width}:{_paddedSize.Height}";
|
||||
string format = string.Empty;
|
||||
foreach (IPixelFormat pixelFormat in _currentState.PixelFormat)
|
||||
{
|
||||
format = $":format={pixelFormat.FFmpegName}";
|
||||
}
|
||||
|
||||
scale = $"scale_npp={targetSize}{format}";
|
||||
}
|
||||
|
||||
return scale;
|
||||
}
|
||||
}
|
||||
|
||||
public override FrameState NextState(FrameState currentState) => currentState;
|
||||
}
|
||||
@@ -4,13 +4,16 @@ public class HardwareUploadFilter : BaseFilter
|
||||
{
|
||||
private readonly FFmpegState _ffmpegState;
|
||||
|
||||
public HardwareUploadFilter(FFmpegState ffmpegState) => _ffmpegState = ffmpegState;
|
||||
public HardwareUploadFilter(FFmpegState ffmpegState)
|
||||
{
|
||||
_ffmpegState = ffmpegState;
|
||||
}
|
||||
|
||||
public override string Filter => _ffmpegState.EncoderHardwareAccelerationMode switch
|
||||
{
|
||||
HardwareAccelerationMode.None => string.Empty,
|
||||
HardwareAccelerationMode.Nvenc => "hwupload_cuda",
|
||||
HardwareAccelerationMode.Qsv => "hwupload=extra_hw_frames=64",
|
||||
HardwareAccelerationMode.Qsv => $"hwupload=extra_hw_frames={_ffmpegState.QsvExtraHardwareFrames}",
|
||||
HardwareAccelerationMode.Vaapi => "format=nv12|vaapi,hwupload",
|
||||
_ => "hwupload"
|
||||
};
|
||||
|
||||
@@ -5,12 +5,17 @@ namespace ErsatzTV.FFmpeg.Filter.Qsv;
|
||||
public class DeinterlaceQsvFilter : BaseFilter
|
||||
{
|
||||
private readonly FrameState _currentState;
|
||||
private readonly int _extraHardwareFrames;
|
||||
|
||||
public DeinterlaceQsvFilter(FrameState currentState) => _currentState = currentState;
|
||||
public DeinterlaceQsvFilter(FrameState currentState, int extraHardwareFrames)
|
||||
{
|
||||
_currentState = currentState;
|
||||
_extraHardwareFrames = extraHardwareFrames;
|
||||
}
|
||||
|
||||
// deinterlace_qsv seems to only support nv12, not p010le
|
||||
public override string Filter => _currentState.FrameDataLocation == FrameDataLocation.Software
|
||||
? "format=nv12,hwupload=extra_hw_frames=64,deinterlace_qsv"
|
||||
? $"format=nv12,hwupload=extra_hw_frames={_extraHardwareFrames},deinterlace_qsv"
|
||||
: "deinterlace_qsv";
|
||||
|
||||
public override FrameState NextState(FrameState currentState)
|
||||
|
||||
@@ -10,13 +10,20 @@ public class ScaleQsvFilter : BaseFilter
|
||||
private readonly FrameState _currentState;
|
||||
private readonly FrameSize _scaledSize;
|
||||
private readonly FrameSize _paddedSize;
|
||||
private readonly int _extraHardwareFrames;
|
||||
|
||||
public ScaleQsvFilter(IRuntimeInfo runtimeInfo, FrameState currentState, FrameSize scaledSize, FrameSize paddedSize)
|
||||
public ScaleQsvFilter(
|
||||
IRuntimeInfo runtimeInfo,
|
||||
FrameState currentState,
|
||||
FrameSize scaledSize,
|
||||
FrameSize paddedSize,
|
||||
int extraHardwareFrames)
|
||||
{
|
||||
_runtimeInfo = runtimeInfo;
|
||||
_currentState = currentState;
|
||||
_scaledSize = scaledSize;
|
||||
_paddedSize = paddedSize;
|
||||
_extraHardwareFrames = extraHardwareFrames;
|
||||
}
|
||||
|
||||
public override string Filter
|
||||
@@ -57,10 +64,10 @@ public class ScaleQsvFilter : BaseFilter
|
||||
string initialPixelFormat = _currentState.PixelFormat.Match(pf => pf.FFmpegName, FFmpegFormat.NV12);
|
||||
if (!string.IsNullOrWhiteSpace(scale))
|
||||
{
|
||||
return $"format={initialPixelFormat},hwupload=extra_hw_frames=64,{scale}";
|
||||
return $"format={initialPixelFormat},hwupload=extra_hw_frames={_extraHardwareFrames},{scale}";
|
||||
}
|
||||
|
||||
return $"format={initialPixelFormat},hwupload=extra_hw_frames=64";
|
||||
return $"format={initialPixelFormat},hwupload=extra_hw_frames={_extraHardwareFrames}";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
using ErsatzTV.FFmpeg.Format;
|
||||
|
||||
namespace ErsatzTV.FFmpeg.Filter.Qsv;
|
||||
|
||||
public class SubtitleScaleQsvFilter : BaseFilter
|
||||
{
|
||||
private readonly FrameState _currentState;
|
||||
private readonly FrameSize _scaledSize;
|
||||
private readonly FrameSize _paddedSize;
|
||||
private readonly int _extraHardwareFrames;
|
||||
|
||||
public SubtitleScaleQsvFilter(
|
||||
FrameState currentState,
|
||||
FrameSize scaledSize,
|
||||
FrameSize paddedSize,
|
||||
int extraHardwareFrames)
|
||||
{
|
||||
_currentState = currentState;
|
||||
_scaledSize = scaledSize;
|
||||
_paddedSize = paddedSize;
|
||||
_extraHardwareFrames = extraHardwareFrames;
|
||||
}
|
||||
|
||||
public override string Filter
|
||||
{
|
||||
get
|
||||
{
|
||||
string scale = string.Empty;
|
||||
if (_currentState.ScaledSize != _scaledSize)
|
||||
{
|
||||
string targetSize = $"w={_paddedSize.Width}:h={_paddedSize.Height}";
|
||||
|
||||
// use software scaling
|
||||
scale = $"scale={targetSize}";
|
||||
}
|
||||
|
||||
string initialPixelFormat = _currentState.PixelFormat.Match(pf => pf.FFmpegName, FFmpegFormat.NV12);
|
||||
if (!string.IsNullOrWhiteSpace(scale))
|
||||
{
|
||||
return $"{scale},format={initialPixelFormat},hwupload=extra_hw_frames={_extraHardwareFrames}";
|
||||
}
|
||||
|
||||
return $"format={initialPixelFormat},hwupload=extra_hw_frames={_extraHardwareFrames}";
|
||||
}
|
||||
}
|
||||
|
||||
public override FrameState NextState(FrameState currentState) => currentState;
|
||||
}
|
||||
@@ -16,7 +16,7 @@ public class SubtitleHardwareUploadFilter : BaseFilter
|
||||
{
|
||||
HardwareAccelerationMode.None => string.Empty,
|
||||
HardwareAccelerationMode.Nvenc => "hwupload_cuda",
|
||||
HardwareAccelerationMode.Qsv => "hwupload=extra_hw_frames=64",
|
||||
HardwareAccelerationMode.Qsv => string.Empty,
|
||||
|
||||
// leave vaapi in software since we don't (yet) use overlay_vaapi
|
||||
HardwareAccelerationMode.Vaapi when _currentState.FrameDataLocation == FrameDataLocation.Software =>
|
||||
|
||||
@@ -6,20 +6,14 @@ public class SubtitlePixelFormatFilter : BaseFilter
|
||||
|
||||
public SubtitlePixelFormatFilter(FFmpegState ffmpegState) => _ffmpegState = ffmpegState;
|
||||
|
||||
public override string Filter
|
||||
{
|
||||
get
|
||||
{
|
||||
Option<string> maybeFormat = _ffmpegState.EncoderHardwareAccelerationMode switch
|
||||
{
|
||||
HardwareAccelerationMode.Nvenc => "yuva420p",
|
||||
HardwareAccelerationMode.Qsv => "yuva420p",
|
||||
_ => None
|
||||
};
|
||||
public override string Filter => MaybeFormat.Match(f => $"format={f}", () => string.Empty);
|
||||
|
||||
return maybeFormat.Match(f => $"format={f}", () => string.Empty);
|
||||
}
|
||||
}
|
||||
public Option<string> MaybeFormat =>_ffmpegState.EncoderHardwareAccelerationMode switch
|
||||
{
|
||||
HardwareAccelerationMode.Nvenc => "yuva420p",
|
||||
HardwareAccelerationMode.Qsv => "yuva420p",
|
||||
_ => None
|
||||
};
|
||||
|
||||
public override FrameState NextState(FrameState currentState) => currentState;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ public class WatermarkHardwareUploadFilter : BaseFilter
|
||||
{
|
||||
HardwareAccelerationMode.None => string.Empty,
|
||||
HardwareAccelerationMode.Nvenc => "hwupload_cuda",
|
||||
HardwareAccelerationMode.Qsv => "hwupload=extra_hw_frames=64",
|
||||
HardwareAccelerationMode.Qsv => $"hwupload=extra_hw_frames={_ffmpegState.QsvExtraHardwareFrames}",
|
||||
|
||||
// leave vaapi in software since we don't (yet) use overlay_vaapi
|
||||
HardwareAccelerationMode.Vaapi when _currentState.FrameDataLocation == FrameDataLocation.Software =>
|
||||
|
||||
@@ -11,6 +11,7 @@ public static class AvailablePixelFormats
|
||||
PixelFormat.YUV420P10LE => new PixelFormatYuv420P10Le(),
|
||||
PixelFormat.YUVJ420P => new PixelFormatYuvJ420P(),
|
||||
PixelFormat.YUV444P => new PixelFormatYuv444P(),
|
||||
PixelFormat.YUVA420P => new PixelFormatYuva420P(),
|
||||
_ => LogUnknownPixelFormat(pixelFormat, logger)
|
||||
};
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ public class FFmpegFormat
|
||||
{
|
||||
public const string YUV420P = "yuv420p";
|
||||
public const string YUV444P = "yuv444p";
|
||||
public const string YUVA420P = "yuva420p";
|
||||
public const string P010LE = "p010le";
|
||||
public const string NV12 = "nv12";
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ public static class PixelFormat
|
||||
{
|
||||
public const string YUV420P = "yuv420p";
|
||||
public const string YUV420P10LE = "yuv420p10le";
|
||||
public const string YUVA420P = "yuva420p";
|
||||
public const string YUVJ420P = "yuvj420p";
|
||||
public const string YUV444P = "yuv444p";
|
||||
public const string YUV444P10LE = "yuv444p10le";
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace ErsatzTV.FFmpeg.Format;
|
||||
|
||||
public class PixelFormatYuva420P : IPixelFormat
|
||||
{
|
||||
public string Name => PixelFormat.YUVA420P;
|
||||
public string FFmpegName => FFmpegFormat.YUVA420P;
|
||||
public int BitDepth => 8;
|
||||
}
|
||||
@@ -400,7 +400,8 @@ public class PipelineBuilder
|
||||
ffmpegState.EncoderHardwareAccelerationMode,
|
||||
currentState,
|
||||
desiredState.ScaledSize,
|
||||
desiredState.PaddedSize);
|
||||
desiredState.PaddedSize,
|
||||
ffmpegState.QsvExtraHardwareFrames);
|
||||
currentState = scaleFilter.NextState(currentState);
|
||||
_videoInputFile.Iter(f => f.FilterSteps.Add(scaleFilter));
|
||||
|
||||
@@ -427,7 +428,8 @@ public class PipelineBuilder
|
||||
ffmpegState.EncoderHardwareAccelerationMode,
|
||||
currentState,
|
||||
desiredState.ScaledSize,
|
||||
desiredState.PaddedSize);
|
||||
desiredState.PaddedSize,
|
||||
ffmpegState.QsvExtraHardwareFrames);
|
||||
currentState = scaleFilter.NextState(currentState);
|
||||
_videoInputFile.Iter(f => f.FilterSteps.Add(scaleFilter));
|
||||
|
||||
@@ -482,7 +484,8 @@ public class PipelineBuilder
|
||||
ffmpegState.EncoderHardwareAccelerationMode,
|
||||
currentState,
|
||||
desiredState.ScaledSize,
|
||||
desiredState.PaddedSize);
|
||||
desiredState.PaddedSize,
|
||||
ffmpegState.QsvExtraHardwareFrames);
|
||||
currentState = scaleFilter.NextState(currentState);
|
||||
_videoInputFile.Iter(f => f.FilterSteps.Add(scaleFilter));
|
||||
}
|
||||
@@ -512,7 +515,8 @@ public class PipelineBuilder
|
||||
ffmpegState.EncoderHardwareAccelerationMode,
|
||||
currentState,
|
||||
desiredState.ScaledSize,
|
||||
desiredState.PaddedSize);
|
||||
desiredState.PaddedSize,
|
||||
ffmpegState.QsvExtraHardwareFrames);
|
||||
currentState = scaleFilter.NextState(currentState);
|
||||
videoInputFile.FilterSteps.Add(scaleFilter);
|
||||
}
|
||||
@@ -619,9 +623,35 @@ public class PipelineBuilder
|
||||
_videoInputFile.Iter(f => f.FilterSteps.Add(downloadFilter));
|
||||
}
|
||||
|
||||
subtitleInputFile.FilterSteps.Add(new SubtitlePixelFormatFilter(ffmpegState));
|
||||
var pixelFormatFilter = new SubtitlePixelFormatFilter(ffmpegState);
|
||||
subtitleInputFile.FilterSteps.Add(pixelFormatFilter);
|
||||
|
||||
subtitleInputFile.FilterSteps.Add(new SubtitleHardwareUploadFilter(currentState, ffmpegState));
|
||||
|
||||
FrameState fakeState = currentState;
|
||||
foreach (string format in pixelFormatFilter.MaybeFormat)
|
||||
{
|
||||
fakeState = fakeState with
|
||||
{
|
||||
PixelFormat = AvailablePixelFormats.ForPixelFormat(format, _logger)
|
||||
};
|
||||
}
|
||||
|
||||
// hacky check for actual scaling or padding
|
||||
if (_videoInputFile.Exists(
|
||||
v => v.FilterSteps.Any(s => s.Filter.Contains(currentState.PaddedSize.Height.ToString()))))
|
||||
{
|
||||
// enable scaling the subtitle stream
|
||||
fakeState = fakeState with { ScaledSize = new FrameSize(1, 1) };
|
||||
}
|
||||
|
||||
IPipelineFilterStep scaleFilter = AvailableSubtitleScaleFilters.ForAcceleration(
|
||||
ffmpegState.EncoderHardwareAccelerationMode,
|
||||
fakeState,
|
||||
desiredState.ScaledSize,
|
||||
desiredState.PaddedSize,
|
||||
ffmpegState.QsvExtraHardwareFrames);
|
||||
subtitleInputFile.FilterSteps.Add(scaleFilter);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -639,7 +669,8 @@ public class PipelineBuilder
|
||||
ffmpegState.EncoderHardwareAccelerationMode,
|
||||
currentState,
|
||||
desiredState.ScaledSize,
|
||||
desiredState.PaddedSize);
|
||||
desiredState.PaddedSize,
|
||||
ffmpegState.QsvExtraHardwareFrames);
|
||||
currentState = scaleFilter.NextState(currentState);
|
||||
_videoInputFile.Iter(f => f.FilterSteps.Add(scaleFilter));
|
||||
}
|
||||
|
||||
@@ -70,4 +70,5 @@
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=videotoolbox/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=xmltv/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=yadif/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=YUVA/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=YUVJ/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
Reference in New Issue
Block a user