Files
singlechat/docs/videochat-umsetzungsplan.md
Torsten Schulz (local) 5d4129b5b3
Some checks failed
Deploy SingleChat / deploy (push) Has been cancelled
Fixeds and enhancements
2026-06-17 15:44:11 +02:00

437 lines
14 KiB
Markdown

# Videochat-Umsetzungsplan
## Ziel
YpChat soll Videochat innerhalb einer bestehenden 1:1-Konversation unterstützen.
Rahmenbedingungen:
- Videochat ist nur aus einer bestehenden Chat-Konversation heraus erreichbar.
- Die Videochat-Funktion soll nur sichtbar sein, wenn beide Gesprächspartner sie für diese Konversation erlaubt haben.
- Es darf keine Direktverbindung zwischen den Endgeräten geben.
- Sämtliche Videoverbindungen laufen über Server-Infrastruktur, damit keine Peer-IP-Adressen offengelegt werden.
## Empfohlene Architektur
### 1. Trennung zwischen Chat-Signaling und Medien-Relay
Die bestehende Socket.IO-Infrastruktur bleibt für:
- Sichtbarkeit der Video-Funktion
- Freigabe-Status pro Konversation
- Einladung / Annahme / Ablehnung
- Klingelstatus
- Gesprächsstatus
- Fehler- und Abbruchsignale
Der Austausch der Grunddaten und Anfragen darf über Socket.IO laufen.
Die eigentlichen Audio-/Videoströme sollen ausdrücklich nicht über die bestehenden Chat-Sockets und nicht über die Chat-Message-Logik laufen, sondern über eine dedizierte serverseitige Medienebene.
### 2. Keine Peer-to-Peer-Verbindung
Empfehlung:
- WebRTC weiterverwenden, aber ausschließlich mit server-relaytem Medientransport
- kein P2P-Mesh
- keine Host-/srflx-Kandidaten verwenden
- nur relay-Kandidaten zulassen oder direkt eine SFU/Media-Server-Lösung einsetzen
Praktisch heißt das:
- Signaling kommt aus `server/broadcast.js`
- Medien laufen über einen separaten Media-Server oder eine integrierte SFU
- der Browser bzw. die mobilen Clients sehen nur die Server-Endpunkte, nicht die Gegenstelle
- Socket.IO transportiert nur Status, Einladungen, Freigaben und Session-Metadaten
- Audio/Video läuft ausschließlich über den Medienpfad
### 3. Bevorzugte Zielarchitektur
Für YpChat ist eine SFU-basierte 1:1-Lösung sinnvoller als vollständiges Server-Transcoding:
- bessere Latenz als kompletter zentraler Decode/Encode-Relay
- trotzdem keine direkte Verbindung zwischen Nutzern
- sauber erweiterbar für Android, iOS und Web
Planungsentscheidung:
- Node-Backend bleibt Orchestrator und Berechtigungsinstanz
- Video-Medienserver wird als eigene Komponente vorgesehen
- pro Videochat wird ein kurzlebiger Raum erzeugt
- Zutritt nur für die beiden durch den Chat verknüpften Nutzer
## Produktlogik
### 1. Sichtbarkeit
Die Videochat-Aktion ist nur sichtbar, wenn:
- `currentConversation` gesetzt ist
- beide Nutzer online bzw. erreichbar sind
- keine Blockierung aktiv ist
- beide Nutzer für genau diese Konversation `videoAllowed = true` gesetzt haben
Empfehlung:
- Freigabe nicht global pro Account, sondern pro Konversation speichern
- Freigabe explizit und widerrufbar machen
- Sichtbarkeit im Chat-Header und optional zusätzlich im Composer
### 2. Verbindungsgrenze pro Nutzer
Pro Nutzer dürfen maximal drei gleichzeitige Videoverbindungen aktiv sein.
Wenn ein vierter Videochat gestartet oder angenommen werden soll:
- lehnt der Server den Vorgang ab
- der anfragende Client erhält einen klaren Fehlerstatus
- die UI zeigt eine verständliche Meldung wie "Maximal drei Videoverbindungen gleichzeitig erlaubt"
Die Begrenzung muss serverseitig erzwungen werden, nicht nur in der UI.
### 3. Zustandsmodell pro Konversation
Pro Benutzerpaar wird zusätzlicher Konversationszustand benötigt:
- `localVideoConsent`
- `remoteVideoConsent`
- `videoVisible`
- `activeCallState`
- `incomingCall`
- `outgoingCall`
- `callRoomId`
Empfohlene Server-Zustände:
- `disabled`
- `local_allowed`
- `mutual_allowed`
- `ringing`
- `connecting`
- `active`
- `ended`
Zusätzlich pro Nutzer:
- `activeVideoConnectionCount`
- Liste aktiver Video-Sessions
- welches Video aktuell im Vordergrund ist
### 4. Gesprächsablauf
1. Nutzer A öffnet eine bestehende Konversation.
2. Nutzer A aktiviert "Videochat erlauben".
3. Nutzer B aktiviert dieselbe Freigabe.
4. Erst jetzt wird der Videochat-Button sichtbar.
5. Nutzer A startet einen Anruf.
6. Server sendet Einladung an Nutzer B.
7. Nutzer B nimmt an oder lehnt ab.
8. Bei Annahme erzeugt der Server einen kurzlebigen Call-Raum und gibt signierte Join-Daten an beide Clients zurück.
9. Beide Clients verbinden sich mit dem Medienserver.
10. Auflegen, Timeout oder Disconnect beendet Raum und Status.
Wenn ein Nutzer bereits drei aktive Videoverbindungen hat, scheitert Schritt 5 oder 7 mit einem Serverfehler.
## Anzeige- und Layoutkonzept
### 1. Rechte Preview-Spalte
Alle aktiven Videoverbindungen eines Nutzers werden rechts als Preview angezeigt.
Vorgaben:
- Position rechts im Browserfenster
- Breite etwa `1/5` des Browserfensters
- ganz oben ein festes Self-Preview des eigenen Video-Streams
- darunter maximal drei Video-Previews fremder aktiver Verbindungen
- damit insgesamt bis zu vier Previews sichtbar
- jede Preview zeigt mindestens:
- Videobild
- Name des Partners
- Mute-Zustand
- Status
- Aktion zum In-den-Vordergrund-Holen, außer beim Self-Preview
- Aktion zum Beenden
Empfehlung:
- eigener Container im Web-Layout, nicht im normalen Chat-Message-Flow
- auf kleineren Viewports responsiv umschalten, aber Desktop bleibt Referenz
- das Self-Preview ist rein informativ und kann nicht in den Vordergrund geholt werden
### 2. Vordergrund-Video als schwebendes Fenster
Ein aktives Video kann in den Vordergrund geholt werden.
Vorgaben:
- Darstellung als schwebendes Fenster
- frei verschiebbar
- über der normalen Chat-Oberfläche
- pro Zeitpunkt genau ein Vordergrundfenster
- Rückweg in die rechte Preview-Leiste möglich
Das Vordergrundfenster sollte enthalten:
- großes Videobild der ausgewählten Verbindung
- Name des Partners sichtbar im Fenster
- Mute-Zustand sichtbar im Fenster
- optional eigenes kleines Self-Preview
- Drag-Handle
- Schließen / minimieren
- Mute / Kamera aus
- Auflegen
### 3. Zustände in der UI
Die UI braucht zusätzlich zu den Call-States:
- `videoDockSessions`
- `selfPreviewStream`
- `foregroundVideoSessionId`
- `floatingVideoPosition`
- `maxVideoConnectionsReached`
## Technischer Zuschnitt
## Phasenstatus
- [x] Phase 1: Signaling und Zustände im Backend
- [x] Phase 2: Web-Client
- [x] Phase 3: Android
- [ ] Phase 4: Medienserver-Integration und End-to-End-Test
- [x] Server- und Web-Relaypfad via WebRTC mit Relay-Only-Signaling
- [x] Native Android-Medienebene
### Phase 1: Signaling und Zustände im Backend
Erweiterung von `server/broadcast.js` um neue Event-Familien:
- `videoConsent:set`
- `videoConsent:update`
- `videoCall:invite`
- `videoCall:incoming`
- `videoCall:accept`
- `videoCall:reject`
- `videoCall:cancel`
- `videoCall:start`
- `videoCall:end`
- `videoCall:error`
- `videoCall:capacity`
Zusätzliche In-Memory-Strukturen:
- Konversations-Metadaten getrennt von `conversations`
- aktive Call-Sessions
- Mapping `conversationKey -> consent/call state`
- Mapping `userName -> aktive Video-Sessions`
Wichtig:
- Call-Zustand an stabile Benutzeridentitäten koppeln, nicht nur an Socket-IDs
- Blocklisten auch für Video-Einladungen durchsetzen
- bei Reconnect Call-Zustand defensiv neu synchronisieren
- Verbindungsobergrenze von drei aktiven Video-Sessions pro Nutzer serverseitig prüfen
Status:
- erledigt
### Phase 2: Web-Client
Im Web-Store `client/src/stores/chat.js` ergänzen:
- Video-Consent-Status pro aktueller Konversation
- neue Socket-Listener für Video-Events
- Actions zum Setzen der Freigabe
- Actions zum Starten, Annehmen, Ablehnen und Beenden eines Calls
- UI-Flags für Ringing, Connecting, Active, Failed
- Verwaltung von bis zu drei parallelen aktiven Video-Sessions
- Auswahl, welches Video im Vordergrund schwebt
- Position des schwebenden Fensters
- Fehlerzustand für Kapazitätsgrenze
UI-Einstiegspunkte:
- `client/src/views/ChatView.vue`
- Sichtbarer Videochat-Button im Header nur bei `mutual_allowed`
- rechte Preview-Spalte für aktive Videos
- `client/src/components/ChatInput.vue`
- optionaler Toggle "Video erlauben" oder sekundärer Einstieg
- `client/src/components/ChatWindow.vue`
- Statusbanner für Einladung, Verbindungsaufbau, aktiv, beendet
Zusätzliche neue Web-Komponente:
- `client/src/components/VideoCallPanel.vue`
- lokales Vorschaufenster
- entferntes Video
- Annehmen / Ablehnen
- Kamera/Mikro muten
- Auflegen
- `client/src/components/VideoDock.vue`
- rechte Preview-Spalte mit festem Self-Preview plus bis zu drei Partner-Previews
- `client/src/components/FloatingVideoWindow.vue`
- verschiebbares Vordergrundfenster
Status:
- erledigt
### Phase 3: Android
Erweiterungen:
- `android/app/src/main/java/de/ypchat/android/data/model/SocketEvent.kt`
- `android/app/src/main/java/de/ypchat/android/data/model/Models.kt`
- `android/app/src/main/java/de/ypchat/android/data/api/SocketClient.kt`
- `android/app/src/main/java/de/ypchat/android/data/repository/ChatRepository.kt`
- `android/app/src/main/java/de/ypchat/android/ui/YpChatRoot.kt`
- `android/app/src/main/java/de/ypchat/android/ui/ChatViewModel.kt`
Benötigt werden:
- neue Eventklassen für Consent und Call-Status
- Repository-State für Sichtbarkeit, Einladung und aktiven Call
- UI für Toggle, Rufannahme und laufenden Call
- UI für bis zu drei Previews und ein hervorgehobenes Vordergrundvideo
- Medienintegration über dieselbe server-relayte Architektur wie im Web
Status:
- erledigt
### Phase 4: Medienserver-Integration und End-to-End-Test
Umfang:
- Auswahl und Einbindung der Relay-/SFU-Lösung
- Ausgabe serverseitiger Join-Daten für Calls
- Medienpfad an Web und Android anbinden
- End-to-End-Test aller Zustände
- Datenschutz- und Kapazitätsprüfungen abschließen
Aktueller Stand:
- Server und Web nutzen jetzt Relay-Only-WebRTC mit Signaling über Socket.IO
- tatsächliche Medien laufen nicht über Chat-Sockets
- Voraussetzung ist eine konfigurierte TURN-/Relay-Infrastruktur über Umgebungsvariablen
- Android nutzt jetzt ebenfalls den Relay-Only-WebRTC-Medienpfad mit nativer Laufzeit und Compose-Rendering
- offen bleibt vor allem der End-to-End-Test mit echter TURN-Konfiguration und Mehrgeräte-Verifikation
Status:
- offen
Benötigte Umgebungsvariablen für den Relay-Betrieb:
- `VIDEO_TURN_URLS`
- `VIDEO_TURN_USERNAME`
- `VIDEO_TURN_CREDENTIAL`
- optional `VIDEO_STUN_URLS`
- alternativ `VIDEO_ICE_SERVERS_JSON` als vollständige ICE-Serverliste
## Server-seitige Persistenzentscheidung
Für einen ersten Schritt kann der Consent-Zustand im RAM gehalten werden, analog zur aktuellen Chat-Architektur.
Empfehlung für produktiven Ausbau:
- Consent pro Benutzerpaar persistent speichern
- aktive Calls nur flüchtig speichern
Begründung:
- Sichtbarkeit des Video-Buttons soll nach Reconnect nicht zufällig verloren gehen
- aktive Calls dürfen bei Server-Neustart beendet werden, aber Consent sollte stabiler sein
## Sicherheits- und Datenschutzregeln
- Videochat nur für eingeloggte, aktive Chat-Teilnehmer
- keine Join-Daten ohne bestehende Konversation
- keine Join-Daten ohne gegenseitige Freigabe
- Blockierungen sperren auch Videochat
- Call-Räume sind kurzlebig und nur für zwei Teilnehmer gültig
- Tokens für Media-Join serverseitig signieren und kurz halten
- keine Offenlegung von Peer-IP-Adressen an Clients
- Logging nur auf Betriebsniveau, keine Speicherung von Medieninhalten
## Offene Architekturentscheidung
Vor der Implementierung sollte genau eine Medienstrategie festgelegt werden:
### Empfohlene Richtung
Server-relayter WebRTC-Videochat mit SFU.
Warum:
- erfüllt die Anforderung "keine Direktverbindung"
- ist für Web, Android und iOS gemeinsam nutzbar
- passt besser zu Echtzeit als Datei- oder Socket-Binary-Transfer
### Nicht empfohlen
- Video als Upload-/Download-Mechanik wie bei Bildern
- vollständiger Eigenbau eines Medienprotokolls über Socket.IO
- klassisches P2P-WebRTC mit STUN-only
## Kritische Dateien für die spätere Umsetzung
- `server/broadcast.js`
- `server/index.js`
- `client/src/stores/chat.js`
- `client/src/views/ChatView.vue`
- `client/src/components/ChatInput.vue`
- `client/src/components/ChatWindow.vue`
- `android/app/src/main/java/de/ypchat/android/data/model/SocketEvent.kt`
- `android/app/src/main/java/de/ypchat/android/data/model/Models.kt`
- `android/app/src/main/java/de/ypchat/android/data/api/SocketClient.kt`
- `android/app/src/main/java/de/ypchat/android/data/repository/ChatRepository.kt`
- `android/app/src/main/java/de/ypchat/android/ui/YpChatRoot.kt`
- `android/app/src/main/java/de/ypchat/android/ui/ChatViewModel.kt`
## Verifikation
### Funktional
1. Zwei Nutzer bauen eine normale Konversation auf.
2. Nur ein Nutzer erlaubt Videochat.
3. Prüfen, dass keine Videochat-Aktion sichtbar ist.
4. Zweiter Nutzer erlaubt Videochat.
5. Prüfen, dass die Aktion jetzt bei beiden sichtbar ist.
6. Nutzer A startet einen Call.
7. Nutzer B erhält Einladung.
8. Nutzer B lehnt ab, Status muss sauber zurückspringen.
9. Nutzer A startet erneut, Nutzer B nimmt an.
10. Beide verbinden sich ausschließlich über den Server-Relay-Pfad.
11. Drei parallele Videoverbindungen für einen Nutzer aufbauen.
12. Vierte Verbindung starten oder annehmen und korrekte Fehlermeldung prüfen.
13. Prüfen, dass rechts ein Self-Preview plus maximal drei Partner-Previews sichtbar sind.
14. Prüfen, dass das Self-Preview nicht vergrößert werden kann.
15. Prüfen, dass unter jedem Partner-Preview der Name und der Mute-Zustand sichtbar sind.
16. Ein Partner-Preview in den Vordergrund holen und als schwebendes Fenster verschieben.
17. Prüfen, dass auch im großen Fenster Name und Mute-Zustand sichtbar sind.
18. Minimieren und Rückkehr in die Preview-Leiste prüfen.
19. Auflegen, Browser-Reload, App-Hintergrundwechsel und Reconnect prüfen.
### Datenschutz
1. Prüfen, dass keine direkte Peer-Verbindung aufgebaut wird.
2. Prüfen, dass nur Relay-/Server-Kandidaten verwendet werden.
3. Prüfen, dass Blockierungen auch Video-Einladungen unterbinden.
### Regression
1. Textnachrichten weiter senden/empfangen.
2. Bildversand weiter senden/empfangen.
3. History, Inbox und Conversation-Reload dürfen unverändert funktionieren.
## Empfohlene Implementierungsreihenfolge
1. Event- und Zustandsmodell für Consent und Call-Lifecycle definieren.
2. Backend-Signaling in `server/broadcast.js` ergänzen.
3. Web-Client vollständig integrieren und als Referenzfluss stabilisieren.
4. Danach Android und iOS auf denselben Event-Vertrag heben.
5. Erst dann Medienserver produktionsnah anbinden und End-to-End testen.