Skip to main content

media_pp/core/
element.rs

1use std::{sync::Arc, time::Duration};
2
3use crate::pp_log::PpLog;
4
5use crate::{
6    buffer::MediaBuffer,
7    bus::Bus,
8    clock::Clock,
9    control::{ControlMsg, ControlReceiver},
10    error::Result,
11    graph::{ElementId, PipelineGraph},
12    pad::SrcPad,
13    playback_clock::PlaybackClock,
14};
15
16/// Which kind of element posted a [`crate::bus::BusEvent`] — cheap to
17/// compare/match, unlike the accompanying `name: Arc<str>` (an
18/// instance-level identifier chosen by whoever constructed it, needed
19/// alongside this to tell apart e.g. two `Queue`s in the same pipeline;
20/// see [`Element::element_type`]).
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub enum ElementType {
23    FileDemuxer,
24    AppSource,
25    RtspSource,
26    TestVideoSource,
27    TestAudioSource,
28    DxgiCaptureSource,
29    PipeWireAudioCaptureSource,
30    PipeWireScreenCaptureSource,
31    WasapiCaptureSource,
32    AudioMixer,
33    SwVideoCompositor,
34    CudaVideoCompositor,
35    D3d11VideoCompositor,
36    WebRtcPeer,
37    SwDecoder,
38    CudaDecoder,
39    CudaUpload,
40    CudaDownload,
41    CudaConverter,
42    D3d12vaDecoder,
43    D3d12Upload,
44    D3d11Decoder,
45    D3d11Upload,
46    D3d11Download,
47    SwEncoder,
48    CudaEncoder,
49    D3d11NvencEncoder,
50    SwAudioEncoder,
51    AudioResampler,
52    AudioVolume,
53    Pacer,
54    VideoSynchronizer,
55    SwScaler,
56    CudaScaler,
57    D3d11Scaler,
58    Tee,
59    Queue,
60    FrameCounter,
61    PacketCounter,
62    CudaRenderer,
63    D3d12Renderer,
64    D3d11Renderer,
65    PipeWireAudioRenderer,
66    WasapiRenderer,
67    RtspSink,
68    AppSink,
69    OrtDetector,
70    HlsMuxer,
71    Mp4Muxer,
72    SegmentedMp4Muxer,
73    /// Anything outside this crate's own elements — a test double, or a
74    /// custom `Sink`/`SourceElement` implemented downstream of this
75    /// crate. Keeps this enum from needing to grow every time someone
76    /// adds their own element.
77    Other,
78}
79
80/// A node in the pipeline graph with a name. Plain identity only — says
81/// nothing about whether the node has an input, an output, both, or
82/// neither.
83pub trait Element: Send {
84    /// Returns a cheap clone (refcount bump, not a deep copy) of this
85    /// element's name — [`crate::bus::BusEvent`] stores names as
86    /// `Arc<str>` for exactly this reason: a hot path like
87    /// [`crate::queue::Queue`] posting `BusEvent::Dropped` once per
88    /// overflowed buffer shouldn't pay for a fresh heap allocation every
89    /// time it wants to report which element it is.
90    fn name(&self) -> Arc<str>;
91
92    /// See [`ElementType`].
93    fn element_type(&self) -> ElementType;
94
95    /// A pre-reserved graph identity for elements that expose dynamic
96    /// attachment handles. Most elements receive an ID from
97    /// `ChainBuilder` and keep the default `None` implementation.
98    fn graph_id(&self) -> Option<ElementId> {
99        None
100    }
101
102    /// This element's identity for [`crate::bus::Bus::post`] — same
103    /// `id`/`name` as [`Element::name`], just already wrapped as the
104    /// [`crate::pp_log::PpLog`] its `pp_info!`/`pp_warn!`/`pp_error!` macros need. A
105    /// stored private field, not built fresh per call, for the same reason
106    /// `name()` returns a cheap `Arc<str>` clone instead of a fresh `String`
107    /// — see its own docs.
108    fn pp_log(&self) -> &PpLog;
109
110    /// Mutable access to the same field [`Element::pp_log`] reads — used by
111    /// [`crate::pipeline::ChainBuilder`] to stamp the owning
112    /// [`crate::pipeline::Pipeline`]'s id onto every element that
113    /// passes through it, via [`element_pp_log`]. Not meant to be called
114    /// from anywhere else.
115    fn pp_log_mut(&mut self) -> &mut PpLog;
116}
117
118/// Builds the [`PpLog`] every element constructs for its own [`Element::pp_log`]
119/// field, and that [`crate::pipeline::ChainBuilder`]/[`crate::pipeline::Pipeline`]
120/// rebuild once they know which pipeline an element belongs to. Keeps the
121/// element type, instance name, and pipeline id as separate fields, so a log
122/// reader does not need to parse a combined display string. The pipeline id is
123/// `None` for an element that isn't wired into a `Pipeline` at all (e.g. most
124/// of this crate's own tests). Public so a custom `Element`
125/// implemented outside this crate (see [`ElementType::Other`]) can build
126/// its own `pp_log` field the same way.
127pub fn element_pp_log(element_type: ElementType, name: &str, pipeline_id: Option<&str>) -> PpLog {
128    PpLog::new(&format!("{element_type:?}"), name, pipeline_id)
129}
130
131/// Builds the [`PpLog`] used for records a [`crate::pipeline::Pipeline`]
132/// emits about itself rather than about any one element — `run` and the
133/// `topology` diagram. A pipeline is not a graph node and so has no
134/// [`ElementType`]; its instance name is its own id. Kept here next to
135/// [`element_pp_log`] so the literal element name appears exactly once.
136pub(crate) fn pipeline_pp_log(pipeline_id: &str) -> PpLog {
137    PpLog::new("Pipeline", pipeline_id, Some(pipeline_id))
138}
139
140/// Everything a [`crate::pipeline::ChainBuilder`]/[`crate::elements::Tee`]
141/// needs to wire itself into a [`crate::pipeline::Pipeline`] — bundled into
142/// one `Arc` instead of threading `bus`/`pipeline_id`/`graph`/the wall and
143/// playback clocks through separately. Built once per source by
144/// [`crate::pipeline::PipelineBuilder::add_source`] (what
145/// [`crate::pipeline::Pipeline::new`] itself calls, for its own
146/// single-source case) and handed to that source's own `wire` closure; a
147/// [`crate::elements::Tee`] keeps its own clone while it is alive, and its
148/// [`crate::elements::TeeHandle`] accesses that clone weakly so retaining
149/// the handle cannot keep the pipeline's `Bus` open after the `Tee` itself
150/// is gone.
151pub struct Context {
152    pub bus: Bus,
153    pub pipeline_id: Arc<str>,
154    pub graph: PipelineGraph,
155    pub clock: Arc<Clock>,
156    /// Shared media-position clock used to hand video scheduling from the
157    /// wall clock to an audio output master without changing pipelines.
158    pub playback_clock: Arc<PlaybackClock>,
159    /// Graph identity of the source whose wiring closure owns this context.
160    pub source_id: ElementId,
161}
162
163#[cfg(test)]
164impl Context {
165    pub(crate) fn for_test(
166        bus: Bus,
167        pipeline_id: impl Into<Arc<str>>,
168        graph: PipelineGraph,
169        source_id: ElementId,
170    ) -> Self {
171        let clock = Arc::new(Clock::new());
172        Self {
173            bus,
174            pipeline_id: pipeline_id.into(),
175            graph,
176            playback_clock: Arc::new(PlaybackClock::new(clock.clone())),
177            clock,
178            source_id,
179        }
180    }
181}
182
183/// Anything that can receive a buffer pushed from upstream — the input
184/// side of an element, or a plain terminal sink. Every `Sink` is named
185/// (via `Element`) so bus events (e.g. EOS) can identify which one they
186/// came from.
187///
188/// This is the only "connection" primitive in the pipeline. By default,
189/// consuming a buffer is a plain function call on the caller's thread —
190/// zero overhead. Thread boundaries are introduced explicitly by wrapping
191/// a `Sink` in a [`crate::queue::Queue`], not by elements spawning their
192/// own threads.
193pub trait Sink: Element {
194    fn consume(&mut self, buf: MediaBuffer) -> Result<()>;
195
196    /// Reacts to a [`ControlMsg`] (pause/resume/stop) and, for anything
197    /// with a downstream of its own, forwards it on — same shape as
198    /// `consume`, just a separate channel from `MediaBuffer` so it can
199    /// reach every element (not just ones that already know how to
200    /// interpret a data buffer) and, at a [`crate::queue::Queue`], jump
201    /// ahead of whatever data is backed up instead of waiting behind it.
202    /// No default: every `Sink` has to consciously decide what this means
203    /// for it, rather than silently dropping it.
204    fn control(&mut self, msg: ControlMsg) -> Result<()>;
205}
206
207/// An element with one or more output ports. It sends data downstream by
208/// pushing into its own `src_pads()` (e.g. `self.src_pads()[0].push(buf)`)
209/// — it's never handed a `downstream` argument from the outside. See
210/// [`SrcPad`].
211///
212/// `Source` and `Sink` are the two halves of the duality: `Sink` is "has
213/// an input", `Source` is "has an output". An element that both receives
214/// and produces (a decoder, say) implements both side by side — `Sink` to
215/// receive, `Source` to push whatever it produces into its own pad(s)
216/// from inside `consume`. There's no separate "processing element" trait
217/// or wrapper needed for that.
218pub trait Source: Element {
219    fn src_pads(&mut self) -> &mut [SrcPad];
220}
221
222/// A pure source: has output but no input. Its `run` method drives the
223/// production loop and pushes buffers into its own src pad(s) until EOS or
224/// an error. [`crate::pipeline::Pipeline::run`] normally invokes that loop
225/// on the pipeline's background source thread; a caller may also invoke a
226/// concrete implementation directly. Sources typically wrap blocking I/O
227/// reads (demuxer, file/network source).
228pub trait SourceElement: Source {
229    /// Drives this source until `Eos` (normal completion),
230    /// [`crate::pipeline::Pipeline::finish`], or `Stop` (see
231    /// [`ControlMsg::Stop`]) — call [`crate::control::drain_control`]
232    /// once per loop iteration to make `control` responsive between
233    /// blocking reads.
234    ///
235    /// `bus` is this source's own way to report a failure pushing into
236    /// one of its pads *without* treating it as fatal — post a
237    /// [`crate::bus::BusEvent::Error`] and keep going (drop that one
238    /// buffer), the same way a [`crate::queue::Queue`] handles a failing
239    /// downstream `Sink` — rather than returning `Err` and ending this
240    /// source's thread over one bad buffer. A returned `Err` is still
241    /// how genuinely fatal failures (this source can't continue at all)
242    /// reach [`crate::pipeline::Pipeline::run`], which posts it to `bus`
243    /// itself.
244    fn run(&mut self, control: &ControlReceiver, bus: &Bus) -> Result<()>;
245
246    /// Repositions this source to `target`, an absolute position from the
247    /// start of the media (e.g. `av_seek_frame` for
248    /// [`crate::elements::FileDemuxer`]). Called by
249    /// [`crate::control::drain_control`] as part of handling
250    /// [`ControlMsg::Seek`], *before* that message is forwarded to the
251    /// source's own pads — so whatever's read next comes from the new
252    /// position by the time downstream elements are told to flush for it.
253    ///
254    /// Returns where this actually landed, which is allowed to differ
255    /// from `target` — a container seek can only ever reposition to a
256    /// keyframe at or before it (landing mid-GOP would leave downstream
257    /// decoders/muxers with no reference frame to start from), so
258    /// `target` is a request, not a guarantee. `drain_control` reports
259    /// the gap between the two via [`crate::bus::BusEvent::Seeked`];
260    /// callers that need to know where playback actually resumed should
261    /// watch that instead of assuming `target` took effect verbatim.
262    fn seek(&mut self, target: Duration) -> Result<Duration>;
263}
264
265/// An element with both an input and an output — decoder, encoder,
266/// filter, thumbnail extractor, ... Just a name for "has a `Sink` to
267/// receive and a `Source` to push what it produces into"; nothing new to
268/// implement beyond those two.
269pub trait Filter: Source + Sink {}
270
271impl<T: Source + Sink> Filter for T {}