What You Will Learn

Tested version: FFmpeg 6.1 (ubuntu-latest / CI-validated)
Target OS: Windows / macOS / Linux


How HLS Works

HLS (HTTP Live Streaming) is Apple’s adaptive streaming protocol, now an industry standard supported on virtually every platform. FFmpeg can generate HLS output with the -f hls muxer.

An HLS output consists of two types of files:

File typeExtensionDescription
Playlist.m3u8Index file listing all segment URLs
Segments.tsShort video/audio chunks (typically 2–10 seconds each)

A web server only needs to serve these static files over HTTP. Players (browsers via hls.js, native iOS/macOS, Android) download the playlist, then fetch segments on demand.


Minimal HLS Output

ffmpeg -i input.mp4 -c:v libx264 -c:a aac -f hls /tmp/playlist.m3u8

This produces /tmp/playlist.m3u8 and numbered segment files (/tmp/out000.ts, /tmp/out001.ts, …) using default settings.


Controlling Segment Duration

-hls_time N sets the target segment duration in seconds (default: 2):

ffmpeg -i input.mp4 -c:v libx264 -crf 23 -c:a aac -f hls -hls_time 6 /tmp/playlist.m3u8

Segments are split on keyframe boundaries, so actual durations may vary slightly. To get consistent splits, add a forced keyframe interval:

ffmpeg -i input.mp4 -c:v libx264 -crf 23 -g 60 -keyint_min 60 -sc_threshold 0 -c:a aac -f hls -hls_time 6 /tmp/playlist.m3u8

-g 60 sets a keyframe every 60 frames (2 seconds at 30 fps). Combined with -hls_time 6, each segment spans exactly 3 keyframe intervals.


Retaining All Segments in the Playlist

By default, -hls_list_size is 5, meaning the playlist contains only the last 5 segments (useful for live streams). For VOD (Video on Demand), set it to 0 to retain all segments:

ffmpeg -i input.mp4 -c:v libx264 -crf 23 -c:a aac -f hls -hls_time 6 -hls_list_size 0 /tmp/playlist.m3u8

Custom Segment Filename Pattern

Use -hls_segment_filename to control the path and naming of segment files:

ffmpeg -i input.mp4 -c:v libx264 -crf 23 -c:a aac -f hls -hls_time 6 -hls_list_size 0 -hls_segment_filename "/tmp/segment_%03d.ts" /tmp/playlist.m3u8

%03d is a printf-style format that produces zero-padded numbers: segment_000.ts, segment_001.ts, etc. You can also include a directory path:

ffmpeg -i input.mp4 -c:v libx264 -crf 23 -c:a aac -f hls -hls_time 6 -hls_list_size 0 -hls_segment_filename "/tmp/hls/seg_%04d.ts" /tmp/hls/playlist.m3u8

Make sure the output directory exists before running (FFmpeg will not create it automatically).


Complete VOD Example

A production-ready command for converting a file to HLS for on-demand playback:

ffmpeg -i input.mp4 -c:v libx264 -preset slow -crf 22 -c:a aac -b:a 128k -f hls -hls_time 6 -hls_list_size 0 -hls_segment_filename "/tmp/hls_vod/seg_%04d.ts" /tmp/hls_vod/index.m3u8

Deploy the entire /tmp/hls_vod/ directory to any HTTP server. Point your player at index.m3u8.


Live Streaming — Delete Old Segments

For a live stream, old segments are no longer needed once they leave the playlist window. Use -hls_flags delete_segments to remove them automatically:

ffmpeg -i input.mp4 -c:v libx264 -crf 23 -c:a aac -f hls -hls_time 6 -hls_list_size 5 -hls_flags delete_segments /tmp/playlist.m3u8

With -hls_list_size 5, the playlist holds 5 segments. Any segment removed from the playlist is automatically deleted from disk.


Adaptive Bitrate Ladder (Overview)

A full ABR ladder requires encoding the same content at multiple bitrates and resolutions, then creating a master playlist that references each variant. The typical workflow:

  1. Encode each bitrate/resolution as a separate HLS stream
  2. Write a master playlist (.m3u8) that lists each variant with #EXT-X-STREAM-INF tags

FFmpeg can produce multiple outputs in one pass using -map and multiple -f hls outputs, but this requires a complex command. For production ABR use cases, dedicated tools such as AWS MediaConvert or open-source transcoders built around FFmpeg are more practical.


Key Options Reference

OptionDefaultDescription
-hls_time N2Target segment duration in seconds
-hls_list_size N5Max segments in playlist (0 = keep all)
-hls_segment_filename PATTERN(auto)Segment file naming pattern
-hls_flags delete_segmentsoffAuto-delete segments removed from playlist
-hls_flags append_listoffAppend to existing playlist instead of overwriting
-start_number N0Starting sequence number for segments

Common Pitfalls

Segments Are Not in the Playlist

If you forget -hls_list_size 0 for a VOD file, only the last N segments will appear in the playlist. The file will play, but only the last portion.

Output Directory Does Not Exist

FFmpeg will fail with a “No such file or directory” error if the parent directory of the segment path does not exist. Create it first:

mkdir -p /tmp/hls_output
ffmpeg -i input.mp4 -c:v libx264 -crf 23 -c:a aac -f hls -hls_time 6 -hls_list_size 0 -hls_segment_filename "/tmp/hls_output/seg_%04d.ts" /tmp/hls_output/playlist.m3u8

Player Shows “No Playable Sources”

Ensure your HTTP server sets the correct MIME types:

Some servers need explicit configuration for these types.



Tested with: ffmpeg 6.1.1 / Ubuntu 24.04 (GitHub Actions runner)
Primary sources: ffmpeg.org/ffmpeg-formats.html#hls-1 / trac.ffmpeg.org/wiki/StreamingGuide