Skip to main content

oaat_core/
wire.rs

1use crate::error::OaatError;
2use crate::format::AudioFormat;
3
4pub const AUDIO_HEADER_SIZE: usize = 32;
5
6bitflags::bitflags! {
7    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
8    pub struct PacketFlags: u8 {
9        const FIRST_PACKET = 0x01;
10        const LAST_PACKET = 0x02;
11        const FEC = 0x04;
12        const FORMAT_CHANGE = 0x08;
13    }
14}
15
16/// Audio packet header (32 bytes, network byte order).
17///
18/// ```text
19/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
20/// |  Ver  | Flags |    Format     |        Sequence (u16 BE)      |
21/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
22/// |                     Stream ID (u32 BE)                        |
23/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
24/// |              Presentation Timestamp (u64 BE, ns)              |
25/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
26/// |                    Sample Offset (u64 BE)                     |
27/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
28/// |     Payload Length (u16 BE)    |       Reserved (u16)         |
29/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
30/// ```
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub struct AudioPacketHeader {
33    pub version: u8,
34    pub flags: PacketFlags,
35    pub format: AudioFormat,
36    pub sequence: u16,
37    pub stream_id: u32,
38    pub pts_ns: u64,
39    pub sample_offset: u64,
40    pub payload_len: u16,
41}
42
43impl AudioPacketHeader {
44    pub const CURRENT_VERSION: u8 = 1;
45
46    pub fn encode(&self, buf: &mut [u8; AUDIO_HEADER_SIZE]) {
47        let ver_flags = (self.version << 4) | self.flags.bits();
48        buf[0] = ver_flags;
49        buf[1] = self.format.wire_id();
50        buf[2..4].copy_from_slice(&self.sequence.to_be_bytes());
51        buf[4..8].copy_from_slice(&self.stream_id.to_be_bytes());
52        buf[8..16].copy_from_slice(&self.pts_ns.to_be_bytes());
53        buf[16..24].copy_from_slice(&self.sample_offset.to_be_bytes());
54        buf[24..26].copy_from_slice(&self.payload_len.to_be_bytes());
55        buf[26..28].copy_from_slice(&0u16.to_be_bytes());
56        buf[28..32].fill(0);
57    }
58
59    pub fn decode(buf: &[u8; AUDIO_HEADER_SIZE]) -> Result<Self, OaatError> {
60        let version = buf[0] >> 4;
61        let flags = PacketFlags::from_bits(buf[0] & 0x0F)
62            .ok_or(OaatError::InvalidPacketFlags(buf[0] & 0x0F))?;
63        let format = AudioFormat::from_wire_id(buf[1]).ok_or(OaatError::UnknownFormat(buf[1]))?;
64        let sequence = u16::from_be_bytes([buf[2], buf[3]]);
65        let stream_id = u32::from_be_bytes([buf[4], buf[5], buf[6], buf[7]]);
66        let pts_ns = u64::from_be_bytes(buf[8..16].try_into().unwrap());
67        let sample_offset = u64::from_be_bytes(buf[16..24].try_into().unwrap());
68        let payload_len = u16::from_be_bytes([buf[24], buf[25]]);
69
70        Ok(Self {
71            version,
72            flags,
73            format,
74            sequence,
75            stream_id,
76            pts_ns,
77            sample_offset,
78            payload_len,
79        })
80    }
81}
82
83/// Clock sync packet (28 bytes).
84/// Ver (4 bits) + Type (4 bits) + Sequence (u16) + T1/T2/T3 (u64 each).
85#[derive(Debug, Clone, Copy, PartialEq, Eq)]
86pub struct ClockSyncPacket {
87    pub version: u8,
88    pub kind: ClockSyncType,
89    pub sequence: u16,
90    pub t1: u64,
91    pub t2: u64,
92    pub t3: u64,
93}
94
95#[derive(Debug, Clone, Copy, PartialEq, Eq)]
96pub enum ClockSyncType {
97    Request = 0x01,
98    Response = 0x02,
99}
100
101impl ClockSyncPacket {
102    pub const SIZE: usize = 28;
103
104    pub fn encode(&self, buf: &mut [u8; Self::SIZE]) {
105        let ver_type = (self.version << 4) | (self.kind as u8);
106        buf[0] = ver_type;
107        buf[1] = 0; // reserved
108        buf[2..4].copy_from_slice(&self.sequence.to_be_bytes());
109        buf[4..12].copy_from_slice(&self.t1.to_be_bytes());
110        buf[12..20].copy_from_slice(&self.t2.to_be_bytes());
111        buf[20..28].copy_from_slice(&self.t3.to_be_bytes());
112    }
113
114    pub fn decode(buf: &[u8; Self::SIZE]) -> Result<Self, OaatError> {
115        let version = buf[0] >> 4;
116        let kind = match buf[0] & 0x0F {
117            0x01 => ClockSyncType::Request,
118            0x02 => ClockSyncType::Response,
119            other => return Err(OaatError::InvalidClockSyncType(other)),
120        };
121        let sequence = u16::from_be_bytes([buf[2], buf[3]]);
122        let t1 = u64::from_be_bytes(buf[4..12].try_into().unwrap());
123        let t2 = u64::from_be_bytes(buf[12..20].try_into().unwrap());
124        let t3 = u64::from_be_bytes(buf[20..28].try_into().unwrap());
125
126        Ok(Self {
127            version,
128            kind,
129            sequence,
130            t1,
131            t2,
132            t3,
133        })
134    }
135
136    pub fn compute_offset(t1: u64, t2: u64, t3: u64, t4: u64) -> i64 {
137        let a = t2 as i64 - t1 as i64;
138        let b = t3 as i64 - t4 as i64;
139        (a + b) / 2
140    }
141
142    pub fn compute_rtt(t1: u64, t2: u64, t3: u64, t4: u64) -> u64 {
143        let total = t4.wrapping_sub(t1);
144        let server = t3.wrapping_sub(t2);
145        total.wrapping_sub(server)
146    }
147}
148
149#[cfg(test)]
150mod tests {
151    use super::*;
152
153    #[test]
154    fn audio_header_roundtrip() {
155        let hdr = AudioPacketHeader {
156            version: 1,
157            flags: PacketFlags::FIRST_PACKET | PacketFlags::FEC,
158            format: AudioFormat::PcmS24le,
159            sequence: 42,
160            stream_id: 0xDEADBEEF,
161            pts_ns: 1_000_000_000,
162            sample_offset: 192000,
163            payload_len: 1440,
164        };
165        let mut buf = [0u8; AUDIO_HEADER_SIZE];
166        hdr.encode(&mut buf);
167        let decoded = AudioPacketHeader::decode(&buf).unwrap();
168        assert_eq!(hdr, decoded);
169    }
170
171    #[test]
172    fn clock_sync_roundtrip() {
173        let pkt = ClockSyncPacket {
174            version: 1,
175            kind: ClockSyncType::Response,
176            sequence: 7,
177            t1: 100_000,
178            t2: 100_050,
179            t3: 100_060,
180        };
181        let mut buf = [0u8; ClockSyncPacket::SIZE];
182        pkt.encode(&mut buf);
183        let decoded = ClockSyncPacket::decode(&buf).unwrap();
184        assert_eq!(pkt, decoded);
185    }
186
187    #[test]
188    fn clock_offset_calculation() {
189        let offset = ClockSyncPacket::compute_offset(100, 200, 210, 310);
190        assert_eq!(offset, 0);
191
192        let rtt = ClockSyncPacket::compute_rtt(100, 200, 210, 310);
193        assert_eq!(rtt, 200);
194    }
195}