rtc_rtcp/source_description/mod.rs
1#[cfg(test)]
2mod source_description_test;
3
4use crate::{header::*, packet::*, util::*};
5use shared::{
6 error::{Error, Result},
7 marshal::{Marshal, MarshalSize, Unmarshal},
8};
9
10use bytes::{Buf, BufMut, Bytes};
11use std::any::Any;
12use std::fmt;
13
14const SDES_SOURCE_LEN: usize = 4;
15const SDES_TYPE_LEN: usize = 1;
16const SDES_TYPE_OFFSET: usize = 0;
17const SDES_OCTET_COUNT_LEN: usize = 1;
18const SDES_OCTET_COUNT_OFFSET: usize = 1;
19const SDES_MAX_OCTET_COUNT: usize = (1 << 8) - 1;
20const SDES_TEXT_OFFSET: usize = 2;
21
22/// SDESType is the item type used in the RTCP SDES control packet.
23/// RTP SDES item types registered with IANA. See: https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml#rtp-parameters-5
24#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
25#[repr(u8)]
26pub enum SdesType {
27 #[default]
28 SdesEnd = 0, // end of SDES list RFC 3550, 6.5
29 SdesCname = 1, // canonical name RFC 3550, 6.5.1
30 SdesName = 2, // user name RFC 3550, 6.5.2
31 SdesEmail = 3, // user's electronic mail address RFC 3550, 6.5.3
32 SdesPhone = 4, // user's phone number RFC 3550, 6.5.4
33 SdesLocation = 5, // geographic user location RFC 3550, 6.5.5
34 SdesTool = 6, // name of application or tool RFC 3550, 6.5.6
35 SdesNote = 7, // notice about the source RFC 3550, 6.5.7
36 SdesPrivate = 8, // private extensions RFC 3550, 6.5.8 (not implemented)
37}
38
39impl fmt::Display for SdesType {
40 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41 let s = match self {
42 SdesType::SdesEnd => "END",
43 SdesType::SdesCname => "CNAME",
44 SdesType::SdesName => "NAME",
45 SdesType::SdesEmail => "EMAIL",
46 SdesType::SdesPhone => "PHONE",
47 SdesType::SdesLocation => "LOC",
48 SdesType::SdesTool => "TOOL",
49 SdesType::SdesNote => "NOTE",
50 SdesType::SdesPrivate => "PRIV",
51 };
52 write!(f, "{s}")
53 }
54}
55
56impl From<u8> for SdesType {
57 fn from(b: u8) -> Self {
58 match b {
59 1 => SdesType::SdesCname,
60 2 => SdesType::SdesName,
61 3 => SdesType::SdesEmail,
62 4 => SdesType::SdesPhone,
63 5 => SdesType::SdesLocation,
64 6 => SdesType::SdesTool,
65 7 => SdesType::SdesNote,
66 8 => SdesType::SdesPrivate,
67 _ => SdesType::SdesEnd,
68 }
69 }
70}
71
72/// A SourceDescriptionChunk contains items describing a single RTP source
73#[derive(Debug, PartialEq, Eq, Default, Clone)]
74pub struct SourceDescriptionChunk {
75 /// The source (ssrc) or contributing source (csrc) identifier this packet describes
76 pub source: u32,
77 pub items: Vec<SourceDescriptionItem>,
78}
79
80impl SourceDescriptionChunk {
81 fn raw_size(&self) -> usize {
82 let mut len = SDES_SOURCE_LEN;
83 for it in &self.items {
84 len += it.marshal_size();
85 }
86 len += SDES_TYPE_LEN; // for terminating null octet
87 len
88 }
89}
90
91impl MarshalSize for SourceDescriptionChunk {
92 fn marshal_size(&self) -> usize {
93 let l = self.raw_size();
94 // align to 32-bit boundary
95 l + get_padding_size(l)
96 }
97}
98
99impl Marshal for SourceDescriptionChunk {
100 /// Marshal encodes the SourceDescriptionChunk in binary
101 fn marshal_to(&self, mut buf: &mut [u8]) -> Result<usize> {
102 if buf.remaining_mut() < self.marshal_size() {
103 return Err(Error::BufferTooShort);
104 }
105 /*
106 * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
107 * | SSRC/CSRC_1 |
108 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
109 * | SDES items |
110 * | ... |
111 * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
112 */
113
114 buf.put_u32(self.source);
115
116 for it in &self.items {
117 let n = it.marshal_to(buf)?;
118 buf = &mut buf[n..];
119 }
120
121 // The list of items in each chunk MUST be terminated by one or more null octets
122 buf.put_u8(SdesType::SdesEnd as u8);
123
124 // additional null octets MUST be included if needed to pad until the next 32-bit boundary
125 put_padding(buf, self.raw_size());
126 Ok(self.marshal_size())
127 }
128}
129
130impl Unmarshal for SourceDescriptionChunk {
131 /// Unmarshal decodes the SourceDescriptionChunk from binary
132 fn unmarshal<B>(raw_packet: &mut B) -> Result<Self>
133 where
134 Self: Sized,
135 B: Buf,
136 {
137 /*
138 * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
139 * | SSRC/CSRC_1 |
140 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
141 * | SDES items |
142 * | ... |
143 * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
144 */
145 let raw_packet_len = raw_packet.remaining();
146 if raw_packet_len < (SDES_SOURCE_LEN + SDES_TYPE_LEN) {
147 return Err(Error::PacketTooShort);
148 }
149
150 let source = raw_packet.get_u32();
151
152 let mut offset = SDES_SOURCE_LEN;
153 let mut items = vec![];
154 while offset < raw_packet_len {
155 let item = SourceDescriptionItem::unmarshal(raw_packet)?;
156 if item.sdes_type == SdesType::SdesEnd {
157 // offset + 1 (one byte for SdesEnd)
158 let padding_len = get_padding_size(offset + 1);
159 if raw_packet.remaining() >= padding_len {
160 raw_packet.advance(padding_len);
161 return Ok(SourceDescriptionChunk { source, items });
162 } else {
163 return Err(Error::PacketTooShort);
164 }
165 }
166 offset += item.marshal_size();
167 items.push(item);
168 }
169
170 Err(Error::PacketTooShort)
171 }
172}
173
174/// A SourceDescriptionItem is a part of a SourceDescription that describes a stream.
175#[derive(Debug, PartialEq, Eq, Default, Clone)]
176pub struct SourceDescriptionItem {
177 /// The type identifier for this item. eg, SDESCNAME for canonical name description.
178 ///
179 /// Type zero or SDESEnd is interpreted as the end of an item list and cannot be used.
180 pub sdes_type: SdesType,
181 /// Text is a unicode text blob associated with the item. Its meaning varies based on the item's Type.
182 pub text: Bytes,
183}
184
185impl MarshalSize for SourceDescriptionItem {
186 fn marshal_size(&self) -> usize {
187 /*
188 * 0 1 2 3
189 * 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
190 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
191 * | CNAME=1 | length | user and domain name ...
192 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
193 */
194 SDES_TYPE_LEN + SDES_OCTET_COUNT_LEN + self.text.len()
195 }
196}
197
198impl Marshal for SourceDescriptionItem {
199 /// Marshal encodes the SourceDescriptionItem in binary
200 fn marshal_to(&self, mut buf: &mut [u8]) -> Result<usize> {
201 /*
202 * 0 1 2 3
203 * 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
204 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
205 * | CNAME=1 | length | user and domain name ...
206 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
207 */
208
209 if self.sdes_type == SdesType::SdesEnd {
210 return Err(Error::SdesMissingType);
211 }
212
213 if buf.remaining_mut() < self.marshal_size() {
214 return Err(Error::BufferTooShort);
215 }
216
217 buf.put_u8(self.sdes_type as u8);
218
219 if self.text.len() > SDES_MAX_OCTET_COUNT {
220 return Err(Error::SdesTextTooLong);
221 }
222 buf.put_u8(self.text.len() as u8);
223 buf.put(self.text.clone());
224
225 //no padding for each SourceDescriptionItem
226 Ok(self.marshal_size())
227 }
228}
229
230impl Unmarshal for SourceDescriptionItem {
231 /// Unmarshal decodes the SourceDescriptionItem from binary
232 fn unmarshal<B>(raw_packet: &mut B) -> Result<Self>
233 where
234 Self: Sized,
235 B: Buf,
236 {
237 /*
238 * 0 1 2 3
239 * 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
240 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
241 * | CNAME=1 | length | user and domain name ...
242 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
243 */
244 let raw_packet_len = raw_packet.remaining();
245 if raw_packet_len < SDES_TYPE_LEN {
246 return Err(Error::PacketTooShort);
247 }
248
249 let sdes_type = SdesType::from(raw_packet.get_u8());
250 if sdes_type == SdesType::SdesEnd {
251 return Ok(SourceDescriptionItem {
252 sdes_type,
253 text: Bytes::new(),
254 });
255 }
256
257 if raw_packet_len < (SDES_TYPE_LEN + SDES_OCTET_COUNT_LEN) {
258 return Err(Error::PacketTooShort);
259 }
260
261 let octet_count = raw_packet.get_u8() as usize;
262 if SDES_TEXT_OFFSET + octet_count > raw_packet_len {
263 return Err(Error::PacketTooShort);
264 }
265
266 let text = raw_packet.copy_to_bytes(octet_count);
267
268 Ok(SourceDescriptionItem { sdes_type, text })
269 }
270}
271
272/// A SourceDescription (SDES) packet describes the sources in an RTP stream.
273#[derive(Debug, Default, PartialEq, Eq, Clone)]
274pub struct SourceDescription {
275 pub chunks: Vec<SourceDescriptionChunk>,
276}
277
278impl fmt::Display for SourceDescription {
279 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
280 let mut out = "Source Description:\n".to_string();
281 for c in &self.chunks {
282 out += format!("\t{:x}\n", c.source).as_str();
283 for it in &c.items {
284 out += format!("\t\t{it:?}\n").as_str();
285 }
286 }
287 write!(f, "{out}")
288 }
289}
290
291impl Packet for SourceDescription {
292 /// Header returns the Header associated with this packet.
293 fn header(&self) -> Header {
294 Header {
295 padding: get_padding_size(self.raw_size()) != 0,
296 count: self.chunks.len() as u8,
297 packet_type: PacketType::SourceDescription,
298 length: ((self.marshal_size() / 4) - 1) as u16,
299 }
300 }
301
302 /// destination_ssrc returns an array of SSRC values that this packet refers to.
303 fn destination_ssrc(&self) -> Vec<u32> {
304 self.chunks.iter().map(|x| x.source).collect()
305 }
306
307 fn raw_size(&self) -> usize {
308 let mut chunks_length = 0;
309 for c in &self.chunks {
310 chunks_length += c.marshal_size();
311 }
312
313 HEADER_LENGTH + chunks_length
314 }
315
316 fn as_any(&self) -> &(dyn Any) {
317 self
318 }
319
320 fn equal(&self, other: &(dyn Packet)) -> bool {
321 other
322 .as_any()
323 .downcast_ref::<SourceDescription>()
324 .map_or(false, |a| self == a)
325 }
326
327 fn cloned(&self) -> Box<dyn Packet> {
328 Box::new(self.clone())
329 }
330}
331
332impl MarshalSize for SourceDescription {
333 fn marshal_size(&self) -> usize {
334 let l = self.raw_size();
335 // align to 32-bit boundary
336 l + get_padding_size(l)
337 }
338}
339
340impl Marshal for SourceDescription {
341 /// Marshal encodes the SourceDescription in binary
342 fn marshal_to(&self, mut buf: &mut [u8]) -> Result<usize> {
343 if self.chunks.len() > COUNT_MAX {
344 return Err(Error::TooManyChunks);
345 }
346
347 if buf.remaining_mut() < self.marshal_size() {
348 return Err(Error::BufferTooShort);
349 }
350
351 /*
352 * 0 1 2 3
353 * 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
354 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
355 * header |V=2|P| SC | PT=SDES=202 | length |
356 * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
357 * chunk | SSRC/CSRC_1 |
358 * 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
359 * | SDES items |
360 * | ... |
361 * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
362 * chunk | SSRC/CSRC_2 |
363 * 2 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
364 * | SDES items |
365 * | ... |
366 * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
367 */
368
369 let h = self.header();
370 let n = h.marshal_to(buf)?;
371 buf = &mut buf[n..];
372
373 for c in &self.chunks {
374 let n = c.marshal_to(buf)?;
375 buf = &mut buf[n..];
376 }
377
378 if h.padding {
379 put_padding(buf, self.raw_size());
380 }
381
382 Ok(self.marshal_size())
383 }
384}
385
386impl Unmarshal for SourceDescription {
387 /// Unmarshal decodes the SourceDescription from binary
388 fn unmarshal<B>(raw_packet: &mut B) -> Result<Self>
389 where
390 Self: Sized,
391 B: Buf,
392 {
393 /*
394 * 0 1 2 3
395 * 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
396 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
397 * header |V=2|P| SC | PT=SDES=202 | length |
398 * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
399 * chunk | SSRC/CSRC_1 |
400 * 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
401 * | SDES items |
402 * | ... |
403 * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
404 * chunk | SSRC/CSRC_2 |
405 * 2 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
406 * | SDES items |
407 * | ... |
408 * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
409 */
410 let raw_packet_len = raw_packet.remaining();
411
412 let h = Header::unmarshal(raw_packet)?;
413 if h.packet_type != PacketType::SourceDescription {
414 return Err(Error::WrongType);
415 }
416
417 let mut offset = HEADER_LENGTH;
418 let mut chunks = vec![];
419 while offset < raw_packet_len {
420 let chunk = SourceDescriptionChunk::unmarshal(raw_packet)?;
421 offset += chunk.marshal_size();
422 chunks.push(chunk);
423 }
424
425 if chunks.len() != h.count as usize {
426 return Err(Error::InvalidHeader);
427 }
428
429 if
430 /*h.padding &&*/
431 raw_packet.has_remaining() {
432 raw_packet.advance(raw_packet.remaining());
433 }
434
435 Ok(SourceDescription { chunks })
436 }
437}