arcly_stream/protocol/srt/
packet.rs1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8#[non_exhaustive]
9pub enum ControlType {
10 Handshake,
12 Keepalive,
14 Ack,
16 Nak,
18 Shutdown,
20 AckAck,
22 Other(u16),
24}
25
26impl ControlType {
27 fn from_u16(v: u16) -> ControlType {
28 match v {
29 0x0000 => ControlType::Handshake,
30 0x0001 => ControlType::Keepalive,
31 0x0002 => ControlType::Ack,
32 0x0003 => ControlType::Nak,
33 0x0005 => ControlType::Shutdown,
34 0x0006 => ControlType::AckAck,
35 other => ControlType::Other(other),
36 }
37 }
38}
39
40#[derive(Debug, Clone, PartialEq, Eq)]
43pub enum SrtPacket {
44 Control {
46 control_type: ControlType,
48 dest_socket_id: u32,
50 },
51 Data {
53 sequence: u32,
55 dest_socket_id: u32,
57 key_flag: u8,
59 retransmit: bool,
61 payload_offset: usize,
63 },
64}
65
66impl SrtPacket {
67 pub fn parse(buf: &[u8]) -> Option<SrtPacket> {
70 use crate::protocol::byteops::ByteReader;
71 let mut r = ByteReader::new(buf);
72 let word0 = r.u32_be()?;
73 let word1 = r.u32_be()?;
74 r.skip(4)?; let dest_socket_id = r.u32_be()?;
76
77 if word0 & 0x8000_0000 == 0 {
78 Some(SrtPacket::Data {
81 sequence: word0 & 0x7FFF_FFFF,
82 dest_socket_id,
83 key_flag: ((word1 >> 27) & 0b11) as u8,
84 retransmit: (word1 >> 26) & 1 != 0,
85 payload_offset: 16,
86 })
87 } else {
88 let control_type = ((word0 >> 16) & 0x7FFF) as u16;
90 Some(SrtPacket::Control {
91 control_type: ControlType::from_u16(control_type),
92 dest_socket_id,
93 })
94 }
95 }
96}
97
98pub fn build_data_packet(
108 sequence: u32,
109 message_number: u32,
110 timestamp: u32,
111 dest_socket_id: u32,
112 key_flag: u8,
113 retransmit: bool,
114 payload: &[u8],
115) -> Vec<u8> {
116 let mut out = Vec::with_capacity(16 + payload.len());
117 out.extend_from_slice(&(sequence & 0x7FFF_FFFF).to_be_bytes());
118 let word1 = (0b11u32 << 30)
120 | (1 << 29)
121 | (((key_flag & 0b11) as u32) << 27)
122 | ((retransmit as u32) << 26)
123 | (message_number & 0x03FF_FFFF);
124 out.extend_from_slice(&word1.to_be_bytes());
125 out.extend_from_slice(×tamp.to_be_bytes());
126 out.extend_from_slice(&dest_socket_id.to_be_bytes());
127 out.extend_from_slice(payload);
128 out
129}
130
131#[cfg(test)]
132mod tests {
133 use super::*;
134
135 fn header(word0: u32, dest: u32) -> Vec<u8> {
136 let mut b = word0.to_be_bytes().to_vec();
137 b.extend_from_slice(&[0; 8]); b.extend_from_slice(&dest.to_be_bytes());
139 b
140 }
141
142 #[test]
143 fn parses_data_packet() {
144 let pkt = SrtPacket::parse(&header(0x0000_002A, 99)).unwrap();
145 assert_eq!(
146 pkt,
147 SrtPacket::Data {
148 sequence: 42,
149 dest_socket_id: 99,
150 key_flag: 0,
151 retransmit: false,
152 payload_offset: 16,
153 }
154 );
155 }
156
157 #[test]
158 fn built_data_packet_round_trips() {
159 let payload = [0x47u8, 0x40, 0x00, 0x10, 0xAA, 0xBB]; let pkt = build_data_packet(1234, 7, 90_000, 0xDEAD_BEEF, 1, true, &payload);
161 match SrtPacket::parse(&pkt).unwrap() {
162 SrtPacket::Data {
163 sequence,
164 dest_socket_id,
165 key_flag,
166 retransmit,
167 payload_offset,
168 } => {
169 assert_eq!(sequence, 1234);
170 assert_eq!(dest_socket_id, 0xDEAD_BEEF);
171 assert_eq!(key_flag, 1, "even-key flag round-trips");
172 assert!(retransmit, "retransmit bit round-trips");
173 assert_eq!(&pkt[payload_offset..], &payload);
174 }
175 other => panic!("expected data packet, got {other:?}"),
176 }
177 }
178
179 #[test]
180 fn parses_control_handshake() {
181 let pkt = SrtPacket::parse(&header(0x8000_0000, 7)).unwrap();
183 assert_eq!(
184 pkt,
185 SrtPacket::Control {
186 control_type: ControlType::Handshake,
187 dest_socket_id: 7,
188 }
189 );
190 }
191
192 #[test]
193 fn parses_control_nak() {
194 let pkt = SrtPacket::parse(&header(0x8003_0000, 1)).unwrap();
195 match pkt {
196 SrtPacket::Control { control_type, .. } => assert_eq!(control_type, ControlType::Nak),
197 _ => panic!("expected control"),
198 }
199 }
200
201 #[test]
202 fn rejects_short_buffer() {
203 assert!(SrtPacket::parse(&[0u8; 8]).is_none());
204 }
205}