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

14 KiB

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

  • 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: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.