Skip to content

HLS Server - Monibuca Large-Scale Distribution

HLS (HTTP Live Streaming) is an HTTP-based streaming protocol developed by Apple. By segmenting live streams into TS segments and generating M3U8 playlists, HLS offers excellent CDN compatibility and device coverage, making it the preferred protocol for large-scale distribution.

PropertyValue
Transport LayerHTTP (shares the engine HTTP port)
Publish❌ Not supported
Subscribe✅ Supported
Latency5-30 seconds (depends on segment duration)
Config sectionhls

The plugin is included in official binaries; configure it in config.yaml:

hls:
enable: true
segment_duration: 6
max_segments: 5
output_dir: "./hls"
write_to_disk: true
OptionTypeDefaultDescription
enablebooltrueWhether to enable the HLS plugin
segment_durationu646TS segment duration (seconds)
max_segmentsusize5Maximum number of segments retained in the playlist
output_dirstring"./hls"Segment file output directory
write_to_diskbooltrueWhether to write segments to disk
dvr_windowu320Duration-based DVR/rewind retention in seconds; 0 = off. Independent of max_segments (live tail)

With the default dvr_window: 0, behavior is unchanged: the live playlist keeps only max_segments segments.

When dvr_window (seconds) is set, completed segments are retained by accumulated duration on this node, without changing the normal live playlist. Clients select the rewind view via query string:

QueryBehavior
(absent) or _HLS_rewind=NOCurrent live tail (byte-identical to DVR-off when rewind is not requested)
_HLS_rewind=YESEXT-X-PLAYLIST-TYPE:EVENT over retained completed segments (correct MEDIA-SEQUENCE / discontinuities; LL-HLS completed PART lines when enabled)
hls:
enable: true
write_to_disk: true # required for DVR; otherwise a warning is logged and DVR is treated as off
max_segments: 5 # live tail (unchanged)
dvr_window: 120 # retain ~2 minutes beyond the live tail
# Live
GET /hls/live/show/index.m3u8
# Rewind (EVENT)
GET /hls/live/show/index.m3u8?_HLS_rewind=YES

ABR: _HLS_rewind=YES on the master propagates to each rendition media URI; each rendition remuxer retains independently.

Retained DVR segments can be atomically exported into a stable HLS VoD asset:

Terminal window
curl -X POST "http://localhost:8180/hls/api/dvr/export" \
-H "Content-Type: application/json" \
-d '{
"stream_path": "live/show",
"start": 120,
"end": 135,
"name": "show-highlight"
}'

Request fields:

FieldDescription
stream_pathSource live stream path
start / endInclusive HLS EXT-X-MEDIA-SEQUENCE range; both endpoints must still be retained in this node’s dvr_window
nameOptional asset name; omitted names are generated automatically

The response includes playlist_url, for example:

{
"status": "ok",
"playlist_url": "/hls/vod/show-highlight/index.m3u8",
"segment_count": 16,
"duration": 48.0
}

The exporter hard-links selected .ts segments into the recordings directory first, falling back to copy across filesystems. It writes index.m3u8.tmp and atomically renames it to index.m3u8; once export succeeds, live DVR eviction can no longer break the VoD asset. Playback still goes through HLS auth:

Terminal window
ffplay http://localhost:8180/hls/vod/show-highlight/index.m3u8

With the default empty abr: [] (or omitting abr), behavior stays single-rendition: one media playlist per stream path. When you configure hls.abr, Monibuca serves a multivariant master playlist for a logical master path, pointing at each rung’s media index.m3u8.

The HLS plugin only builds the master. Encode/publish each rendition as a separate child stream via the transcode plugin onpub outputs (or another publisher), then let HLS remux those paths.

hls:
enable: true
abr:
- master: live/show
renditions:
- path: live/show_720p
bandwidth: 2500000
resolution: "1280x720"
codecs: "avc1.64001f,mp4a.40.2" # optional
- path: live/show_480p
bandwidth: 1200000
resolution: "854x480"
FieldDescription
masterLogical master path used in the master URL
renditionsOrdered ladder (highest bitrate first recommended)
pathStream path for that rung’s media playlist
bandwidth#EXT-X-STREAM-INF:BANDWIDTH (bps, required, > 0)
resolutionOptional, e.g. 1280x720
codecsOptional RFC 6381 string

Canonical:

GET /hls/<master>/master.m3u8

Example: http://localhost:8180/hls/live/show/master.m3u8

When ABR is configured for that master, /hls/<master>/index.m3u8 and /hls/<master>.m3u8 also return the same multivariant playlist (compatibility shorthand). Prefer master.m3u8. Each rendition media playlist remains /hls/<rendition>/index.m3u8.

End-to-end example (RTMP → transcode → ABR)

Section titled “End-to-end example (RTMP → transcode → ABR)”
  1. Publish: ffmpeg -re -i input.mp4 -c copy -f flv rtmp://localhost:1935/live/show
  2. Transcode onpub produces live/show_720p / live/show_480p (see Transcode)
  3. Configure hls.abr as above
  4. Open the master in a player:
Terminal window
ffplay http://localhost:8180/hls/live/show/master.m3u8

Full YAML snippet:

transcode:
enable: true
onpub:
"^live/show$":
outputs:
- target: "rtmp://127.0.0.1/live/show_720p"
options:
video_codec: "libx264"
resolution: "1280x720"
video_bitrate: 2500
audio_codec: "aac"
audio_bitrate: 128
- target: "rtmp://127.0.0.1/live/show_480p"
options:
video_codec: "libx264"
resolution: "854x480"
video_bitrate: 1200
audio_codec: "aac"
audio_bitrate: 96
hls:
enable: true
abr:
- master: live/show
renditions:
- path: live/show_720p
bandwidth: 2500000
resolution: "1280x720"
codecs: "avc1.64001f,mp4a.40.2"
- path: live/show_480p
bandwidth: 1200000
resolution: "854x480"
http://host:port/hls/{streamPath}/index.m3u8

ABR master (when configured):

http://host:port/hls/{masterPath}/master.m3u8
http://host:port/hls/{streamPath}/{sequence}.ts

Examples:

http://localhost:8180/hls/live/test/index.m3u8
http://localhost:8180/hls/live/test/0.ts
Terminal window
ffplay http://localhost:8180/hls/live/test/index.m3u8
  1. Open VLC, select MediaOpen Network Stream
  2. Enter URL: http://localhost:8180/hls/live/test/index.m3u8
  3. Click Play

Use hls.js for browser playback:

<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
<video id="player" controls></video>
<script>
const video = document.getElementById('player');
if (Hls.isSupported()) {
const hls = new Hls();
hls.loadSource('http://localhost:8180/hls/live/test/index.m3u8');
hls.attachMedia(video);
hls.on(Hls.Events.MANIFEST_PARSED, () => video.play());
} else if (video.canPlayType('application/vnd.apple.mpegurl')) {
// Safari native support
video.src = 'http://localhost:8180/hls/live/test/index.m3u8';
video.play();
}
</script>

The HLS plugin provides a recording API to record streams in HLS format (M3U8 + TS files):

The recording playlist stays open while capture is active and receives EXT-X-ENDLIST only on stop. Completed recording segments are retained independently of the live max_segments window; plan and monitor disk capacity for long recordings.

Terminal window
# Start recording
curl -X POST http://localhost:8180/hls/record/start/live/test
# Stop recording
curl -X POST http://localhost:8180/hls/record/stop/live/test
# List recordings
curl http://localhost:8180/hls/record/list
# Check recording status
curl http://localhost:8180/hls/record/status/live/test
# Query historical recordings
curl http://localhost:8180/hls/record/records
  1. Stream subscription: When an HLS playlist is first requested, the plugin automatically subscribes to the corresponding stream
  2. Segment generation: Continuously reads frame data from the stream and splits it into TS segments based on segment_duration
  3. Playlist update: Automatically updates the M3U8 playlist each time a new segment is generated
  4. Sliding window: Retains the most recent max_segments segments; older segments are automatically cleaned up
  5. Storage strategy: Segments are stored in both memory and disk (depending on write_to_disk configuration)

HLS latency is primarily determined by the following factors:

Total latency ≈ segment_duration × (max_segments - 1) + player buffer

Optimization recommendations:

# Low-latency configuration
hls:
segment_duration: 2
max_segments: 3

With this configuration, the theoretical minimum latency is approximately 4-6 seconds.

Terminal window
# 1. Publish via RTMP
ffmpeg -re -i input.mp4 -c copy -f flv rtmp://localhost:1935/live/test
# 2. Subscribe via HLS
ffplay http://localhost:8180/hls/live/test/index.m3u8
# 3. You can also subscribe via other protocols simultaneously
ffplay http://localhost:8180/flv/live/test.flv