Skip to main content

ll_hls_runtime/server/
mod.rs

1//! LL-HLS origin engine (issue #663/#717 Stage 2): the sans-IO rolling
2//! window/store, the blocking-reload + part-availability *decision* logic,
3//! and playlist rendering, moved out of `multimux` so it can be shared with
4//! any async runtime — not just tokio+axum.
5//!
6//! # Sans-IO shape
7//!
8//! Nothing here ever `.await`s or opens a socket. [`MediaStore`] is fed
9//! synchronously (`add_part`/`add_segment`/`set_init`/`set_health`) by
10//! whatever pipeline produces media; [`MediaStore::resolve_playlist`]/
11//! [`MediaStore::resolve_resource`] are poll methods returning
12//! [`PlaylistOutcome`]/[`ResourceOutcome`] — `Ready`, `WouldBlock`,
13//! `BadRequest`, or `NotFound` — never blocking the caller. The only
14//! asynchrony is [`MediaStore::listen`], which hands back a runtime-agnostic
15//! `event_listener::EventListener` (a plain `Future<Output = ()>`) that *any*
16//! executor can await or time out — not a `tokio::sync::watch`.
17//!
18//! # The caller-driven wait loop
19//!
20//! An async adapter (e.g. `multimux::output::llhls`) turns a `WouldBlock`
21//! into an actual wait like this — the same shape as `client`'s poll/step
22//! contract, mirrored on the server side:
23//!
24//! ```text
25//! loop {
26//!     let listener = store.listen(); // register BEFORE re-checking (no missed-wakeup race)
27//!     match store.resolve_playlist(track_id, query) {
28//!         PlaylistOutcome::Ready(body) => return Ready(body),
29//!         PlaylistOutcome::BadRequest => return BadRequest,
30//!         PlaylistOutcome::WouldBlock => {
31//!             // caller's own bounded timeout wraps `listener.await` here
32//!         }
33//!     }
34//! }
35//! ```
36//!
37//! The 5 s blocking-reload cap (RFC 8216bis §6.2.5.2) and the actual
38//! `.await`/`tokio::time::timeout` live entirely in the adapter — this module
39//! never assumes a clock.
40//!
41//! # `std`-only
42//!
43//! Unlike [`crate::client`], this module needs `std::sync::Mutex` (and the
44//! `event-listener` crate's `std` feature), so it is only compiled when the
45//! crate's `std` feature is enabled (the default). A caller building
46//! `--no-default-features` (e.g. an embedded playback-only client) gets
47//! [`crate::client`] but not `server`.
48
49mod engine;
50mod store;
51
52pub use engine::{
53    BlockingQuery, CachePolicy, DEFAULT_TRACK_ID, PlaylistOutcome, ResourceOutcome,
54    master_playlist_m3u8, media_playlist_m3u8,
55};
56pub use store::{HealthState, MediaStore, SegmentWindowEntry};