Per-Participant NDI ISO Controller for Microsoft Teams. Receives Teams NDI streams, normalizes framerate/resolution, and re-emits clean ISO outputs for live production switchers (vMix, OBS, Ross). Wild Dragon LLC.
Find a file
Zac Gaetano 9e176d8f10 feat(winui3): redesigned MainWindow + custom title bar + theme toggle
Lands the approved shape brief as the WinUI 3 MainWindow:

* 64px left rail with brand mark, primary nav (participants), Teams
  launch / hide / settings buttons, and the engine-status puck at the
  bottom. All five rail buttons use Segoe Fluent Icons glyphs at a
  uniform 20px optical size; no more bespoke <Path Data> shapes with
  inconsistent stroke weights.

* 44px custom title bar via ExtendsContentIntoTitleBar +
  SetTitleBar(AppTitleBar). The drag region absorbs the three live-state
  pills inline (session timer 'live * 00:14:32', REC count + elapsed,
  disk free) and a slim sun/moon theme-toggle button to the left of the
  system Min/Max/Close controls. System buttons inherit ButtonForeground
  Color etc. from AppWindow.TitleBar so they match palette in both
  themes.

* Section header with 'Participants * count' display, filter input,
  Refresh + Presets (Secondary buttons), and 'Enable all online' as
  the single cyan Primary button - finally a real button hierarchy
  instead of seven indistinguishable ghost buttons.

* Participants list rendered as ItemsRepeater + DataTemplate for now;
  the CommunityToolkit DataGrid migration follows in a separate commit.
  Row template at 64px height with: 3px cyan left border for active
  speaker, avatar with initials in cyan-muted circle, name + codec line,
  signal lock state with dot, audio meter via ProgressBar, output name
  in JetBrains Mono, ISO state pill (LIVE/OFF/ERROR) at right.

* Conditional in-call control bar below the table: Mute / Camera /
  Share / Marker / Leave + overflow kebab. Muted state binds the
  destructive coral treatment to the Mute button; Leave is also
  destructive (coral border + text); everything else is Secondary.
  Tight 8px spacing keeps the bar dense without crowding.

* Slim 32px status bar at the bottom: control-surface URL on the left
  (cyan dot indicator), keyboard-shortcut hints on the right in
  tertiary mono. Replaces the WPF host's six-column footer.

Implementation notes:

* MockParticipant model populates the table with representative data
  (Maya / Daniel / Aicha / Sam, one as active speaker) until the
  ParticipantViewModel binding migrates over from the WPF host.

* Custom Program.cs takes ownership of Main from the XAML compiler
  (DISABLE_XAML_GENERATED_MAIN). Calls Bootstrap.TryInitialize(0x00010006)
  before Application.Start so the unpackaged .exe can locate the
  WindowsAppSDK 1.6 framework MSIX at launch. Shutdown is paired in
  a finally block.

* Theme toggle in code-behind flips Window.Content.RequestedTheme
  between Dark and Light. {ThemeResource} bindings auto-swap across
  the visual tree; system title-bar buttons (outside the XAML tree)
  get color updates inline so they stay readable in both modes.

* app.manifest deferred from build - the framework-emitted manifest
  covers DPI awareness and supportedOS GUIDs; reintroducing our own
  goes in the next commit alongside the bootstrapper hardening.

Known issue: the unpackaged .exe currently fails to activate on this
build host with 'this application could not be started' before Main
runs. Build is clean; published output runs the same way. Diagnosing
the activation failure is the next session's first task (likely the
runtimeconfig.json including Microsoft.WindowsDesktop.App which WinUI 3
doesn't want, or a missing CRT redistributable). The WPF host remains
the running build until that's resolved.

dotnet build TeamsISO.Windows.slnf -c Debug: 0 warnings, 0 errors.
2026-05-13 00:03:12 -04:00
.forgejo/workflows ci: optional MSI + exe code-signing in release.yml 2026-05-10 09:41:28 -04:00
docs docs: _NEXT.md captures the full E.4 + autorec + UX-pass batch 2026-05-10 21:29:27 -04:00
installer feat: app icon, FPS, drops counter, --version, About dialog, Stop Teams toggle 2026-05-08 13:50:19 -04:00
src feat(winui3): redesigned MainWindow + custom title bar + theme toggle 2026-05-13 00:03:12 -04:00
.editorconfig chore: scaffold repo conventions and global build props 2026-05-07 15:07:53 +00:00
.gitignore chore: scaffold repo conventions and global build props 2026-05-07 15:07:53 +00:00
build-and-test.ps1 chore: sweep orphaned files (UpdateChecker, UpdateBanner, TeamsControlBridge, helper scripts) 2026-05-10 09:42:29 -04:00
CHANGELOG.md docs: CHANGELOG covers MUTED pills / free space / loudest sort / snapshot-all / numpad / active-speaker highlight 2026-05-10 21:28:36 -04:00
commit-and-push.ps1 chore: sweep orphaned files (UpdateChecker, UpdateBanner, TeamsControlBridge, helper scripts) 2026-05-10 09:42:29 -04:00
coverlet.runsettings ci: enforce 80% line coverage gate on TeamsISO.Engine 2026-05-07 15:16:11 +00:00
DESIGN.md docs: PRODUCT.md + DESIGN.md (ground-up GUI redesign brief) 2026-05-12 23:45:04 -04:00
Directory.Build.props chore: scaffold repo conventions and global build props 2026-05-07 15:07:53 +00:00
PRODUCT.md docs: PRODUCT.md + DESIGN.md (ground-up GUI redesign brief) 2026-05-12 23:45:04 -04:00
README.md feat(ui): notes viewer + Stop-All confirm + folder shortcuts + README 2026-05-10 09:41:32 -04:00
TeamsISO.Linux.slnf feat(console): add TeamsISO.Console headless smoke runner 2026-05-07 15:37:44 +00:00
TeamsISO.sln feat(winui3): scaffold TeamsISO.App.WinUI alongside the WPF host 2026-05-12 23:52:35 -04:00
TeamsISO.Windows.slnf feat(winui3): scaffold TeamsISO.App.WinUI alongside the WPF host 2026-05-12 23:52:35 -04:00

TeamsISO

Per-Participant NDI ISO Controller for Microsoft Teams.

TeamsISO sits between Microsoft Teams' raw NDI broadcast output and a live-production environment. It receives each participant's NDI stream, normalizes framerate / resolution / aspect / audio per a configured target, and re-emits clean, individually-addressable NDI sources for ingestion into a switcher (vMix, OBS, Ross, hardware capture).

What it does

  • Discovers participants as Teams broadcasts each one over NDI, surfacing the operator-friendly display name (handles current "MS Teams - Name" format and the legacy "(Teams) Name" format).
  • Normalizes feeds to a consistent framerate, resolution, aspect mode, and audio routing — so the downstream switcher gets predictable inputs regardless of what each participant's webcam is doing.
  • Routes per-participant as separate NDI sources with a configurable output-name template (TEAMSISO_{name}, {guid}, {machine}, {timestamp} tokens).
  • Records each ISO to disk simultaneously — raw BGRA + sidecar manifest.json
    • ffmpeg convert.cmd — so post-production gets a clean per-guest archive.
  • Embeds Teams orchestration: launch and stop Teams from the rail, hide Teams' UI windows during a show, drive in-call controls (mute, camera, share, leave, raise hand) via UIAutomation.
  • Operator presets save the current per-participant ISO assignment and custom output names, applicable on next launch automatically.
  • Live preview thumbnails per participant in the participants table, plus pop-out floating preview windows (right-click → Open preview…) for multi-monitor monitoring.
  • External control surface — REST + WebSocket on 127.0.0.1:9755 and OSC on UDP 127.0.0.1:9000 for Bitfocus Companion / Stream Deck / TouchOSC integration. Self-contained HTML control panel at /ui for phone-as-controller.
  • Crash diagnostics wired to a rolling daily Serilog file sink under %LOCALAPPDATA%\TeamsISO\Logs\.
  • Update check against forge.wilddragon.net's release API — manual or silent on launch (throttled to 24h).
  • Diagnostic bundle export zips logs + config + presets for bug reports.

Status

Pre-1.0. The May 2026 batch is feature-complete; v1.0 cut is gated on code-signing the MSI and a smoke pass against a real Teams meeting. See CHANGELOG.md for the [Unreleased] entry.

Build

Requires .NET 8 SDK on Windows (the TeamsISO.App host is net8.0-windows WPF).

dotnet restore TeamsISO.Windows.slnf
dotnet build TeamsISO.Windows.slnf -c Release
dotnet test TeamsISO.Windows.slnf --filter "Category!=ndi&requires!=ndi"

The shipped helper scripts in the repo root automate this:

pwsh -File .\build-and-test.ps1
pwsh -File .\commit-and-push.ps1

Documentation

Keyboard shortcuts

Key Action
F1 Open help / cheat sheet
Ctrl + M Drop a timestamped marker into every active recording
Ctrl + Shift + S Stop every running ISO (emergency)
Ctrl + R Refresh NDI discovery (rebuild finder)

File locations

Path Contents
%APPDATA%\TeamsISO\config.json Engine settings (framerate, NDI groups, etc.)
%LOCALAPPDATA%\TeamsISO\presets.json Saved operator presets + auto-apply preference
%LOCALAPPDATA%\TeamsISO\Logs\ Rolling daily diagnostic logs
%LOCALAPPDATA%\TeamsISO\Notes\ Per-day show-notes markdown files
%USERPROFILE%\Videos\TeamsISO\<date>\ Default recording output
%APPDATA%\NDI\ndi-config.v1.json NDI Access Manager group routing

License

Proprietary, © Wild Dragon LLC 2026.