Skip to main content

rtc_sdp/description/
session.rs

1use std::collections::HashMap;
2use std::convert::TryFrom;
3use std::time::{Duration, SystemTime, UNIX_EPOCH};
4use std::{fmt, io};
5
6use url::Url;
7
8use crate::lexer::*;
9use crate::util::*;
10use shared::error::{Error, Result};
11
12use super::common::*;
13use super::media::*;
14
15/// Constants for SDP attributes used in JSEP
16pub const ATTR_KEY_CANDIDATE: &str = "candidate";
17pub const ATTR_KEY_END_OF_CANDIDATES: &str = "end-of-candidates";
18pub const ATTR_KEY_IDENTITY: &str = "identity";
19pub const ATTR_KEY_GROUP: &str = "group";
20pub const ATTR_KEY_SSRC: &str = "ssrc";
21pub const ATTR_KEY_SSRC_GROUP: &str = "ssrc-group";
22pub const ATTR_KEY_MSID: &str = "msid";
23pub const ATTR_KEY_MSID_SEMANTIC: &str = "msid-semantic";
24pub const ATTR_KEY_CONNECTION_SETUP: &str = "setup";
25pub const ATTR_KEY_MID: &str = "mid";
26pub const ATTR_KEY_ICELITE: &str = "ice-lite";
27pub const ATTR_KEY_RTCPMUX: &str = "rtcp-mux";
28pub const ATTR_KEY_RTCPRSIZE: &str = "rtcp-rsize";
29pub const ATTR_KEY_INACTIVE: &str = "inactive";
30pub const ATTR_KEY_RECV_ONLY: &str = "recvonly";
31pub const ATTR_KEY_SEND_ONLY: &str = "sendonly";
32pub const ATTR_KEY_SEND_RECV: &str = "sendrecv";
33pub const ATTR_KEY_EXT_MAP: &str = "extmap";
34pub const ATTR_KEY_EXTMAP_ALLOW_MIXED: &str = "extmap-allow-mixed";
35pub const ATTR_KEY_MAX_MESSAGE_SIZE: &str = "max-message-size";
36
37/// Constants for semantic tokens used in JSEP
38pub const SEMANTIC_TOKEN_LIP_SYNCHRONIZATION: &str = "LS";
39pub const SEMANTIC_TOKEN_FLOW_IDENTIFICATION: &str = "FID";
40pub const SEMANTIC_TOKEN_FORWARD_ERROR_CORRECTION: &str = "FEC";
41// https://datatracker.ietf.org/doc/html/rfc5956#section-4.1
42pub const SEMANTIC_TOKEN_FORWARD_ERROR_CORRECTION_FRAMEWORK: &str = "FEC-FR";
43pub const SEMANTIC_TOKEN_WEBRTC_MEDIA_STREAMS: &str = "WMS";
44
45/// Version describes the value provided by the "v=" field which gives
46/// the version of the Session Description Protocol.
47pub type Version = isize;
48
49/// Origin defines the structure for the "o=" field which provides the
50/// originator of the session plus a session identifier and version number.
51#[derive(Debug, Default, Clone)]
52pub struct Origin {
53    pub username: String,
54    pub session_id: u64,
55    pub session_version: u64,
56    pub network_type: String,
57    pub address_type: String,
58    pub unicast_address: String,
59}
60
61impl fmt::Display for Origin {
62    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63        write!(
64            f,
65            "{} {} {} {} {} {}",
66            self.username,
67            self.session_id,
68            self.session_version,
69            self.network_type,
70            self.address_type,
71            self.unicast_address,
72        )
73    }
74}
75
76impl Origin {
77    pub fn new() -> Self {
78        Origin {
79            username: "".to_owned(),
80            session_id: 0,
81            session_version: 0,
82            network_type: "".to_owned(),
83            address_type: "".to_owned(),
84            unicast_address: "".to_owned(),
85        }
86    }
87}
88
89/// SessionName describes a structured representations for the "s=" field
90/// and is the textual session name.
91pub type SessionName = String;
92
93/// EmailAddress describes a structured representations for the "e=" line
94/// which specifies email contact information for the person responsible for
95/// the conference.
96pub type EmailAddress = String;
97
98/// PhoneNumber describes a structured representations for the "p=" line
99/// specify phone contact information for the person responsible for the
100/// conference.
101pub type PhoneNumber = String;
102
103/// TimeZone defines the structured object for "z=" line which describes
104/// repeated sessions scheduling.
105#[derive(Debug, Default, Clone)]
106pub struct TimeZone {
107    pub adjustment_time: u64,
108    pub offset: i64,
109}
110
111impl fmt::Display for TimeZone {
112    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113        write!(f, "{} {}", self.adjustment_time, self.offset)
114    }
115}
116
117/// TimeDescription describes "t=", "r=" fields of the session description
118/// which are used to specify the start and stop times for a session as well as
119/// repeat intervals and durations for the scheduled session.
120#[derive(Debug, Default, Clone)]
121pub struct TimeDescription {
122    /// `t=<start-time> <stop-time>`
123    ///
124    /// <https://tools.ietf.org/html/rfc4566#section-5.9>
125    pub timing: Timing,
126
127    /// `r=<repeat interval> <active duration> <offsets from start-time>`
128    ///
129    /// <https://tools.ietf.org/html/rfc4566#section-5.10>
130    pub repeat_times: Vec<RepeatTime>,
131}
132
133/// Timing defines the "t=" field's structured representation for the start and
134/// stop times.
135#[derive(Debug, Default, Clone)]
136pub struct Timing {
137    pub start_time: u64,
138    pub stop_time: u64,
139}
140
141impl fmt::Display for Timing {
142    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
143        write!(f, "{} {}", self.start_time, self.stop_time)
144    }
145}
146
147/// RepeatTime describes the "r=" fields of the session description which
148/// represents the intervals and durations for repeated scheduled sessions.
149#[derive(Debug, Default, Clone)]
150pub struct RepeatTime {
151    pub interval: i64,
152    pub duration: i64,
153    pub offsets: Vec<i64>,
154}
155
156impl fmt::Display for RepeatTime {
157    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
158        write!(f, "{} {}", self.interval, self.duration)?;
159
160        for value in &self.offsets {
161            write!(f, " {value}")?;
162        }
163        Ok(())
164    }
165}
166
167/// SessionDescription is a a well-defined format for conveying sufficient
168/// information to discover and participate in a multimedia session.
169#[derive(Debug, Default, Clone)]
170pub struct SessionDescription {
171    /// `v=0`
172    ///
173    /// <https://tools.ietf.org/html/rfc4566#section-5.1>
174    pub version: Version,
175
176    /// `o=<username> <sess-id> <sess-version> <nettype> <addrtype> <unicast-address>`
177    ///
178    /// <https://tools.ietf.org/html/rfc4566#section-5.2>
179    pub origin: Origin,
180
181    /// `s=<session name>`
182    ///
183    /// <https://tools.ietf.org/html/rfc4566#section-5.3>
184    pub session_name: SessionName,
185
186    /// `i=<session description>`
187    ///
188    /// <https://tools.ietf.org/html/rfc4566#section-5.4>
189    pub session_information: Option<Information>,
190
191    /// `u=<uri>`
192    ///
193    /// <https://tools.ietf.org/html/rfc4566#section-5.5>
194    pub uri: Option<Url>,
195
196    /// `e=<email-address>`
197    ///
198    /// <https://tools.ietf.org/html/rfc4566#section-5.6>
199    pub email_address: Option<EmailAddress>,
200
201    /// `p=<phone-number>`
202    ///
203    /// <https://tools.ietf.org/html/rfc4566#section-5.6>
204    pub phone_number: Option<PhoneNumber>,
205
206    /// `c=<nettype> <addrtype> <connection-address>`
207    ///
208    /// <https://tools.ietf.org/html/rfc4566#section-5.7>
209    pub connection_information: Option<ConnectionInformation>,
210
211    /// `b=<bwtype>:<bandwidth>`
212    ///
213    /// <https://tools.ietf.org/html/rfc4566#section-5.8>
214    pub bandwidth: Vec<Bandwidth>,
215
216    /// <https://tools.ietf.org/html/rfc4566#section-5.9>
217    /// <https://tools.ietf.org/html/rfc4566#section-5.10>
218    pub time_descriptions: Vec<TimeDescription>,
219
220    /// `z=<adjustment time> <offset> <adjustment time> <offset> ...`
221    ///
222    /// <https://tools.ietf.org/html/rfc4566#section-5.11>
223    pub time_zones: Vec<TimeZone>,
224
225    /// `k=<method>`
226    ///
227    /// `k=<method>:<encryption key>`
228    ///
229    /// <https://tools.ietf.org/html/rfc4566#section-5.12>
230    pub encryption_key: Option<EncryptionKey>,
231
232    /// `a=<attribute>`
233    ///
234    /// `a=<attribute>:<value>`
235    ///
236    /// <https://tools.ietf.org/html/rfc4566#section-5.13>
237    pub attributes: Vec<Attribute>,
238
239    /// <https://tools.ietf.org/html/rfc4566#section-5.14>
240    pub media_descriptions: Vec<MediaDescription>,
241}
242
243impl fmt::Display for SessionDescription {
244    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
245        write_key_value(f, "v=", Some(&self.version))?;
246        write_key_value(f, "o=", Some(&self.origin))?;
247        write_key_value(f, "s=", Some(&self.session_name))?;
248
249        write_key_value(f, "i=", self.session_information.as_ref())?;
250
251        if let Some(uri) = &self.uri {
252            write_key_value(f, "u=", Some(uri))?;
253        }
254        write_key_value(f, "e=", self.email_address.as_ref())?;
255        write_key_value(f, "p=", self.phone_number.as_ref())?;
256        if let Some(connection_information) = &self.connection_information {
257            write_key_value(f, "c=", Some(&connection_information))?;
258        }
259
260        for bandwidth in &self.bandwidth {
261            write_key_value(f, "b=", Some(&bandwidth))?;
262        }
263        for time_description in &self.time_descriptions {
264            write_key_value(f, "t=", Some(&time_description.timing))?;
265            for repeat_time in &time_description.repeat_times {
266                write_key_value(f, "r=", Some(&repeat_time))?;
267            }
268        }
269
270        write_key_slice_of_values(f, "z=", &self.time_zones)?;
271
272        write_key_value(f, "k=", self.encryption_key.as_ref())?;
273        for attribute in &self.attributes {
274            write_key_value(f, "a=", Some(&attribute))?;
275        }
276
277        for media_description in &self.media_descriptions {
278            write_key_value(f, "m=", Some(&media_description.media_name))?;
279            write_key_value(f, "i=", media_description.media_title.as_ref())?;
280            if let Some(connection_information) = &media_description.connection_information {
281                write_key_value(f, "c=", Some(&connection_information))?;
282            }
283            for bandwidth in &media_description.bandwidth {
284                write_key_value(f, "b=", Some(&bandwidth))?;
285            }
286            write_key_value(f, "k=", media_description.encryption_key.as_ref())?;
287            for attribute in &media_description.attributes {
288                write_key_value(f, "a=", Some(&attribute))?;
289            }
290        }
291
292        Ok(())
293    }
294}
295
296/// Reset cleans the SessionDescription, and sets all fields back to their default values
297impl SessionDescription {
298    /// API to match draft-ietf-rtcweb-jsep
299    /// Move to webrtc or its own package?
300    /// NewJSEPSessionDescription creates a new SessionDescription with
301    /// some settings that are required by the JSEP spec.
302    pub fn new_jsep_session_description(identity: bool) -> Self {
303        let d = SessionDescription {
304            version: 0,
305            origin: Origin {
306                username: "-".to_string(),
307                session_id: new_session_id(),
308                session_version: SystemTime::now()
309                    .duration_since(UNIX_EPOCH)
310                    .unwrap_or_else(|_| Duration::from_secs(0))
311                    .subsec_nanos() as u64,
312                network_type: "IN".to_string(),
313                address_type: "IP4".to_string(),
314                unicast_address: "0.0.0.0".to_string(),
315            },
316            session_name: "-".to_string(),
317            session_information: None,
318            uri: None,
319            email_address: None,
320            phone_number: None,
321            connection_information: None,
322            bandwidth: vec![],
323            time_descriptions: vec![TimeDescription {
324                timing: Timing {
325                    start_time: 0,
326                    stop_time: 0,
327                },
328                repeat_times: vec![],
329            }],
330            time_zones: vec![],
331            encryption_key: None,
332            attributes: vec![], // TODO: implement trickle ICE
333            media_descriptions: vec![],
334        };
335
336        if identity {
337            d.with_property_attribute(ATTR_KEY_IDENTITY.to_string())
338        } else {
339            d
340        }
341    }
342
343    /// WithPropertyAttribute adds a property attribute 'a=key' to the session description
344    pub fn with_property_attribute(mut self, key: String) -> Self {
345        self.attributes.push(Attribute::new(key, None));
346        self
347    }
348
349    /// WithValueAttribute adds a value attribute 'a=key:value' to the session description
350    pub fn with_value_attribute(mut self, key: String, value: String) -> Self {
351        self.attributes.push(Attribute::new(key, Some(value)));
352        self
353    }
354
355    /// WithFingerprint adds a fingerprint to the session description
356    pub fn with_fingerprint(self, algorithm: String, value: String) -> Self {
357        self.with_value_attribute("fingerprint".to_string(), algorithm + " " + value.as_str())
358    }
359
360    /// WithMedia adds a media description to the session description
361    pub fn with_media(mut self, md: MediaDescription) -> Self {
362        self.media_descriptions.push(md);
363        self
364    }
365
366    fn build_codec_map(&self) -> HashMap<u8, Codec> {
367        let mut codecs: HashMap<u8, Codec> = HashMap::new();
368
369        for m in &self.media_descriptions {
370            codecs.extend(m.codecs());
371        }
372
373        codecs
374    }
375
376    /// get_codec_for_payload_type scans the SessionDescription for the given payload type and returns the codec
377    pub fn get_codec_for_payload_type(&self, payload_type: u8) -> Result<Codec> {
378        let codecs = self.build_codec_map();
379
380        if let Some(codec) = codecs.get(&payload_type) {
381            Ok(codec.clone())
382        } else {
383            Err(Error::PayloadTypeNotFound)
384        }
385    }
386
387    /// get_payload_type_for_codec scans the SessionDescription for a codec that matches the provided codec
388    /// as closely as possible and returns its payload type
389    pub fn get_payload_type_for_codec(&self, wanted: &Codec) -> Result<u8> {
390        let codecs = self.build_codec_map();
391
392        for (payload_type, codec) in codecs.iter() {
393            if codecs_match(wanted, codec) {
394                return Ok(*payload_type);
395            }
396        }
397
398        Err(Error::CodecNotFound)
399    }
400
401    /// Returns whether an attribute exists
402    pub fn has_attribute(&self, key: &str) -> bool {
403        self.attributes.iter().any(|a| a.key == key)
404    }
405
406    /// Attribute returns the value of an attribute and if it exists
407    pub fn attribute(&self, key: &str) -> Option<&String> {
408        for a in &self.attributes {
409            if a.key == key {
410                return a.value.as_ref();
411            }
412        }
413        None
414    }
415
416    /// Marshal takes a SDP struct to text
417    ///
418    /// <https://tools.ietf.org/html/rfc4566#section-5>
419    ///
420    /// Session description
421    ///    v=  (protocol version)
422    ///    o=  (originator and session identifier)
423    ///    s=  (session name)
424    ///    i=* (session information)
425    ///    u=* (URI of description)
426    ///    e=* (email address)
427    ///    p=* (phone number)
428    ///    c=* (connection information -- not required if included in
429    ///         all media)
430    ///    b=* (zero or more bandwidth information lines)
431    ///    One or more time descriptions ("t=" and "r=" lines; see below)
432    ///    z=* (time zone adjustments)
433    ///    k=* (encryption key)
434    ///    a=* (zero or more session attribute lines)
435    ///    Zero or more media descriptions
436    ///
437    /// Time description
438    ///    t=  (time the session is active)
439    ///    r=* (zero or more repeat times)
440    ///
441    /// Media description, if present
442    ///    m=  (media name and transport address)
443    ///    i=* (media title)
444    ///    c=* (connection information -- optional if included at
445    ///         session level)
446    ///    b=* (zero or more bandwidth information lines)
447    ///    k=* (encryption key)
448    ///    a=* (zero or more media attribute lines)
449    pub fn marshal(&self) -> String {
450        self.to_string()
451    }
452
453    /// Unmarshal is the primary function that deserializes the session description
454    /// message and stores it inside of a structured SessionDescription object.
455    ///
456    /// The States Transition Table describes the computation flow between functions
457    /// (namely s1, s2, s3, ...) for a parsing procedure that complies with the
458    /// specifications laid out by the rfc4566#section-5 as well as by JavaScript
459    /// Session Establishment Protocol draft. Links:
460    ///     <https://tools.ietf.org/html/rfc4566#section-5>
461    ///     <https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-24>
462    ///
463    /// <https://tools.ietf.org/html/rfc4566#section-5>
464    ///
465    /// Session description
466    ///    v=  (protocol version)
467    ///    o=  (originator and session identifier)
468    ///    s=  (session name)
469    ///    i=* (session information)
470    ///    u=* (URI of description)
471    ///    e=* (email address)
472    ///    p=* (phone number)
473    ///    c=* (connection information -- not required if included in
474    ///         all media)
475    ///    b=* (zero or more bandwidth information lines)
476    ///    One or more time descriptions ("t=" and "r=" lines; see below)
477    ///    z=* (time zone adjustments)
478    ///    k=* (encryption key)
479    ///    a=* (zero or more session attribute lines)
480    ///    Zero or more media descriptions
481    ///
482    /// Time description
483    ///    t=  (time the session is active)
484    ///    r=* (zero or more repeat times)
485    ///
486    /// Media description, if present
487    ///    m=  (media name and transport address)
488    ///    i=* (media title)
489    ///    c=* (connection information -- optional if included at
490    ///         session level)
491    ///    b=* (zero or more bandwidth information lines)
492    ///    k=* (encryption key)
493    ///    a=* (zero or more media attribute lines)
494    ///
495    /// In order to generate the following state table and draw subsequent
496    /// deterministic finite-state automota ("DFA") the following regex was used to
497    /// derive the DFA:
498    ///    vosi?u?e?p?c?b*(tr*)+z?k?a*(mi?c?b*k?a*)*
499    /// possible place and state to exit:
500    ///                    **   * * *  ** * * * *
501    ///                    99   1 1 1  11 1 1 1 1
502    ///                         3 1 1  26 5 5 4 4
503    ///
504    /// Please pay close attention to the `k`, and `a` parsing states. In the table
505    /// below in order to distinguish between the states belonging to the media
506    /// description as opposed to the session description, the states are marked
507    /// with an asterisk ("a*", "k*").
508    ///
509    /// ```ignore
510    /// +--------+----+-------+----+-----+----+-----+---+----+----+---+---+-----+---+---+----+---+----+
511    /// | STATES | a* | a*,k* | a  | a,k | b  | b,c | e | i  | m  | o | p | r,t | s | t | u  | v | z  |
512    /// +--------+----+-------+----+-----+----+-----+---+----+----+---+---+-----+---+---+----+---+----+
513    /// |   s1   |    |       |    |     |    |     |   |    |    |   |   |     |   |   |    | 2 |    |
514    /// |   s2   |    |       |    |     |    |     |   |    |    | 3 |   |     |   |   |    |   |    |
515    /// |   s3   |    |       |    |     |    |     |   |    |    |   |   |     | 4 |   |    |   |    |
516    /// |   s4   |    |       |    |     |    |   5 | 6 |  7 |    |   | 8 |     |   | 9 | 10 |   |    |
517    /// |   s5   |    |       |    |     |  5 |     |   |    |    |   |   |     |   | 9 |    |   |    |
518    /// |   s6   |    |       |    |     |    |   5 |   |    |    |   | 8 |     |   | 9 |    |   |    |
519    /// |   s7   |    |       |    |     |    |   5 | 6 |    |    |   | 8 |     |   | 9 | 10 |   |    |
520    /// |   s8   |    |       |    |     |    |   5 |   |    |    |   |   |     |   | 9 |    |   |    |
521    /// |   s9   |    |       |    |  11 |    |     |   |    | 12 |   |   |   9 |   |   |    |   | 13 |
522    /// |   s10  |    |       |    |     |    |   5 | 6 |    |    |   | 8 |     |   | 9 |    |   |    |
523    /// |   s11  |    |       | 11 |     |    |     |   |    | 12 |   |   |     |   |   |    |   |    |
524    /// |   s12  |    |    14 |    |     |    |  15 |   | 16 | 12 |   |   |     |   |   |    |   |    |
525    /// |   s13  |    |       |    |  11 |    |     |   |    | 12 |   |   |     |   |   |    |   |    |
526    /// |   s14  | 14 |       |    |     |    |     |   |    | 12 |   |   |     |   |   |    |   |    |
527    /// |   s15  |    |    14 |    |     | 15 |     |   |    | 12 |   |   |     |   |   |    |   |    |
528    /// |   s16  |    |    14 |    |     |    |  15 |   |    | 12 |   |   |     |   |   |    |   |    |
529    /// +--------+----+-------+----+-----+----+-----+---+----+----+---+---+-----+---+---+----+---+----+
530    /// ```
531    pub fn unmarshal<R: io::BufRead + io::Seek>(reader: &mut R) -> Result<Self> {
532        let mut lexer = Lexer {
533            desc: SessionDescription {
534                version: 0,
535                origin: Origin::new(),
536                session_name: "".to_owned(),
537                session_information: None,
538                uri: None,
539                email_address: None,
540                phone_number: None,
541                connection_information: None,
542                bandwidth: vec![],
543                time_descriptions: vec![],
544                time_zones: vec![],
545                encryption_key: None,
546                attributes: vec![],
547                media_descriptions: vec![],
548            },
549            reader,
550        };
551
552        let mut state = Some(StateFn { f: s1 });
553        while let Some(s) = state {
554            state = (s.f)(&mut lexer)?;
555        }
556
557        Ok(lexer.desc)
558    }
559}
560
561impl From<SessionDescription> for String {
562    fn from(sdp: SessionDescription) -> String {
563        sdp.marshal()
564    }
565}
566
567impl TryFrom<String> for SessionDescription {
568    type Error = Error;
569    fn try_from(sdp_string: String) -> Result<Self> {
570        let mut reader = io::Cursor::new(sdp_string.as_bytes());
571        let session_description = SessionDescription::unmarshal(&mut reader)?;
572        Ok(session_description)
573    }
574}
575
576fn s1<'a, R: io::BufRead + io::Seek>(lexer: &mut Lexer<'a, R>) -> Result<Option<StateFn<'a, R>>> {
577    let (key, _) = read_type(lexer.reader)?;
578    if &key == b"v=" {
579        return Ok(Some(StateFn {
580            f: unmarshal_protocol_version,
581        }));
582    }
583
584    Err(Error::SdpInvalidSyntax(String::from_utf8(key)?))
585}
586
587fn s2<'a, R: io::BufRead + io::Seek>(lexer: &mut Lexer<'a, R>) -> Result<Option<StateFn<'a, R>>> {
588    let (key, _) = read_type(lexer.reader)?;
589    if &key == b"o=" {
590        return Ok(Some(StateFn {
591            f: unmarshal_origin,
592        }));
593    }
594
595    Err(Error::SdpInvalidSyntax(String::from_utf8(key)?))
596}
597
598fn s3<'a, R: io::BufRead + io::Seek>(lexer: &mut Lexer<'a, R>) -> Result<Option<StateFn<'a, R>>> {
599    let (key, _) = read_type(lexer.reader)?;
600    if &key == b"s=" {
601        return Ok(Some(StateFn {
602            f: unmarshal_session_name,
603        }));
604    }
605
606    Err(Error::SdpInvalidSyntax(String::from_utf8(key)?))
607}
608
609fn s4<'a, R: io::BufRead + io::Seek>(lexer: &mut Lexer<'a, R>) -> Result<Option<StateFn<'a, R>>> {
610    let (key, _) = read_type(lexer.reader)?;
611    match key.as_slice() {
612        b"i=" => Ok(Some(StateFn {
613            f: unmarshal_session_information,
614        })),
615        b"u=" => Ok(Some(StateFn { f: unmarshal_uri })),
616        b"e=" => Ok(Some(StateFn { f: unmarshal_email })),
617        b"p=" => Ok(Some(StateFn { f: unmarshal_phone })),
618        b"c=" => Ok(Some(StateFn {
619            f: unmarshal_session_connection_information,
620        })),
621        b"b=" => Ok(Some(StateFn {
622            f: unmarshal_session_bandwidth,
623        })),
624        b"t=" => Ok(Some(StateFn {
625            f: unmarshal_timing,
626        })),
627        _ => Err(Error::SdpInvalidSyntax(String::from_utf8(key)?)),
628    }
629}
630
631fn s5<'a, R: io::BufRead + io::Seek>(lexer: &mut Lexer<'a, R>) -> Result<Option<StateFn<'a, R>>> {
632    let (key, _) = read_type(lexer.reader)?;
633    match key.as_slice() {
634        b"b=" => Ok(Some(StateFn {
635            f: unmarshal_session_bandwidth,
636        })),
637        b"t=" => Ok(Some(StateFn {
638            f: unmarshal_timing,
639        })),
640        _ => Err(Error::SdpInvalidSyntax(String::from_utf8(key)?)),
641    }
642}
643
644fn s6<'a, R: io::BufRead + io::Seek>(lexer: &mut Lexer<'a, R>) -> Result<Option<StateFn<'a, R>>> {
645    let (key, _) = read_type(lexer.reader)?;
646    match key.as_slice() {
647        b"p=" => Ok(Some(StateFn { f: unmarshal_phone })),
648        b"c=" => Ok(Some(StateFn {
649            f: unmarshal_session_connection_information,
650        })),
651        b"b=" => Ok(Some(StateFn {
652            f: unmarshal_session_bandwidth,
653        })),
654        b"t=" => Ok(Some(StateFn {
655            f: unmarshal_timing,
656        })),
657        _ => Err(Error::SdpInvalidSyntax(String::from_utf8(key)?)),
658    }
659}
660
661fn s7<'a, R: io::BufRead + io::Seek>(lexer: &mut Lexer<'a, R>) -> Result<Option<StateFn<'a, R>>> {
662    let (key, _) = read_type(lexer.reader)?;
663    match key.as_slice() {
664        b"u=" => Ok(Some(StateFn { f: unmarshal_uri })),
665        b"e=" => Ok(Some(StateFn { f: unmarshal_email })),
666        b"p=" => Ok(Some(StateFn { f: unmarshal_phone })),
667        b"c=" => Ok(Some(StateFn {
668            f: unmarshal_session_connection_information,
669        })),
670        b"b=" => Ok(Some(StateFn {
671            f: unmarshal_session_bandwidth,
672        })),
673        b"t=" => Ok(Some(StateFn {
674            f: unmarshal_timing,
675        })),
676        _ => Err(Error::SdpInvalidSyntax(String::from_utf8(key)?)),
677    }
678}
679
680fn s8<'a, R: io::BufRead + io::Seek>(lexer: &mut Lexer<'a, R>) -> Result<Option<StateFn<'a, R>>> {
681    let (key, _) = read_type(lexer.reader)?;
682    match key.as_slice() {
683        b"c=" => Ok(Some(StateFn {
684            f: unmarshal_session_connection_information,
685        })),
686        b"b=" => Ok(Some(StateFn {
687            f: unmarshal_session_bandwidth,
688        })),
689        b"t=" => Ok(Some(StateFn {
690            f: unmarshal_timing,
691        })),
692        _ => Err(Error::SdpInvalidSyntax(String::from_utf8(key)?)),
693    }
694}
695
696fn s9<'a, R: io::BufRead + io::Seek>(lexer: &mut Lexer<'a, R>) -> Result<Option<StateFn<'a, R>>> {
697    let (key, num_bytes) = read_type(lexer.reader)?;
698    if key.is_empty() && num_bytes == 0 {
699        return Ok(None);
700    }
701
702    match key.as_slice() {
703        b"z=" => Ok(Some(StateFn {
704            f: unmarshal_time_zones,
705        })),
706        b"k=" => Ok(Some(StateFn {
707            f: unmarshal_session_encryption_key,
708        })),
709        b"a=" => Ok(Some(StateFn {
710            f: unmarshal_session_attribute,
711        })),
712        b"r=" => Ok(Some(StateFn {
713            f: unmarshal_repeat_times,
714        })),
715        b"t=" => Ok(Some(StateFn {
716            f: unmarshal_timing,
717        })),
718        b"m=" => Ok(Some(StateFn {
719            f: unmarshal_media_description,
720        })),
721        _ => Err(Error::SdpInvalidSyntax(String::from_utf8(key)?)),
722    }
723}
724
725fn s10<'a, R: io::BufRead + io::Seek>(lexer: &mut Lexer<'a, R>) -> Result<Option<StateFn<'a, R>>> {
726    let (key, _) = read_type(lexer.reader)?;
727    match key.as_slice() {
728        b"e=" => Ok(Some(StateFn { f: unmarshal_email })),
729        b"p=" => Ok(Some(StateFn { f: unmarshal_phone })),
730        b"c=" => Ok(Some(StateFn {
731            f: unmarshal_session_connection_information,
732        })),
733        b"b=" => Ok(Some(StateFn {
734            f: unmarshal_session_bandwidth,
735        })),
736        b"t=" => Ok(Some(StateFn {
737            f: unmarshal_timing,
738        })),
739        _ => Err(Error::SdpInvalidSyntax(String::from_utf8(key)?)),
740    }
741}
742
743fn s11<'a, R: io::BufRead + io::Seek>(lexer: &mut Lexer<'a, R>) -> Result<Option<StateFn<'a, R>>> {
744    let (key, num_bytes) = read_type(lexer.reader)?;
745    if key.is_empty() && num_bytes == 0 {
746        return Ok(None);
747    }
748
749    match key.as_slice() {
750        b"a=" => Ok(Some(StateFn {
751            f: unmarshal_session_attribute,
752        })),
753        b"m=" => Ok(Some(StateFn {
754            f: unmarshal_media_description,
755        })),
756        _ => Err(Error::SdpInvalidSyntax(String::from_utf8(key)?)),
757    }
758}
759
760fn s12<'a, R: io::BufRead + io::Seek>(lexer: &mut Lexer<'a, R>) -> Result<Option<StateFn<'a, R>>> {
761    let (key, num_bytes) = read_type(lexer.reader)?;
762    if key.is_empty() && num_bytes == 0 {
763        return Ok(None);
764    }
765
766    match key.as_slice() {
767        b"a=" => Ok(Some(StateFn {
768            f: unmarshal_media_attribute,
769        })),
770        b"k=" => Ok(Some(StateFn {
771            f: unmarshal_media_encryption_key,
772        })),
773        b"b=" => Ok(Some(StateFn {
774            f: unmarshal_media_bandwidth,
775        })),
776        b"c=" => Ok(Some(StateFn {
777            f: unmarshal_media_connection_information,
778        })),
779        b"i=" => Ok(Some(StateFn {
780            f: unmarshal_media_title,
781        })),
782        b"m=" => Ok(Some(StateFn {
783            f: unmarshal_media_description,
784        })),
785        _ => Err(Error::SdpInvalidSyntax(String::from_utf8(key)?)),
786    }
787}
788
789fn s13<'a, R: io::BufRead + io::Seek>(lexer: &mut Lexer<'a, R>) -> Result<Option<StateFn<'a, R>>> {
790    let (key, num_bytes) = read_type(lexer.reader)?;
791    if key.is_empty() && num_bytes == 0 {
792        return Ok(None);
793    }
794
795    match key.as_slice() {
796        b"a=" => Ok(Some(StateFn {
797            f: unmarshal_session_attribute,
798        })),
799        b"k=" => Ok(Some(StateFn {
800            f: unmarshal_session_encryption_key,
801        })),
802        b"m=" => Ok(Some(StateFn {
803            f: unmarshal_media_description,
804        })),
805        _ => Err(Error::SdpInvalidSyntax(String::from_utf8(key)?)),
806    }
807}
808
809fn s14<'a, R: io::BufRead + io::Seek>(lexer: &mut Lexer<'a, R>) -> Result<Option<StateFn<'a, R>>> {
810    let (key, num_bytes) = read_type(lexer.reader)?;
811    if key.is_empty() && num_bytes == 0 {
812        return Ok(None);
813    }
814
815    match key.as_slice() {
816        b"a=" => Ok(Some(StateFn {
817            f: unmarshal_media_attribute,
818        })),
819        // Non-spec ordering
820        b"k=" => Ok(Some(StateFn {
821            f: unmarshal_media_encryption_key,
822        })),
823        // Non-spec ordering
824        b"b=" => Ok(Some(StateFn {
825            f: unmarshal_media_bandwidth,
826        })),
827        // Non-spec ordering
828        b"c=" => Ok(Some(StateFn {
829            f: unmarshal_media_connection_information,
830        })),
831        // Non-spec ordering
832        b"i=" => Ok(Some(StateFn {
833            f: unmarshal_media_title,
834        })),
835        b"m=" => Ok(Some(StateFn {
836            f: unmarshal_media_description,
837        })),
838        _ => Err(Error::SdpInvalidSyntax(String::from_utf8(key)?)),
839    }
840}
841
842fn s15<'a, R: io::BufRead + io::Seek>(lexer: &mut Lexer<'a, R>) -> Result<Option<StateFn<'a, R>>> {
843    let (key, num_bytes) = read_type(lexer.reader)?;
844    if key.is_empty() && num_bytes == 0 {
845        return Ok(None);
846    }
847
848    match key.as_slice() {
849        b"a=" => Ok(Some(StateFn {
850            f: unmarshal_media_attribute,
851        })),
852        b"k=" => Ok(Some(StateFn {
853            f: unmarshal_media_encryption_key,
854        })),
855        b"b=" => Ok(Some(StateFn {
856            f: unmarshal_media_bandwidth,
857        })),
858        b"c=" => Ok(Some(StateFn {
859            f: unmarshal_media_connection_information,
860        })),
861        // Non-spec ordering
862        b"i=" => Ok(Some(StateFn {
863            f: unmarshal_media_title,
864        })),
865        b"m=" => Ok(Some(StateFn {
866            f: unmarshal_media_description,
867        })),
868        _ => Err(Error::SdpInvalidSyntax(String::from_utf8(key)?)),
869    }
870}
871
872fn s16<'a, R: io::BufRead + io::Seek>(lexer: &mut Lexer<'a, R>) -> Result<Option<StateFn<'a, R>>> {
873    let (key, num_bytes) = read_type(lexer.reader)?;
874    if key.is_empty() && num_bytes == 0 {
875        return Ok(None);
876    }
877
878    match key.as_slice() {
879        b"a=" => Ok(Some(StateFn {
880            f: unmarshal_media_attribute,
881        })),
882        b"k=" => Ok(Some(StateFn {
883            f: unmarshal_media_encryption_key,
884        })),
885        b"c=" => Ok(Some(StateFn {
886            f: unmarshal_media_connection_information,
887        })),
888        b"b=" => Ok(Some(StateFn {
889            f: unmarshal_media_bandwidth,
890        })),
891        // Non-spec ordering
892        b"i=" => Ok(Some(StateFn {
893            f: unmarshal_media_title,
894        })),
895        b"m=" => Ok(Some(StateFn {
896            f: unmarshal_media_description,
897        })),
898        _ => Err(Error::SdpInvalidSyntax(String::from_utf8(key)?)),
899    }
900}
901
902fn unmarshal_protocol_version<'a, R: io::BufRead + io::Seek>(
903    lexer: &mut Lexer<'a, R>,
904) -> Result<Option<StateFn<'a, R>>> {
905    let (value, _) = read_value(lexer.reader)?;
906
907    let version = value.parse::<u32>()?;
908
909    // As off the latest draft of the rfc this value is required to be 0.
910    // https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-24#section-5.8.1
911    if version != 0 {
912        return Err(Error::SdpInvalidSyntax(value));
913    }
914
915    Ok(Some(StateFn { f: s2 }))
916}
917
918fn unmarshal_origin<'a, R: io::BufRead + io::Seek>(
919    lexer: &mut Lexer<'a, R>,
920) -> Result<Option<StateFn<'a, R>>> {
921    let (value, _) = read_value(lexer.reader)?;
922
923    let fields: Vec<&str> = value.split_whitespace().collect();
924    if fields.len() != 6 {
925        return Err(Error::SdpInvalidSyntax(format!("`o={value}`")));
926    }
927
928    let session_id = fields[1].parse::<u64>()?;
929    let session_version = fields[2].parse::<u64>()?;
930
931    // Set according to currently registered with IANA
932    // https://tools.ietf.org/html/rfc4566#section-8.2.6
933    let i = index_of(fields[3], &["IN"]);
934    if i == -1 {
935        return Err(Error::SdpInvalidValue(fields[3].to_owned()));
936    }
937
938    // Set according to currently registered with IANA
939    // https://tools.ietf.org/html/rfc4566#section-8.2.7
940    let i = index_of(fields[4], &["IP4", "IP6"]);
941    if i == -1 {
942        return Err(Error::SdpInvalidValue(fields[4].to_owned()));
943    }
944
945    // TODO validated UnicastAddress
946
947    lexer.desc.origin = Origin {
948        username: fields[0].to_owned(),
949        session_id,
950        session_version,
951        network_type: fields[3].to_owned(),
952        address_type: fields[4].to_owned(),
953        unicast_address: fields[5].to_owned(),
954    };
955
956    Ok(Some(StateFn { f: s3 }))
957}
958
959fn unmarshal_session_name<'a, R: io::BufRead + io::Seek>(
960    lexer: &mut Lexer<'a, R>,
961) -> Result<Option<StateFn<'a, R>>> {
962    let (value, _) = read_value(lexer.reader)?;
963    lexer.desc.session_name = value;
964    Ok(Some(StateFn { f: s4 }))
965}
966
967fn unmarshal_session_information<'a, R: io::BufRead + io::Seek>(
968    lexer: &mut Lexer<'a, R>,
969) -> Result<Option<StateFn<'a, R>>> {
970    let (value, _) = read_value(lexer.reader)?;
971    lexer.desc.session_information = Some(value);
972    Ok(Some(StateFn { f: s7 }))
973}
974
975fn unmarshal_uri<'a, R: io::BufRead + io::Seek>(
976    lexer: &mut Lexer<'a, R>,
977) -> Result<Option<StateFn<'a, R>>> {
978    let (value, _) = read_value(lexer.reader)?;
979    lexer.desc.uri = Some(Url::parse(&value)?);
980    Ok(Some(StateFn { f: s10 }))
981}
982
983fn unmarshal_email<'a, R: io::BufRead + io::Seek>(
984    lexer: &mut Lexer<'a, R>,
985) -> Result<Option<StateFn<'a, R>>> {
986    let (value, _) = read_value(lexer.reader)?;
987    lexer.desc.email_address = Some(value);
988    Ok(Some(StateFn { f: s6 }))
989}
990
991fn unmarshal_phone<'a, R: io::BufRead + io::Seek>(
992    lexer: &mut Lexer<'a, R>,
993) -> Result<Option<StateFn<'a, R>>> {
994    let (value, _) = read_value(lexer.reader)?;
995    lexer.desc.phone_number = Some(value);
996    Ok(Some(StateFn { f: s8 }))
997}
998
999fn unmarshal_session_connection_information<'a, R: io::BufRead + io::Seek>(
1000    lexer: &mut Lexer<'a, R>,
1001) -> Result<Option<StateFn<'a, R>>> {
1002    let (value, _) = read_value(lexer.reader)?;
1003    lexer.desc.connection_information = unmarshal_connection_information(&value)?;
1004    Ok(Some(StateFn { f: s5 }))
1005}
1006
1007fn unmarshal_connection_information(value: &str) -> Result<Option<ConnectionInformation>> {
1008    let fields: Vec<&str> = value.split_whitespace().collect();
1009    if fields.len() < 2 {
1010        return Err(Error::SdpInvalidSyntax(format!("`c={value}`")));
1011    }
1012
1013    // Set according to currently registered with IANA
1014    // https://tools.ietf.org/html/rfc4566#section-8.2.6
1015    let i = index_of(fields[0], &["IN"]);
1016    if i == -1 {
1017        return Err(Error::SdpInvalidValue(fields[0].to_owned()));
1018    }
1019
1020    // Set according to currently registered with IANA
1021    // https://tools.ietf.org/html/rfc4566#section-8.2.7
1022    let i = index_of(fields[1], &["IP4", "IP6"]);
1023    if i == -1 {
1024        return Err(Error::SdpInvalidValue(fields[1].to_owned()));
1025    }
1026
1027    let address = if fields.len() > 2 {
1028        Some(Address {
1029            address: fields[2].to_owned(),
1030            ttl: None,
1031            range: None,
1032        })
1033    } else {
1034        None
1035    };
1036
1037    Ok(Some(ConnectionInformation {
1038        network_type: fields[0].to_owned(),
1039        address_type: fields[1].to_owned(),
1040        address,
1041    }))
1042}
1043
1044fn unmarshal_session_bandwidth<'a, R: io::BufRead + io::Seek>(
1045    lexer: &mut Lexer<'a, R>,
1046) -> Result<Option<StateFn<'a, R>>> {
1047    let (value, _) = read_value(lexer.reader)?;
1048    lexer.desc.bandwidth.push(unmarshal_bandwidth(&value)?);
1049    Ok(Some(StateFn { f: s5 }))
1050}
1051
1052fn unmarshal_bandwidth(value: &str) -> Result<Bandwidth> {
1053    let mut parts: Vec<&str> = value.split(':').collect();
1054    if parts.len() != 2 {
1055        return Err(Error::SdpInvalidSyntax(format!("`b={value}`")));
1056    }
1057
1058    let experimental = parts[0].starts_with("X-");
1059    if experimental {
1060        parts[0] = parts[0].trim_start_matches("X-");
1061    }
1062    // RFC 8866 section 5.8: SDP parsers MUST ignore bandwidth-fields with unknown <bwtype> names.
1063    // Accept any bandwidth type instead of validating against a specific list.
1064
1065    let bandwidth = parts[1].parse::<u64>()?;
1066
1067    Ok(Bandwidth {
1068        experimental,
1069        bandwidth_type: parts[0].to_owned(),
1070        bandwidth,
1071    })
1072}
1073
1074fn unmarshal_timing<'a, R: io::BufRead + io::Seek>(
1075    lexer: &mut Lexer<'a, R>,
1076) -> Result<Option<StateFn<'a, R>>> {
1077    let (value, _) = read_value(lexer.reader)?;
1078
1079    let fields: Vec<&str> = value.split_whitespace().collect();
1080    if fields.len() < 2 {
1081        return Err(Error::SdpInvalidSyntax(format!("`t={value}`")));
1082    }
1083
1084    let start_time = fields[0].parse::<u64>()?;
1085    let stop_time = fields[1].parse::<u64>()?;
1086
1087    lexer.desc.time_descriptions.push(TimeDescription {
1088        timing: Timing {
1089            start_time,
1090            stop_time,
1091        },
1092        repeat_times: vec![],
1093    });
1094
1095    Ok(Some(StateFn { f: s9 }))
1096}
1097
1098fn unmarshal_repeat_times<'a, R: io::BufRead + io::Seek>(
1099    lexer: &mut Lexer<'a, R>,
1100) -> Result<Option<StateFn<'a, R>>> {
1101    let (value, _) = read_value(lexer.reader)?;
1102
1103    let fields: Vec<&str> = value.split_whitespace().collect();
1104    if fields.len() < 3 {
1105        return Err(Error::SdpInvalidSyntax(format!("`r={value}`")));
1106    }
1107
1108    if let Some(latest_time_desc) = lexer.desc.time_descriptions.last_mut() {
1109        let interval = parse_time_units(fields[0])?;
1110        let duration = parse_time_units(fields[1])?;
1111        let mut offsets = vec![];
1112        for field in fields.iter().skip(2) {
1113            let offset = parse_time_units(field)?;
1114            offsets.push(offset);
1115        }
1116        latest_time_desc.repeat_times.push(RepeatTime {
1117            interval,
1118            duration,
1119            offsets,
1120        });
1121
1122        Ok(Some(StateFn { f: s9 }))
1123    } else {
1124        Err(Error::SdpEmptyTimeDescription)
1125    }
1126}
1127
1128fn unmarshal_time_zones<'a, R: io::BufRead + io::Seek>(
1129    lexer: &mut Lexer<'a, R>,
1130) -> Result<Option<StateFn<'a, R>>> {
1131    let (value, _) = read_value(lexer.reader)?;
1132
1133    // These fields are transimitted in pairs
1134    // z=<adjustment time> <offset> <adjustment time> <offset> ....
1135    // so we are making sure that there are actually multiple of 2 total.
1136    let fields: Vec<&str> = value.split_whitespace().collect();
1137    if !fields.len().is_multiple_of(2) {
1138        return Err(Error::SdpInvalidSyntax(format!("`t={value}`")));
1139    }
1140
1141    for i in (0..fields.len()).step_by(2) {
1142        let adjustment_time = fields[i].parse::<u64>()?;
1143        let offset = parse_time_units(fields[i + 1])?;
1144
1145        lexer.desc.time_zones.push(TimeZone {
1146            adjustment_time,
1147            offset,
1148        });
1149    }
1150
1151    Ok(Some(StateFn { f: s13 }))
1152}
1153
1154fn unmarshal_session_encryption_key<'a, R: io::BufRead + io::Seek>(
1155    lexer: &mut Lexer<'a, R>,
1156) -> Result<Option<StateFn<'a, R>>> {
1157    let (value, _) = read_value(lexer.reader)?;
1158    lexer.desc.encryption_key = Some(value);
1159    Ok(Some(StateFn { f: s11 }))
1160}
1161
1162fn unmarshal_session_attribute<'a, R: io::BufRead + io::Seek>(
1163    lexer: &mut Lexer<'a, R>,
1164) -> Result<Option<StateFn<'a, R>>> {
1165    let (value, _) = read_value(lexer.reader)?;
1166
1167    let fields: Vec<&str> = value.splitn(2, ':').collect();
1168    let attribute = if fields.len() == 2 {
1169        Attribute {
1170            key: fields[0].to_owned(),
1171            value: Some(fields[1].to_owned()),
1172        }
1173    } else {
1174        Attribute {
1175            key: fields[0].to_owned(),
1176            value: None,
1177        }
1178    };
1179    lexer.desc.attributes.push(attribute);
1180
1181    Ok(Some(StateFn { f: s11 }))
1182}
1183
1184fn unmarshal_media_description<'a, R: io::BufRead + io::Seek>(
1185    lexer: &mut Lexer<'a, R>,
1186) -> Result<Option<StateFn<'a, R>>> {
1187    let (value, _) = read_value(lexer.reader)?;
1188
1189    let fields: Vec<&str> = value.split_whitespace().collect();
1190    if fields.len() < 4 {
1191        return Err(Error::SdpInvalidSyntax(format!("`m={value}`")));
1192    }
1193
1194    // <media>
1195    // Set according to currently registered with IANA
1196    // https://tools.ietf.org/html/rfc4566#section-5.14
1197    // including "image", registered here:
1198    // https://datatracker.ietf.org/doc/html/rfc6466
1199    let i = index_of(
1200        fields[0],
1201        &["audio", "video", "text", "application", "message", "image"],
1202    );
1203    if i == -1 {
1204        return Err(Error::SdpInvalidValue(fields[0].to_owned()));
1205    }
1206
1207    // <port>
1208    let parts: Vec<&str> = fields[1].split('/').collect();
1209    let port_value = parts[0].parse::<u16>()? as isize;
1210    let port_range = if parts.len() > 1 {
1211        Some(parts[1].parse::<i32>()? as isize)
1212    } else {
1213        None
1214    };
1215
1216    // <proto>
1217    // RFC 8866 section 5.14: <proto> is the media transport protocol and is
1218    // extensible -- "New transport protocols MAY be defined, and MUST be
1219    // registered with IANA." Accept any "/"-separated proto token instead of
1220    // validating against a fixed list, so IANA-registered transports beyond the
1221    // RTP/SAVPF set interoperate, e.g. UDP/BFCP (RFC 8856), TCP/DTLS/SCTP
1222    // (RFC 8841) or TCP/MRCPv2. A genuinely malformed proto (an empty
1223    // "/"-separated token) is still rejected as SdpInvalidValue.
1224    let mut protos = vec![];
1225    for proto in fields[2].split('/').collect::<Vec<&str>>() {
1226        if proto.is_empty() {
1227            return Err(Error::SdpInvalidValue(fields[2].to_owned()));
1228        }
1229        protos.push(proto.to_owned());
1230    }
1231
1232    // <fmt>...
1233    let mut formats = vec![];
1234    for field in fields.iter().skip(3) {
1235        formats.push(field.to_string());
1236    }
1237
1238    lexer.desc.media_descriptions.push(MediaDescription {
1239        media_name: MediaName {
1240            media: fields[0].to_owned(),
1241            port: RangedPort {
1242                value: port_value,
1243                range: port_range,
1244            },
1245            protos,
1246            formats,
1247        },
1248        media_title: None,
1249        connection_information: None,
1250        bandwidth: vec![],
1251        encryption_key: None,
1252        attributes: vec![],
1253    });
1254
1255    Ok(Some(StateFn { f: s12 }))
1256}
1257
1258fn unmarshal_media_title<'a, R: io::BufRead + io::Seek>(
1259    lexer: &mut Lexer<'a, R>,
1260) -> Result<Option<StateFn<'a, R>>> {
1261    let (value, _) = read_value(lexer.reader)?;
1262
1263    if let Some(latest_media_desc) = lexer.desc.media_descriptions.last_mut() {
1264        latest_media_desc.media_title = Some(value);
1265        Ok(Some(StateFn { f: s16 }))
1266    } else {
1267        Err(Error::SdpEmptyTimeDescription)
1268    }
1269}
1270
1271fn unmarshal_media_connection_information<'a, R: io::BufRead + io::Seek>(
1272    lexer: &mut Lexer<'a, R>,
1273) -> Result<Option<StateFn<'a, R>>> {
1274    let (value, _) = read_value(lexer.reader)?;
1275
1276    if let Some(latest_media_desc) = lexer.desc.media_descriptions.last_mut() {
1277        latest_media_desc.connection_information = unmarshal_connection_information(&value)?;
1278        Ok(Some(StateFn { f: s15 }))
1279    } else {
1280        Err(Error::SdpEmptyTimeDescription)
1281    }
1282}
1283
1284fn unmarshal_media_bandwidth<'a, R: io::BufRead + io::Seek>(
1285    lexer: &mut Lexer<'a, R>,
1286) -> Result<Option<StateFn<'a, R>>> {
1287    let (value, _) = read_value(lexer.reader)?;
1288
1289    if let Some(latest_media_desc) = lexer.desc.media_descriptions.last_mut() {
1290        let bandwidth = unmarshal_bandwidth(&value)?;
1291        latest_media_desc.bandwidth.push(bandwidth);
1292        Ok(Some(StateFn { f: s15 }))
1293    } else {
1294        Err(Error::SdpEmptyTimeDescription)
1295    }
1296}
1297
1298fn unmarshal_media_encryption_key<'a, R: io::BufRead + io::Seek>(
1299    lexer: &mut Lexer<'a, R>,
1300) -> Result<Option<StateFn<'a, R>>> {
1301    let (value, _) = read_value(lexer.reader)?;
1302
1303    if let Some(latest_media_desc) = lexer.desc.media_descriptions.last_mut() {
1304        latest_media_desc.encryption_key = Some(value);
1305        Ok(Some(StateFn { f: s14 }))
1306    } else {
1307        Err(Error::SdpEmptyTimeDescription)
1308    }
1309}
1310
1311fn unmarshal_media_attribute<'a, R: io::BufRead + io::Seek>(
1312    lexer: &mut Lexer<'a, R>,
1313) -> Result<Option<StateFn<'a, R>>> {
1314    let (value, _) = read_value(lexer.reader)?;
1315
1316    let fields: Vec<&str> = value.splitn(2, ':').collect();
1317    let attribute = if fields.len() == 2 {
1318        Attribute {
1319            key: fields[0].to_owned(),
1320            value: Some(fields[1].to_owned()),
1321        }
1322    } else {
1323        Attribute {
1324            key: fields[0].to_owned(),
1325            value: None,
1326        }
1327    };
1328
1329    if let Some(latest_media_desc) = lexer.desc.media_descriptions.last_mut() {
1330        latest_media_desc.attributes.push(attribute);
1331        Ok(Some(StateFn { f: s14 }))
1332    } else {
1333        Err(Error::SdpEmptyTimeDescription)
1334    }
1335}
1336
1337fn parse_time_units(value: &str) -> Result<i64> {
1338    // Some time offsets in the protocol can be provided with a shorthand
1339    // notation. This code ensures to convert it to NTP timestamp format.
1340    let val = value.as_bytes();
1341    let len = val.len();
1342    let (num, factor) = match val.last() {
1343        Some(b'd') => (&value[..len - 1], 86400), // days
1344        Some(b'h') => (&value[..len - 1], 3600),  // hours
1345        Some(b'm') => (&value[..len - 1], 60),    // minutes
1346        Some(b's') => (&value[..len - 1], 1),     // seconds (allowed for completeness)
1347        _ => (value, 1),
1348    };
1349    num.parse::<i64>()?
1350        .checked_mul(factor)
1351        .ok_or_else(|| Error::SdpInvalidValue(value.to_owned()))
1352}