1use std::{
4 borrow::Borrow,
5 cmp::{Eq, Ordering, PartialEq, PartialOrd},
6 fmt::{self, Debug, Formatter},
7 ops::{Add, AddAssign, Deref, Sub, SubAssign},
8 time::Duration,
9};
10
11use crate::math::Rational;
12
13extern "C" {
14 static ffw_null_timestamp: i64;
15
16 fn ffw_rescale_q(n: i64, aq_num: i32, aq_den: i32, bq_num: i32, bq_den: i32) -> i64;
17}
18
19#[derive(Copy, Clone)]
21pub struct TimeBase {
22 inner: Rational,
23}
24
25impl TimeBase {
26 pub const MICROSECONDS: TimeBase = TimeBase::new(1, 1_000_000);
28
29 #[inline]
32 pub const fn new(num: i32, den: i32) -> Self {
33 Self {
34 inner: Rational::new(num, den),
35 }
36 }
37}
38
39impl AsRef<Rational> for TimeBase {
40 #[inline]
41 fn as_ref(&self) -> &Rational {
42 &self.inner
43 }
44}
45
46impl Borrow<Rational> for TimeBase {
47 #[inline]
48 fn borrow(&self) -> &Rational {
49 &self.inner
50 }
51}
52
53impl Deref for TimeBase {
54 type Target = Rational;
55
56 #[inline]
57 fn deref(&self) -> &Self::Target {
58 &self.inner
59 }
60}
61
62impl Debug for TimeBase {
63 #[inline]
64 fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
65 Debug::fmt(&self.inner, f)
66 }
67}
68
69impl From<Rational> for TimeBase {
70 #[inline]
71 fn from(r: Rational) -> Self {
72 Self { inner: r }
73 }
74}
75
76impl From<TimeBase> for Rational {
77 #[inline]
78 fn from(t: TimeBase) -> Self {
79 t.inner
80 }
81}
82
83#[derive(Copy, Clone)]
86pub struct Timestamp {
87 timestamp: i64,
88 time_base: TimeBase,
89}
90
91impl Timestamp {
92 #[inline]
94 pub fn null() -> Self {
95 unsafe {
96 Self {
97 timestamp: ffw_null_timestamp,
98 time_base: TimeBase::MICROSECONDS,
99 }
100 }
101 }
102
103 #[inline]
105 pub const fn new(timestamp: i64, time_base: TimeBase) -> Self {
106 Self {
107 timestamp,
108 time_base,
109 }
110 }
111
112 #[inline]
114 pub const fn from_secs(timestamp: i64) -> Self {
115 Self::new(timestamp, TimeBase::new(1, 1))
116 }
117
118 #[inline]
120 pub const fn from_millis(timestamp: i64) -> Self {
121 Self::new(timestamp, TimeBase::new(1, 1_000))
122 }
123
124 #[inline]
126 pub const fn from_micros(timestamp: i64) -> Self {
127 Self::new(timestamp, TimeBase::new(1, 1_000_000))
128 }
129
130 #[inline]
132 pub const fn from_nanos(timestamp: i64) -> Self {
133 Self::new(timestamp, TimeBase::new(1, 1_000_000_000))
134 }
135
136 #[inline]
138 pub const fn time_base(&self) -> TimeBase {
139 self.time_base
140 }
141
142 #[inline]
144 pub const fn timestamp(&self) -> i64 {
145 self.timestamp
146 }
147
148 #[inline]
150 pub const fn with_raw_timestamp(mut self, timestamp: i64) -> Self {
151 self.timestamp = timestamp;
152 self
153 }
154
155 #[inline]
158 pub fn is_null(&self) -> bool {
159 unsafe { self.timestamp == ffw_null_timestamp }
160 }
161
162 pub fn with_time_base(&self, time_base: TimeBase) -> Self {
164 let timestamp = if self.is_null() {
165 self.timestamp
166 } else {
167 unsafe {
168 ffw_rescale_q(
169 self.timestamp,
170 self.time_base.num(),
171 self.time_base.den(),
172 time_base.num(),
173 time_base.den(),
174 )
175 }
176 };
177
178 Self {
179 timestamp,
180 time_base,
181 }
182 }
183
184 pub fn as_secs(&self) -> Option<i64> {
186 if self.is_null() {
187 None
188 } else {
189 let ts = self.with_time_base(TimeBase::new(1, 1));
190
191 Some(ts.timestamp)
192 }
193 }
194
195 pub fn as_millis(&self) -> Option<i64> {
197 if self.is_null() {
198 None
199 } else {
200 let ts = self.with_time_base(TimeBase::new(1, 1_000));
201
202 Some(ts.timestamp)
203 }
204 }
205
206 pub fn as_micros(&self) -> Option<i64> {
208 if self.is_null() {
209 None
210 } else {
211 let ts = self.with_time_base(TimeBase::new(1, 1_000_000));
212
213 Some(ts.timestamp)
214 }
215 }
216
217 pub fn as_nanos(&self) -> Option<i64> {
219 if self.is_null() {
220 None
221 } else {
222 let ts = self.with_time_base(TimeBase::new(1, 1_000_000_000));
223
224 Some(ts.timestamp)
225 }
226 }
227
228 pub fn as_f32(&self) -> Option<f32> {
231 if self.is_null() {
232 None
233 } else {
234 Some(self.timestamp as f32 * self.time_base.num() as f32 / self.time_base.den() as f32)
235 }
236 }
237
238 pub fn as_f64(&self) -> Option<f64> {
241 if self.is_null() {
242 None
243 } else {
244 Some(self.timestamp as f64 * self.time_base.num() as f64 / self.time_base.den() as f64)
245 }
246 }
247}
248
249impl Debug for Timestamp {
250 fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
251 if let Some(millis) = self.as_millis() {
252 write!(f, "{}.{:03}s", millis / 1_000, millis % 1_000)
253 } else {
254 write!(f, "(null)")
255 }
256 }
257}
258
259impl Add<Duration> for Timestamp {
260 type Output = Timestamp;
261
262 fn add(mut self, rhs: Duration) -> Self::Output {
263 self += rhs;
264 self
265 }
266}
267
268impl AddAssign<Duration> for Timestamp {
269 fn add_assign(&mut self, rhs: Duration) {
270 if self.is_null() {
272 return;
273 }
274
275 self.timestamp += Self::from_secs(rhs.as_secs() as i64)
276 .with_time_base(self.time_base)
277 .timestamp();
278
279 self.timestamp += Self::from_nanos(rhs.subsec_nanos() as i64)
280 .with_time_base(self.time_base)
281 .timestamp();
282 }
283}
284
285impl Sub<Duration> for Timestamp {
286 type Output = Timestamp;
287
288 fn sub(mut self, rhs: Duration) -> Self::Output {
289 self -= rhs;
290 self
291 }
292}
293
294impl SubAssign<Duration> for Timestamp {
295 fn sub_assign(&mut self, rhs: Duration) {
296 if self.is_null() {
298 return;
299 }
300
301 self.timestamp -= Self::from_secs(rhs.as_secs() as i64)
302 .with_time_base(self.time_base)
303 .timestamp();
304
305 self.timestamp -= Self::from_nanos(rhs.subsec_nanos() as i64)
306 .with_time_base(self.time_base)
307 .timestamp();
308 }
309}
310
311impl Sub for Timestamp {
312 type Output = Duration;
313
314 fn sub(mut self, rhs: Self) -> Self::Output {
315 assert!(!self.is_null());
316 assert!(!rhs.is_null());
317
318 let rhs = rhs.with_time_base(self.time_base);
319
320 self.timestamp -= rhs.timestamp;
321
322 if self.timestamp < 0 {
323 panic!("out of range");
324 }
325
326 let secs = self.with_time_base(TimeBase::new(1, 1));
327
328 self.timestamp -= secs.with_time_base(self.time_base).timestamp();
330
331 let nanos = self.as_nanos().unwrap();
332
333 Duration::new(secs.timestamp as u64, nanos as u32)
334 }
335}
336
337impl PartialEq for Timestamp {
338 fn eq(&self, other: &Timestamp) -> bool {
339 let a = self.as_micros();
340 let b = other.as_micros();
341
342 a == b
343 }
344}
345
346impl Eq for Timestamp {}
347
348impl PartialOrd for Timestamp {
349 fn partial_cmp(&self, other: &Timestamp) -> Option<Ordering> {
350 if let Some(a) = self.as_micros() {
351 if let Some(b) = other.as_micros() {
352 return a.partial_cmp(&b);
353 }
354 }
355
356 None
357 }
358}
359
360#[cfg(test)]
361mod tests {
362 use std::time::Duration;
363
364 use super::{TimeBase, Timestamp};
365
366 #[test]
367 fn test_duration_add() {
368 let mut ts = Timestamp::new(333, TimeBase::new(1, 90_000));
369
370 ts += Duration::from_millis(100);
371
372 assert_eq!(ts.timestamp, 9333);
373 }
374
375 #[test]
376 fn test_duration_sub() {
377 let mut ts = Timestamp::new(333, TimeBase::new(1, 90_000));
378
379 ts -= Duration::from_millis(50);
380
381 assert_eq!(ts.timestamp, -4167);
382 }
383
384 #[test]
385 fn test_timestamp_sub() {
386 let a = Timestamp::new(333, TimeBase::new(1, 90_000));
387 let b = Timestamp::new(79, TimeBase::new(1, 50_000));
388
389 let delta = a - b;
390
391 assert_eq!(delta.as_secs(), 0);
392 assert_eq!(delta.subsec_nanos(), 2_122_222);
393 }
394
395 #[test]
396 fn test_comparisons() {
397 let a = Timestamp::from_secs(1);
398 let b = Timestamp::from_millis(1_000);
399
400 assert_eq!(a, b);
401
402 let a = Timestamp::from_secs(1);
403 let b = Timestamp::from_millis(1_001);
404
405 assert_ne!(a, b);
406 assert!(a < b);
407
408 let a = Timestamp::from_secs(1);
409 let b = Timestamp::from_micros(1_000_001);
410
411 assert_ne!(a, b);
412 assert!(a < b);
413
414 let a = Timestamp::from_secs(1);
416 let b = Timestamp::from_nanos(1_000_000_001);
417
418 assert_eq!(a, b);
419 }
420}