Commit graph

25 commits

Author SHA1 Message Date
9c231118de feat(stats): wire IsoHealthStats end-to-end and surface live counters in UI
IsoPipeline now publishes refs to its currently-live NdiReceiver and NdiSender (set by RunInnerPipelineAsync, cleared on exit) so a stats poll from any thread can read FramesCaptured / FramesSent without entangling the pipeline's lifetime with its observer. The receiver's raw-frame channel is wrapped with a TappedChannelWriter so the most recent RawFrame is captured for source-resolution display, again without changing the receiver's contract.

IsoController.GetStats() drops the stub return-Empty and instead reads the live pipeline.GetStats() outside the gate so a slow stats read can't serialize the controller's other operations.

WPF: MainViewModel runs a 1 Hz DispatcherTimer that pulls stats for every participant view-model and pushes them via UpdateStats(). ParticipantViewModel grows three displayable properties — FramesIn, FramesOut, IncomingResolution — bound into the participants DataGrid as a new 'Live' column showing the down/up frame counts and the source resolution underneath the machine name.

Tests: 74/74 unit + 9/9 NDI integration green; the existing round-trip integration test exercises the new wiring at runtime (live receiver/sender refs are set, frames flow, channels close cleanly).
2026-05-08 00:52:44 -04:00
1d85396a90 feat(logging): rolling file sink under %LOCALAPPDATA%\\TeamsISO\\Logs
Adds Serilog.Sinks.File to TeamsISO.Engine and a new EngineLogging.CreateDefault() factory that writes to BOTH the existing console sink and a rolling daily file at %LOCALAPPDATA%\\TeamsISO\\Logs\\teamsiso<date>.log. The WPF host (TeamsISO.exe is a WinExe with no console attached at runtime) now uses CreateDefault so support has something to ask for when users file an issue. The Console build keeps using CreateConsole — stdout is the right surface there and shell redirection beats a competing on-disk sink.

Files roll daily, cap at 10 MB before mid-day rollover, and only the most recent 14 are retained. Disk flush interval is 250 ms so a tail -f from another tool sees lines promptly. Path is announced via the first log line on every startup.

Two unit tests gate the wiring: AllLoggers_WriteToFile (verifies both typed and named CreateLogger() reach the file) and LogsAtBelowMinimumLevel_AreSuppressed (regression guard for level filtering). 74/74 unit tests pass (was 72).

Also adds a startup breadcrumb log line in App.OnStartup carrying the build version + PID so we can correlate a user's log file with a specific commit.
2026-05-08 00:47:25 -04:00
909237f454 feat(ndi): plumb NDI groups (discovery + output) through the engine
Adds an NdiGroupSettings record carrying optional comma-separated NDI group lists for the finder and the senders. Extends INdiInterop.CreateFinder / CreateSender with optional groups arguments and populates NDIlib_find_create_t.p_groups and NDIlib_send_create_t.p_groups via P/Invoke. IsoController reads the settings on construction, threads DiscoveryGroups into NdiDiscoveryService and OutputGroups into IsoPipelineConfig, and exposes SetGroupSettingsAsync for runtime updates (group changes apply on next process restart so live pipelines aren't orphaned).

This unblocks the 'transcoder' topology where Teams broadcasts NDI on a private group (e.g. teamsiso-input) and TeamsISO re-emits clean normalized streams on Public — keeping raw, wrong-framerate Teams sources off the production network.

EngineConfig schema is JSON-back-compat: existing config.json files (no NdiGroups field) deserialize with NdiGroups=null and load as NdiGroupSettings.Default. UI surface for these settings comes in a follow-up.

Tests: 72/72 passing (was 69) — added IsoController coverage that group settings are read from ConfigStore on startup, passed to the finder, threaded into per-pipeline config, and round-trip through SetGroupSettingsAsync/Save/Load.
2026-05-07 23:48:49 -04:00
ca124540a7 fix(parser): accept 'MS Teams' brand prefix from current Teams NDI broadcasts
The new Microsoft Teams desktop client (observed against a live meeting on Teams 26106.1906.4665.7308) emits NDI source strings of the form

    WOOGLIN (MS Teams - Brendon Power)

    WOOGLIN (MS Teams - (Local))

    WOOGLIN (MS Teams - Active Speaker)

rather than the legacy 'MACHINE (Teams - ...)' shape NdiSourceParser was written to. As a result every Teams source was rejected and TeamsISO showed zero participants in real meetings.

Refactor the parser to recognize 'Teams', 'MS Teams', and (defensively) 'Microsoft Teams' as brand prefixes — longest first so 'MS Teams' isn't shadowed. Also recognize reserved suffix tokens after a dash ('Active Speaker' / 'Audio' / 'Audio Mix' / 'Screen Share') so the new active-speaker output is correctly classified as ActiveSpeaker rather than misread as a participant named 'Active Speaker'.

Tests: kept all legacy cases, added MS Teams + Microsoft Teams variants and the new dash-prefixed reserved-suffix cases. 69/69 unit tests passing; verified end-to-end against a live Teams meeting where TeamsISO.exe now shows '(Local)' and 'Brendon Power' in the Participants DataGrid.
2026-05-07 23:33:43 -04:00
88841780af feat(pipeline): add managed BGRA nearest-neighbor scaler with aspect modes
Some checks failed
CI / build-and-test (push) Failing after 27s
2026-05-07 15:37:07 +00:00
af37b4d9e1 refactor(interop): NdiRuntimeProbe now matches by prefix to handle NDI runtime version strings
Some checks failed
CI / build-and-test (push) Failing after 24s
2026-05-07 15:36:26 +00:00
cd5e852a30 feat(controller): add IIsoController and IsoController implementation
Some checks failed
CI / build-and-test (push) Has been cancelled
2026-05-07 15:28:27 +00:00
49b6dfb9ed feat(pipeline): add IsoPipeline with lifecycle and restart supervisor
Some checks failed
CI / build-and-test (push) Failing after 29s
2026-05-07 15:26:54 +00:00
e318514202 feat(interop): add NdiRuntimeProbe with version-mismatch result
Some checks failed
CI / build-and-test (push) Failing after 27s
2026-05-07 15:24:31 +00:00
798a5abd64 feat(pipeline): add ExponentialBackoff policy
Some checks failed
CI / build-and-test (push) Has been cancelled
2026-05-07 15:24:13 +00:00
aecbda674d feat(pipeline): add NdiSender with channel-based input
Some checks failed
CI / build-and-test (push) Has been cancelled
2026-05-07 15:23:51 +00:00
ead5e79935 feat(pipeline): add NdiReceiver with channel-based output
Some checks failed
CI / build-and-test (push) Has been cancelled
2026-05-07 15:23:26 +00:00
38f7db888e feat(domain): default global framerate to 59.94p (user's primary production target)
Some checks failed
CI / build-and-test (push) Failing after 25s
2026-05-07 15:21:58 +00:00
27dc0f90c7 feat(logging): add EngineLogging.CreateConsole helper
Some checks failed
CI / build-and-test (push) Has been cancelled
2026-05-07 15:16:17 +00:00
5c039025fd feat(pipeline): add FrameProcessor with closest-frame timing and slate fallback
Some checks failed
CI / build-and-test (push) Failing after 24s
2026-05-07 15:15:19 +00:00
970f04861d feat(pipeline): add SolidFrameRenderer slate and IFrameScaler/PassthroughFrameScaler
Some checks failed
CI / build-and-test (push) Failing after 25s
2026-05-07 15:14:37 +00:00
1b280e3e77 feat(discovery): add NdiDiscoveryService with diff-based event emission
Some checks failed
CI / build-and-test (push) Failing after 22s
2026-05-07 15:14:15 +00:00
cef9018b6d feat(discovery): add ParticipantTracker with rename heuristic
Some checks failed
CI / build-and-test (push) Failing after 22s
2026-05-07 15:13:42 +00:00
c07a668672 test(fakes): add FakeNdiInterop and FakeFrameClock; feat(discovery): add DiscoveryEvent
Some checks failed
CI / build-and-test (push) Failing after 24s
2026-05-07 15:13:00 +00:00
f562303b47 feat(pipeline,interop): add RawFrame, ProcessedFrame, IFrameClock and INdiInterop test seam
Some checks failed
CI / build-and-test (push) Has been cancelled
2026-05-07 15:12:36 +00:00
3f8b5f1a7b feat(persistence): add ConfigStore with atomic JSON writes and corruption-safe load
Some checks failed
CI / build-and-test (push) Failing after 22s
2026-05-07 15:12:01 +00:00
464f559576 feat(domain): add Participant, IsoAssignment, IsoOutput, IsoHealthStats, FrameProcessingSettings, EngineConfig, EngineAlert
Some checks failed
CI / build-and-test (push) Failing after 22s
2026-05-07 15:11:32 +00:00
aaf3184a8e feat(discovery): add NdiSource record and Teams source string parser
Some checks failed
CI / build-and-test (push) Failing after 22s
2026-05-07 15:11:00 +00:00
b07e3e78e0 feat(domain): add core enums (NdiSourceKind, IsoState, AspectMode, AudioMode, TargetFramerate, TargetResolution)
Some checks failed
CI / build-and-test (push) Failing after 23s
2026-05-07 15:10:29 +00:00
f9ab6fe0e7 feat(engine): scaffold TeamsISO.Engine class library 2026-05-07 15:08:11 +00:00