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