From 6a141c10311ede1ce1a7928fa97ac9a5b1f123d4 Mon Sep 17 00:00:00 2001 From: Zac Gaetano Date: Mon, 6 Jul 2026 21:26:09 -0400 Subject: [PATCH] feat(pipeline): pass the configured frame rate to each ISO NDI sender IsoPipeline now hands config.Settings.FrameRate (the exact rational) to the NdiSender it builds, completing the settings -> sender plumbing. Previously every output advertised 59.94 regardless of the operator-configured target. Co-Authored-By: Claude Fable 5 --- src/Dragon-ISO.Engine/Pipeline/IsoPipeline.cs | 3 ++- .../Pipeline/IsoPipelineTests.cs | 27 +++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/Dragon-ISO.Engine/Pipeline/IsoPipeline.cs b/src/Dragon-ISO.Engine/Pipeline/IsoPipeline.cs index a7f8dba..3a1e363 100644 --- a/src/Dragon-ISO.Engine/Pipeline/IsoPipeline.cs +++ b/src/Dragon-ISO.Engine/Pipeline/IsoPipeline.cs @@ -378,9 +378,10 @@ public sealed class IsoPipeline : IAsyncDisposable using var receiver = new NdiReceiver( interop, config.SourceName, rawWriter, loggerFactory.CreateLogger()); + var (frameRateN, frameRateD) = config.Settings.FrameRate; using var sender = new NdiSender( interop, config.OutputName, processedChannel.Reader, loggerFactory.CreateLogger(), - config.OutputGroups); + config.OutputGroups, frameRateN, frameRateD); var processor = new FrameProcessor( config.Settings, scaler, new SolidFrameRenderer(), diff --git a/src/tests/Dragon-ISO.Engine.Tests/Pipeline/IsoPipelineTests.cs b/src/tests/Dragon-ISO.Engine.Tests/Pipeline/IsoPipelineTests.cs index e9746ed..1d4a646 100644 --- a/src/tests/Dragon-ISO.Engine.Tests/Pipeline/IsoPipelineTests.cs +++ b/src/tests/Dragon-ISO.Engine.Tests/Pipeline/IsoPipelineTests.cs @@ -1,6 +1,7 @@ using Microsoft.Extensions.Logging.Abstractions; using DragonISO.Engine.Domain; using DragonISO.Engine.Pipeline; +using DragonISO.Engine.Tests.Fakes; namespace DragonISO.Engine.Tests.Pipeline; @@ -76,6 +77,32 @@ public class IsoPipelineTests await pipeline.StopAsync(); } + [Fact] + public async Task ProductionCtor_PassesConfiguredFrameRate_ToSender() + { + // The operator configures 29.97 in settings; the NDI sender for every ISO + // output must be created with that exact rational (30000/1001), not the + // hardwired 59.94 default. + var interop = new FakeNdiInterop(); + var config = new IsoPipelineConfig( + Guid.NewGuid(), + "Teams - Alice", + "Alice-ISO", + FrameProcessingSettings.Default with { Framerate = TargetFramerate.Fps29_97 }); + var pipeline = new IsoPipeline( + config, interop, new ManagedNearestNeighborFrameScaler(), new FakeFrameClock(), + FastBackoff(), NoDelay(), NullLoggerFactory.Instance); + + await pipeline.StartAsync(); + var deadline = DateTime.UtcNow.AddSeconds(2); + while (!interop.SenderFrameRates.ContainsKey("Alice-ISO") && DateTime.UtcNow < deadline) + await Task.Delay(10); + await pipeline.StopAsync(); + + interop.SenderFrameRates.Should().ContainKey("Alice-ISO"); + interop.SenderFrameRates["Alice-ISO"].Should().Be((30000, 1001)); + } + [Fact] public async Task Supervisor_FailsRepeatedly_TransitionsToError() {