Skip to main content

Video Streaming - HLS, DASH and Adaptive Bitrate

TL;DR
  • Streaming video is not a special protocol. The video is encoded at several quality levels (a ladder), cut into short segments, and listed in a manifest. The player fetches them as ordinary HTTP files.
  • The client decides. An ABR (adaptive bitrate) controller in the player watches its buffer and measured download speed and picks the rung for the next segment. The server stays dumb and cacheable.
  • HLS (Apple, RFC 8216, .m3u8) and DASH (ISO/IEC 23009-1, .mpd) differ mainly in manifest format. With CMAF (fragmented MP4) the same segments can serve both.
  • Live adds a latency budget: with classic HLS the player sits about three segments behind the edge. LL-HLS, LL-DASH and chunked CMAF cut that to a few seconds, WebRTC goes lower, and each step costs complexity.
  • Rebuffering hurts viewers more than low quality, so real players are conservative, not "highest rate that fits".

Mental model​

ANALOGYEveryday

A tap feeding a bucket. You drink from the bucket (the playback buffer); the tap (the network) refills it. As long as the bucket has water you never notice the tap's speed. When the tap slows, you do not wait until the bucket is empty: you switch to a smaller glass (a lower quality) so the water lasts. When the tap recovers you go back to the big glass.

Where it breaks: a real tap does not change flow because of what you pour. A player's own requests change what it measures: a segment that downloads in short bursts with idle gaps in between does not reveal the true capacity of the path.

ANALOGYFor engineers

Think of it as a client-driven pull loop with a feedback controller. The player is a consumer reading from a queue (the buffer) that drains at a fixed rate (1 second of video per second). The controller's job is to keep the queue level between a floor and a ceiling by choosing the size of the next fetch, like a client that picks between a thumbnail, a medium and a full-size image URL based on how the last fetch went. Each segment is an immutable object addressed by URL, exactly the thing HTTP caches are good at.

Where it breaks: the controller measures throughput that its own requests influence (TCP slow start after idle, other players on the same link), so the sensor is noisy and partly a function of the actuator. That is why this is still a research area and not a solved problem (Huang et al., IMC 2012).

How it works​

A streaming service runs a pipeline once per video and a control loop once per viewer. The pipeline is encode, segment, describe, publish. The loop is measure, choose, fetch, repeat.

SourceEncodeSegmentOriginCDNPlayermaster fileladder+ manifeststorageedgesABR logicGET segment N at rung kthe player chooses k, not the server
Everything left of the CDN happens once per video. The player at the right pulls: it picks the rung and asks for the next segment, and the servers never decide anything per viewer.

Segments and manifests​

A video is cut into segments of a few seconds each. Each segment is a small standalone file that starts on a keyframe, so the player can begin decoding at any segment boundary. A manifest (also called a playlist) lists the rungs and, for each rung, the segments in order. The player downloads the manifest first, then segments.

Apple's authoring guidance for HLS recommends a 6 second target segment duration (HLS Authoring Specification for Apple Devices). Other services choose differently. The trade-off is mechanical: shorter segments let the player switch rungs sooner and cut live latency, but every segment starts with a keyframe (which compresses poorly), and there are more requests per minute of video.

Here is a real media playlist produced by ffmpeg for a 20 second clip with a 6 second target (see Hands-on for the command):

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:6
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-PLAYLIST-TYPE:VOD
#EXTINF:6.000000,
out0.ts
#EXTINF:6.000000,
out1.ts
#EXTINF:6.000000,
out2.ts
#EXTINF:2.000000,
out3.ts
#EXT-X-ENDLIST

#EXT-X-TARGETDURATION is the upper bound on segment length, each #EXTINF is one segment's real duration, and #EXT-X-ENDLIST says the playlist is complete (a VOD playlist). These tags are defined in RFC 8216. A live playlist has no #EXT-X-ENDLIST, and its #EXT-X-MEDIA-SEQUENCE increases as old segments slide out of the window.

The bitrate ladder​

Each video is encoded at several renditions, together called the ladder. A master playlist points to one media playlist per rung:

#EXTM3U
#EXT-X-STREAM-INF:BANDWIDTH=400000,RESOLUTION=426x240,CODECS="avc1.42c01e,mp4a.40.2"
240p/index.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=1200000,RESOLUTION=854x480,CODECS="avc1.4d401f,mp4a.40.2"
480p/index.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=2500000,RESOLUTION=1280x720,CODECS="avc1.64001f,mp4a.40.2"
720p/index.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=5000000,RESOLUTION=1920x1080,CODECS="avc1.640028,mp4a.40.2"
1080p/index.m3u8

The ladder below is illustrative, not any service's real ladder. Segment size is just bitrate times duration, divided by 8 to get bytes.

RenditionBitrateSegment size (6 s)
240p0.4 Mbps0.3 MB
480p1.2 Mbps0.9 MB
720p2.5 Mbps1.9 MB
1080p5.0 Mbps3.75 MB
2160p (4K)15 Mbps11.25 MB

For a real ladder, Apple's public sample stream (used in Hands-on) is an HLS master playlist whose BANDWIDTH values run from about 0.54 Mbps (480x270) to about 8.2 Mbps (1920x1080, 60 fps).

The player loop​

For every segment the player runs the same loop:

  1. Measure how long the last segment took: throughput = bytes received divided by download time.
  2. Estimate near-future throughput and look at the buffer level.
  3. Pick the rung for the next segment.
  4. Fetch it. If the buffer is above its target, wait before the next request; otherwise fetch immediately.

The player's goal is a buffer that never reaches zero. When the buffer empties, playback freezes: a rebuffer (stall).

Worked example: a throughput drop​

Take the ladder above and 6 second segments. The player is on the 1080p rung (5 Mbps) and the network drops from a healthy 10 Mbps to 3 Mbps.

  • A 1080p segment is 5 Mbps x 6 s = 30 Mb. At 3 Mbps it takes 30 / 3 = 10 s to download, longer than the 6 s it plays. The buffer shrinks by 0.4 seconds for every second that passes (3/5 - 1 = -0.4).
  • The 720p segment is 2.5 Mbps x 6 s = 15 Mb. At 3 Mbps it takes 15 / 3 = 5 s, inside the 6 s playback window. The buffer now grows by 0.2 seconds per second (3/2.5 - 1 = +0.2).

The chart builds on exactly those numbers (illustrative, constructed from the arithmetic above, not a measurement): throughput is 10 Mbps until t = 20 s, then 3 Mbps until t = 90 s. Playback starts with 6 s buffered and the buffer reaches 24 s at t = 20 s. A player that stays on 1080p drains 0.4 s per second and stalls at t = 80 s. An adaptive player switches down at t = 34 s with 18.4 s in the bank and never stalls.

01020300 s20 s40 s60 s80 s100 sbuffer level (s)30 s targetnetwork drops to 3 Mbps (was 10)stall (10 s)34 s: ABR down to 2.5 Mbps90 s: network recoversadaptive playerfixed 5 Mbps rung
Buffer level (seconds of video downloaded but not yet played) for two players on the same network trace. The dashed fixed-rung player hits zero and stalls; the solid ABR player drops one rung and keeps a healthy buffer. Constructed from the worked-example arithmetic, not measured.

Notice how the safe choice is not obvious from the numbers alone. If the player trusted a single 3 Mbps sample and wanted to stay under 80% of it (an illustrative safety factor; players differ), it would aim at 2.4 Mbps and pick the 1.2 Mbps rung, not the 2.5 Mbps one. That extra caution is deliberate and is the subject of the next section.

Try it below: pick a throughput trace, choose an algorithm and step through the segments. The simulator uses its own five-rung ladder (0.4, 0.8, 1.5, 2.5 and 5 Mbps), so its numbers differ slightly from the ladder above. Compare how Naive and Smoothed behave on the Flapping trace, and how Buffer-based handles the Sudden drop.

latest segment outlined in orangesegment downloaded in time, playingstall: buffer hit 0 during the download
Network trace (Mbps per 10 s)
Player

Safety factor scales the smoothed estimate only. Naive and buffer-based ignore it; hybrid uses it below 12 s of buffer.

0246810020406080100120Mbpsseconds0102030020406080100120buffer (s)min target 6 smax 30 s (player stops downloading)
    ○ not startedaverage bitrate - Mbpsquality switches 0rebuffer 0.0 s in 0 stallsstartup not started

    Download time is size divided by throughput, with size = bitrate x 6 s (5 Mbps x 6 s = 3.75 MB). Playback drains 1 s of buffer per second, so a segment that takes longer than the buffer holds causes a stall. Try Flapping with Naive, then Smoothed: the throughput hovers around a rung boundary, and only the smoothed estimate stays put.

    Pick a throughput trace and an algorithm, then press Play or Next segment. The first segment always uses the lowest rung: the player has no measurement yet.
    segment 0 / 20

    Where the chunks come from​

    Segments are plain, immutable HTTP objects, so they are served from a CDN edge close to the viewer instead of from the origin. The mechanics (edge caching, tiering, steering, hit ratio) are owned by the CDN page; the headers that make segments cacheable are on HTTP caching.

    PlayerDNSEdge cacheOrigin1 lookup2 GET segment3 on miss onlymechanics, tiers and hit ratio: see the CDN page
    Boxes only, on purpose: the request path is the CDN page's topic. The player resolves a name, asks the nearest edge for a segment, and only a cache miss reaches the origin.

    Deep dive​

    HLS vs DASH vs CMAF​

    HLS is Apple's protocol, documented in RFC 8216: text playlists (.m3u8), originally with MPEG-2 Transport Stream segments and now commonly fragmented MP4. DASH (Dynamic Adaptive Streaming over HTTP) is the ISO standard ISO/IEC 23009-1: an XML manifest called the MPD (Media Presentation Description), with deployment guidance from the DASH Industry Forum guidelines. Both do the same job: list rungs, list segments, tell the player how to fetch them.

    A minimal DASH manifest for a stream with a 6 second segment duration looks like this:

    <?xml version="1.0" encoding="UTF-8"?>
    <MPD xmlns="urn:mpeg:dash:schema:mpd:2011"
    profiles="urn:mpeg:dash:profile:isoff-live:2011"
    type="static" mediaPresentationDuration="PT20S" minBufferTime="PT6S">
    <Period>
    <AdaptationSet mimeType="video/mp4" segmentAlignment="true" startWithSAP="1">
    <SegmentTemplate timescale="1000" duration="6000" startNumber="1"
    initialization="$RepresentationID$/init.mp4"
    media="$RepresentationID$/seg-$Number$.m4s"/>
    <Representation id="480p" codecs="avc1.4d401f" width="854" height="480" bandwidth="1200000"/>
    <Representation id="720p" codecs="avc1.64001f" width="1280" height="720" bandwidth="2500000"/>
    </AdaptationSet>
    </Period>
    </MPD>

    CMAF (Common Media Application Format, ISO/IEC 23000-19) standardises the segment container: fragmented MP4 (.m4s) that both an HLS and a DASH manifest can reference. The practical win is storage and cache: encode and store one set of segments instead of one per protocol, so the CDN caches each segment once. Content protection can also be shared across DRM systems through Common Encryption (ISO/IEC 23001-7), though each DRM still needs its own license exchange.

    HLSDASH
    SpecRFC 8216, Apple authoring specISO/IEC 23009-1, DASH-IF guidelines
    ManifestText .m3u8 (master plus media playlists)XML .mpd
    SegmentsTS or fragmented MP4 / CMAFFragmented MP4 / CMAF (and others)
    Native supportAll Apple platforms and SafariNeeds a player library (for example dash.js, Shaka) on most platforms
    Low latencyLL-HLS (Apple)LL-DASH (chunked CMAF)

    Throughput-based ABR​

    The simplest controller picks the highest rung whose bitrate is below an estimate of throughput. The estimate comes from per-segment samples, and how it is smoothed matters:

    • EWMA (exponentially weighted moving average): est = a * sample + (1 - a) * est. A larger a reacts faster but follows noise. Some players run a fast and a slow EWMA together and use the lower of the two.
    • Harmonic mean of the last few samples: dominated by the slow ones. For samples of 6, 6 and 1.5 Mbps the harmonic mean is 3 / (1/6 + 1/6 + 1/1.5) = 3 Mbps, while the arithmetic mean is 4.5 Mbps. One bad segment drags the harmonic mean down, which is the cautious bias you want when a stall is expensive.

    The estimate is then multiplied by a safety factor below 1, because the next segment may be slower than the last.

    The weakness is the one from the mental model: the sensor is shaped by the controller. When the buffer is full the player idles between requests, so the next measurement starts from an idle (or slow-start) connection and may under-report. See TCP congestion control for slow start and how BBR or CUBIC fill the pipe, and loss, throughput and BDP for why a single flow's goodput depends on RTT and loss. And when several players share a bottleneck, their measurements interact: this is the "confused, timid and unstable" behaviour documented by Huang et al., IMC 2012.

    Buffer-based ABR and BOLA​

    Instead of estimating throughput, buffer-based ABR uses the buffer level directly as the signal: a nearly empty buffer means pick a low rung, a full buffer means pick a high one, with a function in between. Huang et al. (SIGCOMM 2014) built this on Netflix's service and showed that a buffer-based controller can cut rebuffering compared with the throughput-based default, with careful handling of the startup phase when the buffer is still empty. BOLA (Spiteri, Urgaonkar and Sitaraman, INFOCOM 2016) formalises it as an optimisation: it scores each rung by the utility gained per byte and penalises choices as the buffer drains, and the paper proves it near-optimal for its model. It needs no throughput prediction at all.

    Hybrids​

    Throughput is a good signal at startup (the buffer tells you nothing yet) and buffer is a good signal in steady state. Hybrids combine them. dash.js, the DASH-IF reference player, offers a dynamic strategy that switches between a throughput rule and BOLA depending on buffer state (see the dash.js ABR documentation), and model-predictive control (MPC) approaches optimise a quality-of-experience score over a look-ahead horizon using both signals (Yin et al., SIGCOMM 2015). Learned controllers exist too (for example Pensieve, SIGCOMM 2017), but a production player still needs guardrails around any of them.

    Flapping and hysteresis​

    If the controller switches rung every time the estimate wobbles, the viewer sees quality bouncing between, say, 1080p and 480p, which is more annoying than a steady middle rung. Players damp it with:

    • a smoothed estimate (harmonic mean or slow EWMA, above);
    • asymmetric rules: switch down quickly (a stall is imminent) and up slowly (require a healthy buffer and several good segments);
    • a minimum dwell time at a rung and a cap on how far a single switch can jump.

    Startup time​

    Time to first frame is a sum of sequential requests: the master playlist, a media playlist, the init segment (the codec setup data of a fragmented MP4) and the first media segment, plus DNS and connection setup before the first of them. Two levers matter:

    • Start on a low or middle rung. The first segment at the 0.4 Mbps rung is 0.4 x 6 = 2.4 Mb; at 3 Mbps that is 2.4 / 3 = 0.8 s. A first segment at the 5 Mbps rung is 30 Mb and takes 10 s at 3 Mbps. Then step up as the buffer builds.
    • Start playback with a small buffer, or shorten the first segments, at the cost of a smaller cushion against the first throughput dip.

    Prefetching (warming the next title's manifest and first segment while the user browses) cuts the sequential round trips before play.

    VOD vs live​

    VODLive
    Segment lengthFree to choose; long segments encode more efficientlyShort, because latency scales with it
    LatencyNot applicable (everything exists already)Segment length times buffered segments, plus encode and CDN delay
    PlaylistFixed, ends with #EXT-X-ENDLISTRewritten every segment; a sliding window with a rising media sequence
    CachingManifest and segments cache for a long timeSegments cache long (immutable once published); the manifest must stay fresh or players see a stale edge of the stream

    The segment caching headers are covered in HTTP caching; the "stale manifest" failure mode is why a live manifest needs a short TTL even though its segments do not.

    The live latency stack​

    A live HLS client is told how far back to start. RFC 8216, section 6.3.3 says a client SHOULD NOT start playing less than three target durations from the end of the playlist. With 6 second segments that is 3 x 6 = 18 s of player-side delay before adding encode, packaging and CDN time. With 2 second segments it is 3 x 2 = 6 s. This is the classic "live is 20 to 30 seconds behind" effect.

    To go below that, the stream is published in pieces smaller than a segment:

    • LL-HLS (Apple's low-latency HLS documentation) publishes partial segments (#EXT-X-PART), lets a client block a playlist reload until new data exists, and announces the next part with a preload hint (#EXT-X-PRELOAD-HINT).
    • LL-DASH uses chunked CMAF: a segment is sent with HTTP chunked transfer while it is still being produced, so the player can decode the first chunks before the segment is complete.
    • Both reach latencies of a few seconds in practice, depending on the part size, the player buffer and whether every hop (origin, CDN, player) supports the low-latency features. The cost is more requests, a CDN that must forward in-progress objects, and less buffer to absorb a throughput dip.
    • WebRTC is a different problem: UDP-based, no segments, no deep buffer, built for conversation. It reaches sub-second latency but gives up the plain-HTTP cache hierarchy that makes streaming cheap at scale. The interactive side is on real-time patterns.

    Ingest protocols​

    Getting a live stream to the platform is a separate hop from delivering it to viewers.

    ProtocolDirectionTransportTypical use
    RTMPEncoder to platformTCPThe long-standing default for encoders and streaming software
    SRTEncoder to platformUDP, with retransmission and a configurable latency bufferContribution over lossy links such as the public internet
    WebRTC (WHIP)Encoder or browser to platformUDP, SRTPBrowser-based or very-low-latency publishing (RFC 9725)
    HLS / DASHPlatform to viewerHTTP over TCP or QUICDelivery to millions of viewers through a CDN

    The platform transcodes the ingest into the ladder and publishes segments. Viewers rarely see the ingest protocol.

    Latency and delivery summary​

    Typical latency classDeliveryUse
    HLS / DASH (classic)Tens of seconds (3 segments or more behind)HTTP, CDN-cacheableVOD and large-scale live
    LL-HLS / LL-DASHA few secondsHTTP, CDN with low-latency supportLive sport, auctions, chat-heavy streams
    WebRTCSub-second to low secondsUDP, usually peer or SFU, no CDN cacheCalls, interactive live, betting
    RTMP / SRTLow seconds on the contribution legTCP / UDP point to pointIngest only

    Codecs and DRM​

    The ladder is per codec. H.264 (AVC) plays nearly everywhere; HEVC (H.265) and AV1 compress better, at the price of more encode compute and narrower device support, so services often keep an H.264 ladder as the fallback. Content protection is a mention-only topic here: the three common DRM systems are Widevine (Google), FairPlay (Apple) and PlayReady (Microsoft), reached from browsers through the W3C Encrypted Media Extensions API. They protect the keys and license exchange, not the transport.

    Textbook vs real world​

    The textbook saysProduction doesWhy
    DASH is the standard, so everyone uses DASHHLS dominates device reach because Apple devices need it; services often publish both, sharing CMAF segmentsOne encode and one cache entry per segment instead of two
    ABR picks the highest bitrate you can sustainUses smoothed estimates with safety factors, buffer rules, and device or screen-size capsA stall costs viewers more than a lower rung, and a 4K rung is wasted on a phone
    Streaming runs on TCP, so loss does not matterLoss lowers TCP throughput, the ABR sees slower segments and quietly drops qualityLoss shows up as lower resolution rather than as an error
    Live is VOD with a moving windowLive has a latency budget: segment length, buffer depth, encode, CDN and manifest freshness all add upPlayers that start closer to the live edge have a smaller cushion
    A CDN makes live freeSegments cache well, but the manifest changes every segment and must stay freshA stale manifest freezes viewers even when the segments are cached

    In production​

    • Netflix encodes each title with its own ladder (per-title encoding) and later tuned encoding at the shot level, because a flat cartoon and an action film need very different bitrates for the same quality. See the Netflix Tech Blog on per-title encode optimization and the dynamic optimizer. Delivery runs on its own CDN, Open Connect, whose appliances are filled proactively in off-peak hours (Open Connect overview). The buffer-based ABR research of Huang et al. (above) was run on Netflix's service.
    • YouTube delivers video through DASH-style adaptive streaming; yt-dlp -F on a YouTube URL typically lists separate video-only and audio-only formats you can select. Google reports QUIC reducing YouTube rebuffering in its SIGCOMM 2017 deployment paper; transport for segments is covered in QUIC and HTTP/3 and HTTP/1.1 and HTTP/2.
    • Apple's HLS authoring specification is the practical reference for HLS segment duration (6 seconds as the target) and what Apple devices expect from a ladder (spec).
    • Segment durations vary by service and are not published in one place; check a stream's own playlist (Hands-on) instead of trusting a quoted number.

    History and evolution​

    HISTORY
    • 1995 RealNetworks ships RealAudio and later RealPlayer: proprietary streaming over UDP-style protocols, with servers keeping per-viewer state. RTSP (RFC 2326, 1998) standardises the control side.
    • 2005 YouTube launches on progressive download: one file over plain HTTP, no adaptation. It works through firewalls and caches, but a slow link means a stall.
    • 2008-2009 Microsoft's Smooth Streaming (2008) and Apple's HLS (2009, with the iPhone) show that cutting video into small HTTP files and letting the client pick the rung avoids both firewall trouble and per-viewer servers.
    • 2012 MPEG-DASH is published as ISO/IEC 23009-1, giving a vendor-neutral alternative to HLS.
    • 2016 Apple and Microsoft propose a shared segment format, CMAF, to MPEG; it is published as ISO/IEC 23000-19 in 2018. One set of segments can then serve HLS and DASH.
    • 2017 HLS is published as RFC 8216.
    • 2019 Apple introduces LL-HLS; chunked-CMAF LL-DASH follows. Classic 20-plus second live latency is no longer the floor.
    • Now HTTP-based adaptive streaming carries a large share of internet video, delivered through CDNs. Check a live traffic report (for example Sandvine's Global Internet Phenomena Report) for the current share instead of trusting a frozen figure.
    • Next Media over QUIC (IETF MoQ working group) aims to bring low-latency live to large audiences, and WebRTC ingest through WHIP (RFC 9725) is already standardised.

    Hands-on​

    Fetch Apple's public HLS sample (the URL is Apple's and may change) and read the master playlist:

    curl -s https://devstreaming-cdn.apple.com/videos/streaming/examples/img_bipbop_adv_example_fmp4/master.m3u8 | head -8
    #EXTM3U
    #EXT-X-VERSION:6
    #EXT-X-INDEPENDENT-SEGMENTS
     
    #EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=2168183,BANDWIDTH=2177116,CODECS="avc1.640020,mp4a.40.2",RESOLUTION=960x540,FRAME-RATE=60.000,...
    v5/prog_index.m3u8

    Each #EXT-X-STREAM-INF is one rung; BANDWIDTH is the peak, AVERAGE-BANDWIDTH the average. Follow one rung's media playlist:

    curl -s https://devstreaming-cdn.apple.com/videos/streaming/examples/img_bipbop_adv_example_fmp4/v5/prog_index.m3u8 | head -10
    #EXTM3U
    #EXT-X-TARGETDURATION:6
    #EXT-X-VERSION:7
    #EXT-X-MEDIA-SEQUENCE:1
    #EXT-X-PLAYLIST-TYPE:VOD
    #EXT-X-INDEPENDENT-SEGMENTS
    #EXT-X-MAP:URI="main.mp4",BYTERANGE="719@0"
    #EXTINF:6.00000,
    #EXT-X-BYTERANGE:1508000@719
    main.mp4

    This stream uses one fMP4 file and byte ranges instead of one file per segment: #EXT-X-MAP is the init section (719 bytes at offset 0) and each #EXT-X-BYTERANGE is a segment. Check the cache headers the file gets:

    curl -sI https://devstreaming-cdn.apple.com/videos/streaming/examples/img_bipbop_adv_example_fmp4/v5/main.mp4 | grep -i "cache-control\|accept-ranges\|content-type"
    Content-Type: video/mp4
    Accept-Ranges: bytes
    Cache-Control: max-age=600, public

    Accept-Ranges: bytes is what makes byte-range segments work. Header values on Apple's host may change.

    Build your own HLS output from a test pattern (tried with ffmpeg 9.0.2; check ffmpeg -h muxer=hls on your version):

    ffmpeg -f lavfi -i testsrc=duration=20:size=640x360:rate=30 \
    -c:v libx264 -g 60 -keyint_min 60 -sc_threshold 0 \
    -hls_time 6 -hls_playlist_type vod out.m3u8

    -g 60 forces a keyframe every 2 s at 30 fps so segments can be cut on 6 s boundaries. Inspect a stream and list the formats a site offers:

    ffprobe -v error -show_entries stream=codec_name,width,height -of default=nw=1 out.m3u8
    yt-dlp -F <video-url>

    ffprobe prints the codec and resolution of the streams in the playlist; yt-dlp -F prints a table of available formats whose contents vary by site and over time. In a browser, open DevTools, go to the Network tab and filter by m4s, ts or m3u8 to watch the player request the manifest, then segments, and switch rung when you throttle the network.

    Gotchas​

    GOTCHAS4
    Streaming uses UDP
    A higher bitrate is always better
    Live is just VOD with a moving window
    A CDN makes live free

    Interview questions​

    INTERVIEWHow does adaptive bitrate streaming work?junior
    Show answer

    The video is encoded at several quality levels and cut into short segments, and a manifest lists them. The player downloads segments over plain HTTP. After each one it measures how fast the download was and how much video is buffered, then picks the quality of the next segment. If the network slows it steps down instead of stalling, and steps back up when it recovers.

    Follow-ups:

    • What is a manifest? A text or XML file (.m3u8 for HLS, .mpd for DASH) listing the available rungs and where each segment lives.
    • Who decides the rung? The client, which has the best view of its own buffer and screen. The server just serves files.
    INTERVIEWHLS vs DASH?mid
    Show answer

    Both are HTTP-based adaptive streaming with a manifest and short segments. HLS is Apple's (RFC 8216) with .m3u8 text playlists and is required on Apple devices; DASH is the ISO standard (23009-1) with an XML MPD and is typically played with a JavaScript library. Services often publish both because they differ mainly in manifest format.

    Follow-ups:

    • What is CMAF? A standard fragmented-MP4 segment format that both an HLS and a DASH manifest can reference, so one set of segments serves both and the CDN caches each only once.
    • Which one wins on reach? HLS, because of Apple device support; DASH covers most others.
    INTERVIEWWhy is streaming delivered over HTTP and not UDP?mid
    Show answer

    HTTP passes through firewalls and NATs, works with existing CDNs and caches, needs no per-viewer server state, and lets the client adapt. TCP's retransmission hides loss, and the player's buffer hides the retransmission delay. For on-demand and typical live viewing, that is a better trade than UDP's lower latency.

    Follow-ups:

    • What about live? Classic HLS or DASH live runs tens of seconds behind; LL-HLS and LL-DASH cut it to a few seconds over HTTP. For sub-second you use WebRTC over UDP and give up the cache hierarchy.
    • Does QUIC change this? It moves HTTP onto UDP with per-stream loss recovery, which helps segment fetches on lossy paths while keeping the same HTTP model.
    INTERVIEWThroughput-based vs buffer-based ABR? What causes flapping and how do you stop it?mid
    Show answer

    Throughput-based ABR picks the highest rung under a smoothed estimate of download speed. Buffer-based ABR (for example BOLA) ignores throughput and maps the buffer level to a rung. Throughput is better at startup, buffer is better in steady state, so production players combine them. Flapping is the quality bouncing when a noisy estimate crosses a rung threshold.

    Follow-ups:

    • How do you stop flapping? Smooth the estimate (harmonic mean or slow EWMA), switch down fast and up slowly, enforce a minimum dwell time at each rung.
    • Why is the throughput estimate unreliable? The player's own requests shape it: idle gaps between segments, TCP slow start after idle, and other players sharing the link.
    INTERVIEWDesign YouTube or Netflix at the network level.senior
    Show answer

    Ingest the upload, transcode to a ladder per codec, cut into segments, write HLS and DASH manifests, and store in an origin. Put a tiered CDN in front so the origin sees only misses. The player runs an ABR controller and requests segments by URL. Add DRM only if the content needs it, and per-title encoding to save bitrate.

    Follow-ups:

    • Where are the bottlenecks? Egress bandwidth and the cache hit ratio. Every miss sends bytes from the origin, so the design is about getting popular segments cached close to viewers. See the CDN page for the mechanics.
    • What do you cache and for how long? Segments are immutable and cache long; a live manifest needs a short TTL.
    • How do you handle a viral live event? Short manifest TTL, a shielded origin, and request collapsing at the CDN so one miss serves many viewers.
    INTERVIEWHow do you get live latency from 30 s down to 3 s?senior
    Show answer

    The 30 s comes from segment length times the buffered segments (RFC 8216 says three target durations) plus encode and CDN delay. Shorten the segments, then publish in smaller pieces: chunked CMAF for LL-DASH or partial segments for LL-HLS, so the player can start before a segment is complete. For under a second use WebRTC.

    Follow-ups:

    • What is the cost of each? Shorter segments: worse compression and more requests. LL-HLS and LL-DASH: more requests, CDN support for in-progress objects, a thinner buffer. WebRTC: no CDN cache, an SFU fleet and more complex scaling.
    • Do all hops need to support it? Yes. A CDN that buffers whole objects before forwarding erases the gain.
    INTERVIEWHow do you reduce startup time?mid
    Show answer

    Cut the number and cost of sequential requests: keep manifests small, serve them from the edge, and start on a low or middle rung so the first segment arrives fast. Begin playback with a small buffer, then step up. Prefetch the manifest and first segment for the likely next title.

    Follow-ups:

    • Why start low? A 5 Mbps first segment is 30 Mb and takes 10 s at 3 Mbps; the 0.4 Mbps rung's first segment takes 0.8 s.
    • What is the cost of a small initial buffer? Less cushion against the first throughput dip, so the first minute is more stall-prone.

    Fun facts​

    FACTSTrivia worth knowing
    • Apple's public BipBop HLS sample has 100 segments of 6.00 s each in one rung's playlist: 600 s, ten minutes of video.
    • One segment in that sample is 1,508,000 bytes for 6 s, which is 1,508,000 x 8 / 6, about 2.0 Mbps, close to the rung's published 2.17 Mbps average (variable-bitrate encoding moves each segment around the average).
    • RFC 8216 asks a live client to stay at least three target durations from the end of the playlist, which is why a 6 s segment means roughly 18 s of delay before any other source of latency.
    • WebRTC ingest now has a standard: WHIP, RFC 9725, published March 2025.

    Cheat sheet​

    NUMBERSNumbers to remember
    • 6 s - Apple's recommended HLS target segment duration
    • 3 x target duration - minimum distance from the live edge a classic HLS client keeps (RFC 8216, 6.3.3)
    • bitrate x duration / 8 - bytes in a segment (5 Mbps x 6 s = 3.75 MB)
    • throughput / bitrate - 1 - buffer growth in seconds per second (3 / 5 - 1 = -0.4)
    • harmonic mean - 6, 6, 1.5 Mbps gives 3 Mbps; arithmetic gives 4.5