Multicam PR3: MulticamController + scanner multi-select + focus/strip UI - #201
Closed
darioalessandro wants to merge 1 commit into
Closed
Multicam PR3: MulticamController + scanner multi-select + focus/strip UI#201darioalessandro wants to merge 1 commit into
darioalessandro wants to merge 1 commit into
Conversation
The director engine and screen, all behind ENABLE_MULTICAM (off), so every single-camera path stays byte-identical. Engine (new, unit-tested): - MulticamController: a sibling actor of SessionCoordinator, reached only for a multicam director session. Owns the transport as its delegate after the scanner hands it off; keeps a CameraLink per camera keyed by MCPeerID. Per-camera capability handshake, per-camera frame routing with a per-source RequestFrame ack (Seam B), focused-peer command addressing, a per-camera clock-sync loop (ClockSyncPing/Pong -> ClockOffsetEstimator, ~30s + on foreground), keep-browsing + re-invite of a dropped camera (its lane degrades to .reconnecting; the others are untouched), and logical camera removal. The camera side is unchanged: a camera can't tell a multicam director from a single monitor. - CameraLink: per-camera status/capabilities/clock estimate. UI (new): - MulticamViewController hosts MulticamView in focus mode: the focused camera fills the viewfinder (reusing the 1:1 LiveFrameView) with a floating strip of the other cameras' live thumbnails; tap to refocus. Each lane has its own FrameDisplayModel + FrameStreamReceiver, so a frame from camera B never re-renders camera A's tile. Per-tile reconnecting scrim. Grid mode is a later PR. - MulticamViewModel/CameraLane reconcile controller snapshots while preserving lane instances (and their live streams). Scanner (flag-gated, additive): - With ENABLE_MULTICAM and the monitor role, the scanner accumulates connected cameras instead of auto-advancing on the first connect, and shows "Start (N)". One camera runs the classic MonitorViewController unchanged; two or more hand the live transport to a MulticamController and push the director. The SessionCoordinator seam is a single flag-defaulted-false collecting mode; every non-multicam path is untouched. Tests: MulticamControllerTests (handshake, per-lane frame routing + source- only ack, focused-only commands, disconnect isolation, browser re-invite, clock offset, removal) and MulticamViewModelTests (lane reconcile/focus). Full suite green (668 tests). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Contributor
📊 Test Coverage Report✅ 656/668 tests passed · ⏭️ 12 skipped
🎥 Capture & session core
📁 Full report — 94 files
Generated from |
Member
Author
|
Consolidated into the single feature PR per Dario's request: #203 (branch kept, not deleted). |
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.
Stacked on the PR2 clock-sync branch. The director engine and screen, all behind
ENABLE_MULTICAM(off) — every single-camera path stays byte-identical.Engine (unit-tested)
SessionCoordinator, reached only for a multicam director session. Owns the transport as its delegate after the scanner hands it off; holds aCameraLinkper camera keyed byMCPeerID. Per-camera capability handshake, per-camera frame routing with a per-sourceRequestFrameack (Seam B), focused-peer command addressing, a per-camera clock-sync loop (ClockSyncPing/Pong→ClockOffsetEstimator, ~30s + foreground), keep-browsing + re-invite of a dropped camera (its lane degrades to.reconnecting, the others untouched), and logical camera removal.UI
LiveFrameView) with a floating strip of the other cameras' live thumbnails; tap to refocus. Each lane has its ownFrameDisplayModel+FrameStreamReceiver, so a frame from camera B never re-renders camera A's tile. Per-tile reconnecting scrim. Grid mode is PR6.Scanner (flag-gated, additive)
With
ENABLE_MULTICAMand the monitor role, the scanner accumulates connected cameras instead of auto-advancing on first connect, and shows "Start (N)". One camera → classicMonitorViewControllerunchanged; two or more → hand the live transport to aMulticamControllerand push the director. TheSessionCoordinatorseam is a single flag-defaulted-false collecting mode; every non-multicam path is untouched.Tests
MulticamControllerTests(handshake, per-lane frame routing + source-only ack, focused-only commands, disconnect isolation, browser re-invite, clock offset, removal) andMulticamViewModelTests(lane reconcile/focus). Full suite green — 668 tests.Deviations from the brief
CameraLinkis afinal class, not a struct — it owns aFrameStreamReceiver(a class with a running timer) and the actor mutates it per frame; a struct would force a dict read-modify-write on every frame.FrameStreamReceiverlives UI-side (inCameraLane), not inCameraLink. The brief mentioned both; only one can own it. Keeping decode UI-side mirrors the existing 1:1MonitorViewController(which owns its receiver) and preserves the rendering-isolation pattern.CameraLinkkeeps status/caps/clock.🤖 Generated with Claude Code