Skip to main content

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