Skip to content
File

Blob: firmware/vendor/str0m/src/rtp/mtime.rs

rust451 lines
1use std::cmp::Ordering;
2use std::fmt::Display;
3use std::num::NonZeroU32;
4use std::ops::{Add, AddAssign, Sub, SubAssign};
5use std::str::FromStr;
6use std::time::Duration;
7use std::time::Instant;
8 
9use serde::{Deserialize, Serialize};
10 
11/// Media timeline frequency as represented by a non-zero unsigned integer.
12///
13/// The frequency can be found in the negotiated payload parameters for a
14/// media writer.
15///
16/// ```no_run
17/// # use str0m::Rtc;
18/// # use str0m::media::{Mid, MediaTime};
19/// # let rtc: Rtc = todo!();
20/// #
21/// // Obtain mid from Event::MediaAdded
22/// let mid: Mid = todo!();
23///
24/// // Create a media writer for the mid.
25/// let writer = rtc.writer(mid).unwrap();
26///
27/// // Get the payload type (pt) for the wanted codec.
28/// let params = writer.payload_params().nth(0).unwrap();
29///
30/// // Obtain the frequency for the selected codec.
31/// let freq = params.spec().clock_rate;
32///
33/// let mtime = MediaTime::new(2000, freq);
34/// ```
35#[derive(Debug, Clone, Copy, Serialize)]
36pub struct Frequency(NonZeroU32);
37 
38impl Frequency {
39 /// Microseconds in a second.
40 pub const MICROS: Self = Self::make(1_000_000);
41 
42 /// 1 / 2 ^ 18 seconds in a second.
43 pub const FIXED_POINT_6_18: Frequency = Self::make(2u32.pow(18)); // 262_144
44 
45 /// Cycles in a second of a 90 kHz signal.
46 pub const NINETY_KHZ: Frequency = Self::make(90_000);
47 
48 /// Cycles in a second of a 48 kHz signal.
49 pub const FORTY_EIGHT_KHZ: Frequency = Self::make(48_000);
50 
51 /// Cycles in a second of a 16000 Hz signal.
52 ///
53 /// This is the nominal clock rate for G722. G722 samples at 16 kHz but its RTP
54 /// clock rate is 8000 Hz (RFC 3551 §4.5.2). See
55 /// <https://en.wikipedia.org/wiki/RTP_payload_formats#cite_note-55>
56 pub const SIXTEEN_KHZ: Frequency = Self::make(16_000);
57 
58 /// Cycles in a second of a 8000 Hz signal.
59 pub const EIGHT_KHZ: Frequency = Self::make(8_000);
60 
61 /// Milliseconds in a second.
62 pub const MILLIS: Frequency = Self::make(1_000);
63 
64 /// Hundredths in a second.
65 pub const HUNDREDTHS: Frequency = Self::make(100);
66 
67 /// Seconds in a second.
68 pub const SECONDS: Frequency = Self::make(1);
69 
70 /// Private unconditional non-zero constructor for use with constants.
71 const fn make(v: u32) -> Frequency {
72 match NonZeroU32::new(v) {
73 Some(v) => Self(v),
74 None => panic!("assured non-zero value is zero"),
75 }
76 }
77 
78 /// Any non-zero u32 is a valid media timeline frequency.
79 pub const fn from_nonzero(v: NonZeroU32) -> Self {
80 Self(v)
81 }
82 
83 /// Every u32 is a valid media timeline frequency except zero.
84 pub fn new(v: u32) -> Option<Self> {
85 NonZeroU32::new(v).map(Self)
86 }
87 
88 /// The frequency as a u32.
89 pub const fn get(&self) -> u32 {
90 self.0.get()
91 }
92 
93 /// The frequency as a [`std::num::NonZeroU32`] i.e. including a positivity proof.
94 pub const fn nonzero(&self) -> NonZeroU32 {
95 self.0
96 }
97}
98 
99impl PartialEq for Frequency {
100 fn eq(&self, other: &Self) -> bool {
101 self.0 == other.0
102 }
103}
104impl Eq for Frequency {}
105 
106impl From<Frequency> for NonZeroU32 {
107 fn from(value: Frequency) -> Self {
108 value.0
109 }
110}
111 
112impl From<NonZeroU32> for Frequency {
113 fn from(value: NonZeroU32) -> Self {
114 Self(value)
115 }
116}
117 
118impl FromStr for Frequency {
119 type Err = <NonZeroU32 as FromStr>::Err;
120 
121 fn from_str(s: &str) -> Result<Self, Self::Err> {
122 NonZeroU32::from_str(s).map(Self)
123 }
124}
125 
126impl Display for Frequency {
127 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
128 self.0.fmt(f)
129 }
130}
131 
132impl<'de> Deserialize<'de> for Frequency {
133 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
134 where
135 D: serde::Deserializer<'de>,
136 {
137 NonZeroU32::deserialize(deserializer).map(Self)
138 }
139}
140 
141/// Media timeline offset represented by a count (numerator) / frequency (denominator) in seconds.
142///
143/// The numerator is typically the packet time of an Rtp header. The denominator is the clock
144/// frequency of the media source (typically 90kHz for video and 48kHz for audio). The denominator
145/// is guaranteed to be a positive integer while the numerator could be positive, negative, or zero.
146///
147/// The frequency can be found in the negotiated payload parameters for a media writer.
148///
149/// ```no_run
150/// # use str0m::Rtc;
151/// # use str0m::media::{Mid, MediaTime};
152/// # let rtc: Rtc = todo!();
153/// #
154/// // Obtain mid from Event::MediaAdded
155/// let mid: Mid = todo!();
156///
157/// // Create a media writer for the mid.
158/// let writer = rtc.writer(mid).unwrap();
159///
160/// // Get the payload type (pt) for the wanted codec.
161/// let params = writer.payload_params().nth(0).unwrap();
162///
163/// // Obtain the frequency for the selected codec.
164/// let freq = params.spec().clock_rate;
165///
166/// let mtime = MediaTime::new(2000, freq);
167/// ```
168#[derive(Debug, Clone, Copy)]
169pub struct MediaTime(u64, Frequency);
170 
171impl MediaTime {
172 /// The additive identity: 0/1.
173 pub const ZERO: MediaTime = MediaTime::from_secs(0);
174 
175 /// Construct a [`MediaTime`] from a guaranteed non-zero [`Frequency`].
176 pub const fn new(numer: u64, denom: Frequency) -> Self {
177 Self(numer, denom)
178 }
179 
180 /// The numerator of the offset time.
181 #[inline(always)]
182 pub const fn numer(&self) -> u64 {
183 self.0
184 }
185 
186 /// The denominator of the offset time.
187 #[inline(always)]
188 pub const fn denom(&self) -> u32 {
189 self.1.get()
190 }
191 
192 /// The [`Frequency`] of the offset time.
193 #[inline(always)]
194 pub const fn frequency(&self) -> Frequency {
195 self.1
196 }
197 
198 /// Convenience constructor for numbers of microseconds (v/1_000_000).
199 #[inline(always)]
200 pub const fn from_micros(v: u64) -> MediaTime {
201 MediaTime(v, Frequency::MICROS)
202 }
203 
204 /// Convenience constructor for numbers of 24-bit 6.18 fixed point units (v/2^18).
205 #[inline(always)]
206 pub const fn from_fixed_point_6_18(v: u64) -> MediaTime {
207 MediaTime(v, Frequency::FIXED_POINT_6_18)
208 }
209 
210 /// Convenience constructor for numbers of 90kHz units (v/90_000).
211 #[inline(always)]
212 pub const fn from_90khz(v: u64) -> MediaTime {
213 MediaTime(v, Frequency::NINETY_KHZ)
214 }
215 
216 /// Convenience constructor for numbers of milliseconds (v/1000).
217 #[inline(always)]
218 pub const fn from_millis(v: u64) -> MediaTime {
219 MediaTime(v, Frequency::MILLIS)
220 }
221 
222 /// Convenience constructor for numbers of hundredths of seconds (v/100).
223 #[inline(always)]
224 pub const fn from_hundredths(v: u64) -> MediaTime {
225 MediaTime(v, Frequency::HUNDREDTHS)
226 }
227 
228 /// Convenience constructor for numbers of seconds (v/1).
229 #[inline(always)]
230 pub const fn from_secs(v: u64) -> MediaTime {
231 MediaTime(v, Frequency::SECONDS)
232 }
233 
234 /// Convenience constructor for floating point fractions of seconds as microsecond units.
235 #[inline(always)]
236 pub fn from_seconds(v: impl Into<f64>) -> MediaTime {
237 Self::from_micros((v.into() * 1_000_000.0_f64) as u64)
238 }
239 
240 /// A floating point fraction second representation.
241 #[inline(always)]
242 pub fn as_seconds(&self) -> f64 {
243 let denom: f64 = self.1.get().into();
244 self.0 as f64 / denom
245 }
246 
247 /// A microsecond representation.
248 pub const fn as_micros(&self) -> u64 {
249 self.rebase(Frequency::MICROS).numer()
250 }
251 
252 /// Predicate for checking that the numerator is 0.
253 #[inline(always)]
254 pub const fn is_zero(&self) -> bool {
255 self.0 == 0
256 }
257 
258 /// Convert this offset time to have a different denominator
259 /// (frequency). This conversion may lose precision and, after
260 /// arithmetic operations with other times of higher frequencies,
261 /// may have a higher frequency.
262 #[inline(always)]
263 pub const fn rebase(self, denom: Frequency) -> MediaTime {
264 if denom.get() == self.1.get() {
265 self
266 } else {
267 let numer = self.0 as i128 * denom.get() as i128 / self.1.get() as i128;
268 MediaTime::new(numer as u64, denom)
269 }
270 }
271 
272 #[inline(always)]
273 fn same_base(t0: MediaTime, t1: MediaTime) -> (MediaTime, MediaTime) {
274 let max = Frequency(t0.1.0.max(t1.1.0));
275 (t0.rebase(max), t1.rebase(max))
276 }
277 
278 /// Checked `MediaTime` subtraction. Returns [`None`] if the result
279 /// would be negative.
280 ///
281 /// # Examples
282 ///
283 /// Basic usage:
284 /// ```
285 /// use str0m::media::MediaTime;
286 ///
287 /// assert_eq!(
288 /// MediaTime::from_micros(10).checked_sub(MediaTime::from_micros(5)),
289 /// Some(MediaTime::from_micros(5))
290 /// );
291 /// assert_eq!(
292 /// MediaTime::from_micros(5).checked_sub(MediaTime::from_micros(10)),
293 /// None
294 /// );
295 /// ```
296 #[inline]
297 pub fn checked_sub(self, rhs: MediaTime) -> Option<MediaTime> {
298 let (lhs, rhs) = MediaTime::same_base(self, rhs);
299 if lhs.0 < rhs.0 {
300 None
301 } else {
302 Some(MediaTime::new(lhs.0 - rhs.0, lhs.1))
303 }
304 }
305 
306 /// Saturating `MediaTime` subtraction. Returns [`MediaTime::ZERO`] if the result
307 /// would be negative.
308 ///
309 /// # Examples
310 ///
311 /// Basic usage:
312 /// ```
313 /// use str0m::media::MediaTime;
314 ///
315 /// assert_eq!(
316 /// MediaTime::from_micros(10).saturating_sub(MediaTime::from_micros(5)),
317 /// MediaTime::from_micros(5)
318 /// );
319 /// assert_eq!(
320 /// MediaTime::from_micros(5).saturating_sub(MediaTime::from_micros(10)),
321 /// MediaTime::ZERO
322 /// );
323 /// ```
324 #[inline]
325 pub fn saturating_sub(self, rhs: MediaTime) -> MediaTime {
326 match self.checked_sub(rhs) {
327 Some(v) => v,
328 None => MediaTime::ZERO,
329 }
330 }
331}
332 
333impl PartialEq for MediaTime {
334 #[inline(always)]
335 fn eq(&self, other: &Self) -> bool {
336 let (t0, t1) = MediaTime::same_base(*self, *other);
337 t0.0 == t1.0
338 }
339}
340impl Eq for MediaTime {}
341 
342#[cfg(feature = "drv")]
343crate::drv_identity_copy!(Frequency, MediaTime);
344 
345impl PartialOrd for MediaTime {
346 #[inline(always)]
347 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
348 Some(self.cmp(other))
349 }
350}
351 
352impl Ord for MediaTime {
353 #[inline(always)]
354 fn cmp(&self, other: &Self) -> Ordering {
355 let (t0, t1) = MediaTime::same_base(*self, *other);
356 if t0 == t1 {
357 Ordering::Equal
358 } else if t0.0 < t1.0 {
359 Ordering::Less
360 } else {
361 Ordering::Greater
362 }
363 }
364}
365 
366impl Add for MediaTime {
367 type Output = MediaTime;
368 
369 #[inline(always)]
370 fn add(self, rhs: Self) -> Self::Output {
371 let (t0, t1) = MediaTime::same_base(self, rhs);
372 MediaTime::new(t0.0 + t1.0, t0.1)
373 }
374}
375 
376impl Sub for MediaTime {
377 type Output = MediaTime;
378 
379 fn sub(self, rhs: Self) -> Self::Output {
380 self.checked_sub(rhs)
381 .expect("overflow when subtracting MediaTime")
382 }
383}
384 
385impl SubAssign for MediaTime {
386 fn sub_assign(&mut self, rhs: Self) {
387 *self = *self - rhs;
388 }
389}
390 
391impl Sub<MediaTime> for Instant {
392 type Output = Instant;
393 
394 fn sub(self, rhs: MediaTime) -> Self::Output {
395 self - Duration::from(rhs)
396 }
397}
398 
399impl SubAssign<MediaTime> for Instant {
400 fn sub_assign(&mut self, rhs: MediaTime) {
401 *self = *self - rhs;
402 }
403}
404 
405impl AddAssign for MediaTime {
406 fn add_assign(&mut self, rhs: Self) {
407 *self = *self + rhs;
408 }
409}
410 
411impl Add<MediaTime> for Instant {
412 type Output = Instant;
413 
414 fn add(self, rhs: MediaTime) -> Self::Output {
415 self + Duration::from(rhs)
416 }
417}
418 
419impl AddAssign<MediaTime> for Instant {
420 fn add_assign(&mut self, rhs: MediaTime) {
421 *self = *self + rhs;
422 }
423}
424 
425impl From<MediaTime> for Duration {
426 fn from(value: MediaTime) -> Self {
427 Duration::from_micros(value.rebase(Frequency::MICROS).numer())
428 }
429}
430 
431impl From<Duration> for MediaTime {
432 fn from(v: Duration) -> Self {
433 MediaTime::new(v.as_micros() as u64, Frequency::MICROS)
434 }
435}
436 
437#[cfg(test)]
438mod test {
439 use super::*;
440 
441 #[test]
442 fn ts_rebase() {
443 let t1 = MediaTime::from_seconds(10.0);
444 let t2 = t1.rebase(Frequency::NINETY_KHZ);
445 assert_eq!(t2.numer(), 90_000 * 10);
446 assert_eq!(t2.denom(), 90_000);
447 
448 println!("{}", (10.0234_f64).fract());
449 }
450}