Browse Source

implement optional color normalization

pull/2802/head
Jason Dove 6 months ago
parent
commit
601409b09d
No known key found for this signature in database
  1. 2
      ErsatzTV.Core/FFmpeg/FFmpegLibraryProcessService.cs
  2. 1
      ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettings.cs
  3. 5
      ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettingsCalculator.cs
  4. 8
      ErsatzTV.FFmpeg.Tests/PipelineBuilderBaseTests.cs
  5. 1
      ErsatzTV.FFmpeg/FrameState.cs
  6. 6
      ErsatzTV.FFmpeg/Pipeline/AmfPipelineBuilder.cs
  7. 8
      ErsatzTV.FFmpeg/Pipeline/NvidiaPipelineBuilder.cs
  8. 8
      ErsatzTV.FFmpeg/Pipeline/QsvPipelineBuilder.cs
  9. 6
      ErsatzTV.FFmpeg/Pipeline/RkmppPipelineBuilder.cs
  10. 8
      ErsatzTV.FFmpeg/Pipeline/SoftwarePipelineBuilder.cs
  11. 6
      ErsatzTV.FFmpeg/Pipeline/V4l2m2mPipelineBuilder.cs
  12. 14
      ErsatzTV.FFmpeg/Pipeline/VaapiPipelineBuilder.cs
  13. 6
      ErsatzTV.FFmpeg/Pipeline/VideoToolboxPipelineBuilder.cs

2
ErsatzTV.Core/FFmpeg/FFmpegLibraryProcessService.cs

@ -517,6 +517,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService @@ -517,6 +517,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
playbackSettings.VideoBitrate,
playbackSettings.VideoBufferSize,
playbackSettings.VideoTrackTimeScale,
playbackSettings.NormalizeColors,
playbackSettings.Deinterlace);
// only use graphics engine when we have elements, and are normalizing video
@ -722,6 +723,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService @@ -722,6 +723,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
playbackSettings.VideoBitrate,
playbackSettings.VideoBufferSize,
playbackSettings.VideoTrackTimeScale,
playbackSettings.NormalizeColors,
playbackSettings.Deinterlace);
OutputFormatKind outputFormat = OutputFormatKind.MpegTs;

1
ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettings.cs

@ -31,4 +31,5 @@ public class FFmpegPlaybackSettings @@ -31,4 +31,5 @@ public class FFmpegPlaybackSettings
public NormalizeLoudnessMode NormalizeLoudnessMode { get; set; }
public Option<double> TargetLoudness { get; set; }
public Option<FrameRate> FrameRate { get; set; }
public bool NormalizeColors { get; set; }
}

5
ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettingsCalculator.cs

@ -118,6 +118,11 @@ public static class FFmpegPlaybackSettingsCalculator @@ -118,6 +118,11 @@ public static class FFmpegPlaybackSettingsCalculator
result.FrameRate = targetFramerate;
}
if (ffmpegProfile.NormalizeColors)
{
result.NormalizeColors = true;
}
result.VideoTrackTimeScale = 90000;
foreach (MediaStream stream in videoStream.Where(s => !s.AttachedPic))

8
ErsatzTV.FFmpeg.Tests/PipelineBuilderBaseTests.cs

@ -73,6 +73,7 @@ public class PipelineBuilderBaseTests @@ -73,6 +73,7 @@ public class PipelineBuilderBaseTests
2000,
4000,
90_000,
false,
false);
var ffmpegState = new FFmpegState(
@ -175,6 +176,7 @@ public class PipelineBuilderBaseTests @@ -175,6 +176,7 @@ public class PipelineBuilderBaseTests
2000,
4000,
90_000,
false,
false);
var ffmpegState = new FFmpegState(
@ -335,6 +337,7 @@ public class PipelineBuilderBaseTests @@ -335,6 +337,7 @@ public class PipelineBuilderBaseTests
2000,
4000,
90_000,
false,
false);
var ffmpegState = new FFmpegState(
@ -387,7 +390,7 @@ public class PipelineBuilderBaseTests @@ -387,7 +390,7 @@ public class PipelineBuilderBaseTests
// 0.4.0 reference: "-nostdin -threads 1 -hide_banner -loglevel error -nostats -fflags +genpts+discardcorrupt+igndts -re -ss 00:14:33.6195516 -i /tmp/whatever.mkv -map 0:0 -map 0:a -c:v copy -flags cgop -sc_threshold 0 -c:a copy -movflags +faststart -metadata service_provider="ErsatzTV" -metadata service_name="ErsatzTV" -t 00:06:39.6934484 -f mpegts -mpegts_flags +initial_discontinuity pipe:1"
command.ShouldBe(
"-nostdin -hide_banner -nostats -loglevel error -fflags +genpts+discardcorrupt+igndts -readrate 1.0 -i /tmp/whatever.mkv -map 0:0 -map 0:1 -muxdelay 0 -muxpreload 0 -movflags +faststart+frag_keyframe+separate_moof+omit_tfhd_offset+empty_moov+delay_moov -flags cgop -sc_threshold 0 -c:v copy -c:a copy -f mp4 pipe:1");
"-nostdin -hide_banner -nostats -loglevel error -fflags +genpts+discardcorrupt+igndts -readrate 1.0 -i /tmp/whatever.mkv -map 0:0 -map 0:1 -muxdelay 0 -muxpreload 0 -movflags +faststart+frag_keyframe+separate_moof+omit_tfhd_offset+empty_moov+delay_moov -c:v copy -c:a copy -f mp4 pipe:1");
}
[Test]
@ -430,6 +433,7 @@ public class PipelineBuilderBaseTests @@ -430,6 +433,7 @@ public class PipelineBuilderBaseTests
2000,
4000,
90_000,
false,
false);
var ffmpegState = new FFmpegState(
@ -480,7 +484,7 @@ public class PipelineBuilderBaseTests @@ -480,7 +484,7 @@ public class PipelineBuilderBaseTests
string command = PrintCommand(videoInputFile, audioInputFile, None, None, None, result);
command.ShouldBe(
"-nostdin -hide_banner -nostats -loglevel error -fflags +genpts+discardcorrupt+igndts -readrate 1.0 -i /tmp/whatever.mkv -map 0:0 -map 0:a -muxdelay 0 -muxpreload 0 -movflags +faststart+frag_keyframe+separate_moof+omit_tfhd_offset+empty_moov+delay_moov -flags cgop -sc_threshold 0 -c:v copy -c:a copy -f mp4 pipe:1");
"-nostdin -hide_banner -nostats -loglevel error -fflags +genpts+discardcorrupt+igndts -readrate 1.0 -i /tmp/whatever.mkv -map 0:0 -map 0:a -muxdelay 0 -muxpreload 0 -movflags +faststart+frag_keyframe+separate_moof+omit_tfhd_offset+empty_moov+delay_moov -c:v copy -c:a copy -f mp4 pipe:1");
}
[Test]

1
ErsatzTV.FFmpeg/FrameState.cs

@ -19,6 +19,7 @@ public record FrameState( @@ -19,6 +19,7 @@ public record FrameState(
Option<int> VideoBitrate,
Option<int> VideoBufferSize,
Option<int> VideoTrackTimeScale,
bool ColorsAreBt709,
bool Deinterlaced,
FrameDataLocation FrameDataLocation = FrameDataLocation.Unknown)
{

6
ErsatzTV.FFmpeg/Pipeline/AmfPipelineBuilder.cs

@ -94,15 +94,15 @@ public class AmfPipelineBuilder : SoftwarePipelineBuilder @@ -94,15 +94,15 @@ public class AmfPipelineBuilder : SoftwarePipelineBuilder
protected override List<IPipelineFilterStep> SetPixelFormat(
VideoStream videoStream,
Option<IPixelFormat> desiredPixelFormat,
FrameState desiredState,
FrameState currentState,
ICollection<IPipelineStep> pipelineSteps)
{
var result = new List<IPipelineFilterStep>();
foreach (IPixelFormat pixelFormat in desiredPixelFormat)
foreach (IPixelFormat pixelFormat in desiredState.PixelFormat)
{
if (!videoStream.ColorParams.IsBt709)
if (desiredState.ColorsAreBt709 && !videoStream.ColorParams.IsBt709)
{
// _logger.LogDebug("Adding colorspace filter");
var colorspace = new ColorspaceFilter(currentState, videoStream, pixelFormat);

8
ErsatzTV.FFmpeg/Pipeline/NvidiaPipelineBuilder.cs

@ -347,7 +347,7 @@ public class NvidiaPipelineBuilder : SoftwarePipelineBuilder @@ -347,7 +347,7 @@ public class NvidiaPipelineBuilder : SoftwarePipelineBuilder
List<IPipelineFilterStep> pixelFormatFilterSteps = SetPixelFormat(
videoStream,
desiredState.PixelFormat,
desiredState,
ffmpegState,
currentState,
context,
@ -392,7 +392,7 @@ public class NvidiaPipelineBuilder : SoftwarePipelineBuilder @@ -392,7 +392,7 @@ public class NvidiaPipelineBuilder : SoftwarePipelineBuilder
private List<IPipelineFilterStep> SetPixelFormat(
VideoStream videoStream,
Option<IPixelFormat> desiredPixelFormat,
FrameState desiredState,
FFmpegState ffmpegState,
FrameState currentState,
PipelineContext context,
@ -400,7 +400,7 @@ public class NvidiaPipelineBuilder : SoftwarePipelineBuilder @@ -400,7 +400,7 @@ public class NvidiaPipelineBuilder : SoftwarePipelineBuilder
{
var result = new List<IPipelineFilterStep>();
foreach (IPixelFormat pixelFormat in desiredPixelFormat)
foreach (IPixelFormat pixelFormat in desiredState.PixelFormat)
{
IPixelFormat format = pixelFormat;
@ -419,7 +419,7 @@ public class NvidiaPipelineBuilder : SoftwarePipelineBuilder @@ -419,7 +419,7 @@ public class NvidiaPipelineBuilder : SoftwarePipelineBuilder
videoStream.ResetColorParams(ColorParams.Unknown);
}
if (!videoStream.ColorParams.IsBt709)
if (desiredState.ColorsAreBt709 && !videoStream.ColorParams.IsBt709)
{
// _logger.LogDebug("Adding colorspace filter");
var colorspace = new ColorspaceFilter(currentState, videoStream, format);

8
ErsatzTV.FFmpeg/Pipeline/QsvPipelineBuilder.cs

@ -259,7 +259,7 @@ public class QsvPipelineBuilder : SoftwarePipelineBuilder @@ -259,7 +259,7 @@ public class QsvPipelineBuilder : SoftwarePipelineBuilder
List<IPipelineFilterStep> pixelFormatFilterSteps = SetPixelFormat(
videoInputFile,
videoStream,
desiredState.PixelFormat,
desiredState,
ffmpegState,
currentState,
context,
@ -279,7 +279,7 @@ public class QsvPipelineBuilder : SoftwarePipelineBuilder @@ -279,7 +279,7 @@ public class QsvPipelineBuilder : SoftwarePipelineBuilder
private List<IPipelineFilterStep> SetPixelFormat(
VideoInputFile videoInputFile,
VideoStream videoStream,
Option<IPixelFormat> desiredPixelFormat,
FrameState desiredState,
FFmpegState ffmpegState,
FrameState currentState,
PipelineContext context,
@ -287,7 +287,7 @@ public class QsvPipelineBuilder : SoftwarePipelineBuilder @@ -287,7 +287,7 @@ public class QsvPipelineBuilder : SoftwarePipelineBuilder
{
var result = new List<IPipelineFilterStep>();
foreach (IPixelFormat pixelFormat in desiredPixelFormat)
foreach (IPixelFormat pixelFormat in desiredState.PixelFormat)
{
IPixelFormat format = pixelFormat;
@ -352,7 +352,7 @@ public class QsvPipelineBuilder : SoftwarePipelineBuilder @@ -352,7 +352,7 @@ public class QsvPipelineBuilder : SoftwarePipelineBuilder
}
}
if (!videoStream.ColorParams.IsBt709 || usesVppQsv)
if (desiredState.ColorsAreBt709 && (!videoStream.ColorParams.IsBt709 || usesVppQsv))
{
// _logger.LogDebug("Adding colorspace filter");

6
ErsatzTV.FFmpeg/Pipeline/RkmppPipelineBuilder.cs

@ -126,15 +126,15 @@ public class RkmppPipelineBuilder : SoftwarePipelineBuilder @@ -126,15 +126,15 @@ public class RkmppPipelineBuilder : SoftwarePipelineBuilder
protected override List<IPipelineFilterStep> SetPixelFormat(
VideoStream videoStream,
Option<IPixelFormat> desiredPixelFormat,
FrameState desiredState,
FrameState currentState,
ICollection<IPipelineStep> pipelineSteps)
{
var result = new List<IPipelineFilterStep>();
foreach (IPixelFormat pixelFormat in desiredPixelFormat)
foreach (IPixelFormat pixelFormat in desiredState.PixelFormat)
{
if (!videoStream.ColorParams.IsBt709)
if (desiredState.ColorsAreBt709 && !videoStream.ColorParams.IsBt709)
{
// _logger.LogDebug("Adding colorspace filter");
var colorspace = new ColorspaceFilter(currentState, videoStream, pixelFormat);

8
ErsatzTV.FFmpeg/Pipeline/SoftwarePipelineBuilder.cs

@ -152,7 +152,7 @@ public class SoftwarePipelineBuilder : PipelineBuilderBase @@ -152,7 +152,7 @@ public class SoftwarePipelineBuilder : PipelineBuilderBase
List<IPipelineFilterStep> pixelFormatFilterSteps = SetPixelFormat(
videoStream,
desiredState.PixelFormat,
desiredState,
currentState,
pipelineSteps);
@ -169,15 +169,15 @@ public class SoftwarePipelineBuilder : PipelineBuilderBase @@ -169,15 +169,15 @@ public class SoftwarePipelineBuilder : PipelineBuilderBase
protected virtual List<IPipelineFilterStep> SetPixelFormat(
VideoStream videoStream,
Option<IPixelFormat> desiredPixelFormat,
FrameState desiredState,
FrameState currentState,
ICollection<IPipelineStep> pipelineSteps)
{
var result = new List<IPipelineFilterStep>();
foreach (IPixelFormat pixelFormat in desiredPixelFormat)
foreach (IPixelFormat pixelFormat in desiredState.PixelFormat)
{
if (!videoStream.ColorParams.IsBt709)
if (desiredState.ColorsAreBt709 && !videoStream.ColorParams.IsBt709)
{
// _logger.LogDebug("Adding colorspace filter");
var colorspace = new ColorspaceFilter(currentState, videoStream, pixelFormat);

6
ErsatzTV.FFmpeg/Pipeline/V4l2m2mPipelineBuilder.cs

@ -124,15 +124,15 @@ public class V4l2m2mPipelineBuilder : SoftwarePipelineBuilder @@ -124,15 +124,15 @@ public class V4l2m2mPipelineBuilder : SoftwarePipelineBuilder
protected override List<IPipelineFilterStep> SetPixelFormat(
VideoStream videoStream,
Option<IPixelFormat> desiredPixelFormat,
FrameState desiredState,
FrameState currentState,
ICollection<IPipelineStep> pipelineSteps)
{
var result = new List<IPipelineFilterStep>();
foreach (IPixelFormat pixelFormat in desiredPixelFormat)
foreach (IPixelFormat pixelFormat in desiredState.PixelFormat)
{
if (!videoStream.ColorParams.IsBt709)
if (desiredState.ColorsAreBt709 && !videoStream.ColorParams.IsBt709)
{
// _logger.LogDebug("Adding colorspace filter");
var colorspace = new ColorspaceFilter(currentState, videoStream, pixelFormat);

14
ErsatzTV.FFmpeg/Pipeline/VaapiPipelineBuilder.cs

@ -286,7 +286,7 @@ public class VaapiPipelineBuilder : SoftwarePipelineBuilder @@ -286,7 +286,7 @@ public class VaapiPipelineBuilder : SoftwarePipelineBuilder
List<IPipelineFilterStep> pixelFormatFilterSteps = SetPixelFormat(
videoStream,
desiredState.PixelFormat,
desiredState,
ffmpegState,
currentState,
pipelineSteps);
@ -313,14 +313,14 @@ public class VaapiPipelineBuilder : SoftwarePipelineBuilder @@ -313,14 +313,14 @@ public class VaapiPipelineBuilder : SoftwarePipelineBuilder
private List<IPipelineFilterStep> SetPixelFormat(
VideoStream videoStream,
Option<IPixelFormat> desiredPixelFormat,
FrameState desiredState,
FFmpegState ffmpegState,
FrameState currentState,
ICollection<IPipelineStep> pipelineSteps)
{
var result = new List<IPipelineFilterStep>();
foreach (IPixelFormat pixelFormat in desiredPixelFormat)
foreach (IPixelFormat pixelFormat in desiredState.PixelFormat)
{
IPixelFormat format = pixelFormat;
@ -332,7 +332,7 @@ public class VaapiPipelineBuilder : SoftwarePipelineBuilder @@ -332,7 +332,7 @@ public class VaapiPipelineBuilder : SoftwarePipelineBuilder
}
}
if (!videoStream.ColorParams.IsBt709)
if (desiredState.ColorsAreBt709 && !videoStream.ColorParams.IsBt709)
{
// _logger.LogDebug("Adding colorspace filter");
var colorspace = new ColorspaceFilter(
@ -352,12 +352,12 @@ public class VaapiPipelineBuilder : SoftwarePipelineBuilder @@ -352,12 +352,12 @@ public class VaapiPipelineBuilder : SoftwarePipelineBuilder
_logger.LogDebug(
"FrameDataLocation == FrameDataLocation.Hardware, {CurrentPixelFormat} bit => {DesiredPixelFormat}",
currentState.PixelFormat,
desiredPixelFormat);
desiredState.PixelFormat);
// don't try to download from 8-bit to 10-bit, or 10-bit to 8-bit
HardwareDownloadFilter hardwareDownload =
currentState.BitDepth == 8 && desiredPixelFormat.Map(pf => pf.BitDepth).IfNone(8) == 10 ||
currentState.BitDepth == 10 && desiredPixelFormat.Map(pf => pf.BitDepth).IfNone(10) == 8
currentState.BitDepth == 8 && desiredState.PixelFormat.Map(pf => pf.BitDepth).IfNone(8) == 10 ||
currentState.BitDepth == 10 && desiredState.PixelFormat.Map(pf => pf.BitDepth).IfNone(10) == 8
? new HardwareDownloadFilter(currentState)
: new HardwareDownloadFilter(currentState with { PixelFormat = Some(format) });

6
ErsatzTV.FFmpeg/Pipeline/VideoToolboxPipelineBuilder.cs

@ -123,15 +123,15 @@ public class VideoToolboxPipelineBuilder : SoftwarePipelineBuilder @@ -123,15 +123,15 @@ public class VideoToolboxPipelineBuilder : SoftwarePipelineBuilder
protected override List<IPipelineFilterStep> SetPixelFormat(
VideoStream videoStream,
Option<IPixelFormat> desiredPixelFormat,
FrameState desiredState,
FrameState currentState,
ICollection<IPipelineStep> pipelineSteps)
{
var result = new List<IPipelineFilterStep>();
foreach (IPixelFormat pixelFormat in desiredPixelFormat)
foreach (IPixelFormat pixelFormat in desiredState.PixelFormat)
{
if (!videoStream.ColorParams.IsBt709)
if (desiredState.ColorsAreBt709 && !videoStream.ColorParams.IsBt709)
{
// _logger.LogDebug("Adding colorspace filter");
var colorspace = new ColorspaceFilter(currentState, videoStream, pixelFormat);

Loading…
Cancel
Save