Audio streaming protocols decide three things at once: how long a listener waits between a sound being made and hearing it, how many listeners one origin can serve, and whether audio can flow in both directions. No protocol wins all three. Plain HTTP and segmented HTTP (HLS, DASH) scale to millions through ordinary CDNs but buffer seconds of audio. WebRTC delivers conversation-grade latency but every listener holds a stateful media session. Contribution protocols such as RTMP, SRT and WHIP move audio from a studio into the system rather than out to listeners.

This article compares them from first principles: where each one spends its latency, what the wire format looks like, how each fails, and how to choose. It ends with a worked design for a live radio show that takes phone-in callers, which needs two protocol families working together.

Four delivery families and where their latency comes fromProgressive HTTPMP3 / AAC fileor Icecast mountHTTP server / CDNrange requestsPlayer bufferseconds2-30 s liveSegmented HTTPEncoderAAC / OpusPackagerHLS / DASH / CMAFCDN edgeplaylist + segments6-30 s classic2-5 s low-latencyReal-time RTPMic + Opus20 ms framesSFU / TURNSRTP over UDPJitter buffertens of ms0.15-0.4 sContribution (push)Studio / OBSRTMP, SRT, WHIPIngest servertranscode, fan-outAny of the abovefor listenersfeeds the restLatency is mostly buffering you chose: segment length, playlist hold-back and jitter-buffer depth
Each family trades latency against scale differently: HTTP families buffer seconds behind a CDN, real-time RTP keeps a jitter buffer of tens of milliseconds per session, and contribution protocols feed both.

What a streaming protocol decides

Start with the questions a protocol answers. Direction: one-to-many broadcast, one-to-one call, or many-to-many conference. Latency target: a podcast tolerates any delay, a radio stream tolerates tens of seconds, a sports commentary feed wants a few seconds so it does not lag the television, and a conversation breaks down once mouth-to-ear delay passes a few hundred milliseconds. Transport: TCP retransmits lost data and so trades delay for completeness; UDP lets the receiver conceal a lost packet and keep playing. Scale path: anything served as cacheable HTTP files rides existing CDNs; anything that needs per-listener sessions needs media servers you operate or rent.

The codec is a separate choice. AAC and MP3 dominate HTTP delivery, Opus dominates real-time, and most protocols can carry more than one codec. The protocol mainly decides packaging, timing and buffering.

What a streaming protocol decides

Start with the questions a protocol answers. Direction: one-to-many broadcast, one-to-one call, or many-to-many conference. Latency target: a podcast tolerates any delay, a radio stream tolerates tens of seconds, a sports commentary feed wants a few seconds so it does not lag the television, and a conversation breaks down once mouth-to-ear delay passes a few hundred milliseconds. Transport: TCP retransmits lost data and so trades delay for completeness; UDP lets the receiver conceal a lost packet and keep playing. Scale path: anything served as cacheable HTTP files rides existing CDNs; anything that needs per-listener sessions needs media servers you operate or rent.

The codec is a separate choice. AAC and MP3 dominate HTTP delivery, Opus dominates real-time, and most protocols can carry more than one codec. The protocol mainly decides packaging, timing and buffering.

Progressive HTTP and Icecast

Progressive HTTP is a single GET for an MP3 or AAC file. Playback starts as soon as the player has buffered enough bytes, not when the download completes, and seeking uses HTTP range requests, so a podcast client can jump to minute 40 by asking for the matching byte range. For on-demand audio this is the right answer: it caches perfectly, needs no special server and works in every player.

Icecast-style live streams use the same idea for live audio: one long-lived HTTP response whose body never ends. A source client pushes encoded audio to a mount point and the server copies it to every connected listener. Song titles travel in-band using ICY metadata: the client sends Icy-MetaData: 1, the server replies with an icy-metaint interval, and after every interval of audio bytes it inserts one length byte (times 16) followed by a metadata string. A minimal reader:

import requests

def read_icy_titles(url):
    r = requests.get(url, headers={"Icy-MetaData": "1"}, stream=True, timeout=10)
    metaint = int(r.headers["icy-metaint"])   # audio bytes between metadata blocks
    raw = r.raw
    while True:
        raw.read(metaint)                      # audio payload, hand to a decoder in real use
        length = raw.read(1)[0] * 16           # metadata length in 16-byte units
        if length:
            meta = raw.read(length).rstrip(b"\0").decode("utf-8", "replace")
            yield meta                         # e.g. StreamTitle='Artist - Song';

Each listener is a held TCP connection on the origin or relay, so scale comes from relays rather than ordinary file caching, and there is no adaptive bitrate: a listener on a weak network gets rebuffering, not a lower quality. It remains popular because it is simple, cheap and supported by almost every radio app.

Segmented HTTP: HLS, DASH and their low-latency modes

HLS (RFC 8216) and MPEG-DASH cut the live stream into short media segments and publish a manifest listing them. Players poll the manifest, fetch new segments over plain HTTP and keep a few segments buffered. Because segments are immutable files, a CDN caches each one once and serves it to any number of listeners, which is why these formats carry nearly all large-audience live audio. An audio-only HLS media playlist with fragmented MP4 segments looks like this:

#EXTM3U
#EXT-X-VERSION:7
#EXT-X-TARGETDURATION:6
#EXT-X-MEDIA-SEQUENCE:2680
#EXT-X-MAP:URI="init.mp4"
#EXTINF:6.000,
seg2680.m4s
#EXTINF:6.000,
seg2681.m4s
#EXTINF:6.000,
seg2682.m4s

A multivariant playlist above it can offer several bitrates, for example 48 kbit/s HE-AAC and 128 kbit/s AAC-LC, and the player switches between them as throughput changes. That adaptive bitrate is the main reliability advantage over Icecast. Producing it is one ffmpeg command per rendition:

ffmpeg -i rtmp://ingest/live/show -vn -c:a aac -b:a 128k -ar 48000 \
  -f hls -hls_time 6 -hls_list_size 6 -hls_segment_type fmp4 \
  -hls_flags delete_segments+independent_segments  /srv/hls/128k/index.m3u8

Latency follows from the arithmetic. The packager cannot publish a segment until it is complete, so the freshest audio is already one segment old, and the HLS specification tells players not to start closer than three target durations from the live edge. With 6-second segments, 20 seconds or more behind live is normal. Shorter segments cut that but multiply manifest and segment requests.

Low-Latency HLS publishes partial segments of a fraction of a second and lets the player block on a manifest request until the next part exists; low-latency DASH uses CMAF chunks sent with chunked transfer encoding. Both reach a few seconds behind live while keeping CDN delivery. On the client side, Apple platforms play HLS natively and other browsers use a JavaScript player on Media Source Extensions. Packaging CMAF segments once and describing them with both an HLS playlist and a DASH manifest serves both without storing the audio twice.

WebRTC and real-time audio

WebRTC is built for conversation. Audio is encoded in small frames, 20 ms by default for Opus, packetised as RTP, encrypted with SRTP using keys negotiated over DTLS, and sent over UDP. ICE tries direct paths first, uses STUN to learn public addresses and falls back to a TURN relay when NATs or firewalls block everything else. Opus and G.711 are the mandatory-to-implement audio codecs, so any two endpoints can talk. The receiver runs an adaptive jitter buffer, conceals lost packets instead of waiting for retransmission, and the browser audio pipeline adds echo cancellation, noise suppression and gain control.

Two to five participants can connect peer to peer. Beyond that a Selective Forwarding Unit receives each sender once and forwards packets to every subscriber without decoding them, which keeps server CPU low; an MCU decodes and mixes, which costs more CPU but sends each client a single stream. SFUs can be cascaded to serve large audiences, so WebRTC can broadcast, but each viewer is a stateful encrypted session on a media server rather than a cached file, and the cost per listener is far higher than HLS.

Signalling is deliberately left to the application. WHIP (RFC 9725, published March 2025) standardises ingest: the encoder POSTs an SDP offer to an HTTP endpoint and receives the answer. WHEP, the matching playback protocol, was still an IETF draft at the time of writing. Media over QUIC is an IETF working-group effort aiming at CDN-style fan-out with near-real-time latency, also still in draft; treat it as something to watch, not to build on yet.

Contribution: getting audio in

Contribution is the first mile from the studio. RTMP over TCP is still the most widely accepted ingest format, though it predates modern codecs and suffers on lossy links because TCP stalls on loss. SRT runs over UDP with selective retransmission inside a fixed latency window, so a broadcaster can trade a configured delay of a few hundred milliseconds for resilience across the public internet. WHIP brings WebRTC ingest, useful when a browser is the encoder. Telephony keeps its own world of SIP signalling and RTP media, which a gateway bridges into WebRTC or mixes into a broadcast.

Side by side

ProtocolTypical latencyDirectionScale pathAdaptive bitrate
Progressive HTTP filen/a (on demand)one-wayany CDNno
Icecast / ICY2-30 sone-wayrelaysno
HLS / DASH6-30 sone-wayany CDNyes
LL-HLS / LL-DASH2-5 sone-wayCDN with LL supportyes
WebRTC via SFU0.15-0.4 stwo-waymedia serversper-sender layers
RTMP / SRT ingestsub-second to secondscontributionn/an/a

Worked example: a live call-in show

Design a weekly live talk show: a host in a studio, two remote co-hosts, callers dialling in, and an audience that peaks around 50,000 concurrent listeners.

  1. The conversation runs on WebRTC. Host, co-hosts and the caller currently on air join one SFU room, so they hear each other in a few hundred milliseconds. Callers on ordinary phones arrive through a SIP-to-WebRTC gateway, and a producer screens them in a separate room before moving them on air.
  2. A mix-minus feeds back to each participant. Each person hears everyone except themselves, which avoids echo and is standard broadcast practice.
  3. The programme mix goes out once. A mixer subscribes to the room, mixes to a single programme feed and pushes it to the packager over SRT, with a short profanity delay inserted here.
  4. Listeners get HLS. The packager produces 48 and 128 kbit/s AAC renditions in CMAF with 2-second segments, so the audience runs roughly 6-10 seconds behind. The CDN serves 50,000 listeners; at 128 kbit/s that is about 6.4 Gbit/s of egress, all from cache.
  5. An Icecast mount stays for legacy apps. One relay feeds smart speakers and old radio directories that only speak ICY.
  6. The finished show becomes a podcast. The recorded programme mix is loudness normalised and published as a single file served with range requests.

Failure modes

  • Live-edge drift. Players that stall rebuffer and fall further behind; set a maximum latency target in the player and let it skip forward or play slightly faster to catch up.
  • Manifest caching. A CDN that caches a live playlist too long freezes every listener at the same stale segment. Cache segments for a long time, playlists for about half a segment duration or less.
  • Discontinuities. An encoder restart resets timestamps; without #EXT-X-DISCONTINUITY players glitch or stop.
  • TURN-only paths. Corporate networks often block UDP, forcing TURN over TCP or TLS, where latency rises and loss concealment no longer helps much; budget TURN capacity for this traffic.
  • Jitter buffer bloat. An adaptive buffer that grows after a burst of jitter and never shrinks leaves a call feeling sluggish; watch its target delay as a metric.
  • Ingest over TCP on lossy links. RTMP from a venue on cellular stalls and drops; use SRT with a latency window several times the path round-trip time.

Operating a streaming service

Measure what listeners experience rather than what servers report. For HTTP delivery that is startup time, rebuffer ratio, rendition switches and distance from the live edge, collected from the player. For WebRTC it is the receiver statistics the browser exposes through getStats(): packet loss, jitter, concealed samples and round-trip time per session. Put a timecode or tone in the programme feed occasionally and compare end to end; it is the only honest glass-to-ear latency measure. Keep ingest redundant, with two encoders pushing to two ingest points, and make the packager emit identical segment numbers from both so players can fail over without a gap. More on the receive side in jitter buffer design and buffer management for low latency.

Trade-offs

Every move left on the latency axis costs scale or simplicity. Shorter HLS segments multiply requests and make CDN behaviour matter more; WebRTC trades cacheable files for per-session media servers, TURN bandwidth and operational depth; Icecast trades adaptive bitrate for simplicity. The usual production answer is hybrid: real-time protocols for the people talking and segmented HTTP for the people listening. Codec choice interacts with all of this; see MP3 vs AAC vs Opus for streaming, and for the real-time path the WebRTC audio pipeline and Opus for real-time voice.

WebRTC vs DASH: push packets or pull segments

WebRTC vs DASH comes down to who controls timing. WebRTC pushes audio to each receiver as a continuous stream of small encrypted RTP packets over UDP, and the receiver plays whatever has arrived. MPEG-DASH (ISO/IEC 23009-1) lets each player pull media segments of a few seconds over ordinary HTTP, using a manifest called the MPD. The codec matters less than this difference. WebRTC is built for two-way conversation under half a second. DASH is built for one-way delivery that a CDN can cache.

DimensionWebRTCDASH
Unit on the wireRTP packet, typically 20 ms of OpusSegment of 2-6 s, or CMAF chunks in low-latency mode
TransportSRTP over UDP, with TURN over TCP or TLS as fallbackHTTP over TCP, or HTTP/3 over QUIC
A lost packetConcealed by the receiver; NACK and FEC are optionalRetransmitted by the transport; the player stalls if its buffer empties
Who adapts bitrateSender, from congestion feedbackPlayer, by picking a Representation in the MPD
Receiver bufferAdaptive jitter buffer of tens of millisecondsSeveral seconds of segments
Server state per listenerOne session on an SFU or peerNone; segments are cacheable files
EncryptionDTLS-SRTP, mandatory, hop by hopOptional; Common Encryption with DRM through EME
Browser playbackBuilt inJavaScript player such as dash.js or Shaka on Media Source Extensions

The first thing each client receives shows the split. A WebRTC client gets an SDP answer naming a codec and an RTP payload type. A DASH client gets an MPD that lists bitrates and gives a template for segment URLs:

# WebRTC: audio lines of a typical browser SDP
m=audio 9 UDP/TLS/RTP/SAVPF 111
a=rtpmap:111 opus/48000/2
a=fmtp:111 minptime=10;useinbandfec=1

# DASH: audio part of a live MPD
<AdaptationSet contentType="audio" mimeType="audio/mp4">
  <SegmentTemplate timescale="48000" duration="96000"
      initialization="init-$RepresentationID$.mp4"
      media="seg-$RepresentationID$-$Number$.m4s"/>
  <Representation id="a128" codecs="mp4a.40.2" bandwidth="128000"/>
  <Representation id="a48" codecs="mp4a.40.5" bandwidth="48000"/>
</AdaptationSet>

Here duration="96000" at a timescale of 48000 means 2-second segments. Latency therefore starts at one segment and grows with every segment the player buffers. WebRTC latency is set by the 20 ms packet time and the jitter buffer. Choose by the listener's role: people who talk on air need WebRTC, and people who only listen get DASH or HLS. For DASH against HLS, see HLS vs DASH.

What to do next

  1. Write down your latency target and direction first; they eliminate most options before cost does.
  2. For on-demand audio, serve files over HTTP with range requests and stop there.
  3. For broadcast, package CMAF once, publish HLS and DASH manifests, and start with 2-6 second segments.
  4. Try low-latency HLS or DASH only after measuring live-edge distance from real players.
  5. For conversation, use WebRTC with an SFU, budget TURN capacity, and collect getStats() per session.
  6. Ingest over SRT or WHIP from anywhere lossy, with two redundant encoders.
  7. Add an end-to-end timecode check so you can prove the latency you claim.
Key takeaway: Pick an audio streaming protocol from latency target and direction first: HTTP files for on-demand, HLS or DASH for large live audiences, WebRTC for conversation, SRT or WHIP for ingest. Most real systems combine a real-time path for speakers with a segmented HTTP path for listeners.