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}