docs: Live Control Room — multiview, switcher, audio, overlays
The engineering guide still called the control room 'planned' while it ships: multiview grid (2x1-4x4/custom, 4 live HLS tiles, tap-to-focus, adaptive preview polling), broadcast switcher (PST/TAKE/CUT semantics, lock, override modes), per-slot + master audio, overlay editor (lower_third/chyron/logo/clock, layers, rendered revisions), signed previews and the transactional director behind it. User manual gets the operator walkthrough (§7.1); engineering guide documents the media- control architecture and corrects the Level-2 status section; admin guide gets permissions, PUBLIC_ORIGIN, capacity and tally notes.
This commit is contained in:
parent
b8671113f5
commit
ced5e5da63
3 changed files with 59 additions and 5 deletions
|
|
@ -115,15 +115,26 @@ curl -X PATCH https://api.occupymedia.cc/connections/$CONN/publish-governance \
|
|||
|
||||
While held, the publish/boost route answers `202 awaiting_publish_approval` with the poll id; the poll closes after a day and rejects unanswered. Reconnecting an account keeps the setting. Changing the setting does not cancel an already-open poll. The Mastodon **boost** route runs behind the same gate.
|
||||
|
||||
## 6.2 Files SSO (Hub as the OIDC issuer for oCIS)
|
||||
## 6.2 Control Room & media workers
|
||||
|
||||
The Live Control Room drives real encoders — gate it carefully. Permissions: `media.view`/`director.view` (watch), `director.take` (TAKE/CUT/PST), `director.lock_program`, `director.override_auto`, `director.change_layout` (overlays), `media.manage` (desired spec).
|
||||
|
||||
Operational notes:
|
||||
|
||||
- A channel must be **assigned to a media worker** before any switch succeeds ("channel is not assigned to a media worker"). Configure ingest sources and scenes first; select the preview bus.
|
||||
- Capacity admission is part of every command transaction: an over-quota start is queued (`PENDING_CAPACITY`) and a stop offers its freed capacity to that queue.
|
||||
- Preview URLs are signed capabilities on the API origin — set `PUBLIC_ORIGIN` correctly or same-origin checks reject the browser's preview requests.
|
||||
- The worker reports the **realized version**; the UI's tally says "Tally unknown" until the worker confirms. If panels show thumbnails but never go live, check native HLS support and that preview signatures aren't being stripped by a proxy.
|
||||
- Every switch is idempotent (`Idempotency-Key`) and lands in the audit chain as `director.<operation>` — incident review replays commands from the audit log.
|
||||
|
||||
## 6.3 Files SSO (Hub as the OIDC issuer for oCIS)
|
||||
`files.occupymedia.cc` signs in through the Hub: the issuer is `https://api.occupymedia.cc/oidc`, client id `web`, PKCE only, auto-consent for that first-party client. Phase 2 (migration 034) makes sessions revocable and adds rotating refresh tokens bound to the Hub session — Hub logout reaches Drive within one access-token TTL (≤1 h). Operational notes:
|
||||
|
||||
- `OIDC_KEY_DIR` must be a persistent volume (the API generates and seals its ES256 keyset there on first boot; losing it logs every Drive session out).
|
||||
- `FILES_OCID_USER` and `OIDC_RESERVED_USERNAMES` are never claimable as files accounts (`admin` is reserved by default).
|
||||
- Sign-in requires an **active membership** and a username usable as an account name; otherwise the consent endpoint fails closed.
|
||||
|
||||
## 6.3 Verification harnesses
|
||||
## 6.4 Verification harnesses
|
||||
|
||||
```bash
|
||||
RADAR_EMAIL=… RADAR_PASSWORD=… node scripts/e2e/governance-sim.mjs # S1–S9 governance scenarios, live
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ Route: `PATCH /connections/:id/publish-governance` (requires `chatgov.manage`).
|
|||
|
||||
## 6. Frontend (apps/web)
|
||||
|
||||
Tailwind v4 `@theme` tokens (dark-first OKLCH, `@theme inline` brand hook for tenant theming), hand-rolled primitives, cmdk palette, TanStack Virtual streams (500+ items/column, scroll-into-view), Tiptap compose with per-platform char counters/previews. **Streams flagship:** capability-gated amplify bars, keyboard `j/k` navigate, `r` repost, `q` quote. All data from `src/lib/` mock-registry (fixtures are seeded-deterministic). QA: `apps/web/scripts/visual-qa.mjs` — 14 states, 0 console errors; byte-diff confirms every interaction renders.
|
||||
Tailwind v4 `@theme` tokens (dark-first OKLCH, `@theme inline` brand hook for tenant theming), hand-rolled primitives, cmdk palette, TanStack Virtual streams (500+ items/column, scroll-into-view), Tiptap compose with per-platform char counters/previews. **Streams flagship:** capability-gated amplify bars, keyboard `j/k` navigate, `r` repost, `q` quote. The Control Room (`app/(main)/control-room/`) runs on the live API — signed preview taps, realtime tally over the websocket client, fixture fallback when offline. QA: `apps/web/scripts/visual-qa.mjs`.
|
||||
|
||||
## 7. Testing strategy
|
||||
|
||||
|
|
@ -150,9 +150,17 @@ pnpm --filter @tactical-media-broadcast-hub/api test
|
|||
|
||||
`/healthz` (liveness), `/readyz` (TCP probes: postgres/redis/temporal with per-component status). OTel OTLP tracing skeleton (disabled-safe without collector). Structured pino logs with credential-key redaction. Grafana (3001) provisioned against Prometheus (9090). Full metrics rollout is WP14.
|
||||
|
||||
## 9. Level 2 systems *(planned — designs in proposal.md §4.4)*
|
||||
## 9. Level 2 systems — status
|
||||
|
||||
Live Source Monitor (adaptive polling, anti-flapping, per-host budgets); media engine (persistent encoder, graph-rewire switching, GStreamer decision gate); Auto Director (policies/rules/override/program-lock); control room (Preview/Program, TAKE/CUT); archive/grabber/crosspost (S3-mandated, encrypted, content-deduped).
|
||||
Shipped and wired end-to-end:
|
||||
|
||||
- **Media control / director** (`apps/api/src/media-control/`, `apps/api/src/production/`): the `PUT desired` / `take` / `cut` / `pst-select` / `lock` / `override` surface. Each command is a single PostgreSQL transaction — channel row `FOR UPDATE`, idempotency receipt lookup, director-state fold (locked/override), bus moves, capacity admission (§4.4 quota; over-quota rolls back everything, a stop drains the pending queue), desired-version floor across DB+Redis, and an `audit_append` receipt (`director.<op>`, the transactional outbox). The worker observes desired snapshots over signed channels and reports the **realized version**; tally in the UI is worker-observed state, never optimistic.
|
||||
- **Production spec** (`production.ts`, mirrored in `apps/media-worker`): scenes (`single/pip/2x1/2x2/3x3/4x4/custom`, per-slot `fit`, per-slot audio `off/on/primary` + gain, ≤1 primary slot), sources (HLS URL or video-asset playlist), overlays (`lower_third/chyron/logo/clock`, layers 0–7, rendered revisions as content-addressed PNGs, chyron speed/strip params, server clock), transitions (`cut` or `fade` 300–2000 ms), program master gain. Strict parsers reject injectable strings (`;`, `[`, `,`, `'`, `movie=`) so nothing rides along into FFmpeg graphs.
|
||||
- **Control Room UI** (`apps/web/src/app/(main)/control-room/`): multiview grid (2x1–4x4/custom, up to 4 live HLS tiles, tap-to-focus, visibility-gated 1 s/2 s preview polling), switcher bar (PST/TAKE-at-frame-boundary/CUT-immediate, lock, override modes `until_released/five_minutes/until_source_offline/until_next_event`), PST preview, audio monitor, overlay editor (`director.change_layout`), fixture mode when offline. Permission gates: `media.view`, `director.view`, `director.take`, `director.lock_program`, `director.override_auto`, `director.change_layout`, `media.manage`.
|
||||
- **Signed previews** (`production/preview.ts` + controller): capability URLs (expires+HMAC) minted by the authenticated endpoint; the public read route is same-origin only, serves plaintext renders, never redirects encrypted S3 objects, ETag/304-aware.
|
||||
- **Worker fence** (`apps/api/src/director/`): internal `state/fence/apply` endpoints the worker uses to apply desired snapshots under a fencing token; web ingest, PeerTube and the transcode pool per their own docs.
|
||||
|
||||
Still planned: media-engine graph rewiring beyond the current FFmpeg-normalize path, LLM video AI (design only), content search rollout.
|
||||
|
||||
## 10. Key file map
|
||||
|
||||
|
|
@ -165,6 +173,8 @@ Live Source Monitor (adaptive polling, anti-flapping, per-host budgets); media e
|
|||
| RBAC guard | `apps/api/src/rbac/rbac.guard.ts` |
|
||||
| Audit chain | `migrations/001_tenants.sql` (SQL) + `apps/api/src/audit/` |
|
||||
| Publishing + publish governance + amplify | `apps/api/src/publishing/`, `apps/scheduler/src/activities/dispatch-gate.ts` |
|
||||
| Media control / director / production spec | `apps/api/src/media-control/`, `apps/api/src/production/`, `apps/media-worker/src/production.ts` |
|
||||
| Control Room UI | `apps/web/src/app/(main)/control-room/`, `apps/web/src/components/control-room/` |
|
||||
| Matrix SSO broker | `apps/api/src/matrix-sso/` |
|
||||
| Connector SDK | `packages/connector-sdk/src/` |
|
||||
| Frontend mock-registry | `apps/web/src/lib/` |
|
||||
|
|
|
|||
|
|
@ -205,6 +205,39 @@ The math is real: it uses the connector SDK's `capacitySnapshot` rate policy. In
|
|||
|
||||
---
|
||||
|
||||
## 7.1 Live Control Room — multiview switching on a large screen
|
||||
|
||||
**Where.** **Control Room** in the sidebar (`/control-room`). Built for a big browser window: on wide viewports you get the full switcher layout — multiview grid and switcher bar on one side, PST preview, audio monitor and overlay editor on the other. On a phone it collapses to a single focused tile.
|
||||
|
||||
**Prerequisites.** A **virtual channel** assigned to a media worker, with ingest sources and production scenes configured, and the preview bus selected. Without that the page says exactly that and shows no panels. Every panel needs `media.view`/`director.view`; switching needs `director.take`; locking needs `director.lock_program`; overriding auto-director needs `director.override_auto`; changing overlays needs `director.change_layout`.
|
||||
|
||||
**The multiview grid.** Pick a layout — **2x1, 2x2, 3x3, 4x4, or custom** (1–4 columns). Tile 1 is always **PGM** (the encoder's actual output). Every other tile is an ingest source. Behavior per tile:
|
||||
|
||||
- **Tap to focus.** A focused tile is promoted into the limited live slots (up to 4 on desktop, 1 on mobile).
|
||||
- **Upgrade live / Remove live** (3x3/4x4/custom tiers): promote a tile from thumbnail refresh to a live HLS video.
|
||||
- **Tally borders.** Red border = on program, green = on preview. Tally comes from the **worker's realized version** — until the worker confirms it applied your desired state, the header says *"Tally unknown · awaiting matching observed version"* and borders stay neutral.
|
||||
- **Bandwidth care.** Selected tiles poll previews every 1 s, the rest every 2 s; polling pauses when the tab is hidden or a tile scrolls off-screen. Live video uses native HLS; without it you get continuously refreshed thumbnails.
|
||||
- The desired/observed version strip at the top (`Desired v12 · Observed v11`) shows whether the worker has caught up.
|
||||
|
||||
**Switching.** The switcher bar works in broadcast semantics:
|
||||
|
||||
1. **PST scene** — pick the scene to prepare (layouts: `single`, `PiP`, `2x1`, `2x2`, `3x3`, `4x4`, custom).
|
||||
2. **TAKE** — PST→PGM at the *next ready frame boundary* (clean cut).
|
||||
3. **CUT** — request an *immediate* switch.
|
||||
4. Both wait for worker confirmation; nothing claims success from the click alone.
|
||||
5. **Lock program** — freezes switching (the bar shows `LOCKED`).
|
||||
6. **Operator override** — pull the channel out of automation: `Until released`, `Five minutes` (auto-expires), `Until source offline`, `Until next event`, or back to `auto`. The status word reads `AUTO`, `MANUAL`, or `LOCKED`.
|
||||
|
||||
Every switch is an idempotent, audited command (`Idempotency-Key` safe to retry); the API refuses when the preview bus is empty or the worker is not assigned.
|
||||
|
||||
**Audio.** Scenes carry per-slot audio: `off`, `on`, or `primary` (at most one primary slot per scene) with a per-slot gain in dB, plus a program **master gain**. The **Audio Monitor** panel meters the monitored taps; muted-and-polled like the video tiles.
|
||||
|
||||
**Overlays.** The **Overlay Editor** manages per-scene graphics: **lower third**, **chyron** (scrolling strip — speed and strip width are set per overlay), **logo**, and **server clock** (`HH:MM` / `HH:MM:SS`). Overlays stack in layers 0–7 at a set position; rendered overlays go through a render revision (the worker rasterizes them to PNG) and only *rendered* revisions can go live. Editing requires `director.change_layout`.
|
||||
|
||||
**Offline / fixture mode.** With the API unreachable the page still renders with fixture data, clearly marked — buttons record the request but *"Tally awaits a simulated observed frame."*
|
||||
|
||||
---
|
||||
|
||||
## 8. Team
|
||||
|
||||
**Status: live (members, invite, role change, remove) · sample data (asset library, always).**
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue