FFmpeg Multi-Bitrate Ladder Encoding for HLS and DASH
This article provides a step-by-step guide on how to configure a multi-bitrate ladder for HLS (HTTP Live Streaming) and DASH (Dynamic Adaptive Streaming over HTTP) using FFmpeg. You will learn how to take a single high-quality video input, scale it into multiple resolutions and bitrates using FFmpeg’s filter graphs, and package those streams into adaptive playouts (.m3u8 for HLS and .mpd for DASH) to ensure smooth playback across varying network conditions.
Understanding the Bitrate Ladder
A bitrate ladder is a set of matching video streams encoded at different resolutions, frame rates, and bitrates. When network bandwidth fluctuates, the video player automatically switches between these streams (renditions) to prevent buffering.
For a standard 1080p source, a typical three-tier ladder might look like this: * 1080p (High): 1920x1080 at 5,000 kbps * 720p (Medium): 1280x720 at 2,800 kbps * 480p (Low): 854x480 at 1,400 kbps
To ensure seamless switching, all renditions must have aligned keyframes (GOP size).
1. Preparing the Filter Graph in FFmpeg
To avoid decoding the input file multiple times, use FFmpeg’s
split filter to duplicate the input video, then scale each
copy to the target resolution.
-filter_complex "[0:v]split=3[v1][v2][v3]; [v1]scale=w=1920:h=1080[v1out]; [v2]scale=w=1280:h=720[v2out]; [v3]scale=w=854:h=480[v3out]"This configuration splits the input video stream into three identical streams and scales them to 1080p, 720p, and 480p respectively.
2. Encoding and Packaging for HLS
To generate HLS outputs, map the scaled video outputs and the audio
track to individual stream specifiers. Use the -f hls muxer
along with -var_stream_map to link specific video and audio
streams together into variant playlists.
Here is the complete command:
ffmpeg -i input.mp4 \
-filter_complex "[0:v]split=3[v1][v2][v3]; [v1]scale=w=1920:h=1080[v1out]; [v2]scale=w=1280:h=720[v2out]; [v3]scale=w=854:h=480[v3out]" \
-map "[v1out]" -c:v:0 libx264 -b:v:0 5000k -maxrate:v:0 5350k -bufsize:v:0 7500k -g 48 -keyint_min 48 -sc_threshold 0 \
-map "[v2out]" -c:v:1 libx264 -b:v:1 2800k -maxrate:v:1 3000k -bufsize:v:1 4200k -g 48 -keyint_min 48 -sc_threshold 0 \
-map "[v3out]" -c:v:2 libx264 -b:v:2 1400k -maxrate:v:2 1500k -bufsize:v:2 2100k -g 48 -keyint_min 48 -sc_threshold 0 \
-map a:0 -c:a:0 aac -b:a:0 192k \
-map a:0 -c:a:1 aac -b:a:1 128k \
-map a:0 -c:a:2 aac -b:a:2 96k \
-f hls \
-hls_time 4 \
-hls_playlist_type vod \
-master_pl_name master.m3u8 \
-var_stream_map "v:0,a:0 v:1,a:1 v:2,a:2" \
stream_%v.m3u8Key Parameters Explained:
-g 48 -keyint_min 48 -sc_threshold 0: Forces a closed GOP (Group of Pictures) of 48 frames (2 seconds at 24fps) and disables scene-change detection to align GOP boundaries across all streams.-hls_time 4: Sets the segment duration to 4 seconds.-master_pl_name master.m3u8: Generates the master playlist that index-links the sub-playlists.-var_stream_map: Maps video stream 0 to audio stream 0 (v:0,a:0), video 1 to audio 1, and video 2 to audio 2.
3. Encoding and Packaging for DASH
DASH uses a Media Presentation Description (.mpd) file.
Instead of individual variant playlists, DASH groups streams into
“adaptation sets” (typically one set for video streams and one for audio
streams).
Here is the command to encode and package the same ladder for DASH:
ffmpeg -i input.mp4 \
-filter_complex "[0:v]split=3[v1][v2][v3]; [v1]scale=w=1920:h=1080[v1out]; [v2]scale=w=1280:h=720[v2out]; [v3]scale=w=854:h=480[v3out]" \
-map "[v1out]" -c:v:0 libx264 -b:v:0 5000k -g 48 -keyint_min 48 -sc_threshold 0 \
-map "[v2out]" -c:v:1 libx264 -b:v:1 2800k -g 48 -keyint_min 48 -sc_threshold 0 \
-map "[v3out]" -c:v:2 libx264 -b:v:2 1400k -g 48 -keyint_min 48 -sc_threshold 0 \
-map a:0 -c:a:0 aac -b:a:0 128k \
-f dash \
-seg_duration 4 \
-use_timeline 1 \
-use_template 1 \
-adaptation_sets "id=0,streams=v id=1,streams=a" \
manifest.mpdKey Parameters Explained:
-f dash: Instructs FFmpeg to use the DASH muxer.-seg_duration 4: Sets the target segment duration to 4 seconds.-use_template 1 -use_timeline 1: Enables URL templates and media timelines in the manifest, optimizing file structures and playback sync.-adaptation_sets "id=0,streams=v id=1,streams=a": Groups all video streams (v) into adaptation set 0, and all audio streams (a) into adaptation set 1, allowing players to transition smoothly between different video resolutions while keeping a single audio track.