MPEG-DASH Complete Guide: Protocol Principles, Dash.js Implementation vs HLS
MPEG-DASH is the other major adaptive streaming protocol alongside HLS. While HLS dominates Apple ecosystem and mobile, DASH powers web streaming for Netflix, YouTube, and Amazon Prime Video. This guide explains how DASH works, how to implement it with dash.js, and how it compares to HLS.
What is MPEG-DASH?
DASH (Dynamic Adaptive Streaming over HTTP) is an ISO/IEC standard (ISO/IEC 23009-1) for adaptive bitrate streaming over HTTP. Released in 2012, it solves the same problem as HLS: delivering video efficiently over standard HTTP infrastructure with quality adaptation based on network conditions.
Key characteristics:
- Open standard (ISO/IEC, not proprietary)
- HTTP-based (works with any CDN, no special server needed)
- Codec-agnostic (supports H.264, H.265, VP9, AV1, etc.)
- Segmented (video split into small chunks for ABR)
How DASH works
Basic flow
- Encoding: Source video encoded at multiple bitrates/resolutions
- Segmentation: Each version split into small segments (2-10 seconds)
- Manifest creation: MPD file generated describing all available versions
- Distribution: MPD and segments served over HTTP/CDN
- Playback: Client downloads MPD, requests segments based on bandwidth
- Adaptation: Client switches between bitrates as network conditions change
MPD file structure
MPD (Media Presentation Description) is the DASH manifest, equivalent to HLS M3U8. It's XML format:
<?xml version="1.0" encoding="UTF-8"?>
<MPD type="static" mediaPresentationDuration="PT1H30M" minBufferTime="PT2S">
<Period>
<AdaptationSet mimeType="video/mp4" segmentAlignment="true">
<!-- 360p representation -->
<Representation id="v0" bandwidth="800000" width="640" height="360" codecs="avc1.42E01E">
<SegmentTemplate media="video-$Number$.m4s" initialization="video-init.m4s" duration="4" startNumber="1"/>
</Representation>
<!-- 720p representation -->
<Representation id="v1" bandwidth="2000000" width="1280" height="720" codecs="avc1.4D401F">
<SegmentTemplate media="video-$Number$.m4s" initialization="video-init.m4s" duration="4" startNumber="1"/>
</Representation>
<!-- 1080p representation -->
<Representation id="v2" bandwidth="5000000" width="1920" height="1080" codecs="avc1.640028">
<SegmentTemplate media="video-$Number$.m4s" initialization="video-init.m4s" duration="4" startNumber="1"/>
</Representation>
</AdaptationSet>
<AdaptationSet mimeType="audio/mp4" lang="en">
<Representation id="a0" bandwidth="128000" codecs="mp4a.40.2">
<SegmentTemplate media="audio-$Number$.m4s" initialization="audio-init.m4s" duration="4" startNumber="1"/>
</Representation>
</AdaptationSet>
</Period>
</MPD>MPD hierarchy
- MPD: Root element, contains metadata
- Period: Time segment (e.g., main content, ads)
- AdaptationSet: Group of similar streams (video, audio, subtitles)
- Representation: Specific version (bitrate/resolution combination)
- SegmentTemplate: URL pattern for segments
Generate DASH content with FFmpeg
Basic DASH packaging
ffmpeg -i input.mp4 \
-map 0:v -map 0:a \
-c:v libx264 -crf 23 -preset medium \
-c:a aac -b:a 128k \
-f dash \
-use_timeline 1 \
-use_template 1 \
-seg_duration 4 \
-adaptation_sets "id=0,streams=v id=1,streams=a" \
output.mpdMulti-bitrate DASH
ffmpeg -i input.mp4 \
-map 0:v:0 -map 0:v:0 -map 0:v:0 -map 0:a:0 \
-c:v:0 libx264 -b:v:0 800k -s:v:0 640x360 \
-c:v:1 libx264 -b:v:1 2000k -s:v:1 1280x720 \
-c:v:2 libx264 -b:v:2 5000k -s:v:2 1920x1080 \
-c:a aac -b:a 128k \
-f dash \
-use_timeline 1 \
-use_template 1 \
-seg_duration 4 \
-adaptation_sets "id=0,streams=v:0,v:1,v:2 id=1,streams=a" \
output.mpddash.js browser implementation
No browser natively supports DASH. All playback uses JavaScript libraries with Media Source Extensions (MSE).
Basic dash.js setup
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.dashjs.org/latest/dash.all.min.js"></script>
</head>
<body>
<video controls style="width: 100%; max-width: 1280px;"></video>
<script>
const video = document.querySelector('video');
const player = dashjs.MediaPlayer().create();
player.initialize(video, 'https://example.com/stream.mpd', true);
</script>
</body>
</html>Advanced configuration
const player = dashjs.MediaPlayer().create();
// Configure ABR
player.updateSettings({
streaming: {
abr: {
autoSwitchBitrate: { video: true, audio: true },
ABRStrategy: 'abrBola',
minBitrate: { video: 800 },
maxBitrate: { video: 5000 }
},
buffer: {
bufferGoalAtTopQuality: 30,
bufferTimeDefault: 20
}
}
});
// Quality change events
player.on(dashjs.MediaPlayer.events.QUALITY_CHANGE_REQUESTED, (e) => {
console.log(`Switching to quality ${e.newQuality}`);
});
player.initialize(video, 'https://example.com/stream.mpd', true);Custom quality selector
// Get available bitrates
const bitrates = player.getBitrateInfoListFor('video');
console.log('Available qualities:', bitrates);
// Manual quality selection
player.updateSettings({
streaming: { abr: { autoSwitchBitrate: { video: false } } }
});
player.setQualityFor('video', 1); // Index in bitrates arrayDASH vs HLS comparison
| Feature | MPEG-DASH | HLS |
|---|---|---|
| Standard | ISO/IEC 23009-1 | RFC 8216 |
| Manifest format | XML (MPD) | Text (M3U8) |
| Segment format | fMP4, WebM | TS, fMP4 |
| Codec support | All (H.264, H.265, VP9, AV1) | All (limited by player) |
| Browser native support | None | Safari only |
| JS library required | Yes (dash.js) | Yes (hls.js) for non-Safari |
| Apple ecosystem | No | Yes (native iOS/macOS) |
| Latency | 5-30s (standard), 1-2s (LL-DASH) | 5-30s (standard), 1-2s (LL-HLS) |
| DRM | Widevine, PlayReady (CENC) | FairPlay, Widevine, PlayReady |
| Adoption | Netflix, YouTube, Amazon | Apple, Disney+, Twitch |
| Standardization | ISO/IEC | RFC |
When to use which
Use HLS when:
- Targeting iOS/macOS (native support)
- Broad mobile compatibility needed
- Apple ecosystem is primary audience
Use DASH when:
- Web-first streaming
- Need codec flexibility (AV1, VP9)
- Want open standard with no patent concerns
- Targeting Chrome/Android ecosystem
Use both (recommended for large services):
- Multi-format strategy ensures maximum compatibility
- Cloud services (AWS MediaConvert, Mux) can generate both formats from one source
Low-Latency DASH (LL-DASH)
Standard DASH has 5-30 second latency. LL-DASH reduces this to 1-2 seconds using:
- Chunked transfer encoding: Server streams segments as they're encoded
- Resync elements: MPD contains resync points for faster seeking
- Smaller segments: 1-2 seconds instead of 6-10 seconds
- Client-side optimization: Smaller buffer, faster ABR decisions
# Generate LL-DASH content
ffmpeg -i input.mp4 \
-c:v libx264 -crf 23 -preset veryfast -tune zerolatency \
-c:a aac -b:a 128k \
-f dash \
-seg_duration 1 \
-use_timeline 1 \
-use_template 1 \
-write_timeline 0 \
output.mpdDRM support
DASH uses CENC (Common Encryption) standard, supporting multiple DRM systems:
| DRM | Provider | Browser support |
|---|---|---|
| Widevine | Chrome, Firefox, Edge, Android | |
| PlayReady | Microsoft | Edge, IE, Xbox |
| FairPlay | Apple | Safari (limited DASH support) |
For cross-platform DRM, use Multi-DRM services like AWS Elemental MediaPackage, Axinom, or EZDRM.
Common issues
| Issue | Cause | Solution |
|---|---|---|
| Video won't play | CORS not configured | Add Access-Control-Allow-Origin: * to segment responses |
| Stuck on loading | MPD malformed | Validate XML structure |
| No quality switching | ABR disabled | Enable in dash.js settings |
| Buffering frequently | Network bandwidth low | Lower maxBitrate in settings |
| Audio missing | Multiple audio tracks | Check AdaptationSet lang attribute |
| Live stream not updating | Static MPD used | Use type="dynamic" for live |
Summary
MPEG-DASH is a powerful, open alternative to HLS for adaptive bitrate streaming. While HLS has native iOS support, DASH dominates web streaming with Netflix, YouTube, and Amazon using it. For maximum compatibility, use both formats (multi-CDN strategy).
Quick reference:
- Standard: ISO/IEC 23009-1
- Manifest: MPD (XML)
- Segments: fMP4 (typically)
- Browser playback: dash.js with MSE
- Generate content:
ffmpeg -i input.mp4 -f dash -seg_duration 4 output.mpd
For HLS comparison, see our M3U8 file complete guide.