Skip to main content

rtcp/
header.rs

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