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}