This commit is contained in:
436
docs/videochat-umsetzungsplan.md
Normal file
436
docs/videochat-umsetzungsplan.md
Normal file
@@ -0,0 +1,436 @@
|
||||
# Videochat-Umsetzungsplan
|
||||
|
||||
## Ziel
|
||||
|
||||
SingleChat 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 SingleChat 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.
|
||||
Reference in New Issue
Block a user