feat(chroma): detect a green or blue screen and build the key filter

TASK 64 units B and C — the two pure halves, no wiring yet.

- `Services/ChromaKeyDetector.cs`: samples the frame's outer ring (the screen is at the
  edges, the subject in the middle), reports Green/Blue/None plus the colour actually shot
  and a confidence. Confidence is coverage × hue tightness, which is what keeps a lawn or
  a football pitch from being offered as a keyable screen. Letterbox bars are excluded from
  the denominator rather than counted against the screen. Never decides anything: an offer
  is the creator's to accept.
- `Services/VideoFilterChain.cs`: options → `-vf` (scale → colorkey → despill → format=bgra)
  plus a `Signature` so two layers keyed identically can share one decoder. Numbers clamp and
  an unparseable colour falls back to the app default, so ffmpeg is never handed a filter it
  rejects at spawn.
- Keying is done by the bundled ffmpeg, not by us: verified against the shipped binary that
  `colorkey=…,format=bgra` emits straight-alpha BGRA (alpha 0 on keyed pixels), which is what
  the compositor already blends. No per-pixel loop lands in the 16.67 ms render budget.

Test: `ChromaKeyDetectorTests` — green, blue, letterboxed, lawn (must say None), mixed scene,
dark frame, plus the chain strings and signature sharing.

Docs: TASK 64 queued in TASKS.md with its slice table; the decisions.md chroma-key reversal
recorded as a reversal (it was buried for webcam background removal, not for supplied clips).
This commit is contained in:
2026-11-05 08:05:18 -08:00
parent 2670ac022b
commit 21db8a17b8
6 changed files with 457 additions and 1 deletions
+5
View File
@@ -204,6 +204,11 @@ Do not build these; each was explicitly ruled out.
- Per-source audio sync offset — global offset shipped (TASK 22); per-source is the OBS mixer
rabbit hole and breaks the one-knob audio rule.
- Chroma key / NVIDIA-only background removal (the bundled ONNX path is the one shipped).
**Partly reversed 2026-11-05 (TASK 64):** a *video clip* may be keyed — green or blue, detected
from the clip, performed by the bundled ffmpeg `colorkey`/`despill` in the decode pipe, offered
never forced. The original objection was webcam background removal (ML model, lighting, support
weight); none of it applies to a clip the creator supplied. The webcam still gets the shipped ONNX
path, and NVIDIA-only effects stay out.
- Virtual camera output — a different product and a device-driver swamp; our output is one
YouTube stream.
- Replay buffer, stream clipping, multi-destination restreaming, an in-app bug reporter, and an