msf_sdp/
lib.rs

1mod bandwidth;
2mod key;
3mod origin;
4mod parser;
5
6pub mod attribute;
7pub mod connection;
8pub mod media;
9pub mod time;
10
11#[cfg(feature = "h264")]
12pub mod h264;
13
14#[cfg(feature = "ice")]
15pub mod ice;
16
17use std::{
18    convert::Infallible,
19    error::Error,
20    fmt::{self, Display, Formatter},
21    str::FromStr,
22};
23
24use self::{
25    attribute::Attributes,
26    parser::{FromSessionDescriptionLines, SessionDescriptionLines},
27    time::{TimeZoneAdjustment, TimeZoneAdjustments},
28};
29
30pub use self::{
31    attribute::Attribute,
32    bandwidth::{Bandwidth, BandwidthType},
33    connection::{ConnectionAddress, ConnectionInfo},
34    key::EncryptionKey,
35    media::MediaDescription,
36    origin::Origin,
37    time::TimeDescription,
38};
39
40/// SDP parse error.
41#[derive(Debug)]
42pub struct ParseError {
43    msg: String,
44    cause: Option<Box<dyn Error + Send + Sync>>,
45}
46
47impl ParseError {
48    /// Create a plain parse error.
49    pub fn plain() -> Self {
50        Self {
51            msg: String::new(),
52            cause: None,
53        }
54    }
55
56    /// Create a parse error with a given error message.
57    pub fn with_msg<M>(msg: M) -> Self
58    where
59        M: ToString,
60    {
61        Self {
62            msg: msg.to_string(),
63            cause: None,
64        }
65    }
66
67    /// Create a parse error with a given error message and a given cause.
68    pub fn with_cause_and_msg<M, C>(msg: M, cause: C) -> Self
69    where
70        M: ToString,
71        C: Into<Box<dyn Error + Send + Sync>>,
72    {
73        Self {
74            msg: msg.to_string(),
75            cause: Some(cause.into()),
76        }
77    }
78
79    /// Create a parse error with a given cause.
80    pub fn with_cause<C>(cause: C) -> Self
81    where
82        C: Into<Box<dyn Error + Send + Sync>>,
83    {
84        Self {
85            msg: String::new(),
86            cause: Some(cause.into()),
87        }
88    }
89}
90
91impl Display for ParseError {
92    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
93        if let Some(cause) = self.cause.as_ref() {
94            if self.msg.is_empty() {
95                Display::fmt(cause, f)
96            } else {
97                write!(f, "{}: {}", self.msg, cause)
98            }
99        } else if self.msg.is_empty() {
100            f.write_str("parse error")
101        } else {
102            f.write_str(&self.msg)
103        }
104    }
105}
106
107impl Error for ParseError {
108    fn source(&self) -> Option<&(dyn Error + 'static)> {
109        if let Some(cause) = self.cause.as_ref() {
110            Some(cause.as_ref())
111        } else {
112            None
113        }
114    }
115}
116
117impl From<std::convert::Infallible> for ParseError {
118    fn from(_: std::convert::Infallible) -> Self {
119        Self::plain()
120    }
121}
122
123impl From<str_reader::ParseError> for ParseError {
124    fn from(err: str_reader::ParseError) -> Self {
125        Self::with_cause(err)
126    }
127}
128
129impl From<std::net::AddrParseError> for ParseError {
130    fn from(err: std::net::AddrParseError) -> Self {
131        Self::with_cause(err)
132    }
133}
134
135impl From<std::num::ParseIntError> for ParseError {
136    fn from(err: std::num::ParseIntError) -> Self {
137        Self::with_cause(err)
138    }
139}
140
141/// Session description builder.
142#[derive(Clone)]
143pub struct SessionDescriptionBuilder {
144    inner: SessionDescription,
145}
146
147impl SessionDescriptionBuilder {
148    /// Create a new session description builder.
149    fn new() -> Self {
150        let inner = SessionDescription {
151            version: 0,
152            origin: Origin::default(),
153            session_name: String::new(),
154            session_information: None,
155            url: None,
156            emails: Vec::new(),
157            phones: Vec::new(),
158            connection: None,
159            bandwidth: Vec::new(),
160            time_descriptions: Vec::new(),
161            tz_adjustments: TimeZoneAdjustments::empty(),
162            key: None,
163            attributes: Attributes::new(),
164            media: Vec::new(),
165        };
166
167        Self { inner }
168    }
169
170    /// Set the SDP version.
171    #[inline]
172    pub fn version(&mut self, version: u16) -> &mut Self {
173        self.inner.version = version;
174        self
175    }
176
177    /// Set origin.
178    #[inline]
179    pub fn origin(&mut self, origin: Origin) -> &mut Self {
180        self.inner.origin = origin;
181        self
182    }
183
184    /// Set the name of the session.
185    #[inline]
186    pub fn session_name<T>(&mut self, name: T) -> &mut Self
187    where
188        T: ToString,
189    {
190        self.inner.session_name = name.to_string();
191        self
192    }
193
194    /// Set session information.
195    #[inline]
196    pub fn session_information<T>(&mut self, info: T) -> &mut Self
197    where
198        T: ToString,
199    {
200        self.inner.session_information = Some(info.to_string());
201        self
202    }
203
204    /// Set URL.
205    #[inline]
206    pub fn url<T>(&mut self, url: T) -> &mut Self
207    where
208        T: ToString,
209    {
210        self.inner.url = Some(url.to_string());
211        self
212    }
213
214    /// Add a given email address.
215    #[inline]
216    pub fn email<T>(&mut self, email: T) -> &mut Self
217    where
218        T: ToString,
219    {
220        self.inner.emails.push(email.to_string());
221        self
222    }
223
224    /// Add a given phone number.
225    #[inline]
226    pub fn phone<T>(&mut self, phone: T) -> &mut Self
227    where
228        T: ToString,
229    {
230        self.inner.phones.push(phone.to_string());
231        self
232    }
233
234    /// Set a given connection information.
235    #[inline]
236    pub fn connection(&mut self, connection: ConnectionInfo) -> &mut Self {
237        self.inner.connection = Some(connection);
238        self
239    }
240
241    /// Add a given bandwidth information.
242    #[inline]
243    pub fn bandwidth(&mut self, bandwidth: Bandwidth) -> &mut Self {
244        self.inner.bandwidth.push(bandwidth);
245        self
246    }
247
248    /// Add a given time description.
249    #[inline]
250    pub fn time_description(&mut self, td: TimeDescription) -> &mut Self {
251        self.inner.time_descriptions.push(td);
252        self
253    }
254
255    /// Add a given timezone adjustment.
256    #[inline]
257    pub fn tz_adjustment(&mut self, tz_adjustment: TimeZoneAdjustment) -> &mut Self {
258        self.inner.tz_adjustments.push(tz_adjustment);
259        self
260    }
261
262    /// Set a given encryption key.
263    #[inline]
264    pub fn encryption_key(&mut self, key: EncryptionKey) -> &mut Self {
265        self.inner.key = Some(key);
266        self
267    }
268
269    /// Add a given flag.
270    #[inline]
271    pub fn flag<T>(&mut self, name: T) -> &mut Self
272    where
273        T: ToString,
274    {
275        self.inner.attributes.push(Attribute::new_flag(name));
276        self
277    }
278
279    /// Add a given attribute.
280    #[inline]
281    pub fn attribute<T, U>(&mut self, name: T, value: U) -> &mut Self
282    where
283        T: ToString,
284        U: ToString,
285    {
286        self.inner
287            .attributes
288            .push(Attribute::new_attribute(name, value));
289        self
290    }
291
292    /// Add a given media description.
293    #[inline]
294    pub fn media_description(&mut self, desc: MediaDescription) -> &mut Self {
295        self.inner.media.push(desc);
296        self
297    }
298
299    /// Build the session description.
300    pub fn build(mut self) -> SessionDescription {
301        if self.inner.session_name.is_empty() {
302            self.inner.session_name = String::from("-");
303        }
304
305        if self.inner.time_descriptions.is_empty() {
306            self.inner
307                .time_descriptions
308                .push(TimeDescription::default());
309        }
310
311        self.inner
312    }
313}
314
315/// Session description.
316#[derive(Clone)]
317pub struct SessionDescription {
318    version: u16,
319    origin: Origin,
320    session_name: String,
321    session_information: Option<String>,
322    url: Option<String>,
323    emails: Vec<String>,
324    phones: Vec<String>,
325    connection: Option<ConnectionInfo>,
326    bandwidth: Vec<Bandwidth>,
327    time_descriptions: Vec<TimeDescription>,
328    tz_adjustments: TimeZoneAdjustments,
329    key: Option<EncryptionKey>,
330    attributes: Attributes,
331    media: Vec<MediaDescription>,
332}
333
334impl SessionDescription {
335    /// Create an empty session description.
336    fn empty() -> Self {
337        Self {
338            version: 0,
339            origin: Origin::default(),
340            session_name: String::new(),
341            session_information: None,
342            url: None,
343            emails: Vec::new(),
344            phones: Vec::new(),
345            connection: None,
346            bandwidth: Vec::new(),
347            time_descriptions: Vec::new(),
348            tz_adjustments: TimeZoneAdjustments::empty(),
349            key: None,
350            attributes: Attributes::new(),
351            media: Vec::new(),
352        }
353    }
354
355    /// Get a session description builder.
356    #[inline]
357    pub fn builder() -> SessionDescriptionBuilder {
358        SessionDescriptionBuilder::new()
359    }
360
361    /// Get version of this SDP.
362    #[inline]
363    pub fn version(&self) -> u16 {
364        self.version
365    }
366
367    /// Get the origin field.
368    #[inline]
369    pub fn origin(&self) -> &Origin {
370        &self.origin
371    }
372
373    /// Get name of the session.
374    #[inline]
375    pub fn session_name(&self) -> &str {
376        &self.session_name
377    }
378
379    /// Get session information.
380    #[inline]
381    pub fn session_information(&self) -> Option<&str> {
382        self.session_information.as_deref()
383    }
384
385    /// Get URL.
386    #[inline]
387    pub fn url(&self) -> Option<&str> {
388        self.url.as_deref()
389    }
390
391    /// Get a list of email addresses.
392    #[inline]
393    pub fn emails(&self) -> &[String] {
394        &self.emails
395    }
396
397    /// Get a list of phone numbers.
398    #[inline]
399    pub fn phones(&self) -> &[String] {
400        &self.phones
401    }
402
403    /// Get the session-wide connection info.
404    #[inline]
405    pub fn connection(&self) -> Option<&ConnectionInfo> {
406        self.connection.as_ref()
407    }
408
409    /// Get bandwidth information.
410    #[inline]
411    pub fn bandwidth(&self) -> &[Bandwidth] {
412        &self.bandwidth
413    }
414
415    /// Get time description.
416    #[inline]
417    pub fn time_descriptions(&self) -> &[TimeDescription] {
418        &self.time_descriptions
419    }
420
421    /// Get timezone adjustments.
422    #[inline]
423    pub fn tz_adjustments(&self) -> &[TimeZoneAdjustment] {
424        &self.tz_adjustments
425    }
426
427    /// Get the encryption key (if any).
428    #[inline]
429    pub fn encryption_key(&self) -> Option<&EncryptionKey> {
430        self.key.as_ref()
431    }
432
433    /// Get the session-wide attributes.
434    #[inline]
435    pub fn attributes(&self) -> &Attributes {
436        &self.attributes
437    }
438
439    /// Get media descriptions.
440    #[inline]
441    pub fn media_descriptions(&self) -> &[MediaDescription] {
442        &self.media
443    }
444}
445
446impl Display for SessionDescription {
447    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
448        write!(f, "v={}\r\n", self.version)?;
449        write!(f, "o={}\r\n", self.origin)?;
450        write!(f, "s={}\r\n", self.session_name)?;
451
452        if let Some(info) = self.session_information.as_ref() {
453            write!(f, "i={info}\r\n")?;
454        }
455
456        if let Some(url) = self.url.as_ref() {
457            write!(f, "u={url}\r\n")?;
458        }
459
460        for email in &self.emails {
461            write!(f, "e={email}\r\n")?;
462        }
463
464        for phone in &self.phones {
465            write!(f, "p={phone}\r\n")?;
466        }
467
468        if let Some(connection) = self.connection.as_ref() {
469            write!(f, "c={connection}\r\n")?;
470        }
471
472        for bw in &self.bandwidth {
473            write!(f, "b={bw}\r\n")?;
474        }
475
476        for td in &self.time_descriptions {
477            Display::fmt(td, f)?;
478        }
479
480        if !self.tz_adjustments.is_empty() {
481            write!(f, "z={}\r\n", self.tz_adjustments)?;
482        }
483
484        if let Some(k) = self.key.as_ref() {
485            write!(f, "k={k}\r\n")?;
486        }
487
488        for attr in self.attributes.iter() {
489            write!(f, "a={attr}\r\n")?;
490        }
491
492        for media in &self.media {
493            Display::fmt(media, f)?;
494        }
495
496        Ok(())
497    }
498}
499
500impl FromSessionDescriptionLines for SessionDescription {
501    fn from_sdp_lines(lines: &mut SessionDescriptionLines) -> Result<Self, ParseError> {
502        let mut sdp = SessionDescription::empty();
503
504        while let Some((t, _)) = lines.current() {
505            match t {
506                'v' => sdp.version = lines.parse()?,
507                'o' => sdp.origin = lines.parse()?,
508                's' => sdp.session_name = lines.parse()?,
509                'i' => sdp.session_information = Some(lines.parse()?),
510                'u' => sdp.url = Some(lines.parse()?),
511                'e' => sdp.emails.push(lines.parse()?),
512                'p' => sdp.phones.push(lines.parse()?),
513                'c' => sdp.connection = Some(lines.parse()?),
514                'b' => sdp.bandwidth.push(lines.parse()?),
515                't' => sdp.time_descriptions.push(lines.parse_multiple()?),
516                'z' => sdp.tz_adjustments = lines.parse()?,
517                'k' => sdp.key = Some(lines.parse()?),
518                'a' => sdp.attributes.push(lines.parse()?),
519                'm' => sdp.media.push(lines.parse_multiple()?),
520                _ => return Err(ParseError::with_msg(format!("unknown SDP field: {t}"))),
521            }
522        }
523
524        Ok(sdp)
525    }
526}
527
528impl FromStr for SessionDescription {
529    type Err = ParseError;
530
531    fn from_str(s: &str) -> Result<Self, Self::Err> {
532        let mut lines = SessionDescriptionLines::new(s)?;
533
534        SessionDescription::from_sdp_lines(&mut lines)
535    }
536}
537
538/// Network type.
539#[derive(Clone, Eq, PartialEq, Hash)]
540pub enum NetworkType {
541    Internet,
542    Other(String),
543}
544
545impl Display for NetworkType {
546    #[inline]
547    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
548        let s = match self {
549            Self::Internet => "IN",
550            Self::Other(o) => o,
551        };
552
553        f.write_str(s)
554    }
555}
556
557impl FromStr for NetworkType {
558    type Err = Infallible;
559
560    fn from_str(s: &str) -> Result<Self, Self::Err> {
561        let res = match s.trim() {
562            "IN" => Self::Internet,
563            o => Self::Other(o.to_string()),
564        };
565
566        Ok(res)
567    }
568}
569
570/// Address type.
571#[derive(Clone, Eq, PartialEq, Hash)]
572pub enum AddressType {
573    IPv4,
574    IPv6,
575    Other(String),
576}
577
578impl Display for AddressType {
579    #[inline]
580    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
581        let s = match self {
582            Self::IPv4 => "IP4",
583            Self::IPv6 => "IP6",
584            Self::Other(o) => o,
585        };
586
587        f.write_str(s)
588    }
589}
590
591impl FromStr for AddressType {
592    type Err = Infallible;
593
594    fn from_str(s: &str) -> Result<Self, Self::Err> {
595        let res = match s.trim() {
596            "IP4" => Self::IPv4,
597            "IP6" => Self::IPv6,
598            o => Self::Other(o.to_string()),
599        };
600
601        Ok(res)
602    }
603}