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            for a in &m.attributes {
371                let attr = a.to_string();
372                if attr.starts_with("rtpmap:") {
373                    if let Ok(codec) = parse_rtpmap(&attr) {
374                        merge_codecs(codec, &mut codecs);
375                    }
376                } else if attr.starts_with("fmtp:") {
377                    if let Ok(codec) = parse_fmtp(&attr) {
378                        merge_codecs(codec, &mut codecs);
379                    }
380                } else if attr.starts_with("rtcp-fb:")
381                    && let Ok(codec) = parse_rtcp_fb(&attr)
382                {
383                    merge_codecs(codec, &mut codecs);
384                }
385            }
386        }
387
388        codecs
389    }
390
391    /// get_codec_for_payload_type scans the SessionDescription for the given payload type and returns the codec
392    pub fn get_codec_for_payload_type(&self, payload_type: u8) -> Result<Codec> {
393        let codecs = self.build_codec_map();
394
395        if let Some(codec) = codecs.get(&payload_type) {
396            Ok(codec.clone())
397        } else {
398            Err(Error::PayloadTypeNotFound)
399        }
400    }
401
402    /// get_payload_type_for_codec scans the SessionDescription for a codec that matches the provided codec
403    /// as closely as possible and returns its payload type
404    pub fn get_payload_type_for_codec(&self, wanted: &Codec) -> Result<u8> {
405        let codecs = self.build_codec_map();
406
407        for (payload_type, codec) in codecs.iter() {
408            if codecs_match(wanted, codec) {
409                return Ok(*payload_type);
410            }
411        }
412
413        Err(Error::CodecNotFound)
414    }
415
416    /// Returns whether an attribute exists
417    pub fn has_attribute(&self, key: &str) -> bool {
418        self.attributes.iter().any(|a| a.key == key)
419    }
420
421    /// Attribute returns the value of an attribute and if it exists
422    pub fn attribute(&self, key: &str) -> Option<&String> {
423        for a in &self.attributes {
424            if a.key == key {
425                return a.value.as_ref();
426            }
427        }
428        None
429    }
430
431    /// Marshal takes a SDP struct to text
432    ///
433    /// <https://tools.ietf.org/html/rfc4566#section-5>
434    ///
435    /// Session description
436    ///    v=  (protocol version)
437    ///    o=  (originator and session identifier)
438    ///    s=  (session name)
439    ///    i=* (session information)
440    ///    u=* (URI of description)
441    ///    e=* (email address)
442    ///    p=* (phone number)
443    ///    c=* (connection information -- not required if included in
444    ///         all media)
445    ///    b=* (zero or more bandwidth information lines)
446    ///    One or more time descriptions ("t=" and "r=" lines; see below)
447    ///    z=* (time zone adjustments)
448    ///    k=* (encryption key)
449    ///    a=* (zero or more session attribute lines)
450    ///    Zero or more media descriptions
451    ///
452    /// Time description
453    ///    t=  (time the session is active)
454    ///    r=* (zero or more repeat times)
455    ///
456    /// Media description, if present
457    ///    m=  (media name and transport address)
458    ///    i=* (media title)
459    ///    c=* (connection information -- optional if included at
460    ///         session level)
461    ///    b=* (zero or more bandwidth information lines)
462    ///    k=* (encryption key)
463    ///    a=* (zero or more media attribute lines)
464    pub fn marshal(&self) -> String {
465        self.to_string()
466    }
467
468    /// Unmarshal is the primary function that deserializes the session description
469    /// message and stores it inside of a structured SessionDescription object.
470    ///
471    /// The States Transition Table describes the computation flow between functions
472    /// (namely s1, s2, s3, ...) for a parsing procedure that complies with the
473    /// specifications laid out by the rfc4566#section-5 as well as by JavaScript
474    /// Session Establishment Protocol draft. Links:
475    ///     <https://tools.ietf.org/html/rfc4566#section-5>
476    ///     <https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-24>
477    ///
478    /// <https://tools.ietf.org/html/rfc4566#section-5>
479    ///
480    /// Session description
481    ///    v=  (protocol version)
482    ///    o=  (originator and session identifier)
483    ///    s=  (session name)
484    ///    i=* (session information)
485    ///    u=* (URI of description)
486    ///    e=* (email address)
487    ///    p=* (phone number)
488    ///    c=* (connection information -- not required if included in
489    ///         all media)
490    ///    b=* (zero or more bandwidth information lines)
491    ///    One or more time descriptions ("t=" and "r=" lines; see below)
492    ///    z=* (time zone adjustments)
493    ///    k=* (encryption key)
494    ///    a=* (zero or more session attribute lines)
495    ///    Zero or more media descriptions
496    ///
497    /// Time description
498    ///    t=  (time the session is active)
499    ///    r=* (zero or more repeat times)
500    ///
501    /// Media description, if present
502    ///    m=  (media name and transport address)
503    ///    i=* (media title)
504    ///    c=* (connection information -- optional if included at
505    ///         session level)
506    ///    b=* (zero or more bandwidth information lines)
507    ///    k=* (encryption key)
508    ///    a=* (zero or more media attribute lines)
509    ///
510    /// In order to generate the following state table and draw subsequent
511    /// deterministic finite-state automota ("DFA") the following regex was used to
512    /// derive the DFA:
513    ///    vosi?u?e?p?c?b*(tr*)+z?k?a*(mi?c?b*k?a*)*
514    /// possible place and state to exit:
515    ///                    **   * * *  ** * * * *
516    ///                    99   1 1 1  11 1 1 1 1
517    ///                         3 1 1  26 5 5 4 4
518    ///
519    /// Please pay close attention to the `k`, and `a` parsing states. In the table
520    /// below in order to distinguish between the states belonging to the media
521    /// description as opposed to the session description, the states are marked
522    /// with an asterisk ("a*", "k*").
523    ///
524    /// ```ignore
525    /// +--------+----+-------+----+-----+----+-----+---+----+----+---+---+-----+---+---+----+---+----+
526    /// | STATES | a* | a*,k* | a  | a,k | b  | b,c | e | i  | m  | o | p | r,t | s | t | u  | v | z  |
527    /// +--------+----+-------+----+-----+----+-----+---+----+----+---+---+-----+---+---+----+---+----+
528    /// |   s1   |    |       |    |     |    |     |   |    |    |   |   |     |   |   |    | 2 |    |
529    /// |   s2   |    |       |    |     |    |     |   |    |    | 3 |   |     |   |   |    |   |    |
530    /// |   s3   |    |       |    |     |    |     |   |    |    |   |   |     | 4 |   |    |   |    |
531    /// |   s4   |    |       |    |     |    |   5 | 6 |  7 |    |   | 8 |     |   | 9 | 10 |   |    |
532    /// |   s5   |    |       |    |     |  5 |     |   |    |    |   |   |     |   | 9 |    |   |    |
533    /// |   s6   |    |       |    |     |    |   5 |   |    |    |   | 8 |     |   | 9 |    |   |    |
534    /// |   s7   |    |       |    |     |    |   5 | 6 |    |    |   | 8 |     |   | 9 | 10 |   |    |
535    /// |   s8   |    |       |    |     |    |   5 |   |    |    |   |   |     |   | 9 |    |   |    |
536    /// |   s9   |    |       |    |  11 |    |     |   |    | 12 |   |   |   9 |   |   |    |   | 13 |
537    /// |   s10  |    |       |    |     |    |   5 | 6 |    |    |   | 8 |     |   | 9 |    |   |    |
538    /// |   s11  |    |       | 11 |     |    |     |   |    | 12 |   |   |     |   |   |    |   |    |
539    /// |   s12  |    |    14 |    |     |    |  15 |   | 16 | 12 |   |   |     |   |   |    |   |    |
540    /// |   s13  |    |       |    |  11 |    |     |   |    | 12 |   |   |     |   |   |    |   |    |
541    /// |   s14  | 14 |       |    |     |    |     |   |    | 12 |   |   |     |   |   |    |   |    |
542    /// |   s15  |    |    14 |    |     | 15 |     |   |    | 12 |   |   |     |   |   |    |   |    |
543    /// |   s16  |    |    14 |    |     |    |  15 |   |    | 12 |   |   |     |   |   |    |   |    |
544    /// +--------+----+-------+----+-----+----+-----+---+----+----+---+---+-----+---+---+----+---+----+
545    /// ```
546    pub fn unmarshal<R: io::BufRead + io::Seek>(reader: &mut R) -> Result<Self> {
547        let mut lexer = Lexer {
548            desc: SessionDescription {
549                version: 0,
550                origin: Origin::new(),
551                session_name: "".to_owned(),
552                session_information: None,
553                uri: None,
554                email_address: None,
555                phone_number: None,
556                connection_information: None,
557                bandwidth: vec![],
558                time_descriptions: vec![],
559                time_zones: vec![],
560                encryption_key: None,
561                attributes: vec![],
562                media_descriptions: vec![],
563            },
564            reader,
565        };
566
567        let mut state = Some(StateFn { f: s1 });
568        while let Some(s) = state {
569            state = (s.f)(&mut lexer)?;
570        }
571
572        Ok(lexer.desc)
573    }
574}
575
576impl From<SessionDescription> for String {
577    fn from(sdp: SessionDescription) -> String {
578        sdp.marshal()
579    }
580}
581
582impl TryFrom<String> for SessionDescription {
583    type Error = Error;
584    fn try_from(sdp_string: String) -> Result<Self> {
585        let mut reader = io::Cursor::new(sdp_string.as_bytes());
586        let session_description = SessionDescription::unmarshal(&mut reader)?;
587        Ok(session_description)
588    }
589}
590
591fn s1<'a, R: io::BufRead + io::Seek>(lexer: &mut Lexer<'a, R>) -> Result<Option<StateFn<'a, R>>> {
592    let (key, _) = read_type(lexer.reader)?;
593    if &key == b"v=" {
594        return Ok(Some(StateFn {
595            f: unmarshal_protocol_version,
596        }));
597    }
598
599    Err(Error::SdpInvalidSyntax(String::from_utf8(key)?))
600}
601
602fn s2<'a, R: io::BufRead + io::Seek>(lexer: &mut Lexer<'a, R>) -> Result<Option<StateFn<'a, R>>> {
603    let (key, _) = read_type(lexer.reader)?;
604    if &key == b"o=" {
605        return Ok(Some(StateFn {
606            f: unmarshal_origin,
607        }));
608    }
609
610    Err(Error::SdpInvalidSyntax(String::from_utf8(key)?))
611}
612
613fn s3<'a, R: io::BufRead + io::Seek>(lexer: &mut Lexer<'a, R>) -> Result<Option<StateFn<'a, R>>> {
614    let (key, _) = read_type(lexer.reader)?;
615    if &key == b"s=" {
616        return Ok(Some(StateFn {
617            f: unmarshal_session_name,
618        }));
619    }
620
621    Err(Error::SdpInvalidSyntax(String::from_utf8(key)?))
622}
623
624fn s4<'a, R: io::BufRead + io::Seek>(lexer: &mut Lexer<'a, R>) -> Result<Option<StateFn<'a, R>>> {
625    let (key, _) = read_type(lexer.reader)?;
626    match key.as_slice() {
627        b"i=" => Ok(Some(StateFn {
628            f: unmarshal_session_information,
629        })),
630        b"u=" => Ok(Some(StateFn { f: unmarshal_uri })),
631        b"e=" => Ok(Some(StateFn { f: unmarshal_email })),
632        b"p=" => Ok(Some(StateFn { f: unmarshal_phone })),
633        b"c=" => Ok(Some(StateFn {
634            f: unmarshal_session_connection_information,
635        })),
636        b"b=" => Ok(Some(StateFn {
637            f: unmarshal_session_bandwidth,
638        })),
639        b"t=" => Ok(Some(StateFn {
640            f: unmarshal_timing,
641        })),
642        _ => Err(Error::SdpInvalidSyntax(String::from_utf8(key)?)),
643    }
644}
645
646fn s5<'a, R: io::BufRead + io::Seek>(lexer: &mut Lexer<'a, R>) -> Result<Option<StateFn<'a, R>>> {
647    let (key, _) = read_type(lexer.reader)?;
648    match key.as_slice() {
649        b"b=" => Ok(Some(StateFn {
650            f: unmarshal_session_bandwidth,
651        })),
652        b"t=" => Ok(Some(StateFn {
653            f: unmarshal_timing,
654        })),
655        _ => Err(Error::SdpInvalidSyntax(String::from_utf8(key)?)),
656    }
657}
658
659fn s6<'a, R: io::BufRead + io::Seek>(lexer: &mut Lexer<'a, R>) -> Result<Option<StateFn<'a, R>>> {
660    let (key, _) = read_type(lexer.reader)?;
661    match key.as_slice() {
662        b"p=" => Ok(Some(StateFn { f: unmarshal_phone })),
663        b"c=" => Ok(Some(StateFn {
664            f: unmarshal_session_connection_information,
665        })),
666        b"b=" => Ok(Some(StateFn {
667            f: unmarshal_session_bandwidth,
668        })),
669        b"t=" => Ok(Some(StateFn {
670            f: unmarshal_timing,
671        })),
672        _ => Err(Error::SdpInvalidSyntax(String::from_utf8(key)?)),
673    }
674}
675
676fn s7<'a, R: io::BufRead + io::Seek>(lexer: &mut Lexer<'a, R>) -> Result<Option<StateFn<'a, R>>> {
677    let (key, _) = read_type(lexer.reader)?;
678    match key.as_slice() {
679        b"u=" => Ok(Some(StateFn { f: unmarshal_uri })),
680        b"e=" => Ok(Some(StateFn { f: unmarshal_email })),
681        b"p=" => Ok(Some(StateFn { f: unmarshal_phone })),
682        b"c=" => Ok(Some(StateFn {
683            f: unmarshal_session_connection_information,
684        })),
685        b"b=" => Ok(Some(StateFn {
686            f: unmarshal_session_bandwidth,
687        })),
688        b"t=" => Ok(Some(StateFn {
689            f: unmarshal_timing,
690        })),
691        _ => Err(Error::SdpInvalidSyntax(String::from_utf8(key)?)),
692    }
693}
694
695fn s8<'a, R: io::BufRead + io::Seek>(lexer: &mut Lexer<'a, R>) -> Result<Option<StateFn<'a, R>>> {
696    let (key, _) = read_type(lexer.reader)?;
697    match key.as_slice() {
698        b"c=" => Ok(Some(StateFn {
699            f: unmarshal_session_connection_information,
700        })),
701        b"b=" => Ok(Some(StateFn {
702            f: unmarshal_session_bandwidth,
703        })),
704        b"t=" => Ok(Some(StateFn {
705            f: unmarshal_timing,
706        })),
707        _ => Err(Error::SdpInvalidSyntax(String::from_utf8(key)?)),
708    }
709}
710
711fn s9<'a, R: io::BufRead + io::Seek>(lexer: &mut Lexer<'a, R>) -> Result<Option<StateFn<'a, R>>> {
712    let (key, num_bytes) = read_type(lexer.reader)?;
713    if key.is_empty() && num_bytes == 0 {
714        return Ok(None);
715    }
716
717    match key.as_slice() {
718        b"z=" => Ok(Some(StateFn {
719            f: unmarshal_time_zones,
720        })),
721        b"k=" => Ok(Some(StateFn {
722            f: unmarshal_session_encryption_key,
723        })),
724        b"a=" => Ok(Some(StateFn {
725            f: unmarshal_session_attribute,
726        })),
727        b"r=" => Ok(Some(StateFn {
728            f: unmarshal_repeat_times,
729        })),
730        b"t=" => Ok(Some(StateFn {
731            f: unmarshal_timing,
732        })),
733        b"m=" => Ok(Some(StateFn {
734            f: unmarshal_media_description,
735        })),
736        _ => Err(Error::SdpInvalidSyntax(String::from_utf8(key)?)),
737    }
738}
739
740fn s10<'a, R: io::BufRead + io::Seek>(lexer: &mut Lexer<'a, R>) -> Result<Option<StateFn<'a, R>>> {
741    let (key, _) = read_type(lexer.reader)?;
742    match key.as_slice() {
743        b"e=" => Ok(Some(StateFn { f: unmarshal_email })),
744        b"p=" => Ok(Some(StateFn { f: unmarshal_phone })),
745        b"c=" => Ok(Some(StateFn {
746            f: unmarshal_session_connection_information,
747        })),
748        b"b=" => Ok(Some(StateFn {
749            f: unmarshal_session_bandwidth,
750        })),
751        b"t=" => Ok(Some(StateFn {
752            f: unmarshal_timing,
753        })),
754        _ => Err(Error::SdpInvalidSyntax(String::from_utf8(key)?)),
755    }
756}
757
758fn s11<'a, R: io::BufRead + io::Seek>(lexer: &mut Lexer<'a, R>) -> Result<Option<StateFn<'a, R>>> {
759    let (key, num_bytes) = read_type(lexer.reader)?;
760    if key.is_empty() && num_bytes == 0 {
761        return Ok(None);
762    }
763
764    match key.as_slice() {
765        b"a=" => Ok(Some(StateFn {
766            f: unmarshal_session_attribute,
767        })),
768        b"m=" => Ok(Some(StateFn {
769            f: unmarshal_media_description,
770        })),
771        _ => Err(Error::SdpInvalidSyntax(String::from_utf8(key)?)),
772    }
773}
774
775fn s12<'a, R: io::BufRead + io::Seek>(lexer: &mut Lexer<'a, R>) -> Result<Option<StateFn<'a, R>>> {
776    let (key, num_bytes) = read_type(lexer.reader)?;
777    if key.is_empty() && num_bytes == 0 {
778        return Ok(None);
779    }
780
781    match key.as_slice() {
782        b"a=" => Ok(Some(StateFn {
783            f: unmarshal_media_attribute,
784        })),
785        b"k=" => Ok(Some(StateFn {
786            f: unmarshal_media_encryption_key,
787        })),
788        b"b=" => Ok(Some(StateFn {
789            f: unmarshal_media_bandwidth,
790        })),
791        b"c=" => Ok(Some(StateFn {
792            f: unmarshal_media_connection_information,
793        })),
794        b"i=" => Ok(Some(StateFn {
795            f: unmarshal_media_title,
796        })),
797        b"m=" => Ok(Some(StateFn {
798            f: unmarshal_media_description,
799        })),
800        _ => Err(Error::SdpInvalidSyntax(String::from_utf8(key)?)),
801    }
802}
803
804fn s13<'a, R: io::BufRead + io::Seek>(lexer: &mut Lexer<'a, R>) -> Result<Option<StateFn<'a, R>>> {
805    let (key, num_bytes) = read_type(lexer.reader)?;
806    if key.is_empty() && num_bytes == 0 {
807        return Ok(None);
808    }
809
810    match key.as_slice() {
811        b"a=" => Ok(Some(StateFn {
812            f: unmarshal_session_attribute,
813        })),
814        b"k=" => Ok(Some(StateFn {
815            f: unmarshal_session_encryption_key,
816        })),
817        b"m=" => Ok(Some(StateFn {
818            f: unmarshal_media_description,
819        })),
820        _ => Err(Error::SdpInvalidSyntax(String::from_utf8(key)?)),
821    }
822}
823
824fn s14<'a, R: io::BufRead + io::Seek>(lexer: &mut Lexer<'a, R>) -> Result<Option<StateFn<'a, R>>> {
825    let (key, num_bytes) = read_type(lexer.reader)?;
826    if key.is_empty() && num_bytes == 0 {
827        return Ok(None);
828    }
829
830    match key.as_slice() {
831        b"a=" => Ok(Some(StateFn {
832            f: unmarshal_media_attribute,
833        })),
834        // Non-spec ordering
835        b"k=" => Ok(Some(StateFn {
836            f: unmarshal_media_encryption_key,
837        })),
838        // Non-spec ordering
839        b"b=" => Ok(Some(StateFn {
840            f: unmarshal_media_bandwidth,
841        })),
842        // Non-spec ordering
843        b"c=" => Ok(Some(StateFn {
844            f: unmarshal_media_connection_information,
845        })),
846        // Non-spec ordering
847        b"i=" => Ok(Some(StateFn {
848            f: unmarshal_media_title,
849        })),
850        b"m=" => Ok(Some(StateFn {
851            f: unmarshal_media_description,
852        })),
853        _ => Err(Error::SdpInvalidSyntax(String::from_utf8(key)?)),
854    }
855}
856
857fn s15<'a, R: io::BufRead + io::Seek>(lexer: &mut Lexer<'a, R>) -> Result<Option<StateFn<'a, R>>> {
858    let (key, num_bytes) = read_type(lexer.reader)?;
859    if key.is_empty() && num_bytes == 0 {
860        return Ok(None);
861    }
862
863    match key.as_slice() {
864        b"a=" => Ok(Some(StateFn {
865            f: unmarshal_media_attribute,
866        })),
867        b"k=" => Ok(Some(StateFn {
868            f: unmarshal_media_encryption_key,
869        })),
870        b"b=" => Ok(Some(StateFn {
871            f: unmarshal_media_bandwidth,
872        })),
873        b"c=" => Ok(Some(StateFn {
874            f: unmarshal_media_connection_information,
875        })),
876        // Non-spec ordering
877        b"i=" => Ok(Some(StateFn {
878            f: unmarshal_media_title,
879        })),
880        b"m=" => Ok(Some(StateFn {
881            f: unmarshal_media_description,
882        })),
883        _ => Err(Error::SdpInvalidSyntax(String::from_utf8(key)?)),
884    }
885}
886
887fn s16<'a, R: io::BufRead + io::Seek>(lexer: &mut Lexer<'a, R>) -> Result<Option<StateFn<'a, R>>> {
888    let (key, num_bytes) = read_type(lexer.reader)?;
889    if key.is_empty() && num_bytes == 0 {
890        return Ok(None);
891    }
892
893    match key.as_slice() {
894        b"a=" => Ok(Some(StateFn {
895            f: unmarshal_media_attribute,
896        })),
897        b"k=" => Ok(Some(StateFn {
898            f: unmarshal_media_encryption_key,
899        })),
900        b"c=" => Ok(Some(StateFn {
901            f: unmarshal_media_connection_information,
902        })),
903        b"b=" => Ok(Some(StateFn {
904            f: unmarshal_media_bandwidth,
905        })),
906        // Non-spec ordering
907        b"i=" => Ok(Some(StateFn {
908            f: unmarshal_media_title,
909        })),
910        b"m=" => Ok(Some(StateFn {
911            f: unmarshal_media_description,
912        })),
913        _ => Err(Error::SdpInvalidSyntax(String::from_utf8(key)?)),
914    }
915}
916
917fn unmarshal_protocol_version<'a, R: io::BufRead + io::Seek>(
918    lexer: &mut Lexer<'a, R>,
919) -> Result<Option<StateFn<'a, R>>> {
920    let (value, _) = read_value(lexer.reader)?;
921
922    let version = value.parse::<u32>()?;
923
924    // As off the latest draft of the rfc this value is required to be 0.
925    // https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-24#section-5.8.1
926    if version != 0 {
927        return Err(Error::SdpInvalidSyntax(value));
928    }
929
930    Ok(Some(StateFn { f: s2 }))
931}
932
933fn unmarshal_origin<'a, R: io::BufRead + io::Seek>(
934    lexer: &mut Lexer<'a, R>,
935) -> Result<Option<StateFn<'a, R>>> {
936    let (value, _) = read_value(lexer.reader)?;
937
938    let fields: Vec<&str> = value.split_whitespace().collect();
939    if fields.len() != 6 {
940        return Err(Error::SdpInvalidSyntax(format!("`o={value}`")));
941    }
942
943    let session_id = fields[1].parse::<u64>()?;
944    let session_version = fields[2].parse::<u64>()?;
945
946    // Set according to currently registered with IANA
947    // https://tools.ietf.org/html/rfc4566#section-8.2.6
948    let i = index_of(fields[3], &["IN"]);
949    if i == -1 {
950        return Err(Error::SdpInvalidValue(fields[3].to_owned()));
951    }
952
953    // Set according to currently registered with IANA
954    // https://tools.ietf.org/html/rfc4566#section-8.2.7
955    let i = index_of(fields[4], &["IP4", "IP6"]);
956    if i == -1 {
957        return Err(Error::SdpInvalidValue(fields[4].to_owned()));
958    }
959
960    // TODO validated UnicastAddress
961
962    lexer.desc.origin = Origin {
963        username: fields[0].to_owned(),
964        session_id,
965        session_version,
966        network_type: fields[3].to_owned(),
967        address_type: fields[4].to_owned(),
968        unicast_address: fields[5].to_owned(),
969    };
970
971    Ok(Some(StateFn { f: s3 }))
972}
973
974fn unmarshal_session_name<'a, R: io::BufRead + io::Seek>(
975    lexer: &mut Lexer<'a, R>,
976) -> Result<Option<StateFn<'a, R>>> {
977    let (value, _) = read_value(lexer.reader)?;
978    lexer.desc.session_name = value;
979    Ok(Some(StateFn { f: s4 }))
980}
981
982fn unmarshal_session_information<'a, R: io::BufRead + io::Seek>(
983    lexer: &mut Lexer<'a, R>,
984) -> Result<Option<StateFn<'a, R>>> {
985    let (value, _) = read_value(lexer.reader)?;
986    lexer.desc.session_information = Some(value);
987    Ok(Some(StateFn { f: s7 }))
988}
989
990fn unmarshal_uri<'a, R: io::BufRead + io::Seek>(
991    lexer: &mut Lexer<'a, R>,
992) -> Result<Option<StateFn<'a, R>>> {
993    let (value, _) = read_value(lexer.reader)?;
994    lexer.desc.uri = Some(Url::parse(&value)?);
995    Ok(Some(StateFn { f: s10 }))
996}
997
998fn unmarshal_email<'a, R: io::BufRead + io::Seek>(
999    lexer: &mut Lexer<'a, R>,
1000) -> Result<Option<StateFn<'a, R>>> {
1001    let (value, _) = read_value(lexer.reader)?;
1002    lexer.desc.email_address = Some(value);
1003    Ok(Some(StateFn { f: s6 }))
1004}
1005
1006fn unmarshal_phone<'a, R: io::BufRead + io::Seek>(
1007    lexer: &mut Lexer<'a, R>,
1008) -> Result<Option<StateFn<'a, R>>> {
1009    let (value, _) = read_value(lexer.reader)?;
1010    lexer.desc.phone_number = Some(value);
1011    Ok(Some(StateFn { f: s8 }))
1012}
1013
1014fn unmarshal_session_connection_information<'a, R: io::BufRead + io::Seek>(
1015    lexer: &mut Lexer<'a, R>,
1016) -> Result<Option<StateFn<'a, R>>> {
1017    let (value, _) = read_value(lexer.reader)?;
1018    lexer.desc.connection_information = unmarshal_connection_information(&value)?;
1019    Ok(Some(StateFn { f: s5 }))
1020}
1021
1022fn unmarshal_connection_information(value: &str) -> Result<Option<ConnectionInformation>> {
1023    let fields: Vec<&str> = value.split_whitespace().collect();
1024    if fields.len() < 2 {
1025        return Err(Error::SdpInvalidSyntax(format!("`c={value}`")));
1026    }
1027
1028    // Set according to currently registered with IANA
1029    // https://tools.ietf.org/html/rfc4566#section-8.2.6
1030    let i = index_of(fields[0], &["IN"]);
1031    if i == -1 {
1032        return Err(Error::SdpInvalidValue(fields[0].to_owned()));
1033    }
1034
1035    // Set according to currently registered with IANA
1036    // https://tools.ietf.org/html/rfc4566#section-8.2.7
1037    let i = index_of(fields[1], &["IP4", "IP6"]);
1038    if i == -1 {
1039        return Err(Error::SdpInvalidValue(fields[1].to_owned()));
1040    }
1041
1042    let address = if fields.len() > 2 {
1043        Some(Address {
1044            address: fields[2].to_owned(),
1045            ttl: None,
1046            range: None,
1047        })
1048    } else {
1049        None
1050    };
1051
1052    Ok(Some(ConnectionInformation {
1053        network_type: fields[0].to_owned(),
1054        address_type: fields[1].to_owned(),
1055        address,
1056    }))
1057}
1058
1059fn unmarshal_session_bandwidth<'a, R: io::BufRead + io::Seek>(
1060    lexer: &mut Lexer<'a, R>,
1061) -> Result<Option<StateFn<'a, R>>> {
1062    let (value, _) = read_value(lexer.reader)?;
1063    lexer.desc.bandwidth.push(unmarshal_bandwidth(&value)?);
1064    Ok(Some(StateFn { f: s5 }))
1065}
1066
1067fn unmarshal_bandwidth(value: &str) -> Result<Bandwidth> {
1068    let mut parts: Vec<&str> = value.split(':').collect();
1069    if parts.len() != 2 {
1070        return Err(Error::SdpInvalidSyntax(format!("`b={value}`")));
1071    }
1072
1073    let experimental = parts[0].starts_with("X-");
1074    if experimental {
1075        parts[0] = parts[0].trim_start_matches("X-");
1076    } else {
1077        // Set according to currently registered with IANA
1078        // https://tools.ietf.org/html/rfc4566#section-5.8 and
1079        // https://datatracker.ietf.org/doc/html/rfc3890
1080        let i = index_of(parts[0], &["CT", "AS", "TIAS"]);
1081        if i == -1 {
1082            return Err(Error::SdpInvalidValue(parts[0].to_owned()));
1083        }
1084    }
1085
1086    let bandwidth = parts[1].parse::<u64>()?;
1087
1088    Ok(Bandwidth {
1089        experimental,
1090        bandwidth_type: parts[0].to_owned(),
1091        bandwidth,
1092    })
1093}
1094
1095fn unmarshal_timing<'a, R: io::BufRead + io::Seek>(
1096    lexer: &mut Lexer<'a, R>,
1097) -> Result<Option<StateFn<'a, R>>> {
1098    let (value, _) = read_value(lexer.reader)?;
1099
1100    let fields: Vec<&str> = value.split_whitespace().collect();
1101    if fields.len() < 2 {
1102        return Err(Error::SdpInvalidSyntax(format!("`t={value}`")));
1103    }
1104
1105    let start_time = fields[0].parse::<u64>()?;
1106    let stop_time = fields[1].parse::<u64>()?;
1107
1108    lexer.desc.time_descriptions.push(TimeDescription {
1109        timing: Timing {
1110            start_time,
1111            stop_time,
1112        },
1113        repeat_times: vec![],
1114    });
1115
1116    Ok(Some(StateFn { f: s9 }))
1117}
1118
1119fn unmarshal_repeat_times<'a, R: io::BufRead + io::Seek>(
1120    lexer: &mut Lexer<'a, R>,
1121) -> Result<Option<StateFn<'a, R>>> {
1122    let (value, _) = read_value(lexer.reader)?;
1123
1124    let fields: Vec<&str> = value.split_whitespace().collect();
1125    if fields.len() < 3 {
1126        return Err(Error::SdpInvalidSyntax(format!("`r={value}`")));
1127    }
1128
1129    if let Some(latest_time_desc) = lexer.desc.time_descriptions.last_mut() {
1130        let interval = parse_time_units(fields[0])?;
1131        let duration = parse_time_units(fields[1])?;
1132        let mut offsets = vec![];
1133        for field in fields.iter().skip(2) {
1134            let offset = parse_time_units(field)?;
1135            offsets.push(offset);
1136        }
1137        latest_time_desc.repeat_times.push(RepeatTime {
1138            interval,
1139            duration,
1140            offsets,
1141        });
1142
1143        Ok(Some(StateFn { f: s9 }))
1144    } else {
1145        Err(Error::SdpEmptyTimeDescription)
1146    }
1147}
1148
1149fn unmarshal_time_zones<'a, R: io::BufRead + io::Seek>(
1150    lexer: &mut Lexer<'a, R>,
1151) -> Result<Option<StateFn<'a, R>>> {
1152    let (value, _) = read_value(lexer.reader)?;
1153
1154    // These fields are transimitted in pairs
1155    // z=<adjustment time> <offset> <adjustment time> <offset> ....
1156    // so we are making sure that there are actually multiple of 2 total.
1157    let fields: Vec<&str> = value.split_whitespace().collect();
1158    if !fields.len().is_multiple_of(2) {
1159        return Err(Error::SdpInvalidSyntax(format!("`t={value}`")));
1160    }
1161
1162    for i in (0..fields.len()).step_by(2) {
1163        let adjustment_time = fields[i].parse::<u64>()?;
1164        let offset = parse_time_units(fields[i + 1])?;
1165
1166        lexer.desc.time_zones.push(TimeZone {
1167            adjustment_time,
1168            offset,
1169        });
1170    }
1171
1172    Ok(Some(StateFn { f: s13 }))
1173}
1174
1175fn unmarshal_session_encryption_key<'a, R: io::BufRead + io::Seek>(
1176    lexer: &mut Lexer<'a, R>,
1177) -> Result<Option<StateFn<'a, R>>> {
1178    let (value, _) = read_value(lexer.reader)?;
1179    lexer.desc.encryption_key = Some(value);
1180    Ok(Some(StateFn { f: s11 }))
1181}
1182
1183fn unmarshal_session_attribute<'a, R: io::BufRead + io::Seek>(
1184    lexer: &mut Lexer<'a, R>,
1185) -> Result<Option<StateFn<'a, R>>> {
1186    let (value, _) = read_value(lexer.reader)?;
1187
1188    let fields: Vec<&str> = value.splitn(2, ':').collect();
1189    let attribute = if fields.len() == 2 {
1190        Attribute {
1191            key: fields[0].to_owned(),
1192            value: Some(fields[1].to_owned()),
1193        }
1194    } else {
1195        Attribute {
1196            key: fields[0].to_owned(),
1197            value: None,
1198        }
1199    };
1200    lexer.desc.attributes.push(attribute);
1201
1202    Ok(Some(StateFn { f: s11 }))
1203}
1204
1205fn unmarshal_media_description<'a, R: io::BufRead + io::Seek>(
1206    lexer: &mut Lexer<'a, R>,
1207) -> Result<Option<StateFn<'a, R>>> {
1208    let (value, _) = read_value(lexer.reader)?;
1209
1210    let fields: Vec<&str> = value.split_whitespace().collect();
1211    if fields.len() < 4 {
1212        return Err(Error::SdpInvalidSyntax(format!("`m={value}`")));
1213    }
1214
1215    // <media>
1216    // Set according to currently registered with IANA
1217    // https://tools.ietf.org/html/rfc4566#section-5.14
1218    // including "image", registered here:
1219    // https://datatracker.ietf.org/doc/html/rfc6466
1220    let i = index_of(
1221        fields[0],
1222        &["audio", "video", "text", "application", "message", "image"],
1223    );
1224    if i == -1 {
1225        return Err(Error::SdpInvalidValue(fields[0].to_owned()));
1226    }
1227
1228    // <port>
1229    let parts: Vec<&str> = fields[1].split('/').collect();
1230    let port_value = parts[0].parse::<u16>()? as isize;
1231    let port_range = if parts.len() > 1 {
1232        Some(parts[1].parse::<i32>()? as isize)
1233    } else {
1234        None
1235    };
1236
1237    // <proto>
1238    // Set according to currently registered with IANA
1239    // https://tools.ietf.org/html/rfc4566#section-5.14
1240    let mut protos = vec![];
1241    for proto in fields[2].split('/').collect::<Vec<&str>>() {
1242        let i = index_of(
1243            proto,
1244            &[
1245                "UDP", "RTP", "AVP", "SAVP", "SAVPF", "TLS", "DTLS", "SCTP", "AVPF", "udptl",
1246            ],
1247        );
1248        if i == -1 {
1249            return Err(Error::SdpInvalidValue(fields[2].to_owned()));
1250        }
1251        protos.push(proto.to_owned());
1252    }
1253
1254    // <fmt>...
1255    let mut formats = vec![];
1256    for field in fields.iter().skip(3) {
1257        formats.push(field.to_string());
1258    }
1259
1260    lexer.desc.media_descriptions.push(MediaDescription {
1261        media_name: MediaName {
1262            media: fields[0].to_owned(),
1263            port: RangedPort {
1264                value: port_value,
1265                range: port_range,
1266            },
1267            protos,
1268            formats,
1269        },
1270        media_title: None,
1271        connection_information: None,
1272        bandwidth: vec![],
1273        encryption_key: None,
1274        attributes: vec![],
1275    });
1276
1277    Ok(Some(StateFn { f: s12 }))
1278}
1279
1280fn unmarshal_media_title<'a, R: io::BufRead + io::Seek>(
1281    lexer: &mut Lexer<'a, R>,
1282) -> Result<Option<StateFn<'a, R>>> {
1283    let (value, _) = read_value(lexer.reader)?;
1284
1285    if let Some(latest_media_desc) = lexer.desc.media_descriptions.last_mut() {
1286        latest_media_desc.media_title = Some(value);
1287        Ok(Some(StateFn { f: s16 }))
1288    } else {
1289        Err(Error::SdpEmptyTimeDescription)
1290    }
1291}
1292
1293fn unmarshal_media_connection_information<'a, R: io::BufRead + io::Seek>(
1294    lexer: &mut Lexer<'a, R>,
1295) -> Result<Option<StateFn<'a, R>>> {
1296    let (value, _) = read_value(lexer.reader)?;
1297
1298    if let Some(latest_media_desc) = lexer.desc.media_descriptions.last_mut() {
1299        latest_media_desc.connection_information = unmarshal_connection_information(&value)?;
1300        Ok(Some(StateFn { f: s15 }))
1301    } else {
1302        Err(Error::SdpEmptyTimeDescription)
1303    }
1304}
1305
1306fn unmarshal_media_bandwidth<'a, R: io::BufRead + io::Seek>(
1307    lexer: &mut Lexer<'a, R>,
1308) -> Result<Option<StateFn<'a, R>>> {
1309    let (value, _) = read_value(lexer.reader)?;
1310
1311    if let Some(latest_media_desc) = lexer.desc.media_descriptions.last_mut() {
1312        let bandwidth = unmarshal_bandwidth(&value)?;
1313        latest_media_desc.bandwidth.push(bandwidth);
1314        Ok(Some(StateFn { f: s15 }))
1315    } else {
1316        Err(Error::SdpEmptyTimeDescription)
1317    }
1318}
1319
1320fn unmarshal_media_encryption_key<'a, R: io::BufRead + io::Seek>(
1321    lexer: &mut Lexer<'a, R>,
1322) -> Result<Option<StateFn<'a, R>>> {
1323    let (value, _) = read_value(lexer.reader)?;
1324
1325    if let Some(latest_media_desc) = lexer.desc.media_descriptions.last_mut() {
1326        latest_media_desc.encryption_key = Some(value);
1327        Ok(Some(StateFn { f: s14 }))
1328    } else {
1329        Err(Error::SdpEmptyTimeDescription)
1330    }
1331}
1332
1333fn unmarshal_media_attribute<'a, R: io::BufRead + io::Seek>(
1334    lexer: &mut Lexer<'a, R>,
1335) -> Result<Option<StateFn<'a, R>>> {
1336    let (value, _) = read_value(lexer.reader)?;
1337
1338    let fields: Vec<&str> = value.splitn(2, ':').collect();
1339    let attribute = if fields.len() == 2 {
1340        Attribute {
1341            key: fields[0].to_owned(),
1342            value: Some(fields[1].to_owned()),
1343        }
1344    } else {
1345        Attribute {
1346            key: fields[0].to_owned(),
1347            value: None,
1348        }
1349    };
1350
1351    if let Some(latest_media_desc) = lexer.desc.media_descriptions.last_mut() {
1352        latest_media_desc.attributes.push(attribute);
1353        Ok(Some(StateFn { f: s14 }))
1354    } else {
1355        Err(Error::SdpEmptyTimeDescription)
1356    }
1357}
1358
1359fn parse_time_units(value: &str) -> Result<i64> {
1360    // Some time offsets in the protocol can be provided with a shorthand
1361    // notation. This code ensures to convert it to NTP timestamp format.
1362    let val = value.as_bytes();
1363    let len = val.len();
1364    let (num, factor) = match val.last() {
1365        Some(b'd') => (&value[..len - 1], 86400), // days
1366        Some(b'h') => (&value[..len - 1], 3600),  // hours
1367        Some(b'm') => (&value[..len - 1], 60),    // minutes
1368        Some(b's') => (&value[..len - 1], 1),     // seconds (allowed for completeness)
1369        _ => (value, 1),
1370    };
1371    num.parse::<i64>()?
1372        .checked_mul(factor)
1373        .ok_or_else(|| Error::SdpInvalidValue(value.to_owned()))
1374}