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