Index/Video Streaming

SponsorGitHub
Design14 min

Video Streaming

Upload, transcode and deliver video at twenty-five terabits per second of egress.

Primary concepts and the hard part

Concepts
object storagepresigned/resumable uploadsbatch transcoding pipelineCDN strategyadaptive bitrate streamingchunkingDAG-based job orchestrationmetadata vs bytes separation
The hard part they’re probing
Understanding that video bytes never touch your application servers — not on upload, not on playback. Everything is object storage and CDN. The system you actually build is a metadata service plus a transcoding pipeline.

Requirements

Functionalwhat it must do
Upload a video (large files, resumable over flaky connections)
Transcode to multiple resolutions and bitrates
Stream with adaptive quality based on network conditions
Browse, search, view counts, recommendations (treat as separate services)
Thumbnails, captions/subtitles
Non-functionalhow well it must do it
Playback start time < 2s, minimal rebuffering
Upload must survive network interruption
Extremely read-heavy and bandwidth-dominated
Global audience → latency is physics, must serve from nearby
Out of scopedeliberately left out
DRM specifics
Recommendation ML
Live streaming (mention how it differs)
Scale
Uploads:     500 hours of video/minute
Views:       5B/day → ~60,000 concurrent streams/sec initiated
Bandwidth:   this is the real number —
             5M concurrent viewers × 5 Mbps = 25 Tbps
Storage:     1 hour of source ≈ 5 GB; × ~6 renditions ≈ 15 GB stored
             500 hr/min × 15 GB = huge → petabyte scale, tiered
Conclusion
25 Tbps cannot come from your origin. >95% of delivery must be CDN. Your origin's job is to be the cache-miss backstop, nothing more. This single number justifies the entire architecture.

API / Model

POST
/v1/videos
{title, desc}
201Created{video_id, upload_url}
PUT
<presigned S3 url>
direct, multipart, resumable
200OK
POST
/v1/videos/{id}/complete
{parts[]}
triggers the processing pipeline
202Accepted
GET
/v1/videos/{id}
200OKmetadata + manifest URL
GET
<cdn>/videos/{id}/master.m3u8
200OKABR manifest
GET
<cdn>/videos/{id}/720p/seg_0042.ts
200OKmedia segment
POST
/v1/videos/{id}/view
async view event
202Accepted
primary or partition keysort keyforeign key → referenced columnHover a table or column to trace its keys

Note what is not in the database: the video bytes. Metadata in the database, bytes in object storage, delivery via CDN. That split is the whole design.


High-level architecture

Object storageQueue / streamExternal systemFocusClick a node for details

Metadata and bytes take different routes. The Upload Service handles only metadata and upload URLs, while the video itself goes from the creator to object storage, through a transcoding DAG, and out to viewers from CDN edges.

  1. The creator posts the video's metadata, and nothing else, to the Upload Service.
  2. The Upload Service returns a presigned multipart URL.
  3. The creator uploads the file directly to object storage under raw/, in parts that can each be retried and resumed.
  4. The completed upload emits an event to Kafka video.uploaded, and the Orchestrator runs the transcoding pipeline as a job DAG state machine:
    1. Inspect the codec, duration and tracks.
    2. Split the source into chunks of about 10 seconds on keyframe boundaries.
    3. Transcode each chunk into each rendition, from 240p to 4K in H.264 and AV1, on spot instances.
    4. Run the side jobs: thumbnails, audio, captions and moderation.
    5. Package HLS / DASH segments and a master manifest.
  5. The packaged output is written to object storage under processed/, and the video's status becomes READY.

Playback runs the other way, and it doesn't touch the app servers either. The player requests segments from a CDN edge such as London, Tokyo or São Paulo, and the edges serve over 95% of all bytes. On a miss, an edge pulls the segment from the Origin, which exists only to serve those misses from processed/. The player measures throughput and buffer depth to choose the bitrate of each next segment, and sends view events asynchronously to Kafka and Flink for view counts, watch time and QoE.


Trade-offs and deep dives

01
Presigned URLs, and why they matter

If uploads route through your application servers, those servers become a bandwidth bottleneck and you pay for the traffic twice. A presigned URL is a time-limited, permission-scoped credential that lets the client write directly to object storage. Your service authorizes, then gets out of the way. Same idea in reverse for playback: never proxy bytes.

02
Multipart upload

A 5 GB upload over a mobile connection will fail. Multipart splits it into parts uploaded independently; a failed part is retried alone rather than restarting from zero. The client also gets parallelism. This is table stakes for any large-file system, not a video-specific trick.

03
Why chunk before transcoding

Transcoding a 2-hour film serially takes hours. Splitting into ~10-second chunks lets 1,000 workers transcode in parallel, cutting wall-clock time to minutes. It also makes failure cheap: a crashed worker loses 10 seconds of work, not the whole job. This is MapReduce applied to video, and the parallelism argument is the main insight the interviewer wants.

Chunks must split on keyframe boundaries, or the pieces won't decode independently and won't reassemble cleanly. Good detail to drop.

04
Spot instances are the right compute

Transcoding is batch, idempotent, retryable, and not latency-sensitive. That profile is exactly what preemptible/spot capacity is for, at a large discount. Saying this shows cost awareness, which senior interviews do reward.

05
Adaptive bitrate (ABR), explained properly

The video is encoded at several quality levels, each cut into aligned segments. A manifest lists them. The player measures throughput and buffer depth and picks the next segment's quality. Because segments are aligned and independently decodable, it can switch mid-playback without interrupting.

The asymmetry worth mentioning: players step down aggressively and up conservatively, because a rebuffer is far more damaging to perceived quality than a few seconds of lower resolution. That's a product-informed engineering decision.

06
HLS vs DASH

HLS is Apple's, universally supported, historically .ts segments. DASH is the open standard, codec-agnostic. Real services ship both, or use CMAF so one set of segments serves both. Know the names; don't spend time here.

07
CDN strategy

Push popular content proactively to edges before demand (a new season release is predictable); pull for the long tail. Netflix goes further with Open Connect — appliances placed inside ISP networks, so bytes never cross the public internet backbone. Mention it as the logical endpoint of "put the data near the user."

08
Storage tiering

View distribution is extreme power-law: a tiny fraction of videos get almost all views. Keep hot content in standard storage and on CDN, move cold content to infrequent-access or archive tiers automatically via lifecycle policies. Also consider storing fewer renditions for cold content — you can re-transcode on demand if a five-year-old video suddenly trends.

09
View counts are not a database increment

5B/day of UPDATE videos SET views = views + 1 would melt any database, and every view is a hot row on a popular video. Instead: fire an event to Kafka, aggregate in a stream processor over tumbling windows, and write periodic aggregates. The displayed count is approximate and delayed by seconds. That's fine, and saying it's fine (rather than trying to make it exact) is the correct answer.

10
Live streaming differs

Same chunking and ABR, but transcoding must happen in real time with a latency budget of seconds, there's no batch parallelism (chunks arrive as they're recorded), and you need low-latency protocols (LL-HLS, WebRTC) plus origin-shield layers to protect against thundering herds when a stream starts.


Possible follow-up questions

6 questions·try answering before you reveal