pub struct LlHlsClient { /* private fields */ }Expand description
A driveable, sans-IO Low-Latency HLS (RFC 8216bis) playback client.
LlHlsClient never touches a socket or a clock. The caller drives it:
LlHlsClient::newseeds the firstAction::FetchPlaylist; drain it withLlHlsClient::polland perform the GET.- Feed the response back with
LlHlsClient::on_playlist(playlist) orLlHlsClient::on_resource(init/part/segment bytes) —Action::FetchResource’sidcorrelates the two. - Drain
LlHlsClient::pollagain for the next round of actions (a new reload, newly discoverable parts, a preload-hint prefetch, …) andLlHlsClient::next_outputfor newly availableOutputs.
§Behaviour
- Reload scheduling (issue #717 slice 2): once a playlist advertises
EXT-X-SERVER-CONTROL/EXT-X-PART-INFand the origin’sCAN-BLOCK-RELOADattribute isYES(transmux::hls::LowLatencyConfig::can_block_reloadistrue— not merelytransmux::hls::MediaPlaylist::low_latencybeingSome, since an origin may carry parts/PART-INF while still advertisingCAN-BLOCK-RELOAD=NO), every reload is a Blocking Playlist Reload (RFC 8216bis §6.2.5.2) naming the next not-yet-seen Partial Segment’s_HLS_msn/_HLS_part. Otherwise reloads are plain GETs paced by anAction::WaitMshint derived from#EXT-X-TARGETDURATION.EXT-X-SKIP/CAN-SKIP-UNTILPlaylist Delta Updates (RFC 8216bis §4.4.5.2) are requested once a full-playlist baseline exists, and merged back into a full view before further processing — seemerge_deltainternally. - Fetch pipeline (slice 3): the
EXT-X-PRELOAD-HINTed part is fetched ahead of its own appearance as a numberedEXT-X-PART;BYTERANGEparts are supported, including the RFC 8216bis §4.4.4.9 “omitted offset means immediately after the previous sub-range of the same resource” rule (tracked per resource URL). The Media Initialization Section (EXT-X-MAP) is fetched once and reused for every following resource until the map changes. - Dedup / coalescing: once any of a segment’s parts have been
individually fetched, that segment is never re-fetched whole — when it
later closes (
#EXTINF+URI), the client only fetches whichever of its parts (if any) are still missing, and marks the segment “delivered” once every part is accounted for (fetched, orGAP=YES). A playlist whose segments carry no parts at all (a non-LL origin) falls back to fetching the whole segment resource — the two paths never overlap for a single segment, so a part’s samples are never double-counted against its parent’s. - Output adapter (slice 4): exactly one
Output::Initprecedes anyOutput::Samples; parts/segments are demuxed viatransmux::Fmp4Demux(by concatenating the cached init bytes with the fetched resource — this crate never re-implements ISOBMFF box parsing, only reuses transmux’s), soOutput::Samplescarries real access units, not opaque container bytes.#EXT-X-DISCONTINUITYon a segment surfaces asOutput::Discontinuityimmediately before that segment’s first samples. Known limitation: an in-progress (OpenSegment) segment carries no discontinuity flag of its own (only a closedMediaSegmentdoes) — if every part of a segment was already delivered while it was still open, a discontinuity revealed only once it closes is signalled late (after those parts’ samples, not before). This is a gap in the current wire model (transmux::hls::OpenSegment), not something this crate can fix locally.
Implementations§
Source§impl LlHlsClient
impl LlHlsClient
Sourcepub fn new(playlist_url: impl Into<String>) -> Self
pub fn new(playlist_url: impl Into<String>) -> Self
Create a new client for the Media Playlist at playlist_url, seeding
the first Action::FetchPlaylist (a plain, non-blocking GET — the
client does not yet know whether the origin supports blocking reload).
Sourcepub fn playlist_url(&self) -> &str
pub fn playlist_url(&self) -> &str
The Media Playlist URL this client is following.
Sourcepub fn poll(&mut self) -> Option<Action>
pub fn poll(&mut self) -> Option<Action>
Drain the next IO Action the caller must perform, if any.
Sourcepub fn next_output(&mut self) -> Option<Output>
pub fn next_output(&mut self) -> Option<Output>
Drain the next Output event, if any.
Sourcepub fn on_playlist(&mut self, bytes: &[u8]) -> Result<()>
pub fn on_playlist(&mut self, bytes: &[u8]) -> Result<()>
Feed a freshly fetched Media Playlist response.
§Errors
Error::PlaylistNotUtf8 / Error::PlaylistParse on malformed
input.
Sourcepub fn on_resource(&mut self, id: ResourceId, bytes: &[u8]) -> Result<()>
pub fn on_resource(&mut self, id: ResourceId, bytes: &[u8]) -> Result<()>
Feed the bytes fetched for a previously requested ResourceId
(init/part/segment). Part/Segment resources delivered before the
init segment are buffered internally and demuxed once the init
arrives — the caller’s fetches may complete in any order.
§Errors
Error::UnrequestedResource if id was never requested (the
requested bookkeeping — or, for Init, init_uri — has no record
of it): a caller/driver bug, or a stale/duplicate delivery after the
client already moved past this id.
Error::Demux if transmux::Fmp4Demux rejects the concatenation of
the cached init + bytes.
Sourcepub fn on_error(&mut self, id: Option<ResourceId>)
pub fn on_error(&mut self, id: Option<ResourceId>)
Report that a previously requested ResourceId (or the playlist
itself, via None) failed. Clears the id’s “requested” bookkeeping
so the next Self::on_playlist call naturally re-requests it (no
automatic retry timer — the caller drives retry cadence).