-
Notifications
You must be signed in to change notification settings - Fork 5
LLHLS
English | 中文
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.
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,
.m3u8requests serve the LL-HLS playlist. Regular HLS and LL-HLS are mutually exclusive — setllhls.enabled: truefor LL-HLS, orfalsefor regular HLS.
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)
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) |
#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"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.
| 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 |
# 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"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
TS is supported as a fallback:
- Set
container: "ts"in configuration - Container aliases
mpegtsandmpeg-tsare normalized tots - Each part is a sequence of TS packets
- Safari — Full native support on macOS and iOS
-
hls.js 1.0+ — Enable with
lowLatencyMode: true
Players that don't support LL-HLS (e.g., ffplay, older hls.js) will work normally:
- They request
.m3u8without 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)
| 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 |
# 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| 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 |