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