rtp_parse/rtcp/
rtcp_header.rs

1use std::fmt::Debug;
2
3use anyhow::{anyhow, Result};
4use parsely_rs::*;
5
6/// https://datatracker.ietf.org/doc/html/rfc3550#section-6.1
7///  0                   1                   2                   3
8///  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
9/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
10/// |V=2|P|    SC   |  PT=SDES=202  |             length            |
11/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
12///
13/// length: 16 bits
14///   The length of this RTCP packet in 32-bit words minus one,
15///   including the header and any padding.  (The offset of one makes
16///   zero a valid length and avoids a possible infinite loop in
17///   scanning a compound RTCP packet, while counting 32-bit words
18///   avoids a validity check for a multiple of 4.)
19#[derive(Clone, Debug, PartialEq, Eq, ParselyRead, ParselyWrite)]
20#[parsely_write(sync_args("payload_length_bytes: u16", "report_count_value: u5"))]
21pub struct RtcpHeader {
22    #[parsely(assertion = "|v: &u2| *v == 2")]
23    pub version: u2,
24    pub has_padding: bool,
25    #[parsely_write(sync_expr = "report_count_value")]
26    pub report_count: u5,
27    pub packet_type: u8,
28    #[parsely_write(sync_expr = "payload_length_bytes / 4")]
29    pub length_field: u16,
30}
31
32impl Default for RtcpHeader {
33    fn default() -> Self {
34        Self {
35            version: u2::new(2),
36            has_padding: Default::default(),
37            report_count: Default::default(),
38            packet_type: Default::default(),
39            length_field: Default::default(),
40        }
41    }
42}
43
44// When decrypting RTCP, we haven't parsed the packet yet but need to grab the sender SSRC to
45// retrieve the proper srtcp context.  The sender SSRC isn't modeled as part of the header, as
46// different RTCP packets use it differently, so this helper function can be used to retrieve it
47// from an unparsed RTCP packet
48pub fn get_sender_ssrc(buf: &[u8]) -> u32 {
49    u32::from_be_bytes(buf[4..8].try_into().unwrap())
50}
51
52impl RtcpHeader {
53    pub const SIZE_BYTES: usize = 4;
54
55    /// The length of this RTCP packet's payload (i.e. excluding the header) in bytes
56    pub fn payload_length_bytes(&self) -> Result<u16> {
57        self.length_field
58            .checked_mul(4)
59            .ok_or(anyhow!("Invalid length field"))
60    }
61
62    pub fn report_count(mut self, report_count: u5) -> Self {
63        self.report_count = report_count;
64        self
65    }
66
67    pub fn packet_type(mut self, packet_type: u8) -> Self {
68        self.packet_type = packet_type;
69        self
70    }
71}
72
73#[cfg(test)]
74#[allow(clippy::unusual_byte_groupings, clippy::bool_assert_comparison)]
75mod tests {
76    use super::*;
77
78    #[test]
79    fn test_read_rtcp_header() {
80        let mut buf = Bits::from_static_bytes(&[0b10_0_00001, 202, 0, 42]);
81
82        let header = RtcpHeader::read::<NetworkOrder>(&mut buf, ())
83            .context("rtcp header")
84            .unwrap();
85        assert_eq!(header.version, u2::new(2));
86        assert_eq!(header.has_padding, false);
87        assert_eq!(header.report_count, u5::new(1));
88        assert_eq!(header.packet_type, 202);
89        assert_eq!(header.length_field, 42);
90    }
91
92    #[test]
93    fn test_write_rtcp_header() {
94        let header = RtcpHeader {
95            version: u2::new(2),
96            has_padding: false,
97            report_count: u5::new(1),
98            packet_type: 1,
99            length_field: 2,
100        };
101
102        let mut buf_mut = BitsMut::new();
103
104        header
105            .write::<NetworkOrder>(&mut buf_mut, ())
106            .expect("successful write");
107
108        let mut buf = buf_mut.freeze();
109        let read_header = RtcpHeader::read::<NetworkOrder>(&mut buf, ())
110            .context("rtcp header")
111            .unwrap();
112        assert_eq!(header, read_header);
113    }
114}