Case Study 5: YouTube / Hotstar Video Streaming
Video upload, transcoding and smooth adaptive streaming to tens of millions of people through a CDN, both VOD and live events like the IPL.
C.1 Requirements
Functional requirements:
- A creator can upload videos (large files, resumable upload).
- Videos are transcoded into multiple resolutions (240p to 4K) and codecs.
- The viewer watches video with HLS/DASH adaptive bitrate: quality changes with the network.
- Live streaming: an event like an IPL match is watched by crores of people at once, with low delay.
- Search, video metadata (title, description, thumbnail) and view count.
- Watch history and resume playback (from where you left off).
- DRM and an entitlement check (subscription) for paid content.
Non-functional requirements:
- Playback availability 99.99%; video start time < 2 seconds, rebuffering ratio < 1%.
- Live glass-to-glass latency 10-30 seconds (standard HLS), ~3-5 seconds in low-latency mode.
- Live peak: 2-3 crore+ concurrent viewers (Hotstar has publicly reported 2.5 crore+ concurrency during the IPL).
- Durable uploads: not a single uploaded video may be lost (S3-like 11 nines).
- Egress bandwidth at the Tbps level: 90%+ of traffic must be served by the CDN, not the origin.
- Eventual consistency is OK for view counts and metadata.
C.2 Estimation (back-of-the-envelope)
| Item | Number | How it is derived |
|---|---|---|
| Daily active viewers (VOD) | 20 crore | Assumption; every user watches avg 5 videos x 6 min = 30 min/day. |
| Uploads per day | 5 lakh videos | Avg 10 min, raw ~500 MB = 250 TB raw/day. |
| Transcoded storage | ~1 PB/day | Each video has ~6 renditions + codecs; together with the 250 TB raw, the total is ~3-4x = ~1 PB/day, ~350 PB a year (moved to a cold tier). |
| VOD egress bandwidth | ~8 Tbps avg, ~20 Tbps peak | 20 crore x 1800 sec / 86400 = ~42 lakh avg concurrent viewers x 2 Mbps = ~8 Tbps; evening peak 2-3x. |
| Live IPL peak bandwidth | ~60-75 Tbps | 2.5 crore concurrent x avg 2.5-3 Mbps (mobile 480p-720p) = ~60-75 Tbps; possible only with multi-CDN. |
| Segment request rate (live) | ~40-60 lakh/sec | 2.5 crore viewers, 4-6 sec segment => 2.5 crore / 5 = 50 lakh segment GETs/sec + playlist refreshes. |
| Metadata DB | ~1 TB/year | 5 lakh videos/day x 5 KB = 2.5 GB/day; it is small, sharded MySQL/Postgres is enough. |
C.3 API design
| Endpoint | Purpose |
|---|---|
| POST /v1/uploads | Start an upload session; returns a resumable upload URL and upload_id |
| PUT /v1/uploads/{upload_id}/chunks/{n} | Chunk upload (5-10 MB); if it fails only that chunk is retried |
| POST /v1/uploads/{upload_id}/complete | Finalize after all chunks; triggers the transcoding job |
| GET /v1/videos/{id}/playback | After the entitlement check, a signed manifest URL (HLS m3u8 / DASH mpd) and a DRM license URL |
| GET /v1/live/{event_id}/playback | Manifest URL of the live event, with CDN selection |
| POST /v1/videos/{id}/progress | Save the watch position (for resume), batched heartbeat |
C.4 Data model
| Table / Store | Fields | Why |
|---|---|---|
| videos (MySQL, sharded by video_id) | video_id PK, owner_id, title, status (uploading/processing/ready), duration, manifest_path, created_at | Metadata is relational and the status state machine needs ACID. |
| renditions (MySQL) | video_id, resolution, codec, bitrate, segment_path_prefix | Track which quality is ready and generate the manifest. |
| object storage (S3/GCS) | raw/{video_id}, hls/{video_id}/{res}/seg_0001.ts ya .m4s, manifests | Cheap, durable blobs; this is the CDN's origin. |
| watch_history (Cassandra, partition key user_id) | user_id, video_id, position_sec, updated_at | Very many writes (heartbeats), simple key-based access. |
| view_counts (Redis + Kafka -> OLAP) | video_id -> count | A real-time approximate count in Redis, accurate analytics from stream processing. |
| entitlements (Redis cache + SQL) | user_id, plan, valid_till, allowed_regions | A fast check is needed on every playback, and the DB must not take load at live peak. |
C.5 High-level architecture
- 1.Clients: Mobile / TV App and Web Player (ABR, HLS / DASH), Creator App (chunked upload), Broadcast Feed (stadium SRT / RTMP)
- 2.Edge: DNS / GSLB (region + CDN pick), Multi-CDN (Akamai, CloudFront, ISP caches) with origin shield
- 3.Load Balancer -> API Gateway (auth, rate limit)
- 4.Playback path: Playback Service (entitlement, signed URL) -> Redis entitlement check -> signed manifest + DRM URL (DRM Service: Widevine / FairPlay license)
- 5.Player fetches segments from the Multi-CDN edge; on a cache miss -> origin shield -> S3 Origin (raw/{id}, hls/{id}/{res}/seg_0001.m4s, manifests, 11 nines)
- 6.Upload path: Creator -> Upload Service (resumable, S3 multipart) -> Kafka / SQS transcode jobs + events -> Transcoding Workers (FFmpeg, parallel, spot) -> Packager (HLS + DASH CMAF + DRM) -> S3
- 7.Live path: stadium feed -> Live Ingest (redundant, 2 regions) -> Live Transcoder (real-time bitrates) -> Packager -> S3 origin
- 8.Data and events: MySQL (videos, renditions), Cassandra (watch_history), Redis (entitlement, view count); Kafka events to Thumbnail Worker + moderation, Search Indexer (status = ready), Analytics Pipeline (QoE, view counts)
VOD: 42 lakh avg concurrent x 2 Mbps = ~8 Tbps avg, ~20 Tbps peak, 90%+ from CDN. IPL: 2.5 cr concurrent x 2.5-3 Mbps = 60-75 Tbps, multi-CDN only, 95%+ cache hit.
- Playback: the app picks the CDN + region via DNS/GSLB, LB -> API Gateway -> Playback Service; it checks entitlement in Redis and returns a signed manifest + DRM URL.
- The player fetches segments straight from the Multi-CDN edge; on a miss, origin shield -> S3 Origin. 90%+ of traffic comes from the CDN, not the origin.
- Upload: Creator -> Upload Service puts chunks in S3; on complete a Kafka job -> Transcoding Workers -> Packager -> S3.
- Live: stadium feed -> Live Ingest (2 regions) -> Live Transcoder -> Packager -> S3 origin; Kafka events go to Thumbnail, Search, Analytics.
- Bandwidth math: IPL 2.5 cr x 2.5-3 Mbps = 60-75 Tbps; hence multi-CDN and a 95%+ cache hit are essential.
| Component | What it does |
|---|---|
| Upload Service | Handles resumable chunked upload; chunks go straight into an S3 multipart upload, and on complete an event goes into the job queue. |
| Transcoding Workers | Split the video into 2-6 sec segments with FFmpeg and transcode in parallel (H.264/H.265/AV1, multiple bitrates); autoscaling on spot instances. |
| Packager | Packages renditions into HLS (m3u8) and DASH (mpd) manifests; with CMAF one segment serves both. |
| Playback Service | Does the entitlement check, returns the signed CDN URL and DRM license URL; picks the best CDN for the user's region/ISP. |
| Multi-CDN | Segments are cached at the edge; embedded caches inside ISPs (YouTube publicly uses Google Global Cache) save last-mile traffic. |
| Live Ingest + Live Packager | The RTMP/SRT feed comes from the stadium, is transcoded in real time, and a new segment and updated playlist are pushed to the origin every few seconds. |
| Analytics Pipeline | Player heartbeats (bitrate, buffering, errors) go into Kafka; stream processing produces QoE dashboards and view counts. |
C.6 Main flows
Video upload and transcoding:
- The creator app gets an upload_id from POST /v1/uploads.
- The file goes in 10 MB chunks via S3 multipart upload; if the network breaks only missing chunks are retried.
- On the complete call the Upload Service sets video status = processing and puts a transcode job on Kafka.
- The splitter cuts the video into small chunks (at GOP boundaries) so it can be transcoded in parallel.
- Transcoding Workers encode every chunk in every resolution/bitrate, with output going to S3.
- The Packager joins the segments into HLS/DASH manifests and applies DRM encryption.
- Thumbnail and content moderation workers run in parallel.
- When everything is ready, status = ready, the Search Indexer is updated and subscribers are notified.
VOD playback (adaptive bitrate):
- The user presses play and the app calls GET /v1/videos/{id}/playback.
- Playback Service checks entitlement in Redis and returns a signed master manifest URL.
- The player fetches the master manifest from the CDN; it lists all quality levels.
- The player estimates bandwidth, picks a rendition and fetches segments from the CDN edge.
- On an edge cache miss it fetches from the mid-tier/origin shield, then from the S3 origin.
- If the network slows, the player picks the next segment at a lower bitrate (ABR); no rebuffering.
- The player sends progress and a QoE heartbeat every 30 sec.
Live IPL streaming:
- The stadium production feed arrives over SRT/RTMP at redundant ingest servers (two regions).
- The live transcoder produces multiple bitrates in real time, in 4-6 sec segments.
- The Live Packager writes the new segment to the origin and updates the media playlist.
- Multiple CDNs sit behind an origin shield; the CDN TTL of every playlist is very short (1-2 sec), segments get a long one.
- Crores of players refresh the playlist every few seconds and fetch new segments from the edge.
- Request coalescing: when thousands of identical requests miss at one edge, only one request goes to the origin.
- Under overload, graceful degradation: lower the default bitrate, switch off non-critical features (comments, recommendations).
C.7 Deep dives
- 1.Creator App: POST /uploads -> upload_id; Upload Service: 10 MB chunks, only the missing chunk is retried; S3 raw/ multipart
- 2.Complete -> Kafka job, status = processing -> Splitter: small chunks at GOP boundaries
- 3.Transcoding Workers (FFmpeg): every chunk x every rendition in parallel (240p, 480p, 720p, 1080p, 4K)
- 4.Publish low-res first, HD later; popular creators = priority queue
- 5.Packager: HLS m3u8 + DASH mpd, CMAF, DRM encrypt -> S3 hls/{id}/{res}/ segments + manifests
- 6.status = ready -> Search Indexer + notify subscribers; thumbnail + moderation workers in parallel
- 1.Player: GET /playback -> master.m3u8 signed URL (240p, 480p, 720p, 1080p, 4K)
- 2.The player picks the quality for every segment (4-6 s) based on bandwidth: 1080p 1080p 1080p ...
- 3.Network slow: 720p, 480p, 480p; seg 1 ... seg 8: no rebuffering, only the quality drops
- 4.CDN edge (hit 90%+) -> Mid-tier / shield (miss) -> S3 origin
- 5.Progress + QoE heartbeat every 30 s -> Kafka -> analytics
- 1.2.5 cr players: playlist refresh every 4-6 s = ~50 lakh segment GET/s
- 2.Edge PoP 1, 2, 3: 1000 misses on each
- 3.Origin shield: request coalescing, 1 request
- 4.Live Packager origin: playlist TTL 1-2 s, long segment TTL
- 5.Toss/wicket spike: pre-warm, jitter + backoff, rate limit, comments/reco OFF
Problem: crores of concurrent viewers at live peak and origin overload. Multi-CDN + origin shield + request coalescing; 95%+ cache hit. Pre-warm capacity and load test before the match, because autoscaling takes minutes while traffic rises in seconds at the toss.
Problem: thundering herd: everyone logs in/refreshes at once at match start or a wicket. Cache and pre-compute login, entitlement and config; client-side jitter and exponential backoff; API Gateway rate limiting; switch off non-critical APIs with a feature flag (graceful degradation).
Problem: transcoding a large video is slow (a 1-hour 4K video). Split the video into chunks and transcode in parallel on thousands of workers (map-reduce style). Use a priority queue for popular creators; publish the low resolution first, HD later.
Problem: buffering on a poor network (2G/3G, crowded towers). ABR (HLS/DASH) switches quality on every segment; use small segments; efficient codecs (H.265/AV1) give the same quality at a lower bitrate; the player adapts to its buffer health.
Problem: storage cost growing to exabytes. Keep popular videos on a hot tier and old/rarely watched ones on cold storage (S3 Glacier-like); for the long tail keep only a few renditions and transcode the rest on demand.
Problem: piracy and unauthorised access. DRM (Widevine, FairPlay, PlayReady) segment encryption, signed CDN URLs with a short expiry, a concurrent stream limit per account, forensic watermarking.
C.8 Trade-offs
| Decision | What was chosen and why |
|---|---|
| HLS vs DASH | Support both: HLS is needed for Apple devices, DASH on Android/web; with CMAF one segment format is reused by both, halving storage. |
| Live latency vs stability | Standard HLS (6 sec segments) is more stable and cacheable but 20-30 sec behind; LL-HLS has lower latency but more requests on the CDN. |
| Single CDN vs Multi-CDN | Multi-CDN: at IPL scale one CDN has capacity limits and outage risk; cost and complexity are higher but it is essential for availability. |
| Pre-transcode all vs on-demand | Popular content gets all renditions up front; the long tail is on-demand: a storage vs compute trade-off. |
| Push vs pull to CDN | Pull (lazy cache) for VOD, pull through an origin shield + pre-warm for live; push only for known hot content. |
C.9 Interview tips
- Design the upload path and the playback path separately: their scale and nature differ.
- Explain adaptive bitrate and segments clearly (manifest + chunks).
- Show the bandwidth math: concurrent x bitrate = Tbps; it proves the importance of the CDN by itself.
- For live, always mention the thundering herd, pre-warming and graceful degradation.
- Explain transcoding as a queue + parallel workers + idempotent jobs.