1use alloc::string::String;
47use alloc::vec::Vec;
48
49use broadcast_common::{Parse, Serialize};
50
51use crate::error::{Error, Result};
52
53const RTCP_VERSION: u8 = 2;
59const RTCP_BYTE0_V2: u8 = RTCP_VERSION << 6;
61const RTCP_HEADER_LEN: usize = 4;
63const RTCP_PADDING_MASK: u8 = 0x20;
65const RTCP_COUNT_MASK: u8 = 0x1F;
67const WORD_LEN: usize = 4;
69
70pub const PT_SENDER_REPORT: u8 = 200;
72pub const PT_RECEIVER_REPORT: u8 = 201;
74pub const PT_SOURCE_DESCRIPTION: u8 = 202;
76pub const PT_BYE: u8 = 203;
78pub const PT_APP: u8 = 204;
80
81pub const REPORT_BLOCK_LEN: usize = 24;
83const SR_SENDER_INFO_LEN: usize = 20;
85pub const APP_NAME_LEN: usize = 4;
87const MAX_COUNT: usize = RTCP_COUNT_MASK as usize;
89
90fn be_u32(bytes: &[u8], off: usize, what: &'static str) -> Result<u32> {
96 bytes
97 .get(off..off + 4)
98 .map(|s| u32::from_be_bytes([s[0], s[1], s[2], s[3]]))
99 .ok_or(Error::BufferTooShort {
100 need: off + 4,
101 have: bytes.len(),
102 what,
103 })
104}
105
106#[derive(Debug, Clone, Copy, PartialEq, Eq)]
112#[cfg_attr(feature = "serde", derive(serde::Serialize))]
113#[non_exhaustive]
114pub enum RtcpPacketType {
115 SenderReport,
117 ReceiverReport,
119 SourceDescription,
121 Bye,
123 App,
125 Unknown(u8),
127}
128
129impl RtcpPacketType {
130 pub fn from_pt(pt: u8) -> Self {
132 match pt {
133 PT_SENDER_REPORT => RtcpPacketType::SenderReport,
134 PT_RECEIVER_REPORT => RtcpPacketType::ReceiverReport,
135 PT_SOURCE_DESCRIPTION => RtcpPacketType::SourceDescription,
136 PT_BYE => RtcpPacketType::Bye,
137 PT_APP => RtcpPacketType::App,
138 other => RtcpPacketType::Unknown(other),
139 }
140 }
141
142 pub fn pt(&self) -> u8 {
144 match self {
145 RtcpPacketType::SenderReport => PT_SENDER_REPORT,
146 RtcpPacketType::ReceiverReport => PT_RECEIVER_REPORT,
147 RtcpPacketType::SourceDescription => PT_SOURCE_DESCRIPTION,
148 RtcpPacketType::Bye => PT_BYE,
149 RtcpPacketType::App => PT_APP,
150 RtcpPacketType::Unknown(pt) => *pt,
151 }
152 }
153
154 pub fn name(&self) -> &'static str {
156 match self {
157 RtcpPacketType::SenderReport => "SR",
158 RtcpPacketType::ReceiverReport => "RR",
159 RtcpPacketType::SourceDescription => "SDES",
160 RtcpPacketType::Bye => "BYE",
161 RtcpPacketType::App => "APP",
162 RtcpPacketType::Unknown(_) => "reserved",
163 }
164 }
165}
166
167broadcast_common::impl_spec_display!(RtcpPacketType, Unknown);
168
169#[derive(Debug, Clone, Copy, PartialEq, Eq)]
175#[cfg_attr(feature = "serde", derive(serde::Serialize))]
176pub struct CommonHeader {
177 pub padding: bool,
179 pub count: u8,
182 pub packet_type: u8,
184 pub length: u16,
186}
187
188impl CommonHeader {
189 fn new(count: u8, packet_type: u8, length_words_minus_one: u16) -> Self {
191 Self {
192 padding: false,
193 count: count & RTCP_COUNT_MASK,
194 packet_type,
195 length: length_words_minus_one,
196 }
197 }
198
199 fn parse(bytes: &[u8]) -> Result<Self> {
201 if bytes.len() < RTCP_HEADER_LEN {
202 return Err(Error::BufferTooShort {
203 need: RTCP_HEADER_LEN,
204 have: bytes.len(),
205 what: "RTCP common header",
206 });
207 }
208 let version = bytes[0] >> 6;
209 if version != RTCP_VERSION {
210 return Err(Error::InvalidValue {
211 field: "rtcp_version",
212 value: version as u64,
213 reason: "must be 2",
214 });
215 }
216 Ok(Self {
217 padding: bytes[0] & RTCP_PADDING_MASK != 0,
218 count: bytes[0] & RTCP_COUNT_MASK,
219 packet_type: bytes[1],
220 length: u16::from_be_bytes([bytes[2], bytes[3]]),
221 })
222 }
223
224 fn total_len(&self) -> usize {
227 (self.length as usize + 1) * WORD_LEN
228 }
229
230 fn write(&self, buf: &mut [u8]) {
232 buf[0] = RTCP_BYTE0_V2
233 | (if self.padding { RTCP_PADDING_MASK } else { 0 })
234 | (self.count & RTCP_COUNT_MASK);
235 buf[1] = self.packet_type;
236 buf[2..4].copy_from_slice(&self.length.to_be_bytes());
237 }
238}
239
240fn length_words_minus_one(total_len: usize) -> u16 {
244 (total_len / WORD_LEN).saturating_sub(1) as u16
245}
246
247#[derive(Debug, Clone, Copy, PartialEq, Eq)]
254#[cfg_attr(feature = "serde", derive(serde::Serialize))]
255pub struct ReportBlock {
256 pub ssrc: u32,
258 pub fraction_lost: u8,
260 pub cumulative_lost: i32,
262 pub ext_highest_seq: u32,
264 pub jitter: u32,
266 pub lsr: u32,
268 pub dlsr: u32,
270}
271
272impl ReportBlock {
273 fn decode_cumulative_lost(raw: u32) -> i32 {
275 const SIGN_BIT: u32 = 1 << 23;
277 if raw & SIGN_BIT != 0 {
278 (raw | 0xFF00_0000) as i32
279 } else {
280 raw as i32
281 }
282 }
283
284 fn encode_cumulative_lost(&self) -> u32 {
286 (self.cumulative_lost as u32) & 0x00FF_FFFF
287 }
288}
289
290impl<'a> Parse<'a> for ReportBlock {
291 type Error = Error;
292
293 fn parse(bytes: &'a [u8]) -> Result<Self> {
294 if bytes.len() < REPORT_BLOCK_LEN {
295 return Err(Error::BufferTooShort {
296 need: REPORT_BLOCK_LEN,
297 have: bytes.len(),
298 what: "RTCP report block",
299 });
300 }
301 let ssrc = be_u32(bytes, 0, "report block ssrc")?;
302 let fraction_lost = bytes[4];
303 let cumulative_raw = u32::from_be_bytes([0, bytes[5], bytes[6], bytes[7]]);
304 Ok(ReportBlock {
305 ssrc,
306 fraction_lost,
307 cumulative_lost: ReportBlock::decode_cumulative_lost(cumulative_raw),
308 ext_highest_seq: be_u32(bytes, 8, "report block ext seq")?,
309 jitter: be_u32(bytes, 12, "report block jitter")?,
310 lsr: be_u32(bytes, 16, "report block lsr")?,
311 dlsr: be_u32(bytes, 20, "report block dlsr")?,
312 })
313 }
314}
315
316impl Serialize for ReportBlock {
317 type Error = Error;
318
319 fn serialized_len(&self) -> usize {
320 REPORT_BLOCK_LEN
321 }
322
323 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
324 if buf.len() < REPORT_BLOCK_LEN {
325 return Err(Error::OutputBufferTooSmall {
326 need: REPORT_BLOCK_LEN,
327 have: buf.len(),
328 });
329 }
330 buf[0..4].copy_from_slice(&self.ssrc.to_be_bytes());
331 buf[4] = self.fraction_lost;
332 let cum = self.encode_cumulative_lost().to_be_bytes();
333 buf[5..8].copy_from_slice(&cum[1..4]);
334 buf[8..12].copy_from_slice(&self.ext_highest_seq.to_be_bytes());
335 buf[12..16].copy_from_slice(&self.jitter.to_be_bytes());
336 buf[16..20].copy_from_slice(&self.lsr.to_be_bytes());
337 buf[20..24].copy_from_slice(&self.dlsr.to_be_bytes());
338 Ok(REPORT_BLOCK_LEN)
339 }
340}
341
342fn parse_report_blocks(bytes: &[u8], count: usize) -> Result<Vec<ReportBlock>> {
344 let mut blocks = Vec::with_capacity(count);
345 let mut off = 0;
346 for _ in 0..count {
347 let end = off + REPORT_BLOCK_LEN;
348 if end > bytes.len() {
349 return Err(Error::BufferTooShort {
350 need: end,
351 have: bytes.len(),
352 what: "RTCP report blocks",
353 });
354 }
355 blocks.push(ReportBlock::parse(&bytes[off..end])?);
356 off = end;
357 }
358 Ok(blocks)
359}
360
361fn check_report_count(blocks: &[ReportBlock]) -> Result<u8> {
363 if blocks.len() > MAX_COUNT {
364 return Err(Error::InvalidValue {
365 field: "rtcp_report_count",
366 value: blocks.len() as u64,
367 reason: "exceeds 5-bit RC field",
368 });
369 }
370 Ok(blocks.len() as u8)
371}
372
373#[derive(Debug, Clone, PartialEq, Eq)]
379#[cfg_attr(feature = "serde", derive(serde::Serialize))]
380pub struct SenderReport {
381 pub ssrc: u32,
383 pub ntp_msw: u32,
385 pub ntp_lsw: u32,
387 pub rtp_timestamp: u32,
389 pub packet_count: u32,
391 pub octet_count: u32,
393 pub report_blocks: Vec<ReportBlock>,
395}
396
397impl<'a> Parse<'a> for SenderReport {
398 type Error = Error;
399
400 fn parse(bytes: &'a [u8]) -> Result<Self> {
401 let hdr = CommonHeader::parse(bytes)?;
402 if hdr.packet_type != PT_SENDER_REPORT {
403 return Err(Error::InvalidValue {
404 field: "rtcp_pt",
405 value: hdr.packet_type as u64,
406 reason: "expected SR (200)",
407 });
408 }
409 let total = hdr.total_len();
410 if bytes.len() < total {
411 return Err(Error::BufferTooShort {
412 need: total,
413 have: bytes.len(),
414 what: "RTCP SR",
415 });
416 }
417 let body = &bytes[RTCP_HEADER_LEN..total];
418 if body.len() < WORD_LEN + SR_SENDER_INFO_LEN {
419 return Err(Error::BufferTooShort {
420 need: WORD_LEN + SR_SENDER_INFO_LEN,
421 have: body.len(),
422 what: "RTCP SR sender info",
423 });
424 }
425 let ssrc = be_u32(body, 0, "SR ssrc")?;
426 let ntp_msw = be_u32(body, 4, "SR ntp msw")?;
427 let ntp_lsw = be_u32(body, 8, "SR ntp lsw")?;
428 let rtp_timestamp = be_u32(body, 12, "SR rtp ts")?;
429 let packet_count = be_u32(body, 16, "SR packet count")?;
430 let octet_count = be_u32(body, 20, "SR octet count")?;
431 let blocks_off = WORD_LEN + SR_SENDER_INFO_LEN;
432 let report_blocks = parse_report_blocks(&body[blocks_off..], hdr.count as usize)?;
433 Ok(SenderReport {
434 ssrc,
435 ntp_msw,
436 ntp_lsw,
437 rtp_timestamp,
438 packet_count,
439 octet_count,
440 report_blocks,
441 })
442 }
443}
444
445impl Serialize for SenderReport {
446 type Error = Error;
447
448 fn serialized_len(&self) -> usize {
449 RTCP_HEADER_LEN
450 + WORD_LEN
451 + SR_SENDER_INFO_LEN
452 + self.report_blocks.len() * REPORT_BLOCK_LEN
453 }
454
455 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
456 let len = self.serialized_len();
457 if buf.len() < len {
458 return Err(Error::OutputBufferTooSmall {
459 need: len,
460 have: buf.len(),
461 });
462 }
463 let rc = check_report_count(&self.report_blocks)?;
464 let hdr = CommonHeader::new(rc, PT_SENDER_REPORT, length_words_minus_one(len));
465 hdr.write(&mut buf[0..RTCP_HEADER_LEN]);
466 let mut off = RTCP_HEADER_LEN;
467 buf[off..off + 4].copy_from_slice(&self.ssrc.to_be_bytes());
468 buf[off + 4..off + 8].copy_from_slice(&self.ntp_msw.to_be_bytes());
469 buf[off + 8..off + 12].copy_from_slice(&self.ntp_lsw.to_be_bytes());
470 buf[off + 12..off + 16].copy_from_slice(&self.rtp_timestamp.to_be_bytes());
471 buf[off + 16..off + 20].copy_from_slice(&self.packet_count.to_be_bytes());
472 buf[off + 20..off + 24].copy_from_slice(&self.octet_count.to_be_bytes());
473 off += WORD_LEN + SR_SENDER_INFO_LEN;
474 for block in &self.report_blocks {
475 block.serialize_into(&mut buf[off..off + REPORT_BLOCK_LEN])?;
476 off += REPORT_BLOCK_LEN;
477 }
478 Ok(len)
479 }
480}
481
482#[derive(Debug, Clone, PartialEq, Eq)]
488#[cfg_attr(feature = "serde", derive(serde::Serialize))]
489pub struct ReceiverReport {
490 pub ssrc: u32,
492 pub report_blocks: Vec<ReportBlock>,
494}
495
496impl<'a> Parse<'a> for ReceiverReport {
497 type Error = Error;
498
499 fn parse(bytes: &'a [u8]) -> Result<Self> {
500 let hdr = CommonHeader::parse(bytes)?;
501 if hdr.packet_type != PT_RECEIVER_REPORT {
502 return Err(Error::InvalidValue {
503 field: "rtcp_pt",
504 value: hdr.packet_type as u64,
505 reason: "expected RR (201)",
506 });
507 }
508 let total = hdr.total_len();
509 if bytes.len() < total {
510 return Err(Error::BufferTooShort {
511 need: total,
512 have: bytes.len(),
513 what: "RTCP RR",
514 });
515 }
516 let body = &bytes[RTCP_HEADER_LEN..total];
517 let ssrc = be_u32(body, 0, "RR ssrc")?;
518 let report_blocks = parse_report_blocks(&body[WORD_LEN..], hdr.count as usize)?;
519 Ok(ReceiverReport {
520 ssrc,
521 report_blocks,
522 })
523 }
524}
525
526impl Serialize for ReceiverReport {
527 type Error = Error;
528
529 fn serialized_len(&self) -> usize {
530 RTCP_HEADER_LEN + WORD_LEN + self.report_blocks.len() * REPORT_BLOCK_LEN
531 }
532
533 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
534 let len = self.serialized_len();
535 if buf.len() < len {
536 return Err(Error::OutputBufferTooSmall {
537 need: len,
538 have: buf.len(),
539 });
540 }
541 let rc = check_report_count(&self.report_blocks)?;
542 let hdr = CommonHeader::new(rc, PT_RECEIVER_REPORT, length_words_minus_one(len));
543 hdr.write(&mut buf[0..RTCP_HEADER_LEN]);
544 let mut off = RTCP_HEADER_LEN;
545 buf[off..off + 4].copy_from_slice(&self.ssrc.to_be_bytes());
546 off += WORD_LEN;
547 for block in &self.report_blocks {
548 block.serialize_into(&mut buf[off..off + REPORT_BLOCK_LEN])?;
549 off += REPORT_BLOCK_LEN;
550 }
551 Ok(len)
552 }
553}
554
555#[derive(Debug, Clone, Copy, PartialEq, Eq)]
561#[cfg_attr(feature = "serde", derive(serde::Serialize))]
562#[non_exhaustive]
563pub enum SdesItemType {
564 CName,
566 Name,
568 Email,
570 Phone,
572 Loc,
574 Tool,
576 Note,
578 Priv,
580 Unknown(u8),
582}
583
584pub const SDES_CNAME: u8 = 1;
586pub const SDES_NAME: u8 = 2;
588pub const SDES_EMAIL: u8 = 3;
590pub const SDES_PHONE: u8 = 4;
592pub const SDES_LOC: u8 = 5;
594pub const SDES_TOOL: u8 = 6;
596pub const SDES_NOTE: u8 = 7;
598pub const SDES_PRIV: u8 = 8;
600const SDES_TERMINATOR: u8 = 0;
602
603impl SdesItemType {
604 pub fn from_type(t: u8) -> Self {
606 match t {
607 SDES_CNAME => SdesItemType::CName,
608 SDES_NAME => SdesItemType::Name,
609 SDES_EMAIL => SdesItemType::Email,
610 SDES_PHONE => SdesItemType::Phone,
611 SDES_LOC => SdesItemType::Loc,
612 SDES_TOOL => SdesItemType::Tool,
613 SDES_NOTE => SdesItemType::Note,
614 SDES_PRIV => SdesItemType::Priv,
615 other => SdesItemType::Unknown(other),
616 }
617 }
618
619 pub fn item_type(&self) -> u8 {
621 match self {
622 SdesItemType::CName => SDES_CNAME,
623 SdesItemType::Name => SDES_NAME,
624 SdesItemType::Email => SDES_EMAIL,
625 SdesItemType::Phone => SDES_PHONE,
626 SdesItemType::Loc => SDES_LOC,
627 SdesItemType::Tool => SDES_TOOL,
628 SdesItemType::Note => SDES_NOTE,
629 SdesItemType::Priv => SDES_PRIV,
630 SdesItemType::Unknown(t) => *t,
631 }
632 }
633
634 pub fn name(&self) -> &'static str {
636 match self {
637 SdesItemType::CName => "CNAME",
638 SdesItemType::Name => "NAME",
639 SdesItemType::Email => "EMAIL",
640 SdesItemType::Phone => "PHONE",
641 SdesItemType::Loc => "LOC",
642 SdesItemType::Tool => "TOOL",
643 SdesItemType::Note => "NOTE",
644 SdesItemType::Priv => "PRIV",
645 SdesItemType::Unknown(_) => "reserved",
646 }
647 }
648}
649
650broadcast_common::impl_spec_display!(SdesItemType, Unknown);
651
652#[derive(Debug, Clone, PartialEq, Eq)]
654#[cfg_attr(feature = "serde", derive(serde::Serialize))]
655pub struct SdesItem {
656 pub item_type: SdesItemType,
658 pub text: String,
660}
661
662#[derive(Debug, Clone, PartialEq, Eq)]
664#[cfg_attr(feature = "serde", derive(serde::Serialize))]
665pub struct SdesChunk {
666 pub source: u32,
668 pub items: Vec<SdesItem>,
670}
671
672impl SdesChunk {
673 fn unpadded_len(&self) -> usize {
676 WORD_LEN + self.items.iter().map(|it| 2 + it.text.len()).sum::<usize>() + 1
677 }
678
679 fn padded_len(&self) -> usize {
681 self.unpadded_len().div_ceil(WORD_LEN) * WORD_LEN
682 }
683}
684
685#[derive(Debug, Clone, PartialEq, Eq)]
687#[cfg_attr(feature = "serde", derive(serde::Serialize))]
688pub struct SourceDescription {
689 pub chunks: Vec<SdesChunk>,
691}
692
693impl<'a> Parse<'a> for SourceDescription {
694 type Error = Error;
695
696 fn parse(bytes: &'a [u8]) -> Result<Self> {
697 let hdr = CommonHeader::parse(bytes)?;
698 if hdr.packet_type != PT_SOURCE_DESCRIPTION {
699 return Err(Error::InvalidValue {
700 field: "rtcp_pt",
701 value: hdr.packet_type as u64,
702 reason: "expected SDES (202)",
703 });
704 }
705 let total = hdr.total_len();
706 if bytes.len() < total {
707 return Err(Error::BufferTooShort {
708 need: total,
709 have: bytes.len(),
710 what: "RTCP SDES",
711 });
712 }
713 let body = &bytes[RTCP_HEADER_LEN..total];
714 let mut chunks = Vec::with_capacity(hdr.count as usize);
715 let mut off = 0;
716 for _ in 0..hdr.count {
717 let (chunk, consumed) = parse_sdes_chunk(&body[off..])?;
718 chunks.push(chunk);
719 off += consumed;
720 }
721 Ok(SourceDescription { chunks })
722 }
723}
724
725fn parse_sdes_chunk(bytes: &[u8]) -> Result<(SdesChunk, usize)> {
728 let source = be_u32(bytes, 0, "SDES chunk source")?;
729 let mut off = WORD_LEN;
730 let mut items = Vec::new();
731 loop {
732 let t = *bytes.get(off).ok_or(Error::BufferTooShort {
733 need: off + 1,
734 have: bytes.len(),
735 what: "SDES item type",
736 })?;
737 off += 1;
738 if t == SDES_TERMINATOR {
739 break;
740 }
741 let len = *bytes.get(off).ok_or(Error::BufferTooShort {
742 need: off + 1,
743 have: bytes.len(),
744 what: "SDES item length",
745 })? as usize;
746 off += 1;
747 let end = off + len;
748 let text_bytes = bytes.get(off..end).ok_or(Error::BufferTooShort {
749 need: end,
750 have: bytes.len(),
751 what: "SDES item text",
752 })?;
753 let text = String::from_utf8(text_bytes.to_vec()).map_err(|_| Error::InvalidValue {
754 field: "sdes_item_text",
755 value: 0,
756 reason: "not valid UTF-8",
757 })?;
758 items.push(SdesItem {
759 item_type: SdesItemType::from_type(t),
760 text,
761 });
762 off = end;
763 }
764 let padded = off.div_ceil(WORD_LEN) * WORD_LEN;
766 if padded > bytes.len() {
767 return Err(Error::BufferTooShort {
768 need: padded,
769 have: bytes.len(),
770 what: "SDES chunk padding",
771 });
772 }
773 Ok((SdesChunk { source, items }, padded))
774}
775
776impl Serialize for SourceDescription {
777 type Error = Error;
778
779 fn serialized_len(&self) -> usize {
780 RTCP_HEADER_LEN + self.chunks.iter().map(SdesChunk::padded_len).sum::<usize>()
781 }
782
783 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
784 let len = self.serialized_len();
785 if buf.len() < len {
786 return Err(Error::OutputBufferTooSmall {
787 need: len,
788 have: buf.len(),
789 });
790 }
791 if self.chunks.len() > MAX_COUNT {
792 return Err(Error::InvalidValue {
793 field: "rtcp_source_count",
794 value: self.chunks.len() as u64,
795 reason: "exceeds 5-bit SC field",
796 });
797 }
798 let hdr = CommonHeader::new(
799 self.chunks.len() as u8,
800 PT_SOURCE_DESCRIPTION,
801 length_words_minus_one(len),
802 );
803 hdr.write(&mut buf[0..RTCP_HEADER_LEN]);
804 let mut off = RTCP_HEADER_LEN;
805 for chunk in &self.chunks {
806 let padded = chunk.padded_len();
807 for b in buf[off..off + padded].iter_mut() {
809 *b = 0;
810 }
811 buf[off..off + 4].copy_from_slice(&chunk.source.to_be_bytes());
812 let mut io = off + WORD_LEN;
813 for item in &chunk.items {
814 if item.text.len() > u8::MAX as usize {
815 return Err(Error::InvalidValue {
816 field: "sdes_item_len",
817 value: item.text.len() as u64,
818 reason: "exceeds 8-bit SDES item length",
819 });
820 }
821 buf[io] = item.item_type.item_type();
822 buf[io + 1] = item.text.len() as u8;
823 buf[io + 2..io + 2 + item.text.len()].copy_from_slice(item.text.as_bytes());
824 io += 2 + item.text.len();
825 }
826 off += padded;
828 }
829 Ok(len)
830 }
831}
832
833#[derive(Debug, Clone, PartialEq, Eq)]
839#[cfg_attr(feature = "serde", derive(serde::Serialize))]
840pub struct Bye {
841 pub sources: Vec<u32>,
843 pub reason: Option<String>,
845}
846
847impl Bye {
848 fn reason_unpadded_len(&self) -> usize {
850 match &self.reason {
851 Some(r) => 1 + r.len(),
852 None => 0,
853 }
854 }
855}
856
857impl<'a> Parse<'a> for Bye {
858 type Error = Error;
859
860 fn parse(bytes: &'a [u8]) -> Result<Self> {
861 let hdr = CommonHeader::parse(bytes)?;
862 if hdr.packet_type != PT_BYE {
863 return Err(Error::InvalidValue {
864 field: "rtcp_pt",
865 value: hdr.packet_type as u64,
866 reason: "expected BYE (203)",
867 });
868 }
869 let total = hdr.total_len();
870 if bytes.len() < total {
871 return Err(Error::BufferTooShort {
872 need: total,
873 have: bytes.len(),
874 what: "RTCP BYE",
875 });
876 }
877 let body = &bytes[RTCP_HEADER_LEN..total];
878 let sc = hdr.count as usize;
879 if body.len() < sc * WORD_LEN {
880 return Err(Error::BufferTooShort {
881 need: sc * WORD_LEN,
882 have: body.len(),
883 what: "RTCP BYE sources",
884 });
885 }
886 let mut sources = Vec::with_capacity(sc);
887 let mut off = 0;
888 for _ in 0..sc {
889 sources.push(be_u32(body, off, "BYE source")?);
890 off += WORD_LEN;
891 }
892 let reason = if off < body.len() {
894 let len = body[off] as usize;
895 off += 1;
896 let end = off + len;
897 if end > body.len() {
898 return Err(Error::BufferTooShort {
899 need: end,
900 have: body.len(),
901 what: "RTCP BYE reason text",
902 });
903 }
904 let text =
905 String::from_utf8(body[off..end].to_vec()).map_err(|_| Error::InvalidValue {
906 field: "bye_reason",
907 value: 0,
908 reason: "not valid UTF-8",
909 })?;
910 Some(text)
911 } else {
912 None
913 };
914 Ok(Bye { sources, reason })
915 }
916}
917
918impl Serialize for Bye {
919 type Error = Error;
920
921 fn serialized_len(&self) -> usize {
922 let raw = RTCP_HEADER_LEN + self.sources.len() * WORD_LEN + self.reason_unpadded_len();
923 raw.div_ceil(WORD_LEN) * WORD_LEN
924 }
925
926 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
927 let len = self.serialized_len();
928 if buf.len() < len {
929 return Err(Error::OutputBufferTooSmall {
930 need: len,
931 have: buf.len(),
932 });
933 }
934 if self.sources.len() > MAX_COUNT {
935 return Err(Error::InvalidValue {
936 field: "rtcp_source_count",
937 value: self.sources.len() as u64,
938 reason: "exceeds 5-bit SC field",
939 });
940 }
941 if let Some(r) = &self.reason
942 && r.len() > u8::MAX as usize
943 {
944 return Err(Error::InvalidValue {
945 field: "bye_reason_len",
946 value: r.len() as u64,
947 reason: "exceeds 8-bit reason length",
948 });
949 }
950 for b in buf[..len].iter_mut() {
952 *b = 0;
953 }
954 let hdr = CommonHeader::new(
955 self.sources.len() as u8,
956 PT_BYE,
957 length_words_minus_one(len),
958 );
959 hdr.write(&mut buf[0..RTCP_HEADER_LEN]);
960 let mut off = RTCP_HEADER_LEN;
961 for src in &self.sources {
962 buf[off..off + 4].copy_from_slice(&src.to_be_bytes());
963 off += WORD_LEN;
964 }
965 if let Some(r) = &self.reason {
966 buf[off] = r.len() as u8;
967 off += 1;
968 buf[off..off + r.len()].copy_from_slice(r.as_bytes());
969 }
970 Ok(len)
971 }
972}
973
974#[derive(Debug, Clone, PartialEq, Eq)]
980#[cfg_attr(feature = "serde", derive(serde::Serialize))]
981pub struct App {
982 pub subtype: u8,
984 pub ssrc: u32,
986 pub name: [u8; APP_NAME_LEN],
988 pub data: Vec<u8>,
990}
991
992impl<'a> Parse<'a> for App {
993 type Error = Error;
994
995 fn parse(bytes: &'a [u8]) -> Result<Self> {
996 let hdr = CommonHeader::parse(bytes)?;
997 if hdr.packet_type != PT_APP {
998 return Err(Error::InvalidValue {
999 field: "rtcp_pt",
1000 value: hdr.packet_type as u64,
1001 reason: "expected APP (204)",
1002 });
1003 }
1004 let total = hdr.total_len();
1005 if bytes.len() < total {
1006 return Err(Error::BufferTooShort {
1007 need: total,
1008 have: bytes.len(),
1009 what: "RTCP APP",
1010 });
1011 }
1012 let body = &bytes[RTCP_HEADER_LEN..total];
1013 if body.len() < WORD_LEN + APP_NAME_LEN {
1014 return Err(Error::BufferTooShort {
1015 need: WORD_LEN + APP_NAME_LEN,
1016 have: body.len(),
1017 what: "RTCP APP ssrc+name",
1018 });
1019 }
1020 let ssrc = be_u32(body, 0, "APP ssrc")?;
1021 let mut name = [0u8; APP_NAME_LEN];
1022 name.copy_from_slice(&body[WORD_LEN..WORD_LEN + APP_NAME_LEN]);
1023 let data = body[WORD_LEN + APP_NAME_LEN..].to_vec();
1024 Ok(App {
1025 subtype: hdr.count,
1026 ssrc,
1027 name,
1028 data,
1029 })
1030 }
1031}
1032
1033impl Serialize for App {
1034 type Error = Error;
1035
1036 fn serialized_len(&self) -> usize {
1037 RTCP_HEADER_LEN + WORD_LEN + APP_NAME_LEN + self.data.len()
1038 }
1039
1040 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
1041 let len = self.serialized_len();
1042 if buf.len() < len {
1043 return Err(Error::OutputBufferTooSmall {
1044 need: len,
1045 have: buf.len(),
1046 });
1047 }
1048 if !len.is_multiple_of(WORD_LEN) {
1049 return Err(Error::InvalidValue {
1050 field: "app_data_len",
1051 value: self.data.len() as u64,
1052 reason: "APP data must be 32-bit aligned",
1053 });
1054 }
1055 if self.subtype > RTCP_COUNT_MASK {
1056 return Err(Error::InvalidValue {
1057 field: "app_subtype",
1058 value: self.subtype as u64,
1059 reason: "exceeds 5-bit subtype field",
1060 });
1061 }
1062 let hdr = CommonHeader::new(self.subtype, PT_APP, length_words_minus_one(len));
1063 hdr.write(&mut buf[0..RTCP_HEADER_LEN]);
1064 let mut off = RTCP_HEADER_LEN;
1065 buf[off..off + 4].copy_from_slice(&self.ssrc.to_be_bytes());
1066 off += WORD_LEN;
1067 buf[off..off + APP_NAME_LEN].copy_from_slice(&self.name);
1068 off += APP_NAME_LEN;
1069 buf[off..off + self.data.len()].copy_from_slice(&self.data);
1070 Ok(len)
1071 }
1072}
1073
1074#[derive(Debug, Clone, PartialEq, Eq)]
1081#[cfg_attr(feature = "serde", derive(serde::Serialize))]
1082#[non_exhaustive]
1083pub enum RtcpPacket {
1084 SenderReport(SenderReport),
1086 ReceiverReport(ReceiverReport),
1088 SourceDescription(SourceDescription),
1090 Bye(Bye),
1092 App(App),
1094}
1095
1096impl RtcpPacket {
1097 pub fn packet_type(&self) -> RtcpPacketType {
1099 match self {
1100 RtcpPacket::SenderReport(_) => RtcpPacketType::SenderReport,
1101 RtcpPacket::ReceiverReport(_) => RtcpPacketType::ReceiverReport,
1102 RtcpPacket::SourceDescription(_) => RtcpPacketType::SourceDescription,
1103 RtcpPacket::Bye(_) => RtcpPacketType::Bye,
1104 RtcpPacket::App(_) => RtcpPacketType::App,
1105 }
1106 }
1107
1108 pub fn name(&self) -> &'static str {
1110 self.packet_type().name()
1111 }
1112
1113 fn is_report(&self) -> bool {
1116 matches!(
1117 self,
1118 RtcpPacket::SenderReport(_) | RtcpPacket::ReceiverReport(_)
1119 )
1120 }
1121}
1122
1123broadcast_common::impl_spec_display!(RtcpPacket);
1124
1125impl<'a> Parse<'a> for RtcpPacket {
1126 type Error = Error;
1127
1128 fn parse(bytes: &'a [u8]) -> Result<Self> {
1129 let hdr = CommonHeader::parse(bytes)?;
1130 Ok(match RtcpPacketType::from_pt(hdr.packet_type) {
1131 RtcpPacketType::SenderReport => RtcpPacket::SenderReport(SenderReport::parse(bytes)?),
1132 RtcpPacketType::ReceiverReport => {
1133 RtcpPacket::ReceiverReport(ReceiverReport::parse(bytes)?)
1134 }
1135 RtcpPacketType::SourceDescription => {
1136 RtcpPacket::SourceDescription(SourceDescription::parse(bytes)?)
1137 }
1138 RtcpPacketType::Bye => RtcpPacket::Bye(Bye::parse(bytes)?),
1139 RtcpPacketType::App => RtcpPacket::App(App::parse(bytes)?),
1140 RtcpPacketType::Unknown(pt) => {
1141 return Err(Error::InvalidValue {
1142 field: "rtcp_pt",
1143 value: pt as u64,
1144 reason: "not an RFC 3550 §6 core packet type",
1145 });
1146 }
1147 })
1148 }
1149}
1150
1151impl Serialize for RtcpPacket {
1152 type Error = Error;
1153
1154 fn serialized_len(&self) -> usize {
1155 match self {
1156 RtcpPacket::SenderReport(p) => p.serialized_len(),
1157 RtcpPacket::ReceiverReport(p) => p.serialized_len(),
1158 RtcpPacket::SourceDescription(p) => p.serialized_len(),
1159 RtcpPacket::Bye(p) => p.serialized_len(),
1160 RtcpPacket::App(p) => p.serialized_len(),
1161 }
1162 }
1163
1164 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
1165 match self {
1166 RtcpPacket::SenderReport(p) => p.serialize_into(buf),
1167 RtcpPacket::ReceiverReport(p) => p.serialize_into(buf),
1168 RtcpPacket::SourceDescription(p) => p.serialize_into(buf),
1169 RtcpPacket::Bye(p) => p.serialize_into(buf),
1170 RtcpPacket::App(p) => p.serialize_into(buf),
1171 }
1172 }
1173}
1174
1175#[derive(Debug, Clone, PartialEq, Eq)]
1183#[cfg_attr(feature = "serde", derive(serde::Serialize))]
1184pub struct CompoundPacket {
1185 pub packets: Vec<RtcpPacket>,
1187}
1188
1189impl CompoundPacket {
1190 pub fn new(packets: Vec<RtcpPacket>) -> Result<Self> {
1192 let cp = CompoundPacket { packets };
1193 cp.check_leading_report()?;
1194 Ok(cp)
1195 }
1196
1197 fn check_leading_report(&self) -> Result<()> {
1199 match self.packets.first() {
1200 Some(p) if p.is_report() => Ok(()),
1201 Some(_) => Err(Error::InvalidValue {
1202 field: "rtcp_compound",
1203 value: self.packets[0].packet_type().pt() as u64,
1204 reason: "compound packet must begin with SR or RR (RFC 3550 §6.1)",
1205 }),
1206 None => Err(Error::InvalidInput("empty RTCP compound packet")),
1207 }
1208 }
1209}
1210
1211impl<'a> Parse<'a> for CompoundPacket {
1212 type Error = Error;
1213
1214 fn parse(bytes: &'a [u8]) -> Result<Self> {
1215 let mut packets = Vec::new();
1216 let mut off = 0;
1217 while off < bytes.len() {
1218 let hdr = CommonHeader::parse(&bytes[off..])?;
1219 let total = hdr.total_len();
1220 let end = off + total;
1221 if end > bytes.len() {
1222 return Err(Error::BufferTooShort {
1223 need: end,
1224 have: bytes.len(),
1225 what: "RTCP compound sub-packet",
1226 });
1227 }
1228 packets.push(RtcpPacket::parse(&bytes[off..end])?);
1229 off = end;
1230 }
1231 let cp = CompoundPacket { packets };
1232 cp.check_leading_report()?;
1233 Ok(cp)
1234 }
1235}
1236
1237impl Serialize for CompoundPacket {
1238 type Error = Error;
1239
1240 fn serialized_len(&self) -> usize {
1241 self.packets.iter().map(RtcpPacket::serialized_len).sum()
1242 }
1243
1244 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
1245 self.check_leading_report()?;
1246 let len = self.serialized_len();
1247 if buf.len() < len {
1248 return Err(Error::OutputBufferTooSmall {
1249 need: len,
1250 have: buf.len(),
1251 });
1252 }
1253 let mut off = 0;
1254 for pkt in &self.packets {
1255 let n = pkt.serialize_into(&mut buf[off..])?;
1256 off += n;
1257 }
1258 Ok(off)
1259 }
1260}
1261
1262#[cfg(test)]
1263mod tests {
1264 use super::*;
1265 use alloc::string::ToString;
1266 use alloc::vec;
1267
1268 fn sample_block(ssrc: u32, jitter: u32, cumulative: i32) -> ReportBlock {
1269 ReportBlock {
1270 ssrc,
1271 fraction_lost: 12,
1272 cumulative_lost: cumulative,
1273 ext_highest_seq: 0x0001_2345,
1274 jitter,
1275 lsr: 0xAABB_CCDD,
1276 dlsr: 0x0000_1000,
1277 }
1278 }
1279
1280 fn sample_sr() -> SenderReport {
1281 SenderReport {
1282 ssrc: 0x1122_3344,
1283 ntp_msw: 0xE0E1_E2E3,
1284 ntp_lsw: 0x1020_3040,
1285 rtp_timestamp: 0x0009_0000,
1286 packet_count: 4321,
1287 octet_count: 999_999,
1288 report_blocks: vec![
1289 sample_block(0xAAAA_AAAA, 500, 17),
1290 sample_block(0xBBBB_BBBB, 750, -3),
1291 ],
1292 }
1293 }
1294
1295 #[test]
1296 fn report_block_round_trip() {
1297 let b = sample_block(0xDEAD_BEEF, 4242, -5);
1298 let bytes = b.to_bytes();
1299 assert_eq!(bytes.len(), REPORT_BLOCK_LEN);
1300 let parsed = ReportBlock::parse(&bytes).unwrap();
1301 assert_eq!(parsed, b);
1302 assert_eq!(parsed.to_bytes(), bytes);
1303 }
1304
1305 #[test]
1306 fn report_block_negative_cumulative_lost() {
1307 for v in [-1_i32, -2, -100, -0x80_0000, 0, 5, 0x7F_FFFF] {
1309 let b = sample_block(1, 0, v);
1310 let parsed = ReportBlock::parse(&b.to_bytes()).unwrap();
1311 assert_eq!(parsed.cumulative_lost, v, "cumulative_lost {v} round-trip");
1312 }
1313 }
1314
1315 #[test]
1316 fn sr_round_trip_and_header_layout() {
1317 let sr = sample_sr();
1318 let bytes = sr.to_bytes();
1319 assert_eq!(bytes[0] >> 6, 2);
1321 assert_eq!(bytes[0] & 0x1F, 2);
1322 assert_eq!(bytes[1], PT_SENDER_REPORT);
1324 let total_words = bytes.len() / 4;
1326 assert_eq!(
1327 u16::from_be_bytes([bytes[2], bytes[3]]) as usize,
1328 total_words - 1
1329 );
1330 let parsed = SenderReport::parse(&bytes).unwrap();
1331 assert_eq!(parsed, sr);
1332 assert_eq!(parsed.to_bytes(), bytes);
1333 }
1334
1335 #[test]
1336 fn sr_two_report_blocks_boundary() {
1337 let sr = sample_sr();
1338 assert_eq!(sr.report_blocks.len(), 2);
1339 let bytes = sr.to_bytes();
1340 assert_eq!(bytes.len(), 4 + 24 + 2 * REPORT_BLOCK_LEN);
1342 assert_eq!(bytes.len() % 4, 0);
1343 let parsed = SenderReport::parse(&bytes).unwrap();
1344 assert_eq!(parsed.report_blocks.len(), 2);
1345 assert_eq!(
1346 u16::from_be_bytes([bytes[2], bytes[3]]) as usize,
1347 bytes.len() / 4 - 1
1348 );
1349 }
1350
1351 #[test]
1352 fn rr_round_trip() {
1353 let rr = ReceiverReport {
1354 ssrc: 0x0102_0304,
1355 report_blocks: vec![sample_block(0xCAFE_BABE, 33, -7)],
1356 };
1357 let bytes = rr.to_bytes();
1358 assert_eq!(bytes[1], PT_RECEIVER_REPORT);
1359 let parsed = ReceiverReport::parse(&bytes).unwrap();
1360 assert_eq!(parsed, rr);
1361 assert_eq!(parsed.to_bytes(), bytes);
1362 }
1363
1364 #[test]
1365 fn sdes_round_trip_cname_tool() {
1366 let sdes = SourceDescription {
1367 chunks: vec![SdesChunk {
1368 source: 0x1234_5678,
1369 items: vec![
1370 SdesItem {
1371 item_type: SdesItemType::CName,
1372 text: "alice@example.com".to_string(),
1373 },
1374 SdesItem {
1375 item_type: SdesItemType::Tool,
1376 text: "transmux/1.0".to_string(),
1377 },
1378 ],
1379 }],
1380 };
1381 let bytes = sdes.to_bytes();
1382 assert_eq!(bytes[1], PT_SOURCE_DESCRIPTION);
1383 assert_eq!(bytes.len() % 4, 0);
1384 let parsed = SourceDescription::parse(&bytes).unwrap();
1385 assert_eq!(parsed, sdes);
1386 assert_eq!(parsed.to_bytes(), bytes);
1387 }
1388
1389 #[test]
1390 fn bye_round_trip_with_reason() {
1391 let bye = Bye {
1392 sources: vec![0x1111_1111, 0x2222_2222],
1393 reason: Some("teardown".to_string()),
1394 };
1395 let bytes = bye.to_bytes();
1396 assert_eq!(bytes[1], PT_BYE);
1397 assert_eq!(bytes[0] & 0x1F, 2); assert_eq!(bytes.len() % 4, 0);
1399 let parsed = Bye::parse(&bytes).unwrap();
1400 assert_eq!(parsed, bye);
1401 assert_eq!(parsed.to_bytes(), bytes);
1402 }
1403
1404 #[test]
1405 fn bye_round_trip_no_reason() {
1406 let bye = Bye {
1407 sources: vec![0xABCD_0000],
1408 reason: None,
1409 };
1410 let parsed = Bye::parse(&bye.to_bytes()).unwrap();
1411 assert_eq!(parsed, bye);
1412 }
1413
1414 #[test]
1415 fn app_round_trip() {
1416 let app = App {
1417 subtype: 3,
1418 ssrc: 0x9988_7766,
1419 name: *b"TMUX",
1420 data: vec![0xDE, 0xAD, 0xBE, 0xEF, 0x01, 0x02, 0x03, 0x04],
1421 };
1422 let bytes = app.to_bytes();
1423 assert_eq!(bytes[1], PT_APP);
1424 assert_eq!(bytes[0] & 0x1F, 3); let parsed = App::parse(&bytes).unwrap();
1426 assert_eq!(parsed, app);
1427 assert_eq!(parsed.to_bytes(), bytes);
1428 }
1429
1430 #[test]
1431 fn sr_mutation_bites_packet_count() {
1432 let sr = sample_sr();
1433 let mut bytes = sr.to_bytes();
1434 let orig = SenderReport::parse(&bytes).unwrap();
1435 let pc_off = RTCP_HEADER_LEN + 16;
1437 bytes[pc_off] ^= 0xFF;
1438 let mutated = SenderReport::parse(&bytes).unwrap();
1439 assert_ne!(mutated.packet_count, orig.packet_count);
1440 assert_eq!(mutated.to_bytes(), bytes);
1442 }
1443
1444 #[test]
1445 fn report_block_mutation_bites_jitter() {
1446 let mut sr = sample_sr();
1447 sr.report_blocks[0].jitter = 500;
1448 let before = sr.to_bytes();
1449 sr.report_blocks[0].jitter = 999;
1450 let after = sr.to_bytes();
1451 assert_ne!(before, after);
1452 let parsed = SenderReport::parse(&after).unwrap();
1453 assert_eq!(parsed.report_blocks[0].jitter, 999);
1454 }
1455
1456 #[test]
1457 fn compound_sr_sdes_round_trip() {
1458 let sdes = SourceDescription {
1459 chunks: vec![SdesChunk {
1460 source: 0x1122_3344,
1461 items: vec![SdesItem {
1462 item_type: SdesItemType::CName,
1463 text: "cn".to_string(),
1464 }],
1465 }],
1466 };
1467 let cp = CompoundPacket::new(vec![
1468 RtcpPacket::SenderReport(sample_sr()),
1469 RtcpPacket::SourceDescription(sdes),
1470 ])
1471 .unwrap();
1472 let bytes = cp.to_bytes();
1473 let parsed = CompoundPacket::parse(&bytes).unwrap();
1474 assert_eq!(parsed.packets.len(), 2);
1475 assert_eq!(parsed, cp);
1476 assert_eq!(parsed.to_bytes(), bytes);
1477 }
1478
1479 #[test]
1480 fn compound_must_start_with_report() {
1481 let err = CompoundPacket::new(vec![RtcpPacket::Bye(Bye {
1483 sources: vec![1],
1484 reason: None,
1485 })]);
1486 assert!(err.is_err());
1487 let bye = Bye {
1489 sources: vec![1],
1490 reason: None,
1491 };
1492 let bytes = bye.to_bytes();
1493 assert!(CompoundPacket::parse(&bytes).is_err());
1494 }
1495
1496 #[test]
1497 fn any_packet_dispatch() {
1498 let bytes = sample_sr().to_bytes();
1499 let any = RtcpPacket::parse(&bytes).unwrap();
1500 assert_eq!(any.packet_type(), RtcpPacketType::SenderReport);
1501 assert_eq!(any.name(), "SR");
1502 assert_eq!(any.to_bytes(), bytes);
1503 }
1504
1505 #[test]
1506 fn packet_type_display() {
1507 assert_eq!(RtcpPacketType::SenderReport.to_string(), "SR");
1508 assert_eq!(RtcpPacketType::Unknown(207).to_string(), "reserved(0xCF)");
1509 assert_eq!(SdesItemType::CName.to_string(), "CNAME");
1510 }
1511
1512 #[test]
1513 fn rejects_bad_version() {
1514 let mut bytes = sample_sr().to_bytes();
1515 bytes[0] = 0x40; assert!(SenderReport::parse(&bytes).is_err());
1517 }
1518}