Frame-Accurate HLS Chunk Alignment with FFmpeg
To achieve seamless adaptive bitrate (ABR) streaming in HLS, video segments across all bitrate variants must align exactly to the same frame boundaries. This article provides a direct, step-by-step guide on how to configure FFmpeg to force keyframe (IDR) placement at precise intervals, ensuring frame-accurate chunk boundaries across different bitrates and preventing playback stuttering during quality switches.
The Importance of Keyframe Alignment
In HLS (HTTP Live Streaming), players switch between different bitrates depending on the user’s network conditions. For this switch to be seamless and invisible to the viewer, every segment (e.g., segment 1, segment 2) in every bitrate playlist must start with the exact same video frame.
Because video compression relies on Group of Pictures (GOP) structures, a new HLS segment can only begin at an IDR keyframe. If different encoding profiles place keyframes at different times, the chunk boundaries will drift, causing the player to buffer, drop frames, or desynchronize when switching qualities.
Step-by-Step Configuration in FFmpeg
To guarantee frame-accurate chunk alignment, you must strictly control keyframe placement using three crucial FFmpeg parameters in combination with your frame rate.
1. Disable Scene Change Detection
By default, encoders like libx264 insert extra keyframes
at scene cuts to maintain visual quality. This breaks uniform segment
sizes. You must disable this behavior. * Parameter:
-sc_threshold 0
2. Set a Fixed GOP (Group of Pictures) Size
The GOP size determines how often a keyframe is created. To align
with your HLS segment length, the GOP size must be a mathematical
multiple of your frame rate and target segment duration. *
Formula:
GOP = Frame Rate (FPS) * Target Segment Duration (seconds)
* Example: For a 30 FPS video with 4-second segments,
the GOP size must be exactly 30 * 4 = 120 frames. *
Parameter: -g 120
3. Force Minimum Keyframe Interval
To ensure FFmpeg does not insert keyframes earlier than the specified
GOP size, set the minimum keyframe interval to match the GOP size. *
Parameter: -keyint_min 120 (matching the
-g value)
4. Set the HLS Segment Time
Finally, instruct the HLS muxer to split the chunks at your exact
target duration. * Parameter:
-hls_time 4
Practical FFmpeg Command Example
Below is a command demonstrating how to transcode a single input into two different aligned bitrates (720p and 1080p) at 30 FPS with 4-second chunk boundaries:
ffmpeg -i input.mp4 \
-filter_complex "[0:v]split=2[v1][v2]; [v1]scale=1280:720[v1_out]; [v2]scale=1920:1080[v2_out]" \
-map "[v1_out]" -c:v:0 libx264 -b:v:0 2500k -g 120 -keyint_min 120 -sc_threshold 0 \
-map "[v2_out]" -c:v:1 libx264 -b:v:1 5000k -g 120 -keyint_min 120 -sc_threshold 0 \
-map a:0 -c:a aac -b:a 128k \
-f hls -hls_time 4 -hls_playlist_type vod \
-master_pl_name master.m3u8 \
-var_stream_map "v:0,a:0 v:1,a:0" stream_%v.m3u8Summary of Best Practices
- Keep Framerates Identical: Ensure all output
streams use the same framerate (e.g., use
-r 30explicitly if your sources vary). - Match GOP to Segment Size: Always verify that
GOP = FPS * hls_time. - Apply Universally: Apply the exact same
-g,-keyint_min, and-sc_threshold 0settings to every single video stream representation in your ABR ladder.