Exemple #1
0
        private bool HandleNAT(SDP_MediaDescription mediaStream, RTP_Session rtpSession)
        {
            if (mediaStream == null)
            {
                throw new ArgumentNullException("mediaStream");
            }
            if (rtpSession == null)
            {
                throw new ArgumentNullException("rtpSession");
            }

            IPEndPoint rtpPublicEP = null;
            IPEndPoint rtcpPublicEP = null;

            // We have public IP.
            if (!Net_Utils.IsPrivateIP(rtpSession.LocalEP.IP))
            {
                rtpPublicEP = rtpSession.LocalEP.RtpEP;
                rtcpPublicEP = rtpSession.LocalEP.RtcpEP;
            }
            // No NAT handling.
            else if (m_NatHandlingType == "no_nat")
            {
                rtpPublicEP = rtpSession.LocalEP.RtpEP;
                rtcpPublicEP = rtpSession.LocalEP.RtcpEP;
            }
            // Use STUN.
            else if (m_NatHandlingType == "stun")
            {
                rtpSession.StunPublicEndPoints(m_StunServer, 3478, out rtpPublicEP, out rtcpPublicEP);
            }
            // Use UPnP.
            else if (m_NatHandlingType == "upnp")
            {
                // Try to open UPnP ports.
                if (m_pUPnP.IsSupported)
                {
                    int rtpPublicPort = rtpSession.LocalEP.RtpEP.Port;
                    int rtcpPublicPort = rtpSession.LocalEP.RtcpEP.Port;

                    try
                    {
                        UPnP_NAT_Map[] maps = m_pUPnP.GetPortMappings();
                        while (true)
                        {
                            bool conficts = false;
                            // Check that some other application doesn't use that port.
                            foreach (UPnP_NAT_Map map in maps)
                            {
                                // Existing map entry conflicts.
                                if (Convert.ToInt32(map.ExternalPort) == rtpPublicPort || Convert.ToInt32(map.ExternalPort) == rtcpPublicPort)
                                {
                                    rtpPublicPort += 2;
                                    rtcpPublicPort += 2;
                                    conficts = true;

                                    break;
                                }
                            }
                            if (!conficts)
                            {
                                break;
                            }
                        }

                        m_pUPnP.AddPortMapping(true, "LS RTP", "UDP", null, rtpPublicPort, rtpSession.LocalEP.RtpEP, 0);
                        m_pUPnP.AddPortMapping(true, "LS RTCP", "UDP", null, rtcpPublicPort, rtpSession.LocalEP.RtcpEP, 0);

                        IPAddress publicIP = m_pUPnP.GetExternalIPAddress();

                        rtpPublicEP = new IPEndPoint(publicIP, rtpPublicPort);
                        rtcpPublicEP = new IPEndPoint(publicIP, rtcpPublicPort);

                        mediaStream.Tags.Add("upnp_rtp_map", new UPnP_NAT_Map(true, "UDP", "", rtpPublicPort.ToString(), rtpSession.LocalEP.IP.ToString(), rtpSession.LocalEP.RtpEP.Port, "LS RTP", 0));
                        mediaStream.Tags.Add("upnp_rtcp_map", new UPnP_NAT_Map(true, "UDP", "", rtcpPublicPort.ToString(), rtpSession.LocalEP.IP.ToString(), rtpSession.LocalEP.RtcpEP.Port, "LS RTCP", 0));
                    }
                    catch
                    {
                    }
                }
            }

            if (rtpPublicEP != null && rtcpPublicEP != null)
            {
                mediaStream.Port = rtpPublicEP.Port;
                if ((rtpPublicEP.Port + 1) != rtcpPublicEP.Port)
                {
                    // Remove old rport attribute, if any.
                    for (int i = 0; i < mediaStream.Attributes.Count; i++)
                    {
                        if (string.Equals(mediaStream.Attributes[i].Name, "rport", StringComparison.InvariantCultureIgnoreCase))
                        {
                            mediaStream.Attributes.RemoveAt(i);
                            i--;
                        }
                    }
                    mediaStream.Attributes.Add(new SDP_Attribute("rport", rtcpPublicEP.Port.ToString()));
                }
                mediaStream.Connection = new SDP_Connection("IN", "IP4", rtpPublicEP.Address.ToString());

                return true;
            }

            return false;
        }
Exemple #2
0
        private RTP_Address GetRtpTarget(SDP_Message sdp, SDP_MediaDescription mediaStream)
        {
            if (sdp == null)
            {
                throw new ArgumentNullException("sdp");
            }
            if (mediaStream == null)
            {
                throw new ArgumentNullException("mediaStream");
            }

            // We must have SDP global or per media connection info.
            string host = mediaStream.Connection != null ? mediaStream.Connection.Address : null;
            if (host == null)
            {
                host = sdp.Connection.Address != null ? sdp.Connection.Address : null;

                if (host == null)
                {
                    throw new ArgumentException("Invalid SDP message, global or per media 'c'(Connection) attribute is missing.");
                }
            }

            int remoteRtcpPort = mediaStream.Port + 1;
            // Use specified RTCP port, if specified.
            foreach (SDP_Attribute attribute in mediaStream.Attributes)
            {
                if (string.Equals(attribute.Name, "rtcp", StringComparison.InvariantCultureIgnoreCase))
                {
                    remoteRtcpPort = Convert.ToInt32(attribute.Value);

                    break;
                }
            }

            return new RTP_Address(System.Net.Dns.GetHostAddresses(host)[0], mediaStream.Port, remoteRtcpPort);
        }
Exemple #3
0
        private void Call(SIP_t_NameAddress from, SIP_t_NameAddress to)
        {
            if (from == null)
            {
                throw new ArgumentNullException("from");
            }
            if (to == null)
            {
                throw new ArgumentNullException("to");
            }

            #region Setup RTP session

            RTP_MultimediaSession rtpMultimediaSession = new RTP_MultimediaSession(RTP_Utils.GenerateCNAME());
            RTP_Session rtpSession = CreateRtpSession(rtpMultimediaSession);
            // Port search failed.
            if (rtpSession == null)
            {
                throw new Exception("Calling not possible, RTP session failed to allocate IP end points.");
            }

            if (m_IsDebug)
            {
                wfrm_RTP_Debug rtpDebug = new wfrm_RTP_Debug(rtpMultimediaSession);
                rtpDebug.Show();
            }

            #endregion

            #region Create SDP offer

            SDP_Message sdpOffer = new SDP_Message();
            sdpOffer.Version = "0";
            sdpOffer.Origin = new SDP_Origin("-", sdpOffer.GetHashCode(), 1, "IN", "IP4", System.Net.Dns.GetHostAddresses("")[0].ToString());
            sdpOffer.SessionName = "SIP Call";
            sdpOffer.Times.Add(new SDP_Time(0, 0));

            #region Add 1 audio stream

            SDP_MediaDescription mediaStream = new SDP_MediaDescription(SDP_MediaTypes.audio, 0, 1, "RTP/AVP", null);

            rtpSession.NewReceiveStream += delegate(object s, RTP_ReceiveStreamEventArgs e)
            {
                AudioOut_RTP audioOut = new AudioOut_RTP(m_pAudioOutDevice, e.Stream, m_pAudioCodecs);
                audioOut.Start();
                mediaStream.Tags["rtp_audio_out"] = audioOut;
            };

            if (!HandleNAT(mediaStream, rtpSession))
            {
                throw new Exception("Calling not possible, because of NAT or firewall restrictions.");
            }

            foreach (KeyValuePair<int, AudioCodec> entry in m_pAudioCodecs)
            {
                mediaStream.Attributes.Add(new SDP_Attribute("rtpmap", entry.Key + " " + entry.Value.Name + "/" + entry.Value.CompressedAudioFormat.SamplesPerSecond));
                mediaStream.MediaFormats.Add(entry.Key.ToString());
            }
            mediaStream.Attributes.Add(new SDP_Attribute("ptime", "20"));
            mediaStream.Attributes.Add(new SDP_Attribute("sendrecv", ""));
            mediaStream.Tags["rtp_session"] = rtpSession;
            mediaStream.Tags["audio_codecs"] = m_pAudioCodecs;
            sdpOffer.MediaDescriptions.Add(mediaStream);

            #endregion

            #endregion

            // Create INVITE request.
            SIP_Request invite = m_pStack.CreateRequest(SIP_Methods.INVITE, to, from);
            invite.ContentType = "application/sdp";
            invite.Data = sdpOffer.ToByte();

            SIP_RequestSender sender = m_pStack.CreateRequestSender(invite);

            // Create call.
            m_pCall = new SIP_Call(m_pStack, sender, rtpMultimediaSession);
            m_pCall.LocalSDP = sdpOffer;
            m_pCall.StateChanged += new EventHandler(m_pCall_StateChanged);

            bool finalResponseSeen = false;
            List<SIP_Dialog_Invite> earlyDialogs = new List<SIP_Dialog_Invite>();
            sender.ResponseReceived += delegate(object s, SIP_ResponseReceivedEventArgs e)
            {
                // Skip 2xx retransmited response.
                if (finalResponseSeen)
                {
                    return;
                }
                if (e.Response.StatusCode >= 200)
                {
                    finalResponseSeen = true;
                }

                try
                {
                    #region Provisional

                    if (e.Response.StatusCodeType == SIP_StatusCodeType.Provisional)
                    {
                        /* RFC 3261 13.2.2.1.
                            Zero, one or multiple provisional responses may arrive before one or
                            more final responses are received.  Provisional responses for an
                            INVITE request can create "early dialogs".  If a provisional response
                            has a tag in the To field, and if the dialog ID of the response does
                            not match an existing dialog, one is constructed using the procedures
                            defined in Section 12.1.2.
                        */
                        if (e.Response.StatusCode > 100 && e.Response.To.Tag != null)
                        {
                            earlyDialogs.Add((SIP_Dialog_Invite)e.GetOrCreateDialog);
                        }

                        // 180_Ringing.
                        if (e.Response.StatusCode == 180)
                        {
                            //m_pPlayer.Play(ResManager.GetStream("ringing.wav"), 10);

                            // We need BeginInvoke here, otherwise we block client transaction.
                            m_pStatusBar.BeginInvoke(new MethodInvoker(delegate()
                            {
                                m_pStatusBar.Items[0].Text = "Ringing";
                            }));
                        }
                    }

                    #endregion

                    #region Success

                    else if (e.Response.StatusCodeType == SIP_StatusCodeType.Success)
                    {
                        SIP_Dialog dialog = e.GetOrCreateDialog;

                        /* Exit all all other dialogs created by this call (due to forking).
                           That is not defined in RFC but, since UAC can send BYE to early and confirmed dialogs, 
                           all this is 100% valid.
                        */
                        foreach (SIP_Dialog_Invite d in earlyDialogs.ToArray())
                        {
                            if (!d.Equals(dialog))
                            {
                                d.Terminate("Another forking leg accepted.", true);
                            }
                        }

                        m_pCall.InitCalling(dialog, sdpOffer);

                        // Remote-party provided SDP.
                        if (e.Response.ContentType != null && e.Response.ContentType.ToLower().IndexOf("application/sdp") > -1)
                        {
                            try
                            {
                                // SDP offer. We sent offerless INVITE, we need to send SDP answer in ACK request.'
                                if (e.ClientTransaction.Request.ContentType == null || e.ClientTransaction.Request.ContentType.ToLower().IndexOf("application/sdp") == -1)
                                {
                                    // Currently we never do it, so it never happens. This is place holder, if we ever support it.
                                }
                                // SDP answer to our offer.
                                else
                                {
                                    // This method takes care of ACK sending and 2xx response retransmission ACK sending.
                                    HandleAck(m_pCall.Dialog, e.ClientTransaction);

                                    ProcessMediaAnswer(m_pCall, m_pCall.LocalSDP, SDP_Message.Parse(Encoding.UTF8.GetString(e.Response.Data)));
                                }
                            }
                            catch
                            {
                                m_pCall.Terminate("SDP answer parsing/processing failed.");
                            }
                        }
                        else
                        {
                            // If we provided SDP offer, there must be SDP answer.
                            if (e.ClientTransaction.Request.ContentType != null && e.ClientTransaction.Request.ContentType.ToLower().IndexOf("application/sdp") > -1)
                            {
                                m_pCall.Terminate("Invalid 2xx response, required SDP answer is missing.");
                            }
                        }

                        // Stop ringing.
                        m_pPlayer.Stop();
                    }

                    #endregion

                    #region Failure

                    else
                    {
                        /* RFC 3261 13.2.2.3.
                            All early dialogs are considered terminated upon reception of the non-2xx final response.
                        */
                        foreach (SIP_Dialog_Invite dialog in earlyDialogs.ToArray())
                        {
                            dialog.Terminate("All early dialogs are considered terminated upon reception of the non-2xx final response. (RFC 3261 13.2.2.3)", false);
                        }

                        // We need BeginInvoke here, otherwise we block client transaction while message box open.
                        if (m_pCall.State != SIP_CallState.Terminating)
                        {
                            this.BeginInvoke(new MethodInvoker(delegate()
                            {
                                m_pConnect.Image = global::PowerSDR.Properties.Resources.call;
                                connected = false;
                                MessageBox.Show("Calling failed: " + e.Response.StatusCode_ReasonPhrase, "Error:", MessageBoxButtons.OK, MessageBoxIcon.Error);
                            }));
                        }

                        // We need BeginInvoke here, otherwise we block client transaction.
                        m_pStatusBar.BeginInvoke(new MethodInvoker(delegate()
                        {
                            m_pStatusBar.Items[0].Text = "";
                        }));
                        // Stop calling or ringing.
                        m_pPlayer.Stop();

                        // Terminate call.
                        m_pCall.Terminate("Remote party rejected a call.", false);
                    }

                    #endregion
                }
                catch (Exception x)
                {
                    // We need BeginInvoke here, otherwise we block client transaction while message box open.
                    this.BeginInvoke(new MethodInvoker(delegate()
                    {
                        MessageBox.Show("Error: " + x.Message, "Error:", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }));
                }
            };

            m_pStatusBar.Items[0].Text = "Calling";
            m_pStatusBar.Items[1].Text = "00:00:00";
            //m_pPlayer.Play(ResManager.GetStream("calling.wav"), 10);

            // Start calling.
            sender.Start();
        }
Exemple #4
0
        private RTP_StreamMode GetRtpStreamMode(SDP_Message sdp, SDP_MediaDescription media)
        {
            if (sdp == null)
            {
                throw new ArgumentNullException("sdp");
            }
            if (media == null)
            {
                throw new ArgumentNullException("media");
            }

            // Try to get per media stream mode.
            foreach (SDP_Attribute a in media.Attributes)
            {
                if (string.Equals(a.Name, "sendrecv", StringComparison.InvariantCultureIgnoreCase))
                {
                    return RTP_StreamMode.SendReceive;
                }
                else if (string.Equals(a.Name, "sendonly", StringComparison.InvariantCultureIgnoreCase))
                {
                    return RTP_StreamMode.Send;
                }
                else if (string.Equals(a.Name, "recvonly", StringComparison.InvariantCultureIgnoreCase))
                {
                    return RTP_StreamMode.Receive;
                }
                else if (string.Equals(a.Name, "inactive", StringComparison.InvariantCultureIgnoreCase))
                {
                    return RTP_StreamMode.Inactive;
                }
            }

            // No per media stream mode, try to get per session stream mode.
            foreach (SDP_Attribute a in sdp.Attributes)
            {
                if (string.Equals(a.Name, "sendrecv", StringComparison.InvariantCultureIgnoreCase))
                {
                    return RTP_StreamMode.SendReceive;
                }
                else if (string.Equals(a.Name, "sendonly", StringComparison.InvariantCultureIgnoreCase))
                {
                    return RTP_StreamMode.Send;
                }
                else if (string.Equals(a.Name, "recvonly", StringComparison.InvariantCultureIgnoreCase))
                {
                    return RTP_StreamMode.Receive;
                }
                else if (string.Equals(a.Name, "inactive", StringComparison.InvariantCultureIgnoreCase))
                {
                    return RTP_StreamMode.Inactive;
                }
            }

            return RTP_StreamMode.SendReceive;
        }
Exemple #5
0
        private string GetSdpHost(SDP_Message sdp, SDP_MediaDescription mediaStream)
        {
            if (sdp == null)
            {
                throw new ArgumentNullException("sdp");
            }
            if (mediaStream == null)
            {
                throw new ArgumentNullException("mediaStream");
            }

            // We must have SDP global or per media connection info.
            string host = mediaStream.Connection != null ? mediaStream.Connection.Address : null;
            if (host == null)
            {
                host = sdp.Connection.Address != null ? sdp.Connection.Address : null;

                if (host == null)
                {
                    throw new ArgumentException("Invalid SDP message, global or per media 'c'(Connection) attribute is missing.");
                }
            }

            return host;
        }
Exemple #6
0
        private Dictionary<int, AudioCodec> GetOurSupportedAudioCodecs(SDP_MediaDescription media)
        {
            if (media == null)
            {
                throw new ArgumentNullException("media");
            }

            Dictionary<int, AudioCodec> codecs = new Dictionary<int, AudioCodec>();

            // Check for IANA registered payload. Custom range is 96-127 and always must have rtpmap attribute.
            foreach (string format in media.MediaFormats)
            {
                int payload = Convert.ToInt32(format);
                if (payload < 96 && m_pAudioCodecs.ContainsKey(payload))
                {
                    if (!codecs.ContainsKey(payload))
                    {
                        codecs.Add(payload, m_pAudioCodecs[payload]);
                    }
                }
            }

            // Check rtpmap payloads.
            foreach (SDP_Attribute a in media.Attributes)
            {
                if (string.Equals(a.Name, "rtpmap", StringComparison.InvariantCultureIgnoreCase))
                {
                    // Example: 0 PCMU/8000
                    string[] parts = a.Value.Split(' ');
                    int payload = Convert.ToInt32(parts[0]);
                    string codecName = parts[1].Split('/')[0];

                    foreach (AudioCodec codec in m_pAudioCodecs.Values)
                    {
                        if (string.Equals(codec.Name, codecName, StringComparison.InvariantCultureIgnoreCase))
                        {
                            if (!codecs.ContainsKey(payload))
                            {
                                codecs.Add(payload, codec);
                            }
                        }
                    }
                }
            }

            return codecs;
        }
Exemple #7
0
        private bool CanSupportMedia(SDP_MediaDescription media)
        {
            if (media == null)
            {
                throw new ArgumentNullException("media");
            }

            if (!string.Equals(media.MediaType, SDP_MediaTypes.audio, StringComparison.InvariantCultureIgnoreCase))
            {
                return false;
            }
            if (!string.Equals(media.Protocol, "RTP/AVP", StringComparison.InvariantCultureIgnoreCase))
            {
                return false;
            }

            if (GetOurSupportedAudioCodecs(media).Count > 0)
            {
                return true;
            }

            return false;
        }
Exemple #8
0
        private void ProcessMediaOffer(SIP_Dialog dialog, SIP_ServerTransaction transaction, RTP_MultimediaSession rtpMultimediaSession, SDP_Message offer, SDP_Message localSDP)
        {
            if (dialog == null)
            {
                throw new ArgumentNullException("dialog");
            }
            if (transaction == null)
            {
                throw new ArgumentNullException("transaction");
            }
            if (rtpMultimediaSession == null)
            {
                throw new ArgumentNullException("rtpMultimediaSession");
            }
            if (offer == null)
            {
                throw new ArgumentNullException("offer");
            }
            if (localSDP == null)
            {
                throw new ArgumentNullException("localSDP");
            }

            try
            {
                #region SDP basic validation

                // Version field must exist.
                if (offer.Version == null)
                {
                    transaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x500_Server_Internal_Error + ": Invalid SDP answer: Required 'v'(Protocol Version) field is missing.", transaction.Request));

                    return;
                }

                // Origin field must exist.
                if (offer.Origin == null)
                {
                    transaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x500_Server_Internal_Error + ": Invalid SDP answer: Required 'o'(Origin) field is missing.", transaction.Request));

                    return;
                }

                // Session Name field.

                // Check That global 'c' connection attribute exists or otherwise each enabled media stream must contain one.
                if (offer.Connection == null)
                {
                    for (int i = 0; i < offer.MediaDescriptions.Count; i++)
                    {
                        if (offer.MediaDescriptions[i].Connection == null)
                        {
                            transaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x500_Server_Internal_Error + ": Invalid SDP answer: Global or per media stream no: " + i + " 'c'(Connection) attribute is missing.", transaction.Request));

                            return;
                        }
                    }
                }

                #endregion

                // Re-INVITE media streams count must be >= current SDP streams.
                if (localSDP.MediaDescriptions.Count > offer.MediaDescriptions.Count)
                {
                    transaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x500_Server_Internal_Error + ": re-INVITE SDP offer media stream count must be >= current session stream count.", transaction.Request));

                    return;
                }

                bool audioAccepted = false;
                // Process media streams info.
                for (int i = 0; i < offer.MediaDescriptions.Count; i++)
                {
                    SDP_MediaDescription offerMedia = offer.MediaDescriptions[i];
                    SDP_MediaDescription answerMedia = (localSDP.MediaDescriptions.Count > i ? localSDP.MediaDescriptions[i] : null);

                    // Disabled stream.
                    if (offerMedia.Port == 0)
                    {
                        // Remote-party offered new disabled stream.
                        if (answerMedia == null)
                        {
                            // Just copy offer media stream data to answer and set port to zero.
                            localSDP.MediaDescriptions.Add(offerMedia);
                            localSDP.MediaDescriptions[i].Port = 0;
                        }
                        // Existing disabled stream or remote party disabled it.
                        else
                        {
                            answerMedia.Port = 0;

                            #region Cleanup active RTP stream and it's resources, if it exists

                            // Dispose existing RTP session.
                            if (answerMedia.Tags.ContainsKey("rtp_session"))
                            {
                                ((RTP_Session)offerMedia.Tags["rtp_session"]).Dispose();
                                answerMedia.Tags.Remove("rtp_session");
                            }

                            // Release UPnPports if any.
                            if (answerMedia.Tags.ContainsKey("upnp_rtp_map"))
                            {
                                try
                                {
                                    m_pUPnP.DeletePortMapping((UPnP_NAT_Map)answerMedia.Tags["upnp_rtp_map"]);
                                }
                                catch
                                {
                                }
                                answerMedia.Tags.Remove("upnp_rtp_map");
                            }
                            if (answerMedia.Tags.ContainsKey("upnp_rtcp_map"))
                            {
                                try
                                {
                                    m_pUPnP.DeletePortMapping((UPnP_NAT_Map)answerMedia.Tags["upnp_rtcp_map"]);
                                }
                                catch
                                {
                                }
                                answerMedia.Tags.Remove("upnp_rtcp_map");
                            }

                            #endregion
                        }
                    }
                    // Remote-party wants to communicate with this stream.
                    else
                    {
                        // See if we can support this stream.
                        if (!audioAccepted && CanSupportMedia(offerMedia))
                        {
                            // New stream.
                            if (answerMedia == null)
                            {
                                answerMedia = new SDP_MediaDescription(SDP_MediaTypes.audio, 0, 2, "RTP/AVP", null);
                                localSDP.MediaDescriptions.Add(answerMedia);
                            }

                            #region Build audio codec map with codecs which we support

                            Dictionary<int, AudioCodec> audioCodecs = GetOurSupportedAudioCodecs(offerMedia);
                            answerMedia.MediaFormats.Clear();
                            answerMedia.Attributes.Clear();
                            foreach (KeyValuePair<int, AudioCodec> entry in audioCodecs)
                            {
                                answerMedia.Attributes.Add(new SDP_Attribute("rtpmap", entry.Key + " " + entry.Value.Name + "/" + entry.Value.CompressedAudioFormat.SamplesPerSecond));
                                answerMedia.MediaFormats.Add(entry.Key.ToString());
                            }
                            answerMedia.Attributes.Add(new SDP_Attribute("ptime", "20"));
                            answerMedia.Tags["audio_codecs"] = audioCodecs;

                            #endregion

                            #region Create/modify RTP session

                            // RTP session doesn't exist, create it.
                            if (!answerMedia.Tags.ContainsKey("rtp_session"))
                            {
                                RTP_Session rtpSess = CreateRtpSession(rtpMultimediaSession);
                                // RTP session creation failed,disable this stream.
                                if (rtpSess == null)
                                {
                                    answerMedia.Port = 0;

                                    break;
                                }
                                answerMedia.Tags.Add("rtp_session", rtpSess);

                                rtpSess.NewReceiveStream += delegate(object s, RTP_ReceiveStreamEventArgs e)
                                {
                                    if (answerMedia.Tags.ContainsKey("rtp_audio_out"))
                                    {
                                        ((AudioOut_RTP)answerMedia.Tags["rtp_audio_out"]).Dispose();
                                    }

                                    AudioOut_RTP audioOut = new AudioOut_RTP(m_pAudioOutDevice, e.Stream, audioCodecs);
                                    audioOut.Start();
                                    answerMedia.Tags["rtp_audio_out"] = audioOut;
                                };

                                // NAT
                                if (!HandleNAT(answerMedia, rtpSess))
                                {
                                    // NAT handling failed,disable this stream.
                                    answerMedia.Port = 0;

                                    break;
                                }
                            }

                            RTP_StreamMode offerStreamMode = GetRtpStreamMode(offer, offerMedia);
                            if (offerStreamMode == RTP_StreamMode.Inactive)
                            {
                                answerMedia.SetStreamMode("inactive");
                            }
                            else if (offerStreamMode == RTP_StreamMode.Receive)
                            {
                                answerMedia.SetStreamMode("sendonly");
                            }
                            else if (offerStreamMode == RTP_StreamMode.Send)
                            {
                                answerMedia.SetStreamMode("recvonly");
                            }
                            else if (offerStreamMode == RTP_StreamMode.SendReceive)
                            {
                                answerMedia.SetStreamMode("sendrecv");
                            }

                            RTP_Session rtpSession = (RTP_Session)answerMedia.Tags["rtp_session"];
                            rtpSession.Payload = Convert.ToInt32(answerMedia.MediaFormats[0]);
                            rtpSession.StreamMode = GetRtpStreamMode(localSDP, answerMedia);
                            rtpSession.RemoveTargets();
                            if (GetSdpHost(offer, offerMedia) != "0.0.0.0")
                            {
                                rtpSession.AddTarget(GetRtpTarget(offer, offerMedia));
                            }
                            rtpSession.Start();

                            #endregion

                            #region Create/modify audio-in source

                            if (!answerMedia.Tags.ContainsKey("rtp_audio_in"))
                            {
                                AudioIn_RTP rtpAudioIn = new AudioIn_RTP(m_pAudioInDevice, 20, audioCodecs, rtpSession.CreateSendStream());
                                rtpAudioIn.Start();
                                answerMedia.Tags.Add("rtp_audio_in", rtpAudioIn);
                            }
                            else
                            {
                                ((AudioIn_RTP)answerMedia.Tags["rtp_audio_in"]).AudioCodecs = audioCodecs;
                            }

                            #endregion

                            audioAccepted = true;
                        }
                        // We don't accept this stream, so disable it.
                        else
                        {
                            // Just copy offer media stream data to answer and set port to zero.

                            // Delete exisiting media stream.
                            if (answerMedia != null)
                            {
                                localSDP.MediaDescriptions.RemoveAt(i);
                            }
                            localSDP.MediaDescriptions.Add(offerMedia);
                            localSDP.MediaDescriptions[i].Port = 0;
                        }
                    }
                }

                #region Create and send 2xx response

                SIP_Response response = m_pStack.CreateResponse(SIP_ResponseCodes.x200_Ok, transaction.Request, transaction.Flow);
                //response.Contact = SIP stack will allocate it as needed;
                response.ContentType = "application/sdp";
                response.Data = localSDP.ToByte();

                transaction.SendResponse(response);

                // Start retransmitting 2xx response, while ACK receives.
                Handle2xx(dialog, transaction);

                // REMOVE ME: 27.11.2010
                // Start retransmitting 2xx response, while ACK receives.
                //m_pInvite2xxMgr.Add(dialog,transaction);

                #endregion
            }
            catch (Exception x)
            {
                transaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x500_Server_Internal_Error + ": " + x.Message, transaction.Request));
            }
        }
Exemple #9
0
        /// <summary>
        /// Parses media from "m" SDP message field.
        /// </summary>
        /// <param name="mValue">"m" SDP message field.</param>
        /// <returns></returns>
        public static SDP_MediaDescription Parse(string mValue)
        {
            SDP_MediaDescription media = new SDP_MediaDescription();

            // m=<media> <port>/<number of ports> <proto> <fmt> ...
            StringReader r = new StringReader(mValue);
            r.QuotedReadToDelimiter('=');

            //--- <media> ------------------------------------------------------------
            string word = r.ReadWord();
            if(word == null){
                throw new Exception("SDP message \"m\" field <media> value is missing !");
            }
            media.m_MediaType = word;

            //--- <port>/<number of ports> -------------------------------------------
            word = r.ReadWord();
            if(word == null){
                throw new Exception("SDP message \"m\" field <port> value is missing !");
            }
            if(word.IndexOf('/') > -1){
                string[] port_nPorts = word.Split('/');
                media.m_Port = Convert.ToInt32(port_nPorts[0]);
                media.m_NumberOfPorts = Convert.ToInt32(port_nPorts[1]);
            }
            else{
                media.m_Port = Convert.ToInt32(word);
                media.m_NumberOfPorts = 1;
            }

            //--- <proto> --------------------------------------------------------------
            word = r.ReadWord();
            if(word == null){
                throw new Exception("SDP message \"m\" field <proto> value is missing !");
            }
            media.m_Protocol = word;
            
            //--- <fmt> ----------------------------------------------------------------
            word = r.ReadWord();
            while(word != null){
                media.MediaFormats.Add(word);

                word = r.ReadWord();
            }

            return media;
        }
        /// <summary>
        /// Parses SDP from raw data.
        /// </summary>
        /// <param name="data">Raw SDP data.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>data</b> is null reference.</exception>
        public static SDP_Message Parse(string data)
        {
            if (data == null)
            {
                throw new ArgumentNullException("data");
            }

            SDP_Message sdp = new SDP_Message();

            System.IO.StringReader r = new System.IO.StringReader(data);

            string line = r.ReadLine();

            //--- Read global fields ---------------------------------------------
            while (line != null)
            {
                line = line.Trim();

                // We reached to media descriptions
                if (line.ToLower().StartsWith("m"))
                {
                    /*
                     *  m=  (media name and transport address)
                     *  i=* (media title)
                     *  c=* (connection information -- optional if included at session level)
                     *  b=* (zero or more bandwidth information lines)
                     *  k=* (encryption key)
                     *  a=* (zero or more media attribute lines)
                     */

                    SDP_MediaDescription media = SDP_MediaDescription.Parse(line);
                    sdp.m_pMediaDescriptions.Add(media);
                    line = r.ReadLine();
                    // Pasrse media fields and attributes
                    while (line != null)
                    {
                        line = line.Trim();

                        // Next media descrition, just stop active media description parsing,
                        // fall through main while, allow next while loop to process it.
                        if (line.ToLower().StartsWith("m"))
                        {
                            break;
                        }
                        // i media title
                        else if (line.ToLower().StartsWith("i"))
                        {
                            media.Information = line.Split(new char[] { '=' }, 2)[1].Trim();
                        }
                        // c connection information
                        else if (line.ToLower().StartsWith("c"))
                        {
                            media.Connection = SDP_Connection.Parse(line);
                        }
                        // a Attributes
                        else if (line.ToLower().StartsWith("a"))
                        {
                            media.Attributes.Add(SDP_Attribute.Parse(line));
                        }

                        line = r.ReadLine();
                    }

                    if (line == null)
                    {
                        break;
                    }
                    else
                    {
                        continue;
                    }
                }
                // v Protocol Version
                else if (line.ToLower().StartsWith("v"))
                {
                    sdp.Version = line.Split(new char[] { '=' }, 2)[1].Trim();
                }
                // o Origin
                else if (line.ToLower().StartsWith("o"))
                {
                    sdp.Origin = SDP_Origin.Parse(line);
                }
                // s Session Name
                else if (line.ToLower().StartsWith("s"))
                {
                    sdp.SessionName = line.Split(new char[] { '=' }, 2)[1].Trim();
                }
                // i Session Information
                else if (line.ToLower().StartsWith("i"))
                {
                    sdp.SessionDescription = line.Split(new char[] { '=' }, 2)[1].Trim();
                }
                // u URI
                else if (line.ToLower().StartsWith("u"))
                {
                    sdp.Uri = line.Split(new char[] { '=' }, 2)[1].Trim();
                }
                // c Connection Data
                else if (line.ToLower().StartsWith("c"))
                {
                    sdp.Connection = SDP_Connection.Parse(line);
                }
                // t Timing
                else if (line.ToLower().StartsWith("t"))
                {
                    sdp.Times.Add(SDP_Time.Parse(line));
                }
                // a Attributes
                else if (line.ToLower().StartsWith("a"))
                {
                    sdp.Attributes.Add(SDP_Attribute.Parse(line));
                }

                line = r.ReadLine().Trim();
            }

            return(sdp);
        }