Skip to main content

hls_runtime/client/
mod.rs

1//! LL-HLS playback client engine — a sans-IO Low-Latency HLS (RFC 8216bis)
2//! client (issue #717, slices 2-4; formerly the standalone `ll-hls-client`
3//! crate, folded in here as `hls-runtime`'s `client` module — see
4//! `docs/superpowers/specs/2026-07-18-multimux-hub-design.md`).
5//!
6//! [`HlsClient`] is a driveable, caller-driven state machine — the same
7//! sans-IO shape as `srt-runtime` (issue #565): the core never opens a socket
8//! or reads a clock. The caller drains [`Action`]s (what to fetch, and how),
9//! performs the IO itself, and feeds the response back in via
10//! [`HlsClient::on_playlist`] / [`HlsClient::on_resource`] /
11//! [`HlsClient::on_error`]; decoded [`Output`]s (the init segment, then
12//! ordered access units) drain out via [`HlsClient::next_output`].
13//!
14//! # Reuse, not re-description
15//!
16//! This module defines **no playlist model of its own**. Parsing is
17//! [`broadcast_hls::MediaPlaylist::parse`] (issue #717 slice 1 — the
18//! symmetric inverse of the LL-HLS origin's own `to_m3u8()` renderer, so
19//! origin and client share one wire model); demuxing a fetched CMAF part or
20//! segment into access units is [`transmux::Fmp4Demux`]. `client` holds only
21//! the client **engine**: the reload scheduler, the fetch pipeline
22//! (prefetch/byte-range/dedup), and the output ordering — see
23//! [`HlsClient`]'s docs for the full behaviour.
24//!
25//! # Zero IO in the core
26//!
27//! No `tokio`/`reqwest`/socket dependency in [`HlsClient`] itself, ever —
28//! it is driveable by hand (see the crate's `tests/origin_loop.rs` for a
29//! complete in-process example against a real
30//! `transmux::ll_hls::LlHlsSegmenter` origin) with zero IO dependencies at
31//! all (verified by the `--no-default-features` gate).
32//!
33//! # The `tokio` feature (issue #717 slice 5)
34//!
35//! Enabling the `tokio` cargo feature (NOT default) adds
36//! [`tokio_client::TokioClient`], a thin async shell (tokio + reqwest/rustls)
37//! that drives [`HlsClient`] over real HTTP — blocking-reload/preload-hint
38//! query params, byte-range `Range` headers, per-request timeouts, and
39//! retry/backoff on transient failures. See [`tokio_client`]'s module docs
40//! for the full behaviour and its `tests/glass_to_glass.rs` for a
41//! loopback-HTTP, sub-second glass-to-glass proof against a real
42//! `multimux`-served LL-HLS origin. This feature is entirely additive — the
43//! sans-IO core above is completely unaffected by it either way.
44//!
45//! # Example
46//!
47//! ```
48//! use hls_runtime::client::{Action, HlsClient};
49//!
50//! let mut client = HlsClient::new("http://origin/live/stream.m3u8");
51//! // The caller performs this GET; here we just inspect the first action.
52//! match client.poll() {
53//!     Some(Action::FetchPlaylist { url, blocking, .. }) => {
54//!         assert_eq!(url, "http://origin/live/stream.m3u8");
55//!         assert!(blocking.is_none()); // nothing fetched yet, so no LL info.
56//!     }
57//!     other => panic!("unexpected first action: {other:?}"),
58//! }
59//! ```
60
61mod action;
62mod engine;
63mod error;
64mod output;
65#[cfg(feature = "tokio")]
66pub mod tokio_client;
67mod url;
68
69pub use action::{Action, BlockingReload, ResourceId};
70pub use engine::HlsClient;
71pub use error::{Error, Result};
72pub use output::Output;
73#[cfg(feature = "tokio")]
74pub use tokio_client::{TokioClient, TokioClientConfig, TokioClientStats, TokioError};