▶ Video & streaming

Adaptive streaming: how HLS and MPEG-DASH work

Segments, playlists and manifests: how HLS and DASH adapt video quality to the connection, what the player does, and which mistakes cause buffering.

When an online video starts blurry and turns sharp after a few seconds, or loses quality for a moment while you are on a train, you are watching adaptive streaming at work. It is the technique used by almost every modern video platform, and it rests on a simple idea: instead of sending a single file, the server offers the same content in several qualities, cut into small pieces, and the player chooses, each time, the piece best suited to the available connection.

From progressive download to adaptive streaming

The simplest way to put a video online is to upload an MP4 file and let the browser download it while playing. This is called progressive download and it works well for short videos. It has two limits, though:

  • there is only one quality: if the connection is slow, the video stalls;
  • jumping to the middle requires the server to support byte-range requests, and the player cannot adapt.

Adaptive streaming solves both problems by working on top of ordinary HTTP. No special servers are needed: segments are static files that any web server or CDN can deliver.

The basic elements

  1. Renditions: the same video encoded in several versions, for example 1080p at 5 Mbps, 720p at 3 Mbps, 480p at 1.2 Mbps and 360p at 600 kbps.
  2. Segments: each version is split into pieces a few seconds long, typically between 2 and 6.
  3. Manifest: a text file listing the available versions and where the segments are.
  4. Player: the software that reads the manifest, measures bandwidth and decides which segment to download.

Since the player can only switch quality between one segment and the next, every segment must start with a keyframe and the cuts must line up across all versions.

HLS

HTTP Live Streaming was introduced by Apple and is today the most compatible format, supported natively by iOS, macOS and many TVs and, through JavaScript libraries, by every browser. It uses text playlists with the .m3u8 extension:

  • a master playlist lists the variants with resolution, bitrate and codecs;
  • a playlist for each variant lists its segments in order.

An excerpt of a master playlist looks like this:

#EXTM3U
#EXT-X-STREAM-INF:BANDWIDTH=5200000,RESOLUTION=1920x1080
1080p/index.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=3100000,RESOLUTION=1280x720
720p/index.m3u8

Originally segments were MPEG-TS; today HLS also supports fragmented MP4 segments.

MPEG-DASH

Dynamic Adaptive Streaming over HTTP is a vendor-neutral international standard. Its manifest, called the MPD, is an XML file describing periods, adaptation sets and representations. DASH is codec-agnostic and typically works with fragmented MP4.

It is not supported natively in browsers, but it works through Media Source Extensions with libraries such as dash.js or Shaka Player. Nor is it supported natively in iOS Safari, which explains why many services offer both formats.

CMAF: one set of segments

Encoding and storing the same content twice, once for HLS and once for DASH, doubles the storage needed. The CMAF format defines fragmented MP4 segments usable by both protocols: you produce two different manifests pointing to the same files. It is the most efficient solution for anyone who must serve every device.

How the player decides

The adaptation algorithm, called ABR (Adaptive Bitrate), relies on two main pieces of information:

  • measured bandwidth: how quickly the last segments were downloaded;
  • buffer level: how many seconds of video are already ready.

With a full buffer the player can afford to try a higher quality; with an almost empty buffer it drops quality to avoid stalling. A good algorithm also avoids constantly flipping between two levels, which viewers find annoying.

Typical problems and causes

Symptom Probable cause
visible jump when quality changes keyframes not aligned across renditions
frequent buffering on good connections segments too long or steps too large between renditions
slow start heavy first segment or no light rendition
audio disappearing on quality switch different audio tracks across variants
works on a computer but not on an iPhone DASH manifest with no HLS alternative

Creating an HLS stream with FFmpeg

For a single quality level the basic command is:

ffmpeg -i master.mp4 -c:v libx264 -crf 21 -g 50 -sc_threshold 0 -c:a aac -b:a 128k -hls_time 4 -hls_playlist_type vod -hls_segment_filename "seg_%03d.ts" index.m3u8

For several levels you use multiple mapping options and the -var_stream_map parameter, or dedicated packaging tools that start from MP4 files already encoded.

In short

  • Adaptive streaming offers several qualities in segments and lets the player choose.
  • HLS uses .m3u8 playlists and is the most compatible, especially on Apple devices.
  • DASH uses an XML manifest and is an open, codec-agnostic standard.
  • CMAF lets you serve HLS and DASH from the same segments.
  • Aligned keyframes and a well-built quality ladder prevent jumps and buffering.
MM
Marco Marzaduri

Marco Marzaduri works in cybersecurity, digital forensics and web development. A computer science graduate, he combines rigorous technical analysis with attention to the people who use technology.

Read also