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