public static MediaDescription Parse(string line)
        {
            var match = REGEX.Match(line);

            if (!match.Success)
            {
                throw new SdpParseException($"Unable to parse malformed Media Description '{line}'");
            }

            uint port;

            if (!uint.TryParse(match.Groups[2].Value.Trim(), out port))
            {
                throw new SdpParseException($"Unable to parse Media Description port '{match.Groups[2].Value}'");
            }

            var md = new MediaDescription()
            {
                Media    = (MediaType)Enum.Parse(typeof(MediaType), match.Groups[1].Value.Trim().ToUpper()),
                Port     = port,
                Protocol = ToTransportProtocol(match.Groups[3].Value.Trim())
            };

            ParseMediaFormats(match.Groups[4].Value, md);

            return(md);
        }
        private static void ParseMediaFormats(string str, MediaDescription md)
        {
            str = str.Trim();

            Regex.Split(str, @"\s+").Where(s => s != string.Empty).ToList().ForEach(fmt =>
            {
                uint format;
                if (!uint.TryParse(fmt.Trim(), out format))
                {
                    throw new SdpParseException($"Unable to parse Media Description fmt '{fmt}'");
                }

                md.MediaFormats.Add(format);
            });
        }
            public MediaDescription Build()
            {
                var md = new MediaDescription()
                {
                    Media      = _type,
                    Port       = _port,
                    Protocol   = _proto,
                    MediaTitle = _title,
                    Connection = _connection,
                };

                md.MediaFormats.AddRange(_fmts);
                md.Bandwidths.AddRange(_bandwidths);
                md.Attributes.AddRange(_attributes);

                return(md);
            }
        public static SessionDescription Parse(string data)
        {
            TimeDescription  currentTd = null;
            MediaDescription currentMd = null;

            try
            {
                SessionDescription sdp = new SessionDescription();

                var lines = Regex.Split(data, CRLF);

                foreach (var line in lines)
                {
                    var trimmed = line.Trim();

                    if (trimmed.StartsWith("v="))
                    {
                        var value = ParseKeyValueLine(trimmed, "Session Version");

                        decimal version;
                        if (!decimal.TryParse(value, out version))
                        {
                            // TODO(frank.lamar): warn.
                        }
                        else
                        {
                            sdp.Version = version;
                        }
                    }
                    else if (Regex.IsMatch(trimmed, @"^o\s*=\s*."))
                    {
                        sdp.Origin = SessionOriginator.Parse(trimmed);
                    }
                    else if (Regex.IsMatch(trimmed, @"^s\s*=\s*."))
                    {
                        sdp.SessionName = ParseKeyValueLine(trimmed, "Session Name");
                    }
                    else if (Regex.IsMatch(trimmed, @"^i\s*=\s*."))
                    {
                        var info = ParseKeyValueLine(trimmed, "Session Information");

                        if (currentMd != null)
                        {
                            // Media Title at the media level
                            currentMd.MediaTitle = info;
                        }
                        else
                        {
                            // Session information at the Session level.
                            sdp.SessionInformation = info;
                        }
                    }
                    else if (Regex.IsMatch(trimmed, @"^u\s*=\s*."))
                    {
                        var value = ParseKeyValueLine(trimmed, "Session URI");

                        Uri uri;
                        if (!Uri.TryCreate(value, UriKind.RelativeOrAbsolute, out uri))
                        {
                            throw new SdpParseException($"Unable to parse malformed Session URI {value}");
                        }

                        sdp.URI = uri;
                    }
                    else if (Regex.IsMatch(trimmed, @"^e\s*=\s*."))
                    {
                        sdp.Email = ParseKeyValueLine(trimmed, "Session Email");
                    }
                    else if (trimmed.StartsWith("p="))
                    {
                        sdp.PhoneNumber = ParseKeyValueLine(trimmed, "Session Phone Number");
                    }
                    else if (Regex.IsMatch(trimmed, @"^c\s*=\s*."))
                    {
                        var connection = ConnectionInfo.Parse(trimmed);

                        if (currentMd != null)
                        {
                            // Media Description connection
                            currentMd.Connection = connection;
                        }
                        else
                        {
                            // Session level connection
                            sdp.Connection = connection;
                        }
                    }
                    else if (Regex.IsMatch(trimmed, @"^b\s*=\s*."))
                    {
                        var bandwidth = BandwidthInfo.Parse(trimmed);

                        if (currentMd != null)
                        {
                            // Media Description bandwidth
                            currentMd.Bandwidths.Add(bandwidth);
                        }
                        else
                        {
                            // Session level bandwidth
                            sdp.Bandwidths.Add(bandwidth);
                        }
                    }
                    else if (Regex.IsMatch(trimmed, @"^t\s*=\s*."))
                    {
                        currentTd = TimeDescription.Parse(trimmed);
                        sdp.TimeDescriptions.Add(currentTd);
                    }
                    else if (Regex.IsMatch(trimmed, @"^r\s*=\s*."))
                    {
                        if (currentTd != null)
                        {
                            currentTd.RepeatTimes.Add(RepeatTime.Parse(trimmed));
                        }

                        // Just ignore the repeate time if no current time was defined.
                    }
                    else if (Regex.IsMatch(trimmed, @"^z\s*=\s*."))
                    {
                        sdp.TimeZone = TimeZone.Parse(trimmed);
                    }
                    else if (Regex.IsMatch(trimmed, @"^a\s*=\s*."))
                    {
                        if (currentMd != null)
                        {
                            // Media Description level attribute
                            currentMd.Attributes.Add(Attribute.Parse(trimmed));
                        }
                        else
                        {
                            // Session level attribute
                            sdp.Attributes.Add(Attribute.Parse(trimmed));
                        }
                    }
                    else if (Regex.IsMatch(trimmed, @"^m\s*=\s*."))
                    {
                        currentMd = MediaDescription.Parse(trimmed);
                        sdp.MediaDescriptions.Add(currentMd);
                    }
                } // end foreach...

                return(sdp);
            }
            catch (Exception e)
            {
                if (e is SdpParseException)
                {
                    throw e;
                }

                throw new SdpParseException("Failed to parse session description", e);
            }
        }