1use alloc::vec::Vec;
6
7use broadcast_common::{Parse, Serialize};
8
9use crate::error::{Error, Result};
10
11pub const RTP_VERSION: u8 = 2;
18
19pub const FIXED_HEADER_LEN: usize = 12;
22
23const CSRC_ITEM_LEN: usize = 4;
25
26pub const MAX_CSRC_COUNT: usize = 15;
28
29const VERSION_SHIFT: u8 = 6;
31const PADDING_BIT_MASK: u8 = 0x20;
33const EXTENSION_BIT_MASK: u8 = 0x10;
35const CC_MASK: u8 = 0x0F;
37const MARKER_BIT_MASK: u8 = 0x80;
39const PAYLOAD_TYPE_MASK: u8 = 0x7F;
41pub const MAX_PAYLOAD_TYPE: u8 = 0x7F;
43
44const EXTENSION_HEADER_LEN: usize = 4;
47const EXTENSION_WORD_LEN: usize = 4;
50const MAX_EXTENSION_WORDS: usize = u16::MAX as usize;
52
53pub const MAX_PADDING_COUNT: usize = u8::MAX as usize;
57
58#[derive(Debug, Clone, Copy, PartialEq, Eq)]
72#[cfg_attr(feature = "serde", derive(serde::Serialize))]
73pub struct HeaderExtension<'a> {
74 pub profile_id: u16,
77 pub data: &'a [u8],
82}
83
84impl HeaderExtension<'_> {
85 #[must_use]
89 pub fn length_words(&self) -> usize {
90 self.data.len() / EXTENSION_WORD_LEN
91 }
92}
93
94#[derive(Debug, Clone, PartialEq, Eq)]
116#[cfg_attr(feature = "serde", derive(serde::Serialize))]
117pub struct RtpPacket<'a> {
118 pub marker: bool,
120 pub payload_type: u8,
122 pub sequence_number: u16,
124 pub timestamp: u32,
126 pub ssrc: u32,
128 pub csrc: Vec<u32>,
131 pub extension: Option<HeaderExtension<'a>>,
133 pub padding: Option<&'a [u8]>,
139 pub payload: &'a [u8],
142}
143
144impl RtpPacket<'_> {
145 #[must_use]
148 pub fn csrc_count(&self) -> usize {
149 self.csrc.len()
150 }
151}
152
153impl<'a> Parse<'a> for RtpPacket<'a> {
154 type Error = Error;
155
156 fn parse(bytes: &'a [u8]) -> Result<Self> {
157 if bytes.len() < FIXED_HEADER_LEN {
158 return Err(Error::BufferTooShort {
159 need: FIXED_HEADER_LEN,
160 have: bytes.len(),
161 what: "RTP fixed header",
162 });
163 }
164
165 let byte0 = bytes[0];
166 let version = byte0 >> VERSION_SHIFT;
167 if version != RTP_VERSION {
168 return Err(Error::InvalidVersion(version));
169 }
170 let padding_flag = byte0 & PADDING_BIT_MASK != 0;
171 let extension_flag = byte0 & EXTENSION_BIT_MASK != 0;
172 let cc = usize::from(byte0 & CC_MASK);
173
174 let byte1 = bytes[1];
175 let marker = byte1 & MARKER_BIT_MASK != 0;
176 let payload_type = byte1 & PAYLOAD_TYPE_MASK;
177
178 let sequence_number = u16::from_be_bytes([bytes[2], bytes[3]]);
179 let timestamp = u32::from_be_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]);
180 let ssrc = u32::from_be_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]);
181
182 let mut pos = FIXED_HEADER_LEN;
183
184 let csrc_bytes_len = cc * CSRC_ITEM_LEN;
186 if bytes.len() < pos + csrc_bytes_len {
187 return Err(Error::BufferTooShort {
188 need: pos + csrc_bytes_len,
189 have: bytes.len(),
190 what: "CSRC list",
191 });
192 }
193 let mut csrc = Vec::with_capacity(cc);
194 for i in 0..cc {
195 let off = pos + i * CSRC_ITEM_LEN;
196 csrc.push(u32::from_be_bytes([
197 bytes[off],
198 bytes[off + 1],
199 bytes[off + 2],
200 bytes[off + 3],
201 ]));
202 }
203 pos += csrc_bytes_len;
204
205 let extension = if extension_flag {
207 if bytes.len() < pos + EXTENSION_HEADER_LEN {
208 return Err(Error::BufferTooShort {
209 need: pos + EXTENSION_HEADER_LEN,
210 have: bytes.len(),
211 what: "header extension profile-id/length",
212 });
213 }
214 let profile_id = u16::from_be_bytes([bytes[pos], bytes[pos + 1]]);
215 let length_words = usize::from(u16::from_be_bytes([bytes[pos + 2], bytes[pos + 3]]));
216 let data_len = length_words * EXTENSION_WORD_LEN;
217 let data_start = pos + EXTENSION_HEADER_LEN;
218 let data_end = data_start + data_len;
219 if bytes.len() < data_end {
220 return Err(Error::BufferTooShort {
221 need: data_end,
222 have: bytes.len(),
223 what: "header extension data",
224 });
225 }
226 pos = data_end;
227 Some(HeaderExtension {
228 profile_id,
229 data: &bytes[data_start..data_end],
230 })
231 } else {
232 None
233 };
234
235 let (payload, padding) = if padding_flag {
237 if bytes.len() <= pos {
238 return Err(Error::InvalidPadding {
239 count: 0,
240 reason: "padding bit set but no bytes remain for the count octet",
241 });
242 }
243 let count = bytes[bytes.len() - 1];
245 if count == 0 {
246 return Err(Error::InvalidPadding {
247 count,
248 reason: "padding count must be >= 1 (it counts itself)",
249 });
250 }
251 let remaining = bytes.len() - pos;
252 if usize::from(count) > remaining {
253 return Err(Error::InvalidPadding {
254 count,
255 reason: "padding count exceeds the bytes remaining after the header",
256 });
257 }
258 let split = bytes.len() - usize::from(count);
259 (&bytes[pos..split], Some(&bytes[split..]))
260 } else {
261 (&bytes[pos..], None)
262 };
263
264 Ok(Self {
265 marker,
266 payload_type,
267 sequence_number,
268 timestamp,
269 ssrc,
270 csrc,
271 extension,
272 padding,
273 payload,
274 })
275 }
276}
277
278impl Serialize for RtpPacket<'_> {
279 type Error = Error;
280
281 fn serialized_len(&self) -> usize {
282 FIXED_HEADER_LEN
283 + self.csrc.len() * CSRC_ITEM_LEN
284 + self
285 .extension
286 .map(|e| EXTENSION_HEADER_LEN + e.data.len())
287 .unwrap_or(0)
288 + self.payload.len()
289 + self.padding.map(<[u8]>::len).unwrap_or(0)
290 }
291
292 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
293 let len = self.serialized_len();
294 if buf.len() < len {
295 return Err(Error::BufferTooShort {
296 need: len,
297 have: buf.len(),
298 what: "RTP packet serialize output",
299 });
300 }
301 if self.csrc.len() > MAX_CSRC_COUNT {
302 return Err(Error::InvalidValue {
303 field: "csrc.len()",
304 value: self.csrc.len() as u64,
305 reason: "exceeds the 4-bit CC field maximum (15)",
306 });
307 }
308 if self.payload_type > MAX_PAYLOAD_TYPE {
309 return Err(Error::InvalidValue {
310 field: "payload_type",
311 value: u64::from(self.payload_type),
312 reason: "exceeds the 7-bit PT field maximum (127)",
313 });
314 }
315 if let Some(ext) = &self.extension {
316 if ext.data.len() % EXTENSION_WORD_LEN != 0 {
317 return Err(Error::ExtensionNotWordAligned {
318 data_len: ext.data.len(),
319 });
320 }
321 let words = ext.length_words();
322 if words > MAX_EXTENSION_WORDS {
323 return Err(Error::InvalidValue {
324 field: "extension.data.len()/4",
325 value: words as u64,
326 reason: "exceeds the 16-bit length field maximum",
327 });
328 }
329 }
330 if let Some(pad) = self.padding {
331 if pad.is_empty() {
332 return Err(Error::InvalidPadding {
333 count: 0,
334 reason: "a Some(padding) slice must be non-empty",
335 });
336 }
337 if pad.len() > MAX_PADDING_COUNT {
338 return Err(Error::InvalidValue {
339 field: "padding.len()",
340 value: pad.len() as u64,
341 reason: "exceeds the 8-bit padding-count field maximum (255)",
342 });
343 }
344 let last = pad[pad.len() - 1];
346 if usize::from(last) != pad.len() {
347 return Err(Error::InvalidPadding {
348 count: last,
349 reason: "the last padding byte must equal the padding slice's own length",
350 });
351 }
352 }
353
354 let mut byte0 = RTP_VERSION << VERSION_SHIFT;
355 if self.padding.is_some() {
356 byte0 |= PADDING_BIT_MASK;
357 }
358 if self.extension.is_some() {
359 byte0 |= EXTENSION_BIT_MASK;
360 }
361 byte0 |= (self.csrc.len() as u8) & CC_MASK;
362 buf[0] = byte0;
363
364 let mut byte1 = self.payload_type & PAYLOAD_TYPE_MASK;
365 if self.marker {
366 byte1 |= MARKER_BIT_MASK;
367 }
368 buf[1] = byte1;
369
370 buf[2..4].copy_from_slice(&self.sequence_number.to_be_bytes());
371 buf[4..8].copy_from_slice(&self.timestamp.to_be_bytes());
372 buf[8..12].copy_from_slice(&self.ssrc.to_be_bytes());
373
374 let mut pos = FIXED_HEADER_LEN;
375 for &c in &self.csrc {
376 buf[pos..pos + CSRC_ITEM_LEN].copy_from_slice(&c.to_be_bytes());
377 pos += CSRC_ITEM_LEN;
378 }
379
380 if let Some(ext) = &self.extension {
381 buf[pos..pos + 2].copy_from_slice(&ext.profile_id.to_be_bytes());
382 let words = ext.length_words() as u16;
383 buf[pos + 2..pos + EXTENSION_HEADER_LEN].copy_from_slice(&words.to_be_bytes());
384 pos += EXTENSION_HEADER_LEN;
385 buf[pos..pos + ext.data.len()].copy_from_slice(ext.data);
386 pos += ext.data.len();
387 }
388
389 buf[pos..pos + self.payload.len()].copy_from_slice(self.payload);
390 pos += self.payload.len();
391
392 if let Some(pad) = self.padding {
393 buf[pos..pos + pad.len()].copy_from_slice(pad);
394 pos += pad.len();
395 }
396
397 Ok(pos)
398 }
399}
400
401#[cfg(test)]
402mod tests {
403 use super::*;
404 use alloc::vec;
405
406 fn simple_packet() -> RtpPacket<'static> {
407 RtpPacket {
408 marker: true,
409 payload_type: 97,
410 sequence_number: 5,
411 timestamp: 0x0000_1400,
412 ssrc: 0x1234_5678,
413 csrc: Vec::new(),
414 extension: None,
415 padding: None,
416 payload: &[0xAA, 0xBB, 0xCC, 0xDD],
417 }
418 }
419
420 #[test]
421 fn simple_round_trip() {
422 let p = simple_packet();
423 let mut out = vec![0u8; p.serialized_len()];
424 p.serialize_into(&mut out).unwrap();
425 assert_eq!(RtpPacket::parse(&out).unwrap(), p);
426 assert_eq!(out[0], 0x80); assert_eq!(out[1], 0x80 | 97); }
429
430 #[test]
431 fn rejects_bad_version() {
432 let p = simple_packet();
433 let mut out = vec![0u8; p.serialized_len()];
434 p.serialize_into(&mut out).unwrap();
435 out[0] = (1 << VERSION_SHIFT) | (out[0] & 0x3F); assert!(matches!(
437 RtpPacket::parse(&out),
438 Err(Error::InvalidVersion(1))
439 ));
440 }
441
442 #[test]
443 fn csrc_round_trip() {
444 let mut p = simple_packet();
445 p.csrc = vec![0x1111_1111, 0x2222_2222, 0x3333_3333];
446 let mut out = vec![0u8; p.serialized_len()];
447 p.serialize_into(&mut out).unwrap();
448 assert_eq!(out[0] & 0x0F, 3, "CC derived from csrc.len()");
449 let reparsed = RtpPacket::parse(&out).unwrap();
450 assert_eq!(reparsed, p);
451 }
452
453 #[test]
454 fn rejects_csrc_over_15() {
455 let mut p = simple_packet();
456 p.csrc = vec![0; 16];
457 let mut out = vec![0u8; p.serialized_len()];
458 assert!(matches!(
459 p.serialize_into(&mut out),
460 Err(Error::InvalidValue {
461 field: "csrc.len()",
462 ..
463 })
464 ));
465 }
466
467 #[test]
468 fn extension_round_trip() {
469 let mut p = simple_packet();
470 p.extension = Some(HeaderExtension {
471 profile_id: 0xBEDE,
472 data: &[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08],
473 });
474 let mut out = vec![0u8; p.serialized_len()];
475 p.serialize_into(&mut out).unwrap();
476 assert_eq!(out[0] & 0x10, 0x10, "X bit set");
477 let reparsed = RtpPacket::parse(&out).unwrap();
478 assert_eq!(reparsed, p);
479 assert_eq!(reparsed.extension.unwrap().length_words(), 2);
480 }
481
482 #[test]
483 fn extension_zero_length_is_valid() {
484 let mut p = simple_packet();
486 p.extension = Some(HeaderExtension {
487 profile_id: 0x0001,
488 data: &[],
489 });
490 let mut out = vec![0u8; p.serialized_len()];
491 p.serialize_into(&mut out).unwrap();
492 let reparsed = RtpPacket::parse(&out).unwrap();
493 assert_eq!(reparsed, p);
494 }
495
496 #[test]
497 fn rejects_extension_not_word_aligned() {
498 let mut p = simple_packet();
499 p.extension = Some(HeaderExtension {
500 profile_id: 1,
501 data: &[0x01, 0x02, 0x03], });
503 let mut out = vec![0u8; FIXED_HEADER_LEN + EXTENSION_HEADER_LEN + 3 + p.payload.len()];
504 assert!(matches!(
505 p.serialize_into(&mut out),
506 Err(Error::ExtensionNotWordAligned { data_len: 3 })
507 ));
508 }
509
510 #[test]
511 fn padding_round_trip() {
512 let mut p = simple_packet();
513 p.padding = Some(&[0x00, 0x00, 0x00, 0x04]);
515 let mut out = vec![0u8; p.serialized_len()];
516 p.serialize_into(&mut out).unwrap();
517 assert_eq!(out[0] & 0x20, 0x20, "P bit set");
518 assert_eq!(*out.last().unwrap(), 4, "trailing pad-count byte");
519 let reparsed = RtpPacket::parse(&out).unwrap();
520 assert_eq!(reparsed, p);
521 assert_eq!(reparsed.payload, p.payload, "payload stripped of padding");
522 }
523
524 #[test]
525 fn rejects_padding_count_mismatch() {
526 let mut p = simple_packet();
527 p.padding = Some(&[0x00, 0x00, 0x00, 0x03]); let mut out = vec![0u8; p.serialized_len()];
529 assert!(matches!(
530 p.serialize_into(&mut out),
531 Err(Error::InvalidPadding { count: 3, .. })
532 ));
533 }
534
535 #[test]
536 fn rejects_padding_count_exceeding_available() {
537 let mut bytes = vec![0u8; FIXED_HEADER_LEN + 2];
540 bytes[0] = 0x80 | 0x20; bytes[1] = 97;
542 *bytes.last_mut().unwrap() = 0xFF; assert!(matches!(
544 RtpPacket::parse(&bytes),
545 Err(Error::InvalidPadding { count: 0xFF, .. })
546 ));
547 }
548
549 #[test]
550 fn field_mutation_changes_bytes() {
551 let a = simple_packet();
552 let mut b = a.clone();
553 b.sequence_number = a.sequence_number.wrapping_add(1);
554 let mut oa = vec![0u8; a.serialized_len()];
555 let mut ob = vec![0u8; b.serialized_len()];
556 a.serialize_into(&mut oa).unwrap();
557 b.serialize_into(&mut ob).unwrap();
558 assert_ne!(oa, ob);
559 assert_eq!(oa[0], ob[0]);
561 assert_eq!(oa[1], ob[1]);
562 assert_ne!(&oa[2..4], &ob[2..4]);
563 assert_eq!(&oa[4..], &ob[4..]);
564 }
565}