00. Design a Video Streaming / OTT Platform (VOD)¶
~22 min read · Level: advanced · Part 1 of 4 (Overview → HLD → LLD → Q&A)
Problem¶
An OTT platform stores video-on-demand and streams it to millions of viewers on any device and any network, without buffering. This is Netflix/YouTube/Prime Video for pre-recorded content. The engineering lives in two places: an ingest + transcoding pipeline that turns one uploaded master into dozens of renditions, and a delivery path that streams the right rendition from a nearby edge and adapts to each viewer's changing bandwidth.
Thread one scenario: a hit show's new season releases at 8pm, and 10 million people press play within the first hour — many on wifi that fluctuates, some on mobile data. Every one must start playing in ~2 seconds and never buffer, while the origin servers barely notice. That concurrency spike, absorbed by edge caching and adaptive bitrate, is the story.
Functional requirements¶
- Upload/ingest a video master; transcode it into multiple resolutions/bitrates.
- Stream a title to viewers with adaptive bitrate (ABR) based on their bandwidth.
- Resume playback from where the viewer left off.
- Browse/search a catalog with thumbnails and metadata.
- Support many device types and codecs.
De-scoped: live streaming (its own study — different latency profile), recommendations (its own study), and DRM key management internals (noted as a constraint).
Non-functional requirements¶
The dominant constraint is delivering massive concurrent read bandwidth at low startup latency, which pushes almost everything to the edge.
- Startup latency: play starts in ~1–2 seconds.
- Rebuffer ratio: near zero — the quality metric viewers actually feel.
- Availability: playback must stay up globally; the upload/transcode path can lag.
- Consistency: eventual for catalog/metadata; playback position tolerates seconds of lag.
- Durability: masters and renditions must not be lost (re-transcoding is expensive).
Scale estimation¶
Assume 200M subscribers, 100M daily streams averaging 1 hour at ~5 Mbps. That's 100M hours/day of delivery. Peak concurrent: our 8pm release plus normal load, say 20M concurrent streams at 5 Mbps = 20M × 5 Mbps = 100 Tbps of egress. No origin can serve 100 Tbps — this number alone dictates a CDN-first design where origin serves cache-fills only.
Storage: a single 1-hour title transcoded into ~10 renditions × multiple codecs is ~50–100 GB per title; a catalog of 100k titles is ~5–10 PB of renditions plus masters. This lives in tiered blob storage (hot titles on fast/edge storage, long-tail on cold storage).
Transcoding is compute-heavy but offline: a 1-hour 4K master takes many CPU-hours to encode into all renditions, parallelized by splitting the video into chunks and encoding them concurrently — turning hours of wall-clock into minutes across a fleet.
API sketch¶
POST /api/v1/titles/{id}/ingest {master_url} → transcode job id
GET /api/v1/titles/{id}/manifest → ABR manifest (HLS/DASH)
GET /cdn/{title}/{rendition}/seg_{n}.ts → media segment (from edge)
POST /api/v1/titles/{id}/progress {position_sec} → save resume point
GET /api/v1/browse?row=… → catalog rows + thumbnails
Solutioning¶
Separate the offline pipeline from the online delivery, because they have nothing in common. Ingest/transcoding is a batch problem: accept the master, split it into short segments (e.g. 2–10s each), encode every segment into every rendition in parallel across a worker fleet, and write the immutable segments plus a manifest (the index of renditions and segment URLs) to blob storage. Chunk-level parallelism is the key trick — a title's encode time is bounded by the slowest chunk, not the whole runtime.
Delivery is where the 100 Tbps lives, and the reframing is decisive: this is not a compute problem at origin; it is a distribution problem solved by pushing bytes to the edge. Segments are immutable and identical for every viewer, so they cache perfectly. A CDN (often with ISP-embedded appliances) holds popular titles at the edge; a viewer's player fetches the manifest, then pulls segments from the nearest edge. Origin only serves the first cache-fill per edge per segment. For our 8pm release, the season is pre-positioned ("pre-warmed") to edges before release, so 10M viewers hit warm caches and origin sees almost nothing.
The second half of delivery is adaptive bitrate. The player, not the server, drives quality: it measures throughput and buffer level and picks the next segment's rendition from the manifest — dropping to a lower bitrate when wifi dips, climbing back when it recovers. The reframing here: the server offers a menu (the manifest); the client orders per segment. This keeps servers stateless and puts adaptation where the bandwidth signal actually is. The tradeoff is more renditions to store and encode (the menu must be rich enough), justified by near-zero rebuffering.
The rest is small: resume-position is a tiny per-user write, catalog/metadata is a cached read path, and DRM wraps segments with per-title keys. The files below detail the pipeline, the manifest/ABR mechanics, and delivery.