MPEG-DASH Fundamentals
MPEG-DASH (Dynamic Adaptive Streaming over HTTP) is a standard for delivering video over HTTP as many small files, called segments.
| Component | Description |
|---|---|
| MPD file | The manifest (like a playlist) |
| Segment file | The actual video or audio data (usually 2–10 seconds each) |
| Representation | One quality level (a bitrate and resolution) |
| AdaptationSet | A group of video or audio streams |
Basic Command (Single Bitrate)
ffmpeg -i input.mp4 -c:v libx264 -b:v 1500k -c:a aac -b:a 128k -f dash -seg_duration 4 output.mpd
This writes:
output.mpd— the manifest fileinit-stream0.m4s— video initialization segment (setup data the player reads first)init-stream1.m4s— audio initialization segmentchunk-stream0-XXXXX.m4s— numbered video segmentschunk-stream1-XXXXX.m4s— numbered audio segments
A new segment can only start at a keyframe (a frame that can be decoded on its own). By default, libx264 places a keyframe at most every 250 frames (about 8.3 s at 30 fps). So even with -seg_duration 4, the video segments are not 4 s long: on footage without scene cuts they come out at about 8.3 s. To split every 4 s in 30 fps video, add -g 120 -keyint_min 120 -sc_threshold 0.
Multi-Bitrate ABR Setup
Adaptive bitrate streaming (ABR) means offering the same video at several quality levels, so the player can switch to match the connection speed. List the same input stream more than once with -map to write several bitrates in one run.
ffmpeg -i input.mp4 \
-map 0:v -map 0:v -map 0:a \
-c:v libx264 \
-b:v:0 2500k -s:v:0 1280x720 \
-b:v:1 800k -s:v:1 854x480 \
-c:a aac -b:a 128k \
-f dash \
-seg_duration 4 \
-adaptation_sets "id=0,streams=v id=1,streams=a" \
output.mpd
This writes two video bitrates, 720p (2500 kbps) and 480p (800 kbps), plus one audio track. -adaptation_sets puts the two video streams in one group and the audio in another. The player switches between the video qualities automatically as network conditions change.
Key Options
| Option | Example | Description |
|---|---|---|
-seg_duration |
4 |
Segment length in seconds (usually 2–6) |
-use_timeline |
1 |
Write a SegmentTimeline, the list of segment times (default: 1) |
-use_template |
1 |
Write segment URLs as a template (default: 1) |
-window_size |
5 |
Number of segments kept in the manifest during live streaming |
-adaptation_sets |
"id=0,streams=v" |
How representations are grouped |
-init_seg_name |
'init_$RepresentationID$.m4s' |
File name template for initialization segments |
-media_seg_name |
'seg_$RepresentationID$_$Number$.m4s' |
File name template for media segments. Without $RepresentationID$, video and audio segments get the same names and overwrite each other |
VOD Settings
VOD (video on demand) means recorded video that viewers play whenever they like. A VOD example:
ffmpeg -i input.mp4 \
-c:v libx264 -b:v 1500k \
-c:a aac -b:a 128k \
-f dash \
-seg_duration 4 \
-use_timeline 1 \
-use_template 1 \
output.mpd
-use_timeline 1 and -use_template 1 are both defaults, so this writes the same files as the basic command. VOD needs no extra option: when the encode finishes, FFmpeg writes the MPD with type="static", the value used for VOD.
Measured: time and size
This is the command that was measured:
ffmpeg -i input.mp4 -c:v libx264 -b:v 2500k -c:a aac -b:a 128k -f dash -seg_duration 4 output.mpd
It finished in 22.66 s. Only the time was recorded, not the size: DASH writes an MPD plus a directory of segment files, so there is no single output file to measure.
For comparison, other operations from the same run:
| Operation | Time |
|---|---|
This DASH packaging run (-seg_duration 4) |
22.66 s |
| Plain re-encode, no filter (libx264 CRF 23, preset medium, no audio) | 27.97 s |
Stream mapping (-c copy) |
0.41 s |
Relocating the moov atom (-c copy) |
0.49 s |
Audio fade (video -c copy) |
1.03 s |
Environment: Intel Core i9-14900KF (32 threads), Windows, FFmpeg 8.1 (gyan.dev). Source: 1920x1080, 30 fps, 120 s: 351.4 MB of video (Big Buck Bunny, looped, CC BY 3.0) plus a 128 kb/s AAC track, 353.29 MB in total. Commands were run one at a time on 2026-09-05. The raw numbers are in the dataset.
Where the 22.66 s goes
Almost all of it is the H.264 encode. -f dash only cuts the output into segments and writes a manifest; it does no extra work on the pixels. That is why the time falls within the range measured in the same batch for re-encoding commands (17.52 s to 729.16 s) and is similar to the 27.97 s re-encode with no filter.
The two runs are not a fair comparison, though. The re-encode used -crf 23 and dropped the audio, while this command targets -b:v 2500k and also encodes AAC. So the difference between the two times does not show what DASH packaging costs.
The encoder is the cost, not the packaging
In the same run, the operations that used -c copy finished in 0.41 s to 0.49 s. That is how long pure container work takes: reading the streams, writing them into a new file and writing headers. The DASH job took 22.66 s because it encoded every frame again, not because it split the output into segments.
So to make it faster, change the encode, not the segment settings. If the source is already H.264 + AAC ready for delivery and its keyframes line up with the segment length, packaging with -c copy skips the encoder entirely.
Choosing HLS vs. MPEG-DASH
| Criterion | HLS | MPEG-DASH |
|---|---|---|
| Native Safari support | Yes (built-in) | Partial (requires JS) |
| Segment format | .ts / .m4s |
.m4s / .mp4 |
| Standards body | Apple (published as RFC 8216) | ISO international |
| Dynamic ABR switching | Yes | Yes |
| Primary use case | iOS and Safari | General-purpose / OTT |
Frequently Asked Questions
DASH vs HLS — which should I use?
For iOS and Safari, HLS is the safest choice. DASH is common on desktop browsers and smart TVs. To serve both, add -hls_playlist 1 to the -f dash command. It then also writes HLS playlists (master.m3u8, media_0.m3u8, …) that point at the same fMP4 segments. You can also make separate -f hls and -f dash outputs.
What segment length should I pick?
Segments of 4–6 seconds balance start-up time against how quickly the player can switch quality. Shorter segments (2 s) make channel changes faster, but the manifest changes more often and CDN (content delivery network) costs go up.
Why does my DASH player buffer at every quality switch?
Most likely the keyframes do not line up across renditions (the different quality versions). Force a keyframe every 4 seconds in every rendition with -force_key_frames "expr:gte(t,n_forced*4)".
Can I do live DASH from FFmpeg directly?
Yes. -f dash -window_size 5 -extra_window_size 10 gives a sliding live window: the MPD lists only the latest 5 segments, and -extra_window_size 10 keeps 10 more on disk after they drop out of the MPD. Send the output to your CDN’s ingest endpoint (the URL that accepts uploads) with HTTP PUT.
Do I need separate audio segments?
You get them without asking: -f dash writes video and audio to separate segment files (chunk-stream0-…, chunk-stream1-…). -adaptation_sets "id=0,streams=v id=1,streams=a" does something else: it groups streams into AdaptationSets. Without it, each stream gets its own AdaptationSet. Players switch quality only within one AdaptationSet, so for several bitrates, put all the video streams in one group.