Files
LlamaCasty/HANDOFF.md
T
gramps 9d00955004 feat(webcam): app-default gate slice — per-scene offer, Add places default directly, identity survives removal
Chat's camera no longer greys Web Cam in Live (per-scene max, not app-wide);
TASK 26 superseded by creator directive (app-level resource model). Add Webcam
now places the existing app default without the picker; the picker runs only
for the initial selection. Removing the last placement keeps the identity
(_webcam never nulled) so Add stays offered. Startup pass adopts a solo camera
as the app default, so a clean layout offers the layer in Live/Chat at once.
Dynamic WebcamAddToolTip names the why (incl. the 'graduated to OBS' line).

Good Dog: WebcamMenuGateTests rewritten (real app + temp DB + camera seams) —
identity in Chat does not gray Live; Add in Live places same wc-1 no picker;
scene-with-placement stays gray; identity survives both removals (DB row 1).
WebcamStartupResourceTests single-lock fact asserts CanAddWebcam after adoption.
Docs same commit (ai.md supersession, TASKS.md, HANDOFF.md). 304/304, 0 warnings.
2026-09-17 08:56:04 -07:00

9.0 KiB
Raw Blame History

HANDOFF — 2026-09-17 (webcam app-default gate slice; committed locally — no push)

Branch / Commit State

main HEAD = 1e4017d webcam startup resource lifecycle (local). The gate slice lands on top as a NEW commit (stays commit-local until greenlight). Below: 94a934f (reader-ladder), 64a5a6d (slice-18 C4 cache), slice-17, slice-16, slice-15, c01206f, b22d08e (signed audio-sync, pushed). No push after this commit either — still awaiting the device take + user greenlight.

⚠️ Branding (2026-09-14, creator-corrected): product = llamacasty, internals = ytLive

Product is llamacasty; repo path, csproj AssemblyName/RootNamespace, DB/log paths (%APPDATA%\ytLlive\...), most code names are legacy ytLive/ytLlive. User-facing text: "llamacasty" / UI labels use the catalog names ("Web Cam", "YouTube Chat", "Countdown Timer"…).

✅ Committed — webcam app-default gate slice (creator-visible goal: Web Cam offered when switching to Live)

Background: the DB truth (%APPDATA%\ytLlive\ytLlive.db; NOT layout.db, a 0-byte legacy file) put the single WebcamSceneConfig in Chat, so the old TASK 26 app-wide rule grayed Live's "+" → Web Cam even though the camera was available and in use. The creator ruled (rigorous resource model): the webcam is an app-level resource — ONE app-wide default selection, usable in every scene that can host it; it is NOT blocked by other scenes' placements. Scope of this slice: the availability layer. TASK 26's app-wide gate is superseded by creator directive.

  • Per-scene gate: CanAddWebcam = StagedScene is { WebcamConfig: null } && _webcam != null. A scene already hosting the webcam stays gray (one webcam per stream); another scene holding it does NOT gray the row elsewhere.
  • Add places the default directly: when _webcam != null, "Add Webcam" creates the placement with NO picker; the Windows picker runs only for the initial selection (_webcam == null; choosing there swaps app-wide via the existing SwapWebcamIdentityAsync).
  • Identity survives last-placement removal: RemoveElement no longer nulls _webcam (that was the TASK 26 rule). LayoutStore writes the singleton whenever it's non-null, so it persists.
  • Startup adopts a solo camera as the app default (in ValidateWebcamResourceStartupAsync, after the successful single-cam lock): a clean/empty layout now offers the Web Cam layer in Live/Chat immediately; CameraManager.IsRunning skip means a loaded identity already holding a session is never overwritten.
  • Dynamic why-gray tooltip: WebcamAddToolTip (raised alongside the gates) — "Already in this scene — one webcam per stream. A second camera means you've graduated to OBS." / "No webcam detected…" / "Adds the app default webcam to this scene."
  • Good Dog integration test WebcamMenuGateTests rewritten for the new model (real app + temp DB + camera seams): Chat's identity does NOT gray Live; Add in Live places the same wc-1 without a picker; scene-with-placement stays gray; identity survives both removals (DB row count keeps 1). WebcamStartupResourceTests test 1 strengthened: after startup the single camera is adopted → CanAddWebcam true. 304/304 green, build 0 warnings.

Task docs: TASKS.md Open items + ai.md Webcam section updated (supersession recorded).

✅ Earlier committed — webcam resource lifecycle (startup slice, 1e4017d)

Startup poll + tri-state (ValidateWebcamResourceStartupAsync after LoadLayout): 0 → run on, no alarm; 1 → AcquireAsync as app-wide lock; ≥2 → no auto-lock (App Settings selector next). Lock failure → persistent red alert (WebcamLockAlert in the Layers panel + Retry), re-polls every 5 s (_webcamLockPollTimer), clears on lock success or any first real frame. CameraManager.IsRunning(deviceId) = session exists (started OR starting). Camera test seams CameraEnumeratorOverride/CameraFrameSourceFactoryOverride (mirror LayoutPathOverride). Attribution correction committed: "NVIDIA Broadcast opens the webcam exclusively" is a suspect-list claim — CameraConflictProbe reads process names only, no device handles; do not restate as fact.

⚠️ Open items

  • Webcam take fix 2 of 2 — the 09:16 crash is UNFIXED (hard, untested): hypothesis — native WMF death when MediaCapture.Failed fires mid-stream and SafeStopAsync (fire-and-forget) disposes reader+capture while the frame thread sits in TryAcquireLatestFrame/Marshal.Copy (outside the frame's try/catch). No repro, no integration test, native race not catchable — deferred per spin-guard. Record a second occurrence's pattern.
  • Multi-camera selector slice (user-queued): when ≥2 webcams exist, add web-cam selection text + control to the App Settings dialog (gear icon, OverlayHost — left-click opens settings, right-click the context menu). Selecting + successfully allocating/locking activates the WebCam layer. Currently ≥2 cameras → nothing shows in settings. Also: the "Change Webcam" picker should carry the OBS one-liner (one webcam per stream — a second means OBS).
  • Static (+) catalog slice (user-queued): all catalog rows always listed (Background, "Web Cam", "Countdown Timer", "Web Resource", "YouTube Chat", "YouTube Event"…), gray only for (1) system-level unavailability (persistent alert), (2) max already allocated, (3) not available for the current scene; tooltips must name the reason. Static labels only, no new code-behind.
  • Device re-verify (one take session): 1. slice-18 cache verdicts (NR/WH hits, stalls, band ~60); 2. webcam: fresh launch with the browser holding the camera must show the webcam or a NAMED chip (now also the persistent red alert, and the alert must clear after the browser closes + retry); 3. verify Live now OFFERS the Web Cam layer even while Chat holds the camera (the exact creator goal: "webcam offered in the layer stack when I switch over to the Live view").
  • No push yet — after the take verdict, re-measure the clap offset (/tmp/opencode/avsync.py), then decide push with the user.

Open threads (carried)

  • Audio-silence verification — fixed (724af14); creator heard real audio.
  • Webcam MJPG missing / ~10–14Hz, layer SortOrder, truncation-with-dynamic-scenes — queued.
  • Sync control user-doc tutorial — REQUIRED before 1.0 (creator directive; TASK 22).
  • Signed A/V sync: verify the negative (advance) direction on device.
  • Focus-loss capture lag — closed as NOT the cause (1824: delivery healthy ~60-100/s).

Landmines

  • testhost shares startup.log — filter by time; the app also writes fake-device failures ('test-camera', 'dev-physical-9') from CameraManager unit tests.
  • cmd.exe /c "taskkill /F /IM ytLive.exe" (WSL double-slashes mangle) before rebuilds — a live app process locks ytLive.exe (MSB3021).
  • Build/tests: Windows dotnet host (/mnt/c/Program Files/dotnet/dotnet.exe). 0 warnings — only ./scripts/verify.sh "<files>"'s clean build counts. Running ytLive.csproj alone does NOT rebuild ytLive.Tests.dll — run the Tests csproj before vstest. Known audio flake: Mix_HonorsProviderGains_AndGameMute_KillsTheLoopback (float precision) — re-run if it trips alone; unrelated to camera work.
  • Assert footguns in this repo's xUnit: Assert.Null(x, "msg")/Assert.Single(coll, "msg") do NOT take a message — the 2-arg overloads mean something else (predicate/item). Empty message = 1 arg.
  • The webcam's real state lives in %APPDATA%\ytLlive\ytLlive.db (layout.db there is a 0-byte legacy file). sqlite3 at /home/gramps/android-sdk/platform-tools/sqlite3.
  • Contention failure modes (startup.log): "no frames within 4s" = init OK but starved; "OutputFormatNotSupported" = reader subtype below the MJPG-active camera; "file being used by another process / CaptureMode is SharedReadOnly" = re-negotiation refused while others hold the device. CameraConflictProbe = suspect list, not handle evidence.
  • FramePump tests asserting per-frame CONTENT need a STABLE scene; cache-sensitive assertions can't use a call-count resolver flip (tick resolves twice) — key alternation on OutputIndex.
  • ffmpeg/ffprobe: /mnt/c/Program Files/Krita (x64)/bin/ with Windows paths.
  • MyMistakes.md: WINRT resource-allocation RECIPE, freeze-audit RECIPE, A/V sync recipe, deadline pacing, CoreMessaging DQ, WGC-CLIP + slice blocks. Grep before re-deriving.
  • C:\tmpout is for ffmpeg evidence artifacts; keep them out of the repo.

Next step

The multi-camera App Settings selector slice (+ static catalog rows after), then the creator's take on this build. The webcam gate slice answers the creator's "go" ask — switching to the Live view must now offer Web Cam in the layer stack (Chat holding the camera no longer blocks it). The red Layers alert remains the new expected behavior to eyeball on the take: browser holds the camera → fresh launch → alert (unless Chat's config grabbed it) that clears after a Retry once the browser closes.