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) -> Result<u16> {
247 let words = (total_len / WORD_LEN).saturating_sub(1);
248 u16::try_from(words).map_err(|_| Error::InvalidValue {
249 field: "rtcp_length",
250 value: total_len as u64,
251 reason: "packet exceeds the 16-bit RTCP length field (max 262140 bytes)",
252 })
253}
254
255#[derive(Debug, Clone, Copy, PartialEq, Eq)]
262#[cfg_attr(feature = "serde", derive(serde::Serialize))]
263pub struct ReportBlock {
264 pub ssrc: u32,
266 pub fraction_lost: u8,
268 pub cumulative_lost: i32,
270 pub ext_highest_seq: u32,
272 pub jitter: u32,
274 pub lsr: u32,
276 pub dlsr: u32,
278}
279
280impl ReportBlock {
281 fn decode_cumulative_lost(raw: u32) -> i32 {
283 const SIGN_BIT: u32 = 1 << 23;
285 if raw & SIGN_BIT != 0 {
286 (raw | 0xFF00_0000) as i32
287 } else {
288 raw as i32
289 }
290 }
291
292 fn encode_cumulative_lost(&self) -> u32 {
294 (self.cumulative_lost as u32) & 0x00FF_FFFF
295 }
296}
297
298impl<'a> Parse<'a> for ReportBlock {
299 type Error = Error;
300
301 fn parse(bytes: &'a [u8]) -> Result<Self> {
302 if bytes.len() < REPORT_BLOCK_LEN {
303 return Err(Error::BufferTooShort {
304 need: REPORT_BLOCK_LEN,
305 have: bytes.len(),
306 what: "RTCP report block",
307 });
308 }
309 let ssrc = be_u32(bytes, 0, "report block ssrc")?;
310 let fraction_lost = bytes[4];
311 let cumulative_raw = u32::from_be_bytes([0, bytes[5], bytes[6], bytes[7]]);
312 Ok(ReportBlock {
313 ssrc,
314 fraction_lost,
315 cumulative_lost: ReportBlock::decode_cumulative_lost(cumulative_raw),
316 ext_highest_seq: be_u32(bytes, 8, "report block ext seq")?,
317 jitter: be_u32(bytes, 12, "report block jitter")?,
318 lsr: be_u32(bytes, 16, "report block lsr")?,
319 dlsr: be_u32(bytes, 20, "report block dlsr")?,
320 })
321 }
322}
323
324impl Serialize for ReportBlock {
325 type Error = Error;
326
327 fn serialized_len(&self) -> usize {
328 REPORT_BLOCK_LEN
329 }
330
331 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
332 if buf.len() < REPORT_BLOCK_LEN {
333 return Err(Error::OutputBufferTooSmall {
334 need: REPORT_BLOCK_LEN,
335 have: buf.len(),
336 });
337 }
338 buf[0..4].copy_from_slice(&self.ssrc.to_be_bytes());
339 buf[4] = self.fraction_lost;
340 let cum = self.encode_cumulative_lost().to_be_bytes();
341 buf[5..8].copy_from_slice(&cum[1..4]);
342 buf[8..12].copy_from_slice(&self.ext_highest_seq.to_be_bytes());
343 buf[12..16].copy_from_slice(&self.jitter.to_be_bytes());
344 buf[16..20].copy_from_slice(&self.lsr.to_be_bytes());
345 buf[20..24].copy_from_slice(&self.dlsr.to_be_bytes());
346 Ok(REPORT_BLOCK_LEN)
347 }
348}
349
350fn parse_report_blocks(bytes: &[u8], count: usize) -> Result<Vec<ReportBlock>> {
352 let mut blocks = Vec::with_capacity(count);
353 let mut off = 0;
354 for _ in 0..count {
355 let end = off + REPORT_BLOCK_LEN;
356 if end > bytes.len() {
357 return Err(Error::BufferTooShort {
358 need: end,
359 have: bytes.len(),
360 what: "RTCP report blocks",
361 });
362 }
363 blocks.push(ReportBlock::parse(&bytes[off..end])?);
364 off = end;
365 }
366 Ok(blocks)
367}
368
369fn check_report_count(blocks: &[ReportBlock]) -> Result<u8> {
371 if blocks.len() > MAX_COUNT {
372 return Err(Error::InvalidValue {
373 field: "rtcp_report_count",
374 value: blocks.len() as u64,
375 reason: "exceeds 5-bit RC field",
376 });
377 }
378 Ok(blocks.len() as u8)
379}
380
381#[derive(Debug, Clone, PartialEq, Eq)]
387#[cfg_attr(feature = "serde", derive(serde::Serialize))]
388pub struct SenderReport {
389 pub ssrc: u32,
391 pub ntp_msw: u32,
393 pub ntp_lsw: u32,
395 pub rtp_timestamp: u32,
397 pub packet_count: u32,
399 pub octet_count: u32,
401 pub report_blocks: Vec<ReportBlock>,
403}
404
405impl<'a> Parse<'a> for SenderReport {
406 type Error = Error;
407
408 fn parse(bytes: &'a [u8]) -> Result<Self> {
409 let hdr = CommonHeader::parse(bytes)?;
410 if hdr.packet_type != PT_SENDER_REPORT {
411 return Err(Error::InvalidValue {
412 field: "rtcp_pt",
413 value: hdr.packet_type as u64,
414 reason: "expected SR (200)",
415 });
416 }
417 let total = hdr.total_len();
418 if bytes.len() < total {
419 return Err(Error::BufferTooShort {
420 need: total,
421 have: bytes.len(),
422 what: "RTCP SR",
423 });
424 }
425 let body = &bytes[RTCP_HEADER_LEN..total];
426 if body.len() < WORD_LEN + SR_SENDER_INFO_LEN {
427 return Err(Error::BufferTooShort {
428 need: WORD_LEN + SR_SENDER_INFO_LEN,
429 have: body.len(),
430 what: "RTCP SR sender info",
431 });
432 }
433 let ssrc = be_u32(body, 0, "SR ssrc")?;
434 let ntp_msw = be_u32(body, 4, "SR ntp msw")?;
435 let ntp_lsw = be_u32(body, 8, "SR ntp lsw")?;
436 let rtp_timestamp = be_u32(body, 12, "SR rtp ts")?;
437 let packet_count = be_u32(body, 16, "SR packet count")?;
438 let octet_count = be_u32(body, 20, "SR octet count")?;
439 let blocks_off = WORD_LEN + SR_SENDER_INFO_LEN;
440 let report_blocks = parse_report_blocks(&body[blocks_off..], hdr.count as usize)?;
441 Ok(SenderReport {
442 ssrc,
443 ntp_msw,
444 ntp_lsw,
445 rtp_timestamp,
446 packet_count,
447 octet_count,
448 report_blocks,
449 })
450 }
451}
452
453impl Serialize for SenderReport {
454 type Error = Error;
455
456 fn serialized_len(&self) -> usize {
457 RTCP_HEADER_LEN
458 + WORD_LEN
459 + SR_SENDER_INFO_LEN
460 + self.report_blocks.len() * REPORT_BLOCK_LEN
461 }
462
463 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
464 let len = self.serialized_len();
465 if buf.len() < len {
466 return Err(Error::OutputBufferTooSmall {
467 need: len,
468 have: buf.len(),
469 });
470 }
471 let rc = check_report_count(&self.report_blocks)?;
472 let hdr = CommonHeader::new(rc, PT_SENDER_REPORT, length_words_minus_one(len)?);
473 hdr.write(&mut buf[0..RTCP_HEADER_LEN]);
474 let mut off = RTCP_HEADER_LEN;
475 buf[off..off + 4].copy_from_slice(&self.ssrc.to_be_bytes());
476 buf[off + 4..off + 8].copy_from_slice(&self.ntp_msw.to_be_bytes());
477 buf[off + 8..off + 12].copy_from_slice(&self.ntp_lsw.to_be_bytes());
478 buf[off + 12..off + 16].copy_from_slice(&self.rtp_timestamp.to_be_bytes());
479 buf[off + 16..off + 20].copy_from_slice(&self.packet_count.to_be_bytes());
480 buf[off + 20..off + 24].copy_from_slice(&self.octet_count.to_be_bytes());
481 off += WORD_LEN + SR_SENDER_INFO_LEN;
482 for block in &self.report_blocks {
483 block.serialize_into(&mut buf[off..off + REPORT_BLOCK_LEN])?;
484 off += REPORT_BLOCK_LEN;
485 }
486 Ok(len)
487 }
488}
489
490#[derive(Debug, Clone, PartialEq, Eq)]
496#[cfg_attr(feature = "serde", derive(serde::Serialize))]
497pub struct ReceiverReport {
498 pub ssrc: u32,
500 pub report_blocks: Vec<ReportBlock>,
502}
503
504impl<'a> Parse<'a> for ReceiverReport {
505 type Error = Error;
506
507 fn parse(bytes: &'a [u8]) -> Result<Self> {
508 let hdr = CommonHeader::parse(bytes)?;
509 if hdr.packet_type != PT_RECEIVER_REPORT {
510 return Err(Error::InvalidValue {
511 field: "rtcp_pt",
512 value: hdr.packet_type as u64,
513 reason: "expected RR (201)",
514 });
515 }
516 let total = hdr.total_len();
517 if bytes.len() < total {
518 return Err(Error::BufferTooShort {
519 need: total,
520 have: bytes.len(),
521 what: "RTCP RR",
522 });
523 }
524 let body = &bytes[RTCP_HEADER_LEN..total];
525 let ssrc = be_u32(body, 0, "RR ssrc")?;
526 let report_blocks = parse_report_blocks(&body[WORD_LEN..], hdr.count as usize)?;
527 Ok(ReceiverReport {
528 ssrc,
529 report_blocks,
530 })
531 }
532}
533
534impl Serialize for ReceiverReport {
535 type Error = Error;
536
537 fn serialized_len(&self) -> usize {
538 RTCP_HEADER_LEN + WORD_LEN + self.report_blocks.len() * REPORT_BLOCK_LEN
539 }
540
541 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
542 let len = self.serialized_len();
543 if buf.len() < len {
544 return Err(Error::OutputBufferTooSmall {
545 need: len,
546 have: buf.len(),
547 });
548 }
549 let rc = check_report_count(&self.report_blocks)?;
550 let hdr = CommonHeader::new(rc, PT_RECEIVER_REPORT, length_words_minus_one(len)?);
551 hdr.write(&mut buf[0..RTCP_HEADER_LEN]);
552 let mut off = RTCP_HEADER_LEN;
553 buf[off..off + 4].copy_from_slice(&self.ssrc.to_be_bytes());
554 off += WORD_LEN;
555 for block in &self.report_blocks {
556 block.serialize_into(&mut buf[off..off + REPORT_BLOCK_LEN])?;
557 off += REPORT_BLOCK_LEN;
558 }
559 Ok(len)
560 }
561}
562
563#[derive(Debug, Clone, Copy, PartialEq, Eq)]
569#[cfg_attr(feature = "serde", derive(serde::Serialize))]
570#[non_exhaustive]
571pub enum SdesItemType {
572 CName,
574 Name,
576 Email,
578 Phone,
580 Loc,
582 Tool,
584 Note,
586 Priv,
588 Unknown(u8),
590}
591
592pub const SDES_CNAME: u8 = 1;
594pub const SDES_NAME: u8 = 2;
596pub const SDES_EMAIL: u8 = 3;
598pub const SDES_PHONE: u8 = 4;
600pub const SDES_LOC: u8 = 5;
602pub const SDES_TOOL: u8 = 6;
604pub const SDES_NOTE: u8 = 7;
606pub const SDES_PRIV: u8 = 8;
608const SDES_TERMINATOR: u8 = 0;
610
611impl SdesItemType {
612 pub fn from_type(t: u8) -> Self {
614 match t {
615 SDES_CNAME => SdesItemType::CName,
616 SDES_NAME => SdesItemType::Name,
617 SDES_EMAIL => SdesItemType::Email,
618 SDES_PHONE => SdesItemType::Phone,
619 SDES_LOC => SdesItemType::Loc,
620 SDES_TOOL => SdesItemType::Tool,
621 SDES_NOTE => SdesItemType::Note,
622 SDES_PRIV => SdesItemType::Priv,
623 other => SdesItemType::Unknown(other),
624 }
625 }
626
627 pub fn item_type(&self) -> u8 {
629 match self {
630 SdesItemType::CName => SDES_CNAME,
631 SdesItemType::Name => SDES_NAME,
632 SdesItemType::Email => SDES_EMAIL,
633 SdesItemType::Phone => SDES_PHONE,
634 SdesItemType::Loc => SDES_LOC,
635 SdesItemType::Tool => SDES_TOOL,
636 SdesItemType::Note => SDES_NOTE,
637 SdesItemType::Priv => SDES_PRIV,
638 SdesItemType::Unknown(t) => *t,
639 }
640 }
641
642 pub fn name(&self) -> &'static str {
644 match self {
645 SdesItemType::CName => "CNAME",
646 SdesItemType::Name => "NAME",
647 SdesItemType::Email => "EMAIL",
648 SdesItemType::Phone => "PHONE",
649 SdesItemType::Loc => "LOC",
650 SdesItemType::Tool => "TOOL",
651 SdesItemType::Note => "NOTE",
652 SdesItemType::Priv => "PRIV",
653 SdesItemType::Unknown(_) => "reserved",
654 }
655 }
656}
657
658broadcast_common::impl_spec_display!(SdesItemType, Unknown);
659
660#[derive(Debug, Clone, PartialEq, Eq)]
662#[cfg_attr(feature = "serde", derive(serde::Serialize))]
663pub struct SdesItem {
664 pub item_type: SdesItemType,
666 pub text: String,
668}
669
670#[derive(Debug, Clone, PartialEq, Eq)]
672#[cfg_attr(feature = "serde", derive(serde::Serialize))]
673pub struct SdesChunk {
674 pub source: u32,
676 pub items: Vec<SdesItem>,
678}
679
680impl SdesChunk {
681 fn unpadded_len(&self) -> usize {
684 WORD_LEN + self.items.iter().map(|it| 2 + it.text.len()).sum::<usize>() + 1
685 }
686
687 fn padded_len(&self) -> usize {
689 self.unpadded_len().div_ceil(WORD_LEN) * WORD_LEN
690 }
691}
692
693#[derive(Debug, Clone, PartialEq, Eq)]
695#[cfg_attr(feature = "serde", derive(serde::Serialize))]
696pub struct SourceDescription {
697 pub chunks: Vec<SdesChunk>,
699}
700
701impl<'a> Parse<'a> for SourceDescription {
702 type Error = Error;
703
704 fn parse(bytes: &'a [u8]) -> Result<Self> {
705 let hdr = CommonHeader::parse(bytes)?;
706 if hdr.packet_type != PT_SOURCE_DESCRIPTION {
707 return Err(Error::InvalidValue {
708 field: "rtcp_pt",
709 value: hdr.packet_type as u64,
710 reason: "expected SDES (202)",
711 });
712 }
713 let total = hdr.total_len();
714 if bytes.len() < total {
715 return Err(Error::BufferTooShort {
716 need: total,
717 have: bytes.len(),
718 what: "RTCP SDES",
719 });
720 }
721 let body = &bytes[RTCP_HEADER_LEN..total];
722 let mut chunks = Vec::with_capacity(hdr.count as usize);
723 let mut off = 0;
724 for _ in 0..hdr.count {
725 let (chunk, consumed) = parse_sdes_chunk(&body[off..])?;
726 chunks.push(chunk);
727 off += consumed;
728 }
729 Ok(SourceDescription { chunks })
730 }
731}
732
733fn parse_sdes_chunk(bytes: &[u8]) -> Result<(SdesChunk, usize)> {
736 let source = be_u32(bytes, 0, "SDES chunk source")?;
737 let mut off = WORD_LEN;
738 let mut items = Vec::new();
739 loop {
740 let t = *bytes.get(off).ok_or(Error::BufferTooShort {
741 need: off + 1,
742 have: bytes.len(),
743 what: "SDES item type",
744 })?;
745 off += 1;
746 if t == SDES_TERMINATOR {
747 break;
748 }
749 let len = *bytes.get(off).ok_or(Error::BufferTooShort {
750 need: off + 1,
751 have: bytes.len(),
752 what: "SDES item length",
753 })? as usize;
754 off += 1;
755 let end = off + len;
756 let text_bytes = bytes.get(off..end).ok_or(Error::BufferTooShort {
757 need: end,
758 have: bytes.len(),
759 what: "SDES item text",
760 })?;
761 let text = String::from_utf8(text_bytes.to_vec()).map_err(|_| Error::InvalidValue {
762 field: "sdes_item_text",
763 value: 0,
764 reason: "not valid UTF-8",
765 })?;
766 items.push(SdesItem {
767 item_type: SdesItemType::from_type(t),
768 text,
769 });
770 off = end;
771 }
772 let padded = off.div_ceil(WORD_LEN) * WORD_LEN;
774 if padded > bytes.len() {
775 return Err(Error::BufferTooShort {
776 need: padded,
777 have: bytes.len(),
778 what: "SDES chunk padding",
779 });
780 }
781 Ok((SdesChunk { source, items }, padded))
782}
783
784impl Serialize for SourceDescription {
785 type Error = Error;
786
787 fn serialized_len(&self) -> usize {
788 RTCP_HEADER_LEN + self.chunks.iter().map(SdesChunk::padded_len).sum::<usize>()
789 }
790
791 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
792 let len = self.serialized_len();
793 if buf.len() < len {
794 return Err(Error::OutputBufferTooSmall {
795 need: len,
796 have: buf.len(),
797 });
798 }
799 if self.chunks.len() > MAX_COUNT {
800 return Err(Error::InvalidValue {
801 field: "rtcp_source_count",
802 value: self.chunks.len() as u64,
803 reason: "exceeds 5-bit SC field",
804 });
805 }
806 let hdr = CommonHeader::new(
807 self.chunks.len() as u8,
808 PT_SOURCE_DESCRIPTION,
809 length_words_minus_one(len)?,
810 );
811 hdr.write(&mut buf[0..RTCP_HEADER_LEN]);
812 let mut off = RTCP_HEADER_LEN;
813 for chunk in &self.chunks {
814 let padded = chunk.padded_len();
815 for b in buf[off..off + padded].iter_mut() {
817 *b = 0;
818 }
819 buf[off..off + 4].copy_from_slice(&chunk.source.to_be_bytes());
820 let mut io = off + WORD_LEN;
821 for item in &chunk.items {
822 if item.text.len() > u8::MAX as usize {
823 return Err(Error::InvalidValue {
824 field: "sdes_item_len",
825 value: item.text.len() as u64,
826 reason: "exceeds 8-bit SDES item length",
827 });
828 }
829 buf[io] = item.item_type.item_type();
830 buf[io + 1] = item.text.len() as u8;
831 buf[io + 2..io + 2 + item.text.len()].copy_from_slice(item.text.as_bytes());
832 io += 2 + item.text.len();
833 }
834 off += padded;
836 }
837 Ok(len)
838 }
839}
840
841#[derive(Debug, Clone, PartialEq, Eq)]
847#[cfg_attr(feature = "serde", derive(serde::Serialize))]
848pub struct Bye {
849 pub sources: Vec<u32>,
851 pub reason: Option<String>,
853}
854
855impl Bye {
856 fn reason_unpadded_len(&self) -> usize {
858 match &self.reason {
859 Some(r) => 1 + r.len(),
860 None => 0,
861 }
862 }
863}
864
865impl<'a> Parse<'a> for Bye {
866 type Error = Error;
867
868 fn parse(bytes: &'a [u8]) -> Result<Self> {
869 let hdr = CommonHeader::parse(bytes)?;
870 if hdr.packet_type != PT_BYE {
871 return Err(Error::InvalidValue {
872 field: "rtcp_pt",
873 value: hdr.packet_type as u64,
874 reason: "expected BYE (203)",
875 });
876 }
877 let total = hdr.total_len();
878 if bytes.len() < total {
879 return Err(Error::BufferTooShort {
880 need: total,
881 have: bytes.len(),
882 what: "RTCP BYE",
883 });
884 }
885 let body = &bytes[RTCP_HEADER_LEN..total];
886 let sc = hdr.count as usize;
887 if body.len() < sc * WORD_LEN {
888 return Err(Error::BufferTooShort {
889 need: sc * WORD_LEN,
890 have: body.len(),
891 what: "RTCP BYE sources",
892 });
893 }
894 let mut sources = Vec::with_capacity(sc);
895 let mut off = 0;
896 for _ in 0..sc {
897 sources.push(be_u32(body, off, "BYE source")?);
898 off += WORD_LEN;
899 }
900 let reason = if off < body.len() {
902 let len = body[off] as usize;
903 off += 1;
904 let end = off + len;
905 if end > body.len() {
906 return Err(Error::BufferTooShort {
907 need: end,
908 have: body.len(),
909 what: "RTCP BYE reason text",
910 });
911 }
912 let text =
913 String::from_utf8(body[off..end].to_vec()).map_err(|_| Error::InvalidValue {
914 field: "bye_reason",
915 value: 0,
916 reason: "not valid UTF-8",
917 })?;
918 Some(text)
919 } else {
920 None
921 };
922 Ok(Bye { sources, reason })
923 }
924}
925
926impl Serialize for Bye {
927 type Error = Error;
928
929 fn serialized_len(&self) -> usize {
930 let raw = RTCP_HEADER_LEN + self.sources.len() * WORD_LEN + self.reason_unpadded_len();
931 raw.div_ceil(WORD_LEN) * WORD_LEN
932 }
933
934 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
935 let len = self.serialized_len();
936 if buf.len() < len {
937 return Err(Error::OutputBufferTooSmall {
938 need: len,
939 have: buf.len(),
940 });
941 }
942 if self.sources.len() > MAX_COUNT {
943 return Err(Error::InvalidValue {
944 field: "rtcp_source_count",
945 value: self.sources.len() as u64,
946 reason: "exceeds 5-bit SC field",
947 });
948 }
949 if let Some(r) = &self.reason
950 && r.len() > u8::MAX as usize
951 {
952 return Err(Error::InvalidValue {
953 field: "bye_reason_len",
954 value: r.len() as u64,
955 reason: "exceeds 8-bit reason length",
956 });
957 }
958 for b in buf[..len].iter_mut() {
960 *b = 0;
961 }
962 let hdr = CommonHeader::new(
963 self.sources.len() as u8,
964 PT_BYE,
965 length_words_minus_one(len)?,
966 );
967 hdr.write(&mut buf[0..RTCP_HEADER_LEN]);
968 let mut off = RTCP_HEADER_LEN;
969 for src in &self.sources {
970 buf[off..off + 4].copy_from_slice(&src.to_be_bytes());
971 off += WORD_LEN;
972 }
973 if let Some(r) = &self.reason {
974 buf[off] = r.len() as u8;
975 off += 1;
976 buf[off..off + r.len()].copy_from_slice(r.as_bytes());
977 }
978 Ok(len)
979 }
980}
981
982#[derive(Debug, Clone, PartialEq, Eq)]
988#[cfg_attr(feature = "serde", derive(serde::Serialize))]
989pub struct App {
990 pub subtype: u8,
992 pub ssrc: u32,
994 pub name: [u8; APP_NAME_LEN],
996 pub data: Vec<u8>,
998}
999
1000impl<'a> Parse<'a> for App {
1001 type Error = Error;
1002
1003 fn parse(bytes: &'a [u8]) -> Result<Self> {
1004 let hdr = CommonHeader::parse(bytes)?;
1005 if hdr.packet_type != PT_APP {
1006 return Err(Error::InvalidValue {
1007 field: "rtcp_pt",
1008 value: hdr.packet_type as u64,
1009 reason: "expected APP (204)",
1010 });
1011 }
1012 let total = hdr.total_len();
1013 if bytes.len() < total {
1014 return Err(Error::BufferTooShort {
1015 need: total,
1016 have: bytes.len(),
1017 what: "RTCP APP",
1018 });
1019 }
1020 let body = &bytes[RTCP_HEADER_LEN..total];
1021 if body.len() < WORD_LEN + APP_NAME_LEN {
1022 return Err(Error::BufferTooShort {
1023 need: WORD_LEN + APP_NAME_LEN,
1024 have: body.len(),
1025 what: "RTCP APP ssrc+name",
1026 });
1027 }
1028 let ssrc = be_u32(body, 0, "APP ssrc")?;
1029 let mut name = [0u8; APP_NAME_LEN];
1030 name.copy_from_slice(&body[WORD_LEN..WORD_LEN + APP_NAME_LEN]);
1031 let data = body[WORD_LEN + APP_NAME_LEN..].to_vec();
1032 Ok(App {
1033 subtype: hdr.count,
1034 ssrc,
1035 name,
1036 data,
1037 })
1038 }
1039}
1040
1041impl Serialize for App {
1042 type Error = Error;
1043
1044 fn serialized_len(&self) -> usize {
1045 RTCP_HEADER_LEN + WORD_LEN + APP_NAME_LEN + self.data.len()
1046 }
1047
1048 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
1049 let len = self.serialized_len();
1050 if buf.len() < len {
1051 return Err(Error::OutputBufferTooSmall {
1052 need: len,
1053 have: buf.len(),
1054 });
1055 }
1056 if !len.is_multiple_of(WORD_LEN) {
1057 return Err(Error::InvalidValue {
1058 field: "app_data_len",
1059 value: self.data.len() as u64,
1060 reason: "APP data must be 32-bit aligned",
1061 });
1062 }
1063 if self.subtype > RTCP_COUNT_MASK {
1064 return Err(Error::InvalidValue {
1065 field: "app_subtype",
1066 value: self.subtype as u64,
1067 reason: "exceeds 5-bit subtype field",
1068 });
1069 }
1070 let hdr = CommonHeader::new(self.subtype, PT_APP, length_words_minus_one(len)?);
1071 hdr.write(&mut buf[0..RTCP_HEADER_LEN]);
1072 let mut off = RTCP_HEADER_LEN;
1073 buf[off..off + 4].copy_from_slice(&self.ssrc.to_be_bytes());
1074 off += WORD_LEN;
1075 buf[off..off + APP_NAME_LEN].copy_from_slice(&self.name);
1076 off += APP_NAME_LEN;
1077 buf[off..off + self.data.len()].copy_from_slice(&self.data);
1078 Ok(len)
1079 }
1080}
1081
1082#[derive(Debug, Clone, PartialEq, Eq)]
1089#[cfg_attr(feature = "serde", derive(serde::Serialize))]
1090#[non_exhaustive]
1091pub enum RtcpPacket {
1092 SenderReport(SenderReport),
1094 ReceiverReport(ReceiverReport),
1096 SourceDescription(SourceDescription),
1098 Bye(Bye),
1100 App(App),
1102}
1103
1104impl RtcpPacket {
1105 pub fn packet_type(&self) -> RtcpPacketType {
1107 match self {
1108 RtcpPacket::SenderReport(_) => RtcpPacketType::SenderReport,
1109 RtcpPacket::ReceiverReport(_) => RtcpPacketType::ReceiverReport,
1110 RtcpPacket::SourceDescription(_) => RtcpPacketType::SourceDescription,
1111 RtcpPacket::Bye(_) => RtcpPacketType::Bye,
1112 RtcpPacket::App(_) => RtcpPacketType::App,
1113 }
1114 }
1115
1116 pub fn name(&self) -> &'static str {
1118 self.packet_type().name()
1119 }
1120
1121 fn is_report(&self) -> bool {
1124 matches!(
1125 self,
1126 RtcpPacket::SenderReport(_) | RtcpPacket::ReceiverReport(_)
1127 )
1128 }
1129}
1130
1131broadcast_common::impl_spec_display!(RtcpPacket);
1132
1133impl<'a> Parse<'a> for RtcpPacket {
1134 type Error = Error;
1135
1136 fn parse(bytes: &'a [u8]) -> Result<Self> {
1137 let hdr = CommonHeader::parse(bytes)?;
1138 Ok(match RtcpPacketType::from_pt(hdr.packet_type) {
1139 RtcpPacketType::SenderReport => RtcpPacket::SenderReport(SenderReport::parse(bytes)?),
1140 RtcpPacketType::ReceiverReport => {
1141 RtcpPacket::ReceiverReport(ReceiverReport::parse(bytes)?)
1142 }
1143 RtcpPacketType::SourceDescription => {
1144 RtcpPacket::SourceDescription(SourceDescription::parse(bytes)?)
1145 }
1146 RtcpPacketType::Bye => RtcpPacket::Bye(Bye::parse(bytes)?),
1147 RtcpPacketType::App => RtcpPacket::App(App::parse(bytes)?),
1148 RtcpPacketType::Unknown(pt) => {
1149 return Err(Error::InvalidValue {
1150 field: "rtcp_pt",
1151 value: pt as u64,
1152 reason: "not an RFC 3550 §6 core packet type",
1153 });
1154 }
1155 })
1156 }
1157}
1158
1159impl Serialize for RtcpPacket {
1160 type Error = Error;
1161
1162 fn serialized_len(&self) -> usize {
1163 match self {
1164 RtcpPacket::SenderReport(p) => p.serialized_len(),
1165 RtcpPacket::ReceiverReport(p) => p.serialized_len(),
1166 RtcpPacket::SourceDescription(p) => p.serialized_len(),
1167 RtcpPacket::Bye(p) => p.serialized_len(),
1168 RtcpPacket::App(p) => p.serialized_len(),
1169 }
1170 }
1171
1172 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
1173 match self {
1174 RtcpPacket::SenderReport(p) => p.serialize_into(buf),
1175 RtcpPacket::ReceiverReport(p) => p.serialize_into(buf),
1176 RtcpPacket::SourceDescription(p) => p.serialize_into(buf),
1177 RtcpPacket::Bye(p) => p.serialize_into(buf),
1178 RtcpPacket::App(p) => p.serialize_into(buf),
1179 }
1180 }
1181}
1182
1183#[derive(Debug, Clone, PartialEq, Eq)]
1191#[cfg_attr(feature = "serde", derive(serde::Serialize))]
1192pub struct CompoundPacket {
1193 pub packets: Vec<RtcpPacket>,
1195}
1196
1197impl CompoundPacket {
1198 pub fn new(packets: Vec<RtcpPacket>) -> Result<Self> {
1200 let cp = CompoundPacket { packets };
1201 cp.check_leading_report()?;
1202 Ok(cp)
1203 }
1204
1205 fn check_leading_report(&self) -> Result<()> {
1207 match self.packets.first() {
1208 Some(p) if p.is_report() => Ok(()),
1209 Some(_) => Err(Error::InvalidValue {
1210 field: "rtcp_compound",
1211 value: self.packets[0].packet_type().pt() as u64,
1212 reason: "compound packet must begin with SR or RR (RFC 3550 §6.1)",
1213 }),
1214 None => Err(Error::InvalidInput("empty RTCP compound packet")),
1215 }
1216 }
1217}
1218
1219impl<'a> Parse<'a> for CompoundPacket {
1220 type Error = Error;
1221
1222 fn parse(bytes: &'a [u8]) -> Result<Self> {
1223 let mut packets = Vec::new();
1224 let mut off = 0;
1225 while off < bytes.len() {
1226 let hdr = CommonHeader::parse(&bytes[off..])?;
1227 let total = hdr.total_len();
1228 let end = off + total;
1229 if end > bytes.len() {
1230 return Err(Error::BufferTooShort {
1231 need: end,
1232 have: bytes.len(),
1233 what: "RTCP compound sub-packet",
1234 });
1235 }
1236 packets.push(RtcpPacket::parse(&bytes[off..end])?);
1237 off = end;
1238 }
1239 let cp = CompoundPacket { packets };
1240 cp.check_leading_report()?;
1241 Ok(cp)
1242 }
1243}
1244
1245impl Serialize for CompoundPacket {
1246 type Error = Error;
1247
1248 fn serialized_len(&self) -> usize {
1249 self.packets.iter().map(RtcpPacket::serialized_len).sum()
1250 }
1251
1252 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
1253 self.check_leading_report()?;
1254 let len = self.serialized_len();
1255 if buf.len() < len {
1256 return Err(Error::OutputBufferTooSmall {
1257 need: len,
1258 have: buf.len(),
1259 });
1260 }
1261 let mut off = 0;
1262 for pkt in &self.packets {
1263 let n = pkt.serialize_into(&mut buf[off..])?;
1264 off += n;
1265 }
1266 Ok(off)
1267 }
1268}
1269
1270#[cfg(test)]
1271mod tests {
1272 use super::*;
1273 use alloc::string::ToString;
1274 use alloc::vec;
1275
1276 fn sample_block(ssrc: u32, jitter: u32, cumulative: i32) -> ReportBlock {
1277 ReportBlock {
1278 ssrc,
1279 fraction_lost: 12,
1280 cumulative_lost: cumulative,
1281 ext_highest_seq: 0x0001_2345,
1282 jitter,
1283 lsr: 0xAABB_CCDD,
1284 dlsr: 0x0000_1000,
1285 }
1286 }
1287
1288 fn sample_sr() -> SenderReport {
1289 SenderReport {
1290 ssrc: 0x1122_3344,
1291 ntp_msw: 0xE0E1_E2E3,
1292 ntp_lsw: 0x1020_3040,
1293 rtp_timestamp: 0x0009_0000,
1294 packet_count: 4321,
1295 octet_count: 999_999,
1296 report_blocks: vec![
1297 sample_block(0xAAAA_AAAA, 500, 17),
1298 sample_block(0xBBBB_BBBB, 750, -3),
1299 ],
1300 }
1301 }
1302
1303 #[test]
1304 fn report_block_round_trip() {
1305 let b = sample_block(0xDEAD_BEEF, 4242, -5);
1306 let bytes = b.to_bytes();
1307 assert_eq!(bytes.len(), REPORT_BLOCK_LEN);
1308 let parsed = ReportBlock::parse(&bytes).unwrap();
1309 assert_eq!(parsed, b);
1310 assert_eq!(parsed.to_bytes(), bytes);
1311 }
1312
1313 #[test]
1314 fn report_block_negative_cumulative_lost() {
1315 for v in [-1_i32, -2, -100, -0x80_0000, 0, 5, 0x7F_FFFF] {
1317 let b = sample_block(1, 0, v);
1318 let parsed = ReportBlock::parse(&b.to_bytes()).unwrap();
1319 assert_eq!(parsed.cumulative_lost, v, "cumulative_lost {v} round-trip");
1320 }
1321 }
1322
1323 #[test]
1324 fn sr_round_trip_and_header_layout() {
1325 let sr = sample_sr();
1326 let bytes = sr.to_bytes();
1327 assert_eq!(bytes[0] >> 6, 2);
1329 assert_eq!(bytes[0] & 0x1F, 2);
1330 assert_eq!(bytes[1], PT_SENDER_REPORT);
1332 let total_words = bytes.len() / 4;
1334 assert_eq!(
1335 u16::from_be_bytes([bytes[2], bytes[3]]) as usize,
1336 total_words - 1
1337 );
1338 let parsed = SenderReport::parse(&bytes).unwrap();
1339 assert_eq!(parsed, sr);
1340 assert_eq!(parsed.to_bytes(), bytes);
1341 }
1342
1343 #[test]
1344 fn sr_two_report_blocks_boundary() {
1345 let sr = sample_sr();
1346 assert_eq!(sr.report_blocks.len(), 2);
1347 let bytes = sr.to_bytes();
1348 assert_eq!(bytes.len(), 4 + 24 + 2 * REPORT_BLOCK_LEN);
1350 assert_eq!(bytes.len() % 4, 0);
1351 let parsed = SenderReport::parse(&bytes).unwrap();
1352 assert_eq!(parsed.report_blocks.len(), 2);
1353 assert_eq!(
1354 u16::from_be_bytes([bytes[2], bytes[3]]) as usize,
1355 bytes.len() / 4 - 1
1356 );
1357 }
1358
1359 #[test]
1360 fn rr_round_trip() {
1361 let rr = ReceiverReport {
1362 ssrc: 0x0102_0304,
1363 report_blocks: vec![sample_block(0xCAFE_BABE, 33, -7)],
1364 };
1365 let bytes = rr.to_bytes();
1366 assert_eq!(bytes[1], PT_RECEIVER_REPORT);
1367 let parsed = ReceiverReport::parse(&bytes).unwrap();
1368 assert_eq!(parsed, rr);
1369 assert_eq!(parsed.to_bytes(), bytes);
1370 }
1371
1372 #[test]
1373 fn sdes_round_trip_cname_tool() {
1374 let sdes = SourceDescription {
1375 chunks: vec![SdesChunk {
1376 source: 0x1234_5678,
1377 items: vec![
1378 SdesItem {
1379 item_type: SdesItemType::CName,
1380 text: "alice@example.com".to_string(),
1381 },
1382 SdesItem {
1383 item_type: SdesItemType::Tool,
1384 text: "transmux/1.0".to_string(),
1385 },
1386 ],
1387 }],
1388 };
1389 let bytes = sdes.to_bytes();
1390 assert_eq!(bytes[1], PT_SOURCE_DESCRIPTION);
1391 assert_eq!(bytes.len() % 4, 0);
1392 let parsed = SourceDescription::parse(&bytes).unwrap();
1393 assert_eq!(parsed, sdes);
1394 assert_eq!(parsed.to_bytes(), bytes);
1395 }
1396
1397 #[test]
1398 fn bye_round_trip_with_reason() {
1399 let bye = Bye {
1400 sources: vec![0x1111_1111, 0x2222_2222],
1401 reason: Some("teardown".to_string()),
1402 };
1403 let bytes = bye.to_bytes();
1404 assert_eq!(bytes[1], PT_BYE);
1405 assert_eq!(bytes[0] & 0x1F, 2); assert_eq!(bytes.len() % 4, 0);
1407 let parsed = Bye::parse(&bytes).unwrap();
1408 assert_eq!(parsed, bye);
1409 assert_eq!(parsed.to_bytes(), bytes);
1410 }
1411
1412 #[test]
1413 fn bye_round_trip_no_reason() {
1414 let bye = Bye {
1415 sources: vec![0xABCD_0000],
1416 reason: None,
1417 };
1418 let parsed = Bye::parse(&bye.to_bytes()).unwrap();
1419 assert_eq!(parsed, bye);
1420 }
1421
1422 #[test]
1423 fn app_round_trip() {
1424 let app = App {
1425 subtype: 3,
1426 ssrc: 0x9988_7766,
1427 name: *b"TMUX",
1428 data: vec![0xDE, 0xAD, 0xBE, 0xEF, 0x01, 0x02, 0x03, 0x04],
1429 };
1430 let bytes = app.to_bytes();
1431 assert_eq!(bytes[1], PT_APP);
1432 assert_eq!(bytes[0] & 0x1F, 3); let parsed = App::parse(&bytes).unwrap();
1434 assert_eq!(parsed, app);
1435 assert_eq!(parsed.to_bytes(), bytes);
1436 }
1437
1438 #[test]
1439 fn sr_mutation_bites_packet_count() {
1440 let sr = sample_sr();
1441 let mut bytes = sr.to_bytes();
1442 let orig = SenderReport::parse(&bytes).unwrap();
1443 let pc_off = RTCP_HEADER_LEN + 16;
1445 bytes[pc_off] ^= 0xFF;
1446 let mutated = SenderReport::parse(&bytes).unwrap();
1447 assert_ne!(mutated.packet_count, orig.packet_count);
1448 assert_eq!(mutated.to_bytes(), bytes);
1450 }
1451
1452 #[test]
1453 fn report_block_mutation_bites_jitter() {
1454 let mut sr = sample_sr();
1455 sr.report_blocks[0].jitter = 500;
1456 let before = sr.to_bytes();
1457 sr.report_blocks[0].jitter = 999;
1458 let after = sr.to_bytes();
1459 assert_ne!(before, after);
1460 let parsed = SenderReport::parse(&after).unwrap();
1461 assert_eq!(parsed.report_blocks[0].jitter, 999);
1462 }
1463
1464 #[test]
1465 fn compound_sr_sdes_round_trip() {
1466 let sdes = SourceDescription {
1467 chunks: vec![SdesChunk {
1468 source: 0x1122_3344,
1469 items: vec![SdesItem {
1470 item_type: SdesItemType::CName,
1471 text: "cn".to_string(),
1472 }],
1473 }],
1474 };
1475 let cp = CompoundPacket::new(vec![
1476 RtcpPacket::SenderReport(sample_sr()),
1477 RtcpPacket::SourceDescription(sdes),
1478 ])
1479 .unwrap();
1480 let bytes = cp.to_bytes();
1481 let parsed = CompoundPacket::parse(&bytes).unwrap();
1482 assert_eq!(parsed.packets.len(), 2);
1483 assert_eq!(parsed, cp);
1484 assert_eq!(parsed.to_bytes(), bytes);
1485 }
1486
1487 #[test]
1488 fn compound_must_start_with_report() {
1489 let err = CompoundPacket::new(vec![RtcpPacket::Bye(Bye {
1491 sources: vec![1],
1492 reason: None,
1493 })]);
1494 assert!(err.is_err());
1495 let bye = Bye {
1497 sources: vec![1],
1498 reason: None,
1499 };
1500 let bytes = bye.to_bytes();
1501 assert!(CompoundPacket::parse(&bytes).is_err());
1502 }
1503
1504 #[test]
1505 fn any_packet_dispatch() {
1506 let bytes = sample_sr().to_bytes();
1507 let any = RtcpPacket::parse(&bytes).unwrap();
1508 assert_eq!(any.packet_type(), RtcpPacketType::SenderReport);
1509 assert_eq!(any.name(), "SR");
1510 assert_eq!(any.to_bytes(), bytes);
1511 }
1512
1513 #[test]
1514 fn packet_type_display() {
1515 assert_eq!(RtcpPacketType::SenderReport.to_string(), "SR");
1516 assert_eq!(RtcpPacketType::Unknown(207).to_string(), "reserved(0xCF)");
1517 assert_eq!(SdesItemType::CName.to_string(), "CNAME");
1518 }
1519
1520 #[test]
1521 fn app_oversized_payload_rejected_not_wrapped() {
1522 let app = App {
1526 subtype: 0,
1527 ssrc: 1,
1528 name: *b"BIGP",
1529 data: vec![0u8; 262_136],
1530 };
1531 let len = app.serialized_len();
1532 let mut buf = vec![0u8; len];
1533 let err = app.serialize_into(&mut buf).unwrap_err();
1534 assert!(matches!(
1535 err,
1536 Error::InvalidValue {
1537 field: "rtcp_length",
1538 ..
1539 }
1540 ));
1541 }
1542
1543 #[test]
1544 fn rejects_bad_version() {
1545 let mut bytes = sample_sr().to_bytes();
1546 bytes[0] = 0x40; assert!(SenderReport::parse(&bytes).is_err());
1548 }
1549}