xref: /webrtc/rtcp/src/header.rs (revision 83f2d1bb)
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         };
57ffe74184SMartin Algesten         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(),
238ffe74184SMartin Algesten                 "Unmarshal {}: err = {:?}, want {:?}",
239ffe74184SMartin Algesten                 name,
240ffe74184SMartin Algesten                 got,
241ffe74184SMartin Algesten                 want_error
242ffe74184SMartin Algesten             );
243ffe74184SMartin Algesten 
244*83f2d1bbSVincent Esche             if let Some(want_error) = want_error {
245ffe74184SMartin Algesten                 let got_err = got.err().unwrap();
246ffe74184SMartin Algesten                 assert_eq!(
247*83f2d1bbSVincent Esche                     want_error, got_err,
248ffe74184SMartin Algesten                     "Unmarshal {}: err = {:?}, want {:?}",
249*83f2d1bbSVincent Esche                     name, got_err, want_error,
250ffe74184SMartin Algesten                 );
251ffe74184SMartin Algesten             } else {
252ffe74184SMartin Algesten                 let actual = got.unwrap();
253ffe74184SMartin Algesten                 assert_eq!(
254ffe74184SMartin Algesten                     actual, want,
255ffe74184SMartin Algesten                     "Unmarshal {}: got {:?}, want {:?}",
256ffe74184SMartin Algesten                     name, actual, want
257ffe74184SMartin Algesten                 );
258ffe74184SMartin Algesten             }
259ffe74184SMartin Algesten         }
260ffe74184SMartin Algesten     }
261ffe74184SMartin Algesten 
262ffe74184SMartin Algesten     #[test]
263ffe74184SMartin Algesten     fn test_header_roundtrip() {
264ffe74184SMartin Algesten         let tests = vec![
265ffe74184SMartin Algesten             (
266ffe74184SMartin Algesten                 "valid",
267ffe74184SMartin Algesten                 Header {
268ffe74184SMartin Algesten                     padding: true,
269ffe74184SMartin Algesten                     count: 31,
270ffe74184SMartin Algesten                     packet_type: PacketType::SenderReport,
271ffe74184SMartin Algesten                     length: 4,
272ffe74184SMartin Algesten                 },
273ffe74184SMartin Algesten                 None,
274ffe74184SMartin Algesten             ),
275ffe74184SMartin Algesten             (
276ffe74184SMartin Algesten                 "also valid",
277ffe74184SMartin Algesten                 Header {
278ffe74184SMartin Algesten                     padding: false,
279ffe74184SMartin Algesten                     count: 28,
280ffe74184SMartin Algesten                     packet_type: PacketType::ReceiverReport,
281ffe74184SMartin Algesten                     length: 65535,
282ffe74184SMartin Algesten                 },
283ffe74184SMartin Algesten                 None,
284ffe74184SMartin Algesten             ),
285ffe74184SMartin Algesten             (
286ffe74184SMartin Algesten                 "invalid count",
287ffe74184SMartin Algesten                 Header {
288ffe74184SMartin Algesten                     padding: false,
289ffe74184SMartin Algesten                     count: 40,
290ffe74184SMartin Algesten                     packet_type: PacketType::Unsupported,
291ffe74184SMartin Algesten                     length: 0,
292ffe74184SMartin Algesten                 },
293ffe74184SMartin Algesten                 Some(Error::InvalidHeader),
294ffe74184SMartin Algesten             ),
295ffe74184SMartin Algesten         ];
296ffe74184SMartin Algesten 
297ffe74184SMartin Algesten         for (name, want, want_error) in tests {
298ffe74184SMartin Algesten             let got = want.marshal();
299ffe74184SMartin Algesten 
300ffe74184SMartin Algesten             assert_eq!(
301ffe74184SMartin Algesten                 got.is_ok(),
302ffe74184SMartin Algesten                 want_error.is_none(),
303ffe74184SMartin Algesten                 "Marshal {}: err = {:?}, want {:?}",
304ffe74184SMartin Algesten                 name,
305ffe74184SMartin Algesten                 got,
306ffe74184SMartin Algesten                 want_error
307ffe74184SMartin Algesten             );
308ffe74184SMartin Algesten 
309ffe74184SMartin Algesten             if let Some(err) = want_error {
310ffe74184SMartin Algesten                 let got_err = got.err().unwrap();
311ffe74184SMartin Algesten                 assert_eq!(
312ffe74184SMartin Algesten                     err, got_err,
313ffe74184SMartin Algesten                     "Unmarshal {} rr: err = {:?}, want {:?}",
314ffe74184SMartin Algesten                     name, got_err, err,
315ffe74184SMartin Algesten                 );
316ffe74184SMartin Algesten             } else {
317ffe74184SMartin Algesten                 let data = got.ok().unwrap();
318ffe74184SMartin Algesten                 let buf = &mut data.clone();
319ffe74184SMartin Algesten                 let actual =
320ffe74184SMartin Algesten                     Header::unmarshal(buf).unwrap_or_else(|_| panic!("Unmarshal {}", name));
321ffe74184SMartin Algesten 
322ffe74184SMartin Algesten                 assert_eq!(
323ffe74184SMartin Algesten                     actual, want,
324ffe74184SMartin Algesten                     "{} round trip: got {:?}, want {:?}",
325ffe74184SMartin Algesten                     name, actual, want
326ffe74184SMartin Algesten                 )
327ffe74184SMartin Algesten             }
328ffe74184SMartin Algesten         }
329ffe74184SMartin Algesten     }
330ffe74184SMartin Algesten }
331