Guide · System design
Design Video Conferencing Like Zoom: SFU vs MCU and WebRTC
23 min readUpdated By Saket Bhatnagar
Researched and drafted with AI, reviewed and edited by Saket Bhatnagar.
In short
- Video conferencing system design is planning how live audio and video move between people with low delay. Apps use WebRTC, the calling technology built into browsers, plus a media server in the middle.
- Before media flows, the apps swap call details through a signaling server you build. ICE, the path-finding step, then uses a STUN server to learn public addresses and a TURN server to relay when nothing direct works.
- Mesh, where every phone sends a copy to every other phone, works for one-to-one calls. Each extra person adds one more upload for everyone, so group calls need a server.
- An SFU (selective forwarding unit) forwards each person's stream without decoding it. An MCU (multipoint control unit) decodes, mixes and re-encodes one combined picture. For group calls, choose an SFU.
- Beginners can stop after the SFU vs MCU comparison. Engineers then get a runnable two-peer WebRTC call showing the encryption lines in an offer, a TURN relay check, safe server removal and SFrame end-to-end encryption.
What goes wrong when a video call grows from two people to five?
The video freezes, because every phone now sends and receives more video than its network can carry. The fix is not a faster phone. It is a different shape for the call, and that shape is the core of video conferencing system design.
Picture a common setup. A small engineering team runs a telehealth service that lets doctors at a city clinic in Visakhapatnam see patients in coastal villages over video. The setup is made up, but the problem is real.
The naive first version connected the doctor and patient directly, phone to phone. For two people it worked well. Then the clinic started group consults: a doctor, a patient, a family member in Hyderabad, an interpreter and sometimes a heart specialist.
With five people, every phone had to send its camera four times, once to each other person. A patient on village 4G had about 3 Mbps (megabits per second) of upload. Four HD (high-definition) copies needed about 6 Mbps (made up). The patient's video froze.
The rest of this guide follows the team as it rebuilds the service one decision at a time, with the same five-person consult as its test case.
What is video conferencing system design?
Video conferencing system design is planning how live audio and video travel between many people with low delay, good quality and privacy. It covers the app, the servers that set up calls, and the servers that carry media.
Live video is different from a video site. On a video site, the player can buffer (load a few seconds ahead) and nobody minds. In a call, a two-second delay makes people talk over each other. So a call keeps only a small buffer, and data that arrives too late to play is skipped rather than waited for.
Before drawing any boxes, the team wrote down what the service must do:
- Start a call in a few seconds from a link sent by SMS, with no app install for family members.
- Support rooms of 2 to 8 people, with about 1,500 rooms at the busiest hour (made up).
- Keep delay low enough for a natural talk, with a target of 300 ms (milliseconds) one way (made up).
- Keep working on weak village networks, even if video drops to a small picture or audio only.
- Encrypt every call, because consults carry private health details, and record only when the patient agrees.
The first four are about the network. The last is about trust. Most of the design comes down to one question: where does the video go between the sender and the receivers?
How does WebRTC connect two people in a video call?
WebRTC connects two people in three moves: the apps swap call details through signaling, find a working network path with ICE, then send encrypted media over that path. Modern browsers ship WebRTC built in, and native apps add a WebRTC library.
WebRTC (Web Real-Time Communication) is a set of browser APIs and network protocols for live audio, video and data. Its main API object is RTCPeerConnection, which holds one connection to another endpoint.
Signaling: swapping call details
Before any video flows, the two apps must agree on details. These include which codecs (ways of compressing video) they support and which network addresses might work. The details travel as an offer and an answer written in SDP, a plain text format for describing sessions.
The W3C WebRTC spec, a W3C Recommendation whose latest version is dated 13 March 2025 as of 19 October 2026, does not define how they travel. It says they go by unspecified means. So you build signaling yourself. The team uses a WebSocket (a connection that stays open so either side can send at any time) to its own signaling server.
ICE, STUN and TURN: finding a path
Many phones and laptops sit behind NAT, a router feature that shares one public address among many devices. NAT hides the device's real address, so the other side cannot simply connect to it. ICE (Interactive Connectivity Establishment) is the method for finding a path anyway. It is defined in RFC 8445; an RFC is a numbered internet standards document.
- Host candidate: an address on the device's own network card. It works when both people sit on the same network.
- Server reflexive candidate: the public address the NAT gives the device. A STUN server tells the device what that address is.
- Relayed candidate: an address on a TURN server, which forwards every packet for you. It works on almost any network, but you pay for the bandwidth.
RFC 8445 also defines a fourth kind, the peer reflexive candidate, which an app discovers during the checks themselves. Each side sends its candidates through signaling and tests pairs with small STUN requests. One side, the controlling agent, then nominates a working pair.
ICE prefers direct routes. RFC 8445 recommends a type preference of 126 for host candidates and 0 for relayed ones, so TURN is the last resort. Follow the boxes below from address gathering to the media flowing.
How a one-to-one call starts
Drag to exploreEach app gathers its candidate addresses before it sends its own offer or answer. Notice that media takes the path ICE finds: direct if possible, through TURN if not.
Diagram connections:
- Apps learn public addresses to Doctor sends an offer
- Doctor sends an offer to Offer reaches the patient
- Offer reaches the patient to Patient sends an answer
- Patient sends an answer to Direct path works?
- Direct path works? to Media flows directly (Yes)
- Direct path works? to Media goes through TURN (No, firewall blocks)
- Apps learn public addresses: Both apps → STUN server
- Doctor sends an offer: Doctor app → Signaling server
- Offer reaches the patient: Signaling server → Patient app
- Patient sends an answer: Patient app → Signaling server → Doctor app
- Direct path works?: Doctor app → Patient app, STUN checks
- Media flows directly: Doctor app → Patient app
- Media goes through TURN: Both apps → TURN server
RFC 8656, the TURN standard, notes that some firewalls block UDP entirely. UDP is quick delivery with no built-in resending; TCP is reliable, in-order delivery. TURN's default ports are 3478 for UDP and TCP, and 5349 for TURN over TLS (the encryption behind HTTPS). You can also run TURN over TLS on port 443, the HTTPS port, so a firewall that allows web traffic may let it through.
In the team's logs, about 15 percent of calls end up relayed (made up). Your own rate decides your TURN bill.
Why does peer-to-peer mesh break down in group calls?
Mesh breaks down because each person must upload a separate copy of their video to every other person. Upload is often the slower direction of a home or mobile connection, so it runs out first.
In a mesh, every pair of people has its own RTCPeerConnection. With n people, each one sends n minus 1 streams and receives n minus 1 streams. The phone also compresses its camera picture once per copy, which drains the battery.
Pause and predict: with a media server in the middle, how many video streams does each person upload in the five-person consult? The short Python script below counts them. It assumes 1,500 kbps (kilobits per second) for one HD stream, a made-up round number.
HD_KBPS = 1500 # one HD stream, a made-up round number
UPLOAD_KBPS = {
"mesh": HD_KBPS, # each copy is a full HD stream
"sfu": 1500 + 500 + 150, # one upload: high, medium and low encodings
"mcu": HD_KBPS, # one HD stream to the mixer
}
def streams_per_person(n, topology):
"""Streams one person sends and receives in an n-person call."""
others = n - 1
if topology == "mesh":
return others, others # a copy to and from every other person
if topology == "sfu":
return 1, others # one upload to the SFU, one stream per other person
if topology == "mcu":
return 1, 1 # send once, receive one mixed picture
raise ValueError(topology)
for t in ("mesh", "sfu", "mcu"):
up, down = streams_per_person(5, t)
print(f"{t}: sends {up}, receives {down}, uploads {up * UPLOAD_KBPS[t]} kbps")Run it with python3 and you should see these three lines:
mesh: sends 4, receives 4, uploads 6000 kbps
sfu: sends 1, receives 4, uploads 2150 kbps
mcu: sends 1, receives 1, uploads 1500 kbpsWhat just happened? Mesh uploads grow with the room: 6,000 kbps for five people, double the patient's 3 Mbps. With an SFU, the patient sends one upload to the server carrying three separate encodings, 2,150 kbps in total, whatever the room size. An MCU also cuts downloads to one stream, but the server pays for that in processing.
What is an SFU and how does it work?
An SFU (Selective Forwarding Unit) is a media server that receives each person's stream and forwards copies to the others, without decoding or mixing the video. It picks which streams, and which quality, each receiver gets.
Picture a Carnatic kutcheri (a classical concert) in a sabha hall. The singer, violinist and mridangam player each have a stage monitor, a small speaker that faces the performer. Each artist hears every other musician as a separate feed and sets their own levels.
The violinist turns the singer up to follow the melody closely. The mridangam player turns the violin down. Nobody blends the music for them. That is an SFU: separate feeds, chosen per listener, with the combining done at each end.
Where the picture breaks: stage monitors carry sound instantly across a room. An SFU sends compressed packets over a network, and every receiver must decode each stream it gets, which costs CPU and battery on the receiver's device.
RFC 7667 from the IETF, the body that writes internet standards, describes this box. It covers RTP, the standard packet format for live media, and calls the box a Selective Forwarding Middlebox. It works without signal processing, and the RFC says SFMs have extremely low delay compared with MCUs that transcode (decode and re-encode) media.
The cost moves to the receivers. RFC 7667 says each receiver needs as many decoders as streams it receives, or must switch a smaller set of decoders between streams.
Simulcast: several qualities from one sender
Simulcast means one sender encodes the same camera at several qualities at once, often high, medium and low. RFC 8853 defines how to signal this in SDP and RTP, with each encoding sent as its own RTP stream. This guide calls those encodings layers for short. The SFU then forwards one layer to each receiver, based on their bandwidth and tile size.
In the diagram, follow the patient's three layers into the SFU. Notice that each receiver gets only one layer, chosen for them.
One sender, three layers, three receivers
Drag to exploreThe patient uploads three qualities once. Notice that the SFU sends each receiver only the layer that fits their network and screen.
Diagram connections:
- Patient's phone to SFU in Visakhapatnam (3 layers, one upload)
- SFU in Visakhapatnam to Doctor at the clinic (High layer)
- SFU in Visakhapatnam to Daughter in Hyderabad (Medium layer)
- SFU in Visakhapatnam to Specialist on hotel Wi-Fi (Low layer)
- Patient's phone: Sends high, medium and low layers
- SFU in Visakhapatnam: Forwards packets without decoding video
- Doctor at the clinic: Gets high: patient fills the screen
- Daughter in Hyderabad: Gets medium: one of four tiles
- Specialist on hotel Wi-Fi: Gets low: weak network
RFC 8853 says the choice of which simulcast stream to forward is application specific. The team's rule is simple: split the receiver's measured download across the tiles on screen, keep some spare, then pick the best layer that fits.
LAYERS = [("high", 1500), ("medium", 500), ("low", 150)] # kbps, best first
def pick_layer(receiver_kbps, tiles):
"""Choose the simulcast layer for each tile one receiver shows."""
# receiver_kbps is the download left after audio has its share
share = receiver_kbps * 0.8 / max(tiles, 1) # keep 20% spare
for name, kbps in LAYERS:
if kbps <= share:
return name
return "audio-only"
print("doctor:", pick_layer(2500, 1)) # clinic broadband, patient full screen
print("daughter:", pick_layer(2500, 4)) # home Wi-Fi, four tiles
print("specialist:", pick_layer(800, 4)) # weak hotel Wi-Fi, four tiles
print("weakest:", pick_layer(500, 4)) # below even the low layerRun it and you should see the same choices as the diagram, plus one receiver that falls back to sound only:
doctor: high
daughter: medium
specialist: low
weakest: audio-onlyThe share line matters most. It keeps 20 percent spare, so a small dip in bandwidth does not freeze the picture. The weakest receiver gets 100 kbps per tile, below even the 150 kbps low layer, so it gets audio only.
You can refine this rule. Give the active speaker a higher layer, and stop forwarding layers nobody is watching. When the app is minimised, it can ask the SFU to stop sending it video.
What is an MCU and when is it still useful?
An MCU (Multipoint Control Unit) is a media server that decodes every incoming stream, mixes them into one picture and one sound, then re-encodes that mix for each receiver. Each person receives just one stream.
Back at the kutcheri, an MCU is the front-of-house mix. The person at the mixing desk blends all the instruments into one sound for the hall speakers. Everyone hears the same blend, and nobody can turn up just the violin.
RFC 7667 names three problems with this mixer. It consumes large amounts of media processing. Decoding and re-encoding lowers quality, like photocopying a photocopy. And the mixing adds delay, which the RFC says can be substantial.
The upside is that receivers do almost no work. An MCU still makes sense in a few places:
- Very weak devices, such as an old hospital room display that can decode only one stream.
- Phone dial-in, where a patient calls a normal number and needs one mixed audio stream.
- Recordings and broadcasts, where you want one finished video file in a fixed layout.
You can also combine the two ideas: forward video like an SFU and mix only audio, which needs far less computing than video. The team does exactly that for patients who join by phone call from villages without data.
SFU vs MCU: which should you choose for video conferencing?
Choose an SFU for most group calls, because it needs far less server computing, adds less delay and lets each person choose their own layout. Add MCU-style mixing only for the special cases above.
| Mesh | SFU | MCU | |
|---|---|---|---|
| Uploads per person | 4 | 1 to the SFU, carrying 3 simulcast encodings | 1 |
| Downloads per person | 4 | 4, at chosen layers | 1 mixed stream |
| Server work | None for media | Forward packets and pick layers | Decode, mix and re-encode everything |
| Extra delay from the server | None | Extremely low (RFC 7667) | Can be substantial (RFC 7667) |
| Layout | Each person picks | Each person picks | Fixed by the server |
| End-to-end encryption | Already end to end between the two apps | Needs SFrame on top | Not possible, the server must decode |
| Best fit | One-to-one calls | Group calls | Phone dial-in, recordings, weak devices |
The team keeps a direct connection for one-to-one calls, where no media server is needed unless TURN has to relay. Recorded one-to-one consults go through the SFU instead, so the recorder can join. Rooms with three or more people go through an SFU. A small audio mixer serves phone dial-in.
What does a real WebRTC offer and answer contain?
A real offer is a block of SDP text that names the media, the network candidates and a fingerprint of a DTLS certificate. DTLS is TLS adapted for UDP, and WebRTC uses its handshake to set up the media keys. The quickest way to see one is to run two peers in one process with aiortc, an open-source Python WebRTC library.
The script below was run on aiortc 1.15.0 with Python 3.13. The doctor sends a test video track, the patient answers, and the script prints a few lines from each description, then waits for the first frame. It passes no STUN or TURN servers, so it stays on one machine; a real app lists its own servers there.
import asyncio
from aiortc import RTCConfiguration, RTCPeerConnection
from aiortc.mediastreams import VideoStreamTrack
def show(label, sdp):
print(label)
for line in sdp.splitlines():
if line.startswith("m=video"):
print(" media line, profile:", line.split()[2])
elif line.startswith("a=candidate"):
print(" candidate type:", line.split("typ ")[1].split()[0])
elif line.startswith("a=fingerprint:sha-256"):
print(" DTLS fingerprint:", line.split()[1][:17] + "...")
elif line.startswith("a=setup"):
print(" DTLS role:", line.split(":")[1])
async def main():
local_only = RTCConfiguration(iceServers=[]) # no STUN or TURN: host candidates only
doctor, patient = RTCPeerConnection(local_only), RTCPeerConnection(local_only)
doctor.addTrack(VideoStreamTrack()) # a test picture stands in for a camera
got = asyncio.get_running_loop().create_future()
@patient.on("track")
def on_track(track):
async def first_frame():
frame = await track.recv()
got.set_result((frame.width, frame.height))
asyncio.ensure_future(first_frame())
# Signaling: in a real app these two descriptions travel over a WebSocket.
await doctor.setLocalDescription(await doctor.createOffer())
show("OFFER (doctor)", doctor.localDescription.sdp)
await patient.setRemoteDescription(doctor.localDescription)
await patient.setLocalDescription(await patient.createAnswer())
show("ANSWER (patient)", patient.localDescription.sdp)
await doctor.setRemoteDescription(patient.localDescription)
w, h = await asyncio.wait_for(got, 20)
print("ICE:", doctor.iceConnectionState, "| connection:", doctor.connectionState)
print("first video frame at the patient:", w, "x", h, "pixels")
await doctor.close()
await patient.close()
asyncio.run(main())Install it with pip install aiortc==1.15.0, then run the script. Your fingerprints will differ on every run, and a machine with several network cards prints several host candidates. The rest should match:
OFFER (doctor)
media line, profile: UDP/TLS/RTP/SAVPF
candidate type: host
DTLS fingerprint: 23:86:CE:1C:24:3D... (differs per run)
DTLS role: actpass
ANSWER (patient)
media line, profile: UDP/TLS/RTP/SAVPF
candidate type: host
DTLS fingerprint: F4:FB:DC:1B:F3:33... (differs per run)
DTLS role: active
ICE: completed | connection: connected
first video frame at the patient: 640 x 480 pixelsWhat just happened? The profile UDP/TLS/RTP/SAVPF means secure RTP keyed through DTLS over UDP, as defined in RFC 5764. Each side puts a hash of its DTLS certificate in its description, and RFC 8842 says the certificate in the handshake must match it.
The DTLS role lines decide who starts the handshake. RFC 8842 says the offerer must send actpass, meaning either role. Here the answerer chose active, so it sends the first DTLS message. With no STUN or TURN servers given, ICE could only use host candidates, which is enough on one machine. aiortc reports the finished ICE state as completed.
How to check whether a browser call is relayed
In a browser, call getStats() on the RTCPeerConnection and find the report of type transport. Its selectedCandidatePairId points to the candidate pair in use. That pair report has two ids: localCandidateId for your own candidate and remoteCandidateId for the other side's.
Look up both candidates. Each has a candidateType of host, srflx, prflx or relay. If either is relay, the call goes through a TURN server, yours or the other side's. The same pair report gives currentRoundTripTime in seconds and availableOutgoingBitrate in bits per second, an estimate of how much this side can send right now.
MDN, Mozilla's web developer reference, marks selectedCandidatePairId as working in the latest major browsers only since July 2026, as of 19 October 2026. Older browsers may lack it, and Firefox also offers a non-standard selected flag on candidate pairs.
How do you scale a video conferencing system to thousands of calls?
You scale by keeping each room on one SFU, adding SFUs as rooms grow, and linking SFUs across regions when people in one room are far apart. The signaling service tracks which room lives where.
An SFU is stateful. It holds live connections, per-hop encryption keys and layer choices for every person in its rooms. So you cannot spread one room's packets across random servers, as you would with web requests.
Pick an SFU when the room opens
The signaling service picks an SFU near the first person, with spare capacity. It saves the choice in a room directory, a fast key-value store (a lookup table from room id to SFU).
Send everyone to the same SFU
Each new person asks signaling where the room lives and connects to that SFU. All of the room's media meets in one place.
Count capacity in bandwidth, not rooms
A ten-person room costs far more than two small ones. The team caps each SFU at 70 percent of its network card (made up).
Cascade for faraway guests
The daughter in Hyderabad connects to a nearby SFU, which links to the Visakhapatnam one. Each stream then crosses the long route once, however many guests sit in Hyderabad.
Drain before you remove a server
When load drops, mark an SFU as draining. It takes no new rooms, and shuts down only after its last call ends.
Autoscaling (adding or removing servers as load changes) works here, but with care. Scale on network use and packet loss (data that never arrives), not CPU alone. Never remove a server that still has live calls, or a consult cuts off mid-sentence.
Here is the full design. Start at the apps on the left and notice two separate paths: call details go to signaling, while media goes to the SFUs.
Telehealth video service architecture
Drag to exploreNotice that signaling and media are separate paths. Each room stays on one SFU, and SFUs cascade for distant guests.
Diagram connections:
- Doctor and patient apps to Load balancer (Call details)
- Load balancer to Signaling service
- Signaling service to Room directory (Find or set room's SFU)
- Doctor and patient apps to SFU pool, Visakhapatnam (Media)
- Doctor and patient apps to TURN servers (When blocked)
- TURN servers to SFU pool, Visakhapatnam (Relayed media)
- SFU pool, Visakhapatnam to SFU pool, Hyderabad (Cascade link)
- SFU pool, Visakhapatnam to Recording worker (Streams to record)
- Recording worker to Encrypted recordings
- SFU pool, Visakhapatnam to Call quality monitoring (Quality stats)
- Doctor and patient apps: WebRTC in browser or phone
- Load balancer: Spreads WebSocket connections
- Signaling service: Offers, answers, ICE candidates, joins
- Room directory: Maps each room to its SFU
- TURN servers: Relay media through strict firewalls
- SFU pool, Visakhapatnam: Forwards media for each room
- SFU pool, Hyderabad: Serves faraway guests, cascaded
- Recording worker: Joins as a hidden guest, with consent
- Encrypted recordings: Stores consult videos
- Call quality monitoring: Loss, delay, freezes, bandwidth
The recording worker joins the room like a hidden participant, receives streams from the SFU, and mixes them into one file. That is MCU-style mixing, used only where one finished video is needed.
How do you keep a video call secure and private?
WebRTC encrypts every media stream on every hop by rule, and you add end-to-end encryption if even your own servers must not see the video. For telehealth, you also control who joins and who can record.
RFC 8827, the WebRTC security architecture, says all media must be secured with SRTP (Secure Real-time Transport Protocol) and must not be sent as plain RTP. Keys come from the DTLS handshake you saw in the offer. Calls prefer UDP because TCP delivers data strictly in order, so one lost packet would hold up every packet behind it.
A TURN server only relays packets. In a direct call through TURN, the DTLS session runs between the two apps, so the relay passes SRTP packets it has no keys for. It still sees addresses and timing.
An SFU is different. The encryption is hop by hop: phone to SFU, then SFU to phone. The SFU holds the keys for each hop, so it decrypts packets as they arrive and could, in principle, read the media.
Signaling is a trust point too: RFC 8827 warns that, even if HTTPS is used, the signaling server can potentially mount a man-in-the-middle attack. In that attack, a middle party reads or changes messages, for example by swapping the fingerprints you saw in the offer. Serve signaling over WSS (WebSocket over TLS), which stops outsiders but not the server itself. RFC 8827 says the defence is an independent way to verify keys, such as its identity mechanism.
SFrame (RFC 9605, August 2024) closes the SFU gap. Each sender encrypts whole frames or packet payloads with keys the SFU never gets. The SFU still sees the RTP headers it needs for forwarding, but not the faces or voices.
SFrame does not define how keys are shared; the application must provide that. RFC 9605 describes MLS (Messaging Layer Security), a group key exchange protocol, as one way to do it.
- Join control: each consult has a one-time link, and the doctor admits people from a waiting room.
- Short-lived tokens: the signaling service gives each person a token valid only for that room and a few hours.
- Recording consent: the recorder joins only after the patient taps yes, and everyone sees a recording badge.
- Logs without content: call logs keep times, quality and room ids, never video or chat text.
End-to-end encryption has a cost. A server that cannot see the video cannot record it, add captions, or mix it for dial-in. The team turns it on for consults that will not be recorded, and says so in the app.
How do you explain video conferencing system design in a system design interview?
Explain it as two paths, signaling and media, then justify an SFU with stream maths and finish with scaling and security. Here is the order that keeps the answer clear.
Ask about the room size
Two people, ten or a thousand watchers change everything. Also ask about devices, networks and whether calls are recorded.
Split signaling from media
Draw a signaling server over WebSocket for offers, answers and ICE candidates. Draw media as a separate path.
Explain NAT and TURN
Say that STUN finds the public address and TURN relays when nothing else works. Offer TURN over TLS on port 443 as an option for strict firewalls.
Do the stream maths
Count uploads per person for a five-person mesh. Then show how an SFU cuts that to one upload carrying simulcast encodings.
Compare SFU and MCU
Pick the SFU for normal calls. Use mixing only for dial-in audio, recordings and very weak devices.
Scale and secure it
Keep a room on one SFU, cascade across regions and drain before scaling down. Finish with DTLS-SRTP per hop and SFrame for end to end.
Questions people ask
- Is WebRTC peer-to-peer?
- It can be, but it does not have to be. The W3C spec describes an RTCPeerConnection talking to another browser or another compliant endpoint, and that endpoint can be a media server. A group-call design can instead connect each person to an SFU, which forwards streams to everyone else.
- What is the difference between an SFU and an MCU?
- An SFU forwards each person's stream without decoding it, and an MCU decodes, mixes and re-encodes everything into one stream. The SFU adds extremely low delay, but receivers decode several streams. The MCU costs the server heavy processing and adds delay, but each receiver decodes only one stream.
- Why do video calls need a TURN server?
- Some networks block every direct path between two devices, so a relay is the only way through. RFC 8656 notes that some firewalls block UDP entirely. A TURN server, which you can also run over TLS on port 443, relays the call's packets in both directions so the call still connects.
- How many people can a mesh video call support?
- There is no fixed number; each person's upload decides it. Each person sends one copy per other person, so a five-person mesh at 1,500 kbps per copy needs 6,000 kbps of upload each. The team switches to an SFU at three people; your own upload maths decides your number.
- What is simulcast in WebRTC?
- Simulcast means a sender encodes the same camera at several qualities at once, such as high, medium and low. The SFU forwards one of those encodings, often called layers, to each receiver. A person with a weak network or a small tile gets a lighter layer, while others still get full quality.
- Are video calls through an SFU end-to-end encrypted?
- Not by default. WebRTC always encrypts each hop with DTLS-SRTP, but the SFU holds the keys for each hop and decrypts as it forwards. For end-to-end encryption you add SFrame, where senders encrypt media with keys the SFU never gets. The app must share those keys itself.
- How do large webinars with thousands of viewers work?
- A common design splits talkers from watchers. A few speakers join an SFU as normal. Their streams then reach viewers through cascaded SFUs, or through a streaming setup that buffers a few seconds. Watchers rarely need the very low delay that talkers need, so the cheaper path is fine.
Sources and further reading
- RFC 7667: RTP Topologies (IETF), sections 3.6 and 3.7 on mixers and Selective Forwarding Middleboxes
- RFC 8445: Interactive Connectivity Establishment (ICE) (IETF), candidate types, checks and priorities
- RFC 8656: Traversal Using Relays around NAT (TURN) (IETF), transports and default ports
- RFC 8853: Using Simulcast in SDP and RTP Sessions (IETF), section 6.2 on stream selection
- RFC 8827: WebRTC Security Architecture (IETF), section 6.5 on media encryption and section 9.1 on the signaling server
- RFC 5764: DTLS Extension to Establish Keys for SRTP (IETF), section 8 on UDP/TLS/RTP/SAVPF
- RFC 8842: SDP Offer/Answer Considerations for DTLS and TLS (IETF), setup and fingerprint rules
- RFC 9605: Secure Frame (SFrame) end-to-end encryption and authentication for media (IETF)
- WebRTC: Real-Time Communication in Browsers (W3C Recommendation, 13 March 2025 version)
- MDN: RTCTransportStats selectedCandidatePairId
- MDN: RTCIceCandidatePairStats, local and remote candidate ids, round-trip time and available bitrate
- MDN: RTCIceCandidate type values host, srflx, prflx and relay
- MDN: RTCIceCandidateStats candidateType, the field getStats reports for each candidate
- RFC 9293: Transmission Control Protocol (TCP) (IETF), reliable in-order byte-stream service
- aiortc 1.15.0 on PyPI, the Python WebRTC library used for the offer and answer demo