Exemplo n.º 1
0
        private void Handle2xx(SIP_Dialog dialog, SIP_ServerTransaction transaction)
        {
            if (dialog == null)
            {
                throw new ArgumentNullException("dialog");
            }
            if (transaction == null)
            {
                throw new ArgumentException("transaction");
            }

            /* RFC 6026 8.1.
                Once the response has been constructed, it is passed to the INVITE
                server transaction.  In order to ensure reliable end-to-end
                transport of the response, it is necessary to periodically pass
                the response directly to the transport until the ACK arrives.  The
                2xx response is passed to the transport with an interval that
                starts at T1 seconds and doubles for each retransmission until it
                reaches T2 seconds (T1 and T2 are defined in Section 17).
                Response retransmissions cease when an ACK request for the
                response is received.  This is independent of whatever transport
                protocols are used to send the response.
             
                If the server retransmits the 2xx response for 64*T1 seconds without
                receiving an ACK, the dialog is confirmed, but the session SHOULD be
                terminated.  This is accomplished with a BYE, as described in Section
                15.
              
                 T1 - 500
                 T2 - 4000
            */

            TimerEx timer = null;

            EventHandler<SIP_RequestReceivedEventArgs> callback = delegate(object s1, SIP_RequestReceivedEventArgs e)
            {
                try
                {
                    if (e.Request.RequestLine.Method == SIP_Methods.ACK)
                    {
                        // ACK for INVITE 2xx response received, stop retransmitting INVITE 2xx response.
                        if (transaction.Request.CSeq.SequenceNumber == e.Request.CSeq.SequenceNumber)
                        {
                            if (timer != null)
                            {
                                timer.Dispose();
                            }
                        }
                    }
                }
                catch
                {
                    // We don't care about errors here.
                }
            };
            dialog.RequestReceived += callback;

            // Create timer and sart retransmitting INVITE 2xx response.
            timer = new TimerEx(500);
            timer.AutoReset = false;
            timer.Elapsed += delegate(object s, System.Timers.ElapsedEventArgs e)
            {
                try
                {
                    lock (transaction.SyncRoot)
                    {
                        if (transaction.State == SIP_TransactionState.Accpeted)
                        {
                            transaction.SendResponse(transaction.FinalResponse);
                        }
                        else
                        {
                            timer.Dispose();

                            return;
                        }
                    }

                    timer.Interval = Math.Min(timer.Interval * 2, 4000);
                    timer.Enabled = true;
                }
                catch
                {
                    // We don't care about errors here.
                }
            };
            timer.Disposed += delegate(object s1, EventArgs e1)
            {
                try
                {
                    dialog.RequestReceived -= callback;
                }
                catch
                {
                    // We don't care about errors here.
                }
            };
            timer.Enabled = true;
        }
Exemplo n.º 2
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));
            }
        }