Skip to content

kim-company/kim_hls

Repository files navigation

HLS

Hex.pm Version

HTTP Live Streaming (HLS) library implementing RFC 8216 specifications.

Installation

Add kim_hls to your dependencies in mix.exs:

def deps do
  [
    {:kim_hls, "~> 3.0"}
  ]
end

From v2.x.x to v3.x.x

This release is a major architectural update focused on a fully functional packager and stricter RFC 8216 compliance. Highlights:

  • HLS.Packager is now pure functional: operations return state + actions, and callers execute I/O.
  • GenServer-based packager APIs are removed (no start_link/1 or GenServer.call/cast usage).
  • 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-MEDIA before #EXT-X-STREAM-INF.
  • #EXT-X-MEDIA validation 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

Architecture

Core Component

  • 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.

Supporting Modules

  • 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.

Usage

Basic Packager Usage

# 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))

Storage Helpers

# 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)

Configuration Options

Key options for HLS.Packager.new/1:

  • manifest_uri (required) - URI of the master playlist
  • max_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.

Packager Edge Cases (Production)

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 beyond timing_tolerance_ms. Callers should ignore the segment and call skip_sync_point/2 before continuing.
  • Grouped recovery: skip_sync_point/2 marks the sync point as skipped for all tracks and schedules a discontinuity. The next put_segment/3 call 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/1 returns {:error, %HLS.Packager.Error{code: :discontinuity_point_missed}, state} if any track already passed the sync point.
  • Sync readiness: sync/2 returns {: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/2 returns {:error, %HLS.Packager.Error{code: :track_timing_mismatch_at_sync}, state} if timestamps are misaligned at the sync point.
  • Upload confirmation: confirm_upload/2 returns {:warning, %HLS.Packager.Error{code: :upload_id_not_found}, state} when the upload id is unknown, without changing state.
  • Sliding window cleanup: when max_segments is set, it trims old segments, bumps EXT-X-MEDIA-SEQUENCE and EXT-X-DISCONTINUITY-SEQUENCE, and deletes orphaned init sections to keep playlists consistent.

Copyright and License

Copyright 2024, KIM Keep In Mind GmbH

Licensed under the Apache License, Version 2.0

About

HTTP Live Streaming (HLS) library implementing RFC 8216 specifications.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages