Skip to main content

kithara_stream/
hooks.rs

1use std::sync::{Arc, Mutex};
2
3use crate::{preroll::PrerollHint, source::PendingReason};
4
5/// Lightweight read-side signal fed into [`DecoderHooks::on_chunk`].
6///
7/// Mirrors the meaningful shape of `DecoderChunkOutcome` (in
8/// `kithara-decode`) without the PCM payload — hooks emit events based
9/// on byte-cursor state from the [`Timeline`](crate::Timeline), not on
10/// the raw audio frames.
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12#[non_exhaustive]
13pub enum ReaderChunkSignal {
14    /// Decoder produced a PCM chunk.
15    Chunk,
16    /// Decoder is alive but produced nothing this call (typed reason).
17    Pending(PendingReason),
18    /// Natural end of stream — no more chunks will arrive.
19    Eof,
20}
21
22/// Lightweight seek-side signal fed into [`DecoderHooks::on_seek`].
23///
24/// Mirrors the meaningful shape of `DecoderSeekOutcome` (in
25/// `kithara-decode`) for event-emission purposes only.
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27#[non_exhaustive]
28pub enum ReaderSeekSignal {
29    /// Decoder parked at the destination. `landed_byte` is the absolute
30    /// byte offset the decoder picked (granule-aligned), when the
31    /// backend exposes one. `preroll` hints at an earlier byte position
32    /// the source must keep available so the decoder can warm its MDCT
33    /// state before emitting the first user-visible chunk. HLS uses this
34    /// to delay segment eviction; random-access sources (file) ignore it.
35    Landed {
36        landed_byte: Option<u64>,
37        preroll: PrerollHint,
38    },
39    /// Seek target was past the decoder's known duration. The decoder
40    /// is now parked at EOF.
41    PastEof,
42}
43
44/// Reader-side hooks invoked by the decoder layer's `HookedDecoder`
45/// right before it forwards the inner decoder's typed outcome to the
46/// caller.
47///
48/// One call per `next_chunk` / `seek` — granularity is decoder
49/// operations, not byte-level reads.
50pub trait DecoderHooks: Send + Sync {
51    /// Called once per `next_chunk` after the inner decoder produced
52    /// an outcome.
53    fn on_chunk(&mut self, signal: ReaderChunkSignal);
54
55    /// Called once per `seek` after the inner decoder parked at the
56    /// destination (or signalled `PastEof`).
57    fn on_seek(&mut self, signal: ReaderSeekSignal);
58}
59
60/// Shared, lockable hook handle. Used so that `Source::take_reader_hooks`
61/// can hand off Clone-able ownership and the hook implementation can
62/// hold `&mut self` state behind a single lock.
63pub type SharedHooks = Arc<Mutex<dyn DecoderHooks>>;