1// Symphonia
2// Copyright (c) 2019-2022 The Project Symphonia Developers.
3//
4// This Source Code Form is subject to the terms of the Mozilla Public
5// License, v. 2.0. If a copy of the MPL was not distributed with this
6// file, You can obtain one at https://mozilla.org/MPL/2.0/.
78//! The `units` module provides definitions for common units.
910use std::fmt;
1112/// A `TimeStamp` represents an instantenous instant in time since the start of a stream. One
13/// `TimeStamp` "tick" is equivalent to the stream's `TimeBase` in seconds.
14pub type TimeStamp = u64;
1516/// A `Duration` indicates a positive span of time.
17pub type Duration = u64;
1819/// `Time` represents a duration of time in seconds, or the number of seconds since an arbitrary
20/// epoch. `Time` is stored as an integer number of seconds plus any remaining fraction of a second
21/// as a floating point value.
22#[derive(Copy, Clone, Debug, Default, PartialEq, PartialOrd)]
23pub struct Time {
24pub seconds: u64,
25pub frac: f64,
26}
2728impl Time {
29const SECONDS_PER_MINUTE: u64 = 60;
30const SECONDS_PER_HOUR: u64 = 60 * 60;
31const NANOSECONDS_PER_SECOND: u32 = 1_000_000_000;
32const NANOSECONDS_PER_SECOND_INV: f64 = 1.0 / 1_000_000_000.0;
3334pub fn new(seconds: u64, frac: f64) -> Self {
35 Time { seconds, frac }
36 }
3738pub fn from_ss(s: u8, ns: u32) -> Option<Time> {
39if s > 59 || ns >= Time::NANOSECONDS_PER_SECOND {
40return None;
41 }
4243let seconds = u64::from(s);
44let frac = Time::NANOSECONDS_PER_SECOND_INV * f64::from(ns);
4546Some(Time { seconds, frac })
47 }
4849pub fn from_mmss(m: u8, s: u8, ns: u32) -> Option<Time> {
50if m > 59 || s > 59 || ns >= Time::NANOSECONDS_PER_SECOND {
51return None;
52 }
5354let seconds = (Time::SECONDS_PER_MINUTE * u64::from(m)) + u64::from(s);
55let frac = Time::NANOSECONDS_PER_SECOND_INV * f64::from(ns);
5657Some(Time { seconds, frac })
58 }
5960pub fn from_hhmmss(h: u32, m: u8, s: u8, ns: u32) -> Option<Time> {
61if m > 59 || s > 59 || ns >= Time::NANOSECONDS_PER_SECOND {
62return None;
63 }
6465let seconds = (Time::SECONDS_PER_HOUR * u64::from(h))
66 + (Time::SECONDS_PER_MINUTE * u64::from(m))
67 + u64::from(s);
6869let frac = Time::NANOSECONDS_PER_SECOND_INV * f64::from(ns);
7071Some(Time { seconds, frac })
72 }
73}
7475impl From<u8> for Time {
76fn from(seconds: u8) -> Self {
77 Time::new(u64::from(seconds), 0.0)
78 }
79}
8081impl From<u16> for Time {
82fn from(seconds: u16) -> Self {
83 Time::new(u64::from(seconds), 0.0)
84 }
85}
8687impl From<u32> for Time {
88fn from(seconds: u32) -> Self {
89 Time::new(u64::from(seconds), 0.0)
90 }
91}
9293impl From<u64> for Time {
94fn from(seconds: u64) -> Self {
95 Time::new(seconds, 0.0)
96 }
97}
9899impl From<f32> for Time {
100fn from(seconds: f32) -> Self {
101if seconds >= 0.0 {
102 Time::new(seconds.trunc() as u64, f64::from(seconds.fract()))
103 }
104else {
105 Time::new(0, 0.0)
106 }
107 }
108}
109110impl From<f64> for Time {
111fn from(seconds: f64) -> Self {
112if seconds >= 0.0 {
113 Time::new(seconds.trunc() as u64, seconds.fract())
114 }
115else {
116 Time::new(0, 0.0)
117 }
118 }
119}
120121impl From<std::time::Duration> for Time {
122fn from(duration: std::time::Duration) -> Self {
123 Time::new(duration.as_secs(), f64::from(duration.subsec_nanos()) / 1_000_000_000.0)
124 }
125}
126127impl From<Time> for std::time::Duration {
128fn from(time: Time) -> Self {
129 std::time::Duration::new(time.seconds, (1_000_000_000.0 * time.frac) as u32)
130 }
131}
132133/// A `TimeBase` is the conversion factor between time, expressed in seconds, and a `TimeStamp` or
134/// `Duration`.
135///
136/// In other words, a `TimeBase` is the length in seconds of one tick of a `TimeStamp` or
137/// `Duration`.
138#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
139pub struct TimeBase {
140/// The numerator.
141pub numer: u32,
142/// The denominator.
143pub denom: u32,
144}
145146impl TimeBase {
147/// Creates a new `TimeBase`. Panics if either the numerator or denominator is 0.
148pub fn new(numer: u32, denom: u32) -> Self {
149if numer == 0 || denom == 0 {
150panic!("TimeBase cannot have 0 numerator or denominator");
151 }
152153 TimeBase { numer, denom }
154 }
155156/// Accurately calculates a `Time` using the `TimeBase` and the provided `TimeStamp`. On
157 /// overflow, the seconds field of `Time` wraps.
158pub fn calc_time(&self, ts: TimeStamp) -> Time {
159assert!(self.numer > 0 && self.denom > 0, "TimeBase numerator or denominator are 0.");
160161// The dividend requires up-to 96-bits (32-bit timebase numerator * 64-bit timestamp).
162let dividend = u128::from(ts) * u128::from(self.numer);
163164// For an accurate floating point division, both the dividend and divisor must have an
165 // accurate floating point representation. A 64-bit floating point value has a mantissa of
166 // 52 bits and can therefore accurately represent a 52-bit integer. The divisor (the
167 // denominator of the timebase) is limited to 32-bits. Therefore, if the dividend
168 // requires less than 52-bits, a straight-forward floating point division can be used to
169 // calculate the time.
170if dividend < (1 << 52) {
171let seconds = (dividend as f64) / f64::from(self.denom);
172173 Time::new(seconds.trunc() as u64, seconds.fract())
174 }
175else {
176// If the dividend requires more than 52 bits, calculate the integer portion using
177 // integer arithmetic, then calculate the fractional part separately.
178let quotient = dividend / u128::from(self.denom);
179180// The remainder is the fractional portion before being divided by the divisor (the
181 // denominator). The remainder will never equal or exceed the divisor (or else the
182 // fractional part would be >= 1.0), so the remainder must fit within a u32.
183let rem = (dividend - (quotient * u128::from(self.denom))) as u32;
184185// Calculate the fractional portion. Since both the remainder and denominator are 32-bit
186 // integers now, 64-bit floating point division will provide enough accuracy.
187let frac = f64::from(rem) / f64::from(self.denom);
188189 Time::new(quotient as u64, frac)
190 }
191 }
192193/// Accurately calculates a `TimeStamp` from the given `Time` using the `TimeBase` as the
194 /// conversion factor. On overflow, the `TimeStamp` wraps.
195pub fn calc_timestamp(&self, time: Time) -> TimeStamp {
196assert!(self.numer > 0 && self.denom > 0, "TimeBase numerator or denominator are 0.");
197assert!(time.frac >= 0.0 && time.frac < 1.0, "Invalid range for Time fractional part.");
198199// The dividing factor.
200let k = 1.0 / f64::from(self.numer);
201202// Multiplying seconds by the denominator requires up-to 96-bits (32-bit timebase
203 // denominator * 64-bit timestamp).
204let product = u128::from(time.seconds) * u128::from(self.denom);
205206// Like calc_time, a 64-bit floating-point value only has 52-bits of integer precision.
207 // If the product requires more than 52-bits, split the product into upper and lower parts
208 // and multiply by k separately, before adding back together.
209let a = if product > (1 << 52) {
210// Split the 96-bit product into 48-bit halves.
211let u = ((product & !0xffff_ffff_ffff) >> 48) as u64;
212let l = ((product & 0xffff_ffff_ffff) >> 0) as u64;
213214let uk = (u as f64) * k;
215let ul = (l as f64) * k;
216217// Add the upper and lower halves.
218((uk as u64) << 48).wrapping_add(ul as u64)
219 }
220else {
221 ((product as f64) * k) as u64
222 };
223224// The fractional portion can be calculate directly using floating point arithemtic.
225let b = (k * f64::from(self.denom) * time.frac) as u64;
226227 a.wrapping_add(b)
228 }
229}
230231impl From<TimeBase> for f64 {
232fn from(timebase: TimeBase) -> Self {
233 f64::from(timebase.numer) / f64::from(timebase.denom)
234 }
235}
236237impl fmt::Display for TimeBase {
238fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
239write!(f, "{}/{}", self.numer, self.denom)
240 }
241}
242243#[cfg(test)]
244mod tests {
245use super::{Time, TimeBase};
246use std::time::Duration;
247248#[test]
249fn verify_timebase() {
250// Verify accuracy of timestamp -> time
251let tb1 = TimeBase::new(1, 320);
252253assert_eq!(tb1.calc_time(0), Time::new(0, 0.0));
254assert_eq!(tb1.calc_time(12_345), Time::new(38, 0.578125));
255assert_eq!(tb1.calc_time(0x0f_ffff_ffff_ffff), Time::new(14_073_748_835_532, 0.796875));
256assert_eq!(tb1.calc_time(0x10_0000_0000_0001), Time::new(14_073_748_835_532, 0.803125));
257assert_eq!(tb1.calc_time(u64::MAX), Time::new(57_646_075_230_342_348, 0.796875));
258259// Verify overflow wraps seconds
260let tb2 = TimeBase::new(320, 1);
261assert_eq!(tb2.calc_time(u64::MAX), Time::new(18_446_744_073_709_551_296, 0.0));
262263// Verify accuracy of time -> timestamp
264assert_eq!(tb1.calc_timestamp(Time::new(0, 0.0)), 0);
265assert_eq!(tb1.calc_timestamp(Time::new(38, 0.578125)), 12_345);
266assert_eq!(
267 tb1.calc_timestamp(Time::new(14_073_748_835_532, 0.796875)),
2680x0f_ffff_ffff_ffff
269);
270assert_eq!(
271 tb1.calc_timestamp(Time::new(14_073_748_835_532, 0.803125)),
2720x10_0000_0000_0001
273);
274assert_eq!(tb1.calc_timestamp(Time::new(57_646_075_230_342_348, 0.796875)), u64::MAX);
275 }
276277#[test]
278fn verify_duration_to_time() {
279// Verify accuracy of Duration -> Time
280let dur1 = Duration::from_secs_f64(38.578125);
281let time1 = Time::from(dur1);
282283assert_eq!(time1.seconds, 38);
284assert_eq!(time1.frac, 0.578125);
285 }
286287#[test]
288fn verify_time_to_duration() {
289// Verify accuracy of Time -> Duration
290let time1 = Time::new(38, 0.578125);
291let dur1 = Duration::from(time1);
292293let seconds = dur1.as_secs_f64();
294295assert_eq!(seconds.trunc(), 38.0);
296assert_eq!(seconds.fract(), 0.578125);
297 }
298}