Repository navigation
Architektura
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.
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)
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.
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.
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.
Użytkownicy
Serwer
Programiści