Virtual green screen: segmentation, depth assist, and subject-distance cutoff - #90
Merged
Conversation
PROTOCOL.md specifies the green_screen CONTROL command (on / maxDistance, camera-only, SDR-forced) and the STATE fields (supportsGreenScreen / greenScreen / greenScreenDepth / greenScreenMaxDistance), including the plugin's add-once-by-name chroma-key reaction. UI_DESIGN pins the "Green screen" vocabulary and the subject-distance slider anatomy; PERFORMANCE adds the off-is-free / on-pays-once invariant with the depth and Center Stage caveats; the README gains the feature bullet; the roadmap prunes everything shipped through v1.9.0 and records the deliberate deprioritization of pairing & encryption. Release-Skip: true Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015dWUcqAkNNuNELnMPS4RdT
All three planned stages in one feature. The phone segments the person (Vision, stateful request, .fast above 30 fps) and composites the background to BT.709 chroma green (#00B140) in a single Metal pass into pool buffers — capture's own buffers are never written (they race the encoder's async read), OFF keeps the pre-feature hot path verbatim, and every failure fails open to the uncomposited frame. On Face ID front and LiDAR rear Main cameras a synchronized depth stream (~15 Hz, NaN-safe gate) sharpens the matte and powers a max subject distance cutoff — the only iOS 15 tool that can drop background people, since the segmentation mask is one mask for everyone. The plugin reacts to the STATE snapshot by auto-adding a pre-configured chroma-key filter (once, by name, on the dial thread per the set_video_delay precedent, respecting user deletion via a persisted flag) and the web panel gains the Green screen chip and a subject-distance slider matching the app's Live row exactly (drag = real cutoff, readout tap = All). Green screen forces Standard colour through the settings exclusivity triangle; STATE advertises supportsGreenScreen/greenScreen/greenScreenDepth/greenScreenMaxDistance with true-only booleans (old plugins truncate snapshots past 1 KiB; new caches grow to 2 KiB), and the green_screen CONTROL command carries on/maxDistance per docs/PROTOCOL.md. Adversarially reviewed: fixes a cross-queue race in the synchronizer delegate (outputs now resolved from the callback's own synchronizer), a double live-reconfigure when picking HDR/Log with green screen on, substring JSON probes that mic names could spoof (colon-anchored now), a slider-convention mismatch between panel and app, and a per-arm Metal shader recompile (now a process-wide static). Release-Bump: minor Release-Beta: true Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015dWUcqAkNNuNELnMPS4RdT
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
The roadmap's virtual green screen, all three planned stages shipped together:
Stage 1 — background removal on any camera. A Green screen toggle on the main screen (Camera & color section). The phone runs Vision person segmentation per frame (
.fastabove 30 fps,.balancedat 30, one stateful request per stream for temporal smoothing) and composites the background to BT.709 chroma green (#00B140) in a single Metal compute pass — the bilinear mask upsample doubles as the edge feather. The wire carries ordinary video; no protocol change to decode. Off is free: the hot path with the toggle off is the pre-feature closure verbatim (now a PERFORMANCE.md invariant). Every failure fails open to the uncomposited frame — a Vision hiccup can never blank a stream.Stage 2 — depth assist + auto chroma key. On Face ID front cameras and LiDAR rear Main cameras, capture swaps to the depth-sibling device and synchronizes a ~15 Hz depth stream that gates the matte (NaN-safe), fixing segmentation's classic failures: posters/monitors keyed as people, low light, held objects. The plugin reacts to the STATE snapshot by auto-adding a pre-configured chroma-key filter to the source — once, by name, respecting later user deletion via a persisted per-source flag — so the phone toggle alone yields a keyed subject in OBS. Degrades silently to segmentation-only when the lens/format can't carry depth (never changes resolution/fps to chase it).
Stage 3 — subject distance. A Subject distance cutoff (0.5–5.0 m, or All) on the app's Live screen and the web panel — same anatomy both surfaces: dragging always sets a real cutoff, tapping the readout returns to All. With one semantic mask covering everyone (per-person masks need iOS 17), the depth cutoff is the only iOS 15 tool that drops people walking by behind you. Rides the existing CONTROL/STATE machinery (
green_screencommand;supportsGreenScreen/greenScreen/greenScreenDepth/greenScreenMaxDistancefields, spec'd in PROTOCOL.md).Green screen is SDR-only and forces Standard colour through the same exclusivity pattern the codec uses. Depth delivery deactivates Center Stage/system video effects by iOS policy (documented in-app). STATE caches grow 1 KiB → 2 KiB on the plugin side; the app emits true-only booleans to minimize snapshot growth for old plugins.
Adversarial review pass (three independent lenses) caught and this PR includes fixes for: a cross-queue data race in the depth synchronizer delegate, a double live-reconfigure when switching to HDR/Log with green screen on, substring JSON probes spoofable by Bluetooth mic names (now colon-anchored), a panel/app slider-convention mismatch, and a per-arm Metal shader recompile (now process-wide static).
How it was tested
syntax-check.sh); full type check is CI's macOS job — first compile of Vision/Metal/depth API usage, watch that job.-Wall -Wextra -Werror; the real-HTTP webtest harness passes all checks against the editedweb-control.c, and the served panel JS parses (node --check) with the slider logic verified by a value-table smoke test.🤖 Generated with Claude Code
https://claude.ai/code/session_015dWUcqAkNNuNELnMPS4RdT
Generated by Claude Code