rtp_parse/rtp/
header_extensions.rs

1use std::{collections::HashMap, io::Cursor};
2
3use bytes::{Buf, Bytes};
4
5//  https://datatracker.ietf.org/doc/html/rfc3550#section-5.3.1
6//  0                   1                   2                   3
7//  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
8// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
9// |      defined by profile       |           length              |
10// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
11// |                        header extension                       |
12// |                             ....                              |
13// The header extension contains a 16-bit length field that
14//   counts the number of 32-bit words in the extension, excluding the
15//   four-octet extension header (therefore zero is a valid length).
16
17// https://datatracker.ietf.org/doc/html/rfc8285#section-4.2
18// One Byte Header
19//
20// In the one-byte header form of extensions, the 16-bit value required
21//    by the RTP specification for a header extension, labeled in the RTP
22//    specification as "defined by profile", MUST have the fixed bit
23//    pattern 0xBEDE (the pattern was picked for the trivial reason that
24//    the first version of this specification was written on May 25th --
25//    the feast day of the Venerable Bede).
26//
27//    Each extension element MUST start with a byte containing an ID and a
28//    length:
29//
30//        0
31//        0 1 2 3 4 5 6 7
32//       +-+-+-+-+-+-+-+-+
33//       |  ID   |  len  |
34//       +-+-+-+-+-+-+-+-+
35//
36//    The 4-bit ID is the local identifier of this element in the range
37//    1-14 inclusive.  In the signaling section, this is referred to as the
38//    valid range.
39//
40//    The local identifier value 15 is reserved for a future extension and
41//    MUST NOT be used as an identifier.  If the ID value 15 is
42//    encountered, its length field MUST be ignored, processing of the
43//    entire extension MUST terminate at that point, and only the extension
44//    elements present prior to the element with ID 15 SHOULD be
45//    considered.
46//
47//    The 4-bit length is the number, minus one, of data bytes of this
48//    header extension element following the one-byte header.  Therefore,
49//    the value zero (0) in this field indicates that one byte of data
50//    follows, and a value of 15 (the maximum) indicates element data of
51//    16 bytes.  (This permits carriage of 16-byte values, which is a
52//    common length of labels and identifiers, while losing the possibility
53//    of zero-length values, which would often be padded anyway.)
54#[derive(Debug)]
55pub struct OneByteHeaderExtension(Bytes);
56
57impl OneByteHeaderExtension {
58    pub const TYPE: u16 = 0xBEDE;
59
60    pub fn type_matches(ext_type: u16) -> bool {
61        ext_type == Self::TYPE
62    }
63
64    pub fn id(&self) -> u8 {
65        (self.0[0] & 0xF0) >> 4
66    }
67
68    pub fn data(&self) -> Bytes {
69        self.0.slice(1..)
70    }
71}
72
73pub fn read_one_byte_header_extension(buf: &mut Bytes) -> OneByteHeaderExtension {
74    let id = (buf[0] & 0xF0) >> 4;
75
76    let length_bytes = match id {
77        // A 0 id means we've hit the end of the actual extensions, so consume the rest of the
78        // buffer
79        0 => buf.len() - 1,
80        _ => ((buf[0] & 0xF) + 1) as usize,
81    };
82
83    // TODO: here (and two byte) i think we need to validate against buf.len() before splitting
84    let he = buf.split_to(1 + length_bytes);
85
86    OneByteHeaderExtension(he)
87}
88
89// https://datatracker.ietf.org/doc/html/rfc8285#section-4.3
90// Two Byte Header
91//
92// In the two-byte header form, the 16-bit value defined by the RTP
93//    specification for a header extension, labeled in the RTP
94//    specification as "defined by profile", is defined as shown below.
95//
96//        0                   1
97//        0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
98//       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
99//       |         0x100         |appbits|
100//       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
101//
102//    The appbits field is 4 bits that are application dependent and MAY be
103//    defined to be any value or meaning; this topic is outside the scope
104//    of this specification.  For the purposes of signaling, this field is
105//    treated as a special extension value assigned to the local identifier
106//    256.  If no extension has been specified through configuration or
107//    signaling for this local identifier value (256), the appbits field
108//    SHOULD be set to all 0s (zeros) by the sender and MUST be ignored by
109//    the receiver.
110//
111//    Each extension element starts with a byte containing an ID and a byte
112//    containing a length:
113//
114//        0                   1
115//        0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
116//       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
117//       |       ID      |     length    |
118//       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
119//
120//    The 8-bit ID is the local identifier of this element in the range
121//    1-255 inclusive.  In the signaling section, the range 1-256 is
122//    referred to as the valid range, with the values 1-255 referring to
123//
124//    extension elements and the value 256 referring to the 4-bit appbits
125//    field (above).  Note that there is one ID space for both the one-byte
126//    form and the two-byte form.  This means that the lower values (1-14)
127//    can be used in the 4-bit ID field in the one-byte header format with
128//    the same meanings.
129//
130//    The 8-bit length field is the length of extension data in bytes, not
131//    including the ID and length fields.  The value zero (0) indicates
132//    that there is no subsequent data.
133#[derive(Debug)]
134pub struct TwoByteHeaderExtension(Bytes);
135
136impl TwoByteHeaderExtension {
137    const TYPE_MASK: u16 = 0xFFF0;
138    pub const TYPE: u16 = 0x1000;
139
140    pub fn type_matches(ext_type: u16) -> bool {
141        (ext_type & Self::TYPE_MASK) == Self::TYPE
142    }
143
144    pub fn id(&self) -> u8 {
145        self.0[0]
146    }
147
148    pub fn data(&self) -> Bytes {
149        self.0.slice(2..)
150    }
151}
152
153/// [`buf`] should start at the beginning of the header extension (the id)
154pub fn read_two_byte_header_extension(buf: &mut Bytes) -> TwoByteHeaderExtension {
155    let id = buf[0];
156    let length_bytes = match id {
157        0 => 0,
158        _ => buf[1] + 1,
159    };
160    // The length field is in the second byte, and the '2' is to account for the id and length
161    // field bytes before the actul data
162    let he = buf.split_to(2 + length_bytes as usize);
163    TwoByteHeaderExtension(he)
164}
165
166#[derive(Debug)]
167pub enum SomeHeaderExtension {
168    OneByteHeaderExtension(OneByteHeaderExtension),
169    TwoByteHeaderExtension(TwoByteHeaderExtension),
170}
171
172impl SomeHeaderExtension {
173    pub fn id(&self) -> u8 {
174        match self {
175            SomeHeaderExtension::OneByteHeaderExtension(e) => e.id(),
176            SomeHeaderExtension::TwoByteHeaderExtension(e) => e.id(),
177        }
178    }
179
180    pub fn data(&self) -> Bytes {
181        match self {
182            SomeHeaderExtension::OneByteHeaderExtension(e) => e.data(),
183            SomeHeaderExtension::TwoByteHeaderExtension(e) => e.data(),
184        }
185    }
186}
187
188pub fn read_header_extensions(buf: Bytes) -> HashMap<u8, SomeHeaderExtension> {
189    // TODO: should be consistent with use of cursor/bitcursor and Vec<u8> and Bytes
190    let mut cursor = Cursor::new(buf);
191
192    let ext_type = cursor.get_u16();
193    // Length field is length in 4 byte words
194    let length_bytes = cursor.get_u16() * 4;
195    let buf = cursor.into_inner();
196
197    let mut header_extensions_bytes = buf.slice(4..).slice(..length_bytes as usize);
198
199    let mut header_extensions: HashMap<u8, SomeHeaderExtension> = HashMap::new();
200    while !header_extensions_bytes.is_empty() {
201        let ext = if TwoByteHeaderExtension::type_matches(ext_type) {
202            SomeHeaderExtension::TwoByteHeaderExtension(read_two_byte_header_extension(
203                &mut header_extensions_bytes,
204            ))
205        } else if OneByteHeaderExtension::type_matches(ext_type) {
206            SomeHeaderExtension::OneByteHeaderExtension(read_one_byte_header_extension(
207                &mut header_extensions_bytes,
208            ))
209        } else {
210            // TODO: change this to result
211            panic!("Invalid header extension type: {ext_type:x?}");
212        };
213
214        header_extensions.insert(ext.id(), ext);
215    }
216    header_extensions
217}
218
219#[cfg(test)]
220mod test {
221    use bytes::Bytes;
222
223    use super::read_header_extensions;
224
225    #[test]
226    fn test_one_byte_header_extensions() {
227        #[rustfmt::skip]
228        let data: Vec<u8> = vec![
229            0xBE, 0xDE, 0x00, 0x01,
230            0x10, 0xFF, 0x00, 0x00
231        ];
232
233        let bytes = Bytes::from(data);
234        let he = read_header_extensions(bytes);
235        // The padding bytes are parsed as a header extension
236        assert_eq!(he.len(), 2);
237        let ext_one = he
238            .get(&1)
239            .expect("should contain a header extension with ID 1");
240        assert_eq!(ext_one.data(), Bytes::from_static(&[0xFF]));
241    }
242
243    #[test]
244    fn test_one_byte_header_extensions_one_byte_padding() {
245        #[rustfmt::skip]
246        let data: Vec<u8> = vec![
247            0xBE, 0xDE, 0x00, 0x01,
248            0x51, 0x00, 0x01, 0x00
249
250        ];
251        let bytes = Bytes::from(data);
252        let he = read_header_extensions(bytes);
253        assert_eq!(he.len(), 2);
254        let ext_one = he
255            .get(&5)
256            .expect("should contain a header extension with ID 1");
257        assert_eq!(ext_one.data(), Bytes::from_static(&[0x00, 0x01]));
258    }
259
260    #[test]
261    fn test_one_byte_header_extensions_two_bytes_padding() {
262        #[rustfmt::skip]
263        let data: Vec<u8> = vec![
264            0xBE, 0xDE, 0x00, 0x01,
265            0x10, 0xFF, 0x00, 0x00
266
267        ];
268        let bytes = Bytes::from(data);
269        let he = read_header_extensions(bytes);
270        assert_eq!(he.len(), 2);
271        let ext_one = he
272            .get(&1)
273            .expect("should contain a header extension with ID 1");
274        assert_eq!(ext_one.data(), Bytes::from_static(&[0xFF]));
275    }
276}