HTTP Live Streaming (HLS) library implementing RFC 8216 specifications.
Add kim_hls to your dependencies in mix.exs:
def deps do
[
{:kim_hls, "~> 3.0"}
]
endThis release is a major architectural update focused on a fully functional packager and stricter RFC 8216 compliance. Highlights:
HLS.Packageris now pure functional: operations return state + actions, and callers execute I/O.- GenServer-based packager APIs are removed (no
start_link/1orGenServer.call/castusage). - Segment upload flow is explicit (
put_segment+confirm_upload), enabling caller-controlled concurrency. - Discontinuities reset program date-time to a new shared reference.
- Master playlist output is reordered to place
#EXT-X-MEDIAbefore#EXT-X-STREAM-INF. #EXT-X-MEDIAvalidation tightened (required attributes, CLOSED-CAPTIONS URI rules).- Master and media playlist tests now assert RFC tag ordering and required attributes.
- Media target duration remains fixed to configured value (no per-playlist drift).
- Tracker has been removed
- HLS.Packager - Pure functional module for HLS playlist generation and segment handling. Returns actions for the caller to execute, providing explicit control over I/O operations.
- HLS.Playlist - Playlist marshaling/unmarshaling with Master and Media playlist modules
- HLS.Playlist.Tag - Individual HLS tag implementations (EXT-X-* tags)
- HLS.Segment - Represents HLS segments with duration, URI, and optional init sections
- HLS.VariantStream & HLS.AlternativeRendition - Stream representation structures
- HLS.Storage - Protocol for get/put/delete helpers with file and Req-based implementations.
# Initialize packager state
{:ok, state} = HLS.Packager.new(
manifest_uri: URI.new!("stream.m3u8"),
max_segments: 10 # Optional: enables sliding window
)
# Add a variant stream
{state, []} = HLS.Packager.add_track(state, "video_480p",
stream: %HLS.VariantStream{
bandwidth: 800_000,
resolution: {854, 480},
codecs: ["avc1.64001e"]
},
segment_extension: ".ts",
target_segment_duration: 6.0
)
# Add segment (returns upload action)
{state, [action]} = HLS.Packager.put_segment(state, "video_480p", duration: 6.0, pts: 0)
# Caller uploads the segment via storage helper
storage = HLS.Storage.File.new(base_dir: "./output")
:ok = HLS.Storage.put(storage, action.uri, segment_data)
# Confirm upload (may return playlist write actions)
{state, actions} = HLS.Packager.confirm_upload(state, action.id)
# Execute write actions
Enum.each(actions, fn
%HLS.Packager.Action.WritePlaylist{uri: uri, content: content} ->
HLS.Storage.put(storage, uri, content)
end)
# Sync and flush to create VOD playlist
{state, actions} = HLS.Packager.flush(state)
Enum.each(actions, &execute_action(&1, storage))# File-backed storage for local output
storage = HLS.Storage.File.new(base_dir: "./output")
# Req-backed storage for HTTP(S)
req = Req.new(base_url: "https://cdn.example.com")
storage = HLS.Storage.Req.new(req)Key options for HLS.Packager.new/1:
manifest_uri(required) - URI of the master playlistmax_segments- Maximum segments to retain (enables sliding window mode with automatic cleanup)
For resuming from existing playlists, use HLS.Packager.resume/1 with loaded playlist data.
The caller must load the master and media playlists; resume trims tracks to the
last common sync point and schedules a discontinuity at the next segment.
If a referenced media playlist is missing or empty, the track is marked incomplete and
put_segment/3 returns {:error, %HLS.Packager.Error{code: :resume_track_not_ready}, state}
until the caller reconciles it via add_track/3 and put_init_section/2.
HLS.Packager enforces RFC compliance and returns errors or stall warnings
instead of emitting non-compliant playlists:
- Segment duration overruns: returns
{:error, %HLS.Packager.Error{code: :segment_duration_over_target}, state}when a segment exceeds the configured target duration. - Timing drift between segments: returns
{:error, %HLS.Packager.Error{code: :timing_drift}, state}when timestamps drift beyondtiming_tolerance_ms. Callers should ignore the segment and callskip_sync_point/2before continuing. - Grouped recovery:
skip_sync_point/2marks the sync point as skipped for all tracks and schedules a discontinuity. The nextput_segment/3call for each track at that index returns{:warning, %HLS.Packager.Error{code: :sync_point_skipped}, state}so callers can drop that segment across the group. - Discontinuity synchronization:
discontinue/1returns{:error, %HLS.Packager.Error{code: :discontinuity_point_missed}, state}if any track already passed the sync point. - Sync readiness:
sync/2returns{:warning, [%HLS.Packager.Error{code: :mandatory_track_missing_segment_at_sync}], state}when mandatory tracks are missing segments (stall without advancing playlists). - Track timing alignment:
sync/2returns{:error, %HLS.Packager.Error{code: :track_timing_mismatch_at_sync}, state}if timestamps are misaligned at the sync point. - Upload confirmation:
confirm_upload/2returns{:warning, %HLS.Packager.Error{code: :upload_id_not_found}, state}when the upload id is unknown, without changing state. - Sliding window cleanup: when
max_segmentsis set, it trims old segments, bumpsEXT-X-MEDIA-SEQUENCEandEXT-X-DISCONTINUITY-SEQUENCE, and deletes orphaned init sections to keep playlists consistent.
Copyright 2024, KIM Keep In Mind GmbH
Licensed under the Apache License, Version 2.0