System Design Interviews, Step by Step · Classic Walkthroughs, Part 2
Design a Video Streaming Platform
Upload once, watch anywhere: transcode each video into several qualities split into short segments, serve them from a CDN, and let the player switch quality as the network changes.
The question
"Design a video platform: creators upload videos; viewers watch them on phones, laptops and TVs with smooth playback."
Clarify the requirements
- Functional: upload videos (up to 4 hours); process them for playback; watch with adaptive quality; view counts; basic search and recommendations (mention only).
- Scale: 500,000 uploads per day; 100 million daily viewers watching ~30 minutes each.
- Non-functional: playback starts in under 2 seconds and rarely buffers; uploads survive bad connections; processing can take minutes; storage is the biggest cost.
Estimate the scale
- Watch traffic: 100M × 30 min ≈ 3 billion minutes/day. At an average 3 Mbps, that's roughly 100+ Tbps at peak: only a CDN can serve this.
- Storage: 500K uploads × 10 min average × ~5 renditions ≈ 25M minutes of video per day. At ~20 MB per minute across renditions, ≈ 500 TB/day (~180 PB/year): object storage with cheaper tiers for old, rarely watched videos.
- Upload ingest: 500K/day ≈ 6/s, but each is large; resumable uploads straight to object storage.
API and data model
POST /videos -> { video_id, upload_url (resumable, signed) }
PUT {upload_url} (chunks; resumes after failures)
GET /videos/{id} -> { title, status: processing|ready, manifest_url }
GET {cdn}/videos/{id}/master.m3u8 (HLS or DASH manifest listing renditions)
POST /videos/{id}/views (batched by the player)
videos: video_id | owner | title | status | duration | created_at
renditions: video_id | resolution | bitrate | manifest_key
object storage: originals/{id}, segments/{id}/{rendition}/{n}.ts
High-level design
creator ─► upload service ─► object storage (original) ─► queue ─► transcoding workers (many)
│
segments + manifests ◄──────────┘
│
viewer ─► API (metadata) ─► manifest URL ─► CDN edge ─(miss)─► origin (object storage)
player picks quality per segment based on measured bandwidth
Deep dives
1. Transcoding and segments. Each upload becomes a ladder of renditions (240p, 480p, 720p, 1080p, 4K) and each rendition is split into segments of 2–6 seconds. Transcoding is split into chunks of the video processed in parallel by many workers (a directed workflow: split → encode chunks → stitch → package → publish), then the video's status flips to ready.
2. Adaptive bitrate streaming. The player downloads a manifest, measures how fast segments arrive, and picks the quality for the next segment: if bandwidth drops, switch down before the buffer runs dry; when it recovers, step back up. HLS and DASH are the standard formats.
LADDER = [("240p", 0.4), ("480p", 1.2), ("720p", 3.0), ("1080p", 6.0)] # (name, Mbps)
SEGMENT = 4 # seconds of video per segment
def choose(bandwidth, buffer):
# Use at most 80% of measured bandwidth; be cautious when the buffer is low.
budget = bandwidth * (0.5 if buffer < 8 else 0.8)
best = LADDER[0]
for name, mbps in LADDER:
if mbps <= budget:
best = (name, mbps)
return best
network = [8, 8, 7, 2.5, 1.0, 1.0, 3, 6, 9, 9] # Mbps measured while downloading each segment
def simulate(pick):
buffer, stalled, chosen = 0.0, 0.0, []
for bandwidth in network:
name, mbps = pick(bandwidth, buffer)
download = SEGMENT * mbps / bandwidth # seconds to fetch this segment
if download > buffer:
stalled += download - buffer # the picture freezes while we wait
buffer = max(0.0, buffer - download) + SEGMENT
chosen.append(name)
return chosen, stalled
adaptive, adaptive_stall = simulate(choose)
fixed, fixed_stall = simulate(lambda bandwidth, buffer: LADDER[-1]) # always 1080p
print("network: ", " ".join(f"{b:>5}" for b in network))
print("adaptive: ", " ".join(f"{q:>5}" for q in adaptive))
print(f"stalled: adaptive {adaptive_stall:.1f}s, always-1080p {fixed_stall:.1f}s")
Output
network: 8 8 7 2.5 1.0 1.0 3 6 9 9 adaptive: 720p 720p 720p 480p 240p 240p 480p 720p 1080p 1080p stalled: adaptive 1.5s, always-1080p 51.0s
The adaptive player's 1.5 seconds is mostly the first segment loading at startup. The fixed-quality player freezes for most of a minute when the network dips, which is exactly the experience adaptive streaming exists to prevent.
3. The CDN does the heavy lifting. Popular videos are cached at edge locations near viewers; the long tail is fetched from origin on demand. Pre-warm the CDN for expected hits (a big premiere), and use cheaper storage tiers for old videos nobody watches.
4. View counts at scale. Players send view events in batches to a queue; stream processors aggregate counts per minute and update the displayed number periodically. Exact real-time counts aren't needed, and per-view database writes would be enormous.
Trade-offs
- More renditions improve quality on every connection but multiply storage and transcoding cost; popular videos can get extra renditions later.
- Segment length: short segments adapt faster and start quicker; long segments compress better and mean fewer requests.
- Transcode on upload vs on demand: upfront is simpler and faster to play; on-demand saves storage for the many videos that are rarely watched.
- Own CDN vs commercial CDN: cost at huge scale vs time to market.
What interviewers look for
- Separating upload/processing (async, queue-driven) from playback (CDN).
- Renditions + segments + manifests and adaptive bitrate.
- Realistic bandwidth and storage numbers, and cost awareness.
- Batched, approximate view counting.
Key points
- Upload resumably to object storage, then transcode asynchronously in parallel.
- Each video → several renditions → short segments + a manifest (HLS/DASH).
- Players adapt quality per segment to measured bandwidth.
- The CDN serves almost all traffic; counts are aggregated, not written per view.
Practice questions
What is adaptive bitrate streaming?
Answer
The video is encoded at several qualities, each split into short segments listed in a manifest. The player measures download speed and buffer level and chooses the quality for each next segment, dropping quality before the buffer runs out and stepping up when bandwidth allows.
Why is transcoding done asynchronously with a queue and many workers?
Answer
Transcoding a long video into several renditions is CPU-heavy and slow. Queueing the job lets the upload finish immediately, and splitting the video into chunks processed in parallel by many workers makes processing time short and scalable.
Open in the app to mark lessons complete, take notes and track your progress.