A user drops in one big video file and expects it to play smoothly on a phone in a tunnel and a TV on fibre, anywhere on Earth. This chapter follows that one file through upload, a transcoding pipeline, adaptive streaming, and a global CDN.
A video platform looks like a video problem. It's really a pipeline-and-delivery problem: take one giant file someone uploaded, grind it into dozens of smaller files, and get the right one to the right screen with the least waiting.
Think about what the system actually has to juggle. The original upload might be a 4K, multi-gigabyte file. The viewer might be on a 3G phone, a laptop on hotel wifi, or a smart TV on gigabit fibre — and their connection speed can change mid-video. They could be in Tokyo, São Paulo, or Lagos. And there could be billions of these files, watched billions of times, where a tiny fraction of videos get nearly all the views.
So the design splits cleanly into two halves. The ingest & processing half happens once per upload: store the original, then run a pipeline that produces many versions (renditions) at different qualities, each chopped into bite-sized segments. The delivery half happens billions of times: a player picks the quality that matches the viewer's current bandwidth and streams segments from the nearest edge cache. We'll walk both, then squeeze cost out with deduplication and make the whole thing survive failure.
The first job is dull but critical: receive a possibly-enormous file without melting your servers and without making the user re-upload from scratch if their wifi blips at 98%. The trick is to keep the heavy bytes away from your application servers entirely.
Here's the flow when someone clicks "upload":
cat.mov, 2.1 GB." Your API doesn't accept the file itself — it hands back a presigned URL: a temporary, signed link that lets the client upload directly to blob storage (S3, GCS) for a short window. Your app servers never touch the raw bytes.uploaded → processing go into a metadata DB — small structured rows, totally separate from the giant file.Notice the separation: structured metadata in a database, giant opaque bytes in blob storage, and the slow processing handed off via a queue. That split repeats everywhere in this design.
"Transcoding" means rewriting the video into other formats and qualities. But it's not one step — it's many small steps with dependencies. Some must happen before others; many can happen at the same time. The clean way to model "steps with dependencies" is a directed acyclic graph (DAG): boxes (tasks) with arrows (this must finish before that can start), and no loops.
A typical pipeline for one video:
To run this, you need a few moving parts. A preprocessor turns the upload into a concrete DAG (which renditions, which presets). A DAG scheduler walks the graph and, whenever a task's dependencies are all done, marks it ready. A resource manager assigns ready tasks to free machines. Task workers do the actual encoding. The results land in encoded storage. The scheduler's whole job is one tiny rule, which we'll write in code at the end: a task is ready when all of its dependencies are done.
assemble can't start until the slowest encode is done. That's the parallelism story; we'll push it further in Section 6.Why make all those renditions? Because the viewer's connection isn't fixed. If you only shipped a single 1080p file, a phone on a weak signal would stall constantly, buffering. If you only shipped 240p, the fibre user would get a blurry mess. The answer is to let the player decide, second by second, which quality it can sustain. That's adaptive bitrate streaming (ABR).
It works because of how the video is packaged:
.m3u8 for HLS, or a .mpd for DASH) tells the player which renditions exist, their bitrates, and the URLs of every segment.HLS (Apple) and DASH (the open standard) are the two dominant formats; both express the same idea — segmented renditions plus a manifest. The buffer is the safety margin: the player keeps a few seconds queued ahead, and as long as it refills the buffer faster than playback drains it, you never see the spinner.
Video is mostly bytes leaving your system, not requests hitting your logic. At scale, the bandwidth bill dwarfs everything else, and serving a viewer in Lagos from a data center in Virginia is slow and expensive. So you push the segments out to a content delivery network: a global mesh of edge caches near the viewers (the same idea as Chapter 1's CDN, now carrying the heaviest payload in the whole book).
The cost lever is the hit ratio. A high cache-hit ratio means most bytes leave from cheap edge bandwidth and your origin stays quiet; a low ratio means expensive origin pulls. Popularity drives the ratio, which is exactly what the next widget lets you feel.
Step back and look at where everything lives. There are three very different kinds of data, and each gets a home tuned for its shape.
video_id — so no single database holds every video.On top sits the CDN cache layer for the hottest segments. The whole stack is a tiering story: hot segments at the edge, warm in fast transcoded storage, cold originals archived. The art is moving data down the tiers as it cools and up when something suddenly goes viral, so you pay for speed only where speed is being watched.
A two-hour 4K film could take many hours to encode on one machine. Viewers won't wait, and creators won't either. The way out is parallelism — and there are two levels of it.
Across renditions (Section 2): the 240p, 480p, 720p, and 1080p encodes are independent, so they run on different workers at once. But that only gives you a handful of parallel tasks. The bigger win is parallelism within a single rendition:
So the speed comes from two splits: split across renditions, and split each rendition across GOPs, fanning the work out over a large worker fleet and reassembling at the end.
Transcoding is expensive: it burns CPU/GPU time and produces storage you then pay to keep and serve. The cheapest work is the work you never do — so before kicking off a pipeline, ask: have we seen this exact video before?
The mindset is the opposite of "encode everything to everything, everywhere." It's "compute the minimum, store the minimum, and serve the minimum from the most expensive places."
A transcoding job touches many machines over a long time. Workers crash, encodes fail, storage hiccups. The pipeline has to expect failure and shrug it off.
Here's the whole platform as a checklist — the path one uploaded file takes from raw bytes to a smooth stream on the other side of the world:
Section 2 said the scheduler's whole job is one rule: a task is ready when all of its dependencies are done. Here it is in code. A Stage is just a name plus the names it depends on; ready checks whether every dependency is in the "done" set. The scheduler loops: find every ready, not-yet-done stage, run them (in parallel — they don't depend on each other), add them to done, repeat until the graph is finished. Flip between the two languages; the shape is identical.