From 7869f16573b9f8d29790a830f2bfc7960847170b Mon Sep 17 00:00:00 2001
From: Jason Dove <1695733+jasongdove@users.noreply.github.com>
Date: Sun, 3 May 2026 15:43:26 -0500
Subject: [PATCH] feat: use configured scaling behavior with next engine
(#2880)
* update dependencies
* feat: respect desired scaling behavior using next engine
---
.../Commands/StartFFmpegNextSessionHandler.cs | 6 +++
.../ErsatzTV.Core.Tests.csproj | 2 +-
ErsatzTV.Core/Next/Config/ChannelConfig.cs | 52 +++++++++++++++++++
.../ErsatzTV.FFmpeg.Tests.csproj | 2 +-
ErsatzTV.FFmpeg/ErsatzTV.FFmpeg.csproj | 2 +-
.../ErsatzTV.Infrastructure.Tests.csproj | 4 +-
.../ErsatzTV.Scanner.Tests.csproj | 4 +-
ErsatzTV.Tests/ErsatzTV.Tests.csproj | 4 +-
ErsatzTV/ErsatzTV.csproj | 2 +-
9 files changed, 68 insertions(+), 10 deletions(-)
diff --git a/ErsatzTV.Application/Streaming/Commands/StartFFmpegNextSessionHandler.cs b/ErsatzTV.Application/Streaming/Commands/StartFFmpegNextSessionHandler.cs
index 81bb2eaeb..dcc8d3db2 100644
--- a/ErsatzTV.Application/Streaming/Commands/StartFFmpegNextSessionHandler.cs
+++ b/ErsatzTV.Application/Streaming/Commands/StartFFmpegNextSessionHandler.cs
@@ -348,6 +348,12 @@ public class StartFFmpegNextSessionHandler(
Width = ffmpegProfile.Resolution.Width,
BitrateKbps = ffmpegProfile.VideoBitrate,
BufferKbps = ffmpegProfile.VideoBufferSize,
+ ScalingMode = ffmpegProfile.ScalingBehavior switch
+ {
+ ScalingBehavior.Stretch => ScalingMode.Stretch,
+ ScalingBehavior.Crop => ScalingMode.Crop,
+ _ => ScalingMode.ScaleAndPad
+ },
// TODO: NEXT: more tonemap algorithms
TonemapAlgorithm = "linear",
VaapiDevice = ffmpegProfile.VaapiDevice,
diff --git a/ErsatzTV.Core.Tests/ErsatzTV.Core.Tests.csproj b/ErsatzTV.Core.Tests/ErsatzTV.Core.Tests.csproj
index 20287e7a8..dba59bfc2 100644
--- a/ErsatzTV.Core.Tests/ErsatzTV.Core.Tests.csproj
+++ b/ErsatzTV.Core.Tests/ErsatzTV.Core.Tests.csproj
@@ -16,7 +16,7 @@
-
+
diff --git a/ErsatzTV.Core/Next/Config/ChannelConfig.cs b/ErsatzTV.Core/Next/Config/ChannelConfig.cs
index 7b7eda95f..b9351939d 100644
--- a/ErsatzTV.Core/Next/Config/ChannelConfig.cs
+++ b/ErsatzTV.Core/Next/Config/ChannelConfig.cs
@@ -119,6 +119,9 @@ namespace ErsatzTV.Core.Next.Config
[JsonProperty("height")]
public long? Height { get; set; }
+ [JsonProperty("scaling_mode", NullValueHandling = NullValueHandling.Ignore)]
+ public ScalingMode? ScalingMode { get; set; }
+
[JsonProperty("tonemap_algorithm")]
public string TonemapAlgorithm { get; set; }
@@ -152,6 +155,8 @@ namespace ErsatzTV.Core.Next.Config
public enum VideoFormat { H264, Hevc };
+ public enum ScalingMode { Crop, ScaleAndPad, Stretch };
+
public enum VaapiDriverEnum { I965, Ihd, Radeonsi };
public partial class ChannelConfig
@@ -176,6 +181,7 @@ namespace ErsatzTV.Core.Next.Config
ModeConverter.Singleton,
AccelEnumConverter.Singleton,
VideoFormatConverter.Singleton,
+ ScalingModeConverter.Singleton,
VaapiDriverEnumConverter.Singleton,
new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }
},
@@ -361,6 +367,52 @@ namespace ErsatzTV.Core.Next.Config
public static readonly VideoFormatConverter Singleton = new VideoFormatConverter();
}
+ internal class ScalingModeConverter : JsonConverter
+ {
+ public override bool CanConvert(Type t) => t == typeof(ScalingMode) || t == typeof(ScalingMode?);
+
+ public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
+ {
+ if (reader.TokenType == JsonToken.Null) return null;
+ var value = serializer.Deserialize(reader);
+ switch (value)
+ {
+ case "crop":
+ return ScalingMode.Crop;
+ case "scale_and_pad":
+ return ScalingMode.ScaleAndPad;
+ case "stretch":
+ return ScalingMode.Stretch;
+ }
+ throw new Exception("Cannot unmarshal type ScalingMode");
+ }
+
+ public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
+ {
+ if (untypedValue == null)
+ {
+ serializer.Serialize(writer, null);
+ return;
+ }
+ var value = (ScalingMode)untypedValue;
+ switch (value)
+ {
+ case ScalingMode.Crop:
+ serializer.Serialize(writer, "crop");
+ return;
+ case ScalingMode.ScaleAndPad:
+ serializer.Serialize(writer, "scale_and_pad");
+ return;
+ case ScalingMode.Stretch:
+ serializer.Serialize(writer, "stretch");
+ return;
+ }
+ throw new Exception("Cannot marshal type ScalingMode");
+ }
+
+ public static readonly ScalingModeConverter Singleton = new ScalingModeConverter();
+ }
+
internal class VaapiDriverEnumConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(VaapiDriverEnum) || t == typeof(VaapiDriverEnum?);
diff --git a/ErsatzTV.FFmpeg.Tests/ErsatzTV.FFmpeg.Tests.csproj b/ErsatzTV.FFmpeg.Tests/ErsatzTV.FFmpeg.Tests.csproj
index 9c7508d5c..5d7dee790 100644
--- a/ErsatzTV.FFmpeg.Tests/ErsatzTV.FFmpeg.Tests.csproj
+++ b/ErsatzTV.FFmpeg.Tests/ErsatzTV.FFmpeg.Tests.csproj
@@ -12,7 +12,7 @@
-
+
diff --git a/ErsatzTV.FFmpeg/ErsatzTV.FFmpeg.csproj b/ErsatzTV.FFmpeg/ErsatzTV.FFmpeg.csproj
index 78f7ca0a1..d89318fd2 100644
--- a/ErsatzTV.FFmpeg/ErsatzTV.FFmpeg.csproj
+++ b/ErsatzTV.FFmpeg/ErsatzTV.FFmpeg.csproj
@@ -14,7 +14,7 @@
-
+
diff --git a/ErsatzTV.Infrastructure.Tests/ErsatzTV.Infrastructure.Tests.csproj b/ErsatzTV.Infrastructure.Tests/ErsatzTV.Infrastructure.Tests.csproj
index b77128c8a..6b0f7dc5e 100644
--- a/ErsatzTV.Infrastructure.Tests/ErsatzTV.Infrastructure.Tests.csproj
+++ b/ErsatzTV.Infrastructure.Tests/ErsatzTV.Infrastructure.Tests.csproj
@@ -11,9 +11,9 @@
-
+
-
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
diff --git a/ErsatzTV.Scanner.Tests/ErsatzTV.Scanner.Tests.csproj b/ErsatzTV.Scanner.Tests/ErsatzTV.Scanner.Tests.csproj
index 9d591d668..8bb380de5 100644
--- a/ErsatzTV.Scanner.Tests/ErsatzTV.Scanner.Tests.csproj
+++ b/ErsatzTV.Scanner.Tests/ErsatzTV.Scanner.Tests.csproj
@@ -12,9 +12,9 @@
-
+
-
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
diff --git a/ErsatzTV.Tests/ErsatzTV.Tests.csproj b/ErsatzTV.Tests/ErsatzTV.Tests.csproj
index 4599a96c9..3d6148f94 100644
--- a/ErsatzTV.Tests/ErsatzTV.Tests.csproj
+++ b/ErsatzTV.Tests/ErsatzTV.Tests.csproj
@@ -11,8 +11,8 @@
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/ErsatzTV/ErsatzTV.csproj b/ErsatzTV/ErsatzTV.csproj
index 10b7f08e1..20b03f4db 100644
--- a/ErsatzTV/ErsatzTV.csproj
+++ b/ErsatzTV/ErsatzTV.csproj
@@ -55,7 +55,7 @@
-
+