HTTP Live Streaming (HLS) is an adaptive bitrate streaming protocol that delivers media as a series of segmented files over standard HTTP. It uses .m3u8 playlists to manage segment references and supports multiple bitrate renditions for dynamic client-side switching. Implementation involves segment creation, manifest configuration, and HTTP-based delivery using tools like FFmpeg and NGINX.

HLS Architecture Overview

HLS splits a continuous video stream into short .ts segments and generates an .m3u8 playlist (manifest) that references these chunks. The client downloads the manifest and fetches segments using standard HTTP GET requests. Bitrate adaptation occurs when multiple renditions (bitrates/resolutions) are specified in a master playlist.

Core Components

  • Media Segments: .ts files (typically 4"6 seconds each)
  • Media Playlist: .m3u8 file listing segments for a single bitrate
  • Master Playlist: .m3u8 file referencing multiple media playlists with different bandwidths
HLS Streaming

Segmenting and Encoding with FFmpeg

FFmpeg can be used to generate HLS-compatible .ts segments and playlists from a source video.

code
ffmpeg -i input.mp4 -codec: copy -start_number 0 -hls_time 6 -hls_list_size 0 -f hls stream.m3u8

This command creates a single-bitrate HLS stream without re-encoding. It splits the input into .ts segments, generates a media playlist, and names segments starting from 0.

Explanation:

  • -codec: copy: Avoid re-encoding (faster but requires H.264/AAC input)
  • -hls_time 6: Segment duration in seconds
  • -hls_list_size 0: Include all segments in playlist (VOD use case)
  • -start_number 0: Start segment numbering from 0

To encode and segment with transcoding:

code
ffmpeg -i input.mp4 -c:v libx264 -b:v 1500k -c:a aac -f hls -hls_time 6 -hls_segment_filename "segment_%03d.ts" stream.m3u8

This version re-encodes video at 1500 kbps and saves segments as segment_000.ts, segment_001.ts, etc.

Multi-Bitrate Streaming (ABR)

Adaptive Bitrate Streaming (ABR) enables seamless switching between renditions depending on current network conditions. This is achieved by encoding the source into multiple versions and generating a separate .m3u8 playlist for each.

Generate renditions:

code
ffmpeg -i input.mp4 \
-map 0:v -s:v:0 1920x1080 -b:v:0 5000k \
-map 0:v -s:v:1 1280x720 -b:v:1 3000k \
-map 0:v -s:v:2 854x480 -b:v:2 1500k \
-map 0:a -c:a aac -b:a 128k \
-var_stream_map "v:0,a:0 v:1,a:0 v:2,a:0" \
-c:v libx264 -f hls -hls_time 6 -hls_segment_filename "v%v_segment_%03d.ts" -master_pl_name master.m3u8 v%v.m3u8

Explantation:

  • master.m3u8: Contains stream info for v0.m3u8, v1.m3u8, v2.m3u8.
  • v0_segment_000.ts, v1_segment_000.ts, etc.: Corresponding segments for each resolution.
  • -var_stream_map: Maps each video and audio stream pair to a unique variant.

MIME Types and CORS Settings

HLS requires specific MIME types and CORS headers for correct behavior across different browsers and environments.

code
types {
application/vnd.apple.mpegurl m3u8;
video/mp2t ts;
}

Enable CORS for cross-origin playback:

code
add_header Access-Control-Allow-Origin *;

Improper MIME types will cause HLS playback to fail on some clients.

Live Streaming with HLS

Live HLS streaming requires segmenting the input stream in real time and updating the playlist dynamically to reflect the most recent segments. FFmpeg handles this by maintaining a sliding window of segments in the playlist and deleting older files to save storage.

code
ffmpeg -re -i input.mp4 -c:v libx264 -c:a aac -f hls \
-hls_time 4 -hls_list_size 5 -hls_flags delete_segments \
-hls_segment_filename live_%03d.ts live.m3u8

Explantation:

  • -re: Read input at native frame rate (simulates live input)
  • -hls_list_size: Limits number of entries in the playlist
  • -hls_flags delete_segments: Deletes old segments after rotation

This configuration provides a moving window of recent segments for a continuous live feed.

Encryption (AES-128)

HLS supports AES-128 encryption for content protection. Encrypted segments can only be played if the key is accessible and valid.

Generate key and key file:

To enable AES-128 encryption in HLS, a 16-byte key must be generated and stored securely. This key will be used to encrypt each .ts segment. In addition, FFmpeg requires a key information file (.keyinfo) that specifies the public URL of the key, the local path to the key file, and the path to write the initialization vector (IV).

code
openssl rand 16 > enc.key
echo "https://yourdomain.com/hls/enc.key" > enc.keyinfo
echo "enc.key" >> enc.keyinfo
echo "iv" >> enc.keyinfo

This creates:

  • enc.key: A binary 16-byte encryption key.
  • enc.keyinfo: A 3-line file with a Public URL of the key, a local file path for FFmpeg to read the key, and a path to write the initialization vector.

Run FFmpeg with encryption enabled:

Once the key and .keyinfo file are ready, FFmpeg can be instructed to encrypt each segment during HLS generation. Use the -hls_key_info_file flag to pass the key information:

code
ffmpeg -i input.mp4 -hls_time 6 -hls_key_info_file enc.keyinfo \-hls_segment_filename secure_%03d.ts -f hls secure.m3u8

Explantation:

  • -hls_key_info_file: Instructs FFmpeg to use the key and IV paths defined in enc.keyinfo
  • -hls_segment_filename secure_%03d.ts: Sets the naming pattern for encrypted segment files
  • secure.m3u8: The output playlist will contain an #EXT-X-KEY tag pointing to the key URL

Latency Considerations

HLS introduces inherent delay due to segmenting. Factors impacting latency are segment duration (hls_time), playlist size (hls_list_size), player buffer size, and CDN edge cache TTL.

To reduce latency, you must use 2-second segments (-hls_time 2), lLimit playlist to 3"5 entries, use HTTP/2 with CDN support, and combine with LL-HLS extensions if supported by player and server.

Directory Structure and Deployment

A standard HLS deployment consists of the master playlist, media playlists, and segment files organized in a single directory. This structure can be served via a web server or CDN.

/hls/ "" → master.m3u8 "" → v0.m3u8 "" → v1.m3u8 "" → v2.m3u8 "" → v0_segment_000.ts "" → v1_segment_000.ts "" → v2_segment_000.ts

This directory should be served over HTTP by a CDN or static web server. No server-side session state is required. HLS clients (HTML5 video players, Apple TV, Safari, etc.) will request only the necessary playlists and segments.

Best Practices for HLS Deployment

  • Use 2"6 second segments for a balance of latency and efficiency.
  • Align keyframes with segment boundaries for smooth playback and ABR.
  • Always test streams on multiple devices and players (Safari, hls.js, etc.).
  • Monitor CDN and server logs for segment delivery issues.
  • Secure encryption keys and playlists with HTTPS and proper access controls.