Skip to main content

rtc_rtcp/
header.rs

1use shared::{
2    error::{Error, Result},
3    marshal::{Marshal, MarshalSize, Unmarshal},
4};
5
6use bytes::{Buf, BufMut};
7
8/// PacketType specifies the type of an RTCP packet
9/// RTCP packet types registered with IANA. See: <https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml#rtp-parameters-4>
10#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
11#[repr(u8)]
12pub enum PacketType {
13    #[default]
14    Unsupported = 0,
15    SenderReport = 200,              // RFC 3550, 6.4.1
16    ReceiverReport = 201,            // RFC 3550, 6.4.2
17    SourceDescription = 202,         // RFC 3550, 6.5
18    Goodbye = 203,                   // RFC 3550, 6.6
19    ApplicationDefined = 204,        // RFC 3550, 6.7 (unimplemented)
20    TransportSpecificFeedback = 205, // RFC 4585, 6051
21    PayloadSpecificFeedback = 206,   // RFC 4585, 6.3
22    ExtendedReport = 207,            // RFC 3611
23}
24
25/// Transport and Payload specific feedback messages overload the count field to act as a message type. those are listed here
26pub const FORMAT_SLI: u8 = 2;
27/// Transport and Payload specific feedback messages overload the count field to act as a message type. those are listed here
28pub const FORMAT_PLI: u8 = 1;
29/// Transport and Payload specific feedback messages overload the count field to act as a message type. those are listed here
30pub const FORMAT_FIR: u8 = 4;
31/// Transport and Payload specific feedback messages overload the count field to act as a message type. those are listed here
32pub const FORMAT_TLN: u8 = 1;
33/// Transport and Payload specific feedback messages overload the count field to act as a message type. those are listed here
34pub const FORMAT_RRR: u8 = 5;
35/// Transport and Payload specific feedback messages overload the count field to act as a message type. those are listed here
36pub const FORMAT_REMB: u8 = 15;
37/// Transport and Payload specific feedback messages overload the count field to act as a message type. those are listed here.
38/// <https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01#page-5>
39pub const FORMAT_TCC: u8 = 15;
40/// FMT value for CCFB (Congestion Control Feedback) per RFC 8888
41pub const FORMAT_CCFB: u8 = 11;
42
43impl std::fmt::Display for PacketType {
44    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45        let s = match self {
46            PacketType::Unsupported => "Unsupported",
47            PacketType::SenderReport => "SR",
48            PacketType::ReceiverReport => "RR",
49            PacketType::SourceDescription => "SDES",
50            PacketType::Goodbye => "BYE",
51            PacketType::ApplicationDefined => "APP",
52            PacketType::TransportSpecificFeedback => "TSFB",
53            PacketType::PayloadSpecificFeedback => "PSFB",
54            PacketType::ExtendedReport => "XR",
55        };
56        write!(f, "{s}")
57    }
58}
59
60impl From<u8> for PacketType {
61    fn from(b: u8) -> Self {
62        match b {
63            200 => PacketType::SenderReport,              // RFC 3550, 6.4.1
64            201 => PacketType::ReceiverReport,            // RFC 3550, 6.4.2
65            202 => PacketType::SourceDescription,         // RFC 3550, 6.5
66            203 => PacketType::Goodbye,                   // RFC 3550, 6.6
67            204 => PacketType::ApplicationDefined,        // RFC 3550, 6.7 (unimplemented)
68            205 => PacketType::TransportSpecificFeedback, // RFC 4585, 6051
69            206 => PacketType::PayloadSpecificFeedback,   // RFC 4585, 6.3
70            207 => PacketType::ExtendedReport,            // RFC 3611
71            _ => PacketType::Unsupported,
72        }
73    }
74}
75
76pub const RTP_VERSION: u8 = 2;
77pub const VERSION_SHIFT: u8 = 6;
78pub const VERSION_MASK: u8 = 0x3;
79pub const PADDING_SHIFT: u8 = 5;
80pub const PADDING_MASK: u8 = 0x1;
81pub const COUNT_SHIFT: u8 = 0;
82pub const COUNT_MASK: u8 = 0x1f;
83
84pub const HEADER_LENGTH: usize = 4;
85pub const COUNT_MAX: usize = (1 << 5) - 1;
86pub const SSRC_LENGTH: usize = 4;
87pub const SDES_MAX_OCTET_COUNT: usize = (1 << 8) - 1;
88
89/// A Header is the common header shared by all RTCP packets
90#[derive(Debug, PartialEq, Eq, Default, Clone)]
91pub struct Header {
92    /// If the padding bit is set, this individual RTCP packet contains
93    /// some additional padding octets at the end which are not part of
94    /// the control information but are included in the length field.
95    pub padding: bool,
96    /// The number of reception reports, sources contained or FMT in this packet (depending on the Type)
97    pub count: u8,
98    /// The RTCP packet type for this packet
99    pub packet_type: PacketType,
100    /// The length of this RTCP packet in 32-bit words minus one,
101    /// including the header and any padding.
102    pub length: u16,
103}
104
105/// Marshal encodes the Header in binary
106impl MarshalSize for Header {
107    fn marshal_size(&self) -> usize {
108        HEADER_LENGTH
109    }
110}
111
112impl Marshal for Header {
113    fn marshal_to(&self, mut buf: &mut [u8]) -> Result<usize> {
114        if self.count > 31 {
115            return Err(Error::InvalidHeader);
116        }
117        if buf.remaining_mut() < HEADER_LENGTH {
118            return Err(Error::BufferTooShort);
119        }
120
121        /*
122         *  0                   1                   2                   3
123         *  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
124         * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
125         * |V=2|P|    RC   |   PT=SR=200   |             length            |
126         * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
127         */
128        let b0 = (RTP_VERSION << VERSION_SHIFT)
129            | ((self.padding as u8) << PADDING_SHIFT)
130            | (self.count << COUNT_SHIFT);
131
132        buf.put_u8(b0);
133        buf.put_u8(self.packet_type as u8);
134        buf.put_u16(self.length);
135
136        Ok(HEADER_LENGTH)
137    }
138}
139
140impl Unmarshal for Header {
141    /// Unmarshal decodes the Header from binary
142    fn unmarshal<B>(raw_packet: &mut B) -> Result<Self>
143    where
144        Self: Sized,
145        B: Buf,
146    {
147        if raw_packet.remaining() < HEADER_LENGTH {
148            return Err(Error::PacketTooShort);
149        }
150
151        /*
152         *  0                   1                   2                   3
153         *  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
154         * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
155         * |V=2|P|    RC   |      PT       |             length            |
156         * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
157         */
158        let b0 = raw_packet.get_u8();
159        let version = (b0 >> VERSION_SHIFT) & VERSION_MASK;
160        if version != RTP_VERSION {
161            return Err(Error::BadVersion);
162        }
163
164        let padding = ((b0 >> PADDING_SHIFT) & PADDING_MASK) > 0;
165        let count = (b0 >> COUNT_SHIFT) & COUNT_MASK;
166        let packet_type = PacketType::from(raw_packet.get_u8());
167        let length = raw_packet.get_u16();
168
169        Ok(Header {
170            padding,
171            count,
172            packet_type,
173            length,
174        })
175    }
176}
177
178#[cfg(test)]
179mod test {
180    use super::*;
181    use bytes::Bytes;
182
183    #[test]
184    fn test_header_unmarshal() {
185        let tests = vec![
186            (
187                "valid",
188                Bytes::from_static(&[
189                    // v=2, p=0, count=1, RR, len=7
190                    0x81u8, 0xc9, 0x00, 0x07,
191                ]),
192                Header {
193                    padding: false,
194                    count: 1,
195                    packet_type: PacketType::ReceiverReport,
196                    length: 7,
197                },
198                None,
199            ),
200            (
201                "also valid",
202                Bytes::from_static(&[
203                    // v=2, p=1, count=1, BYE, len=7
204                    0xa1, 0xcc, 0x00, 0x07,
205                ]),
206                Header {
207                    padding: true,
208                    count: 1,
209                    packet_type: PacketType::ApplicationDefined,
210                    length: 7,
211                },
212                None,
213            ),
214            (
215                "bad version",
216                Bytes::from_static(&[
217                    // v=0, p=0, count=0, RR, len=4
218                    0x00, 0xc9, 0x00, 0x04,
219                ]),
220                Header {
221                    padding: false,
222                    count: 0,
223                    packet_type: PacketType::Unsupported,
224                    length: 0,
225                },
226                Some(Error::BadVersion),
227            ),
228        ];
229
230        for (name, data, want, want_error) in tests {
231            let buf = &mut data.clone();
232            let got = Header::unmarshal(buf);
233
234            assert_eq!(
235                got.is_err(),
236                want_error.is_some(),
237                "Unmarshal {name}: err = {got:?}, want {want_error:?}"
238            );
239
240            if let Some(want_error) = want_error {
241                let got_err = got.err().unwrap();
242                assert_eq!(
243                    want_error, got_err,
244                    "Unmarshal {name}: err = {got_err:?}, want {want_error:?}",
245                );
246            } else {
247                let actual = got.unwrap();
248                assert_eq!(
249                    actual, want,
250                    "Unmarshal {name}: got {actual:?}, want {want:?}"
251                );
252            }
253        }
254    }
255
256    #[test]
257    fn test_header_roundtrip() {
258        let tests = vec![
259            (
260                "valid",
261                Header {
262                    padding: true,
263                    count: 31,
264                    packet_type: PacketType::SenderReport,
265                    length: 4,
266                },
267                None,
268            ),
269            (
270                "also valid",
271                Header {
272                    padding: false,
273                    count: 28,
274                    packet_type: PacketType::ReceiverReport,
275                    length: 65535,
276                },
277                None,
278            ),
279            (
280                "invalid count",
281                Header {
282                    padding: false,
283                    count: 40,
284                    packet_type: PacketType::Unsupported,
285                    length: 0,
286                },
287                Some(Error::InvalidHeader),
288            ),
289        ];
290
291        for (name, want, want_error) in tests {
292            let got = want.marshal();
293
294            assert_eq!(
295                got.is_ok(),
296                want_error.is_none(),
297                "Marshal {name}: err = {got:?}, want {want_error:?}"
298            );
299
300            if let Some(err) = want_error {
301                let got_err = got.err().unwrap();
302                assert_eq!(
303                    err, got_err,
304                    "Unmarshal {name} rr: err = {got_err:?}, want {err:?}",
305                );
306            } else {
307                let data = got.ok().unwrap();
308                let buf = &mut data.clone();
309                let actual = Header::unmarshal(buf).unwrap_or_else(|_| panic!("Unmarshal {name}"));
310
311                assert_eq!(
312                    actual, want,
313                    "{name} round trip: got {actual:?}, want {want:?}"
314                )
315            }
316        }
317    }
318}