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