13 KiB
13 KiB
Changelog
All notable changes to TeamsISO are documented here. The format follows Keep a Changelog and this project adheres to Semantic Versioning.
Unreleased
Added — May 2026 feature batch
Engine
- NDI Groups: discovery + sender support so Teams' raw broadcasts can be pinned to a private "teamsiso-input" group while TeamsISO's own normalized outputs broadcast on Public.
- One-click "Apply transcoder topology" writes
ndi-config.v1.jsonso all Teams broadcasts go to the private group and TeamsISO re-emits on Public. RawBgraRecorderSinkper-output recorder:IRecorderSinkinterface + raw BGRA stream +manifest.json+convert.cmdscript for FFmpeg conversion to H.264 MKV.- Recording markers:
IRecorderSink.AddMarker(label)fan-out viaIIsoController.AddRecordingMarker. Markers land inmanifest.jsonundermarkers[]for post-production chaptering. - Preview thumbnails:
IsoPipeline.LatestProcessedFramepublished viaVolatile.Readso the UI can render 160×90 BGRA thumbnails in the participants DataGrid at 1Hz. - Idempotent
ParticipantTracker.HandleAdded: re-emitting Added for an already-live source refreshes LastSeen instead of duplicating the row. Fixes the "click Refresh and rows ghost-duplicate" bug introduced by the Refresh-Discovery affordance. IIsoController.RefreshDiscovery()rebuilds the NDI finder on the next poll tick — useful right after applying a new transcoder topology.IIsoController.AddRecordingMarker(label)fan-out to every active recorder.IIsoController.SetRecording(enabled, dir)global recording toggle; per-participant override viaEnableIsoAsync(...recordOverride...).
Host (WPF)
- Active Speaker as a synthetic routable participant with deterministic v5
GUID derived from
auto-mix:<machine>. - Auto-disable on departure: when a participant's NDI source disappears, optionally tear down their pipeline.
- Operator presets: chromeless
Presets…dialog with Save / Apply / Delete / Duplicate / Export / Import. Persisted at%LOCALAPPDATA%\TeamsISO\presets.json. Bundle formatteamsiso-presets-bundle/v1for migration between machines. - Auto-apply last preset on launch (configurable, off by default).
--apply-preset NAMECLI flag for desktop-shortcut workflows.PresetApplier— single source of truth for "apply preset to live participants" used by the dialog, REST surface, and auto-apply path.- Live preview thumbnails per participant (160×90 BGRA WriteableBitmap).
- Right-click context menu on participant rows: Toggle ISO, Record-this- participant, Copy NDI source name.
- Live filter input (substring match on display name).
- "Enable all online" + "Stop all ISOs" + "Refresh" header actions.
- Per-participant recording opt-out checkbox (Rec column).
- Custom NDI output name template with
{name}/{guid}/{machine}/{timestamp}tokens. - Phase E.1 — Launcher: rail "Launch / Stop Teams" toggle.
- Phase E.2 — Window orchestration: hide / show Teams windows from the rail.
- Phase E.3 — In-call controls (UIA): Mute, Camera, Share, Leave, Raise hand, plus PostMessage shortcut forwarding fallback. Candidate names localized for English / German / Spanish / French / Portuguese / Japanese.
- Crash diagnostics: AppDomain + Dispatcher + TaskScheduler unhandled exception handlers wired to Serilog.Critical + user-facing dialog.
- First-launch onboarding dialog with 5-step setup checklist.
- About dialog gained "Show welcome", "Check for updates", "Export diagnostics" buttons.
- Diagnostic bundle export: zips logs + config + presets + version metadata
into
~/Downloads/teamsiso-diagnostics-<ts>.zipfor bug reports. - Update check: manual via About + auto-on-launch banner (throttled to 24h,
opt-out via flag file at
%LOCALAPPDATA%\TeamsISO\no-update-check.flag). - Disk space watcher auto-disables recording at <1GB free.
- Settings panel refactored into OUTPUT / NETWORK / DISPLAY tabs.
- Reset-to-defaults button in OUTPUT tab.
- Enriched footer: REC badge, control-surface badge, session timer (HH:MM:SS since first ISO went live), dynamic status text ("3/5 ISOs live · 2 recording").
- Window-scoped keyboard shortcuts: F1 (help), Ctrl+M (marker), Ctrl+Shift+S (stop all), Ctrl+R (refresh discovery).
- F1 help / cheat-sheet dialog.
UIPreferencesstatic persistsHideLocalSelf,AutoDisableOnDeparture,ParticipantSort(JoinOrder / Alphabetical / OnlineFirst) across launches to%LOCALAPPDATA%\TeamsISO\ui-prefs.json.- Pop-out per-participant preview window (right-click → Open preview…) refreshes at ~20Hz and is multi-monitor friendly.
- Configurable participant sort order via the DISPLAY tab dropdown.
- Stop-All confirms before tearing down running pipelines (catches mid-show misclicks).
- About dialog gained "Logs / Recordings / Notes" folder shortcut buttons.
NotesWindowinline viewer for today's show-notes file with 2s polling.- Duplicate-preset action in the Presets dialog with smart
(copy N)name suggestions. --apply-preset NAMEcommand-line flag for desktop-shortcut workflows.- New
TeamsISO.App.Testsnet8.0-windows test project. Initial coverage:OperatorPresetStoreTests(round-trip, name collisions, schema, bundle import/export, garbage-file resilience),OutputNameTemplateTests(token expansion + sanitization),OscMessageTests(wire-format parsing of int/float/string/T/F type tags). Backed by anInternalsVisibleTogrant- a test-only
OperatorPresetStore.PathOverridehook.
- a test-only
IsoHealthStats.PeakAudioLevelfield + DataGrid VU-bar UI scaffolding. Engine still emits 0.0 (audio capture is a focused follow-up); the bar's decay logic is in place so it animates as soon as engine-side audio parsing lands.MediaFoundationRecorderSinkscaffold under#if MF_AVAILABLEfor inline H.264 encoding via Vortice.MediaFoundation. ~10× smaller files than the raw BGRA recorder. Activation steps documented atdocs/REAL-TIME-RECORDING.md.- System-tray icon + minimize-to-tray toggle. Adds
<UseWindowsForms>true</UseWindowsForms>forNotifyIcon; theTrayIconHostlives onApp(process lifetime, not main-window lifetime). Right-click menu has Show / Stop all ISOs / Exit. - Built-in NDI test pattern:
TeamsISO.Console --test-patternbroadcasts a synthetic 1280×720 30fps source namedTEAMSISO_TESTshowing SMPTE color bars + a moving sweep band. Verifies NDI runtime, sender configuration, and downstream discovery without needing Teams running. Backed byTestPatternGeneratorin the engine + 4 unit tests covering buffer size, alpha, color distinctness, and sweep animation. - Always-toast on participant disconnect, regardless of
AutoDisableOnDeparturesetting. Distinguishes "ISO torn down" (auto-disable on) from "ISO still running on slate" (auto-disable off) so operators don't miss a silent drop mid-show. - Restart this ISO right-click action — disable + brief delay + re-enable for one participant only. Useful when a single feed flakes without affecting other ISOs.
- Roll recording action: rolls every active recording into a new chunk
(disable + re-enable each pipeline; recorder finalizes its
manifest.jsonand starts a fresh subdirectory). Surfaced viaMainViewModel.RollRecordingCommand, RESTPOST /recording/roll, and OSC/teamsiso/recording/roll. Useful for chaptering between show segments. - Engine audio peak metering —
IsoHealthStats.PeakAudioLevelnow reports real values (was always 0.0). NewINdiInterop.CaptureAudioPeakmethod polls audio frames; productionNdiInteropPInvokeparsesNDIlib_audio_frame_v3_tand computes max-absolute peak across all channels.NdiReceiverruns a sibling audio capture loop on the same lifetime so the existing video path is unaffected. UI VU bars in the participants DataGrid now animate against real source audio. Failures in the audio loop are caught + logged but never re-thrown — a misbehaving audio path must never tear down the live video pipeline. 14 new unit tests inAudioPeakComputerTestscovering FLTP / FLT / PCM s16 across edge cases (clipping,short.MinValueoverflow, defensivetotalSamples-vs-buffer mismatch handling).
LAN-reachable control surface
ControlSurfaceServer.Start(port, bindToLan)andOscBridge.Start(port, bindToLan)switch between127.0.0.1and all-interfaces (http://+:port/,IPAddress.Any) based on the newControlSurfaceLanReachableUI preference. Settings VM persists the toggle, restarts both surfaces on flip, and surfaces aControlSurfaceUrl(computed from the host's first physical-NIC routable IPv4 — Tailscale / VPN / APIPA addresses are skipped) plus a Copy button. Use case: headless host PC running Teams + TeamsISO; thin client on the same LAN drives/uior hits the REST endpoints. Closed-network deployment, no auth — documented as a trusted-LAN-only mode indocs/CONTROL-SURFACE.md. First-time use requires a one-shotnetsh http add urlacl url=http://+:9755/ user=Everyone(so the listener can bind without admin); the diagnostic warning fires the exact command string in the log if the bind fails.
UI polish — visible affordances on the dark canvas
- Hover state on every themed button (Ghost / Caption / RailIcon / IsoToggle)
was barely distinguishable from the resting state. Bumped
Wd.SurfaceHoverWd.SurfaceActivecolours for sufficient contrast, added dedicatedWd.Button.HoverBg/Wd.Button.PressBgbrushes with a slight chroma tint, and added cyan accent borders so mouse-hover and tab-focus give an unmistakable affordance regardless of which surface the button sits on. IsoToggle keeps its status-coded background (LIVE cyan / ERROR coral / NO SIGNAL amber) on hover; the affordance is a 2px cyan border pop.
IsKeyboardFocusedtriggers added on every themed button so tab-cycling through the UI gives visual feedback (was nothing —FocusVisualStylewasx:Nullwith no replacement).- ScrollBar restyled: slim transparent track + tinted thumb (Edge / VS Code pattern) in place of the chunky Win9x default with line-up / line-down arrow buttons. Track-clicks above/below the thumb still page-scroll.
- ToolTip restyled: SurfaceElevated card with rounded 6px corner + 320px text wrap, replacing the cream Win98 popup. Affects every tooltip across MainWindow.
- ContextMenu / MenuItem restyled: dark card with rounded corners + cyan- tinted hover. Affects the right-click menu on participant rows (Toggle ISO / Restart this ISO / Open preview / Record / Copy NDI source name).
Control surface
- REST API on
127.0.0.1:9755with endpoints for participant ISO toggle (by Id or display name), preset apply, refresh discovery, stop-all, recording on/off, marker drop, notes, and Teams in-call commands. Documented atdocs/CONTROL-SURFACE.md. - WebSocket
/wspushes live participant state at 4Hz with snapshot diffing. - OSC bridge on UDP
127.0.0.1:9000mirrors the REST vocabulary (/teamsiso/iso "Jane" 1,/teamsiso/preset "...", etc.). - Embedded HTML control panel at
GET /ui— phone-friendly remote with live state and one-click action buttons. - Show notes service:
POST /notesand/teamsiso/notes "..."append timestamped lines to%LOCALAPPDATA%\TeamsISO\Notes\<YYYY-MM-DD>.md.
CI / Release
- Forgejo CI is green; tag-push release workflow builds + tests + publishes
- builds MSI on a Windows runner and attaches it to the auto-created release via the REST API.
- Optional MSI + exe code-signing wired into
release.yml— gated onSIGN_CERT_PFX_BASE64+SIGN_CERT_PASSWORDForgejo Secrets.
Fixed
.slnfpath-separator mismatch (forward slashes for cross-platform).- NDI native DLL resolution via
NativeLibraryresolver. ExpectedRuntimeVersionPrefixupdated to NDI 6 banner format.NdiSourceParseraccepts current Teams desktop'sMS Teams - <name>brand format.- ActiveSpeaker source removal no longer poisons the rename-window heuristic for a Participant joining the same machine within the window.
IsoPipeline.Stateaccess synchronized viaVolatile.Read/Write.- REST handlers now correctly marshal
ObservableCollectionreads + writes through the UI dispatcher. - WebSocket upgrade no longer falls into
res.Close()finally block (was killing freshly-upgraded connections). ParticipantViewModel.UpdateThumbnaildefends against malformed frames (width*height*4 > Pixels.Length).HasThumbnailcorrectly firesPropertyChangedwhenThumbnailtransitions from null.- WinForms / WPF
ApplicationandMessageBoxnamespace collision (introduced when<UseWindowsForms>true</UseWindowsForms>was added for the system tray) resolved via project-wideGlobalUsings.cs. GetLanIPv4()now skips Tailscale / VPN tunnel adapters and APIPA (169.254.x) so the displayed control-surface URL points at the routable LAN IP (verified on a host with both Ethernet 10.x and a Tailscale 169.254 link-local — picker now correctly returns the Ethernet address).