ac_ffmpeg/
time.rs

1//! Time base aware timestamps.
2
3use 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/// A rational time base (e.g. 1/1000 is a millisecond time base).
20#[derive(Copy, Clone)]
21pub struct TimeBase {
22    inner: Rational,
23}
24
25impl TimeBase {
26    /// A microseconds time base.
27    pub const MICROSECONDS: TimeBase = TimeBase::new(1, 1_000_000);
28
29    /// Create a new time base as a rational number with a given numerator and
30    /// denominator.
31    #[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/// A timestamp supporting various time bases. All comparisons are done within
84/// microsecond time base.
85#[derive(Copy, Clone)]
86pub struct Timestamp {
87    timestamp: i64,
88    time_base: TimeBase,
89}
90
91impl Timestamp {
92    /// Create a "null" timestamp (i.e. a timestamp set to the AV_NOPTS_VALUE).
93    #[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    /// Create a new timestamp with a given time base.
104    #[inline]
105    pub const fn new(timestamp: i64, time_base: TimeBase) -> Self {
106        Self {
107            timestamp,
108            time_base,
109        }
110    }
111
112    /// Create a new timestamp with 1/1 time base.
113    #[inline]
114    pub const fn from_secs(timestamp: i64) -> Self {
115        Self::new(timestamp, TimeBase::new(1, 1))
116    }
117
118    /// Create a new timestamp with 1/1_000 time base.
119    #[inline]
120    pub const fn from_millis(timestamp: i64) -> Self {
121        Self::new(timestamp, TimeBase::new(1, 1_000))
122    }
123
124    /// Create a new timestamp with 1/1_000_000 time base.
125    #[inline]
126    pub const fn from_micros(timestamp: i64) -> Self {
127        Self::new(timestamp, TimeBase::new(1, 1_000_000))
128    }
129
130    /// Create a new timestamp with 1/1_000_000_000 time base.
131    #[inline]
132    pub const fn from_nanos(timestamp: i64) -> Self {
133        Self::new(timestamp, TimeBase::new(1, 1_000_000_000))
134    }
135
136    /// Get the time base.
137    #[inline]
138    pub const fn time_base(&self) -> TimeBase {
139        self.time_base
140    }
141
142    /// Get the raw timestamp value.
143    #[inline]
144    pub const fn timestamp(&self) -> i64 {
145        self.timestamp
146    }
147
148    /// Set the timestamp with the current time base.
149    #[inline]
150    pub const fn with_raw_timestamp(mut self, timestamp: i64) -> Self {
151        self.timestamp = timestamp;
152        self
153    }
154
155    /// Check if this is the "null" timestamp (i.e. it is equal to the
156    /// AV_NOPTS_VALUE).
157    #[inline]
158    pub fn is_null(&self) -> bool {
159        unsafe { self.timestamp == ffw_null_timestamp }
160    }
161
162    /// Rescale the timestamp value to a given time base.
163    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    /// Get the timestamp value in seconds.
185    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    /// Get the timestamp value in milliseconds.
196    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    /// Get the timestamp value in microseconds.
207    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    /// Get the timestamp value in nanoseconds.
218    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    /// Get the timestamp value as a floating point number with 32-bit
229    /// precision.
230    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    /// Get the timestamp value as a floating point number with 64-bit
239    /// precision.
240    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        // do not add anything to null timestamps
271        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        // do not subtract anything from null timestamps
297        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        // calculate the remainder
329        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        // this is outside of the comparison scale
415        let a = Timestamp::from_secs(1);
416        let b = Timestamp::from_nanos(1_000_000_001);
417
418        assert_eq!(a, b);
419    }
420}