feat(domain): add Participant, IsoAssignment, IsoOutput, IsoHealthStats, FrameProcessingSettings, EngineConfig, EngineAlert
Some checks failed
CI / build-and-test (push) Failing after 22s

This commit is contained in:
Zac Gaetano 2026-05-07 15:11:32 +00:00
parent aaf3184a8e
commit 464f559576
7 changed files with 105 additions and 0 deletions

View file

@ -0,0 +1,19 @@
namespace TeamsISO.Engine.Domain;
/// <summary>
/// Structured engine alerts for UI banner display and ops logging.
/// </summary>
public abstract record EngineAlert(string Message)
{
public sealed record NdiRuntimeMismatch(string DetectedVersion, string ExpectedVersion)
: EngineAlert($"NDI runtime version mismatch: detected {DetectedVersion}, expected {ExpectedVersion}.");
public sealed record OutputNameCollision(string Name)
: EngineAlert($"Another TeamsISO instance on the LAN is emitting an output named '{Name}'.");
public sealed record PipelineError(Guid ParticipantId, string Reason)
: EngineAlert($"Pipeline {ParticipantId} entered Error: {Reason}");
public sealed record ConfigSaveFailed(string Reason)
: EngineAlert($"Failed to save configuration: {Reason}");
}

View file

@ -0,0 +1,9 @@
namespace TeamsISO.Engine.Domain;
public sealed record EngineConfig(
FrameProcessingSettings Global,
IReadOnlyList<IsoAssignment> Assignments)
{
public static readonly EngineConfig Default =
new(FrameProcessingSettings.Default, Array.Empty<IsoAssignment>());
}

View file

@ -0,0 +1,34 @@
namespace TeamsISO.Engine.Domain;
public sealed record FrameProcessingSettings(
TargetFramerate Framerate,
TargetResolution Resolution,
AspectMode Aspect,
AudioMode Audio)
{
public static readonly FrameProcessingSettings Default =
new(TargetFramerate.Fps30, TargetResolution.R1080p, AspectMode.Pillarbox, AudioMode.Auto);
/// <summary>Returns the framerate enum value as a numeric frames-per-second.</summary>
public double FramerateHz => Framerate switch
{
TargetFramerate.Fps23_976 => 24000.0 / 1001.0,
TargetFramerate.Fps24 => 24.0,
TargetFramerate.Fps25 => 25.0,
TargetFramerate.Fps29_97 => 30000.0 / 1001.0,
TargetFramerate.Fps30 => 30.0,
TargetFramerate.Fps50 => 50.0,
TargetFramerate.Fps59_94 => 60000.0 / 1001.0,
TargetFramerate.Fps60 => 60.0,
_ => throw new InvalidOperationException($"Unknown framerate: {Framerate}")
};
/// <summary>Returns the resolution as (width, height).</summary>
public (int Width, int Height) ResolutionSize => Resolution switch
{
TargetResolution.R720p => (1280, 720),
TargetResolution.R1080p => (1920, 1080),
TargetResolution.R4K => (3840, 2160),
_ => throw new InvalidOperationException($"Unknown resolution: {Resolution}")
};
}

View file

@ -0,0 +1,9 @@
namespace TeamsISO.Engine.Domain;
/// <summary>
/// Operator's intent for an ISO output. Persisted to <c>config.json</c>.
/// </summary>
public sealed record IsoAssignment(
Guid ParticipantId,
bool IsEnabled,
string? CustomOutputName);

View file

@ -0,0 +1,15 @@
namespace TeamsISO.Engine.Domain;
public sealed record IsoHealthStats(
long FramesIn,
long FramesOut,
long FramesDropped,
long FramesDuplicated,
DateTimeOffset? LastFrameAt,
double IncomingFps,
int IncomingWidth,
int IncomingHeight)
{
public static readonly IsoHealthStats Empty =
new(0, 0, 0, 0, null, 0, 0, 0);
}

View file

@ -0,0 +1,7 @@
namespace TeamsISO.Engine.Domain;
public sealed record IsoOutput(
Guid ParticipantId,
string EffectiveOutputName,
IsoHealthStats Stats,
IsoState State);

View file

@ -0,0 +1,12 @@
namespace TeamsISO.Engine.Domain;
/// <summary>
/// Operator-facing identity for a single human in the meeting.
/// <c>Id</c> is engine-assigned and stable across the rename heuristic.
/// </summary>
public sealed record Participant(
Guid Id,
string DisplayName,
NdiSource? CurrentSource,
DateTimeOffset FirstSeen,
DateTimeOffset LastSeen);