Skip to content
pingo edited this page Apr 9, 2026 · 1 revision

English | 中文

LL-HLS (Low-Latency HLS)

LiveForge implements Apple's Low-Latency HLS specification for sub-second latency HTTP streaming. LL-HLS extends standard HLS with partial segments, blocking playlist reload, and preload hints.

Configuration

http_stream:
  enabled: true
  listen: ":8080"
  llhls:
    enabled: true
    part_duration: 0.2
    segment_count: 4
    container: "fmp4"
Field Type Default Description
llhls.enabled bool false Enable LL-HLS (replaces regular HLS for .m3u8 requests)
llhls.part_duration float 0.2 Target partial segment (PART) duration in seconds
llhls.segment_count int 4 Number of completed segments in the sliding window
llhls.container string "fmp4" Container format: "fmp4" (recommended) or "ts"

When LL-HLS is enabled, .m3u8 requests serve the LL-HLS playlist. Regular HLS and LL-HLS are mutually exclusive — set llhls.enabled: true for LL-HLS, or false for regular HLS.

How It Works

Partial Segments

Instead of waiting for a full segment (typically 6 seconds), LL-HLS splits segments into small parts (default 200ms). Each part is independently addressable and deliverable.

Full Segment (6s)
├── Part 0 (200ms)   ← available immediately
├── Part 1 (200ms)
├── Part 2 (200ms)
├── ...
└── Part 29 (200ms)

Blocking Playlist Reload

Traditional HLS requires the client to poll for playlist updates. LL-HLS uses blocking reload: the client specifies which media sequence number (MSN) and part it's waiting for, and the server holds the response until that content is available.

Query parameters:

Parameter Description
_HLS_msn=N Block until media sequence N is available
_HLS_part=P Block until part P of the current segment is available
_HLS_skip=YES Return a delta playlist (omit segments the client already has)

Playlist Example

#EXTM3U
#EXT-X-VERSION:9
#EXT-X-TARGETDURATION:6
#EXT-X-SERVER-CONTROL:CAN-BLOCK-RELOAD=YES,PART-HOLD-BACK=0.6
#EXT-X-PART-INF:PART-TARGET=0.200000
#EXT-X-MEDIA-SEQUENCE:3

#EXTINF:6.000000,
3.m4s
#EXTINF:6.000000,
4.m4s
#EXT-X-PART:DURATION=0.200000,URI="5.0.m4s"
#EXT-X-PART:DURATION=0.200000,URI="5.1.m4s"
#EXT-X-PART:DURATION=0.200000,URI="5.2.m4s"
#EXT-X-PRELOAD-HINT:TYPE=PART,URI="5.3.m4s"

Delta Playlists

When the client sends _HLS_skip=YES, the server returns a playlist with EXT-X-SKIP that omits segments the client already has, reducing bandwidth usage.

URL Format

Resource URL Pattern
Playlist /{app}/{key}.m3u8
Init segment /{app}/{key}/init.mp4
Full segment /{app}/{key}/{MSN}.m4s
Partial segment /{app}/{key}/{MSN}.{part}.m4s

Blocking Reload Examples

# Wait for media sequence 5
curl "http://localhost:8080/live/stream1.m3u8?_HLS_msn=5"

# Wait for part 3 of current segment
curl "http://localhost:8080/live/stream1.m3u8?_HLS_msn=5&_HLS_part=3"

# Delta playlist
curl "http://localhost:8080/live/stream1.m3u8?_HLS_skip=YES"

Container Formats

fMP4 (Recommended)

Fragmented MP4 is the recommended container for LL-HLS:

  • Each part is a standalone moof+mdat box
  • Init segment contains codec configuration (moov box)
  • Better compression and codec support than TS

MPEG-TS

TS is supported as a fallback:

  • Set container: "ts" in configuration
  • Container aliases mpegts and mpeg-ts are normalized to ts
  • Each part is a sequence of TS packets

Player Compatibility

Native LL-HLS Support

  • Safari — Full native support on macOS and iOS
  • hls.js 1.0+ — Enable with lowLatencyMode: true

Legacy Player Fallback

Players that don't support LL-HLS (e.g., ffplay, older hls.js) will work normally:

  • They request .m3u8 without blocking parameters
  • LiveForge waits for at least 3 completed segments before serving the playlist
  • FFmpeg's HLS demuxer uses live_start_index=-3, so 3 segments ensure smooth startup
  • Playback works but at standard HLS latency (~15-30 seconds)

Latency Tuning

Setting Effect on Latency Trade-off
part_duration: 0.1 Lower latency (~1s) More HTTP requests, higher overhead
part_duration: 0.2 Balanced (~2s) Good default
part_duration: 0.5 Higher latency (~3s) Fewer requests, more stable
segment_count: 3 Shorter buffer Less resilient to network jitter
segment_count: 6 Longer buffer More resilient, higher latency

Testing LL-HLS

# Push a stream
ffmpeg -re -i input.mp4 -c copy -f flv rtmp://localhost:1935/live/test

# Play with ffplay (legacy mode, ~15s latency)
ffplay http://localhost:8080/live/test.m3u8

# Play with Safari or hls.js for true LL-HLS (~2s latency)

# Test with lf-test
go run ./tools/lf-test play --protocol llhls --url http://localhost:8080/live/test.m3u8

Architecture

Component File Role
LLHLSManager module/httpstream/llhls_manager.go Orchestrates segment production and playlist serving
LLHLSSegmenter module/httpstream/llhls_segmenter.go Splits incoming frames into partial segments
LLHLSPlaylist module/httpstream/llhls_playlist.go Generates LL-HLS playlists with blocking and delta support
handler_hls.go module/httpstream/handler_hls.go HTTP handler for playlist and segment requests

Clone this wiki locally