Skip to content

Architektura

kauczu edited this page Oct 16, 2026 · 1 revision

Obraz i dźwięk płyną bezpośrednio między uczestnikami (P2P mesh). Serwer jest potrzebny do pierwszego „przedstawienia się” (sygnalizacji) i może nim być sama aplikacja desktopowa jednej z osób. Gdy mesh nie daje rady (3–4 osoby i słabe łącze, przeciążony procesor albo ruch przez TURN), pokój automatycznie przechodzi na SFU (mediasoup) i wraca do P2P, gdy zostaną 2 osoby.

Pakiety

packages/
  shared/    protocol.d.ts – typy protokołu sygnalizacji (klient ⇄ serwer)
  server/    Node + ws: pokoje, przekazywanie sygnałów, poświadczenia TURN, hosting klienta, HTTPS,
             SFU (mediasoup), polityka wyboru trybu mesh/SFU, moderacja
  client/    TypeScript + Vite (bez frameworka), PWA
  desktop/   Electron: wbudowany serwer, okno aplikacji, wybór źródła ekranu, nakładka, aktualizacje
  e2e/       testy end-to-end (Playwright)

Sygnalizacja

WebSocket, JSON: join → welcome (lista uczestników, serwery ICE, token wznowienia, stan pokoju), peer-joined / peer-left, signal (SDP/ICE do konkretnej osoby), relay (wiadomości aplikacji, zapas, gdy kanał danych nie jest jeszcze otwarty), quality / set-mode / mode (wybór trybu mediów), sfu → sfu-response

  • sfu-notify (zapytania do SFU), moderate / room-state / moderated (gospodarz pokoju).

Negocjacja bez kolizji: ofertę zawsze wysyła osoba dołączająca; druga strona tylko odpowiada. Po zestawieniu połączenia nic nie jest renegocjowane – przełączanie kamery, urządzeń i ekranu odbywa się przez replaceTrack. Kanały danych są pre-negocjowane (negotiated: true): app (stan, czat) i file (pliki).

Mesh i przepustowość: przy 4 osobach każdy wysyła 3 strumienie, więc limit bitrate wideo na połączenie maleje z liczbą osób (1,5 → 1,0 → 0,7 Mb/s).

Odporność: utrata WebSocketu nie zrywa P2P. Klient łączy się ponownie z tokenem wznowienia (HMAC id+pokój), dostaje to samo id, a zdrowe połączenia zostają. Każde RTCPeerConnection ma sid, więc nową sesję drugiej strony da się odróżnić od starej.

SFU: kiedy

Mesh jest optymalny dla 2 osób (i zwykle 3). Serwer (policy.ts) przełącza pokój na SFU w trybie automatycznym, gdy są co najmniej 3 osoby i:

  • ktoś przez ~15 s ma ograniczone wysyłanie wideo przez łącze (qualityLimitationReason = bandwidth) albo jego szacowany upload jest mniejszy niż (N−1) × 350 kb/s,
  • ktoś ma przeciążony procesor (qualityLimitationReason = cpu),
  • połączenia mesh idą przez TURN (SFU odciąża wtedy i klienta, i przekaźnik),
  • ktoś wysyła w wysokiej jakości (profil „Gra”) – wtedy bez czekania.

Warunek konieczny: wszyscy uczestnicy obsługują Szyfrowanie end-to-end klatek – inaczej pokój zostaje w P2P.

Raporty jakości klienci wysyłają co 5 s (tylko w trybie mesh, po 10 s „rozgrzewki” połączeń). Na SFU pokój zostaje, dopóki są co najmniej 3 osoby (bez „migotania”), i wraca do P2P przy 2. Każdy może też wybrać tryb ręcznie: Auto / P2P / SFU.

SFU: jak

Przełączanie bez przerwy (make-before-break): przy przejściu na SFU klient najpierw zestawia transporty mediasoup i zaczyna wysyłać, a dopiero potem wyłącza media na połączeniach mesh; kafelek pokazuje strumień z SFU, gdy ten już dociera. Połączenia mesh zostają otwarte jako kanały danych – czat i pliki nadal idą P2P. Przy powrocie do P2P media mesh są wznawiane przed zamknięciem SFU. Jeśli serwer (a więc SFU) zniknie, klienci natychmiast wracają do P2P.

Szczegóły: jeden worker mediasoup, jeden WebRtcServer (wszystkie media na jednym porcie UDP+TCP), router na pokój, simulcast kamery (3 warstwy od 540p, 2 poniżej – SFU wybiera warstwę dla każdego odbiorcy, suma mieści się w limicie wysyłania), Opus stereo z FEC. Moduł mediasoup-client jest ładowany leniwie – pobiera się dopiero, gdy pokój użyje SFU.

Clone this wiki locally