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