SD System Design In Depth
0%
lesson system-designmediaplayback

Adaptive bitrate and the CDN decider

Module 5.2: Media Processing & CDN Distribution Updated Sep 18, 2026
📄 Cheat Sheet
HLS Adaptive Bitrate Streaming - System Design
Piyush Garg HLS Adaptive Bitrate Streaming - System Design Watch on YouTube ↗

A thorough system-design walkthrough of HLS master and media playlists, renditions and how the player switches bitrates.

0:00 / --:--

Adaptive bitrate and CDN selection

Adaptive bitrate streaming lets a player choose among encoded representations as network and playback conditions change. A smaller next segment can help avoid a stall. It cannot undo time already spent waiting for an earlier segment.

There are three related decisions: which rendition the viewer requests now, which renditions the service prepares, and where their bytes should be cached. We'll separate them, then use real segment sizes in a small simulation to see why reacting to the last transfer can be too late.

The player chooses from a prepared offer

The transcoding pipeline creates the representations. In HLS, a master playlist identifies available variants, and their media playlists identify ordered segments. The player chooses files that already exist; it does not ask the encoder to change an in-flight file's quality.

The ladder is the set of offered resolutions and bitrates. It must fit supported devices and codecs, with corresponding content aligned in time. A higher resolution is not automatically useful on a small display, and bitrate alone does not compare visual quality across different codecs or scenes.

Three quantities guide playback:

QuantityMeaning
Media bitrateEncoded bits per second of media
Download throughputReceived bits per second of transfer time
BufferSeconds of media available ahead of playback
A rendition can have a modest average bitrate and still contain a large segment. The player needs enough buffer to survive that segment's actual transfer. A path's last observed throughput is evidence about the next request, not knowledge of its future capacity.

Production selectors may combine throughput estimates, buffer levels, device limits and switching history. For example, dash.js documents distinct throughput, buffer, dropped-frame and request-abandonment rules. Our rule below intentionally uses only the previous completed transfer so its delay is easy to see.

A three-request experiment

The lab and media helper generate the same HLS files as the preceding lesson. Use its pinned Python environment and FFmpeg 8.1.2. File sizes are measured; download times and stalls are calculated under a chosen capacity schedule, with no real congested network.

Both policies start with one second buffered. Fixed-high always requests the larger rendition. Adaptive requests high if its previous observed capacity is at least 200,000 bits/s, otherwise low. Its initial observation is 400,000 bits/s; neither policy can see the next capacity before choosing.

/tmp/fanout-m24/bin/python m24-lab.py abr
#EXTM3U
#EXT-X-VERSION:6
#EXT-X-STREAM-INF:BANDWIDTH=55648,RESOLUTION=160x90
low/index.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=188000,RESOLUTION=320x180
high/index.m3u8
chosen capacity bits/s=[400000, 40000, 400000] initial buffer=1.000s
fixed-high download/buffer/stall: high:0.447s/1.553s/0.000s, high:4.474s/1.000s/2.922s, high:0.470s/1.530s/0.000s total_stall=2.922s
adaptive download/buffer/stall: high:0.447s/1.553s/0.000s, high:4.474s/1.000s/2.922s, low:0.139s/1.861s/0.000s total_stall=2.922s
simulated origin observations: A age=4 refused; B age=1 selected=B

Each generated segment lasts one second. The largest high segment contains 23,500 bytes, giving the advertised peak of 188,000 bits/s. This calculation is valid for the fixture's equal one-second segments and one-second target duration; it is not a promise of network capacity.

For a segment of S bytes and a path capacity of C bits/s, ideal transfer time is 8*S/C. If the player starts that request with B seconds buffered, the stall is max(0, transfer time − B). Afterward, the buffer is max(0, B − transfer time) + segment duration.

The first high segment is 22,372 bytes. At 400,000 bits/s it takes 0.44744 seconds, leaving 1.55256 seconds buffered after adding the new second of media. The next high segment has the same size, but capacity drops to 40,000 bits/s.

That transfer takes 4.4744 seconds: 2.92184 seconds longer than the available buffer. Both policies stall by the displayed 2.922 seconds. Adaptive learns about the drop only after that transfer completes, then switches low just as capacity recovers.

flowchart TD accTitle: A playback decision arrives late accDescr: The first fast transfer makes the selector choose high again. The second transfer is slow and stalls. Only afterward does the selector choose low, when the path has already recovered. A["Request 1: high
Fast transfer"] --> B["Request 2: high
Capacity drops; stall"] B --> C["Observe slow transfer"] C --> D["Request 3: low
Capacity has recovered"]

The last low segment's 6,956 bytes arrive quickly, but cannot recover time already stalled. On this schedule the adaptive rule does not improve the total-stall metric. The experiment omits round trips, protocol overhead, contention and decoder startup; it tests the consequence of delayed information, not a production ABR algorithm.

A buffer-aware rule could become more cautious as playable time runs low. More startup buffer can absorb longer transfers, but makes the viewer wait before playback. Evaluate startup delay, rebuffering and quality changes together rather than choosing a universal threshold from this three-second clip.

Which renditions should exist?

The player cannot select a version the service has not prepared. Encoding every upload into every possible format spends compute and storage even on videos that nobody watches. Deferring everything makes the first viewer wait for processing.

Preparation policyUseful whenCost or delay
Eager ladderThe expected audience needs several representations immediatelyWork is paid even if some renditions are never watched
Baseline, then more on demandA complete initial offer is enough for less-watched uploadsNew representations take time to become available
Prepare for predicted demandA release or rising audience provides advance noticeA wrong prediction wastes work or misses demand
In our proposed service, an on-demand miss queues a deduplicated job keyed by source and encoding recipe. The existing baseline remains playable while the new rendition is prepared. Add it to the advertised offer only after validation; a viewer request should not receive a playlist pointing at unfinished work.

This is independent of the request-by-request ABR rule. It changes what the rule will be able to choose later.

The CDN decider controls preparation and placement

“CDN decider” is a name for our proposed policy service, not a standard CDN API. It combines recent view velocity, viewer geography, channel audience, content type, shares, trending signals and known release spikes to decide where extra work is justified.

Its actions may include generating another rendition, warming selected ready objects in a region, retaining an origin copy or retiring an unused cache placement. The available controls depend on the delivery provider; ordinary CDN eviction is often automatic, not a per-object command the application owns.

Warming transfers bytes before a viewer requests them. It can reduce a first miss while consuming fill bandwidth and cache space. Moving a source to colder storage is a separate retention choice and may increase retrieval delay. Keep encoding, cache placement and storage tiering as separate actions with explicit costs.

Netflix's Open Connect fill documentation gives a concrete placement example: appliances hold portions of the catalog and primarily receive updates in off-peak windows. Popularity changes and new or re-encoded titles affect those updates. This is Netflix's documented delivery model, not a guarantee offered by every CDN.

Our controller can consume product events such as video.published, video.viewed and video.trending, and emit a request to prepare or cache a version. Treat variant.generated as evidence of a ready output, not merely a queued task. Repeated events should converge on one intended action.

Use bounded budgets and a quiet period before reversing a placement decision. Otherwise noisy demand can repeatedly trigger expensive encodes or cache fills. Measure useful cache hits and avoided viewer delay against the work spent; traffic volume alone does not show that warming paid off.

Choosing a delivery location

After selecting a ready object, a player or delivery service may still choose which CDN or origin serves it. This is a separate decision from choosing the rendition. Old latency samples can mislead either choice.

The final line of the replay compares two simulated origins transferring the same 1,000 bytes. A took 0.1 seconds at tick 1; B took 0.2 seconds at tick 4. At tick 5, a maximum age of two ticks excludes the faster but stale A sample, so B wins.

That toy rule only compares fresh equal-size observations. A real selector also needs failure rates, comparable workloads and a fallback when no candidate is eligible. Neither the example nor the label “CDN” establishes a global performance result.

The next access lesson checks permission on every playlist and segment the player chooses.


Adaptive Bitrate (ABR) Protocols: HLS & DASH

Adaptive Bitrate streaming dynamically adjusts video quality in real time based on the viewer's current network bandwidth and device capabilities. If a viewer walks into an elevator, the stream drops from 1080p to 480p, usually before the buffer runs dry, so playback continues at lower quality instead of stalling.

Manifest and Segment Hierarchy

The video player first downloads a Master Playlist containing a list of available quality stream variants and their respective bandwidths:

#EXTM3U
#EXT-X-STREAM-INF:BANDWIDTH=4500000,RESOLUTION=1920x1080
1080p/index.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=2200000,RESOLUTION=1280x720
720p/index.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=800000,RESOLUTION=854x480
480p/index.m3u8

The player continuously measures download speeds for each 6-second segment. It keeps a smoothed throughput estimate, watches buffer level, and picks a lower rendition (with a safety margin) before downloads fall behind playback; waiting until a download takes longer than the segment duration means the buffer is already draining.

Worked example: a throughput-based decision

Ladder: 4.5, 2.2 and 0.8 Mbit/s; segments are 6 seconds.

  • The player's smoothed throughput estimate is 3.0 Mbit/s. With a 0.8 safety factor it may choose up to 2.4 Mbit/s, so it picks 720p (2.2 Mbit/s).
  • A 720p segment is 2.2 x 6 = 13.2 Mbit. At 3.0 Mbit/s it downloads in 4.4 seconds, so the buffer grows by 1.6 seconds per segment.
  • The network drops to 1.0 Mbit/s. The same segment now takes 13.2 seconds while only 6 seconds of video are added: the buffer shrinks 7.2 seconds per segment. With a 20 second buffer, the player has about 2 segments before stalling, so it must drop to 0.8 Mbit/s promptly (a 480p segment at 1.0 Mbit/s takes 4.8 seconds, and the buffer grows again).

Buffer-based adaptation

The buffer-based approach (Huang et al., SIGCOMM 2014, with Netflix) chooses rate mainly from buffer occupancy: below a reservoir of, say, 10 seconds always pick the lowest rung; above 30 seconds pick the highest; in between map linearly. It uses throughput mainly at startup, and it reduced rebuffering in Netflix's experiments while keeping similar average quality.

Algorithm familySignalStrengthWeakness
Throughput-basedRecent segment download speedsFast startup, simpleNoisy estimates cause oscillation and stalls
Buffer-based (BBA, BOLA)Buffer levelFewer rebuffers, stableSlow to ramp up at startup
Hybrid or model predictiveBoth, plus a short-horizon predictionBest quality versus stall trade-offMore complex to tune

The CDN decider

sequenceDiagram participant P as "Player" participant S as "Steering service" participant A as "CDN A" participant B as "CDN B" P->>S: "start session, region, ISP, device" S-->>P: "ordered CDNs A then B, playback URLs" P->>A: "GET segments" A-->>P: "errors or slow throughput" P->>B: "fail over, GET next segment" P->>S: "QoE beacons, rebuffers, throughput"
  • Inputs: per ISP and region quality data (throughput, rebuffer ratio, error rate), CDN health, capacity and cost commitments.
  • Standards: HLS content steering (EXT-X-CONTENT-STEERING) and DASH-IF content steering let a server update the preferred CDN order during playback.
  • Netflix's steering service chooses Open Connect appliances, often inside the viewer's ISP, and returns ranked URLs so the client can fall back if one server fails.
Check yourself Question 1 of 3
Switch before draining recall
From: Manifest and Segment Hierarchy

A simple player switches down only when a segment download takes longer than the segment's duration. Why does the lesson call this too late?

Adaptive Bitrate: throughput vs buffer-based

Both players fetch the same 120 s of network, one segment at a time, from a 10-rung ladder (235k to 5.8M). The throughput player picks the highest rung under 85% of its recent measured throughput; the BBA-0 player ignores throughput and maps its buffer level (5 s reservoir, 20 s cushion) to a rung. Click or drag on the top chart to redraw the bandwidth.

Key Takeaways

HLS and DASH slice video streams into small, standalone HTTP chunks (typically 2–6 seconds).
The client video player, not the server, dynamically chooses which quality segment to fetch based on buffer health.
HTTP-based chunk streaming enables standard CDN web caches to cache video chunks without specialized streaming servers.
Was this lesson helpful? Spot an issue or have a suggestion? Let us know on GitHub.
Report an issue