rtp_parse/rtcp/
rtcp_header.rs

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