rtc_shared/time.rs
1//! Monotonic, Unix and NTP time.
2//!
3//! Protocol logic measures time with a monotonic [`Instant`](std::time::Instant), which cannot go
4//! backwards but has no absolute meaning. RTCP timestamps need the opposite: wall-clock time in
5//! NTP format. [`SystemInstant`](crate::time::SystemInstant) captures both once, so either can be derived from the other later
6//! without re-reading a clock that may have been adjusted in between.
7use std::ops::Add;
8use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
9
10#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
11/// A monotonic [`Instant`] paired with the wall-clock time it was taken at.
12///
13/// Sans-I/O protocol code measures time with a monotonic [`Instant`], but RTCP timestamps
14/// and NTP fields need wall-clock time. Capturing both once lets either be derived from the
15/// other later without re-reading the (non-monotonic) system clock.
16pub struct SystemInstant {
17 instant: Instant,
18 duration_since_unix_epoch: Duration,
19}
20
21impl SystemInstant {
22 /// Captures the current monotonic instant together with the current wall-clock time.
23 pub fn now() -> Self {
24 Self {
25 instant: Instant::now(),
26 duration_since_unix_epoch: SystemTime::now()
27 .duration_since(UNIX_EPOCH)
28 .unwrap_or_else(|_| Duration::from_secs(0)),
29 }
30 }
31
32 /// Converts a Unix-epoch duration back into the monotonic [`Instant`] it corresponds to.
33 pub fn instant(&self, duration_since_unix_epoch: Duration) -> Instant {
34 self.instant + duration_since_unix_epoch - self.duration_since_unix_epoch
35 }
36
37 /// The wall-clock time, as a duration since the Unix epoch, captured at construction.
38 pub fn duration_since_unix_epoch(&self) -> Duration {
39 self.duration_since_unix_epoch
40 }
41
42 /// Converts the monotonic `now` into wall-clock time as a duration since the Unix epoch.
43 pub fn unix(&self, now: Instant) -> Duration {
44 now.duration_since(self.instant)
45 .add(self.duration_since_unix_epoch)
46 }
47
48 /// Converts the monotonic `now` into a 64-bit NTP timestamp, as RTCP Sender Reports carry.
49 pub fn ntp(&self, now: Instant) -> u64 {
50 SystemInstant::unix2ntp(self.unix(now))
51 }
52
53 /// Converts a Unix-epoch duration into a 64-bit NTP timestamp.
54 ///
55 /// The result is seconds since the NTP epoch (1900-01-01) in the high 32 bits and a binary
56 /// fraction of a second in the low 32.
57 pub fn unix2ntp(duration_since_unix_epoch: Duration) -> u64 {
58 let u = duration_since_unix_epoch.as_nanos() as u64;
59
60 let mut s = u / 1_000_000_000;
61 s += 0x83AA7E80; //offset in seconds between unix epoch and ntp epoch
62 let mut f = u % 1_000_000_000;
63 f <<= 32;
64 f /= 1_000_000_000;
65 s <<= 32;
66
67 s | f
68 }
69
70 /// Converts a 64-bit NTP timestamp into a duration since the Unix epoch.
71 ///
72 /// The inverse of [`Self::unix2ntp`].
73 pub fn ntp2unix(ntp: u64) -> Duration {
74 let mut s = ntp >> 32;
75 let mut f = ntp & 0xFFFFFFFF;
76 f *= 1_000_000_000;
77 f >>= 32;
78 s -= 0x83AA7E80;
79 let u = s * 1_000_000_000 + f;
80
81 /*let duration_since_unix_epoch =*/
82 Duration::new(u / 1_000_000_000, (u % 1_000_000_000) as u32)
83 }
84}