HLS Manifest Internals: How Players Read Master and Media Playlists
A line-by-line tour of HLS .m3u8 manifests — master vs media playlists, EXT-X-MAP, byte ranges, fMP4/CMAF vs TS segments, and encryption.
An HLS stream is, at heart, a set of plain-text .m3u8 files that tell a player
what to download and in what order. Everything else — adaptive switching,
encryption, subtitles, trick-play — is described in those text files. If you can
read a manifest fluently, you can debug most HLS problems before you ever open a
packet capture. This post walks through the two playlist tiers and the tags that
matter, using real manifest snippets you can paste into the
manifest inspector on the player page to see parsed live.
Two tiers: master playlist and media playlist
HLS uses a two-level structure:
- Master playlist (also called the multivariant playlist) — the entry point. It lists the available variants (renditions) and points at a media playlist for each. It contains no segment URLs.
- Media playlist — one per rendition. It enumerates the actual segments (or their byte ranges) in playback order, plus timing and encryption metadata.
A player loads the master first, picks a starting variant based on its bandwidth estimate, then fetches that variant's media playlist and begins downloading segments. When the network changes, it switches by loading a different media playlist from the same master — same timeline, different quality.
Reading a master playlist
#EXTM3U
#EXT-X-VERSION:7
#EXT-X-INDEPENDENT-SEGMENTS
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud-en",NAME="English",LANGUAGE="en",DEFAULT=YES,AUTOSELECT=YES,URI="audio/en/index.m3u8"
#EXT-X-STREAM-INF:BANDWIDTH=5400000,AVERAGE-BANDWIDTH=4900000,RESOLUTION=1920x1080,FRAME-RATE=29.970,CODECS="avc1.640028,mp4a.40.2",AUDIO="aud-en"
v1080/index.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=2600000,AVERAGE-BANDWIDTH=2350000,RESOLUTION=1280x720,FRAME-RATE=29.970,CODECS="avc1.64001f,mp4a.40.2",AUDIO="aud-en"
v720/index.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=900000,AVERAGE-BANDWIDTH=820000,RESOLUTION=640x360,FRAME-RATE=29.970,CODECS="avc1.64001e,mp4a.40.2",AUDIO="aud-en"
v360/index.m3u8
What the player learns here:
#EXTM3Umust be the first line of every playlist — it is the file's magic number.#EXT-X-VERSION:7declares the protocol version, which gates which tags and segment formats are legal (version 7 is required for fMP4 segments).#EXT-X-INDEPENDENT-SEGMENTSpromises every segment starts with a keyframe, so the player can switch variants at any segment boundary without artifacts.- Each
#EXT-X-STREAM-INFline plus the URI on the following line is one variant.BANDWIDTHis the peak bitrate (the value ABR logic must fit under),AVERAGE-BANDWIDTHthe typical one, andCODECSlets the player confirm it can decode the stream before downloading a byte. AUDIO="aud-en"ties the video variants to an#EXT-X-MEDIAaudio group, meaning audio is delivered as a separate track ("demuxed") rather than muxed into the video segments.
Reading a media playlist
Pick the 720p variant above and its media playlist might look like this:
#EXTM3U
#EXT-X-VERSION:7
#EXT-X-TARGETDURATION:6
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-MAP:URI="v720/init.mp4"
#EXTINF:6.006,
v720/seg-00000.m4s
#EXTINF:6.006,
v720/seg-00001.m4s
#EXTINF:6.006,
v720/seg-00002.m4s
#EXTINF:4.004,
v720/seg-00003.m4s
#EXT-X-ENDLIST
Line by line:
#EXT-X-TARGETDURATION:6is the maximum segment duration (rounded up). A player uses it to size its reload interval for live streams.#EXT-X-MEDIA-SEQUENCE:0is the sequence number of the first segment listed. For VOD it stays 0; for live it increments as old segments roll off the window.#EXT-X-PLAYLIST-TYPE:VODtells the player the playlist will never change, so it can build the full timeline immediately and enable seeking.#EXT-X-MAPpoints at the initialization segment (more below).- Each
#EXTINFgives the exact duration of the segment named on the next line. These are the numbers the player sums to build the seek bar — they must be accurate to the millisecond. #EXT-X-ENDLISTmarks the playlist as complete. Its absence is how a player knows a stream is live and must keep reloading.
fMP4/CMAF vs TS segments
Two segment containers dominate HLS:
- MPEG-2 Transport Stream (
.ts) — the original HLS container. Each segment is self-contained and carries its own decoding setup, so no init segment is needed. Still common in legacy and some live workflows. - Fragmented MP4 (
.m4s/.mp4) — the modern default, and the basis of CMAF. The codec configuration (themoovbox) lives once in a separate initialization segment, and each media segment is a lightweightmoof+mdatfragment. CMAF fMP4 is what lets you package once and serve both HLS and DASH from the same media files.
The presence of #EXT-X-MAP is the tell-tale sign of fMP4: TS playlists do not
use it because there is nothing to initialize separately.
EXT-X-MAP and the init segment
#EXT-X-MAP:URI="v720/init.mp4"
The init segment carries the ftyp and moov boxes — codec, resolution, track
layout — that every following fragment assumes. A player must fetch it before
any media segment of that rendition. When the player switches variants, it loads
the new variant's init segment first, which is why independent segments and a
fresh EXT-X-MAP matter for clean switching.
Byte-range addressing
Instead of one file per segment, you can store a whole rendition as a single file and address segments by byte range. This cuts the number of objects on your CDN dramatically:
#EXT-X-MAP:URI="v720/stream.mp4",BYTERANGE="1180@0"
#EXTINF:6.006,
#EXT-X-BYTERANGE:742108@1180
v720/stream.mp4
#EXTINF:6.006,
#EXT-X-BYTERANGE:698242@743288
v720/stream.mp4
#EXT-X-BYTERANGE:n@o means "this segment is n bytes starting at offset o."
When the offset is contiguous with the previous range, it can be omitted. The
player issues HTTP Range requests instead of fetching whole files.
Encryption lines
When content is encrypted, an #EXT-X-KEY tag tells the player how to decrypt
the segments that follow it:
#EXT-X-KEY:METHOD=AES-128,URI="https://drm.example.com/key/abc",IV=0x9c7db8778570d05c3177c0fab2eb8f8f
#EXTINF:6.006,
v720/seg-00000.m4s
For Apple FairPlay over CMAF you will instead see SAMPLE-AES with a key-format identifier:
#EXT-X-KEY:METHOD=SAMPLE-AES,URI="skd://drm.example.com/asset-42",KEYFORMAT="com.apple.streamingkeydelivery",KEYFORMATVERSIONS="1"
METHOD=AES-128 encrypts whole segments; SAMPLE-AES (and cbcs) encrypts only
the media samples, which is what modern multi-DRM CMAF uses. A METHOD=NONE line
turns encryption back off partway through a playlist — useful for clear ad breaks
inside encrypted content.
Putting it together
The whole HLS playback flow is encoded in these files: master → pick variant → media playlist → init segment → media segments, with encryption and timing described inline. The fastest way to internalize the structure is to paste a real manifest into the player's manifest inspector, then read it against this guide. For a tag-by-tag reference, continue with Key HLS Tags; for how the player chooses which variant to load, see Adaptive Bitrate (ABR) at the Player Level.
Try it yourself
Reading about streaming is one thing — pull apart a real manifest, play a stream, and measure its QoE with the toolkit.
Browse all tools