Want it harder? Hide the model answer and work each phase in a scratchpad against its time budget, then reveal and score yourself. Or simply read on: the checkpoints below work either way.
How Do You Put Fourteen People on Three Continents Into One Room, Twenty Million Times at Once?
It is 9:58 on a Monday morning in Bengaluru. Kavya, a product manager, opens the link for her team's weekly review. Fourteen people will join in the next three minutes: Arjun on Jio 4G in a cab crawling down Baner Road in Pune, Meenal from a bank's office in Gurugram whose firewall blocks everything except web traffic, two engineers in Singapore, and Rahul in San Jose, where it is 9:28 on Sunday night.
At 10:00:00, Kavya isn't the only one. Across India, standups, classes and client calls all start on the hour, and the join button is pressed a few hundred thousand times in the same minute. Every one of those people expects to hear the others within a fraction of a second, see faces that don't freeze when a cab goes under a flyover, and never once think about which server is carrying their voice.
- 10 million — maximum daily meeting participants at the end of December 2019; 200 million+ in March 2020 (Eric S. Yuan, Zoom, 1 April 2020)
- 300 million — daily meeting participants in April 2020. Zoom first wrote "users" and "people", then corrected it: a person in five meetings counts five times (SiliconANGLE, reporting the correction, 30 April 2020)
- 3.5 trillion — annualized meeting minutes at the end of the quarter to 31 October 2020 (Zoom's CFO, Q3 FY2021 earnings call)
- 17 — co-located data centres in 2020, with 2 in India (Mumbai and Hyderabad) that grew 4× in capacity in four months; free sign-ups in India up 6,700% from January to April 2020 (Zoom, July 2020)
- 7 PB a day — transferred through Oracle Cloud alone in spring 2020; AWS added "thousands of instances in a day" as usage demanded (Oracle, April 2020; AWS, November 2020)
- 1,000 interactive video panelists and up to 1 million attendees in a single webinar (Zoom, August 2024)
For context:
Zoom publishes the outline of its media architecture (multimedia routers grouped in meeting zones, UDP with TCP/TLS fallback, multiple streams per sender) but not its internals
Zoom doesn't publish concurrent users, bitrates or server counts, so the estimates below state their own assumptions
Everything not in the list above is an illustrative design, not Zoom's internal architecture
This is the system we'll design today.
Zoom went from 10 million daily meeting participants in December 2019 to 300 million in April 2020 (a count of joins, not people) and reported 3.5 trillion annualized meeting minutes in late 2020. On stated assumptions that is ~20 million people in meetings at the peak hour, ~30 Tbps of media out of the servers and ~60,000 joins a second at the top of the hour. A control plane (one meeting controller per meeting, a zone selector) is kept apart from a media plane of multimedia routers that forward packets without decoding them: mixing would need ~80 times more servers, and mesh overloads phone uplinks. Senders send layers (simulcast or SVC) and each router picks a layer per receiver from its bandwidth estimate; media rides UDP with FEC, NACK and adaptive jitter buffers, falling back to TCP and TLS on 443 behind firewalls; participants join their nearest zone and routers cascade across regions so each stream crosses an ocean once. The deep dives cover forwarding vs mixing, layer selection, loss and firewalls, placement and cascades, meeting state with leases and fencing, recording and end-to-end encryption, then the top-of-hour herd, router failure, keyframe storms and a 40,000-person town hall.
The Interview Begins#
You're interviewing at a company building a video meeting product. The interviewer shares their screen: a gallery of fourteen faces, one of them pixelated, one frozen mid-blink, one a grey tile that says "Connecting…".
"Three of these people have a bad experience, for three different reasons. Design the system so that none of them do, and so that it works for millions of meetings at the same time."
People join meetings by link or ID from desktop apps, phones and browsers, on any network. Everyone hears everyone with conversational delay, sees the active speaker and a gallery of others, and shares screens. Hosts control who gets in and who may speak. Meetings range from two people to webinars with tens of thousands watching.
Requirements
- Join a meeting in a couple of seconds, on any network, even behind strict firewalls
- Audio and video between participants with delay low enough for conversation
- Each receiver gets the best quality its network and screen can use, without hurting others
- Host controls: waiting room, admit, mute, remove, lock, co-hosts
- Meetings from 2 to 1,000 interactive participants; webinars to tens of thousands of viewers
- Cloud recording; optional end-to-end encryption
"Leave out chat, calendars, whiteboards, phone dial-in and AI summaries. Join, media, control."
Phase 1: Requirements Clarification (5 minutes)#
"This is a real-time system first. Unlike video on demand, nothing can be buffered for long, and a packet that arrives late is as useless as one that never arrives. I want to know the scale, the latency budget and the hostile networks."
Your Questions#
"How many people?"
"Zoom reported 10 million daily meeting participants at the end of 2019 and 300 million in April 2020. Notice the unit: a daily meeting participant is one person in one meeting, so someone in five meetings counts five times. Zoom first called them users, and corrected it within days."
"And how much time do they spend?"
"Zoom's CFO reported an annualized run rate of 3.5 trillion meeting minutes at the end of October 2020. Treat that as participant-minutes; Zoom doesn't define it further."
"Where do people connect to?"
"Zoom said it ran 17 co-located data centres in 2020, two of them in India, and added capacity on AWS and Oracle Cloud during the surge. Its architecture notes say the app is steered by geolocation to the nearest available resources."
"What delay is acceptable?"
"Use ITU-T G.114: below about 150 ms one way, most conversations aren't noticeably affected, and it treats 400 ms as the limit for general network planning. Between India and California you won't hit 150. Don't make it worse than the speed of light already does."
"And the hostile networks?"
"Phones on congested 4G, hotel Wi-Fi, and corporate firewalls that allow only HTTPS on port 443. Zoom documents that UDP is used when available, with fallback to TCP and TLS, including HTTPS on 443."
Functional Requirements#
- Join: by link or meeting ID and passcode; waiting room; join before host if allowed; reconnect after a network change without rejoining
- Media: audio for everyone, video from anyone with a camera on, screen share; active-speaker and gallery layouts
- Adaptation: each receiver gets layers that fit its bandwidth and the size of each tile on its screen
- Control: host and co-hosts admit, mute, remove, lock, spotlight; the roster is the same on every screen
- Scale: meetings up to 1,000 interactive participants; webinars with view-only attendees in the tens of thousands
- Recording and encryption: cloud recording to storage; an end-to-end encrypted mode with its tradeoffs
Non-Functional Requirements#
Latency (design targets)
- Audio mouth-to-ear: < 150 ms within a region, < 300 ms across continents
- Join: in the meeting with audio in < 3 s at p95
- A switch to a lower video layer within ~1 s of the network degrading
Scale
- ~20 million concurrent participants at the busiest hour (estimated below)
- ~60,000 joins a second at the top of the busiest hour (estimated below)
Resilience
- Audio survives 10–20% packet loss intelligibly; video degrades before audio
- A media server failure costs a participant a few seconds, not the meeting
Phase 2: Back of the Envelope Estimation (5 minutes)#
"Zoom doesn't publish concurrency or bitrates, so every rate below is an assumption built on the two published totals: 300 million daily participants and 3.5 trillion annualized minutes."
Before the numbers: how many people are in a meeting right now?
3.5 trillion meeting minutes a year are participant-minutes (an assumption). How many minutes is that a day? Spread over 1,440 minutes, how many people are in a meeting at any moment on average? If the busiest hour carries 3 times the average, how many at the peak? And with 300 million participant-joins a day, how long is the average stay?
One strong answer
- A day: 3.5 trillion ÷ 365 ≈ 9.6 billion participant-minutes.
- On average: 9.6 billion ÷ 1,440 ≈ 6.7 million people in a meeting at any moment; at a 3× peak, ~20 million.
- Average stay: 9.6 billion ÷ 300 million ≈ 32 minutes per participant-join, which matches a half-hour meeting.
- So the design point is about 20 million concurrent participants, and every one of them is a live, two-way media stream, not a page view.
Media Estimation#
| Value | |
|---|---|
| Participant-minutes a day (3.5T annualized, Zoom) | ~9.6 billion |
| Concurrent participants, average / peak | ~6.7 million / ~20 million |
| Average meeting size (assumed) | 6 |
| Concurrent meetings at peak | ~3.3 million |
| Participants with camera on (assumed) | 60% |
- Upload per participant: a camera sender uploads ~1.2 Mbps of video layers on average, plus ~40 kbps of audio: 20 million × (0.6 × 1.2 + 0.04) Mbps ≈ 15 Tbps into the media servers
- Download per participant: an active speaker at 720p and a few thumbnails, ~1.5 Mbps on average: 20 million × 1.5 Mbps ≈ 30 Tbps out of the media servers
- Packets: 30 Tbps at ~1,200 bytes a packet ≈ 3 billion packets a second forwarded at peak
- A day: average traffic is about a third of peak, ~10 Tbps out: ~100 PB a day. For scale, Oracle said 7 PB a day passed through its cloud alone in April 2020, one supplier among several
Conclusion: the media plane is a packet-forwarding problem measured in tens of terabits a second. Whatever the servers do per packet has to be cheap.
Server Estimation#
- Forwarding (SFU): assume one media server forwards ~10 Gbps of egress, bounded by packet rate rather than CPU: 30 Tbps ÷ 10 Gbps = ~3,000 servers; with the 50% spare capacity Zoom says it keeps, ~4,500
- Mixing (MCU): decode every camera, ~0.25 core per 720p stream: 12 million cameras × 0.25 = 3 million cores; then encode a separate composite for every receiver, ~1 core each: 20 million cores. ~23 million cores, ~360,000 servers of 64 cores
- Mesh (no servers): in a 10-person meeting each phone uploads its video 9 times: 9 × 1.2 Mbps ≈ 11 Mbps up, more than a congested 4G uplink sustains
Conclusion: mixing costs about 80 times more servers than forwarding, and mesh moves the cost onto the weakest link, the sender's uplink. Route packets; don't mix them.
Join and Control Estimation#
- Joins in the busiest hour: 300 million a day ÷ 24 × 3 ≈ 37.5 million, ~10,400 a second on average
- On the hour: meetings start at :00 and :30. If 40% of the hour's joins land in the two minutes around each, 15 million in 240 s ≈ ~60,000 joins a second; design for 100,000
- Meeting state: 3.3 million live meetings × ~20 KB (roster, roles, settings, layer map) ≈ ~66 GB, held in memory across meeting controllers
- Recordings: 9.6 billion participant-minutes ÷ 6 ≈ 1.6 billion meeting-minutes a day; if 3% are recorded at ~1 Mbps (7.5 MB a minute), ~360 TB a day, ~11 PB for 30 days' retention
Conclusion: signalling is small and spiky; the top of the hour is a thundering herd that must not reach the media servers all at once.
Key Metrics Summary#
| Metric | Value |
|---|---|
| Daily meeting participants (Zoom, April 2020) | 300 million |
| Concurrent participants at peak | ~20 million |
| Concurrent meetings at peak | ~3.3 million |
| Media in / out at peak | ~15 Tbps / ~30 Tbps |
| Forwarding servers vs mixing | ~4,500 vs ~360,000 |
| Joins at the top of the hour | ~60,000/s (design for 100,000) |
| One-way audio delay target | < 150 ms in region |
Requirements and estimation
Q1/3 Zoom reported 300 million daily meeting participants in April 2020. What does that count?
Answer & explanation
Answer: B
Zoom first wrote "users", then corrected it to participants. The difference matters: it's a count of joins, not of people.
Q2/3 9.6 billion participant-minutes a day, spread evenly. Roughly how many people are in a meeting at any moment?
Answer & explanation
Answer: B
9.6 billion ÷ 1,440 minutes ≈ 6.7 million on average; about 20 million at a 3× peak hour.
Q3/3 Why does mixing (an MCU) need so many more servers than forwarding (an SFU)?
Answer & explanation
Answer: B
Forwarding touches packets, not pixels. Decoding and encoding are the expensive part, and a mixer does both for every participant.
Phase 3: High-Level Design (10 minutes)#
Sketch it first: Kavya presses Join
Trace Kavya's click from the link to hearing Arjun's voice. What has to answer before any media flows? Who decides which media server she connects to, and Arjun, and Rahul in San Jose? Does a server decode anyone's video? And where does Meenal's media go, behind a firewall that allows only HTTPS?
One strong answer
- Signalling first: the app calls the API with the meeting ID; the meeting service checks the passcode and waiting room, and the meeting controller for this meeting adds her to the roster and returns a short-lived media token.
- Placement: a zone selector picks the nearest meeting zone with capacity for each participant by geolocation: Mumbai for Kavya, Arjun and Meenal, Singapore for the two engineers, San Jose for Rahul. Each zone assigns a multimedia router (MMR), a forwarding media server.
- Media: every client sends its audio and its video layers over UDP to its MMR. The MMR forwards packets without decoding them. MMRs of the same meeting in different zones link to each other, a cascade, so each stream crosses an ocean once.
- Meenal: her UDP probes fail, so her client tunnels the same media over TLS on port 443 to the MMR, at a cost in delay when packets are lost.
- Control stays on the signalling channel: mute, admit and roster changes flow through the meeting controller, never through the media path.
"Two planes. The control plane is small and stateful: meetings, rosters, permissions, placement. The media plane is huge and nearly stateless: servers that forward packets they never decode, chosen near each participant and linked together when a meeting spans regions."
System Architecture#
%% title: Video meetings: a control plane and a forwarding media plane
flowchart TD
subgraph L1["Layer 1 · Clients"]
direction LR
DESK@{ icon: "sd:desktop", label: "Desktop app" }
MOB@{ icon: "sd:phone", label: "Android · iOS app" }
WEB@{ icon: "tech:webrtc", label: "Web client<br/>WebRTC data channels" }
end
subgraph L2["Layer 2 · Control plane"]
direction LR
API@{ icon: "sd:api", label: "API gateway<br/>join, auth, signalling sockets" }
MS["Meeting service<br/>passcode, waiting room, roles"]:::service
CTRL["Meeting controller<br/>one owner per meeting"]:::core
ZS["Zone selector<br/>geolocation + capacity"]:::service
ST@{ icon: "tech:redis", label: "Redis<br/>live meeting state, leases" }
DB@{ icon: "sd:database", label: "Meetings DB<br/>schedules, settings" }
end
subgraph L3["Layer 3 · Media plane, per zone"]
direction LR
ZC@{ icon: "sd:server", label: "Zone controller<br/>MMR health and load" }
MMR["MMR<br/>forwards packets, picks layers"]:::hot
CAS@{ icon: "sd:globe", label: "Backbone<br/>MMR-to-MMR cascade" }
end
subgraph L4["Layer 4 · After the meeting"]
direction LR
REC@{ icon: "sd:worker", label: "Recorder<br/>a silent participant" }
OBJ@{ icon: "sd:object-storage", label: "Object storage<br/>recordings" }
end
DESK ==> API
MOB ==> API
WEB ==> API
API --> MS --> CTRL
MS --> DB
CTRL --> ST
CTRL --> ZS --> ZC
ZC --> MMR
DESK ==>|"UDP media"| MMR
MOB ==>|"UDP media"| MMR
WEB ==>|"data channel"| MMR
MMR <==> CAS
MMR -.-> REC --> OBJ
class DESK,MOB,WEB client
class API edge
class ST,DB,OBJ store
class ZC,REC service
class CAS edgeKEY INSIGHTS:
- Route, don't mix. The MMR reads packet headers and decides which layer of which sender goes to which receiver; the pixels are only ever decoded on the clients
- Control and media never share a path. A mute or an admit is a small, ordered message through the meeting controller; media is a firehose that tolerates loss
- Place each participant, not each meeting. Everyone connects to their nearest zone, and the meeting spans zones through a cascade over a managed backbone
Data Flow: Kavya Joins, Then Speaks#
%% title: Kavya joins the review, then speaks sequenceDiagram autonumber participant K as Kavya's app participant A as API gateway participant C as Meeting controller participant Z as Zone selector participant M as MMR, Mumbai zone participant R as Rahul's MMR, San Jose K->>A: Join 841 2290 7731, passcode A->>C: Admit Kavya (host) C->>Z: Nearest zone with room Z-->>K: Mumbai MMR, media token, 60 s K->>M: UDP hello with token M-->>K: Layers available from 13 senders Note over K,M: about 1.5 s from click to first audio K-)M: Opus audio, 3 video layers M-)R: One copy of each layer Rahul's side needs R-)R: Fan out to Rahul's receivers
MEANWHILE, IN PARALLEL:
- Every receiver reports what it got and when; each MMR estimates every downlink and picks layers per receiver several times a second
- The meeting controller pushes roster changes ("Arjun joined", "Meenal is muted") to every client over its signalling socket, in order
High-level design
Q1/3 Why does a mute command travel through the meeting controller instead of the media server?
Answer & explanation
Answer: B
If the host mutes Arjun and Arjun unmutes himself at the same moment, one owner has to decide the order. A lossy media path can't.
Q2/3 Rahul is in San Jose and the rest of the team is in Asia. Where should his media server be?
Answer & explanation
Answer: B
Each participant connects to the nearest zone; only one copy of each stream crosses the Pacific, on a managed backbone.
Q3/3 What does the MMR do with a video packet from Arjun?
Answer & explanation
Answer: B
Forwarding without decoding is what lets one server move about 10 Gbps.
Phase 4: Deep Dives (20 minutes)#
Deep Dive 1: Mesh, MCU or SFU, and Why Route Rather Than Mix (Week 10 Concepts: Capacity Planning; Week 1: Hot Keys and Skew)#
"There are three ways to connect N people. Each puts the cost somewhere different: on the sender's uplink, on the server's CPU, or on the server's network card. Only one of them scales to millions of meetings."
This builds on Week 10, Day 4 · Capacity Planning.
Three ways to connect fourteen people
Kavya's meeting has 14 people, 10 with cameras on. Compare three designs: every client sends its video directly to every other (mesh); a server decodes everyone and sends each person one mixed picture (MCU); a server forwards packets without decoding (SFU). For each, how many video streams does Arjun's phone upload, how many does it download, and what does the server do?
One strong answer
- Mesh: Arjun uploads 13 copies (one per other person) and downloads 9. At 1.2 Mbps a copy that's ~16 Mbps up from a phone in a cab: impossible. Mesh works for 2 or 3 people and nothing more.
- MCU (mixing): Arjun uploads 1 stream and downloads 1 composite. The phone is happy, but the server decodes 10 cameras and encodes 14 different composites (each person must not see themselves; layouts differ). That's the ~80× server cost from Phase 2, plus a decode and encode in the path, adding delay, and one layout for everyone.
- SFU (forwarding): Arjun uploads 1 stream with a few layers and downloads only what his screen shows: the active speaker plus small thumbnails. The server reads headers and copies packets. Cost is network, not CPU.
- So route. An SFU gives each receiver its own layout and quality, adds about a millisecond, and scales with network cards, which are cheap.
What is published: Zoom calls its media server a multimedia router and describes "multiple simultaneous streams" from which the app selects a layer. Zoom's web client, as Philipp Hancke found in 2018 and 2019, first carried media over WebSockets and later over WebRTC data channels, running Zoom's own codecs compiled to WebAssembly: the browser was another client of the same forwarding servers.
%% title: Mesh, mixing and forwarding for one meeting
flowchart TD
subgraph MESH["Mesh"]
direction LR
A1@{ icon: "sd:phone", label: "Arjun<br/>uploads 13 copies" }
O1["13 other clients"]:::bad
end
subgraph MCU["MCU · mixing"]
direction LR
A2@{ icon: "sd:phone", label: "Arjun<br/>uploads 1" }
MIX["Mixer<br/>decode 10, encode 14 composites"]:::bad
end
subgraph SFU["SFU · forwarding"]
direction LR
A3@{ icon: "sd:phone", label: "Arjun<br/>uploads 1, three layers" }
FWD["MMR<br/>copies packets, picks layers"]:::ok
end
A1 ==> O1
A2 ==> MIX
A3 ==> FWD
class A1,A2,A3 client- Audio is different. Forwarding 13 audio streams to every receiver is wasteful when only one or two people talk. The MMR reads each packet's audio level (an RTP header extension the sender fills in), forwards only the 3 loudest speakers, and the client mixes those. Still no decoding on the server.
- The active speaker is chosen the same way: a smoothed audio level per participant, with a little hysteresis, so a cough doesn't steal the big tile.
- Gallery view is a subscription. A receiver tells the MMR which tiles are on its screen and how big. A thumbnail off-screen costs nothing; a 49-tile gallery asks for 49 small layers, not 49 large ones.
- Big meetings are hot keys. A 1,000-person meeting can't fit on one MMR's network card if everyone watches everyone. Split it across several MMRs in the zone, linked like a cascade, so no single server holds the whole meeting.
# mmr/forward.py
"""
The heart of a forwarding media server (illustrative). It never decodes: it reads
the RTP header and a few header extensions (sender, layer, audio level), then
copies the packet to each receiver whose subscription wants that layer of that
sender. Per-packet work is a dictionary lookup and a send.
"""
from dataclasses import dataclass, field
TOP_SPEAKERS = 3
@dataclass
class Receiver:
rid: str
wants: dict = field(default_factory=dict) # sender_id -> layer the selector chose
send: callable = None
class MeetingRoute:
def __init__(self):
self.receivers: dict[str, Receiver] = {}
self.loudest: list[str] = [] # updated every 100 ms from audio levels
def on_video(self, pkt) -> None:
for r in self.receivers.values():
if r.rid != pkt.sender and r.wants.get(pkt.sender) == pkt.layer:
r.send(pkt.rewrite_for(r.rid)) # SSRC and sequence rewritten per receiver
def on_audio(self, pkt) -> None:
if pkt.sender not in self.loudest:
return # silence and background voices aren't forwarded
for r in self.receivers.values():
if r.rid != pkt.sender:
r.send(pkt.rewrite_for(r.rid))
def update_loudest(self, levels: dict) -> None:
"""levels: sender -> smoothed audio level in dBov (closer to 0 is louder)."""
self.loudest = sorted(levels, key=levels.get, reverse=True)[:TOP_SPEAKERS]Deep Dive 2: Simulcast, SVC and Picking a Layer for Every Receiver (Week 3 Concepts: Backpressure and Flow Control)#
"Arjun's cab goes under a flyover and his downlink drops from 3 Mbps to 600 kbps. Kavya's laptop on office Wi-Fi has 50 Mbps. They watch the same speaker. Without decoding, how does one server give each the best picture they can take?"
This builds on Week 3, Day 3 · Backpressure and Flow Control.
One sender, two very different receivers
Kavya is speaking. Her laptop can send 3 Mbps; Arjun can receive 600 kbps; Rahul in San Jose has 20 Mbps. The server can't re-encode. What must Kavya's laptop send so that the server has something to pick from? How does the server learn that Arjun's link just shrank, and what does it do in the next second? What does Arjun see?
One strong answer
- Send layers. With simulcast, Kavya's laptop encodes the same camera three times: ~150 kbps at 180p, ~400 kbps at 360p, ~1.5 Mbps at 720p (illustrative rates). With SVC, it encodes one stream in layers, where higher layers add frame rate or resolution on top of lower ones.
- Estimate each downlink. Arjun's client timestamps every packet's arrival and reports back several times a second. If one-way delay starts rising, a queue is building in front of him: congestion before loss. The MMR's estimate for Arjun falls.
- Pick per receiver. The MMR switches Arjun to the 360p layer, or 180p if needed, and keeps Rahul and the rest on 720p. With simulcast the switch waits for a keyframe of the new layer; with SVC the server just stops forwarding the top layer's packets.
- Arjun sees a softer picture for a few seconds, not a frozen one, and Kavya's encoder never slowed down for him.
- Backpressure upstream too: if nobody currently wants 720p of Kavya, the MMR tells her laptop to pause that layer, saving her uplink and CPU.
What is published: Zoom describes "multiple simultaneous streams" with the app selecting "the most appropriate layer". The standard pieces for doing this in RTP are written down: simulcast in SDP and RTP (RFC 8853), the RTP payload format for H.264 SVC (RFC 6190), and Google Congestion Control (draft-ietf-rmcat-gcc), whose loss-based rule keeps the estimate unchanged between 2% and 10% loss, raises it 5% below 2%, and cuts it by half the loss ratio above 10%, while a delay-based controller watches the variation in packet arrival times to catch a queue before it overflows.
%% title: One sender, three layers, a layer per receiver
flowchart TD
K@{ icon: "sd:laptop", label: "Kavya's laptop<br/>180p · 360p · 720p" }
MMR["MMR<br/>layer per receiver"]:::hot
BWE["Bandwidth estimator<br/>per downlink, from arrival-time feedback"]:::core
AR@{ icon: "sd:phone", label: "Arjun, 4G in a cab<br/>~600 kbps: 360p" }
RA@{ icon: "sd:laptop", label: "Rahul, San Jose<br/>~20 Mbps: 720p" }
TH@{ icon: "sd:desktop", label: "Gallery viewers<br/>small tile: 180p" }
K ==> MMR
BWE -.-> MMR
MMR ==> AR
MMR ==> RA
MMR ==> TH
AR -.->|"feedback"| BWE
class K,AR,RA,TH client- Simulcast or SVC? With these rates, simulcast costs the sender about a third more upload than the top layer alone and needs a keyframe to switch layers; SVC costs a little coding efficiency but lets the server drop layers on any packet boundary. Both work; the server logic is the same: a budget per receiver, filled from the most important stream down.
- Fill the budget by importance: audio first, always; then screen share; then the active speaker; then thumbnails, smallest layer first. Arjun at 600 kbps gets all audio, the speaker at 360p and thumbnails at 180p only if room remains.
- Probe before upgrading. Moving Arjun back up to 720p could overflow his link. Send padding or FEC packets at the higher rate for a second first; upgrade only if delay stays flat.
- Hysteresis: downgrade fast (within ~1 s of rising delay), upgrade slowly (after several seconds of headroom), so a receiver doesn't flicker between layers.
# mmr/layer_select.py
"""
Per-receiver layer selection (illustrative). Every ~200 ms, given the receiver's
estimated downlink and what is on its screen, choose a layer per sender: audio is
reserved first, then streams in order of importance, each at the best layer that
fits what's left and the tile it will be drawn in.
"""
LAYERS = [("q", 180, 150), ("h", 360, 400), ("f", 720, 1500)] # (name, height px, kbps)
AUDIO_KBPS = 40
def select(estimate_kbps: float, tiles: list, speakers: int = 3) -> dict:
"""tiles: [(sender_id, tile_height_px, importance)], most important first."""
budget = estimate_kbps * 0.9 - speakers * AUDIO_KBPS # 10% headroom, audio reserved
chosen = {}
for sender, tile_px, _ in sorted(tiles, key=lambda t: -t[2]):
fit = None
for name, height, kbps in LAYERS:
if kbps <= budget and height <= max(tile_px * 1.5, 180):
fit = (name, kbps) # best layer that fits so far
if fit is None:
chosen[sender] = None # paused: show the avatar
continue
chosen[sender] = fit[0]
budget -= fit[1]
return chosen
# Arjun at 600 kbps, speaker tile 360 px, two thumbnails of 90 px:
# select(600, [("kavya", 360, 3), ("wei", 90, 1), ("rahul", 90, 1)]) -> {"kavya": "h", "wei": None, "rahul": None}Deep Dive 3: UDP, Packet Loss, Jitter Buffers and the Way Through Firewalls (Week 2 Concepts: Timeouts and Retries)#
"Real-time media prefers UDP because a late packet is worthless. TCP will retransmit a lost packet and hold up every packet behind it until it arrives; by then the moment has passed. So we take UDP and handle loss ourselves, and keep TCP for the networks that leave no choice."
This builds on Week 2, Day 1 · Timeout Hell.
Arjun's 4G and Meenal's firewall
Arjun's cab connection loses 8% of packets in bursts and its delay varies by 80 ms from packet to packet. Meenal's office blocks all UDP and allows only HTTPS through a proxy. For Arjun: how do you recover lost audio and video without waiting a round trip each time, and how do you play audio smoothly when packets arrive unevenly? For Meenal: how does her client get media through, and what does she lose?
One strong answer
- Audio: forward error correction. Opus can carry a low-bitrate copy of the previous frame inside each packet (in-band FEC), and the sender can add parity packets (RFC 5109 style) so that one loss in a group is rebuilt with no round trip. Turn FEC up as measured loss rises.
- Video: NACK when the round trip is short. The receiver asks for exactly the missing packets (RTCP NACK, RFC 4585); the MMR, one short hop away, resends from a small buffer. Over a long round trip, rely on FEC instead, and ask for a keyframe (PLI) only if a frame can't be repaired.
- A jitter buffer holds packets for a moment and plays them at an even pace. Size it adaptively, about the 95th percentile of recent delay variation: ~80 ms for Arjun, ~20 ms on office Wi-Fi. Too small and audio crackles; too large and the conversation drags.
- Meenal: try, then fall back. The client races UDP to the MMR on its media ports; if nothing returns within a second or so, it tries TCP, then TLS on port 443, through her proxy if it must. Zoom documents exactly this fallback.
- She pays in delay. Over TCP a single lost packet stalls everything behind it for a retransmission. Her jitter buffer has to be bigger, and her video drops to lower layers sooner.
What is published: Zoom's network guidance lists UDP ports 8801–8810 for media and TCP ports 443, 8801 and 8802, and its architecture notes say UDP is used when available "with seamless fallback to TCP/TLS (including HTTPS/443)". The standards behind the recovery tools are RTCP feedback with NACK and picture-loss indication (RFC 4585), generic FEC for RTP (RFC 5109) and Opus, whose specification includes in-band FEC (RFC 6716).
%% title: Connecting through whatever the network allows
flowchart TD
C@{ icon: "sd:laptop", label: "Meenal's client<br/>races the options" }
U["UDP to the MMR<br/>media ports"]:::ok
T["TCP to the MMR<br/>media ports"]:::service
S["TLS on 443<br/>looks like HTTPS"]:::service
P@{ icon: "sd:firewall", label: "Corporate proxy<br/>HTTPS only" }
MMR["MMR"]:::hot
C -->|"race: UDP"| U
C -->|"fallback: TCP"| T
C -->|"fallback: TLS 443"| S
S ==> P ==> MMR
U -.->|"preferred"| MMR
class C client
class P edge- Race, don't queue the attempts. Start UDP and TLS together and keep whichever works first, upgrading to UDP later if it starts working. A sequential ladder with generous timeouts can add several seconds to a join.
- Keep the media session independent of the transport. When Arjun's phone hops from Wi-Fi to 4G, his IP address changes. The client reconnects with its media token and session ID, and the MMR swaps the address under the same participant: a second of silence, not a rejoin.
- Protect audio above everything. Audio is ~40 kbps; doubling it with FEC is cheap insurance. Video is where you save bandwidth.
- Measure loss after repair, not before. 8% loss in the network that becomes 0.3% after FEC and NACK is fine; that residual number is what users hear.
# client/jitter_buffer.py
"""
An adaptive jitter buffer for audio (illustrative). Packets are held until their
playout time; the target delay tracks a high percentile of recent delay variation,
so a smooth network gets a small buffer and a jittery one a larger buffer. Missing
frames are concealed (FEC copy if present, otherwise the decoder's guess).
"""
from collections import deque
FRAME_MS = 20
class JitterBuffer:
def __init__(self, min_ms: int = 20, max_ms: int = 200):
self.min_ms, self.max_ms = min_ms, max_ms
self.target_ms = 40
self.jitter = deque(maxlen=250) # last ~5 s of samples
self.packets = {} # seq -> payload
def on_packet(self, seq: int, sent_ms: float, arrived_ms: float, payload) -> None:
self.jitter.append(arrived_ms - sent_ms) # relative one-way delay
self.packets[seq] = payload
if len(self.jitter) >= 50:
base = min(self.jitter)
spread = sorted(d - base for d in self.jitter)[int(len(self.jitter) * 0.95)]
self.target_ms = max(self.min_ms, min(self.max_ms, spread + FRAME_MS))
def play(self, seq: int):
if seq in self.packets:
return self.packets.pop(seq)
nxt = self.packets.get(seq + 1)
if nxt is not None and nxt.fec_copy is not None:
return nxt.fec_copy # Opus in-band FEC rebuilds the lost frame
return None # decoder conceals a 20 ms gapRouting, layers and loss
Q1/4 Why doesn't the media server forward all 13 audio streams to every participant?
Answer & explanation
Answer: B
Selecting by a header field keeps the server from decoding and saves every receiver bandwidth.
Q2/4 Arjun's downlink drops to 600 kbps. What does the media server do?
Answer & explanation
Answer: C
Each receiver gets its own layer choice; the sender keeps sending all layers someone wants.
Q3/4 Why prefer UDP over TCP for meeting media?
Answer & explanation
Answer: B
Head-of-line blocking turns one loss into a stall. With UDP you choose: FEC, a NACK, or concealment.
Q4/4 Meenal's network allows only HTTPS through a proxy. What happens?
Answer & explanation
Answer: C
The fallback is per participant; the rest of the meeting stays on UDP.
Deep Dive 4: Meeting Placement and Cascading Across Regions (Week 1 Concepts: Partitioning; Week 9: Data Residency)#
"Where does a meeting live? Not in one place. Each participant connects to the nearest zone with capacity, and the meeting is the set of MMRs those participants landed on, linked together."
This builds on Week 1, Day 1 · Partitioning Deep-Dive and Week 9, Day 3 · Data Residency.
Bengaluru, Singapore and San Jose in one meeting
Kavya's meeting has 11 people in India, 2 in Singapore and Rahul in San Jose. Option A: put the whole meeting on one MMR in Mumbai. Option B: each participant connects to their nearest zone, and the MMRs exchange streams. Compare Rahul's experience, the traffic that crosses the Pacific, and what happens when a 1,000-person all-hands spans the same three regions.
One strong answer
- Option A, one MMR: Rahul's packets cross the public internet from California to Mumbai, about 230 ms or more round trip (an estimate), with whatever loss and jitter the path has. NACKs are useless at that distance, his jitter buffer must be large, and every one of his packets crosses the ocean individually.
- Option B, a cascade: Rahul connects to the San Jose zone a few milliseconds away, so his loss is repaired locally. The San Jose MMR gets one copy of each layer it needs from Mumbai over a managed backbone with little loss, and fans out locally.
- Cross-ocean traffic becomes per stream, not per receiver: for the all-hands with 300 people in San Jose watching the CEO, one stream crosses, not 300.
- Delay is still bounded by distance: Bengaluru to San Jose can't meet G.114's 150 ms. But the long hop is on a clean link, so the jitter buffers stay small and the total stays well under 400 ms.
- Residency: a meeting whose account requires India-only processing places all MMRs in Indian zones; a participant abroad pays the extra distance.
What is published: Zoom's notes describe meeting zones duplicated in each data centre "with the exact same architecture", zone controllers that report MMR status to a global cloud controller, and clients steered by geolocation to the nearest available resources. In 2020 Zoom said it had 17 co-located data centres, nine of them in Equinix facilities as of November 2019, and two in India that had grown four times in capacity in four months.
%% title: One meeting across three regions
flowchart TD
subgraph IN["India · Mumbai zone"]
direction LR
KV@{ icon: "sd:users", label: "Kavya and 10 others" }
M1["MMR Mumbai<br/>meeting origin"]:::hot
end
subgraph SG["Singapore zone"]
direction LR
M2["MMR Singapore"]:::service
SGU@{ icon: "sd:users", label: "2 engineers" }
end
subgraph US["San Jose zone"]
direction LR
M3["MMR San Jose"]:::service
RH@{ icon: "sd:laptop", label: "Rahul" }
end
BB@{ icon: "sd:globe", label: "Backbone<br/>one copy per layer, per link" }
KV ==> M1
M1 <==> BB
BB <==> M2
BB <==> M3
M2 ==> SGU
M3 ==> RH
class KV,SGU,RH client
class BB edge- The zone selector scores zones, not just distance: measured round trip from the client's region, free capacity in the zone (keep the 50% headroom), the account's residency rule, and whether the meeting already has an MMR there. Joining a zone the meeting is already in saves a cascade link.
- Cascades are trees, not meshes. With five regions, linking every MMR to every other multiplies the links; a tree rooted where most speakers are keeps each stream crossing each link once. Re-root if the speakers move.
- Webinars are the same tree, wider. Panelists sit on an interactive tier; tens of thousands of view-only attendees hang off leaf MMRs that receive only the speaker, the screen share and audio. Zoom said in 2024 its webinars reach up to 1 million attendees with 1,000 video panelists, without the multi-second delay of a streaming service.
- A full MMR splits the meeting, it doesn't refuse it: a second MMR in the same zone joins the cascade and new participants land there.
# placement/zone_select.py
"""
Choosing a meeting zone for one participant (illustrative). Prefer zones the
meeting already uses, then low round trip, while respecting residency and keeping
each zone's spare capacity.
"""
from dataclasses import dataclass
HEADROOM = 0.5 # keep half of each zone's capacity free
SAME_MEETING_MS = 40 # a zone already in the meeting is worth 40 ms of extra distance
@dataclass
class Zone:
name: str
country: str
capacity_gbps: float
used_gbps: float
def pick_zone(rtt_ms: dict, zones: list, meeting_zones: set, allowed_countries=None) -> str:
best, best_score = None, float("inf")
for z in zones:
if allowed_countries and z.country not in allowed_countries:
continue # data residency
if z.used_gbps > z.capacity_gbps * (1 - HEADROOM):
continue # keep the spare capacity
score = rtt_ms.get(z.name, 999)
if z.name in meeting_zones:
score -= SAME_MEETING_MS # avoids a new cascade link
if score < best_score:
best, best_score = z.name, score
if best is None:
raise RuntimeError("no zone with capacity: shed to the next region tier")
return bestDeep Dive 5: Signalling and Meeting State (Week 5 Concepts: Leader Election, Fencing)#
"The media plane can lose packets; the control plane can't lose a decision. Who is host, who is in the waiting room, who is muted, whether the meeting is locked: that's state with one owner and an order."
This builds on Week 5, Day 5 · Leader Election.
Host controls under a race
Kavya is host; Meenal is co-host. At the same moment Kavya locks the meeting and Meenal admits a latecomer from the waiting room. Separately, Arjun unmutes himself just as Kavya presses "Mute all". Then the server that owns the meeting's state crashes. What decides the outcome of each race, how does every client end up with the same roster, and how do you stop a stale owner from admitting someone after a new owner has taken over?
One strong answer
- One owner per meeting: a meeting controller holds the meeting's state in memory and applies commands one at a time. Lock-then-admit or admit-then-lock: whichever it applies first wins, and both clients see the same result.
- Versioned events out: each change gets the next version number and goes to every client over its signalling socket. A client that misses version 41 asks for a snapshot; nobody guesses.
- Policy, not timing, for mute: "Mute all" mutes everyone present; whether Arjun may unmute himself is a setting the host chose. His unmute after version 57 is checked against that setting.
- Failover with a lease and an epoch: the controller holds a lease on the meeting (in Redis or a coordination service) with an epoch number. A new controller takes over with epoch + 1 and rebuilds state from the snapshot and the clients' reconnects; any write from the old epoch is rejected (fencing).
- Media doesn't stop: MMRs keep forwarding during a controller failover. Only control actions pause for a few seconds.
What is published: after a wave of uninvited guests disrupting meetings in early 2020, Zoom turned on waiting rooms and passcodes by default from 5 April 2020 for many accounts, and in Zoom 5.0 (April 2020) grouped the host's security controls behind one icon. The control plane in this deep dive is a generic design for those features.
%% title: A participant's life in a meeting stateDiagram-v2 [*] --> JOINING JOINING --> WAITING: waiting room on JOINING --> IN_MEETING: admitted by policy WAITING --> IN_MEETING: host admits WAITING --> [*]: host denies IN_MEETING --> RECONNECTING: network change RECONNECTING --> IN_MEETING: same session within 60 s RECONNECTING --> [*]: timed out IN_MEETING --> [*]: leaves or removed class IN_MEETING ok class WAITING queue
- The waiting room holds signalling only. A waiting participant has a socket to the controller but no media token, so they see and hear nothing; admitting them issues the token.
- Media tokens are short-lived and scoped: meeting, participant, role, expiry of about a minute, signed by the control plane. The MMR checks the signature and never calls back on the hot path. Removing someone revokes by telling their MMR to drop the session now.
- The top-of-hour herd hits the control plane, not the media plane: ~60,000 joins a second. Spread controllers by meeting ID, keep join a single round trip after authentication, and let clients jitter their retries.
- The host leaves: the controller promotes a co-host, or the earliest-joined participant if the account allows; a meeting with no one left ends after a grace period.
# meeting/controller.py
"""
A meeting controller (illustrative). One owner per meeting, commands applied in
order, every change versioned and fanned out; the lease epoch fences off a stale
owner after failover.
"""
class Fenced(Exception):
pass
class MeetingController:
def __init__(self, meeting_id: str, epoch: int, store, bus):
self.id, self.epoch, self.store, self.bus = meeting_id, epoch, store, bus
self.version = 0
self.state = {"locked": False, "waiting": [], "in": {}, "hosts": set(), "self_unmute": True}
def apply(self, actor: str, cmd: dict) -> dict:
if not self.store.lease_is(self.id, self.epoch):
raise Fenced(f"epoch {self.epoch} no longer owns {self.id}")
s, op = self.state, cmd["op"]
if op in ("lock", "admit", "remove", "mute_all") and actor not in s["hosts"]:
return {"ok": False, "why": "host or co-host only"}
if op == "lock":
s["locked"] = True
elif op == "admit":
if s["locked"]:
return {"ok": False, "why": "meeting is locked"}
s["waiting"].remove(cmd["who"])
s["in"][cmd["who"]] = {"muted": True}
elif op == "mute_all":
for p in s["in"].values():
p["muted"] = True
elif op == "unmute_self":
if not s["self_unmute"]:
return {"ok": False, "why": "host has disabled self-unmute"}
s["in"][actor]["muted"] = False
self.version += 1
self.bus.publish(self.id, {"v": self.version, "by": actor, **cmd}) # to every client, in order
return {"ok": True, "v": self.version}Deep Dive 6: Cloud Recording and End-to-End Encryption (Week 9 Concepts: Security Architecture)#
"Two features pull in opposite directions. A cloud recording needs a server that can see the media. End-to-end encryption means no server can. You can offer both, but not in the same meeting."
This builds on Week 9, Day 5 · Security Architecture.
- The recorder is a participant. A recording worker joins the meeting like a silent client, subscribes to the active speaker, the screen share and all audio, decodes and composes a file, and uploads it to object storage in chunks as the meeting goes. If it crashes, a new one joins and the file has a gap, not a corruption.
- Ingest, then process. The live composite is written once; transcoding to other sizes, thumbnails and transcripts run afterwards from a queue. ~360 TB a day (Phase 2's estimate) is a storage-tier and lifecycle problem: move older recordings to colder storage, delete on the account's retention policy.
- Standard encryption protects media on every hop: Zoom 5.0 added 256-bit AES-GCM in April 2020, enabled system-wide from 30 May 2020. But the keys come from Zoom's servers, so the service could in principle decrypt.
- End-to-end encryption moves key generation to the clients. Zoom's design, shipped as a preview in October 2020, has the meeting's host generate keys and distribute them with public-key cryptography; Zoom's servers become relays that "do not have the necessary decryption key". The tradeoff Zoom listed: up to 200 participants, and no cloud recording, live transcription, breakout rooms or polling.
- Why an SFU makes E2EE possible at all: a forwarding server only needs the headers, which stay readable; the payload can be encrypted end to end. A mixing MCU must decode, so it could never be end-to-end encrypted.
Placement, state and security
Q1/4 One meeting has 300 viewers in San Jose and a speaker in Mumbai. With a cascade, how many copies of the speaker's top layer cross the Pacific?
Answer & explanation
Answer: A
The San Jose MMR receives one copy and fans out locally.
Q2/4 The meeting controller fails over. How do you stop the old controller from admitting someone afterwards?
Answer & explanation
Answer: B
Fencing with a monotonically increasing epoch is the standard defence against a stale leader.
Q3/4 Why does a participant in the waiting room see and hear nothing?
Answer & explanation
Answer: B
Admission is a control-plane decision that results in a token the media plane checks.
Q4/4 Why can't an end-to-end encrypted meeting be recorded in the cloud?
Answer & explanation
Answer: B
A cloud recorder is a server that sees the media; E2EE exists so that no server can.
Phase 5: Scaling and Edge Cases (5 minutes)#
"Walk me through Kavya's Monday morning, and then the cases that bite."
Kavya's Monday Morning#
- 9:58 AM · Join
- Signalling to the meeting controller, Mumbai zone picked, media token issued; first audio about 1.5 s after the click
- 10:00 AM · Thirteen others join
- Singapore and San Jose MMRs join the cascade; Meenal's UDP fails, TLS on 443 succeeds in under 2 s
- 10:07 AM · Arjun's cab enters a cellular dead spot
- Delay rises, his estimate falls to 600 kbps; he drops to 360p within a second; audio holds with FEC
- 10:12 AM · Arjun switches from 4G to the cab's Wi-Fi
- New IP address, same session: a second of silence, no rejoin
- 10:30 AM · Kavya shares her screen
- Screen share takes priority over thumbnails in every receiver's budget; the meeting ends at 10:41 and the recording lands in storage
Edge Cases#
Edge case 1: The top of the hour
Problem: At 10:00:00, joins jump several times above the minute before; controllers, auth and zone selection all spike at once.
Solution: Pre-warm capacity before :00 and :30 from yesterday's curve; make join one round trip after authentication; keep zone selection local to each region; have clients add a little random delay to retries. MMR capacity is reserved per zone, so the herd never reaches the media plane as a burst of placement calls.
Edge case 2: An MMR dies mid-meeting
Problem: 400 participants on one server lose media at once.
Solution: The zone controller marks it dead within a couple of seconds; clients notice silence, reconnect with their token and session ID, and are placed on another MMR in the same zone; the cascade relinks. A few seconds of freeze, then the meeting continues: its state lives in the controller, not the MMR. A whole data centre failing moves meetings to another zone, which Zoom aims to do in under five minutes.
Edge case 3: A keyframe storm
Problem: In a 5,000-person webinar, the speaker changes. Every receiver needs a keyframe of the new speaker, and 5,000 picture-loss requests hit one laptop's encoder.
Solution: The MMR answers for the sender: it collapses requests into at most one upstream keyframe request every few hundred milliseconds, and leaf MMRs in a cascade do the same. Pre-subscribe the likely next speakers at a low layer so the switch has a decodable picture at once.
Edge case 4: Uninvited guests
Problem: A meeting ID posted publicly fills with strangers who share their screens.
Solution: Passcodes and waiting rooms on by default (Zoom did this from 5 April 2020), screen sharing limited to the host by default, lock and remove in one place, and a removed participant's token revoked at their MMR immediately.
Scaling and edge cases
Q1/3 An MMR with 400 participants crashes. What brings them back?
Answer & explanation
Answer: B
The MMR holds no meeting state worth saving. A few seconds of media are lost; the meeting isn't.
Q2/3 The speaker changes in a 5,000-person webinar. How do you stop 5,000 keyframe requests reaching the speaker's laptop?
Answer & explanation
Answer: C
Aggregating feedback at each hop keeps the sender's encoder from melting.
Q3/3 Why pre-warm before :00 and :30?
Answer & explanation
Answer: B
Predictable spikes are capacity planning, not luck.
Phase 6: Monitoring and Operations#
"The dashboard asks four questions: can people get in, can they hear each other, is video adapting rather than freezing, and is the media plane healthy?"
Monitoring Dashboard#
Joins and control
| Metric | Now |
|---|---|
| Joins | 58,200/s |
| Join success | 99.93% |
| Click to first audio, p95 | 2.1 s |
| Controller command latency, p99 | 45 ms |
Audio and video quality
- Packet loss before repair / after repair: 2.4% / 0.2%
- Audio jitter buffer delay, p95: 62 ms; one-way audio delay in region, p95: 118 ms
- Video freezes over 1 s per participant-hour: 0.7
- Participants on TCP/TLS fallback: 4.1%
Media plane
- Egress 27.8 Tbps; busiest zone at 46% of capacity; cascade backbone at 38%
- MMR failures in the last hour: 3, median recovery 3.4 s
SLOs for the Platform#
SLO 1: Get in fast
- Target: 99.9% of joins succeed; p95 click to first audio < 3 s
SLO 2: Hear each other
- Target: p95 one-way audio delay < 150 ms within a region, < 300 ms across continents; packet loss after repair < 1% for 99% of participant-minutes
SLO 3: Video degrades, it doesn't freeze
- Target: fewer than 1 video freeze over a second per participant-hour
SLO 4: Recover without the user
- Target: after an MMR failure, media restored in < 10 s for 99% of affected participants; 99.9% meeting availability
Reading the dashboard
Q1/3 Packet loss before repair rises from 2.4% to 6%, but loss after repair stays at 0.2%. What should you do?
Answer & explanation
Answer: A
Users hear the residual loss. Watch the repair overhead, but the experience is intact.
Q2/3 The share of participants on TCP/TLS fallback jumps from 4% to 30% in one country. Likeliest cause?
Answer & explanation
Answer: B
Fallback is per participant and caused by their network. A regional jump points at a network change.
Q3/3 Video freezes double while bandwidth estimates and loss are normal. Where do you look?
Answer & explanation
Answer: C
Without loss or congestion, a freeze usually means a decoder waiting for a picture it can decode.
Final Challenge: The Results-Day Town Hall#
The interviewer leans back. "One more."
"An Indian IT company announces its quarterly results at 4 pm IST with a town hall: 8 panelists in Mumbai and Bengaluru, and 40,000 employees watching, 30,000 in India, 6,000 in the US and 4,000 in Europe. Most join in the last two minutes. Then the CEO takes questions. Keep it running and tell me what you'd degrade."
- Attendees: 40,000 view-only (30,000 India, 6,000 US, 4,000 Europe); 8 video panelists
- Each attendee receives the active speaker (~1.5 Mbps) and slides (~300 kbps): ~1.8 Mbps (assumption)
- 20,000 join in the minute before 4 pm (assumption)
- An MMR forwards ~10 Gbps (Phase 2 estimate); keep 50% headroom
- The speaker changes ~30 times an hour during questions
What do you protect, and what do you degrade?
Work out the egress per region and the MMRs, how streams reach each region, the join rate on one meeting's controller, and what happens at every speaker change. Then decide what to degrade.
One strong answer
- Egress: 40,000 × 1.8 Mbps = 72 Gbps: India 54 Gbps, US ~11 Gbps, Europe ~7 Gbps. At 10 Gbps an MMR and 50% headroom: about 11 leaf MMRs in India, 3 in the US, 2 in Europe, say 16.
- A tree, not a fan-out from Mumbai: panelists on an interactive MMR in Mumbai; one cascade link to a root MMR in each region; leaves under each root. Each stream crosses each ocean once, a few Mbps, not 10,000 times.
- Joins: 20,000 in a minute is ~330 a second, small for the platform but all on one meeting. Attendees don't get the roster, so a join doesn't fan out to 40,000 sockets; panelists see a count. Admission is policy, with no waiting room for attendees.
- Speaker changes: each change means a keyframe for 40,000 decoders. Leaf MMRs collapse requests; the next likely speakers are pre-subscribed at a low layer to the roots.
- Questions: 40,000 people raising hands or posting questions go to a rate-limited queue for the moderators, not into the meeting controller's ordered event stream.
- Degrade in this order: attendee video from 720p to 360p (cuts egress by two-thirds), then slides to lower frame rate, then thumbnails of panelists. Never audio, the active speaker's picture, or the moderators' controls.
Final challenge · Quest check
Q1/4 40,000 attendees each receive ~1.8 Mbps. What is the total egress?
Answer & explanation
Answer: B
40,000 × 1.8 Mbps = 72,000 Mbps = 72 Gbps.
Q2/4 How should the US attendees receive the panelists' streams?
Answer & explanation
Answer: B
A tree keeps the ocean crossing to one copy per stream.
Q3/4 20,000 attendees join in one minute. What keeps the meeting controller from melting?
Answer & explanation
Answer: B
Per-join work must be constant, not proportional to the audience.
Q4/4 Which should be degraded first?
Answer & explanation
Answer: A
Resolution is a nicety; voice and control are the meeting.
Interview Conclusion#
"Why not mix everything on the server, like the old conference bridges?"
"Because mixing means decoding every camera and encoding a picture for every receiver: on my estimate about 80 times more servers than forwarding, extra delay, one layout for everyone, and no possibility of end-to-end encryption. A forwarding server reads headers and copies packets, and every receiver gets its own layers."
"And why not peer to peer, with no servers at all?"
"Because the sender's uplink pays for every receiver. Ten people means nine uploads from a phone on 4G. Mesh is fine for two people; meetings need a router."
"The single most important decision?"
"Separate control from media, and make the media plane a forwarder that adapts per receiver. Signalling is small, ordered and owned by one controller per meeting; media is lossy UDP through the nearest zone, layered at the sender, selected per receiver, and cascaded between regions so each stream crosses an ocean once."
Summary: Concepts Applied from 10-Week Course#
- Week 1: Data at scale
- Partitioning — participants placed by zone; meetings split across MMRs; hot keys — the 1,000-person meeting and the town hall
- Week 2: Failure-first design
- Timeouts and retries — racing UDP and TLS; NACK only when the round trip allows; reconnect without rejoining
- Week 3: Messaging and async processing
- Backpressure and flow control — bandwidth estimation, layer selection, pausing layers nobody wants
- Week 5: Consistency and coordination
- Leader election and fencing — one controller per meeting, leases and epochs
- Week 9: Multi-tenancy and security
- Data residency for placement; tokens, waiting rooms and end-to-end encryption
- Week 10: Production readiness
- Capacity planning — forwarding vs mixing, 50% headroom, the top-of-hour herd; SLOs on delay and freezes
Why Zoom Matters#
Late is the same as lost
- A packet that arrives after its playout time is worthless, so reliability comes from FEC, concealment and fast adaptation, not from retries
The server must not look
- Scaling, delay and encryption all point the same way: a server that forwards packets without decoding them
Every receiver is different
- One sender's video reaches a phone in a cab and a laptop on fibre; the system adapts per receiver, many times a second
"Separate control from media, forward rather than mix, and give every receiver the best layer its network can take."
Self-Assessment Checklist#
After studying this case study, you should be able to:
Media architecture:
- Compare mesh, MCU and SFU on uplink, CPU, delay and encryption
- Estimate concurrent participants and media bandwidth from published totals
- Explain how an SFU forwards audio and video without decoding
Adaptation and transport:
- Explain simulcast and SVC, and pick layers per receiver from a bandwidth estimate
- Combine FEC, NACK, PLI and an adaptive jitter buffer for lossy networks
- Get media through firewalls with TCP/TLS fallback, and explain its cost
Placement, control and operations:
- Place participants by zone and cascade MMRs across regions
- Design a meeting controller with ordered events, leases and fencing
- Plan a 40,000-person town hall: trees, keyframe storms and what to degrade
Sources#
Zoom and its suppliers:
- Blog
Eric S. Yuan — A Message to Our Users (about 10 million daily meeting participants at the end of December 2019; more than 200 million in March 2020; 90,000 schools in 20 countries), Zoom, 1 April 2020: https://www.zoom.com/en/blog/a-message-to-our-users/
- Read
SiliconANGLE — Zoom retracts claim of 300M daily active users (the 22 April figure described as users, corrected to 300 million daily meeting participants; participants counted per meeting), 30 April 2020: https://siliconangle.com/2020/04/30/zoom-retracts-claim-300m-daily-active-users-rivals-narrow-gap/
- Blog
Zoom — Zoom Hits Milestone on 90-Day Security Plan, Releases Zoom 5.0 (256-bit AES-GCM; system-wide enablement on 30 May), 22 April 2020: https://www.zoom.com/en/blog/zoom-hits-milestone-on-90-day-security-plan-releases-zoom-5-0/
- Talk
Zoom Q3 FY2021 earnings call, Kelly Steckelberg (annualized run rate of 3.5 trillion meeting minutes at the end of the quarter), 30 November 2020: https://www.fool.com/earnings/call-transcripts/2020/12/01/zoom-video-communications-zm-q3-2021-earnings-call
- Talk
Zoom Video Communications — Form 10-K for the fiscal year ended 31 January 2021 (a multimedia router on commodity hardware and globally distributed cloud infrastructure; AWS and Oracle Cloud): https://www.sec.gov/Archives/edgar/data/1585521/000158552121000048/zm-20210131.htm
- Read
Zoom — Zoom: Architected for Reliability (MMRs grouped in meeting zones; zone controllers and a global cloud controller; geolocation steering; UDP with fallback to TCP/TLS including HTTPS/443; multiple simultaneous streams and layer selection; 50% excess capacity; 99.9% availability; RTO goal under five minutes): https://library.zoom.com/admin-corner/architecture-and-design/zoom-architected-for-reliability
- Blog
Zoom Support — Zoom network firewall or proxy server settings (UDP 8801–8810; TCP 443, 8801, 8802): https://support.zoom.us/hc/en-us/articles/201362683
- Blog
Max Krohn — Zoom Rolling Out End-to-End Encryption Offering (host-generated keys distributed with public-key cryptography; up to 200 participants; no cloud recording, live transcription, breakout rooms, polling or private chat), Zoom, 14 October 2020: https://www.zoom.com/en/blog/zoom-rolling-out-end-to-end-encryption-offering/
- Talk
Zoom — Zoom unveils 1 million attendee capability for Zoom Webinars (up to 1,000 interactive video panelists), 19 August 2024: https://news.zoom.com/zoom-unveils-1-million-attendee-zoom-webinars/
- Read
AWS — AWS and Zoom Extend Strategic Relationship (from 10 million daily meeting participants in December 2019 to more than 300 million a day regularly since April 2020; thousands of EC2 instances added in a day), 30 November 2020: https://press.aboutamazon.com/2020/11/aws-and-zoom-extend-strategic-relationship
Reporting:
- Docs
Data Center Frontier — Inside Zoom's Infrastructure: Scaling Up Massively With Colo and Cloud (17 data centres; nine Equinix sites as of November 2019; seven petabytes a day through Oracle Cloud Infrastructure), 22 May 2020: https://datacenterfrontier.com/inside-zooms-infrastructure-scaling-up-massively-with-colo-and-cloud/
- Talk
KrASIA — Video conferencing platform Zoom to strengthen its presence in India (2 of 17 data centres in Mumbai and Hyderabad, 4× capacity in four months; 6,700% more free sign-ups in India from January to April 2020), 22 July 2020: https://kr-asia.com/video-conferencing-platform-zoom-to-strengthen-its-presence-in-india
- Book
The Register — Zoom's end-to-end encryption preview (keys generated by participants' machines; servers without the decryption key; 200 participants), 27 October 2020: https://www.theregister.com/2020/10/27/zoom_endtoend_encryption/
- Book
Engadget — Zoom will enable waiting rooms by default to keep trolls out (waiting rooms and passwords from 5 April 2020), 4 April 2020: https://www.engadget.com/2020-04-04-zoom-waiting-rooms-default.html
- Read
Philipp Hancke — How Zoom's web client avoids using WebRTC (WebSockets and WebAssembly in 2018; WebRTC data channels from 2019), webrtcHacks, updated 8 September 2019: https://webrtchacks.com/zoom-avoids-using-webrtc/
Standards:
- Read
ITU-T G.114 — One-way transmission time (most applications not significantly affected below about 150 ms; 400 ms as the planning limit), May 2003: https://www.itu.int/rec/T-REC-G.114
- Docs
Stefan Holmer, Henrik Lundin, Gaetano Carlucci, Luca De Cicco, Saverio Mascolo — A Google Congestion Control Algorithm for Real-Time Communication, draft-ietf-rmcat-gcc-02 (loss-based rules at 2% and 10%; delay-gradient over-use detection), July 2016: https://datatracker.ietf.org/doc/html/draft-ietf-rmcat-gcc-02
- Docs
RFC 4585 — Extended RTP Profile for RTCP-Based Feedback (NACK, PLI): https://www.rfc-editor.org/rfc/rfc4585
- Docs
RFC 5109 — RTP Payload Format for Generic Forward Error Correction: https://www.rfc-editor.org/rfc/rfc5109
- Docs
RFC 6716 — Definition of the Opus Audio Codec (in-band FEC): https://www.rfc-editor.org/rfc/rfc6716
- Talk
RFC 6190 — RTP Payload Format for Scalable Video Coding: https://www.rfc-editor.org/rfc/rfc6190
- Docs
RFC 8853 — Using Simulcast in SDP and RTP Sessions: https://www.rfc-editor.org/rfc/rfc8853
Design note: the architecture, code, thresholds, bitrates, server counts and dashboards here are an illustrative design for the problem, not a description of Zoom's internal systems beyond what Zoom has published. Estimates in Phase 2 are labelled as assumptions or estimates; the round-trip times between continents are estimates.
Further Reading#
Related systems to study:
- Read
WhatsApp (Bonus Problem 2 in this series): signalling, presence and end-to-end encryption for messages
- Talk
Netflix (Bonus Problem 6): adaptive bitrate for video on demand, where seconds of buffer are allowed
- Talk
YouTube (Bonus Problem 19): transcoding ladders and why live and on-demand differ
- Docs
Figma (Bonus Problem 15): one server per document, ordered changes and cursors in real time
Papers and specifications:
- Paper
Zoom — E2E Encryption for Zoom Meetings (the cryptographic design whitepaper, first published May 2020): https://github.com/zoom/zoom-e2e-whitepaper
- Docs
RFC 8888 — RTP Control Protocol Feedback for Congestion Control
Foundational concepts (this course):
- Read
Week 1, Day 1 — Partitioning Deep-Dive
- Read
Week 2, Day 1 — Timeout Hell
- Read
Week 3, Day 3 — Backpressure and Flow Control
- Read
Week 5, Day 5 — Leader Election
- Read
Week 9, Day 3 — Data Residency
- Read
Week 9, Day 5 — Security Architecture
- Read
Week 10, Day 4 — Capacity Planning
"Late is the same as lost; so forward, don't mix, and give every receiver the best picture its network can carry."
- 0/7stages cleared
New lessons by email
New lessons, side quests and case files as they ship, at most once a week. No spam, unsubscribe anytime.
Discussion · …
Public: everyone reading this lesson sees comments. Be kind and keep it on the material.