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}