From 1e4017df0351679c51ead624543a5c4eac0d9ad3 Mon Sep 17 00:00:00 2001 From: gramps Date: Thu, 17 Sep 2026 08:03:58 -0700 Subject: [PATCH] =?UTF-8?q?feat(webcam):=20resource=20lifecycle=20startup?= =?UTF-8?q?=20slice=20=E2=80=94=20poll-on-start,=20single-cam=20lock,=20pe?= =?UTF-8?q?rsistent=20Layers=20alert?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The creator couldn't add a webcam to Live (grayed app-wide) and nothing in the app explained why. Ground truth from the live DB: one Webcam identity AND one WebcamSceneConfig in the Chat scene — so the gray was the single-identity rule working, but the reason was unobservable. This slice makes the webcam a resource the app validates and locks, mirroring how OBS reserves its devices. At startup we enumerate the OS once (ValidateWebcamResourceStartupAsync, fired after LoadLayout, stored as WebcamStartupValidationTask for tests to await): - 0 webcams -> app runs on, layer inactive, no alarm - exactly 1 -> attempt CameraManager.AcquireAsync as an app-wide lock; on failure show a persistent red alert at the bottom of the Layers panel (WebcamLockAlert + Retry) that re-polls every 5s and clears itself the moment the camera locks, or on any first real frame - >=2 -> deliberately no auto-lock; camera selection belongs to the App Settings dialog (gear) — next slice CameraManager.IsRunning(deviceId) tells the pass a session already exists (started OR still starting) so loaded identity configs count as the lock and the pass never double-acquires. Test seams mirror LayoutPathOverride: CameraEnumeratorOverride / CameraFrameSourceFactoryOverride so the startup probe never touches real hardware under test. Good Dog test: WebcamStartupResourceTests x3 — single-cam locked + app runs on, zero-cams no-alarm/no-lock, lock-fails -> red alert -> Retry -> clears. Full suite 304/304, build 0 warnings, scope-check passed. Docs in-commit: TASKS Open items + ai.md Webcam section + HANDOFF rewrite. Also corrects the record: 'NVIDIA Broadcast opens the webcam exclusively' was a suspect-list claim (CameraConflictProbe reads process names only, no handles) — not restated as fact. --- Controls/LeftPanel.xaml | 22 ++- HANDOFF.md | 145 ++++++++--------- Services/CameraManager.cs | 12 ++ TASKS.md | 4 + ViewModels/MainViewModel.Webcam.cs | 89 +++++++++++ ViewModels/MainViewModel.cs | 16 +- ai.md | 18 +++ ytLive.Tests/WebcamStartupResourceTests.cs | 174 +++++++++++++++++++++ 8 files changed, 405 insertions(+), 75 deletions(-) create mode 100644 ytLive.Tests/WebcamStartupResourceTests.cs diff --git a/Controls/LeftPanel.xaml b/Controls/LeftPanel.xaml index 03d252f..1087ff7 100644 --- a/Controls/LeftPanel.xaml +++ b/Controls/LeftPanel.xaml @@ -17,6 +17,7 @@ + @@ -167,15 +168,30 @@ HorizontalAlignment="Center" VerticalAlignment="Center" Visibility="{Binding ShowSourcesEmptyHint, Converter={StaticResource BoolToVis}}"/> + + + + +