rtcp_types/
utils.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3//! Utilites for handling RTCP packets
4
5#[track_caller]
6#[inline(always)]
7pub(crate) fn u16_from_be_bytes(bytes: &[u8]) -> u16 {
8    u16::from_be_bytes(bytes.try_into().expect("expecting 2 bytes"))
9}
10
11#[track_caller]
12#[inline(always)]
13pub(crate) fn u32_from_be_bytes(bytes: &[u8]) -> u32 {
14    u32::from_be_bytes(bytes.try_into().expect("expecting 4 bytes"))
15}
16
17#[track_caller]
18#[inline(always)]
19pub(crate) fn u64_from_be_bytes(bytes: &[u8]) -> u64 {
20    u64::from_be_bytes(bytes.try_into().expect("expecting 8 bytes"))
21}
22
23#[inline(always)]
24pub(crate) const fn pad_to_4bytes(num: usize) -> usize {
25    (num + 3) & !3
26}
27
28/// Parsing utility helpers
29pub mod parser {
30    use crate::{RtcpPacket, RtcpParseError};
31
32    /// Performs checks common to every RTCP packets.
33    ///
34    /// Call this before parsing the specificities of the RTCP packet.
35    #[inline(always)]
36    pub fn check_packet<P: RtcpPacket>(packet: &[u8]) -> Result<(), RtcpParseError> {
37        if packet.len() < P::MIN_PACKET_LEN {
38            return Err(RtcpParseError::Truncated {
39                expected: P::MIN_PACKET_LEN,
40                actual: packet.len(),
41            });
42        }
43
44        let version = parse_version(packet);
45        if version != P::VERSION {
46            return Err(RtcpParseError::UnsupportedVersion(version));
47        }
48
49        if parse_packet_type(packet) != P::PACKET_TYPE {
50            return Err(RtcpParseError::PacketTypeMismatch {
51                actual: parse_packet_type(packet),
52                requested: P::PACKET_TYPE,
53            });
54        }
55
56        let length = parse_length(packet);
57        if packet.len() < length {
58            return Err(RtcpParseError::Truncated {
59                expected: length,
60                actual: packet.len(),
61            });
62        }
63
64        if packet.len() > length {
65            return Err(RtcpParseError::TooLarge {
66                expected: length,
67                actual: packet.len(),
68            });
69        }
70
71        if let Some(padding) = parse_padding(packet) {
72            if padding == 0 {
73                return Err(RtcpParseError::InvalidPadding);
74            }
75        }
76
77        Ok(())
78    }
79
80    /// Parses the version from the provided packet data.
81    #[inline(always)]
82    pub fn parse_version(packet: &[u8]) -> u8 {
83        packet[0] >> 6
84    }
85
86    /// Parses the padding bit from the provided packet data.
87    #[inline(always)]
88    pub fn parse_padding_bit(packet: &[u8]) -> bool {
89        (packet[0] & 0x20) != 0
90    }
91
92    /// Parses the padding from the provided packet data.
93    ///
94    /// Returns the last byte of the packet if the padding bit is set
95    /// otherwise returns `None`.
96    #[inline(always)]
97    pub fn parse_padding(packet: &[u8]) -> Option<u8> {
98        if parse_padding_bit(packet) {
99            let length = parse_length(packet);
100            Some(packet[length - 1])
101        } else {
102            None
103        }
104    }
105
106    /// Parses the count from the provided packet data.
107    #[inline(always)]
108    pub fn parse_count(packet: &[u8]) -> u8 {
109        packet[0] & 0x1f
110    }
111
112    /// Parses the packet type from the provided packet data.
113    #[inline(always)]
114    pub fn parse_packet_type(packet: &[u8]) -> u8 {
115        packet[1]
116    }
117
118    /// Parses the length from the provided packet data.
119    #[inline(always)]
120    pub fn parse_length(packet: &[u8]) -> usize {
121        4 * (super::u16_from_be_bytes(&packet[2..4]) as usize + 1)
122    }
123
124    /// Parses the SSRC from the provided packet data.
125    ///
126    /// This is applicable for packets where SSRC is available at [4..8].
127    #[inline(always)]
128    pub fn parse_ssrc(packet: &[u8]) -> u32 {
129        super::u32_from_be_bytes(&packet[4..8])
130    }
131}
132
133/// Writing utility helpers
134pub mod writer {
135    use crate::{RtcpPacket, RtcpWriteError};
136
137    /// Checks that the provided padding is a mutliple of 4.
138    #[inline(always)]
139    pub fn check_padding(padding: u8) -> Result<(), RtcpWriteError> {
140        if padding % 4 != 0 {
141            return Err(RtcpWriteError::InvalidPadding { padding });
142        }
143
144        Ok(())
145    }
146
147    /// Writes the common header for this RTCP packet into `buf` without any validity checks.
148    ///
149    /// Uses the length of the buffer for the length field.
150    ///
151    /// Returns the number of bytes written.
152    ///
153    /// # Panic
154    ///
155    /// Panics if the buf is not large enough.
156    #[inline(always)]
157    pub fn write_header_unchecked<P: RtcpPacket>(padding: u8, count: u8, buf: &mut [u8]) -> usize {
158        buf[0] = P::VERSION << 6;
159        if padding > 0 {
160            buf[0] |= 0x20;
161        }
162        buf[0] |= count;
163        buf[1] = P::PACKET_TYPE;
164        let len = buf.len();
165        buf[2..4].copy_from_slice(&((len / 4 - 1) as u16).to_be_bytes());
166
167        4
168    }
169
170    /// Writes the padding for this RTCP packet into `buf` without any validity checks.
171    ///
172    /// Returns the number of bytes written.
173    ///
174    /// # Panic
175    ///
176    /// Panics if the buf is not large enough.
177    #[inline(always)]
178    pub fn write_padding_unchecked(padding: u8, buf: &mut [u8]) -> usize {
179        let mut end = 0;
180        if padding > 0 {
181            end += padding as usize;
182
183            buf[0..end - 1].fill(0);
184            buf[end - 1] = padding;
185        }
186
187        end
188    }
189}