Browse Source

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
pull/951/head
Jason Dove 4 years ago committed by GitHub
parent
commit
9a4f772f53
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      CHANGELOG.md
  2. 4
      ErsatzTV.Core.Tests/FFmpeg/TranscodingTests.cs
  3. 6
      ErsatzTV.Core/FFmpeg/FFmpegLibraryProcessService.cs
  4. 4
      ErsatzTV.FFmpeg.Tests/PipelineBuilderTests.cs
  5. 6
      ErsatzTV.FFmpeg/Encoder/AvailableEncoders.cs
  6. 13
      ErsatzTV.FFmpeg/Encoder/Qsv/EncoderH264Qsv.cs
  7. 13
      ErsatzTV.FFmpeg/Encoder/Qsv/EncoderHevcQsv.cs
  8. 6
      ErsatzTV.FFmpeg/FFmpegState.cs
  9. 5
      ErsatzTV.FFmpeg/Filter/AvailableScaleFilters.cs
  10. 24
      ErsatzTV.FFmpeg/Filter/AvailableSubtitleScaleFilters.cs
  11. 40
      ErsatzTV.FFmpeg/Filter/Cuda/SubtitleScaleNppFilter.cs
  12. 7
      ErsatzTV.FFmpeg/Filter/HardwareUploadFilter.cs
  13. 9
      ErsatzTV.FFmpeg/Filter/Qsv/DeinterlaceQsvFilter.cs
  14. 13
      ErsatzTV.FFmpeg/Filter/Qsv/ScaleQsvFilter.cs
  15. 48
      ErsatzTV.FFmpeg/Filter/Qsv/SubtitleScaleQsvFilter.cs
  16. 2
      ErsatzTV.FFmpeg/Filter/SubtitleHardwareUploadFilter.cs
  17. 20
      ErsatzTV.FFmpeg/Filter/SubtitlePixelFormatFilter.cs
  18. 2
      ErsatzTV.FFmpeg/Filter/WatermarkHardwareUploadFilter.cs
  19. 1
      ErsatzTV.FFmpeg/Format/AvailablePixelFormats.cs
  20. 1
      ErsatzTV.FFmpeg/Format/FFmpegFormat.cs
  21. 1
      ErsatzTV.FFmpeg/Format/PixelFormat.cs
  22. 8
      ErsatzTV.FFmpeg/Format/PixelFormatYuva420P.cs
  23. 43
      ErsatzTV.FFmpeg/PipelineBuilder.cs
  24. 1
      ErsatzTV.sln.DotSettings

1
CHANGELOG.md

@ -11,6 +11,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). @@ -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

4
ErsatzTV.Core.Tests/FFmpeg/TranscodingTests.cs

@ -198,11 +198,11 @@ public class TranscodingTests @@ -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")
{

6
ErsatzTV.Core/FFmpeg/FFmpegLibraryProcessService.cs

@ -239,7 +239,8 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService @@ -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 @@ -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);

4
ErsatzTV.FFmpeg.Tests/PipelineBuilderTests.cs

@ -70,6 +70,7 @@ public class PipelineGeneratorTests @@ -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 @@ -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 @@ -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 @@ -304,6 +307,7 @@ public class PipelineGeneratorTests
Option<string>.None,
Option<string>.None,
0,
Option<int>.None,
Option<int>.None);
var builder = new PipelineBuilder(

6
ErsatzTV.FFmpeg/Encoder/AvailableEncoders.cs

@ -43,14 +43,16 @@ public static class AvailableEncoders @@ -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,

13
ErsatzTV.FFmpeg/Encoder/Qsv/EncoderH264Qsv.cs

@ -6,21 +6,26 @@ public class EncoderH264Qsv : EncoderBase @@ -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 @@ -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}";
}
}

13
ErsatzTV.FFmpeg/Encoder/Qsv/EncoderHevcQsv.cs

@ -6,21 +6,26 @@ public class EncoderHevcQsv : EncoderBase @@ -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 @@ -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}";
}
}

6
ErsatzTV.FFmpeg/FFmpegState.cs

@ -18,8 +18,11 @@ public record FFmpegState( @@ -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( @@ -37,5 +40,6 @@ public record FFmpegState(
Option<string>.None,
Option<string>.None,
0,
Option<int>.None,
Option<int>.None);
}

5
ErsatzTV.FFmpeg/Filter/AvailableScaleFilters.cs

@ -12,13 +12,14 @@ public static class AvailableScaleFilters @@ -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)
};

24
ErsatzTV.FFmpeg/Filter/AvailableSubtitleScaleFilters.cs

@ -0,0 +1,24 @@ @@ -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)
};
}

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

@ -0,0 +1,40 @@ @@ -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;
}

7
ErsatzTV.FFmpeg/Filter/HardwareUploadFilter.cs

@ -4,13 +4,16 @@ public class HardwareUploadFilter : BaseFilter @@ -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"
};

9
ErsatzTV.FFmpeg/Filter/Qsv/DeinterlaceQsvFilter.cs

@ -5,12 +5,17 @@ namespace ErsatzTV.FFmpeg.Filter.Qsv; @@ -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)

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

@ -10,13 +10,20 @@ public class ScaleQsvFilter : BaseFilter @@ -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 @@ -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}";
}
}

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

@ -0,0 +1,48 @@ @@ -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;
}

2
ErsatzTV.FFmpeg/Filter/SubtitleHardwareUploadFilter.cs

@ -16,7 +16,7 @@ public class SubtitleHardwareUploadFilter : BaseFilter @@ -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 =>

20
ErsatzTV.FFmpeg/Filter/SubtitlePixelFormatFilter.cs

@ -6,20 +6,14 @@ public class SubtitlePixelFormatFilter : BaseFilter @@ -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;
}

2
ErsatzTV.FFmpeg/Filter/WatermarkHardwareUploadFilter.cs

@ -15,7 +15,7 @@ public class WatermarkHardwareUploadFilter : BaseFilter @@ -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 =>

1
ErsatzTV.FFmpeg/Format/AvailablePixelFormats.cs

@ -11,6 +11,7 @@ public static class AvailablePixelFormats @@ -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)
};

1
ErsatzTV.FFmpeg/Format/FFmpegFormat.cs

@ -4,6 +4,7 @@ public class FFmpegFormat @@ -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";
}

1
ErsatzTV.FFmpeg/Format/PixelFormat.cs

@ -4,6 +4,7 @@ public static class PixelFormat @@ -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";

8
ErsatzTV.FFmpeg/Format/PixelFormatYuva420P.cs

@ -0,0 +1,8 @@ @@ -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;
}

43
ErsatzTV.FFmpeg/PipelineBuilder.cs

@ -400,7 +400,8 @@ public class PipelineBuilder @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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));
}

1
ErsatzTV.sln.DotSettings

@ -70,4 +70,5 @@ @@ -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>
Loading…
Cancel
Save