14 KiB
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:
currentConversationgesetzt ist- beide Nutzer online bzw. erreichbar sind
- keine Blockierung aktiv ist
- beide Nutzer für genau diese Konversation
videoAllowed = truegesetzt 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:
localVideoConsentremoteVideoConsentvideoVisibleactiveCallStateincomingCalloutgoingCallcallRoomId
Empfohlene Server-Zustände:
disabledlocal_allowedmutual_allowedringingconnectingactiveended
Zusätzlich pro Nutzer:
activeVideoConnectionCount- Liste aktiver Video-Sessions
- welches Video aktuell im Vordergrund ist
4. Gesprächsablauf
- Nutzer A öffnet eine bestehende Konversation.
- Nutzer A aktiviert "Videochat erlauben".
- Nutzer B aktiviert dieselbe Freigabe.
- Erst jetzt wird der Videochat-Button sichtbar.
- Nutzer A startet einen Anruf.
- Server sendet Einladung an Nutzer B.
- Nutzer B nimmt an oder lehnt ab.
- Bei Annahme erzeugt der Server einen kurzlebigen Call-Raum und gibt signierte Join-Daten an beide Clients zurück.
- Beide Clients verbinden sich mit dem Medienserver.
- 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/5des 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:
videoDockSessionsselfPreviewStreamforegroundVideoSessionIdfloatingVideoPositionmaxVideoConnectionsReached
Technischer Zuschnitt
Phasenstatus
- Phase 1: Signaling und Zustände im Backend
- Phase 2: Web-Client
- Phase 3: Android
- Phase 4: Medienserver-Integration und End-to-End-Test
- Server- und Web-Relaypfad via WebRTC mit Relay-Only-Signaling
- Native Android-Medienebene
Phase 1: Signaling und Zustände im Backend
Erweiterung von server/broadcast.js um neue Event-Familien:
videoConsent:setvideoConsent:updatevideoCall:invitevideoCall:incomingvideoCall:acceptvideoCall:rejectvideoCall:cancelvideoCall:startvideoCall:endvideoCall:errorvideoCall: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
- Sichtbarer Videochat-Button im Header nur bei
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.ktandroid/app/src/main/java/de/ypchat/android/data/model/Models.ktandroid/app/src/main/java/de/ypchat/android/data/api/SocketClient.ktandroid/app/src/main/java/de/ypchat/android/data/repository/ChatRepository.ktandroid/app/src/main/java/de/ypchat/android/ui/YpChatRoot.ktandroid/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_URLSVIDEO_TURN_USERNAMEVIDEO_TURN_CREDENTIAL- optional
VIDEO_STUN_URLS - alternativ
VIDEO_ICE_SERVERS_JSONals 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.jsserver/index.jsclient/src/stores/chat.jsclient/src/views/ChatView.vueclient/src/components/ChatInput.vueclient/src/components/ChatWindow.vueandroid/app/src/main/java/de/ypchat/android/data/model/SocketEvent.ktandroid/app/src/main/java/de/ypchat/android/data/model/Models.ktandroid/app/src/main/java/de/ypchat/android/data/api/SocketClient.ktandroid/app/src/main/java/de/ypchat/android/data/repository/ChatRepository.ktandroid/app/src/main/java/de/ypchat/android/ui/YpChatRoot.ktandroid/app/src/main/java/de/ypchat/android/ui/ChatViewModel.kt
Verifikation
Funktional
- Zwei Nutzer bauen eine normale Konversation auf.
- Nur ein Nutzer erlaubt Videochat.
- Prüfen, dass keine Videochat-Aktion sichtbar ist.
- Zweiter Nutzer erlaubt Videochat.
- Prüfen, dass die Aktion jetzt bei beiden sichtbar ist.
- Nutzer A startet einen Call.
- Nutzer B erhält Einladung.
- Nutzer B lehnt ab, Status muss sauber zurückspringen.
- Nutzer A startet erneut, Nutzer B nimmt an.
- Beide verbinden sich ausschließlich über den Server-Relay-Pfad.
- Drei parallele Videoverbindungen für einen Nutzer aufbauen.
- Vierte Verbindung starten oder annehmen und korrekte Fehlermeldung prüfen.
- Prüfen, dass rechts ein Self-Preview plus maximal drei Partner-Previews sichtbar sind.
- Prüfen, dass das Self-Preview nicht vergrößert werden kann.
- Prüfen, dass unter jedem Partner-Preview der Name und der Mute-Zustand sichtbar sind.
- Ein Partner-Preview in den Vordergrund holen und als schwebendes Fenster verschieben.
- Prüfen, dass auch im großen Fenster Name und Mute-Zustand sichtbar sind.
- Minimieren und Rückkehr in die Preview-Leiste prüfen.
- Auflegen, Browser-Reload, App-Hintergrundwechsel und Reconnect prüfen.
Datenschutz
- Prüfen, dass keine direkte Peer-Verbindung aufgebaut wird.
- Prüfen, dass nur Relay-/Server-Kandidaten verwendet werden.
- Prüfen, dass Blockierungen auch Video-Einladungen unterbinden.
Regression
- Textnachrichten weiter senden/empfangen.
- Bildversand weiter senden/empfangen.
- History, Inbox und Conversation-Reload dürfen unverändert funktionieren.
Empfohlene Implementierungsreihenfolge
- Event- und Zustandsmodell für Consent und Call-Lifecycle definieren.
- Backend-Signaling in
server/broadcast.jsergänzen. - Web-Client vollständig integrieren und als Referenzfluss stabilisieren.
- Danach Android und iOS auf denselben Event-Vertrag heben.
- Erst dann Medienserver produktionsnah anbinden und End-to-End testen.