Example #1
0
        /// <summary>
        /// Creates a new RTP session. The synchronisation source and sequence number are initialised to
        /// pseudo random values.
        /// </summary>
        /// <param name="formatTypeID">The format type ID for the media. It's what gets set in the payload
        /// type ID field in the RTP header. A default media announcement will be created.</param>
        /// <param name="srtpProtect">Optional secure DTLS context for encrypting RTP packets.</param>
        /// <param name="srtcpProtect">Optional secure DTLS context for encrypting RTCP packets.</param>
        /// <param name="rtpEventSupport">True if RTP event sending and receiving should be supported.</param>
        /// <param name="addrFamily">Determines whether the RTP channel will use an IPv4 or IPv6 socket.</param>
        public RTPSession(int formatTypeID, ProtectRtpPacket srtpProtect, ProtectRtpPacket srtcpProtect, bool rtpEventSupport, AddressFamily addrFamily)
        {
            MediaFormat       = new SDPMediaFormat(formatTypeID);
            MediaAnnouncement = new SDPMediaAnnouncement
            {
                Media        = SDPMediaTypesEnum.audio,
                MediaFormats = new List <SDPMediaFormat> {
                    MediaFormat
                },
                MediaStreamStatus = MediaStreamStatusEnum.SendRecv
            };

            if (rtpEventSupport)
            {
                int            clockRate      = MediaFormat.GetClockRate();
                SDPMediaFormat rtpEventFormat = new SDPMediaFormat(DTMF_EVENT_PAYLOAD_ID);
                rtpEventFormat.SetFormatAttribute($"{TELEPHONE_EVENT_ATTRIBUTE}/{clockRate}");
                rtpEventFormat.SetFormatParameterAttribute("0-16");
                MediaAnnouncement.MediaFormats.Add(rtpEventFormat);
            }

            m_rtpEventSupport = rtpEventSupport;
            FormatTypeID      = formatTypeID;
            SrtpProtect       = srtpProtect;

            Initialise(addrFamily, srtcpProtect);
        }
Example #2
0
        /// <summary>
        /// Adds an additional RTP stream to this session. The effect of this is to multiplex
        /// two or more RTP sessions on a single socket. Multiplexing is used by WebRTC.
        /// </summary>
        /// <param name="payloadTypeID">The payload type ID for this RTP stream. It's what gets set in the payload
        /// type ID field in the RTP header.</param>
        /// <returns>The ID of the stream of this media type. It must be supplied when
        /// doing a send for this stream.</returns>
        public int AddStream(int payloadTypeID, SDPMediaAnnouncement mediaAnnouncement)
        {
            int nextID        = m_sessionStreams.OrderByDescending(x => x.ID).First().ID + 1;
            var sessionStream = new RTPSessionStream(nextID, payloadTypeID);

            m_sessionStreams.Add(sessionStream);
            return(sessionStream.ID);
        }
Example #3
0
        /// <summary>
        /// Generates the base SDP for an offer or answer. The SDP will then be tailored depending
        /// on whether it's being used in an offer or an answer.
        /// </summary>
        /// <param name="audioCapabilities">Optional. The audio formats to support in the SDP. This list can differ from
        /// the local audio track if an answer is being generated and only mutually supported formats are being
        /// used.</param>
        /// <param name="videoCapabilities">Optional. The video formats to support in the SDP. This list can differ from
        /// the local video track if an answer is being generated and only mutually supported formats are being
        /// used.</param>
        /// <remarks>
        /// From https://tools.ietf.org/html/draft-ietf-mmusic-ice-sip-sdp-39#section-4.2.5:
        ///   "The transport address from the peer for the default destination
        ///   is set to IPv4/IPv6 address values "0.0.0.0"/"::" and port value
        ///   of "9".  This MUST NOT be considered as a ICE failure by the peer
        ///   agent and the ICE processing MUST continue as usual."
        /// </remarks>
        private SDP createBaseSdp(List <MediaStreamTrack> tracks, List <SDPMediaFormat> audioCapabilities, List <SDPMediaFormat> videoCapabilities)
        {
            SDP offerSdp = new SDP(IPAddress.Loopback);

            offerSdp.SessionId = LocalSdpSessionID;

            bool iceCandidatesAdded = false;

            // Add a bundle attribute. Indicates that audio and video sessions will be multiplexed
            // on a single RTP socket.
            offerSdp.Group           = BUNDLE_ATTRIBUTE;
            offerSdp.DtlsFingerprint = _currentCertificate.getFingerprints().First().ToString();

            // Media announcements must be in the same order in the offer and answer.
            foreach (var track in tracks.OrderBy(x => x.MLineIndex))
            {
                offerSdp.Group += $" {track.MID}";

                SDPMediaAnnouncement announcement = new SDPMediaAnnouncement(
                    track.Kind,
                    SDP.IGNORE_RTP_PORT_NUMBER,
                    (track.Kind == SDPMediaTypesEnum.video) ? videoCapabilities : audioCapabilities);

                announcement.Transport  = RTP_MEDIA_PROFILE;
                announcement.Connection = new SDPConnectionInformation(IPAddress.Any);
                announcement.AddExtra(RTCP_MUX_ATTRIBUTE);
                announcement.AddExtra(RTCP_ATTRIBUTE);
                announcement.MediaStreamStatus = track.StreamStatus;
                announcement.MediaID           = track.MID;

                announcement.IceUfrag        = _rtpIceChannel.LocalIceUser;
                announcement.IcePwd          = _rtpIceChannel.LocalIcePassword;
                announcement.IceOptions      = ICE_OPTIONS;
                announcement.DtlsFingerprint = offerSdp.DtlsFingerprint;

                if (iceCandidatesAdded == false && _rtpIceChannel.Candidates?.Count > 0)
                {
                    announcement.IceCandidates = new List <string>();

                    // Add ICE candidates.
                    foreach (var iceCandidate in _rtpIceChannel.Candidates)
                    {
                        announcement.IceCandidates.Add(iceCandidate.ToString());
                    }

                    iceCandidatesAdded = true;
                }

                offerSdp.Media.Add(announcement);
            }

            return(offerSdp);
        }
Example #4
0
        /// <summary>
        /// Gets an SDP packet that can be used by VoIP clients to negotiate an audio connection. The SDP will only
        /// offer PCMU since that's all I've gotten around to handling.
        /// </summary>
        /// <param name="usePublicIP">If true and the public IP address is available from the STUN client then
        /// the public IP address will be used in the SDP otherwise the host machine's default IPv4 address will
        /// be used.</param>
        /// <returns>An SDP packet that can be used by a VoIP client when initiating a call.</returns>
        public SDP GetSDP(bool usePublicIP)
        {
            IPAddress rtpIPAddress = (usePublicIP && SoftphoneSTUNClient.PublicIPAddress != null) ? SoftphoneSTUNClient.PublicIPAddress : _defaultLocalAddress;

            var sdp = new SDP()
            {
                SessionId = Crypto.GetRandomInt(5).ToString(),
                Address = rtpIPAddress.ToString(),
                SessionName = "sipsorcery",
                Timing = "0 0",
                Connection = new SDPConnectionInformation(rtpIPAddress.ToString()),
            };

            if (_rtpAudioChannel != null)
            {
                var audioAnnouncement = new SDPMediaAnnouncement()
                {
                    Media = SDPMediaTypesEnum.audio,
                    MediaFormats = new List<SDPMediaFormat>() { new SDPMediaFormat((int)SDPMediaFormatsEnum.PCMU, "PCMU", 8000) }
                };
                audioAnnouncement.Port = _rtpAudioChannel.RTPPort;
                sdp.Media.Add(audioAnnouncement);
            }

            if (_rtpVideoChannel != null)
            {
                var videoAnnouncement = new SDPMediaAnnouncement()
                {
                    Media = SDPMediaTypesEnum.video,
                    MediaFormats = new List<SDPMediaFormat>() { new SDPMediaFormat(96, "VP8", 90000) }
                };
                videoAnnouncement.Port = _rtpVideoChannel.RTPPort;
                sdp.Media.Add(videoAnnouncement);
            }

            return sdp;
        }
Example #5
0
        public static SDP ParseSDPDescription(string sdpDescription)
        {
            try
            {
                if (sdpDescription != null && sdpDescription.Trim().Length > 0)
                {
                    SDP sdp = new SDP();
                    sdp.m_rawSdp = sdpDescription;
                    SDPMediaAnnouncement activeAnnouncement = null;

                    string[] sdpLines = Regex.Split(sdpDescription, CRLF);

                    foreach (string sdpLine in sdpLines)
                    {
                        string sdpLineTrimmed = sdpLine.Trim();
                        if (sdpLineTrimmed.StartsWith("v="))
                        {
                            if (!Decimal.TryParse(sdpLineTrimmed.Substring(2), out sdp.Version))
                            {
                                logger.LogWarning(
                                    "The Version value in an SDP description could not be parsed as a decimal: " +
                                    sdpLine + ".");
                            }
                        }
                        else if (sdpLineTrimmed.StartsWith("o="))
                        {
                            string[] ownerFields = sdpLineTrimmed.Substring(2).Split(' ');
                            sdp.Username  = ownerFields[0];
                            sdp.SessionId = ownerFields[1];
                            Int32.TryParse(ownerFields[2], out sdp.AnnouncementVersion);
                            sdp.NetworkType   = ownerFields[3];
                            sdp.AddressType   = ownerFields[4];
                            sdp.AddressOrHost = ownerFields[5];
                        }
                        else if (sdpLineTrimmed.StartsWith("s="))
                        {
                            sdp.SessionName = sdpLineTrimmed.Substring(2);
                        }
                        else if (sdpLineTrimmed.StartsWith("c="))
                        {
                            if (sdp.Connection == null)
                            {
                                sdp.Connection = SDPConnectionInformation.ParseConnectionInformation(sdpLineTrimmed);
                            }

                            if (activeAnnouncement != null)
                            {
                                activeAnnouncement.Connection =
                                    SDPConnectionInformation.ParseConnectionInformation(sdpLineTrimmed);
                            }
                        }
                        else if (sdpLineTrimmed.StartsWith("b="))
                        {
                            if (activeAnnouncement != null)
                            {
                                activeAnnouncement.BandwidthAttributes.Add(sdpLineTrimmed.Substring(2));
                            }
                            else
                            {
                                sdp.BandwidthAttributes.Add(sdpLineTrimmed.Substring(2));
                            }
                        }
                        else if (sdpLineTrimmed.StartsWith("t="))
                        {
                            sdp.Timing = sdpLineTrimmed.Substring(2);
                        }
                        else if (sdpLineTrimmed.StartsWith("m="))
                        {
                            Match mediaMatch = Regex.Match(sdpLineTrimmed.Substring(2),
                                                           @"(?<type>\w+)\s+(?<port>\d+)\s+(?<transport>\S+)(\s*)(?<formats>.*)$");
                            if (mediaMatch.Success)
                            {
                                SDPMediaAnnouncement announcement = new SDPMediaAnnouncement();
                                announcement.Media = SDPMediaTypes.GetSDPMediaType(mediaMatch.Result("${type}"));
                                Int32.TryParse(mediaMatch.Result("${port}"), out announcement.Port);
                                announcement.Transport = mediaMatch.Result("${transport}");
                                announcement.ParseMediaFormats(mediaMatch.Result("${formats}"));
                                sdp.Media.Add(announcement);

                                activeAnnouncement = announcement;
                            }
                            else
                            {
                                logger.LogWarning("A media line in SDP was invalid: " + sdpLineTrimmed.Substring(2) +
                                                  ".");
                            }
                        }
                        else if (sdpLineTrimmed.StartsWith("a=" + GROUP_ATRIBUTE_PREFIX))
                        {
                            sdp.Group = sdpLineTrimmed.Substring(sdpLineTrimmed.IndexOf(':') + 1);
                        }
                        else if (sdpLineTrimmed.StartsWith("a=" + ICE_UFRAG_ATTRIBUTE_PREFIX))
                        {
                            if (activeAnnouncement != null)
                            {
                                activeAnnouncement.IceUfrag = sdpLineTrimmed.Substring(sdpLineTrimmed.IndexOf(':') + 1);
                            }
                            else
                            {
                                sdp.IceUfrag = sdpLineTrimmed.Substring(sdpLineTrimmed.IndexOf(':') + 1);
                            }
                        }
                        else if (sdpLineTrimmed.StartsWith("a=" + ICE_PWD_ATTRIBUTE_PREFIX))
                        {
                            if (activeAnnouncement != null)
                            {
                                activeAnnouncement.IcePwd = sdpLineTrimmed.Substring(sdpLineTrimmed.IndexOf(':') + 1);
                            }
                            else
                            {
                                sdp.IcePwd = sdpLineTrimmed.Substring(sdpLineTrimmed.IndexOf(':') + 1);
                            }
                        }
                        else if (sdpLineTrimmed.StartsWith("a=" + DTLS_FINGERPRINT_ATTRIBUTE_PREFIX))
                        {
                            if (activeAnnouncement != null)
                            {
                                activeAnnouncement.DtlsFingerprint =
                                    sdpLineTrimmed.Substring(sdpLineTrimmed.IndexOf(':') + 1);
                            }
                            else
                            {
                                sdp.DtlsFingerprint = sdpLineTrimmed.Substring(sdpLineTrimmed.IndexOf(':') + 1);
                            }
                        }
                        else if (sdpLineTrimmed.StartsWith($"a={END_ICE_CANDIDATES_ATTRIBUTE}"))
                        {
                            // Do nothing.
                        }
                        else if (sdpLineTrimmed.StartsWith(SDPMediaAnnouncement.MEDIA_FORMAT_ATTRIBUE_PREFIX))
                        {
                            if (activeAnnouncement != null)
                            {
                                Match formatAttributeMatch = Regex.Match(sdpLineTrimmed,
                                                                         SDPMediaAnnouncement.MEDIA_FORMAT_ATTRIBUE_PREFIX +
                                                                         @"(?<id>\d+)\s+(?<attribute>.*)$");
                                if (formatAttributeMatch.Success)
                                {
                                    int formatID;
                                    if (Int32.TryParse(formatAttributeMatch.Result("${id}"), out formatID))
                                    {
                                        activeAnnouncement.AddFormatAttribute(formatID,
                                                                              formatAttributeMatch.Result("${attribute}"));
                                    }
                                    else
                                    {
                                        logger.LogWarning("Invalid media format attribute in SDP: " + sdpLine);
                                    }
                                }
                                else
                                {
                                    activeAnnouncement.AddExtra(sdpLineTrimmed);
                                }
                            }
                            else
                            {
                                logger.LogWarning(
                                    "There was no active media announcement for a media format attribute, ignoring.");
                            }
                        }
                        else if (sdpLineTrimmed.StartsWith(SDPMediaAnnouncement.MEDIA_FORMAT_PARAMETERS_ATTRIBUE_PREFIX)
                                 )
                        {
                            if (activeAnnouncement != null)
                            {
                                Match formatAttributeMatch = Regex.Match(sdpLineTrimmed,
                                                                         SDPMediaAnnouncement.MEDIA_FORMAT_PARAMETERS_ATTRIBUE_PREFIX +
                                                                         @"(?<id>\d+)\s+(?<attribute>.*)$");
                                if (formatAttributeMatch.Success)
                                {
                                    int formatID;
                                    if (Int32.TryParse(formatAttributeMatch.Result("${id}"), out formatID))
                                    {
                                        activeAnnouncement.AddFormatParameterAttribute(formatID,
                                                                                       formatAttributeMatch.Result("${attribute}"));
                                    }
                                    else
                                    {
                                        logger.LogWarning("Invalid media format parameter attribute in SDP: " +
                                                          sdpLine);
                                    }
                                }
                                else
                                {
                                    activeAnnouncement.AddExtra(sdpLineTrimmed);
                                }
                            }
                            else
                            {
                                logger.LogWarning(
                                    "There was no active media announcement for a media format parameter attribute, ignoring.");
                            }
                        }
                        else if (sdpLineTrimmed.StartsWith("a=" + ICE_CANDIDATE_ATTRIBUTE_PREFIX))
                        {
                            if (activeAnnouncement != null)
                            {
                                if (activeAnnouncement.IceCandidates == null)
                                {
                                    activeAnnouncement.IceCandidates = new List <string>();
                                }

                                activeAnnouncement.IceCandidates.Add(
                                    sdpLineTrimmed.Substring(sdpLineTrimmed.IndexOf(':') + 1));
                            }
                            else
                            {
                                if (sdp.IceCandidates == null)
                                {
                                    sdp.IceCandidates = new List <string>();
                                }

                                sdp.IceCandidates.Add(sdpLineTrimmed.Substring(sdpLineTrimmed.IndexOf(':') + 1));
                            }
                        }
                        //2018-12-21 rj2: add a=crypto
                        else if (sdpLineTrimmed.StartsWith(SDPSecurityDescription.CRYPTO_ATTRIBUE_PREFIX))
                        {
                            if (activeAnnouncement != null)
                            {
                                try
                                {
                                    activeAnnouncement.AddCryptoLine(sdpLineTrimmed);
                                }
                                catch (FormatException fex)
                                {
                                    logger.LogWarning("Error Parsing SDP-Line(a=crypto) " + fex);
                                }
                            }
                        }
                        else if (MediaStreamStatusType.IsMediaStreamStatusAttribute(sdpLine.Trim(),
                                                                                    out var mediaStreamStatus))
                        {
                            if (activeAnnouncement != null)
                            {
                                activeAnnouncement.MediaStreamStatus = mediaStreamStatus;
                            }
                            else
                            {
                                sdp.SessionMediaStreamStatus = mediaStreamStatus;
                            }
                        }
                        else if (sdpLineTrimmed.StartsWith("a=" + MEDIA_ID_ATTRIBUTE_PREFIX))
                        {
                            if (activeAnnouncement != null)
                            {
                                activeAnnouncement.MediaID = sdpLineTrimmed.Substring(sdpLineTrimmed.IndexOf(':') + 1);
                            }
                            else
                            {
                                logger.LogWarning("A media ID can only be set on a media announcement.");
                            }
                        }
                        else if (sdpLineTrimmed.StartsWith(SDPMediaAnnouncement.MEDIA_FORMAT_SSRC_GROUP_ATTRIBUE_PREFIX)
                                 )
                        {
                            if (activeAnnouncement != null)
                            {
                                string[] fields = sdpLineTrimmed.Substring(sdpLineTrimmed.IndexOf(':') + 1).Split(' ');

                                // Set the ID.
                                if (fields.Length > 0)
                                {
                                    activeAnnouncement.SsrcGroupID = fields[0];
                                }

                                // Add attributes for each of the SSRC values.
                                for (int i = 1; i < fields.Length; i++)
                                {
                                    if (uint.TryParse(fields[i], out var ssrc))
                                    {
                                        activeAnnouncement.SsrcAttributes.Add(
                                            new SDPSsrcAttribute(ssrc, null, activeAnnouncement.SsrcGroupID));
                                    }
                                }
                            }
                            else
                            {
                                logger.LogWarning("A ssrc-group ID can only be set on a media announcement.");
                            }
                        }
                        else if (sdpLineTrimmed.StartsWith(SDPMediaAnnouncement.MEDIA_FORMAT_SSRC_ATTRIBUE_PREFIX))
                        {
                            if (activeAnnouncement != null)
                            {
                                string[] ssrcFields = sdpLineTrimmed.Substring(sdpLineTrimmed.IndexOf(':') + 1)
                                                      .Split(' ');

                                if (ssrcFields.Length > 0 && uint.TryParse(ssrcFields[0], out var ssrc))
                                {
                                    var ssrcAttribute =
                                        activeAnnouncement.SsrcAttributes.FirstOrDefault(x => x.SSRC == ssrc);
                                    if (ssrcAttribute == null)
                                    {
                                        ssrcAttribute = new SDPSsrcAttribute(ssrc, null, null);
                                        activeAnnouncement.SsrcAttributes.Add(ssrcAttribute);
                                    }

                                    if (ssrcFields.Length > 1)
                                    {
                                        if (ssrcFields[1].StartsWith(SDPSsrcAttribute.MEDIA_CNAME_ATTRIBUE_PREFIX))
                                        {
                                            ssrcAttribute.Cname =
                                                ssrcFields[1].Substring(ssrcFields[1].IndexOf(':') + 1);
                                        }
                                    }
                                }
                            }
                            else
                            {
                                logger.LogWarning("A ssrc attribute can only be set on a media announcement.");
                            }
                        }
                        else
                        {
                            if (activeAnnouncement != null)
                            {
                                activeAnnouncement.AddExtra(sdpLineTrimmed);
                            }
                            else
                            {
                                sdp.AddExtra(sdpLineTrimmed);
                            }
                        }
                    }

                    return(sdp);
                }
                else
                {
                    return(null);
                }
            }
            catch (Exception excp)
            {
                logger.LogError("Exception ParseSDPDescription. " + excp.Message);
                throw excp;
            }
        }
Example #6
0
        public static SDP ParseSDPDescription(string sdpDescription)
        {
            try
            {
                if (sdpDescription != null && sdpDescription.Trim().Length > 0)
                {
                    SDP sdp = new SDP();
                    sdp.m_rawSdp = sdpDescription;
                    int mLineIndex = 0;
                    SDPMediaAnnouncement activeAnnouncement = null;

                    // If a media announcement fmtp atribute is found before the rtpmap it will be stored
                    // in this dictionary. A dynamic media format type cannot be created without an rtpmap.
                    Dictionary <int, string> _pendingFmtp = new Dictionary <int, string>();

                    //string[] sdpLines = Regex.Split(sdpDescription, CRLF);
                    string[] sdpLines = sdpDescription.Split(new string[] { "\r\n", "\r", "\n" }, StringSplitOptions.RemoveEmptyEntries);

                    foreach (string sdpLine in sdpLines)
                    {
                        string sdpLineTrimmed = sdpLine.Trim();

                        switch (sdpLineTrimmed)
                        {
                        case var l when l.StartsWith("v="):
                            if (!Decimal.TryParse(sdpLineTrimmed.Substring(2), out sdp.Version))
                            {
                                logger.LogWarning("The Version value in an SDP description could not be parsed as a decimal: " + sdpLine + ".");
                            }
                            break;

                        case var l when l.StartsWith("o="):
                            string[] ownerFields = sdpLineTrimmed.Substring(2).Split(' ');

                            sdp.Username  = ownerFields[0];
                            sdp.SessionId = ownerFields[1];
                            Int32.TryParse(ownerFields[2], out sdp.AnnouncementVersion);
                            sdp.NetworkType   = ownerFields[3];
                            sdp.AddressType   = ownerFields[4];
                            sdp.AddressOrHost = ownerFields[5];
                            break;

                        case var l when l.StartsWith("s="):
                            sdp.SessionName = sdpLineTrimmed.Substring(2);

                            break;

                        case var l when l.StartsWith("i="):
                            if (activeAnnouncement != null)
                            {
                                activeAnnouncement.MediaDescription = sdpLineTrimmed.Substring(2);
                            }
                            else
                            {
                                sdp.SessionDescription = sdpLineTrimmed.Substring(2);
                            }

                            break;

                        case var l when l.StartsWith("c="):

                            if (activeAnnouncement != null)
                            {
                                activeAnnouncement.Connection = SDPConnectionInformation.ParseConnectionInformation(sdpLineTrimmed);
                            }
                            else if (sdp.Connection == null)
                            {
                                sdp.Connection = SDPConnectionInformation.ParseConnectionInformation(sdpLineTrimmed);
                            }
                            else
                            {
                                logger.LogWarning("The SDP message had a duplicate connection attribute which was ignored.");
                            }

                            break;

                        case var l when l.StartsWith("b="):
                            if (activeAnnouncement != null)
                            {
                                if (l.StartsWith(SDPMediaAnnouncement.TIAS_BANDWIDTH_ATTRIBUE_PREFIX))
                                {
                                    if (uint.TryParse(l.Substring(l.IndexOf(':') + 1), out uint tias))
                                    {
                                        activeAnnouncement.TIASBandwidth = tias;
                                    }
                                }
                                else
                                {
                                    activeAnnouncement.BandwidthAttributes.Add(sdpLineTrimmed.Substring(2));
                                }
                            }
                            else
                            {
                                sdp.BandwidthAttributes.Add(sdpLineTrimmed.Substring(2));
                            }
                            break;

                        case var l when l.StartsWith("t="):
                            sdp.Timing = sdpLineTrimmed.Substring(2);

                            break;

                        case var l when l.StartsWith("m="):
                            Match mediaMatch = Regex.Match(sdpLineTrimmed.Substring(2), @"(?<type>\w+)\s+(?<port>\d+)\s+(?<transport>\S+)(\s*)(?<formats>.*)$");

                            if (mediaMatch.Success)
                            {
                                SDPMediaAnnouncement announcement = new SDPMediaAnnouncement();
                                announcement.MLineIndex = mLineIndex;
                                announcement.Media      = SDPMediaTypes.GetSDPMediaType(mediaMatch.Result("${type}"));
                                Int32.TryParse(mediaMatch.Result("${port}"), out announcement.Port);
                                announcement.Transport = mediaMatch.Result("${transport}");
                                announcement.ParseMediaFormats(mediaMatch.Result("${formats}"));
                                if (announcement.Media == SDPMediaTypesEnum.audio || announcement.Media == SDPMediaTypesEnum.video)
                                {
                                    announcement.MediaStreamStatus = sdp.SessionMediaStreamStatus != null ? sdp.SessionMediaStreamStatus.Value :
                                                                     MediaStreamStatusEnum.SendRecv;
                                }
                                sdp.Media.Add(announcement);

                                activeAnnouncement = announcement;
                            }
                            else
                            {
                                logger.LogWarning("A media line in SDP was invalid: " + sdpLineTrimmed.Substring(2) + ".");
                            }

                            mLineIndex++;
                            break;

                        case var x when x.StartsWith($"a={GROUP_ATRIBUTE_PREFIX}"):
                            sdp.Group = sdpLineTrimmed.Substring(sdpLineTrimmed.IndexOf(':') + 1);

                            break;

                        case var x when x.StartsWith($"a={ICE_UFRAG_ATTRIBUTE_PREFIX}"):
                            if (activeAnnouncement != null)
                            {
                                activeAnnouncement.IceUfrag = sdpLineTrimmed.Substring(sdpLineTrimmed.IndexOf(':') + 1);
                            }
                            else
                            {
                                sdp.IceUfrag = sdpLineTrimmed.Substring(sdpLineTrimmed.IndexOf(':') + 1);
                            }
                            break;

                        case var x when x.StartsWith($"a={ICE_PWD_ATTRIBUTE_PREFIX}"):
                            if (activeAnnouncement != null)
                            {
                                activeAnnouncement.IcePwd = sdpLineTrimmed.Substring(sdpLineTrimmed.IndexOf(':') + 1);
                            }
                            else
                            {
                                sdp.IcePwd = sdpLineTrimmed.Substring(sdpLineTrimmed.IndexOf(':') + 1);
                            }
                            break;

                        case var x when x.StartsWith($"a={ICE_SETUP_ATTRIBUTE_PREFIX}"):
                            int colonIndex = sdpLineTrimmed.IndexOf(':');

                            if (colonIndex != -1 && sdpLineTrimmed.Length > colonIndex)
                            {
                                string iceRoleStr = sdpLineTrimmed.Substring(colonIndex + 1).Trim();
                                if (Enum.TryParse <IceRolesEnum>(iceRoleStr, true, out var iceRole))
                                {
                                    if (activeAnnouncement != null)
                                    {
                                        activeAnnouncement.IceRole = iceRole;
                                    }
                                    else
                                    {
                                        sdp.IceRole = iceRole;
                                    }
                                }
                                else
                                {
                                    logger.LogWarning($"ICE role was not recognised from SDP attribute: {sdpLineTrimmed}.");
                                }
                            }
                            else
                            {
                                logger.LogWarning($"ICE role SDP attribute was missing the mandatory colon: {sdpLineTrimmed}.");
                            }
                            break;

                        case var x when x.StartsWith($"a={DTLS_FINGERPRINT_ATTRIBUTE_PREFIX}"):
                            if (activeAnnouncement != null)
                            {
                                activeAnnouncement.DtlsFingerprint = sdpLineTrimmed.Substring(sdpLineTrimmed.IndexOf(':') + 1);
                            }
                            else
                            {
                                sdp.DtlsFingerprint = sdpLineTrimmed.Substring(sdpLineTrimmed.IndexOf(':') + 1);
                            }
                            break;

                        case var x when x.StartsWith($"a={ICE_CANDIDATE_ATTRIBUTE_PREFIX}"):
                            if (activeAnnouncement != null)
                            {
                                if (activeAnnouncement.IceCandidates == null)
                                {
                                    activeAnnouncement.IceCandidates = new List <string>();
                                }
                                activeAnnouncement.IceCandidates.Add(sdpLineTrimmed.Substring(sdpLineTrimmed.IndexOf(':') + 1));
                            }
                            else
                            {
                                if (sdp.IceCandidates == null)
                                {
                                    sdp.IceCandidates = new List <string>();
                                }
                                sdp.IceCandidates.Add(sdpLineTrimmed.Substring(sdpLineTrimmed.IndexOf(':') + 1));
                            }
                            break;

                        case var x when x == $"a={END_ICE_CANDIDATES_ATTRIBUTE}":
                            // TODO: Set a flag.
                            break;

                        case var l when l.StartsWith(SDPMediaAnnouncement.MEDIA_FORMAT_ATTRIBUE_PREFIX):
                            if (activeAnnouncement != null)
                            {
                                if (activeAnnouncement.Media == SDPMediaTypesEnum.audio || activeAnnouncement.Media == SDPMediaTypesEnum.video)
                                {
                                    // Parse the rtpmap attribute for audio/video announcements.
                                    Match formatAttributeMatch = Regex.Match(sdpLineTrimmed, SDPMediaAnnouncement.MEDIA_FORMAT_ATTRIBUE_PREFIX + @"(?<id>\d+)\s+(?<attribute>.*)$");
                                    if (formatAttributeMatch.Success)
                                    {
                                        string formatID = formatAttributeMatch.Result("${id}");
                                        string rtpmap   = formatAttributeMatch.Result("${attribute}");

                                        if (Int32.TryParse(formatID, out int id))
                                        {
                                            if (activeAnnouncement.MediaFormats.ContainsKey(id))
                                            {
                                                activeAnnouncement.MediaFormats[id] = activeAnnouncement.MediaFormats[id].WithUpdatedRtpmap(rtpmap, activeAnnouncement.MediaFormats[id]);
                                            }
                                            else
                                            {
                                                string fmtp = _pendingFmtp.ContainsKey(id) ? _pendingFmtp[id] : null;
                                                activeAnnouncement.MediaFormats.Add(id, new SDPAudioVideoMediaFormat(activeAnnouncement.Media, id, rtpmap, fmtp));
                                            }
                                        }
                                        else
                                        {
                                            logger.LogWarning("Non-numeric audio/video media format attribute in SDP: " + sdpLine);
                                        }
                                    }
                                    else
                                    {
                                        activeAnnouncement.AddExtra(sdpLineTrimmed);
                                    }
                                }
                                else
                                {
                                    // Parse the rtpmap attribute for NON audio/video announcements.
                                    Match formatAttributeMatch = Regex.Match(sdpLineTrimmed, SDPMediaAnnouncement.MEDIA_FORMAT_ATTRIBUE_PREFIX + @"(?<id>\S+)\s+(?<attribute>.*)$");
                                    if (formatAttributeMatch.Success)
                                    {
                                        string formatID = formatAttributeMatch.Result("${id}");
                                        string rtpmap   = formatAttributeMatch.Result("${attribute}");

                                        if (activeAnnouncement.ApplicationMediaFormats.ContainsKey(formatID))
                                        {
                                            activeAnnouncement.ApplicationMediaFormats[formatID] = activeAnnouncement.ApplicationMediaFormats[formatID].WithUpdatedRtpmap(rtpmap);
                                        }
                                        else
                                        {
                                            activeAnnouncement.ApplicationMediaFormats.Add(formatID, new SDPApplicationMediaFormat(formatID, rtpmap, null));
                                        }
                                    }
                                    else
                                    {
                                        activeAnnouncement.AddExtra(sdpLineTrimmed);
                                    }
                                }
                            }
                            else
                            {
                                logger.LogWarning("There was no active media announcement for a media format attribute, ignoring.");
                            }
                            break;

                        case var l when l.StartsWith(SDPMediaAnnouncement.MEDIA_FORMAT_PARAMETERS_ATTRIBUE_PREFIX):
                            if (activeAnnouncement != null)
                            {
                                if (activeAnnouncement.Media == SDPMediaTypesEnum.audio || activeAnnouncement.Media == SDPMediaTypesEnum.video)
                                {
                                    // Parse the fmtp attribute for audio/video announcements.
                                    Match formatAttributeMatch = Regex.Match(sdpLineTrimmed, SDPMediaAnnouncement.MEDIA_FORMAT_PARAMETERS_ATTRIBUE_PREFIX + @"(?<id>\d+)\s+(?<attribute>.*)$");
                                    if (formatAttributeMatch.Success)
                                    {
                                        string avFormatID = formatAttributeMatch.Result("${id}");
                                        string fmtp       = formatAttributeMatch.Result("${attribute}");

                                        if (Int32.TryParse(avFormatID, out int id))
                                        {
                                            if (activeAnnouncement.MediaFormats.ContainsKey(id))
                                            {
                                                activeAnnouncement.MediaFormats[id] = activeAnnouncement.MediaFormats[id].WithUpdatedFmtp(fmtp, activeAnnouncement.MediaFormats[id]);
                                            }
                                            else
                                            {
                                                // Store the fmtp attribute for use when the rtpmap attribute turns up.
                                                if (_pendingFmtp.ContainsKey(id))
                                                {
                                                    _pendingFmtp.Remove(id);
                                                }
                                                _pendingFmtp.Add(id, fmtp);
                                            }
                                        }
                                        else
                                        {
                                            logger.LogWarning("Invalid media format parameter attribute in SDP: " + sdpLine);
                                        }
                                    }
                                    else
                                    {
                                        activeAnnouncement.AddExtra(sdpLineTrimmed);
                                    }
                                }
                                else
                                {
                                    // Parse the fmtp attribute for NON audio/video announcements.
                                    Match formatAttributeMatch = Regex.Match(sdpLineTrimmed, SDPMediaAnnouncement.MEDIA_FORMAT_PARAMETERS_ATTRIBUE_PREFIX + @"(?<id>\S+)\s+(?<attribute>.*)$");
                                    if (formatAttributeMatch.Success)
                                    {
                                        string formatID = formatAttributeMatch.Result("${id}");
                                        string fmtp     = formatAttributeMatch.Result("${attribute}");

                                        if (activeAnnouncement.ApplicationMediaFormats.ContainsKey(formatID))
                                        {
                                            activeAnnouncement.ApplicationMediaFormats[formatID] = activeAnnouncement.ApplicationMediaFormats[formatID].WithUpdatedFmtp(fmtp);
                                        }
                                        else
                                        {
                                            activeAnnouncement.ApplicationMediaFormats.Add(formatID, new SDPApplicationMediaFormat(formatID, null, fmtp));
                                        }
                                    }
                                    else
                                    {
                                        activeAnnouncement.AddExtra(sdpLineTrimmed);
                                    }
                                }
                            }
                            else
                            {
                                logger.LogWarning("There was no active media announcement for a media format parameter attribute, ignoring.");
                            }
                            break;

                        case var l when l.StartsWith(SDPSecurityDescription.CRYPTO_ATTRIBUE_PREFIX):
                            //2018-12-21 rj2: add a=crypto
                            if (activeAnnouncement != null)
                            {
                                try
                                {
                                    activeAnnouncement.AddCryptoLine(sdpLineTrimmed);
                                }
                                catch (FormatException fex)
                                {
                                    logger.LogWarning("Error Parsing SDP-Line(a=crypto) " + fex);
                                }
                            }
                            break;

                        case var x when x.StartsWith($"a={MEDIA_ID_ATTRIBUTE_PREFIX}"):
                            if (activeAnnouncement != null)
                            {
                                activeAnnouncement.MediaID = sdpLineTrimmed.Substring(sdpLineTrimmed.IndexOf(':') + 1);
                            }
                            else
                            {
                                logger.LogWarning("A media ID can only be set on a media announcement.");
                            }
                            break;

                        case var l when l.StartsWith(SDPMediaAnnouncement.MEDIA_FORMAT_SSRC_GROUP_ATTRIBUE_PREFIX):
                            if (activeAnnouncement != null)
                            {
                                string[] fields = sdpLineTrimmed.Substring(sdpLineTrimmed.IndexOf(':') + 1).Split(' ');

                                // Set the ID.
                                if (fields.Length > 0)
                                {
                                    activeAnnouncement.SsrcGroupID = fields[0];
                                }

                                // Add attributes for each of the SSRC values.
                                for (int i = 1; i < fields.Length; i++)
                                {
                                    if (uint.TryParse(fields[i], out var ssrc))
                                    {
                                        activeAnnouncement.SsrcAttributes.Add(new SDPSsrcAttribute(ssrc, null, activeAnnouncement.SsrcGroupID));
                                    }
                                }
                            }
                            else
                            {
                                logger.LogWarning("A ssrc-group ID can only be set on a media announcement.");
                            }
                            break;

                        case var l when l.StartsWith(SDPMediaAnnouncement.MEDIA_FORMAT_SSRC_ATTRIBUE_PREFIX):
                            if (activeAnnouncement != null)
                            {
                                string[] ssrcFields = sdpLineTrimmed.Substring(sdpLineTrimmed.IndexOf(':') + 1).Split(' ');

                                if (ssrcFields.Length > 0 && uint.TryParse(ssrcFields[0], out var ssrc))
                                {
                                    var ssrcAttribute = activeAnnouncement.SsrcAttributes.FirstOrDefault(x => x.SSRC == ssrc);
                                    if (ssrcAttribute == null)
                                    {
                                        ssrcAttribute = new SDPSsrcAttribute(ssrc, null, null);
                                        activeAnnouncement.SsrcAttributes.Add(ssrcAttribute);
                                    }

                                    if (ssrcFields.Length > 1)
                                    {
                                        if (ssrcFields[1].StartsWith(SDPSsrcAttribute.MEDIA_CNAME_ATTRIBUE_PREFIX))
                                        {
                                            ssrcAttribute.Cname = ssrcFields[1].Substring(ssrcFields[1].IndexOf(':') + 1);
                                        }
                                    }
                                }
                            }
                            else
                            {
                                logger.LogWarning("An ssrc attribute can only be set on a media announcement.");
                            }
                            break;

                        case var x when MediaStreamStatusType.IsMediaStreamStatusAttribute(x, out var mediaStreamStatus):
                            if (activeAnnouncement != null)
                            {
                                activeAnnouncement.MediaStreamStatus = mediaStreamStatus;
                            }
                            else
                            {
                                sdp.SessionMediaStreamStatus = mediaStreamStatus;
                            }
                            break;

                        case var l when l.StartsWith(SDPMediaAnnouncement.MEDIA_FORMAT_SCTP_MAP_ATTRIBUE_PREFIX):
                            if (activeAnnouncement != null)
                            {
                                activeAnnouncement.SctpMap = sdpLineTrimmed.Substring(sdpLineTrimmed.IndexOf(':') + 1);

                                (var sctpPortStr, _, var maxMessageSizeStr) = activeAnnouncement.SctpMap.Split(' ');

                                if (ushort.TryParse(sctpPortStr, out var sctpPort))
                                {
                                    activeAnnouncement.SctpPort = sctpPort;
                                }
                                else
                                {
                                    logger.LogWarning($"An sctp-port value of {sctpPortStr} was not recognised as a valid port.");
                                }

                                if (!long.TryParse(maxMessageSizeStr, out activeAnnouncement.MaxMessageSize))
                                {
                                    logger.LogWarning($"A max-message-size value of {maxMessageSizeStr} was not recognised as a valid long.");
                                }
                            }
                            else
                            {
                                logger.LogWarning("An sctpmap attribute can only be set on a media announcement.");
                            }
                            break;

                        case var l when l.StartsWith(SDPMediaAnnouncement.MEDIA_FORMAT_SCTP_PORT_ATTRIBUE_PREFIX):
                            if (activeAnnouncement != null)
                            {
                                string sctpPortStr = sdpLineTrimmed.Substring(sdpLineTrimmed.IndexOf(':') + 1);

                                if (ushort.TryParse(sctpPortStr, out var sctpPort))
                                {
                                    activeAnnouncement.SctpPort = sctpPort;
                                }
                                else
                                {
                                    logger.LogWarning($"An sctp-port value of {sctpPortStr} was not recognised as a valid port.");
                                }
                            }
                            else
                            {
                                logger.LogWarning("An sctp-port attribute can only be set on a media announcement.");
                            }
                            break;

                        case var l when l.StartsWith(SDPMediaAnnouncement.MEDIA_FORMAT_MAX_MESSAGE_SIZE_ATTRIBUE_PREFIX):
                            if (activeAnnouncement != null)
                            {
                                string maxMessageSizeStr = sdpLineTrimmed.Substring(sdpLineTrimmed.IndexOf(':') + 1);

                                if (!long.TryParse(maxMessageSizeStr, out activeAnnouncement.MaxMessageSize))
                                {
                                    logger.LogWarning($"A max-message-size value of {maxMessageSizeStr} was not recognised as a valid long.");
                                }
                            }
                            else
                            {
                                logger.LogWarning("A max-message-size attribute can only be set on a media announcement.");
                            }
                            break;

                        case var l when l.StartsWith(SDPMediaAnnouncement.MEDIA_FORMAT_PATH_ACCEPT_TYPES_PREFIX):
                            if (activeAnnouncement != null)
                            {
                                string acceptTypesStr  = sdpLineTrimmed.Substring(sdpLineTrimmed.IndexOf(':') + 1);
                                var    acceptTypesList = acceptTypesStr.Trim().Split(' ').ToList();

                                activeAnnouncement.MessageMediaFormat.AcceptTypes = acceptTypesList;
                            }
                            else
                            {
                                logger.LogWarning("A accept-types attribute can only be set on a media announcement.");
                            }
                            break;

                        case var l when l.StartsWith(SDPMediaAnnouncement.MEDIA_FORMAT_PATH_MSRP_PREFIX):
                            if (activeAnnouncement != null)
                            {
                                string pathStr        = sdpLineTrimmed.Substring(sdpLineTrimmed.IndexOf(':') + 1);
                                string pathTrimmedStr = pathStr.Substring(pathStr.IndexOf(':') + 3);

                                activeAnnouncement.MessageMediaFormat.IP = pathTrimmedStr.Substring(0, pathTrimmedStr.IndexOf(':'));

                                pathTrimmedStr = pathTrimmedStr.Substring(pathTrimmedStr.IndexOf(':') + 1);
                                activeAnnouncement.MessageMediaFormat.Port = pathTrimmedStr.Substring(0, pathTrimmedStr.IndexOf('/'));

                                pathTrimmedStr = pathTrimmedStr.Substring(pathTrimmedStr.IndexOf('/') + 1);
                                activeAnnouncement.MessageMediaFormat.Endpoint = pathTrimmedStr;
                            }
                            else
                            {
                                logger.LogWarning("A path attribute can only be set on a media announcement.");
                            }
                            break;

                        default:
                            if (activeAnnouncement != null)
                            {
                                activeAnnouncement.AddExtra(sdpLineTrimmed);
                            }
                            else
                            {
                                sdp.AddExtra(sdpLineTrimmed);
                            }
                            break;
                        }
                    }

                    return(sdp);
                }
                else
                {
                    return(null);
                }
            }
            catch (Exception excp)
            {
                logger.LogError("Exception ParseSDPDescription. " + excp.Message);
                throw;
            }
        }
Example #7
0
        public static SDP ParseSDPDescription(string sdpDescription)
        {
            try
            {
                if (sdpDescription != null && sdpDescription.Trim().Length > 0)
                {
                    SDP sdp = new SDP();
                    SDPMediaAnnouncement activeAnnouncement = null;

                    string[] sdpLines = Regex.Split(sdpDescription, CRLF);

                    foreach (string sdpLine in sdpLines)
                    {
                        if (sdpLine.Trim().StartsWith("v="))
                        {
                            if (!Decimal.TryParse(sdpLine.Substring(2), out sdp.Version))
                            {
                                logger.Warn("The Version value in an SDP description could not be parsed as a decimal: " + sdpLine + ".");
                            }
                        }
                        else if (sdpLine.Trim().StartsWith("o="))
                        {
                            string[] ownerFields = sdpLine.Substring(2).Split(' ');
                            sdp.Username = ownerFields[0];
                            sdp.SessionId = ownerFields[1];
                            Int32.TryParse(ownerFields[2], out sdp.AnnouncementVersion);
                            sdp.NetworkType = ownerFields[3];
                            sdp.AddressType = ownerFields[4];
                            sdp.Address = ownerFields[5];
                        }
                        else if (sdpLine.Trim().StartsWith("s="))
                        {
                            sdp.SessionName = sdpLine.Substring(2);
                        }
                        else if (sdpLine.Trim().StartsWith("c="))
                        {
                            if(activeAnnouncement != null)
                                activeAnnouncement.Connection = SDPConnectionInformation.ParseConnectionInformation(sdpLine);
                            else
                                sdp.Connection = SDPConnectionInformation.ParseConnectionInformation(sdpLine);
                        }
                        else if(sdpLine.Trim().StartsWith("b="))
                        {
                            if(activeAnnouncement != null)
                                activeAnnouncement.BandwidthAttributes.Add(sdpLine.Substring(2));
                            else
                                sdp.BandwidthAttributes.Add(sdpLine.Substring(2));
                        }
                        else if (sdpLine.Trim().StartsWith("t="))
                        {
                            sdp.Timing = sdpLine.Substring(2);
                        }
                        else if (sdpLine.Trim().StartsWith("m="))
                        {
                            Match mediaMatch = Regex.Match(sdpLine.Substring(2).Trim(), @"(?<type>\w+)\s+(?<port>\d+)\s+(?<transport>\S+)\s+(?<formats>.*)$");
                            if (mediaMatch.Success)
                            {
                                SDPMediaAnnouncement announcement = new SDPMediaAnnouncement();
                                announcement.Media = SDPMediaTypes.GetSDPMediaType(mediaMatch.Result("${type}"));
                                Int32.TryParse(mediaMatch.Result("${port}"), out announcement.Port);
                                announcement.Transport = mediaMatch.Result("${transport}");
                                announcement.ParseMediaFormats(mediaMatch.Result("${formats}"));
                                sdp.Media.Add(announcement);

                                activeAnnouncement = announcement;
                            }
                            else
                            {
                                logger.Warn("A media line in SDP was invalid: " + sdpLine.Substring(2) + ".");
                            }
                        }
                        else if (sdpLine.Trim().StartsWith("a=" + ICE_UFRAG_ATTRIBUTE_PREFIX))
                        {
                            sdp.IceUfrag = sdpLine.Substring(sdpLine.IndexOf(':') + 1);
                        }
                        else if (sdpLine.Trim().StartsWith("a=" + ICE_PWD_ATTRIBUTE_PREFIX))
                        {
                            sdp.IcePwd = sdpLine.Substring(sdpLine.IndexOf(':') + 1);
                        }
                        else if (sdpLine.Trim().StartsWith(SDPMediaAnnouncement.MEDIA_FORMAT_ATTRIBUE_PREFIX))
                        {
                            if (activeAnnouncement != null)
                            {
                                Match formatAttributeMatch = Regex.Match(sdpLine.Trim(), SDPMediaAnnouncement.MEDIA_FORMAT_ATTRIBUE_PREFIX + @"(?<id>\d+)\s+(?<attribute>.*)$");
                                if (formatAttributeMatch.Success)
                                {
                                    int formatID;
                                    if (Int32.TryParse(formatAttributeMatch.Result("${id}"), out formatID))
                                    {
                                        activeAnnouncement.AddFormatAttribute(formatID, formatAttributeMatch.Result("${attribute}"));
                                    }
                                    else
                                    {
                                        logger.Warn("Invalid media format attribute in SDP: " + sdpLine);
                                    }
                                }
                                else
                                {
                                    activeAnnouncement.AddExtra(sdpLine);
                                }
                            }
                            else
                            {
                                logger.Warn("There was no active media announcement for a media format attribute, ignoring.");
                            }
                        }
                        else if(sdpLine.Trim().StartsWith(SDPMediaAnnouncement.MEDIA_FORMAT_PARAMETERS_ATTRIBUE_PREFIX))
                        {
                            if(activeAnnouncement != null)
                            {
                                Match formatAttributeMatch = Regex.Match(sdpLine.Trim(), SDPMediaAnnouncement.MEDIA_FORMAT_PARAMETERS_ATTRIBUE_PREFIX + @"(?<id>\d+)\s+(?<attribute>.*)$");
                                if(formatAttributeMatch.Success)
                                {
                                    int formatID;
                                    if(Int32.TryParse(formatAttributeMatch.Result("${id}"), out formatID))
                                    {
                                        activeAnnouncement.AddFormatParameterAttribute(formatID, formatAttributeMatch.Result("${attribute}"));
                                    }
                                    else
                                    {
                                        logger.Warn("Invalid media format parameter attribute in SDP: " + sdpLine);
                                    }
                                }
                                else
                                {
                                    activeAnnouncement.AddExtra(sdpLine);
                                }
                            }
                            else
                            {
                                logger.Warn("There was no active media announcement for a media format parameter attribute, ignoring.");
                            }
                        }
                        else if (sdpLine.Trim().StartsWith("a=" + ICE_CANDIDATE_ATTRIBUTE_PREFIX))
                        {
                            if(sdp.IceCandidates == null)
                            {
                                sdp.IceCandidates = new List<IceCandidate>();
                            }

                            sdp.IceCandidates.Add(IceCandidate.Parse(sdpLine.Substring(sdpLine.IndexOf(':') + 1)));
                        }
                        else
                        {
                            if(activeAnnouncement != null)
                                activeAnnouncement.AddExtra(sdpLine);
                            else
                                sdp.AddExtra(sdpLine);
                        }
                    }

                    return sdp;
                }
                else
                {
                    return null;
                }
            }
            catch (Exception excp)
            {
                logger.Error("Exception ParseSDPDescription. " + excp.Message);
                throw excp;
            }
        }
Example #8
0
        public static SDP ParseSDPDescription(string sdpDescription)
        {
            try
            {
                if (sdpDescription != null && sdpDescription.Trim().Length > 0)
                {
                    SDP sdp = new SDP();
                    sdp.m_rawSdp = sdpDescription;
                    int mLineIndex = 0;
                    SDPMediaAnnouncement activeAnnouncement = null;

                    string[] sdpLines = Regex.Split(sdpDescription, CRLF);

                    foreach (string sdpLine in sdpLines)
                    {
                        string sdpLineTrimmed = sdpLine.Trim();

                        switch (sdpLineTrimmed)
                        {
                        case var l when l.StartsWith("v="):
                            if (!Decimal.TryParse(sdpLineTrimmed.Substring(2), out sdp.Version))
                            {
                                logger.LogWarning("The Version value in an SDP description could not be parsed as a decimal: " + sdpLine + ".");
                            }
                            break;

                        case var l when l.StartsWith("o="):
                            string[] ownerFields = sdpLineTrimmed.Substring(2).Split(' ');

                            sdp.Username  = ownerFields[0];
                            sdp.SessionId = ownerFields[1];
                            Int32.TryParse(ownerFields[2], out sdp.AnnouncementVersion);
                            sdp.NetworkType   = ownerFields[3];
                            sdp.AddressType   = ownerFields[4];
                            sdp.AddressOrHost = ownerFields[5];
                            break;

                        case var l when l.StartsWith("s="):
                            sdp.SessionName = sdpLineTrimmed.Substring(2);

                            break;

                        case var l when l.StartsWith("c="):

                            if (activeAnnouncement != null)
                            {
                                activeAnnouncement.Connection = SDPConnectionInformation.ParseConnectionInformation(sdpLineTrimmed);
                            }
                            else if (sdp.Connection == null)
                            {
                                sdp.Connection = SDPConnectionInformation.ParseConnectionInformation(sdpLineTrimmed);
                            }
                            else
                            {
                                logger.LogWarning("The SDP message had a duplicate connection attribute which was ignored.");
                            }

                            break;

                        case var l when l.StartsWith("b="):
                            if (activeAnnouncement != null)
                            {
                                activeAnnouncement.BandwidthAttributes.Add(sdpLineTrimmed.Substring(2));
                            }
                            else
                            {
                                sdp.BandwidthAttributes.Add(sdpLineTrimmed.Substring(2));
                            }
                            break;

                        case var l when l.StartsWith("t="):
                            sdp.Timing = sdpLineTrimmed.Substring(2);

                            break;

                        case var l when l.StartsWith("m="):
                            Match mediaMatch = Regex.Match(sdpLineTrimmed.Substring(2), @"(?<type>\w+)\s+(?<port>\d+)\s+(?<transport>\S+)(\s*)(?<formats>.*)$");

                            if (mediaMatch.Success)
                            {
                                SDPMediaAnnouncement announcement = new SDPMediaAnnouncement();
                                announcement.MLineIndex = mLineIndex;
                                announcement.Media      = SDPMediaTypes.GetSDPMediaType(mediaMatch.Result("${type}"));
                                Int32.TryParse(mediaMatch.Result("${port}"), out announcement.Port);
                                announcement.Transport = mediaMatch.Result("${transport}");
                                announcement.ParseMediaFormats(mediaMatch.Result("${formats}"));
                                announcement.MediaStreamStatus = sdp.SessionMediaStreamStatus != null ? sdp.SessionMediaStreamStatus.Value :
                                                                 MediaStreamStatusEnum.SendRecv;
                                sdp.Media.Add(announcement);

                                activeAnnouncement = announcement;
                            }
                            else
                            {
                                logger.LogWarning("A media line in SDP was invalid: " + sdpLineTrimmed.Substring(2) + ".");
                            }

                            mLineIndex++;
                            break;

                        case var x when x.StartsWith($"a={GROUP_ATRIBUTE_PREFIX}"):
                            sdp.Group = sdpLineTrimmed.Substring(sdpLineTrimmed.IndexOf(':') + 1);

                            break;

                        case var x when x.StartsWith($"a={ICE_UFRAG_ATTRIBUTE_PREFIX}"):
                            if (activeAnnouncement != null)
                            {
                                activeAnnouncement.IceUfrag = sdpLineTrimmed.Substring(sdpLineTrimmed.IndexOf(':') + 1);
                            }
                            else
                            {
                                sdp.IceUfrag = sdpLineTrimmed.Substring(sdpLineTrimmed.IndexOf(':') + 1);
                            }
                            break;

                        case var x when x.StartsWith($"a={ICE_PWD_ATTRIBUTE_PREFIX}"):
                            if (activeAnnouncement != null)
                            {
                                activeAnnouncement.IcePwd = sdpLineTrimmed.Substring(sdpLineTrimmed.IndexOf(':') + 1);
                            }
                            else
                            {
                                sdp.IcePwd = sdpLineTrimmed.Substring(sdpLineTrimmed.IndexOf(':') + 1);
                            }
                            break;

                        case var x when x.StartsWith($"a={DTLS_FINGERPRINT_ATTRIBUTE_PREFIX}"):
                            if (activeAnnouncement != null)
                            {
                                activeAnnouncement.DtlsFingerprint = sdpLineTrimmed.Substring(sdpLineTrimmed.IndexOf(':') + 1);
                            }
                            else
                            {
                                sdp.DtlsFingerprint = sdpLineTrimmed.Substring(sdpLineTrimmed.IndexOf(':') + 1);
                            }
                            break;

                        case var x when x.StartsWith($"a={ICE_CANDIDATE_ATTRIBUTE_PREFIX}"):
                            if (activeAnnouncement != null)
                            {
                                if (activeAnnouncement.IceCandidates == null)
                                {
                                    activeAnnouncement.IceCandidates = new List <string>();
                                }
                                activeAnnouncement.IceCandidates.Add(sdpLineTrimmed.Substring(sdpLineTrimmed.IndexOf(':') + 1));
                            }
                            else
                            {
                                if (sdp.IceCandidates == null)
                                {
                                    sdp.IceCandidates = new List <string>();
                                }
                                sdp.IceCandidates.Add(sdpLineTrimmed.Substring(sdpLineTrimmed.IndexOf(':') + 1));
                            }
                            break;

                        case var x when x == $"a={END_ICE_CANDIDATES_ATTRIBUTE}":
                            // TODO: Set a flag.
                            break;

                        case var l when l.StartsWith(SDPMediaAnnouncement.MEDIA_FORMAT_ATTRIBUE_PREFIX):
                            if (activeAnnouncement != null)
                            {
                                Match formatAttributeMatch = Regex.Match(sdpLineTrimmed, SDPMediaAnnouncement.MEDIA_FORMAT_ATTRIBUE_PREFIX + @"(?<id>\d+)\s+(?<attribute>.*)$");

                                if (formatAttributeMatch.Success)
                                {
                                    string formatID = formatAttributeMatch.Result("${id}");
                                    string rtpmap   = formatAttributeMatch.Result("${attribute}");

                                    if (activeAnnouncement.Media == SDPMediaTypesEnum.application)
                                    {
                                        var appMediaFormat = activeAnnouncement.ApplicationMediaFormats.Where(x => x.ID == formatID).FirstOrDefault();
                                        if (appMediaFormat.ID != null)
                                        {
                                            appMediaFormat.Rtpmap = rtpmap;
                                        }
                                    }
                                    else
                                    {
                                        if (Int32.TryParse(formatID, out int id))
                                        {
                                            if (activeAnnouncement.MediaFormats.ContainsKey(id))
                                            {
                                                activeAnnouncement.MediaFormats[id] = activeAnnouncement.MediaFormats[id].WithUpdatedRtpmap(rtpmap, activeAnnouncement.MediaFormats[id]);
                                            }
                                            else
                                            {
                                                activeAnnouncement.MediaFormats.Add(id, new SDPAudioVideoMediaFormat(activeAnnouncement.Media, id, rtpmap, null));
                                            }
                                        }
                                        else
                                        {
                                            logger.LogWarning("Invalid media format attribute in SDP: " + sdpLine);
                                        }
                                    }
                                }
                                else
                                {
                                    activeAnnouncement.AddExtra(sdpLineTrimmed);
                                }
                            }
                            else
                            {
                                logger.LogWarning("There was no active media announcement for a media format attribute, ignoring.");
                            }
                            break;

                        case var l when l.StartsWith(SDPMediaAnnouncement.MEDIA_FORMAT_PARAMETERS_ATTRIBUE_PREFIX):
                            if (activeAnnouncement != null)
                            {
                                Match formatAttributeMatch = Regex.Match(sdpLineTrimmed, SDPMediaAnnouncement.MEDIA_FORMAT_PARAMETERS_ATTRIBUE_PREFIX + @"(?<id>\d+)\s+(?<attribute>.*)$");

                                if (formatAttributeMatch.Success)
                                {
                                    string formatID = formatAttributeMatch.Result("${id}");
                                    string fmtp     = formatAttributeMatch.Result("${attribute}");

                                    if (activeAnnouncement.Media == SDPMediaTypesEnum.application)
                                    {
                                        var appMediaFormat = activeAnnouncement.ApplicationMediaFormats.Where(x => x.ID == formatID).FirstOrDefault();
                                        if (appMediaFormat.ID != null)
                                        {
                                            appMediaFormat.Fmtmp = fmtp;
                                        }
                                    }
                                    else
                                    {
                                        if (Int32.TryParse(formatID, out int id))
                                        {
                                            if (activeAnnouncement.MediaFormats.ContainsKey(id))
                                            {
                                                activeAnnouncement.MediaFormats[id] = activeAnnouncement.MediaFormats[id].WithUpdatedFmtp(fmtp, activeAnnouncement.MediaFormats[id]);
                                            }
                                            else
                                            {
                                                activeAnnouncement.MediaFormats.Add(id, new SDPAudioVideoMediaFormat(activeAnnouncement.Media, id, null, fmtp));
                                            }
                                        }
                                        else
                                        {
                                            logger.LogWarning("Invalid media format parameter attribute in SDP: " + sdpLine);
                                        }
                                    }
                                }
                                else
                                {
                                    activeAnnouncement.AddExtra(sdpLineTrimmed);
                                }
                            }
                            else
                            {
                                logger.LogWarning("There was no active media announcement for a media format parameter attribute, ignoring.");
                            }
                            break;

                        case var l when l.StartsWith(SDPSecurityDescription.CRYPTO_ATTRIBUE_PREFIX):
                            //2018-12-21 rj2: add a=crypto
                            if (activeAnnouncement != null)
                            {
                                try
                                {
                                    activeAnnouncement.AddCryptoLine(sdpLineTrimmed);
                                }
                                catch (FormatException fex)
                                {
                                    logger.LogWarning("Error Parsing SDP-Line(a=crypto) " + fex);
                                }
                            }
                            break;

                        case var x when x.StartsWith($"a={MEDIA_ID_ATTRIBUTE_PREFIX}"):
                            if (activeAnnouncement != null)
                            {
                                activeAnnouncement.MediaID = sdpLineTrimmed.Substring(sdpLineTrimmed.IndexOf(':') + 1);
                            }
                            else
                            {
                                logger.LogWarning("A media ID can only be set on a media announcement.");
                            }
                            break;

                        case var l when l.StartsWith(SDPMediaAnnouncement.MEDIA_FORMAT_SSRC_GROUP_ATTRIBUE_PREFIX):
                            if (activeAnnouncement != null)
                            {
                                string[] fields = sdpLineTrimmed.Substring(sdpLineTrimmed.IndexOf(':') + 1).Split(' ');

                                // Set the ID.
                                if (fields.Length > 0)
                                {
                                    activeAnnouncement.SsrcGroupID = fields[0];
                                }

                                // Add attributes for each of the SSRC values.
                                for (int i = 1; i < fields.Length; i++)
                                {
                                    if (uint.TryParse(fields[i], out var ssrc))
                                    {
                                        activeAnnouncement.SsrcAttributes.Add(new SDPSsrcAttribute(ssrc, null, activeAnnouncement.SsrcGroupID));
                                    }
                                }
                            }
                            else
                            {
                                logger.LogWarning("A ssrc-group ID can only be set on a media announcement.");
                            }
                            break;

                        case var l when l.StartsWith(SDPMediaAnnouncement.MEDIA_FORMAT_SSRC_ATTRIBUE_PREFIX):
                            if (activeAnnouncement != null)
                            {
                                string[] ssrcFields = sdpLineTrimmed.Substring(sdpLineTrimmed.IndexOf(':') + 1).Split(' ');

                                if (ssrcFields.Length > 0 && uint.TryParse(ssrcFields[0], out var ssrc))
                                {
                                    var ssrcAttribute = activeAnnouncement.SsrcAttributes.FirstOrDefault(x => x.SSRC == ssrc);
                                    if (ssrcAttribute == null)
                                    {
                                        ssrcAttribute = new SDPSsrcAttribute(ssrc, null, null);
                                        activeAnnouncement.SsrcAttributes.Add(ssrcAttribute);
                                    }

                                    if (ssrcFields.Length > 1)
                                    {
                                        if (ssrcFields[1].StartsWith(SDPSsrcAttribute.MEDIA_CNAME_ATTRIBUE_PREFIX))
                                        {
                                            ssrcAttribute.Cname = ssrcFields[1].Substring(ssrcFields[1].IndexOf(':') + 1);
                                        }
                                    }
                                }
                            }
                            else
                            {
                                logger.LogWarning("An ssrc attribute can only be set on a media announcement.");
                            }
                            break;

                        case var x when MediaStreamStatusType.IsMediaStreamStatusAttribute(x, out var mediaStreamStatus):
                            if (activeAnnouncement != null)
                            {
                                activeAnnouncement.MediaStreamStatus = mediaStreamStatus;
                            }
                            else
                            {
                                sdp.SessionMediaStreamStatus = mediaStreamStatus;
                            }
                            break;

                        case var l when l.StartsWith(SDPMediaAnnouncement.MEDIA_FORMAT_SCTP_MAP_ATTRIBUE_PREFIX):
                            if (activeAnnouncement != null)
                            {
                                activeAnnouncement.SctpMap = sdpLineTrimmed.Substring(sdpLineTrimmed.IndexOf(':') + 1);

                                (var sctpPortStr, _, var maxMessageSizeStr) = activeAnnouncement.SctpMap.Split(' ');

                                if (ushort.TryParse(sctpPortStr, out var sctpPort))
                                {
                                    activeAnnouncement.SctpPort = sctpPort;
                                }
                                else
                                {
                                    logger.LogWarning($"An sctp-port value of {sctpPortStr} was not recognised as a valid port.");
                                }

                                if (!long.TryParse(maxMessageSizeStr, out activeAnnouncement.MaxMessageSize))
                                {
                                    logger.LogWarning($"A max-message-size value of {maxMessageSizeStr} was not recognised as a valid long.");
                                }
                            }
                            else
                            {
                                logger.LogWarning("An sctpmap attribute can only be set on a media announcement.");
                            }
                            break;

                        case var l when l.StartsWith(SDPMediaAnnouncement.MEDIA_FORMAT_SCTP_PORT_ATTRIBUE_PREFIX):
                            if (activeAnnouncement != null)
                            {
                                string sctpPortStr = sdpLineTrimmed.Substring(sdpLineTrimmed.IndexOf(':') + 1);

                                if (ushort.TryParse(sctpPortStr, out var sctpPort))
                                {
                                    activeAnnouncement.SctpPort = sctpPort;
                                }
                                else
                                {
                                    logger.LogWarning($"An sctp-port value of {sctpPortStr} was not recognised as a valid port.");
                                }
                            }
                            else
                            {
                                logger.LogWarning("An sctp-port attribute can only be set on a media announcement.");
                            }
                            break;

                        case var l when l.StartsWith(SDPMediaAnnouncement.MEDIA_FORMAT_MAX_MESSAGE_SIZE_ATTRIBUE_PREFIX):
                            if (activeAnnouncement != null)
                            {
                                string maxMessageSizeStr = sdpLineTrimmed.Substring(sdpLineTrimmed.IndexOf(':') + 1);

                                if (!long.TryParse(maxMessageSizeStr, out activeAnnouncement.MaxMessageSize))
                                {
                                    logger.LogWarning($"A max-message-size value of {maxMessageSizeStr} was not recognised as a valid long.");
                                }
                            }
                            else
                            {
                                logger.LogWarning("A max-message-size attribute can only be set on a media announcement.");
                            }
                            break;

                        default:
                            if (activeAnnouncement != null)
                            {
                                activeAnnouncement.AddExtra(sdpLineTrimmed);
                            }
                            else
                            {
                                sdp.AddExtra(sdpLineTrimmed);
                            }
                            break;
                        }
                    }

                    return(sdp);
                }
                else
                {
                    return(null);
                }
            }
            catch (Exception excp)
            {
                logger.LogError("Exception ParseSDPDescription. " + excp.Message);
                throw;
            }
        }
Example #9
0
        public static SDP ParseSDPDescription(string sdpDescription)
        {
            try
            {
                if (sdpDescription != null && sdpDescription.Trim().Length > 0)
                {
                    SDP sdp = new SDP();
                    SDPMediaAnnouncement activeAnnouncement = null;

                    string[] sdpLines = Regex.Split(sdpDescription, CRLF);

                    foreach (string sdpLine in sdpLines)
                    {
                        if (sdpLine.Trim().StartsWith("v="))
                        {
                            if (!Decimal.TryParse(sdpLine.Substring(2), out sdp.Version))
                            {
                                logger.LogWarning("The Version value in an SDP description could not be parsed as a decimal: " + sdpLine + ".");
                            }
                        }
                        else if (sdpLine.Trim().StartsWith("o="))
                        {
                            string[] ownerFields = sdpLine.Substring(2).Split(' ');
                            sdp.Username  = ownerFields[0];
                            sdp.SessionId = ownerFields[1];
                            Int32.TryParse(ownerFields[2], out sdp.AnnouncementVersion);
                            sdp.NetworkType = ownerFields[3];
                            sdp.AddressType = ownerFields[4];
                            sdp.Address     = ownerFields[5];
                        }
                        else if (sdpLine.Trim().StartsWith("s="))
                        {
                            sdp.SessionName = sdpLine.Substring(2);
                        }
                        else if (sdpLine.Trim().StartsWith("c="))
                        {
                            if (sdp.Connection == null)
                            {
                                sdp.Connection = SDPConnectionInformation.ParseConnectionInformation(sdpLine);
                            }

                            if (activeAnnouncement != null)
                            {
                                activeAnnouncement.Connection = SDPConnectionInformation.ParseConnectionInformation(sdpLine);
                            }
                        }
                        else if (sdpLine.Trim().StartsWith("b="))
                        {
                            if (activeAnnouncement != null)
                            {
                                activeAnnouncement.BandwidthAttributes.Add(sdpLine.Substring(2));
                            }
                            else
                            {
                                sdp.BandwidthAttributes.Add(sdpLine.Substring(2));
                            }
                        }
                        else if (sdpLine.Trim().StartsWith("t="))
                        {
                            sdp.Timing = sdpLine.Substring(2);
                        }
                        else if (sdpLine.Trim().StartsWith("m="))
                        {
                            Match mediaMatch = Regex.Match(sdpLine.Substring(2).Trim(), @"(?<type>\w+)\s+(?<port>\d+)\s+(?<transport>\S+)(\s*)(?<formats>.*)$");
                            if (mediaMatch.Success)
                            {
                                SDPMediaAnnouncement announcement = new SDPMediaAnnouncement();
                                announcement.Media = SDPMediaTypes.GetSDPMediaType(mediaMatch.Result("${type}"));
                                Int32.TryParse(mediaMatch.Result("${port}"), out announcement.Port);
                                announcement.Transport = mediaMatch.Result("${transport}");
                                announcement.ParseMediaFormats(mediaMatch.Result("${formats}"));
                                sdp.Media.Add(announcement);

                                activeAnnouncement = announcement;
                            }
                            else
                            {
                                logger.LogWarning("A media line in SDP was invalid: " + sdpLine.Substring(2) + ".");
                            }
                        }
                        else if (sdpLine.Trim().StartsWith("a=" + ICE_UFRAG_ATTRIBUTE_PREFIX))
                        {
                            sdp.IceUfrag = sdpLine.Substring(sdpLine.IndexOf(':') + 1);
                        }
                        else if (sdpLine.Trim().StartsWith("a=" + ICE_PWD_ATTRIBUTE_PREFIX))
                        {
                            sdp.IcePwd = sdpLine.Substring(sdpLine.IndexOf(':') + 1);
                        }
                        else if (sdpLine.Trim().StartsWith(SDPMediaAnnouncement.MEDIA_FORMAT_ATTRIBUE_PREFIX))
                        {
                            if (activeAnnouncement != null)
                            {
                                Match formatAttributeMatch = Regex.Match(sdpLine.Trim(), SDPMediaAnnouncement.MEDIA_FORMAT_ATTRIBUE_PREFIX + @"(?<id>\d+)\s+(?<attribute>.*)$");
                                if (formatAttributeMatch.Success)
                                {
                                    int formatID;
                                    if (Int32.TryParse(formatAttributeMatch.Result("${id}"), out formatID))
                                    {
                                        activeAnnouncement.AddFormatAttribute(formatID, formatAttributeMatch.Result("${attribute}"));
                                    }
                                    else
                                    {
                                        logger.LogWarning("Invalid media format attribute in SDP: " + sdpLine);
                                    }
                                }
                                else
                                {
                                    activeAnnouncement.AddExtra(sdpLine);
                                }
                            }
                            else
                            {
                                logger.LogWarning("There was no active media announcement for a media format attribute, ignoring.");
                            }
                        }
                        else if (sdpLine.Trim().StartsWith(SDPMediaAnnouncement.MEDIA_FORMAT_PARAMETERS_ATTRIBUE_PREFIX))
                        {
                            if (activeAnnouncement != null)
                            {
                                Match formatAttributeMatch = Regex.Match(sdpLine.Trim(), SDPMediaAnnouncement.MEDIA_FORMAT_PARAMETERS_ATTRIBUE_PREFIX + @"(?<id>\d+)\s+(?<attribute>.*)$");
                                if (formatAttributeMatch.Success)
                                {
                                    int formatID;
                                    if (Int32.TryParse(formatAttributeMatch.Result("${id}"), out formatID))
                                    {
                                        activeAnnouncement.AddFormatParameterAttribute(formatID, formatAttributeMatch.Result("${attribute}"));
                                    }
                                    else
                                    {
                                        logger.LogWarning("Invalid media format parameter attribute in SDP: " + sdpLine);
                                    }
                                }
                                else
                                {
                                    activeAnnouncement.AddExtra(sdpLine);
                                }
                            }
                            else
                            {
                                logger.LogWarning("There was no active media announcement for a media format parameter attribute, ignoring.");
                            }
                        }
                        else if (sdpLine.Trim().StartsWith("a=" + ICE_CANDIDATE_ATTRIBUTE_PREFIX))
                        {
                            if (sdp.IceCandidates == null)
                            {
                                sdp.IceCandidates = new List <IceCandidate>();
                            }

                            sdp.IceCandidates.Add(IceCandidate.Parse(sdpLine.Substring(sdpLine.IndexOf(':') + 1)));
                        }
                        else
                        {
                            if (activeAnnouncement != null)
                            {
                                activeAnnouncement.AddExtra(sdpLine);
                            }
                            else
                            {
                                sdp.AddExtra(sdpLine);
                            }
                        }
                    }

                    return(sdp);
                }
                else
                {
                    return(null);
                }
            }
            catch (Exception excp)
            {
                logger.LogError("Exception ParseSDPDescription. " + excp.Message);
                throw excp;
            }
        }
Example #10
0
        /// <summary>
        /// Generates the base SDP for an offer or answer. The SDP will then be tailored depending
        /// on whether it's being used in an offer or an answer.
        /// </summary>
        private async Task <SDP> createBaseSdp()
        {
            DateTime startGatheringTime = DateTime.Now;

            IceConnectionState = IceConnectionStatesEnum.Gathering;

            await GetIceCandidatesAsync().ConfigureAwait(false);

            logger.LogDebug($"ICE gathering completed for in {DateTime.Now.Subtract(startGatheringTime).TotalMilliseconds:#}ms, candidate count {LocalIceCandidates.Count}.");

            IceConnectionState = IceConnectionStatesEnum.GatheringComplete;

            if (LocalIceCandidates.Count == 0)
            {
                //logger.LogWarning("No local socket candidates were found for WebRTC call closing.");
                //Close("No local ICE candidates available.");
                throw new ApplicationException("No local ICE candidates available.");
            }
            else
            {
                SDP offerSdp = new SDP(IPAddress.Loopback);
                offerSdp.SessionId = LocalSdpSessionID;

                bool   haveIceCandidatesBeenAdded = false;
                string localIceCandidateString    = null;

                foreach (var iceCandidate in LocalIceCandidates)
                {
                    localIceCandidateString += iceCandidate.ToString();
                }

                // Add a bundle attribute. Indicates that audio and video sessions will be multiplexed
                // on a single RTP socket.
                if (AudioLocalTrack != null && VideoLocalTrack != null)
                {
                    offerSdp.Group = MEDIA_GROUPING;
                }

                // The media is being multiplexed so the audio and video RTP channel is the same.
                var rtpChannel = GetRtpChannel(SDPMediaTypesEnum.audio);

                // --- Audio announcement ---
                if (AudioLocalTrack != null)
                {
                    SDPMediaAnnouncement audioAnnouncement = new SDPMediaAnnouncement(
                        SDPMediaTypesEnum.audio,
                        rtpChannel.RTPPort,
                        AudioLocalTrack.Capabilties);

                    audioAnnouncement.Transport = RTP_MEDIA_PROFILE;

                    if (!haveIceCandidatesBeenAdded)
                    {
                        audioAnnouncement.IceCandidates = LocalIceCandidates;
                        haveIceCandidatesBeenAdded      = true;
                    }

                    audioAnnouncement.Connection      = new SDPConnectionInformation(IPAddress.Any);
                    audioAnnouncement.IceUfrag        = LocalIceUser;
                    audioAnnouncement.IcePwd          = LocalIcePassword;
                    audioAnnouncement.DtlsFingerprint = _dtlsCertificateFingerprint;
                    audioAnnouncement.AddExtra(RTCP_MUX_ATTRIBUTE);
                    audioAnnouncement.MediaStreamStatus = AudioLocalTrack.Transceiver.Direction;
                    audioAnnouncement.MediaID           = AudioLocalTrack.Transceiver.MID;

                    offerSdp.Media.Add(audioAnnouncement);
                }

                // --- Video announcement ---
                if (VideoLocalTrack != null)
                {
                    SDPMediaAnnouncement videoAnnouncement = new SDPMediaAnnouncement(
                        SDPMediaTypesEnum.video,
                        rtpChannel.RTPPort,
                        VideoLocalTrack.Capabilties);

                    videoAnnouncement.Transport = RTP_MEDIA_PROFILE;

                    if (!haveIceCandidatesBeenAdded)
                    {
                        videoAnnouncement.IceCandidates = LocalIceCandidates;
                        haveIceCandidatesBeenAdded      = true;
                    }

                    videoAnnouncement.Connection      = new SDPConnectionInformation(IPAddress.Any);
                    videoAnnouncement.IceUfrag        = LocalIceUser;
                    videoAnnouncement.IcePwd          = LocalIcePassword;
                    videoAnnouncement.DtlsFingerprint = _dtlsCertificateFingerprint;
                    videoAnnouncement.AddExtra(RTCP_MUX_ATTRIBUTE);
                    videoAnnouncement.MediaStreamStatus = VideoLocalTrack.Transceiver.Direction;
                    videoAnnouncement.MediaID           = VideoLocalTrack.Transceiver.MID;

                    offerSdp.Media.Add(videoAnnouncement);
                }

                return(offerSdp);
            }
        }
Example #11
0
        /// <summary>
        /// Generates the SDP for an offer or answer.
        /// </summary>
        private async Task <SDP> getSdp(string setupAttribute)
        {
            try
            {
                DateTime startGatheringTime = DateTime.Now;

                IceConnectionState = IceConnectionStatesEnum.Gathering;

                await GetIceCandidatesAsync();

                logger.LogDebug($"ICE gathering completed for in {DateTime.Now.Subtract(startGatheringTime).TotalMilliseconds:#}ms, candidate count {LocalIceCandidates.Count}.");

                IceConnectionState = IceConnectionStatesEnum.GatheringComplete;

                if (LocalIceCandidates.Count == 0)
                {
                    //logger.LogWarning("No local socket candidates were found for WebRTC call closing.");
                    //Close("No local ICE candidates available.");
                    throw new ApplicationException("No local ICE candidates available.");
                }
                else
                {
                    bool haveIceCandidatesBeenAdded = false;

                    string localIceCandidateString = null;

                    foreach (var iceCandidate in LocalIceCandidates)
                    {
                        localIceCandidateString += iceCandidate.ToString();
                    }

                    LocalIceUser     = LocalIceUser ?? Crypto.GetRandomString(20);
                    LocalIcePassword = LocalIcePassword ?? Crypto.GetRandomString(20) + Crypto.GetRandomString(20);

                    SDP offerSdp = new SDP(IPAddress.Loopback);
                    offerSdp.SessionId = Crypto.GetRandomInt(5).ToString();

                    // Add a bundle attribute. Indicates that audio and video sessions will be multiplexed
                    // on a single RTP socket.
                    if (RtpSession.AudioTrack != null && RtpSession.VideoTrack != null)
                    {
                        offerSdp.Group = MEDIA_GROUPING;
                    }

                    // --- Audio announcement ---
                    if (RtpSession.AudioTrack != null)
                    {
                        SDPMediaAnnouncement audioAnnouncement = new SDPMediaAnnouncement(
                            SDPMediaTypesEnum.audio,
                            _rtpChannel.RTPPort,
                            RtpSession.AudioTrack.Capabilties);

                        audioAnnouncement.Transport = RTP_MEDIA_PROFILE;
                        if (!haveIceCandidatesBeenAdded)
                        {
                            audioAnnouncement.IceCandidates = LocalIceCandidates;
                            haveIceCandidatesBeenAdded      = true;
                        }

                        audioAnnouncement.Connection      = new SDPConnectionInformation(IPAddress.Any);
                        audioAnnouncement.IceUfrag        = LocalIceUser;
                        audioAnnouncement.IcePwd          = LocalIcePassword;
                        audioAnnouncement.DtlsFingerprint = _dtlsCertificateFingerprint;
                        audioAnnouncement.AddExtra(RTCP_MUX_ATTRIBUTE);
                        audioAnnouncement.AddExtra(setupAttribute);
                        audioAnnouncement.MediaStreamStatus = RtpSession.AudioTrack.Transceiver.Direction;
                        audioAnnouncement.MediaID           = RtpSession.AudioTrack.Transceiver.MID;

                        offerSdp.Media.Add(audioAnnouncement);
                    }

                    // --- Video announcement ---
                    if (RtpSession.VideoTrack != null)
                    {
                        SDPMediaAnnouncement videoAnnouncement = new SDPMediaAnnouncement(
                            SDPMediaTypesEnum.video,
                            _rtpChannel.RTPPort,
                            RtpSession.VideoTrack.Capabilties);

                        videoAnnouncement.Transport = RTP_MEDIA_PROFILE;
                        if (!haveIceCandidatesBeenAdded)
                        {
                            videoAnnouncement.IceCandidates = LocalIceCandidates;
                            haveIceCandidatesBeenAdded      = true;
                        }

                        videoAnnouncement.Connection      = new SDPConnectionInformation(IPAddress.Any);
                        videoAnnouncement.IceUfrag        = LocalIceUser;
                        videoAnnouncement.IcePwd          = LocalIcePassword;
                        videoAnnouncement.DtlsFingerprint = _dtlsCertificateFingerprint;
                        videoAnnouncement.AddExtra(RTCP_MUX_ATTRIBUTE);
                        videoAnnouncement.AddExtra(setupAttribute);
                        videoAnnouncement.MediaStreamStatus = RtpSession.VideoTrack.Transceiver.Direction;
                        videoAnnouncement.MediaID           = RtpSession.VideoTrack.Transceiver.MID;;

                        offerSdp.Media.Add(videoAnnouncement);
                    }

                    OnSdpOfferReady?.Invoke(offerSdp);

                    return(offerSdp);
                }

                // We may have received some remote candidates from the remote part SDP so perform an immediate STUN check.
                // If there are no remote candidates this call will end up being a NOP.
                //SendStunConnectivityChecks(null);

                //if (_doDtlsHandshake != null)
                //{
                //    _ = Task.Run(() =>
                //    {
                //        int result = _doDtlsHandshake(this);
                //        IsDtlsNegotiationComplete = (result == 0);
                //    });
                //}
            }
            catch (Exception excp)
            {
                logger.LogError("Exception getSdp. " + excp);
                Close(excp.Message);

                throw;
            }
        }
Example #12
0
        public static SDP ParseSDPDescription(string sdpDescription)
        {
            try
            {
                if (sdpDescription != null && sdpDescription.Trim().Length > 0)
                {
                    SDP sdp = new SDP();
                    SDPMediaAnnouncement media = new SDPMediaAnnouncement();

                    string[] sdpLines = Regex.Split(sdpDescription, CRLF);

                    //added WebRTC handling -> dont parse SDP especially
                    sdp.isWebRTC = sdpDescription.Contains("a=source:webrtc");

                    if (sdp.isWebRTC)
                    {
                        foreach (string sdpLine in sdpLines)
                        {
                            sdp.ExtraAttributes.Add(sdpLine);
                        }
                    }
                    else
                    {
                        foreach (string sdpLine in sdpLines)
                        {
                            if (sdpLine.Trim().StartsWith("v="))
                            {
                                if (!Decimal.TryParse(sdpLine.Substring(2), out sdp.Version))
                                {
                                    logger.Warn("The Version value in an SDP description could not be parsed as a decimal: " + sdpLine + ".");
                                }
                            }
                            else if (sdpLine.Trim().StartsWith("o="))
                            {
                                string[] ownerFields = sdpLine.Substring(2).Split(' ');
                                sdp.Username = ownerFields[0];
                                sdp.SessionId = ownerFields[1];
                                Int32.TryParse(ownerFields[2], out sdp.AnnouncementVersion);
                                sdp.NetworkType = ownerFields[3];
                                sdp.AddressType = ownerFields[4];
                                sdp.Address = ownerFields[5];
                            }
                            else if (sdpLine.Trim().StartsWith("s="))
                            {
                                sdp.SessionName = sdpLine.Substring(2);
                            }
                            else if (sdpLine.Trim().StartsWith("c="))
                            {
                                sdp.Connection = SDPConnectionInformation.ParseConnectionInformation(sdpLine);
                            }
                            else if (sdpLine.Trim().StartsWith("t="))
                            {
                                sdp.Timing = sdpLine.Substring(2);
                            }
                            else if (sdpLine.Trim().StartsWith("m="))
                            {
                                Match mediaMatch = Regex.Match(sdpLine.Substring(2).Trim(), @"(?<type>\w+)\s+(?<port>\d+)\s+(?<transport>\S+)\s+(?<formats>.*)$");
                                if (mediaMatch.Success)
                                {
                                    media.Media = SDPMediaTypes.GetSDPMediaType(mediaMatch.Result("${type}"));
                                    Int32.TryParse(mediaMatch.Result("${port}"), out media.Port);
                                    media.Transport = mediaMatch.Result("${transport}");
                                    media.ParseMediaFormats(mediaMatch.Result("${formats}"));
                                }
                                else
                                {
                                    logger.Warn("A media line in SDP was invalid: " + sdpLine.Substring(2) + ".");
                                }
                            }
                            else if (sdpLine.Trim().StartsWith("a=" + ICE_UFRAG_ATTRIBUTE_PREFIX))
                            {
                                sdp.IceUfrag = sdpLine.Substring(sdpLine.IndexOf(':') + 1);
                            }
                            else if (sdpLine.Trim().StartsWith("a=" + ICE_PWD_ATTRIBUTE_PREFIX))
                            {
                                sdp.IcePwd = sdpLine.Substring(sdpLine.IndexOf(':') + 1);
                            }
                            else if (sdpLine.Trim().StartsWith(SDPMediaAnnouncement.MEDIA_FORMAT_ATTRIBUE_PREFIX))
                            {
                                Match formatAttributeMatch = Regex.Match(sdpLine.Trim(), SDPMediaAnnouncement.MEDIA_FORMAT_ATTRIBUE_PREFIX + @"(?<id>\d+)\s+(?<attribute>.*)$");
                                if (formatAttributeMatch.Success)
                                {
                                    int formatID;
                                    if (Int32.TryParse(formatAttributeMatch.Result("${id}"), out formatID))
                                    {
                                        media.AddFormatAttribute(formatID, formatAttributeMatch.Result("${attribute}"));
                                    }
                                    else
                                    {
                                        logger.Warn("Invalid media format attribute in SDP: " + sdpLine);
                                    }
                                }
                                else
                                {
                                    sdp.ExtraAttributes.Add(sdpLine);
                                }
                            }
                            else
                            {
                                sdp.ExtraAttributes.Add(sdpLine);
                            }
                        }
                        sdp.Media.Add(media);
                    }
                    return sdp;
                }
                else
                {
                    return null;
                }
            }
            catch (Exception excp)
            {
                logger.Error("Exception ParseSDPDescription. " + excp.Message);
                throw excp;
            }
        }
Example #13
0
        /// <summary>
        /// Initialises the WebRTC session by carrying out the ICE connectivity steps and when complete
        /// handing the RTP socket off for the DTLS handshake. Once the handshake is complete the session
        /// is ready for to exchange encrypted RTP and RTCP packets.
        /// </summary>
        /// <param name="turnServerEndPoint">An optional parameter that can be used include a TURN
        /// server in this session's ICE candidate gathering.</param>
        public async Task Initialise(DoDtlsHandshakeDelegate doDtlsHandshake, IPEndPoint turnServerEndPoint)
        {
            try
            {
                _doDtlsHandshake    = doDtlsHandshake;
                _turnServerEndPoint = turnServerEndPoint;

                DateTime startGatheringTime = DateTime.Now;

                IceConnectionState = IceConnectionStatesEnum.Gathering;

                await GetIceCandidatesAsync();

                logger.LogDebug($"ICE gathering completed for in {DateTime.Now.Subtract(startGatheringTime).TotalMilliseconds:#}ms, candidate count {LocalIceCandidates.Count}.");

                IceConnectionState = IceConnectionStatesEnum.GatheringComplete;

                if (LocalIceCandidates.Count == 0)
                {
                    logger.LogWarning("No local socket candidates were found for WebRTC call closing.");
                    Close("No local ICE candidates available.");
                }
                else
                {
                    bool includeAudioOffer          = _supportedAudioFormats?.Count() > 0;
                    bool includeVideoOffer          = _supportedVideoFormats?.Count() > 0;
                    bool haveIceCandidatesBeenAdded = false;
                    bool isMediaBundle = includeAudioOffer && includeVideoOffer;    // Is this SDP offer bundling audio and video on the same RTP connection.

                    string localIceCandidateString = null;

                    foreach (var iceCandidate in LocalIceCandidates)
                    {
                        localIceCandidateString += iceCandidate.ToString();
                    }

                    LocalIceUser     = LocalIceUser ?? Crypto.GetRandomString(20);
                    LocalIcePassword = LocalIcePassword ?? Crypto.GetRandomString(20) + Crypto.GetRandomString(20);

                    SDP offerSdp = new SDP(IPAddress.Loopback);
                    offerSdp.SessionId = Crypto.GetRandomInt(5).ToString();

                    // Add a bundle attribute. Indicates that audio and video sessions will be multiplexed
                    // on a single RTP socket.
                    if (isMediaBundle)
                    {
                        offerSdp.Group = MEDIA_GROUPING;
                    }

                    if (includeAudioOffer)
                    {
                        SDPMediaAnnouncement audioAnnouncement = new SDPMediaAnnouncement(
                            SDPMediaTypesEnum.audio,
                            _rtpChannel.RTPPort,
                            _supportedAudioFormats);

                        audioAnnouncement.Transport = RTP_MEDIA_PROFILE;
                        if (!haveIceCandidatesBeenAdded)
                        {
                            audioAnnouncement.IceCandidates = LocalIceCandidates;
                            haveIceCandidatesBeenAdded      = true;
                        }

                        audioAnnouncement.Connection      = new SDPConnectionInformation(IPAddress.Any);
                        audioAnnouncement.IceUfrag        = LocalIceUser;
                        audioAnnouncement.IcePwd          = LocalIcePassword;
                        audioAnnouncement.DtlsFingerprint = _dtlsCertificateFingerprint;
                        audioAnnouncement.AddExtra(RTCP_MUX_ATTRIBUTE);
                        audioAnnouncement.AddExtra(SETUP_ATTRIBUTE);
                        audioAnnouncement.MediaStreamStatus = AudioStreamStatus;

                        if (isMediaBundle)
                        {
                            audioAnnouncement.MediaID = AUDIO_MEDIA_ID;
                        }

                        offerSdp.Media.Add(audioAnnouncement);
                    }

                    if (includeVideoOffer)
                    {
                        SDPMediaAnnouncement videoAnnouncement = new SDPMediaAnnouncement(
                            SDPMediaTypesEnum.video,
                            _rtpChannel.RTPPort,
                            _supportedVideoFormats);

                        videoAnnouncement.Transport = RTP_MEDIA_PROFILE;
                        if (!haveIceCandidatesBeenAdded)
                        {
                            videoAnnouncement.IceCandidates = LocalIceCandidates;
                            haveIceCandidatesBeenAdded      = true;
                        }

                        videoAnnouncement.Connection      = new SDPConnectionInformation(IPAddress.Any);
                        videoAnnouncement.IceUfrag        = LocalIceUser;
                        videoAnnouncement.IcePwd          = LocalIcePassword;
                        videoAnnouncement.DtlsFingerprint = _dtlsCertificateFingerprint;
                        videoAnnouncement.AddExtra(RTCP_MUX_ATTRIBUTE);
                        videoAnnouncement.AddExtra(SETUP_ATTRIBUTE);
                        videoAnnouncement.MediaStreamStatus = VideoStreamStatus;

                        if (isMediaBundle)
                        {
                            videoAnnouncement.MediaID = VIDEO_MEDIA_ID;
                        }

                        offerSdp.Media.Add(videoAnnouncement);
                    }

                    SDP = offerSdp;

                    OnSdpOfferReady?.Invoke(SDP);
                }

                // We may have received some remote candidates from the remote part SDP so perform an immediate STUN check.
                // If there are no remote candidates this call will end up being a NOP.
                SendStunConnectivityChecks(null);

                if (_doDtlsHandshake != null)
                {
                    _ = Task.Run(() =>
                    {
                        int result = _doDtlsHandshake(this);
                        IsDtlsNegotiationComplete = (result == 0);
                    });
                }
            }
            catch (Exception excp)
            {
                logger.LogError("Exception WebRtcPeer.Initialise. " + excp);
                Close(excp.Message);
            }
        }