feat: screen sharing via pixelpass (Discord-style, presence-borne ticket)
Surface pixelpass screen-sharing from inside a peerspeak room. peerspeak owns voice, pixelpass owns pixels — they're never Cargo deps of each other; the contract is pixelpass's CLI flags + its `--output json` stdout stream. Modelled on Discord: multiple simultaneous sharers, a 🔴 Live badge + 👁 Watch on each sharing peer's card, and in-progress shares visible to late joiners. - New `src/screenshare` module: pure `parse_pixelpass_event` seam + `pixelpass_path` discovery (13 unit tests), async `spawn_host` (→ ticket) and `spawn_viewer` (→ parse connected{url} → open mpv, vlc fallback). No new deps. - Sharing rides presence: `PeerState.sharing: Option<ticket>` (serde-defaulted), so the existing gossip re-announce delivers the offer to late joiners for free and a PeerUpdated fires on start/stop — no separate gossip message needed. - core: Start/Stop/ViewShare commands; host + viewer children tracked in the session, killed on stop/leave (kill_on_drop backstop). Viewer limit left to pixelpass's bandwidth-measured cap. - UI: Share/Stop button (graceful "needs pixelpass" disabled state), Live badge + Watch on peer cards, Sharing badge on the self card. Verified by screenshot. - config: optional `pixelpass_path` override (hand-editable). Tests-green; the 2-machine gossip/remote path is not yet field-verified. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,8 +1,26 @@
|
||||
# Screen-share integration (peerspeak ↔ pixelpass)
|
||||
|
||||
Design decisions for surfacing pixelpass screen-sharing from inside a peerspeak
|
||||
room. Locked 2026-06-06; **not yet implemented.** peerspeak owns voice,
|
||||
pixelpass owns pixels — this doc is the seam between them.
|
||||
room. Locked 2026-06-06; **implemented 2026-06-06** (`src/screenshare/mod.rs` +
|
||||
presence-borne ticket; tests-green, the 2-machine gossip path not yet
|
||||
field-verified). peerspeak owns voice, pixelpass owns pixels — this doc is the
|
||||
seam between them.
|
||||
|
||||
## Open questions — resolved (Discord-modelled)
|
||||
|
||||
- **Concurrency:** multiple simultaneous sharers, Discord-style. Sharing rides
|
||||
*presence* (`PeerState.sharing: Option<ticket>`), so each sharer shows one
|
||||
offer; no artificial one-at-a-time cap.
|
||||
- **Late joiners:** yes — because sharing is presence, the existing gossip
|
||||
re-announce delivers an in-progress offer to a new joiner for free (no
|
||||
separate `ScreenShareOffer` message was needed; the doc's sketch is superseded
|
||||
by the presence-field approach).
|
||||
- **Viewer limit:** not overridden — pixelpass bandwidth-measures its own safe
|
||||
cap; a full host's `viewer_refused` surfaces as a UI error.
|
||||
- **UI placement:** per-peer card (🔴 Live + 👁 Watch on the sharer's card;
|
||||
🔴 Sharing badge + Stop on the self card) — the Discord "Live badge" model.
|
||||
- **Player:** viewer parses `connected{url}` and opens mpv (vlc fallback),
|
||||
mirroring pixelpass's own low-latency invocation.
|
||||
|
||||
## Core principle: mutually optional, runtime-only coupling
|
||||
|
||||
|
||||
Reference in New Issue
Block a user