# 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

### Frameless modus

Noder som dras inn i skjermen rendres **som standard uten
grensesnittelementer** — ingen BlockShell-header, ingen resize-handles,
ingen border, ingen skygge. Bare innholdet. Skjermen er output.

Frameless er default, men **valgfritt per element**. Vert kan slå
på ramme, navneskilt, skygge eller andre visuelle effekter per
element i skjermen. Nyttig for f.eks. et bilde med border, eller
en videostrøm med semi-transparent navnebanner over.

Resize og flytting av elementer *inne i skjermen* gjøres av
skjermens egen editor-modus (kun tilgjengelig for vert), ikke
av BlockShell. Elementene er rene rektangler med innhold.

Dette betyr at noder trenger en `frameless`-renderingsmodus:
- **Videoramme:** bare `<video>` — ingen kontroller, ingen header
- **Bildenode:** bare `<img>` — ingen border, ingen metadata
- **Tekstnode:** bare teksten — med font/farge/størrelse
- **Chat-overlay:** bare meldingsstrøm — transparent bakgrunn

BlockShell brukes fortsatt for paneler *utenfor* skjermen
(i arbeidsflaten). Det er bare inne i skjerm-panelet at
innhold rendres frameless.

### Ting som kan dras inn

| Kilde | Resultat i skjermen |
|-------|---------------------|
| Videoramme (deltaker) | Ren video-strøm (frameless) |
| Bildenode (CAS) | Rent bilde (frameless) |
| Tekstnode | Ren tekst med styling (frameless) |
| Chat-panel | Meldingsstrøm med transparent bg |
| Lydvisualisering | Waveform/VU uten ramme |
| Presentasjon | Slides (frameless) |
| Skjermdeling | Deltakerens skjerm (frameless) |
| Annen node | Minimal rendering, tilpasset innhold |

### Fade og overganger

**Master opacity slider:**
- 0% (helt svart) til 100% (fullt synlig)
- Live, sanntids — brukes som manuell fade

**Sceneskifte-knapp (dedikert, godt synlig):**

Én knapp. To trykk. Ferdig.

```
Trykk 1: "Sceneskifte"
  → Rask fade til svart (0.2s default, konfigurerbar)
  → Opptak pauses automatisk ved full blackout
  → Knappen endrer tekst/farge: "Fortsett" (pulserer)
  → Bygg om scenen i ro og mak

Trykk 2: "Fortsett"
  → Opptak resumer
  → Rask fade inn fra svart (0.2s)
  → Knappen tilbake til "Sceneskifte"
```

I opptaksfilen: ...scene A [0.2s fade] scene B...
Ser ut som en profesjonell overgang. Pausen er usynlig.

Fade-varigheten er kort som default (0.2s) — dette er et
praktisk verktøy for å bytte scene raskt, ikke en kunstnerisk
overgang. Kan justeres i innstillinger om ønskelig.

**Master opacity slider (separat):**
- For manuell kontroll / kreativ bruk
- 0-100%, live
- Uavhengig av sceneskifte-knappen

**Overgangstyper (for multi-skjerm switching):**
- Fade til svart (default)
- Fade til hvitt
- Fade til egendefinert farge
- Crossfade (mellom to skjermer — se multi-skjerm)
- Cut (øyeblikkelig, ingen fade)

### 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: '<tronds-video>', x: 0, y: 0, w: 50, h: 50, z: 1 },
    { node_id: '<arnes-video>', x: 50, y: 0, w: 50, h: 50, z: 1 },
    { node_id: '<logo>', 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<Color>,  // 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

## Ytelsesanalyse (mars 2026)

### Serverspesifikasjoner

- 8 kjerner AMD EPYC @ 2.4 GHz (ingen hyperthreading)
- 16 GB RAM (~10 GB tilgjengelig)
- 204 GB ledig disk
- ~270 Mbit/s båndbredde (Hetzner)
- Ingen GPU

### Video-strømmer (LiveKit SFU)

LiveKit er en SFU (Selective Forwarding Unit) — den videresender
strømmer uten transkoding. Minimal CPU, primært båndbredde.

- 3 deltakere: 9 strømmer (3 inn + 6 ut), ~15-25 Mbit/s
- 10 deltakere: 100 strømmer, ~150-250 Mbit/s uten simulcast
- 10 deltakere med simulcast: ~80-120 Mbit/s (realistisk)
- LiveKit RAM: ~50-100 MB per sesjon med 3 deltakere

### Server-side compositing (FFmpeg)

FFmpeg compositor: flere video-strømmer → én output.
- 3 strømmer @ 720p → 1080p: ~2-3 CPU-kjerner kontinuerlig
- RAM: ~500 MB - 1 GB per FFmpeg-instans

### Kapasitetstabell

| Scenario | Gjennomførbart? | Flaskehals |
|----------|----------------|------------|
| 3 deltakere, klientside opptak | ✅ Enkelt | Ingen |
| 3 deltakere, server compositing | ✅ Ja | 2-3 CPU-kjerner |
| 10 deltakere, klientside | ✅ Ja (med simulcast) | Båndbredde |
| 10 deltakere, server compositing | ⚠️ Grenseland | CPU + båndbredde |
| 3 samtidige team, klientside | ✅ Ja | LiveKit RAM |
| 3 samtidige team, server comp. | ❌ Nei | CPU (trenger 6-9 kjerner) |
| 3 team klient + 1 team server | ✅ Ja | Balansert |

### Strategi

**Klientside compositing som default.** Skjerm-panelet bruker
Canvas API til å komponere video-strømmene i nettleseren.
Opptak via MediaRecorder API. Serveren bare videresender strømmer.

Fordeler:
- Serveren belastes minimalt — skalerer til mange samtidige team
- Null ekstra CPU for compositing
- Lavere latens (ingen round-trip til server)

Ulemper:
- Opptak-kvalitet avhenger av klientens maskin
- Klienten må ha nok CPU (moderne laptop/desktop er nok)

**Server-side compositing som valgfri premium:**
- For opptak i høyere kvalitet enn klienten klarer
- For eksport/re-rendering i etterkant
- Begrenset til én sesjon om gangen på nåværende server
- Købasert: "bestill rendering" → jobbkø → ferdig → varsling

**Oppgradering ved behov:**
- 16-kjerner, 32 GB hos Hetzner: ~€30/mnd ekstra
- Gir kapasitet for 2-3 samtidige server-side compositinger
- Eller: dedikert render-server (billig Hetzner-boks kun for FFmpeg)

## 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
