pub struct Timestamp { /* private fields */ }Expand description
A presentation timestamp, expressed as a PTS value in units of an associated Timebase.
§Equality and ordering
Comparison is value-based (same instant compares equal even across
different timebases): Timestamp(1000, 1/1000) equals
Timestamp(90_000, 1/90_000). Hash hashes the reduced-form rational
instant (pts · num, den), so equal timestamps hash the same.
Cross-timebase comparisons use 128-bit cross-multiplication — no division,
no rounding error. Same-timebase comparisons take a fast path on pts,
except under a degenerate 0/den timebase, where every PTS names instant
zero and the counts therefore say nothing about the instants: those fall
back to the cross-multiply, and all of them compare equal.
This type is the one of the three that has an Ord, and it is
Self::cmp_semantic — instants are totally ordered by when they are,
which is the only reading of “before” a timestamp has, so there is nothing
for a derived order to disagree with. SignedDuration and TimeRange
each have a second reading and therefore no Ord at all.
Implementations§
Source§impl Timestamp
impl Timestamp
Sourcepub fn parse_seconds(
text: &str,
timebase: Timebase,
rounding: Rounding,
) -> Result<Timestamp, ParseSecondsError>
pub fn parse_seconds( text: &str, timebase: Timebase, rounding: Rounding, ) -> Result<Timestamp, ParseSecondsError>
Reads decimal seconds — 12.5, -0.040, 3 — as an instant counted in
timebase, from PTS zero.
The digits are read exactly, as integers, never through a float: 0.1
is one tenth of a second, not the double nearest it. Seconds that land on
a tick of timebase are that tick under every rounding; seconds between
two ticks go where rounding says — or, under Rounding::Exact, are
refused by name, with ParseSecondsError::BetweenTicks.
use mediatime::{ParseSecondsError, Rounding, Timebase, Timestamp};
let ntsc = Timebase::NTSC_VIDEO; // 1001/30000 s per frame
// 1.001 s is 30 frames exactly; 1 s is 29.97 frames.
let at = Timestamp::parse_seconds("1.001", ntsc, Rounding::Exact);
assert_eq!(at, Ok(Timestamp::new(30, ntsc)));
let start = Timestamp::parse_seconds("1", ntsc, Rounding::Ceil);
assert_eq!(start, Ok(Timestamp::new(30, ntsc)));
assert_eq!(
Timestamp::parse_seconds("1", ntsc, Rounding::Exact),
Err(ParseSecondsError::BetweenTicks)
);The grammar is one decimal number: an optional + or -, one or more
ASCII digits, and optionally a . followed by one or more digits.
Surrounding whitespace is trimmed. There is no exponent, no digit
separator, no bare .5 or 5., and no inf or NaN.
Nor is there a clock. The H:MM:SS.mmm form Timestamp’s Display
writes is truncated to the millisecond and names no timebase, so it has
no inverse — here or on the FromStr door — and a clock a person typed
is a second grammar, sexagesimal, which a caller can turn into seconds
before it gets here.
§Errors
ParseSecondsError::NotDecimalfor text outside the grammar;ParseSecondsError::DegenerateTimebasefor atimebasewhose numerator is zero, which can count no instant;ParseSecondsError::BetweenTicksunderRounding::Exact, for seconds between two ticks that ani64both counts;ParseSecondsError::OutOfRangefor a count pasti64, or for more significant digits than an exacti128reading holds.
Source§impl Timestamp
impl Timestamp
Sourcepub const fn new(pts: i64, timebase: Timebase) -> Timestamp
pub const fn new(pts: i64, timebase: Timebase) -> Timestamp
Creates a new Timestamp with the given PTS and timebase.
Sourcepub const fn pts(&self) -> i64
pub const fn pts(&self) -> i64
Returns the presentation timestamp, in units of Self::timebase.
To obtain a StdDuration, use Self::duration_since against a reference
timestamp, or rescale via Self::rescale_to.
Sourcepub const fn with_pts(self, pts: i64) -> Timestamp
pub const fn with_pts(self, pts: i64) -> Timestamp
Set the value of the presentation timestamp.
Sourcepub const fn set_pts(&mut self, pts: i64) -> &mut Timestamp
pub const fn set_pts(&mut self, pts: i64) -> &mut Timestamp
Set the value of the presentation timestamp in place.
Sourcepub const fn rescale_to(self, target: Timebase) -> Timestamp
pub const fn rescale_to(self, target: Timebase) -> Timestamp
Returns a new Timestamp representing the same instant in a different timebase.
Converts through Timebase::saturating_rescale, so the new PTS is the
nearest tick of target (halfway cases away from zero); round-tripping
through a coarser timebase can still lose precision.
§Panics
Panics if target.num() == 0, as Timebase::saturating_rescale does:
a degenerate timebase names one instant, and cannot receive another.
Sourcepub const fn checked_rescale_with(
self,
target: Timebase,
rounding: Rounding,
) -> Option<Timestamp>
pub const fn checked_rescale_with( self, target: Timebase, rounding: Rounding, ) -> Option<Timestamp>
The same instant counted in target, rounded as rounding says, or
None where Timebase::checked_rescale_with has no answer.
The road a trim takes: a start rescaled with Rounding::Ceil lands on
the first tick of target at or after it, an end rescaled with
Rounding::Floor on the last tick at or before it, so the trimmed
stretch stays inside the one asked for. None for a PTS outside i64 in
target, and for a degenerate target, which can count no instant — the
refusals Self::rescale_to answers with a clamp and a panic.
Sourcepub const fn saturating_sub_duration(self, d: Duration) -> Timestamp
pub const fn saturating_sub_duration(self, d: Duration) -> Timestamp
Returns a new Timestamp representing this instant shifted backward
by d, in the same timebase. Saturates at i64::MIN if the subtraction
would underflow (pathological for real video).
Useful for “virtual past” seeding: e.g., initializing a warmup-filter
state to ts - min_duration so the first detected cut can fire
immediately.
§Panics
Panics if self.timebase().num() == 0, as
Timebase::saturating_duration_to_pts does: a degenerate timebase
spans no time per tick, so no tick count stands for d.
Sourcepub const fn saturating_add_duration(self, d: Duration) -> Timestamp
pub const fn saturating_add_duration(self, d: Duration) -> Timestamp
Returns a new Timestamp representing this instant shifted forward
by d, in the same timebase. Saturates at i64::MAX if the addition
would overflow (pathological for real video).
The forward twin of Self::saturating_sub_duration: use it to close a
window opened at self, e.g. ts + max_gap for the deadline a
detector will stop waiting at.
Saturating in both steps, and only the second is visible in the result:
Timebase::saturating_duration_to_pts itself saturates when d is
enormous for this timebase, so a saturated answer can mean either “the
duration did not fit” or “the sum did not”. Both say the same thing about
the instant — it is past the end of what an i64 PTS can name.
§Panics
Panics if self.timebase().num() == 0, as
Timebase::saturating_duration_to_pts does: a degenerate timebase
spans no time per tick, so no tick count stands for d.
Sourcepub const fn signed_duration_since(&self, other: &Timestamp) -> SignedDuration
pub const fn signed_duration_since(&self, other: &Timestamp) -> SignedDuration
The span from other to self, counted in self’s timebase — negative
when other is the later instant.
Point minus point is a vector, which is why this is the subtraction two
timestamps have and addition is not: an instant plus an instant names
nothing. Self::duration_since is the same difference through the
unsigned StdDuration, and refuses the direction this one reports.
Saturating in both steps: an other in a different timebase is rescaled
into self’s first, to the nearest tick, and the difference clamps at
i64::MIN/i64::MAX rather than wrapping.
Self::checked_signed_duration_since is the rung that refuses instead
of clamping.
§Panics
Panics if the timebases differ and self’s is degenerate, as
Timebase::saturating_rescale does. Two instants counted in the same
degenerate timebase are subtracted without a rescale, and without a
panic.
Sourcepub const fn checked_signed_duration_since(
&self,
other: &Timestamp,
) -> Option<SignedDuration>
pub const fn checked_signed_duration_since( &self, other: &Timestamp, ) -> Option<SignedDuration>
The span from other to self in self’s timebase, or None if it is
not an exact i64 count of its ticks.
The checked rung of Self::signed_duration_since: None where that
one clamps or panics — other outside what self’s timebase can count,
a difference outside i64, or a degenerate self timebase with a
differing other.
Sourcepub const fn checked_add_signed(self, d: SignedDuration) -> Option<Timestamp>
pub const fn checked_add_signed(self, d: SignedDuration) -> Option<Timestamp>
This instant shifted forward by the signed span d, or None if the
result is not an i64 PTS in this timebase.
A d counted in another timebase is rescaled into this one first, so
None also covers a span this timebase cannot count and a degenerate
timebase that can count none. Shifting backward is
Self::checked_sub_signed rather than a negated d, which
i64::MIN ticks has no room for.
Named as u64::checked_add_signed is, and for the same reason: the
operand is a signed offset, the receiver is not one.
Sourcepub const fn saturating_add_signed(self, d: SignedDuration) -> Timestamp
pub const fn saturating_add_signed(self, d: SignedDuration) -> Timestamp
This instant shifted forward by d, clamping at i64::MIN/i64::MAX
instead of overflowing — the saturating rung of
Self::checked_add_signed, saturating in the rescale of d as well as
in the addition.
§Panics
Panics if d is counted in a different timebase and this one is
degenerate, as Timebase::saturating_rescale does.
Sourcepub const fn checked_sub_signed(self, d: SignedDuration) -> Option<Timestamp>
pub const fn checked_sub_signed(self, d: SignedDuration) -> Option<Timestamp>
This instant shifted backward by the signed span d, or None if the
result is not an i64 PTS in this timebase.
The mirror of Self::checked_add_signed, and not a negation of d:
the most negative count has no positive twin, so subtracting it is
reachable where negating it is not.
Sourcepub const fn saturating_sub_signed(self, d: SignedDuration) -> Timestamp
pub const fn saturating_sub_signed(self, d: SignedDuration) -> Timestamp
This instant shifted backward by d, clamping at i64::MIN/i64::MAX
instead of overflowing — the saturating rung of
Self::checked_sub_signed.
§Panics
Panics if d is counted in a different timebase and this one is
degenerate, as Timebase::saturating_rescale does.
Sourcepub const fn cmp_semantic(&self, other: &Timestamp) -> Ordering
pub const fn cmp_semantic(&self, other: &Timestamp) -> Ordering
const fn form of Ord::cmp. Compares two timestamps by the instant
they represent, rescaling if timebases differ.
Uses a 128-bit cross-multiply for the mixed-timebase case; no division, so no rounding error. Same-timebase comparisons take a direct fast path, the degenerate timebase excepted — see the guard at the site.
Sourcepub const fn duration(&self) -> Option<Duration>
pub const fn duration(&self) -> Option<Duration>
Returns the StdDuration from PTS zero (in this timebase) to self, or
None if self.pts() < 0 (pre-roll / edit-list cases can produce
negative PTS, which has no StdDuration representation).
Equivalent to self.duration_since(&Timestamp::new(0, self.timebase())).
Under a degenerate 0/den timebase every PTS names instant zero, so the
answer is StdDuration::ZERO for any count, a negative one included.
Sourcepub const fn duration_since(&self, earlier: &Timestamp) -> Option<Duration>
pub const fn duration_since(&self, earlier: &Timestamp) -> Option<Duration>
Returns the elapsed StdDuration from earlier to self, or None if
earlier is after self.
Works across different timebases. Computes the exact rational difference
first using a common denominator, then truncates once when converting to
nanoseconds for the returned StdDuration.
If the result would exceed StdDuration::MAX (pathological: seconds don’t
fit in u64), saturates to StdDuration::MAX rather than wrapping.
Trait Implementations§
Source§impl<'a> Arbitrary<'a> for Timestamp
impl<'a> Arbitrary<'a> for Timestamp
Source§fn arbitrary(u: &mut Unstructured<'a>) -> Result<Timestamp, Error>
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Timestamp, Error>
Self from the given unstructured data. Read moreSource§fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self, Error>
fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self, Error>
Self from the entirety of the given
unstructured data. Read moreSource§fn size_hint(depth: usize) -> (usize, Option<usize>)
fn size_hint(depth: usize) -> (usize, Option<usize>)
Unstructured this type
needs to construct itself. Read moreSource§fn try_size_hint(
depth: usize,
) -> Result<(usize, Option<usize>), MaxRecursionReached>
fn try_size_hint( depth: usize, ) -> Result<(usize, Option<usize>), MaxRecursionReached>
Unstructured this type
needs to construct itself. Read moreimpl Copy for Timestamp
Source§impl<'de> Deserialize<'de> for Timestamp
impl<'de> Deserialize<'de> for Timestamp
Source§fn deserialize<__D>(
__deserializer: __D,
) -> Result<Timestamp, <__D as Deserializer<'de>>::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(
__deserializer: __D,
) -> Result<Timestamp, <__D as Deserializer<'de>>::Error>where
__D: Deserializer<'de>,
Source§impl Display for Timestamp
Writes the instant on the clock as H:MM:SS.mmm — 0:00:00.137 — so a log
line reads as a time instead of as a division to carry out. video-rs
prints the unreduced rational (12345/90000 secs) in the same position;
the readable form is the default here because readable log messages are
what the request this impl answers asked for, and the rational is
still one # away.
impl Display for Timestamp
Writes the instant on the clock as H:MM:SS.mmm — 0:00:00.137 — so a log
line reads as a time instead of as a division to carry out. video-rs
prints the unreduced rational (12345/90000 secs) in the same position;
the readable form is the default here because readable log messages are
what the request this impl answers asked for, and the rational is
still one # away.
Hours are unpadded and unbounded — 123:45:06.789 is a normal rendering,
not an overflow. Minutes and seconds are two digits, milliseconds three. A
negative PTS (pre-roll, or an edit list) signs the whole rendering:
-0:00:01.500.
The instant is truncated toward zero at millisecond resolution, and
deliberately not rounded the way the rescale ladder rounds (see
Timebase::checked_rescale): a clock is read as elapsed time, and must
not name an instant the stream has not reached yet. So this form is lossy
twice over: below a millisecond nothing survives, and the timebase the PTS
was counted in is not shown at all. One consequence is worth stating
outright — a PTS smaller
in magnitude than one millisecond renders 0:00:00.000 without a sign,
because the value being printed is zero and a signed zero would claim a
precision this form does not have.
{:#} is the exact form: the stored PTS beside its timebase, as
12345 @ 1/90000. So is the derived Debug. Being the exact one, {:#}
is also the form FromStr reads back; the clock is
lossy and has no inverse.
Width and alignment flags ({:>12}) are ignored, so this will not line a
log up into columns: honouring them means measuring the finished string,
and this crate has no alloc to build one in.
impl Eq for Timestamp
Source§impl FromStr for Timestamp
Parses pts @ num/den — the form Timestamp’s Display writes under
{:#}.
impl FromStr for Timestamp
Parses pts @ num/den — the form Timestamp’s Display writes under
{:#}.
Whitespace around each part is trimmed, so 12345@1/90000 parses as well
as 12345 @ 1/90000. The default {} clock is not accepted: it is
truncated to milliseconds and names no timebase, so it cannot name back
the instant it was printed from.
§Errors
Returns ParseTimestampError if the @ is missing, if the PTS is not
an i64, or if the timebase half is not one Timebase accepts.