1use crate::error::{Error, Result};
6
7const TS_MASK: u64 = (1 << 33) - 1;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
12#[cfg_attr(feature = "serde", derive(serde::Serialize))]
13pub struct Pts(pub u64);
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
17#[cfg_attr(feature = "serde", derive(serde::Serialize))]
18pub struct Dts(pub u64);
19
20impl Pts {
21 #[must_use]
23 pub const fn ticks(self) -> u64 {
24 self.0
25 }
26 #[must_use]
28 pub fn seconds(self) -> f64 {
29 self.0 as f64 / 90_000.0
30 }
31 #[must_use]
33 pub fn to_field_bytes(self) -> [u8; 5] {
34 write(self.0, 0b0010)
35 }
36}
37
38impl Dts {
39 #[must_use]
41 pub const fn ticks(self) -> u64 {
42 self.0
43 }
44 #[must_use]
46 pub fn seconds(self) -> f64 {
47 self.0 as f64 / 90_000.0
48 }
49 #[must_use]
51 pub fn to_field_bytes(self) -> [u8; 5] {
52 write(self.0, 0b0001)
53 }
54}
55
56pub(crate) fn read(b: &[u8], prefix: u8, what: &'static str) -> Result<u64> {
60 if b.len() < 5 {
61 return Err(Error::BufferTooShort {
62 need: 5,
63 have: b.len(),
64 what,
65 });
66 }
67 if (b[0] >> 4) != prefix {
68 return Err(Error::BadTimestampPrefix(what));
69 }
70 if b[0] & 0x01 == 0 || b[2] & 0x01 == 0 || b[4] & 0x01 == 0 {
71 return Err(Error::BadTimestampMarker(what));
72 }
73 let hi = u64::from((b[0] >> 1) & 0x07); let mid = (u64::from(b[1]) << 7) | u64::from(b[2] >> 1); let lo = (u64::from(b[3]) << 7) | u64::from(b[4] >> 1); Ok((hi << 30) | (mid << 15) | lo)
77}
78
79pub(crate) fn write(ts: u64, prefix: u8) -> [u8; 5] {
81 let ts = ts & TS_MASK;
82 [
83 (prefix << 4) | ((((ts >> 30) & 0x07) as u8) << 1) | 0x01,
84 ((ts >> 22) & 0xFF) as u8,
85 ((((ts >> 15) & 0x7F) as u8) << 1) | 0x01,
86 ((ts >> 7) & 0xFF) as u8,
87 (((ts & 0x7F) as u8) << 1) | 0x01,
88 ]
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94
95 #[test]
96 fn pts_round_trip_boundary_values() {
97 for ts in [0u64, 1, 90_000, 0x1_2345_6789, TS_MASK] {
98 let enc = write(ts, 0b0010);
99 assert_eq!(read(&enc, 0b0010, "pts").unwrap(), ts, "ts={ts:#x}");
100 }
101 }
102
103 #[test]
104 fn rejects_bad_prefix() {
105 let enc = write(0, 0b0011);
106 assert!(matches!(
107 read(&enc, 0b0010, "pts"),
108 Err(Error::BadTimestampPrefix(_))
109 ));
110 }
111
112 #[test]
113 fn rejects_bad_marker() {
114 let mut enc = write(0, 0b0010);
115 enc[2] &= 0xFE; assert!(matches!(
117 read(&enc, 0b0010, "pts"),
118 Err(Error::BadTimestampMarker(_))
119 ));
120 }
121
122 #[test]
123 fn seconds() {
124 assert!((Pts(90_000).seconds() - 1.0).abs() < 1e-9);
125 }
126}