pub struct Pacer { /* private fields */ }Expand description
Delays each buffer until its presentation time, so downstream sees
frames (or, upstream of a decoder, compressed packets) at real playback
speed instead of as fast as demux/decode can produce them. A Filter:
receives via Sink, waits in short interruptible sleeps inside
consume, then pushes the same buffer through its own (single) src pad.
A pending pause/seek/stop interrupts that wait so the owning worker can
process control: pause retains the in-flight buffer for resume, while
seek and stop discard it.
Normally place a crate::queue::Queue upstream so the paced waits do
not stall the demux/decoder feeding it and those stages can run ahead
into the queue. The type does not enforce that placement; without the
queue, pacing simply blocks the upstream caller on the same thread.
Every Pacer in a pipeline (one per stream — video, audio, …) measures
against the same crate::playback_clock::PlaybackClock, so they agree
on one t=0 instead of each anchoring to its own first frame. That
agreement is what keeps the picture with the sound: the offset a
container gives its streams is part of their sync, and a pacer zeroing on
its own stream would throw it away.
Which clock that is can change while it runs. A pipeline starts on the pause-aware wall clock and hands the position to an audio renderer once its endpoint is running, and a pacer follows: what it waits on is where playback has actually reached, not where a wall-clock deadline computed at the start says it should be.
Implementations§
Source§impl Pacer
impl Pacer
Sourcepub fn new(
name: impl Into<String>,
time_base: Rational,
) -> Result<Self, PacerError>
pub fn new( name: impl Into<String>, time_base: Rational, ) -> Result<Self, PacerError>
Creates a pacer using time_base to convert input PTS values to wall
time.
The clock it paces against is the pipeline’s and arrives when this is
wired into one — see Element::attach_context.
Sourcepub fn with_discontinuity_limit(
name: impl Into<String>,
time_base: Rational,
limit: Duration,
) -> Result<Self, PacerError>
pub fn with_discontinuity_limit( name: impl Into<String>, time_base: Rational, limit: Duration, ) -> Result<Self, PacerError>
The same, for a stream whose timeline can restart under it.
A file’s timestamps only ever move forward from where they began, so a buffer due far ahead is a real gap in the stream and waiting it out is the correct thing to do. A live sender is not like that: a camera that reboots, or an RTP timestamp base that wraps, hands over a timestamp with no relation to the one before it, and a pacer that believes it sleeps for as long as the jump says — a still picture, no error, and nothing to reconnect from, since as far as the pipeline is concerned it is working.
Past limit such a jump is read as a new timeline: the origin
re-anchors so the buffer that carried it is due now, and a warning
says so. Both branches of one source see the same jump and re-anchor
within a buffer of each other, so the picture keeps its sound.
Pick a limit longer than the longest gap the stream can really
have — for most cameras a second or two of nothing is already a
problem, not a pause. Shorter than the spacing between its own
frames and every ordinary wait reads as a jump, which is this pacer
no longer pacing at all.
Trait Implementations§
Source§impl Element for Pacer
impl Element for Pacer
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 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 Sink for Pacer
impl Sink for Pacer
Source§fn input_contract(&self) -> InputContract
fn input_contract(&self) -> InputContract
Pacing is a delay, not a transform: every kind is held until its own PTS comes due and then forwarded unchanged.
Source§fn consume(&mut self, buf: MediaBuffer) -> Result<()>
fn consume(&mut self, buf: MediaBuffer) -> Result<()>
Source§fn control(&mut self, msg: ControlMsg) -> Result<()>
fn control(&mut self, msg: ControlMsg) -> Result<()>
ControlMsg (pause/resume/stop) and, for anything
with a downstream of its own, forwards it on — same shape as
consume, just a separate channel from MediaBuffer so it can
reach every element (not just ones that already know how to
interpret a data buffer) and, at a crate::queue::Queue, jump
ahead of whatever data is backed up instead of waiting behind it.
No default: every Sink has to consciously decide what this means
for it, rather than silently dropping it.Source§fn ready_consume(&mut self) -> bool
fn ready_consume(&mut self) -> bool
Self::consume can make progress now. Read more