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(now: Instant) -> Self {
24 Self {
25 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 /// Only used for deserialization
33 pub fn from_epoch(duration_since_unix_epoch: Duration) -> Self {
34 let system_now = SystemTime::now(); // Exemption: wall-clock is correct here to deserialization only
35 let instant_now = Instant::now(); // Exemption: Instant-now is correct here to deserialization only
36
37 let duration_since_approx = system_now
38 .duration_since(UNIX_EPOCH + duration_since_unix_epoch)
39 .unwrap_or_else(|_| Duration::from_secs(0));
40
41 let instant = instant_now - duration_since_approx;
42
43 Self {
44 instant,
45 duration_since_unix_epoch,
46 }
47 }
48
49 /// Converts a Unix-epoch duration back into the monotonic [`Instant`] it corresponds to.
50 pub fn instant(&self, duration_since_unix_epoch: Duration) -> Instant {
51 self.instant + duration_since_unix_epoch - self.duration_since_unix_epoch
52 }
53
54 /// The wall-clock time, as a duration since the Unix epoch, captured at construction.
55 pub fn duration_since_unix_epoch(&self) -> Duration {
56 self.duration_since_unix_epoch
57 }
58
59 /// Converts the monotonic `now` into wall-clock time as a duration since the Unix epoch.
60 pub fn unix(&self, now: Instant) -> Duration {
61 now.duration_since(self.instant)
62 .add(self.duration_since_unix_epoch)
63 }
64
65 /// Converts the monotonic `now` into a 64-bit NTP timestamp, as RTCP Sender Reports carry.
66 pub fn ntp(&self, now: Instant) -> u64 {
67 SystemInstant::unix2ntp(self.unix(now))
68 }
69
70 /// Converts a Unix-epoch duration into a 64-bit NTP timestamp.
71 ///
72 /// The result is seconds since the NTP epoch (1900-01-01) in the high 32 bits and a binary
73 /// fraction of a second in the low 32.
74 pub fn unix2ntp(duration_since_unix_epoch: Duration) -> u64 {
75 let u = duration_since_unix_epoch.as_nanos() as u64;
76
77 let mut s = u / 1_000_000_000;
78 s += 0x83AA7E80; //offset in seconds between unix epoch and ntp epoch
79 let mut f = u % 1_000_000_000;
80 f <<= 32;
81 f /= 1_000_000_000;
82 s <<= 32;
83
84 s | f
85 }
86
87 /// Converts a 64-bit NTP timestamp into a duration since the Unix epoch.
88 ///
89 /// The inverse of [`Self::unix2ntp`].
90 pub fn ntp2unix(ntp: u64) -> Duration {
91 let mut s = ntp >> 32;
92 let mut f = ntp & 0xFFFFFFFF;
93 f *= 1_000_000_000;
94 f >>= 32;
95 s -= 0x83AA7E80;
96 let u = s * 1_000_000_000 + f;
97
98 /*let duration_since_unix_epoch =*/
99 Duration::new(u / 1_000_000_000, (u % 1_000_000_000) as u32)
100 }
101}