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