1pub const PT_PCMU: u8 = 0;
18pub const PT_PCMA: u8 = 8;
20
21pub const SAMPLES_PER_PACKET: usize = 160;
23
24#[derive(Debug, Clone, PartialEq)]
26pub struct RtpPacket {
27 pub payload_type: u8,
29 pub marker: bool,
31 pub sequence: u16,
33 pub timestamp: u32,
35 pub ssrc: u32,
37 pub payload: Vec<u8>,
39}
40
41pub fn build(packet: &RtpPacket) -> Vec<u8> {
43 let mut out = Vec::with_capacity(12 + packet.payload.len());
44 out.push(0x80); out.push((packet.payload_type & 0x7F) | if packet.marker { 0x80 } else { 0 });
46 out.extend_from_slice(&packet.sequence.to_be_bytes());
47 out.extend_from_slice(&packet.timestamp.to_be_bytes());
48 out.extend_from_slice(&packet.ssrc.to_be_bytes());
49 out.extend_from_slice(&packet.payload);
50 out
51}
52
53pub fn parse(datagram: &[u8]) -> Option<RtpPacket> {
58 if datagram.len() < 12 {
59 return None;
60 }
61 let b0 = datagram[0];
62 if b0 >> 6 != 2 {
63 return None; }
65 let has_padding = b0 & 0x20 != 0;
66 let has_extension = b0 & 0x10 != 0;
67 let csrc_count = (b0 & 0x0F) as usize;
68 let b1 = datagram[1];
69
70 let mut offset = 12 + csrc_count * 4;
71 if datagram.len() < offset {
72 return None;
73 }
74 if has_extension {
75 if datagram.len() < offset + 4 {
76 return None;
77 }
78 let ext_words = u16::from_be_bytes([datagram[offset + 2], datagram[offset + 3]]) as usize;
79 offset += 4 + ext_words * 4;
80 if datagram.len() < offset {
81 return None;
82 }
83 }
84 let mut end = datagram.len();
85 if has_padding {
86 let pad = *datagram.last()? as usize;
87 if pad == 0 || offset + pad > end {
88 return None;
89 }
90 end -= pad;
91 }
92
93 Some(RtpPacket {
94 payload_type: b1 & 0x7F,
95 marker: b1 & 0x80 != 0,
96 sequence: u16::from_be_bytes([datagram[2], datagram[3]]),
97 timestamp: u32::from_be_bytes([datagram[4], datagram[5], datagram[6], datagram[7]]),
98 ssrc: u32::from_be_bytes([datagram[8], datagram[9], datagram[10], datagram[11]]),
99 payload: datagram[offset..end].to_vec(),
100 })
101}
102
103#[derive(Debug, Clone, Copy, PartialEq, Eq)]
108pub struct TelephoneEvent {
109 pub event: u8,
111 pub end: bool,
115 pub duration: u16,
117}
118
119impl TelephoneEvent {
120 pub fn digit(&self) -> Option<char> {
122 Some(match self.event {
123 0..=9 => (b'0' + self.event) as char,
124 10 => '*',
125 11 => '#',
126 12..=15 => (b'A' + self.event - 12) as char,
127 _ => return None,
128 })
129 }
130}
131
132pub fn parse_telephone_event(payload: &[u8]) -> Option<TelephoneEvent> {
137 if payload.len() < 4 {
138 return None;
139 }
140 Some(TelephoneEvent {
141 event: payload[0],
142 end: payload[1] & 0x80 != 0,
143 duration: u16::from_be_bytes([payload[2], payload[3]]),
144 })
145}
146
147#[derive(Debug)]
149pub struct RtpSender {
150 payload_type: u8,
151 sequence: u16,
152 timestamp: u32,
153 ssrc: u32,
154 mark_next: bool,
156}
157
158impl RtpSender {
159 pub fn new(payload_type: u8, ssrc: u32, initial_sequence: u16, initial_timestamp: u32) -> Self {
164 Self {
165 payload_type,
166 sequence: initial_sequence,
167 timestamp: initial_timestamp,
168 ssrc,
169 mark_next: true,
170 }
171 }
172
173 pub fn packetize(&mut self, payload: &[u8], samples: u32) -> Vec<u8> {
178 let packet = build(&RtpPacket {
179 payload_type: self.payload_type,
180 marker: self.mark_next,
181 sequence: self.sequence,
182 timestamp: self.timestamp,
183 ssrc: self.ssrc,
184 payload: payload.to_vec(),
185 });
186 self.mark_next = false;
187 self.sequence = self.sequence.wrapping_add(1);
188 self.timestamp = self.timestamp.wrapping_add(samples);
189 packet
190 }
191
192 pub fn skip_silence(&mut self, samples: u32) {
195 self.timestamp = self.timestamp.wrapping_add(samples);
196 self.mark_next = true;
197 }
198}
199
200#[cfg(test)]
201mod tests {
202 use super::*;
203
204 #[test]
205 fn builds_the_canonical_wire_layout() {
206 let bytes = build(&RtpPacket {
207 payload_type: PT_PCMU,
208 marker: true,
209 sequence: 0x0102,
210 timestamp: 0x03040506,
211 ssrc: 0x0708090A,
212 payload: vec![0xFF, 0xFE],
213 });
214 assert_eq!(
215 bytes,
216 vec![
217 0x80, 0x80, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0xFF, 0xFE
218 ]
219 );
220 }
221
222 #[test]
223 fn parse_round_trips_build() {
224 let packet = RtpPacket {
225 payload_type: PT_PCMA,
226 marker: false,
227 sequence: 65_535,
228 timestamp: u32::MAX - 1,
229 ssrc: 42,
230 payload: vec![1, 2, 3, 4],
231 };
232 assert_eq!(parse(&build(&packet)), Some(packet));
233 }
234
235 #[test]
236 fn parse_skips_csrc_extension_and_padding() {
237 let mut bytes = vec![0xB1, 0x00, 0x00, 0x01, 0, 0, 0, 2, 0, 0, 0, 3];
239 bytes.extend_from_slice(&[9, 9, 9, 9]); bytes.extend_from_slice(&[0xBE, 0xDE, 0x00, 0x01, 0, 0, 0, 0]); bytes.extend_from_slice(&[0xAA, 0xBB]); bytes.extend_from_slice(&[0, 0, 3]); let packet = parse(&bytes).expect("valid despite extras");
244 assert_eq!(packet.payload, vec![0xAA, 0xBB]);
245 assert_eq!(packet.sequence, 1);
246 }
247
248 #[test]
249 fn parse_rejects_garbage() {
250 assert_eq!(parse(&[]), None);
251 assert_eq!(parse(&[0x80; 5]), None); assert_eq!(parse(&[0x00; 20]), None); }
254
255 #[test]
256 fn telephone_events_parse_digits_and_end_bits() {
257 let event = parse_telephone_event(&[5, 0x0A, 0x00, 0xA0]).unwrap();
259 assert_eq!(event.digit(), Some('5'));
260 assert!(!event.end);
261 assert_eq!(event.duration, 160);
262
263 let end = parse_telephone_event(&[11, 0x8A, 0x03, 0x20]).unwrap();
265 assert_eq!(end.digit(), Some('#'));
266 assert!(end.end);
267
268 assert_eq!(
269 parse_telephone_event(&[12, 0x80, 0, 60]).unwrap().digit(),
270 Some('A')
271 );
272 assert_eq!(
273 parse_telephone_event(&[10, 0x80, 0, 60]).unwrap().digit(),
274 Some('*')
275 );
276 assert_eq!(
278 parse_telephone_event(&[16, 0x80, 0, 60]).unwrap().digit(),
279 None
280 );
281 assert_eq!(parse_telephone_event(&[5, 0x80]), None);
283 }
284
285 #[test]
286 fn sender_advances_and_marks_talkspurts() {
287 let mut sender = RtpSender::new(PT_PCMU, 7, 100, 1000);
288 let first = parse(&sender.packetize(&[0u8; 160], 160)).unwrap();
289 let second = parse(&sender.packetize(&[0u8; 160], 160)).unwrap();
290 assert!(first.marker, "first packet starts a talkspurt");
291 assert!(!second.marker);
292 assert_eq!(second.sequence, 101);
293 assert_eq!(second.timestamp, 1160);
294
295 sender.skip_silence(800); let resumed = parse(&sender.packetize(&[0u8; 160], 160)).unwrap();
297 assert!(resumed.marker, "resuming after silence re-marks");
298 assert_eq!(resumed.timestamp, 2120);
300 }
301
302 #[test]
303 fn rtp_packet_with_no_payload() {
304 let packet = RtpPacket {
306 payload_type: PT_PCMU,
307 marker: false,
308 sequence: 100,
309 timestamp: 1000,
310 ssrc: 42,
311 payload: Vec::new(),
312 };
313 let built = build(&packet);
314 let parsed = parse(&built).expect("should parse empty payload");
315 assert_eq!(parsed.payload, Vec::<u8>::new());
316 }
317
318 #[test]
319 fn rtp_sequence_wrapping() {
320 let mut sender = RtpSender::new(PT_PCMU, 1, u16::MAX - 1, 0);
322 let p1 = parse(&sender.packetize(&[0u8; 160], 160)).unwrap();
323 let p2 = parse(&sender.packetize(&[0u8; 160], 160)).unwrap();
324 assert_eq!(p1.sequence, u16::MAX - 1);
325 assert_eq!(p2.sequence, u16::MAX);
326 let p3 = parse(&sender.packetize(&[0u8; 160], 160)).unwrap();
327 assert_eq!(p3.sequence, 0, "sequence should wrap to 0");
328 }
329
330 #[test]
331 fn rtp_timestamp_wrapping() {
332 let mut sender = RtpSender::new(PT_PCMU, 1, 0, u32::MAX - 100);
334 let p1 = parse(&sender.packetize(&[0u8; 160], 160)).unwrap();
335 assert_eq!(p1.timestamp, u32::MAX - 100);
336 let p2 = parse(&sender.packetize(&[0u8; 160], 160)).unwrap();
337 let expected = (u32::MAX as u64 - 100 + 160) as u32;
338 assert_eq!(p2.timestamp, expected, "timestamp should wrap correctly");
339 }
340
341 #[test]
342 fn rtp_csrc_count_limits() {
343 let mut bytes = vec![0x8F, 0x00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; for _ in 0..15 {
346 bytes.extend_from_slice(&[0, 0, 0, 0]); }
348 bytes.extend_from_slice(&[1, 2]); let packet = parse(&bytes).expect("should parse max CSRCs");
350 assert_eq!(packet.payload, vec![1, 2]);
351 }
352
353 #[test]
354 fn rtp_extension_header_handling() {
355 let mut bytes = vec![0x90, 0x00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; bytes.extend_from_slice(&[0xAB, 0xCD, 0x00, 0x04]); for _ in 0..4 {
359 bytes.extend_from_slice(&[0xFF, 0xEE, 0xDD, 0xCC]);
360 }
361 bytes.extend_from_slice(&[0x12, 0x34]); let packet = parse(&bytes).expect("should parse extension");
363 assert_eq!(packet.payload, vec![0x12, 0x34]);
364 }
365
366 #[test]
367 fn rtp_padding_edge_cases() {
368 let mut bytes = vec![0xA0, 0x00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; bytes.extend_from_slice(&[1, 2]); bytes.extend_from_slice(&[0, 0, 3]); let packet = parse(&bytes).expect("should parse when pad equals end");
373 assert_eq!(packet.payload, vec![1, 2]); }
375
376 #[test]
377 fn rtp_parse_rejects_invalid_padding() {
378 let mut bytes = vec![0xA0, 0x00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; bytes.extend_from_slice(&[1, 2]);
381 bytes.push(0); assert_eq!(parse(&bytes), None, "should reject zero padding count");
383
384 let mut bytes = vec![0xA0, 0x00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; bytes.extend_from_slice(&[1, 2]);
387 bytes.push(10); assert_eq!(parse(&bytes), None, "should reject oversized padding");
389 }
390
391 #[test]
392 fn telephone_event_all_digits() {
393 for event_code in 0u8..=15 {
395 let payload = [event_code, 0x00, 0x00, 0xA0];
396 let event = parse_telephone_event(&payload).unwrap();
397 assert_eq!(event.event, event_code);
398 assert!(!event.end);
399 match event_code {
401 0..=9 => {
402 assert!(event.digit().is_some());
403 }
404 10..=11 => {
405 assert!(event.digit().is_some());
406 }
407 12..=15 => {
408 assert!(event.digit().is_some());
409 }
410 _ => unreachable!(),
411 }
412 }
413 }
414
415 #[test]
416 fn telephone_event_duration_limits() {
417 let payload = [5, 0x80, 0xFF, 0xFF]; let event = parse_telephone_event(&payload).unwrap();
420 assert_eq!(event.duration, u16::MAX);
421
422 let payload = [5, 0x80, 0x00, 0x01]; let event = parse_telephone_event(&payload).unwrap();
424 assert_eq!(event.duration, 1);
425 }
426
427 #[test]
428 fn payload_type_only_uses_7_bits() {
429 let packet = RtpPacket {
431 payload_type: 0x7F, marker: true,
433 sequence: 0,
434 timestamp: 0,
435 ssrc: 0,
436 payload: vec![1, 2],
437 };
438 let built = build(&packet);
439 let parsed = parse(&built).unwrap();
440 assert_eq!(parsed.payload_type, 0x7F);
441 assert!(parsed.marker);
442 }
443}