diff --git a/src/TeamsISO.Engine/Domain/EngineAlert.cs b/src/TeamsISO.Engine/Domain/EngineAlert.cs new file mode 100644 index 0000000..b9189cd --- /dev/null +++ b/src/TeamsISO.Engine/Domain/EngineAlert.cs @@ -0,0 +1,19 @@ +namespace TeamsISO.Engine.Domain; + +/// +/// Structured engine alerts for UI banner display and ops logging. +/// +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}"); +} diff --git a/src/TeamsISO.Engine/Domain/EngineConfig.cs b/src/TeamsISO.Engine/Domain/EngineConfig.cs new file mode 100644 index 0000000..6b37927 --- /dev/null +++ b/src/TeamsISO.Engine/Domain/EngineConfig.cs @@ -0,0 +1,9 @@ +namespace TeamsISO.Engine.Domain; + +public sealed record EngineConfig( + FrameProcessingSettings Global, + IReadOnlyList Assignments) +{ + public static readonly EngineConfig Default = + new(FrameProcessingSettings.Default, Array.Empty()); +} diff --git a/src/TeamsISO.Engine/Domain/FrameProcessingSettings.cs b/src/TeamsISO.Engine/Domain/FrameProcessingSettings.cs new file mode 100644 index 0000000..74c3cc8 --- /dev/null +++ b/src/TeamsISO.Engine/Domain/FrameProcessingSettings.cs @@ -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); + + /// Returns the framerate enum value as a numeric frames-per-second. + 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}") + }; + + /// Returns the resolution as (width, height). + 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}") + }; +} diff --git a/src/TeamsISO.Engine/Domain/IsoAssignment.cs b/src/TeamsISO.Engine/Domain/IsoAssignment.cs new file mode 100644 index 0000000..a62e0cd --- /dev/null +++ b/src/TeamsISO.Engine/Domain/IsoAssignment.cs @@ -0,0 +1,9 @@ +namespace TeamsISO.Engine.Domain; + +/// +/// Operator's intent for an ISO output. Persisted to config.json. +/// +public sealed record IsoAssignment( + Guid ParticipantId, + bool IsEnabled, + string? CustomOutputName); diff --git a/src/TeamsISO.Engine/Domain/IsoHealthStats.cs b/src/TeamsISO.Engine/Domain/IsoHealthStats.cs new file mode 100644 index 0000000..84130a0 --- /dev/null +++ b/src/TeamsISO.Engine/Domain/IsoHealthStats.cs @@ -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); +} diff --git a/src/TeamsISO.Engine/Domain/IsoOutput.cs b/src/TeamsISO.Engine/Domain/IsoOutput.cs new file mode 100644 index 0000000..4a660f4 --- /dev/null +++ b/src/TeamsISO.Engine/Domain/IsoOutput.cs @@ -0,0 +1,7 @@ +namespace TeamsISO.Engine.Domain; + +public sealed record IsoOutput( + Guid ParticipantId, + string EffectiveOutputName, + IsoHealthStats Stats, + IsoState State); diff --git a/src/TeamsISO.Engine/Domain/Participant.cs b/src/TeamsISO.Engine/Domain/Participant.cs new file mode 100644 index 0000000..2a38b67 --- /dev/null +++ b/src/TeamsISO.Engine/Domain/Participant.cs @@ -0,0 +1,12 @@ +namespace TeamsISO.Engine.Domain; + +/// +/// Operator-facing identity for a single human in the meeting. +/// Id is engine-assigned and stable across the rename heuristic. +/// +public sealed record Participant( + Guid Id, + string DisplayName, + NdiSource? CurrentSource, + DateTimeOffset FirstSeen, + DateTimeOffset LastSeen);