Skip to main content

TimestampOrigin

Struct TimestampOrigin 

Source
pub struct TimestampOrigin { /* private fields */ }
Expand description

Shifts a packet stream so that its timeline starts at zero, taking the first packet it sees as the origin.

§What it is for

A branch attached to a Tee that is already running inherits whatever timeline the source is up to. A D3d11VideoCompositor stamps its output with a tick counter that starts when the compositor did, so a recording branch attached ten minutes in receives its first frame stamped ten minutes — and a muxer, which has no reason to disbelieve it, writes a file whose first sample is ten minutes from the start. Players show the leading gap as empty, and a thumbnailer asking for an early frame finds nothing there.

Nothing upstream can fix that for the branch: the compositor’s timeline is correct, and it is shared with every other branch. What is wrong is only the assumption that this branch’s timeline is the same one. Putting this between the encoder and the muxer makes the file start where the recording did.

§Where it goes

After the encoder, on packets, rather than in front of it on frames. Frames travel as pooled references whose slots the producer reuses, so re-stamping one means holding its pool slot for as long as the copy is downstream — a packet carries no such tie. Placed here it also shifts dts alongside pts, which is the pair a muxer actually reads.

§What the origin is

The first packet’s pts — where the recording begins to be seen, which is what has to land at zero.

With a reordering encoder that is not its dts: the first packet is decoded before it is displayed, so dts runs a reorder delay behind. Take dts as the origin and the shifted stream starts at zero for decoding but a frame or two above it for presentation — and a muxer writes that as content genuinely beginning that far in. Alone in a file nothing notices. Beside an audio track, which has no reordering and so starts at zero, the picture is simply late by the delay: about 50 ms for H.264 with two B-frames at 60 fps, and on the side of the sync window a listener notices first.

So dts is allowed to go negative for those first packets, which is what FFmpeg’s own encoders hand a muxer and what an mp4 muxer’s edit list exists to absorb. Checked against ffmpeg -c:v h264_nvenc -c:a aac, whose output starts pts 0.000 / dts -0.050 on the video track and 0.000 on the audio one, with both streams reporting a start_time of zero.

§One stream at a time

Each of these establishes its own origin from its own first packet, so two of them on the two tracks of one file agree only to within whatever separates those first packets. That has been measured rather than left as a worry: a flash and a beep recorded together through obs-rs, on four runs, landed −1.7, +5.0, +8.3 and +16.7 ms apart — scatter the width of one video frame, which is the measurement’s own resolution rather than a systematic offset.

So a shared origin is still not here, and now for a reason: it would be a different type carrying a handle, and nothing has yet been shown to need it.

§Cost

One packet copy each, to leave the Arc this was handed untouched — a sibling branch off the same Tee must not see this branch’s timeline. The same copy crate::elements::FileMuxer already makes to rescale.

Implementations§

Source§

impl TimestampOrigin

Source

pub fn new(name: impl Into<String>) -> Self

Trait Implementations§

Source§

impl Element for TimestampOrigin

Source§

fn name(&self) -> Arc<str>

Returns a cheap clone (refcount bump, not a deep copy) of this element’s name — 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

Source§

fn pp_log(&self) -> &PpLog

This element’s identity for 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

Mutable access to the same field 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>

A pre-reserved graph identity for elements that expose dynamic attachment handles. Most elements receive an ID from ChainBuilder and keep the default None implementation.
Source§

fn attach_context(&mut self, _context: &Arc<Context>)

Hands this element the pipeline it is being wired into, at the moment and for the reason 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 more
Source§

impl Sink for TimestampOrigin

Source§

fn input_contract(&self) -> InputContract

Encoded media of either kind. It reads timestamps and nothing else, so which medium the packets carry does not matter — but frames are not what it handles, and a chain that wired them here would be wrong before it ever ran.

Source§

fn consume(&mut self, buf: MediaBuffer) -> Result<()>

Processes one buffer synchronously on the caller’s thread. Read more
Source§

fn control(&mut self, msg: ControlMsg) -> Result<()>

Reacts to a 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

Returns whether calling Self::consume can make progress now. Read more
Source§

impl Source for TimestampOrigin

Source§

fn src_pads(&mut self) -> &mut [SrcPad]

Returns every output pad owned by this element. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> Filter for T
where T: Source + Sink,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more