pub struct PipelineBridge { /* private fields */ }Expand description
Carries buffers from one crate::pipeline::Pipeline into another, so
the two can start, end and fail independently.
§What it is for
A pipeline is one-shot: a source that dies is not restarted, it is replaced, and replacing it means building a new pipeline. Everything downstream of it would go with it — unless the boundary falls between them. This is that boundary, in the general case.
The general case is what was missing. Crossing from one pipeline into
another was already possible through crate::elements::AudioMixer or a
video compositor, and an application whose graph meets at one of those
needs nothing here. But both of them decide what they carry: a media
kind, a format, and a rate of their own. Packets, or frames that are not
to be composited, or anything else that only needs to cross, had
nowhere to do it.
§Shape
pipeline "up" pipeline "down"
source ─ … ─ [ PipelineBridgeSink ] [ PipelineBridge ] ─ … ─ sink
└──────── one bounded queue ───────┘The downstream half is this element, driven as its pipeline’s own
SourceElement. The upstream half is a Sink from
PipelineBridgeHandle::connect, which whichever pipeline is feeding it
terminates at.
§One input at a time
Deliberately, and unlike a mixer: with no way to combine buffers, several
inputs would only interleave in whatever order they arrived. A second
PipelineBridgeHandle::connect replaces the first, which is the
reconnection path — the old Sink then refuses with
PipelineBridgeError::Superseded rather than feeding its own
replacement.
§What an empty bridge does
Nothing, and its pipeline stays alive doing it. A mixer with no input emits silence and a compositor re-emits its last picture, because each has a rate of its own; a bridge has only what it is given. So a downstream pipeline with no upstream is idle rather than finished, and picks up again when something connects.
§Ends
An input’s Eos ends the input, not the bridge — otherwise the first
disconnection would tear down the very pipeline this exists to keep
running. PipelineBridgeHandle::input_ended reports it and the next
connect starts another.
What ends the bridge is PipelineBridgeHandle::finish, which sends
Eos downstream and returns from run. A muxer down there writes its
trailer on that; stopping the pipeline instead tells it to abandon the
file. The downstream pipeline’s own finish does the same thing from the
other side — two doors into one room, which is right when the two halves
have different owners.
§Both sides can still be controlled
Each pipeline keeps its own pause, resume, finish and stop, and
they mean what they always did. Pausing the downstream one stops the
bridge emitting, which fills the queue between them, which is felt on the
feeding side as ordinary backpressure — blocking or dropping according to
PipelineBridgeOptions::policy. That is a queue behaving like a queue
rather than anything the bridge decides.
What does not cross is control itself, with one exception: see
Sink::control on the input this hands out. Seeking is refused here
outright (SourceElement::is_seekable is false) — the timeline
belongs to whatever feeds the bridge, and an application holding both
pipelines seeks the one that owns it.
§Timestamps cross unchanged
The two pipelines have their own crate::clock::Clock and
crate::playback_clock::PlaybackClock, and this does not re-time what
passes through it — it cannot, not knowing what that is. A mixer re-times
to its own tick and a compositor to its own rate; a bridge hands over the
timestamps it was given.
So downstream must be somewhere those still mean something: a muxer,
which writes what it is handed, or anything preceded by
crate::elements::TimestampOrigin to re-base them onto the clock that
is actually going to be measured against. What must not follow a bridge
unguarded is a crate::elements::Pacer — it would be pacing one
pipeline’s timestamps against another pipeline’s clock, which is a stream
released all at once or one that never arrives.
Implementations§
Source§impl PipelineBridge
impl PipelineBridge
Sourcepub fn new(
name: impl Into<String>,
options: PipelineBridgeOptions,
) -> (Self, PipelineBridgeHandle)
pub fn new( name: impl Into<String>, options: PipelineBridgeOptions, ) -> (Self, PipelineBridgeHandle)
Creates one, and the handle the feeding side connects through.
Trait Implementations§
Source§impl Element for PipelineBridge
impl Element for PipelineBridge
Source§fn name(&self) -> Arc<str> ⓘ
fn name(&self) -> Arc<str> ⓘ
crate::bus::BusEvent stores names as
Arc<str> for exactly this reason: a hot path like
crate::queue::Queue posting BusEvent::Dropped once per
overflowed buffer shouldn’t pay for a fresh heap allocation every
time it wants to report which element it is.Source§fn element_type(&self) -> ElementType
fn element_type(&self) -> ElementType
ElementType.Source§fn pp_log(&self) -> &PpLog
fn pp_log(&self) -> &PpLog
crate::bus::Bus::post — same
id/name as Element::name, just already wrapped as the
crate::pp_log::PpLog its pp_info!/pp_warn!/pp_error! macros need. A
stored private field, not built fresh per call, for the same reason
name() returns a cheap Arc<str> clone instead of a fresh String
— see its own docs.Source§fn pp_log_mut(&mut self) -> &mut PpLog
fn pp_log_mut(&mut self) -> &mut PpLog
Element::pp_log reads — used by
crate::pipeline::ChainBuilder to stamp the owning
crate::pipeline::Pipeline’s id onto every element that
passes through it, via element_pp_log. Not meant to be called
from anywhere else.Source§fn graph_id(&self) -> Option<ElementId>
fn graph_id(&self) -> Option<ElementId>
ChainBuilder and keep the default None implementation.Source§fn attach_context(&mut self, _context: &Arc<Context>)
fn attach_context(&mut self, _context: &Arc<Context>)
Element::pp_log_mut hands it the pipeline’s
identity: the clock, the playback clock and the bus are the
pipeline’s to give, not the caller’s to choose. Read moreSource§impl Source for PipelineBridge
impl Source for PipelineBridge
Source§impl SourceElement for PipelineBridge
impl SourceElement for PipelineBridge
Source§fn is_live(&self) -> bool
fn is_live(&self) -> bool
Live, because it cannot be asked for a buffer it has not been given. Whether what feeds it is live is the other pipeline’s business and not something this can see.
Source§fn is_seekable(&self) -> bool
fn is_seekable(&self) -> bool
The timeline belongs to whatever is upstream, in a pipeline this one has no authority over.
Source§fn seek(&mut self, _target: Duration) -> Result<Duration>
fn seek(&mut self, _target: Duration) -> Result<Duration>
target, an absolute position from the
start of the media (e.g. av_seek_frame for
crate::elements::FileDemuxer). Called by
crate::control::drain_control as part of handling
ControlMsg::Seek, before that message is forwarded to the
source’s own pads — so whatever’s read next comes from the new
position by the time downstream elements receive the new timeline
announcement. Buffered and stateful old-timeline data is discarded by
the preceding ControlMsg::Flush. Read moreSource§fn run(&mut self, control: &ControlReceiver, bus: &Bus) -> Result<()>
fn run(&mut self, control: &ControlReceiver, bus: &Bus) -> Result<()>
Eos (normal completion),
crate::pipeline::Pipeline::finish, or Stop (see
ControlMsg::Stop) — call crate::control::drain_control
once per loop iteration to make control responsive between
blocking reads. Read moreSource§fn on_control(&mut self, _msg: &ControlMsg)
fn on_control(&mut self, _msg: &ControlMsg)
Self::seek gets, and for the same
reason: whatever this source holds must already reflect the message by
the time downstream elements see it. Read more