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 pub fn from_field_bytes(b: &[u8; 5]) -> crate::Result<Self> {
41 Ok(Pts(read(b, 0b0010, "PTS")?))
42 }
43 pub fn from_field_bytes_with_dts(b: &[u8; 5]) -> crate::Result<Self> {
45 Ok(Pts(read(b, 0b0011, "PTS(with DTS)")?))
46 }
47}
48
49impl Dts {
50 #[must_use]
52 pub const fn ticks(self) -> u64 {
53 self.0
54 }
55 #[must_use]
57 pub fn seconds(self) -> f64 {
58 self.0 as f64 / 90_000.0
59 }
60 #[must_use]
62 pub fn to_field_bytes(self) -> [u8; 5] {
63 write(self.0, 0b0001)
64 }
65 pub fn from_field_bytes(b: &[u8; 5]) -> crate::Result<Self> {
69 Ok(Dts(read(b, 0b0001, "DTS")?))
70 }
71}
72
73pub(crate) fn read(b: &[u8], prefix: u8, what: &'static str) -> Result<u64> {
77 if b.len() < 5 {
78 return Err(Error::BufferTooShort {
79 need: 5,
80 have: b.len(),
81 what,
82 });
83 }
84 if (b[0] >> 4) != prefix {
85 return Err(Error::BadTimestampPrefix(what));
86 }
87 if b[0] & 0x01 == 0 || b[2] & 0x01 == 0 || b[4] & 0x01 == 0 {
88 return Err(Error::BadTimestampMarker(what));
89 }
90 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)
94}
95
96pub(crate) fn write(ts: u64, prefix: u8) -> [u8; 5] {
98 let ts = ts & TS_MASK;
99 [
100 (prefix << 4) | ((((ts >> 30) & 0x07) as u8) << 1) | 0x01,
101 ((ts >> 22) & 0xFF) as u8,
102 ((((ts >> 15) & 0x7F) as u8) << 1) | 0x01,
103 ((ts >> 7) & 0xFF) as u8,
104 (((ts & 0x7F) as u8) << 1) | 0x01,
105 ]
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111
112 #[test]
113 fn pts_round_trip_boundary_values() {
114 for ts in [0u64, 1, 90_000, 0x1_2345_6789, TS_MASK] {
115 let enc = write(ts, 0b0010);
116 assert_eq!(read(&enc, 0b0010, "pts").unwrap(), ts, "ts={ts:#x}");
117 }
118 }
119
120 #[test]
121 fn rejects_bad_prefix() {
122 let enc = write(0, 0b0011);
123 assert!(matches!(
124 read(&enc, 0b0010, "pts"),
125 Err(Error::BadTimestampPrefix(_))
126 ));
127 }
128
129 #[test]
130 fn rejects_bad_marker() {
131 let mut enc = write(0, 0b0010);
132 enc[2] &= 0xFE; assert!(matches!(
134 read(&enc, 0b0010, "pts"),
135 Err(Error::BadTimestampMarker(_))
136 ));
137 }
138
139 #[test]
140 fn seconds() {
141 assert!((Pts(90_000).seconds() - 1.0).abs() < 1e-9);
142 }
143
144 #[test]
148 fn pts_from_field_bytes_round_trip() {
149 for val in [0u64, 1, 90_000, 0x1_FFFF_FFFF, TS_MASK] {
150 let pts = Pts(val);
151 let bytes = pts.to_field_bytes();
152 let decoded = Pts::from_field_bytes(&bytes).unwrap();
153 assert_eq!(decoded, pts, "val={val:#x}");
154 }
155 }
156
157 #[test]
159 fn pts_from_field_bytes_with_dts_round_trip() {
160 let pts = Pts(0x1234_5678);
161 let bytes = crate::timestamp::write(pts.0, 0b0011);
163 let decoded = Pts::from_field_bytes_with_dts(&bytes).unwrap();
164 assert_eq!(decoded, pts);
165 }
166
167 #[test]
169 fn dts_from_field_bytes_round_trip() {
170 for val in [0u64, 1, 90_000, TS_MASK] {
171 let dts = Dts(val);
172 let bytes = dts.to_field_bytes();
173 let decoded = Dts::from_field_bytes(&bytes).unwrap();
174 assert_eq!(decoded, dts, "val={val:#x}");
175 }
176 }
177}