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