Hoe groepsvideogesprekken werken: signaling, mesh en SFU-mediaservers

Een videogesprek tussen twee mensen kan rechtstreeks van het ene apparaat naar het andere gaan. WebRTC stuurt de camera en de microfoon van elke persoon direct naar de ander, alleen via een relay als er een firewall in de weg zit, zoals ons artikel over STUN- en TURN-servers uitlegt.
Komt er een derde persoon bij, dan werkt dezelfde aanpak nog. Bij een zesde begint het uit elkaar te vallen: het beeld bevriest, de ventilator van de laptop slaat aan en wie op een telefoon zit, valt weg. Groepsgesprekken vragen om een andere opzet, en bijna elke app die ze draait, komt op dezelfde uit.
In dit artikel lopen we de onderdelen langs: de signalingserver die elk gesprek nodig heeft, de mesh die werkt voor een paar mensen, de twee soorten mediaservers, en wat er verandert aan de versleuteling als er een server tussen zit.
Elk gesprek heeft signaling nodig
Voordat er audio of video stroomt, moeten de apparaten in een gesprek afspreken hoe. Elk apparaat beschrijft wat het kan versturen en ontvangen, de codecs, de resoluties en de adressen waarop het bereikbaar is, en de andere kant antwoordt. In WebRTC zijn dat de offer, de answer en de ICE-kandidaten.
WebRTC zegt niet hoe die berichten reizen. Dat laat het aan je app over, en het deel dat ze vervoert is de signalingserver. In de praktijk is dat een WebSocket-server waarmee elke pagina en app verbonden blijft, want een gesprek vraagt meer dan de eerste uitwisseling:
- Overgaan. Wie gebeld wordt, moet het gesprek binnen zien komen in elk tabblad en op elk apparaat dat open staat, en niet meer zodra er op één ervan is opgenomen.
- Wie er in het gesprek zit. Iedereen moet horen wanneer iemand binnenkomt of vertrekt, de camera aanzet of het scherm deelt.
- Wie mag deelnemen. Op de signalingserver wordt een gesprek toegelaten of geweigerd, dus daar bepaalt je app wie wie mag bellen en wie aan elk groepsgesprek deelneemt.
De signalingserver vervoert weinig data, maar hij moet in de lucht zijn wanneer er een gesprek kan beginnen, en hij moet je gebruikers kennen.
Mesh: iedereen stuurt naar iedereen
Het eenvoudigste groepsgesprek is een mesh. Elk apparaat opent een directe verbinding met elk ander apparaat en stuurt zijn camerabeeld naar elk van hen.
Met drie mensen verstuurt elk apparaat twee kopieën van zijn beeld en ontvangt het er twee. Dat gaat prima. Het probleem is hoe het groeit:
| Mensen in het gesprek | Kopieën die elk apparaat verstuurt | Verbindingen in het gesprek |
|---|---|---|
| 3 | 2 | 3 |
| 5 | 4 | 10 |
| 8 | 7 | 28 |
| 12 | 11 | 66 |
Een camerabeeld in goede kwaliteit wordt vaak met één tot twee megabit per seconde verstuurd. In een gesprek met acht mensen zou elk apparaat er zeven tegelijk versturen, meer dan veel thuis- en mobiele verbindingen stabiel kunnen uploaden. Meestal codeert het apparaat het beeld ook nog apart voor elke verbinding, en daardoor slaat de ventilator aan en loopt de telefoon leeg.
Mesh past goed bij drie of vier mensen op behoorlijke verbindingen, zonder server die media vervoert. Daarboven heeft het gesprek een server in het midden nodig.
De MCU: één gemengd beeld voor iedereen
Het oudere antwoord is een multipoint control unit, of MCU. Elk apparaat stuurt zijn beeld één keer naar de server. De server decodeert ze allemaal, tekent ze samen in één gecombineerd beeld, codeert dat opnieuw en stuurt dat ene beeld naar iedereen.
Elk apparaat verstuurt dan één stream en ontvangt er één, wat licht is voor telefoons en trage netwerken. De kosten verschuiven naar de server: elk beeld van elk gesprek decoderen en opnieuw coderen vraagt veel rekenkracht, het mengen voegt vertraging toe, en iedereen ziet dezelfde indeling, gekozen door de server. MCU’s kom je nog tegen waar één beeld nodig is, bijvoorbeeld als een telefoonlijn aan een videovergadering deelneemt, maar de meeste groepsgesprekken gebruiken tegenwoordig de andere soort server.
De SFU: één keer versturen, naar iedereen doorsturen
Een selective forwarding unit, of SFU, is de mediaserver waarop de meeste groepsgesprekken draaien. Elk apparaat stuurt zijn beeld één keer naar de SFU, en de SFU stuurt het door naar alle anderen in het gesprek, zonder het te decoderen.
Elk apparaat verstuurt één stream, hoeveel mensen er ook in het gesprek zitten. Het ontvangt één stream voor elke persoon die het toont, en daar komt het “selective” vandaan. Omdat de SFU tussen elke zender en elke ontvanger zit, kan hij kiezen wat ieder krijgt:
- Meerdere kwaliteiten van elke zender. Met simulcast stuurt een apparaat zijn beeld in twee of drie formaten tegelijk, en de SFU stuurt het kleine door naar wie het als miniatuur toont of een zwakke verbinding heeft, en het grote naar wie het schermvullend toont.
- Alleen wat in beeld is. In een gesprek met dertig mensen heeft een pagina die negen tegels toont de andere eenentwintig beelden niet nodig. De SFU stuurt ze pas weer door als ze getoond worden.
- Wie er spreekt. De SFU ziet de audioniveaus van iedereen en kan elke pagina vertellen wie er nu spreekt, zodat de pagina die persoon groot kan tonen.
- Sprekers en luisteraars. In een les of een panel versturen een paar mensen en ontvangt de rest alleen. De SFU stuurt de sprekers door en neemt niets aan van de luisteraars, zodat een gesprek kan groeien tot veel meer mensen dan er in beeld zijn.
Omdat de SFU de media nooit decodeert, kan één server heel veel streams doorsturen. Een heel groot gesprek kan over meerdere SFU’s verspreid zijn, die elk de mensen bij hen in de buurt bedienen en de streams onderling doorgeven, zodat mensen op verschillende continenten elk verbinding maken met een server dicht bij hen.
Als één persoon voor iedereen presenteert en het publiek alleen kijkt, verandert de opzet weer: dat is een livestream, gebouwd voor een publiek van elke omvang met een chat ernaast, en die behandelen we in livestreaming voor je app.
Versleuteling met een server in het midden
Elke WebRTC-verbinding is versleuteld, dus niemand op het netwerk tussen een apparaat en de server kan het gesprek zien of horen. In een direct gesprek tussen twee mensen is dat end-to-end: alleen de twee apparaten hebben de sleutels.
Met een SFU eindigt de verbinding van elk apparaat bij de server. De SFU ontsleutelt wat binnenkomt en versleutelt het opnieuw voor elke ontvanger, dus de server zou alles kunnen zien en horen wat hij doorstuurt. Voor veel apps is dat aanvaardbaar. Voor een arts en een patiënt, een advocaat en een cliënt, of een team dat iets vertrouwelijks bespreekt, niet.
Het antwoord is om elk frame audio en video een tweede keer te versleutelen, in het apparaat zelf, voordat het vertrekt, met een sleutel die de server nooit heeft. Huidige browsers ondersteunen dat via WebRTC encoded transforms. De SFU ziet nog steeds welke stream welke is en stuurt ze door zoals eerst, maar wat hij doorstuurt is verzegeld. De sleutels komen van je eigen server, die ze alleen geeft aan de mensen die in het gesprek mogen.
De keerzijde is dat een server die de media niet kan lezen, ze ook niet kan mengen, opnemen of naar een ander formaat omzetten. Daar gaat het juist om.
Je eigen SFU draaien
Er bestaan opensource-SFU’s, en ze zijn degelijk. Een team dat er zelf een draait, neemt dit op zich:
- Servers met openbare adressen en open UDP-poorten, in de regio’s waar je gebruikers zitten, met bandbreedte voor elke stream die ze doorsturen.
- Ook TURN-relays, omdat sommige mensen de SFU nog steeds niet rechtstreeks kunnen bereiken, net als in een gesprek tussen twee mensen.
- Een signalingserver die je gebruikers kent, hun apparaten laat overgaan en bepaalt wie deelneemt.
- Elk gesprek een plek geven, zodat de deelnemers terechtkomen op een server bij hen in de buurt die nog ruimte heeft, en een groot gesprek over meerdere servers spreiden.
- Alles draaiende houden: updates, monitoring, en gesprekken die naar een andere server verhuizen als er een uitvalt.
Niets daarvan is exotisch, maar het is een dienst die je draaiende moet houden zolang je app gesprekken heeft.
Hoe WordPlus Cloud groepsgesprekken draait
WordPlus Cloud draait het allemaal voor je, van de signaling tot de mediaservers:
- Signaling over de verbinding die je pagina’s al hebben. Dezelfde realtime verbinding die je ruimtes en wie er online is vervoert, laat een gesprek overgaan in elk tabblad en elke app van wie gebeld wordt, en vertelt iedereen in een groepsgesprek wie binnenkomt, vertrekt en spreekt.
- Groepsgesprekken op onze mediaservers, in meerdere regio’s. Elk apparaat verstuurt zijn camerabeeld één keer, hoeveel mensen er ook in het gesprek zitten, en er is geen limiet op het aantal mensen in een gesprek.
- Je server beslist. Een regel op je server bepaalt wie aan elk groepsgesprek mag deelnemen, wie spreekt en wie alleen luistert, en wie host is. Hosts zetten een microfoon uit, laten iemand spreken en verwijderen iemand, en je server kan hetzelfde doen zonder dat er een pagina open is.
- End-to-end versleuteling wanneer jouw regel dat zegt. Elk frame wordt in de browser versleuteld met een sleutel die je server afleidt, dus onze mediaservers sturen door wat ze niet kunnen lezen. Een gesprek tussen twee mensen waar iemand anders bij komt, wordt een groepsgesprek en blijft end-to-end versleuteld.
- Een belscherm klaar voor gebruik, of je eigen. Gebruik het kant-en-klare gespreksvenster op het web, iOS en Android, of bouw je eigen interface met dezelfde SDK.
Het werkt vanuit Next.js, Laravel, Django en andere stacks met onze SDK’s, en Better Messages draait zijn groepsgesprekken op WordPress op dezelfde mediaservers. Videogesprekken en groepsgesprekken voor je app laat zien wat de SDK’s je geven, en hoe gesprekken werken in onze documentatie laat de code zien.