How HLS Segment Prefetch Works — and Why a Converter Fetches Segments Differently
July 28, 2026 · 6 min read
When you press play on a streaming video and the first frame appears without delay, the player has already downloaded several seconds of video you haven't watched yet. That preloaded buffer is the product of segment prefetch — the mechanism by which HLS players queue downloads ahead of the current playback position. Understanding it explains both why playback is smooth and why a conversion tool that fetches segments sequentially is doing something fundamentally different.
What a segment is, and how many the player needs
An HLS media playlist divides a video into short segments — typically two to six seconds each. The #EXT-X-TARGETDURATIONtag declares the maximum segment length in the playlist. A player does not wait for one segment to finish playing before requesting the next; it maintains a lookahead queue of pending downloads so that network jitter or a slow CDN response doesn't reach the playback clock.
In hls.js, the lookahead size is controlled by the maxBufferLength configuration option (default: 30 seconds) and the maxMaxBufferLengthcap (default: 600 seconds for VOD). The player tracks the “buffer end” — the timestamp of the last buffered frame — and keeps issuing segment download requests until the buffer ahead of the playback position reaches maxBufferLength. On a fast connection, this means three to ten segments are typically in flight or already decoded before the user sees the first one.
How the prefetch queue works
The lifecycle of a segment in a player's buffer goes through four stages:
- Queued: the segment URL is identified from the playlist but not yet downloaded
- Loading: an HTTP request has been issued; the response is in transit
- Appending: the downloaded bytes are being passed to the Media Source Extensions buffer for decoding
- Buffered: the segment is decoded and resident in the SourceBuffer; the player can seek into it instantly
Concurrently loading multiple segments from different quality levels is how adaptive bitrate switching works: the ABR algorithm can switch to a lower-bandwidth variant mid-queue by canceling the next high-quality request and reissuing it against the lower-quality playlist, without stalling the already-loaded buffer.
Playlist polling and the live stream case
For VOD streams, the playlist is static — all segment URLs are known from the first fetch. The player pre-loads the buffer to its configured depth and then idles until playback consumes it.
For live streams, the playlist is dynamic: new segments are appended every few seconds and old ones expire. The player must re-poll the playlist at an interval roughly equal to the target duration (or half that for Low-Latency HLS partial segments) to discover new segment URLs. Prefetch still applies — the player downloads the newest segment as soon as it appears in the refreshed playlist — but the buffer depth is kept short to avoid excessive latency between the encoder and the viewer.
What a converter does instead
A converter like this site's /convert page has a different job than a player. It does not need a live playback clock; it needs all the bytes of all the segments, in order, as a single output file. The fetch pattern is therefore different:
- The full segment list is extracted from the playlist (the highest available quality variant, as explained in how /convert picks quality)
- Segments are fetched concurrently in small batches to maximize throughput without overwhelming the CDN or the browser's connection pool
- Each segment's bytes are passed to ffmpeg.wasm in index order; the muxer assembles them into a single MP4 without re-encoding
For fMP4 streams, the initialization segment (signaled by #EXT-X-MAP) is fetched first and placed at the head of the queue, since the decoder cannot interpret any media segment without it.
Why segments sometimes fail to load
CDN-signed segment URLs typically carry short expiry times — sometimes as little as thirty seconds from when the playlist was fetched. A player that buffers aggressively stays ahead of expiry because it downloads segments immediately. A converter that pauses mid-download (for example, if the tab loses focus and the browser throttles it) can find that a segment URL has expired by the time its turn arrives.
This is why error recovery matters for both players and converters: a 403 or 410 on a segment URL that was valid seconds ago is usually a sign of expiry, not a permission problem, and the correct response is to re-fetch the playlist for a fresh URL rather than to give up.
Try it yourself
The Stream Video Downloader extension detects HLS streams on any page and surfaces them in the popup. From there, /play uses hls.js's full prefetch pipeline for smooth playback, and /convert uses the batch-fetch approach to assemble the complete file. The underlying segment logic — CORS bypass, expiry handling, fMP4 init segment placement — is the same in both.