System designDeep dive9 min
Video streaming: transcoding & adaptive bitrate
A video service never sends "the video". It sends a stream of few-second segments, each available at several qualities, and the player picks the next one based on how fast the last ones arrived. Transcoding builds those versions, playlists describe them, a CDN delivers them, and the player's choices decide whether anyone sees a spinner.
1 · The problem
One video file is too big for some viewers and too poor for others
An online fitness platform has 8,000 on-demand classes of about 45 minutes each, plus live classes that draw 20,000 viewers at once. The simplest design stores each class as one 1080p file at 6,000 kbit/s and lets the app download it: 6,000 kbit/s × 2,700 s ÷ 8 ≈ 2 GB per class.
That fails three kinds of viewers at once:
- On a train, with 2 Mbit/s, the video downloads three times slower than it plays and stalls every few seconds.
- On a phone screen, 1080p wastes data on pixels nobody sees.
- On fast Wi-Fi, the viewer gets one fixed quality even when a better one would fit.
And a viewer who jumps to minute 30 must wait for the download to get there.
Adaptive bitrate streaming solves all of it. Each class is encoded at several qualities (a ladder of renditions), each rendition is cut into segments a few seconds long, and a playlist lists them all. The player downloads one segment at a time and picks the rendition for the next segment from what it has just measured. Everything is plain HTTP files, so any CDN can serve them:
Heavy work happens once, at upload. Playback is just HTTP requests for small files, which the CDN serves from the edge (Content delivery networks (CDNs)).
2 · Transcoding
Transcoding turns one upload into a ladder of renditions
Compressed video stores most frames as differences from other frames. An I-frame (keyframe) can be decoded on its own; P- and B-frames describe only what changed. A group of pictures (GOP) starts at a keyframe, and playback can only start, or switch rendition, at one. HLS streams therefore use a keyframe every 2 seconds, segments that each start with one, and keyframes aligned across all renditions, so the player can switch at any segment boundary without a glitch.
The ladder itself trades bits for pixels. Apple's recommended H.264 ladder for 16:9 video:
Nine renditions from 145 to 7,800 kbit/s. Each rung is 1.3 to 2.5 times the one below, so a player always has a nearby step to move to. An HLS stream for cellular also needs a variant of at most 192 kbit/s.
Every rendition is stored, so the ladder multiplies storage: the nine rungs add up to 25,640 kbit/s, so one 45-minute class takes 25,640 × 2,700 ÷ 8 ≈ 8.7 GB, and 8,000 classes about 69 TB. Newer codecs reduce that: Apple's HEVC ladder reaches 1080p at 5,800 kbit/s instead of 7,800, about a quarter less, but older devices can't decode it, so services keep H.264 alongside newer codecs.
Transcoding is the expensive part, done once per upload. Because each segment begins with a keyframe and can be decoded on its own, a long video can be split into chunks that are encoded on many machines in parallel and joined afterward. Many services also tune the ladder per title instead of using one fixed ladder, because simple footage reaches the same quality with far fewer bits: a static instructor against a plain wall needs much less than fast cuts in a dance class.
3 · Packaging
Playlists describe the segments; the player fetches them one by one
The two dominant formats work the same way. Apple's HLS (HTTP Live Streaming) uses a multivariant playlist listing the renditions, and one media playlist per rendition listing its segments. MPEG-DASH, an international standard, describes the same structure in an XML manifest (an MPD). The multivariant playlist for one class looks like this:
class-1042/master.m3u8 (shortened)
#EXTM3U
#EXT-X-STREAM-INF:BANDWIDTH=730000,RESOLUTION=768x432
432p-730/index.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=2000000,RESOLUTION=960x540
540p-2000/index.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=6000000,RESOLUTION=1920x1080
1080p-6000/index.m3u8Each media playlist then lists that rendition's segments, with a target duration that every segment must stay within. With Apple's recommended 6-second segments, a 45-minute class has 2,700 ÷ 6 = 450 segments per rendition, or 4,050 files across nine renditions, plus the playlists.
Historically HLS used one container format and DASH another, so services stored everything twice. CMAF (Common Media Application Format), standardized in 2018, defines fragmented MP4 segments that both an HLS playlist and a DASH manifest can point to, so one set of segments serves both kinds of player.
4 · Adaptive bitrate
The player chooses every segment, balancing quality against stalls
The server doesn't decide quality; the player does, one segment at a time. A viewer starts a class on a train:
A 6-second segment that takes 9 seconds to arrive drains the buffer by 3 seconds. Keep doing that and playback stalls, so the player steps down before the buffer runs out.
Players combine two signals:
- Throughput-based rules estimate bandwidth from recent downloads and pick the highest rendition that fits. They react fast but are fooled by short bursts.
- Buffer-based rules, such as BOLA, pick quality from how many seconds are already buffered: a full buffer can afford a big segment, a nearly empty one can't. They are stable but slow to start.
The dash.js reference player switches between the two depending on the buffer level, and adds further rules, for example one that looks at the history of recent switches, because quality that flickers up and down is noticeable too.
Startup is its own decision. A high first rendition means a slow first frame; a low one means a blurry first few seconds. The usual first variant is 2,000 kbit/s on Wi-Fi and 730 kbit/s on cellular. Every one of these rules serves the goal adaptive streaming was designed for: the best quality that can still arrive in time, without stalling.
5 · Delivery
Segments are ideal CDN content, and the CDN carries almost all the traffic
A popular live class with 20,000 viewers at an average of 3 Mbit/s needs 20,000 × 3 = 60 Gbit/s of delivery. No origin should see that. Segments are perfect cache content: immutable files with unique names, requested by thousands of viewers within seconds of each other.
File | How long to cache | Why |
|---|---|---|
On-demand segments | Very long (days or more) | A segment never changes; a new encode gets new names |
On-demand playlists | Long | They change only when the class is re-encoded |
Live media playlists | Short: seconds at most | A new segment is added every few seconds |
Live segments | Long, once published | Immutable after they appear in the playlist |
Two CDN features matter most for live video. Request collapsing consolidates simultaneous requests for the same brand-new segment into as few as one request to the origin. An origin shield, an extra cache layer in front of the origin, means every edge and regional cache fetches from it instead of from the origin, so each segment leaves the origin about once. Storage and lifecycle rules for the files themselves are in Object & blob storage.
6 · Live
In live streaming, segment length sets the delay
A live class adds a clock. The camera feed goes to a live encoder that produces the ladder in real time, the packager publishes each segment as soon as it is complete, and players follow the growing playlist. HLS players don't start closer than three target durations to the end of a live playlist, so the delay behind real time is at least three segments plus encoding and delivery:
Minimum distance from the live edge at start: 3 × target duration. Encoding, packaging and the CDN add more. Shorter segments cut delay but mean more files and more requests for every viewer.
For a fitness class, an 18-second delay is awkward: the instructor answers a chat question long after it was asked. Low-Latency HLS keeps 6-second segments for normal playback but also publishes partial segments, for example 200 ms each, as soon as they are encoded. Players hold an open request for the next playlist update instead of polling (blocking playlist reload), and fetch hinted parts before they finish. The resulting latency is in the range of standard television broadcasts.
The cost is load: a player that fetched one segment every 6 seconds now fetches a part every 200 ms, and every edge server must handle requests that wait for media that doesn't exist yet. For a broadcast to thousands, LL-HLS is a good trade. For a truly interactive session, such as a personal trainer correcting your form, segment-based streaming is the wrong tool, and real-time protocols built for calls are used instead.
Check yourself
4 questions
Takeaways
Remember this
- Adaptive bitrate streaming encodes each video as a ladder of renditions cut into short segments, and the player picks a rendition per segment.
- Keyframes every couple of seconds, aligned across renditions, make segments independently decodable, switchable and encodable in parallel.
- HLS and DASH describe the same idea with different playlists; CMAF lets one set of fragmented MP4 segments serve both.
- Players balance throughput estimates and buffer level to pick the best quality that can still arrive without stalling.
- Live delay is at least three segment durations; Low-Latency HLS uses partial segments and blocking playlist reloads to reach broadcast-like delay.