All posts
HLSStreaming22 Oct 2026 · 3 min read

How a player picks the next rendition

A 1080p segment in the simulator's default ladder is 4.5 MB: 6000 kbps for 6 seconds, which is bitrate x duration / 8. On a link that sustains 3500 kbps, that segment takes about 10.3 seconds to arrive. The viewer spends those 10.3 seconds watching and gains 6 seconds of video, so every segment fetched at that rung costs 4.3 seconds of buffer. Pick wrong a few times and the picture freezes.

Adaptive bitrate (ABR) is the player's answer to that arithmetic. After each segment it decides which rung of the ladder to fetch next.

The ladder and the guess

The default ladder in @beemmeup/media-sim has four rungs: 1080p at 6000 kbps, 720p at 3000, 480p at 1200 and 360p at 800, all cut into 6-second segments. The player never measures the network directly. It times the download of each segment and computes bytes x 8 / milliseconds, which gives kbps for that one sample.

One sample is noisy, so the estimator keeps two exponentially weighted averages, a fast one with a 3-second half-life and a slow one with 8 (both measured in seconds of download time), and reports the lower of the two. A drop in throughput pulls the fast average down almost at once and the player believes it. A rise has to persist before the slow average follows. The asymmetry is the point. Believing a bad sample too early costs some sharpness, and believing a good one too early can cost a stall.

The simplest rule built on that estimate picks the highest rung whose bitrate fits under 0.9 times the estimate. With 3500 kbps measured, 0.9 gives 3150, and the answer is 720p at 3000. That is the throughput rule, and it is the one most people reinvent first.

The buffer is the real signal

Throughput is a guess about the future from the past. The buffer is a fact: the seconds of media already downloaded ahead of the playhead. The session model in the simulator drains it in real time while playing and adds one segment's duration each time a fetch completes. When it reaches zero the player stalls, and the state changes from playing to rebuffering. Playback resumes only when the buffer refills to the startup threshold, 3 seconds by default.

That makes the buffer a better input than the estimate. A full buffer can afford a risky upgrade, and an empty one cannot afford anything. BOLA, the buffer-based algorithm from Spiteri and colleagues, scores each rung as (Vp x (utility + gp) - buffer) / bitrate, where utility is ln(bitrate / lowest bitrate) + 1, so the lowest rung scores 1. As the buffer grows the score shifts toward higher rungs, and the score itself uses no throughput estimate.

No single rule covers everything, so the package default is dynamic. It is dash.js-inspired and simpler than the original: dash.js switches between its throughput rule and BOLA depending on the buffer level, while here three rules each propose a rung (throughput, BOLA, and a guard that forces the lowest rung when the buffer is under half a segment's duration) and the lowest bitrate wins. The Media Network Lab scenario runs plain throughput instead.

Switching too eagerly

A rule that follows the estimate exactly will oscillate when throughput varies. A dip pulls the estimate down and the player drops a rung. The link recovers, the player climbs to 1080p on a stale high estimate, the next samples come in lower, it falls back, and the cycle repeats. Each switch can show as a visible quality change, so the session model counts rung switches per minute alongside rebuffering as two separate numbers.

The port of BOLA here keeps the guard from the BOLA-O variant. If BOLA picks a rung above the current one, the throughput estimate has to support it. If the estimate supports a rung that is above the current one but below BOLA's pick, the player climbs only that far. Otherwise it holds. I haven't measured how much of the climb-fall-climb pattern that removes.

rebuffer ratio = stalled time / (stalled + played time)

That ratio is one of the numbers each cohort of simulated sessions reports, next to startup time percentiles, average bitrate and rung switches per minute. A simulator is useful here because the same ladder and the same link can run under throughput, bola and dynamic, and the only difference between the runs is the rule.