diff --git a/docs/proposals/skjermen.md b/docs/proposals/skjermen.md new file mode 100644 index 0000000..e927dd3 --- /dev/null +++ b/docs/proposals/skjermen.md @@ -0,0 +1,265 @@ +# Proposal: Skjermen — live-produksjon i arbeidsflaten + +## Konsept + +"Skjermen" er et panel i arbeidsflaten som fungerer som et +produksjonsbord. Du drar inn videostrømmer, bilder, tekst og +andre noder — og arrangerer dem fritt. Det som er i skjermen +er det som tas opp eller strømmes. + +Skjermen er ikke en video-editor. Den er en **live kompositor** +som bruker drag-and-drop med eksisterende noder. + +## Delt arbeidsflate som møterom/studio + +Et møterom eller studio er en delt arbeidsflate med riktige traits. +Alle deltakere ser sin egen arbeidsflate, men skjermen(e) er +synkronisert — alle ser samme komposisjon. + +``` +Delt arbeidsflate (podcaststudio): + + Synkronisert: Personlig: + ┌──────────────────────┐ ┌─────────┐ + │ SKJERM (program) │ │ Notater │ ← kun deg + │ Trond │ Arne │ └─────────┘ + │ Vegard│ Logo │ ┌─────────┐ + │ ●REC │ │ Chat │ ← alle ser + └──────────────────────┘ └─────────┘ + ┌──────────────────────┐ ┌─────────┐ + │ SKJERM (preview) │ │ Mixer │ ← delt + │ Arne (stor) │ └─────────┘ + │ Trond│Vegard (små) │ + └──────────────────────┘ +``` + +## Videoramme + +Hvert deltaker har et panel — "videoramme" — som fanger kamera +og mikrofon. Produserer en LiveKit-track som er en strøm-node. + +- Styres av eieren (velg kamera, mikrofon, on/off) +- Kan dras inn i skjermer av vert/produsent +- Samme strøm kan refereres fra flere skjermer +- Kan også fange skjermdeling + +## Skjerm-panelet + +### Grunnfunksjoner + +- **Drop-target:** aksepterer videostrømmer, bilder, tekst, noder +- **Fritt arrangement:** hvert element har posisjon, størrelse, z-order +- **Resizerbart:** elementene inne i skjermen resizes fritt +- **Aspect ratio:** låses til 16:9 (default), 4:3, 1:1 eller fritt +- **Bakgrunn:** farge, gradient eller bilde +- **Snap-to-grid:** valgfritt, for presis plassering + +### Ting som kan dras inn + +| Kilde | Resultat i skjermen | +|-------|---------------------| +| Videoramme (deltaker) | Live video-strøm | +| Bildenode (CAS) | Statisk bilde (logo, illustrasjon) | +| Tekstnode | Tittel, banner, navn-skilt | +| Chat-panel | Live chat-overlay | +| Lydvisualisering | Waveform/VU som visuelt element | +| Presentasjon | Slides (bla manuelt eller auto) | +| Skjermdeling | Deltakerens skjerm som strøm | +| Annen node | Rendres som kort/boks | + +### Fade og overganger + +**Master opacity slider:** +- 0% (helt svart) til 100% (fullt synlig) +- Live, sanntids — brukes som manuell fade + +**Sceneskifte-knapp:** +``` +1. Trykk "Sceneskifte" +2. Fade ut til svart (0.5-3s, konfigurerbar) +3. Opptak pauses automatisk ved full blackout +4. Bygg om scenen bak svartsensuren (flytt, resize, legg til, fjern) +5. Trykk "Sceneskifte" igjen (eller "Fortsett") +6. Opptak resumer +7. Fade inn fra svart (samme varighet) +``` + +I opptaksfilen ser det ut som én jevn overgang. Ingen klipping +nødvendig. Live jukseredigering. + +**Overgangstyper:** +- Fade til svart (default) +- Fade til hvitt +- Fade til egendefinert farge +- Crossfade (mellom to skjermer — se multi-skjerm) + +### Opptak + +- **REC-knapp** (rød prikk, synlig i skjerm-headeren) +- **Pause/resume** (usynlig i output, sømløst) +- **Tidskode-display** (total innspilt tid) +- **Markører:** klikk for å sette en markør ("dette var bra", + "klipp her") som lagres som tidsstempel-metadata +- **Output:** server-side compositing via FFmpeg, eller + canvas-capture API for klientsiden + +### Forhåndsdefinerte layouts + +Hurtigknapper for vanlige oppsett: +- **Fullskjerm:** én kilde fyller alt +- **2-split:** to kilder side om side +- **3-split:** tre kilder (1 stor + 2 små, eller 3 like) +- **PiP:** én stor + én liten i hjørnet +- **Grid:** 2x2 eller 3x3 +- Alltid mulig å justere manuelt etterpå + +## Multi-skjerm: Program / Preview + +### Konsept + +To (eller flere) skjermer som fungerer som TV-studio: + +- **Program:** det som tas opp/strømmes akkurat nå +- **Preview:** neste scene du forbereder + +Samme videostrøm kan ligge i begge, med ulik layout. +"Take" bytter dem. + +### Bytte mellom skjermer + +**Cut (øyeblikkelig):** +- Preview → Program, Program → Preview +- Bytte skjer på én frame + +**Take (med overgang):** +- Crossfade mellom Program og Preview +- Konfigurerbar varighet +- Program og Preview bytter roller etter overgangen + +**Manuelt:** +- Velg hvilken skjerm som er "live" via knapp/dropdown +- De andre er tilgjengelige for redigering + +### Brukseksempel: podcast med kameraveksling + +``` +Skjerm A (program): Alle tre i grid +Skjerm B (preview): Arne stor, Trond+Vegard små + +Arne snakker → produsent trykker "Take" → + Crossfade til Skjerm B (Arne stor) → + Skjerm A er nå preview, redigeres for neste bytte + +Trond tar over → produsent bygger Skjerm A om: + Trond stor, Arne+Vegard små → + Trykker "Take" → crossfade til Skjerm A +``` + +Alt live, under innspilling, uten klipping etterpå. + +### Ubegrenset antall skjermer + +Ikke begrenset til to. Du kan ha: +- Skjerm A: alle i grid (oversikt) +- Skjerm B: Arne stor (nærportrett) +- Skjerm C: Trond stor +- Skjerm D: Vegard stor +- Skjerm E: presentasjon fullskjerm +- Skjerm F: chat-overlay over video + +Bytt mellom dem etter behov. Hver har sin egen layout +men deler de samme kilde-strømmene. + +## Teknisk modell + +### Skjerm som node + +``` +node_kind: 'screen' +metadata: { + aspect_ratio: '16:9', + background: { type: 'color', value: '#000000' }, + elements: [ + { node_id: '', x: 0, y: 0, w: 50, h: 50, z: 1 }, + { node_id: '', x: 50, y: 0, w: 50, h: 50, z: 1 }, + { node_id: '', x: 80, y: 80, w: 15, h: 15, z: 10 }, + ], + is_program: true, // denne skjermen tas opp +} +``` + +Posisjon/størrelse i prosent (0-100) for oppløsningsuavhengighet. + +### Skjerm i arbeidsflaten + +Skjermen er et BlockShell-panel som: +- Viser intern canvas med elementene +- Synkroniseres via PG NOTIFY (alle ser samme layout) +- Kun `owner`/`admin` kan endre innhold (dra inn/ut, resize) +- Deltakere ser resultatet i sanntid + +### Opptak + +**Klientside (enklest):** +- Canvas-capture API → MediaRecorder → upload til CAS +- Begrensning: kvalitet avhenger av klientens maskin + +**Serverside (bedre):** +- Maskinrommet mottar layout-metadata + LiveKit-strømmene +- FFmpeg compositor renderer til fil i sanntid +- Uavhengig av klientens ytelse +- Kan produsere flere oppløsninger + +**Hybrid:** +- Klientside for preview/monitoring +- Serverside for endelig opptak + +### Overgangsmotor + +```rust +struct Transition { + from_screen: NodeId, + to_screen: NodeId, + kind: TransitionKind, // Fade, Cut, Crossfade, DipToColor + duration_ms: u32, + color: Option, // for DipToColor +} +``` + +Ved sceneskifte med pause: +``` +1. Start DipToColor(svart) overgang +2. Ved midtpunkt (100% svart): emit PauseRecording +3. Vent på ResumeRecording signal +4. Fullfør DipToColor (svart → transparent) +``` + +## Hva dette bygger på (eksisterende) + +- BlockShell: panel-wrapper med resize, drag, drop +- Canvas: spatial layout med pan/zoom +- LiveKit: WebRTC for video/lyd-strømmer +- Drag-and-drop: universell overføring mellom paneler +- CAS: lagring av opptak og bilder +- PG NOTIFY + WebSocket: synkronisering av skjerm-layout +- Node/edge-modell: alt er noder med relasjoner + +## Hva som er nytt + +- Skjerm-panel (canvas-i-canvas) med elementliste +- Videoramme-panel (kamera/mikrofon-fangst) +- Fade/overgang-motor +- Multi-skjerm switching (program/preview) +- Server-side compositing (FFmpeg) +- Opptak med pause/resume og markører + +## Prioritet + +Høy ambisjon, kan bygges inkrementelt: +1. Videoramme-panel (LiveKit-kamera i BlockShell) +2. Enkel skjerm (drop-target med elementer, statisk layout) +3. Opptak (klientside canvas-capture) +4. Fade/overgang +5. Multi-skjerm switching +6. Server-side compositing +7. Sceneskifte med auto-pause