Skip to main content

media_pp\core/
pad.rs

1use std::sync::Arc;
2
3use crate::{
4    buffer::MediaBuffer,
5    control::ControlMsg,
6    element::{ElementType, Sink},
7    error::Result,
8    pp_log::{PpLog, pp_trace},
9};
10
11/// An output port an [`Element`](crate::element::Element) owns. Data only
12/// ever leaves an element through one of its src pads — there is no other
13/// way to reach downstream.
14///
15/// This is what fan-out is, in this design: an element with more than one
16/// src pad *is* a tee — there's no separate "Tee" primitive in the
17/// pad/element model itself. [`crate::elements::FileDemuxer`] is the
18/// plain form of it: one pad per container stream, chosen once at wiring
19/// time via a normal `&mut [SrcPad]`. [`crate::elements::Tee`] is the one
20/// deliberate exception to that plain shape — its pads live behind a lock
21/// instead, so a `TeeHandle` can add or remove one from a different
22/// thread than whatever is driving it; see that module for why.
23pub struct SrcPad {
24    name: String,
25    peer: Option<Box<dyn Sink>>,
26}
27
28impl SrcPad {
29    pub fn new(name: impl Into<String>) -> Self {
30        Self {
31            name: name.into(),
32            peer: None,
33        }
34    }
35
36    pub fn name(&self) -> &str {
37        &self.name
38    }
39
40    pub fn is_linked(&self) -> bool {
41        self.peer.is_some()
42    }
43
44    /// The linked sink's own identity, without touching the link itself —
45    /// lets a caller that just saw [`SrcPad::push`]/[`SrcPad::control`]
46    /// fail (e.g. [`crate::elements::Tee`], fanning out to several pads at
47    /// once) report *which* downstream element the failure actually came
48    /// from, instead of only knowing its own. `None` for an unlinked pad.
49    pub fn peer_identity(&self) -> Option<(ElementType, Arc<str>)> {
50        self.peer
51            .as_ref()
52            .map(|sink| (sink.element_type(), sink.name()))
53    }
54
55    /// Runtime half of a connection. Pipeline users connect through
56    /// [`crate::element::Context::attach`], which keeps the graph and this
57    /// peer in sync. Kept crate-visible for element-level unit tests.
58    pub(crate) fn link(&mut self, sink: Box<dyn Sink>) {
59        self.peer = Some(sink);
60    }
61
62    /// Pushes a buffer to whatever this pad is linked to. Pushing into an
63    /// unlinked pad silently drops the buffer (e.g. a demuxer stream
64    /// nobody cared to link).
65    pub fn push(&mut self, buf: MediaBuffer) -> Result<()> {
66        match &mut self.peer {
67            Some(sink) => sink.consume(buf),
68            None => Ok(()),
69        }
70    }
71
72    /// Sends a source-originated EOS with explicit pad-level trace records.
73    /// Filters are traced by the pipeline's common element wrapper; this is
74    /// for the source boundary where EOS first enters the dataflow graph.
75    pub(crate) fn push_eos(&mut self, pp_log: &PpLog) -> Result<()> {
76        if self.peer.is_none() {
77            pp_trace!(
78                pp_log: pp_log,
79                "event=eos phase=skipped pad={} reason=unlinked",
80                self.name
81            );
82            return Ok(());
83        }
84
85        pp_trace!(
86            pp_log: pp_log,
87            "event=eos phase=sending pad={}",
88            self.name
89        );
90        let result = self.push(MediaBuffer::Eos);
91        match &result {
92            Ok(()) => pp_trace!(
93                pp_log: pp_log,
94                "event=eos phase=sent pad={} outcome=ok",
95                self.name
96            ),
97            Err(error) => pp_trace!(
98                pp_log: pp_log,
99                "event=eos phase=sent pad={} outcome=error error={error}",
100                self.name
101            ),
102        }
103        result
104    }
105
106    /// Forwards a [`ControlMsg`] to whatever this pad is linked to —
107    /// mirrors [`SrcPad::push`], just for control instead of data.
108    /// Pushing into an unlinked pad is a no-op, same as `push`.
109    pub fn control(&mut self, msg: ControlMsg) -> Result<()> {
110        match &mut self.peer {
111            Some(sink) => sink.control(msg),
112            None => Ok(()),
113        }
114    }
115}