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)]
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}