public Answer ( string contentType, string body, |
||
contentType | string | |
body | string | |
answeredDialogue | ||
transferMode | SIPDialogueTransferModesEnum | |
return |
/// <summary> /// Answers the call request contained in the user agent server parameter. Note the /// <see cref="AcceptCall(SIPRequest)"/> method should be used to create the user agent server. /// Any existing call will be hungup. /// </summary> /// <param name="uas">The user agent server holding the pending call to answer.</param> /// <param name="mediaSession">The media session used for this call</param> /// <param name="customHeaders">Custom SIP-Headers to use in Answer.</param> public async Task Answer(SIPServerUserAgent uas, IMediaSession mediaSession, string[] customHeaders) { // This call is now taking over any existing call. if (IsCallActive) { Hangup(); } else if (uas.IsCancelled) { logger.LogDebug("The incoming call has been cancelled."); mediaSession?.Close("call cancelled"); } else { m_cts = new CancellationTokenSource(); var sipRequest = uas.ClientTransaction.TransactionRequest; MediaSession = mediaSession; MediaSession.OnRtpEvent += OnRemoteRtpEvent; //MediaSession.OnRtpClosed += (reason) => Hangup(); MediaSession.OnRtpClosed += (reason) => { if (!MediaSession.IsClosed) { logger.LogWarning($"RTP channel was closed with reason {reason}."); } }; SDP remoteSdp = SDP.ParseSDPDescription(sipRequest.Body); MediaSession.setRemoteDescription(new RTCSessionDescription { sdp = remoteSdp, type = RTCSdpType.offer });; var sdpAnswer = await MediaSession.createAnswer(null).ConfigureAwait(false); MediaSession.setLocalDescription(new RTCSessionDescription { sdp = sdpAnswer, type = RTCSdpType.answer }); await MediaSession.Start().ConfigureAwait(false); m_uas = uas; m_uas.Answer(m_sdpContentType, sdpAnswer.ToString(), null, SIPDialogueTransferModesEnum.Default, customHeaders); Dialogue.DialogueState = SIPDialogueStateEnum.Confirmed; } }
/// <summary> /// Answers the call request contained in the user agent server parameter. Note the /// <see cref="AcceptCall(SIPRequest)"/> method should be used to create the user agent server. /// Any existing call will be hungup. /// </summary> /// <param name="uas">The user agent server holding the pending call to answer.</param> /// <param name="mediaSession">The media session used for this call</param> public async Task Answer(SIPServerUserAgent uas, IMediaSession mediaSession) { // This call is now taking over any existing call. if (IsCallActive) { Hangup(); } var sipRequest = uas.ClientTransaction.TransactionRequest; MediaSession = mediaSession; MediaSession.SessionMediaChanged += MediaSessionOnSessionMediaChanged; MediaSession.OnRtpClosed += (reason) => Hangup(); var sdpAnswer = await MediaSession.AnswerOffer(sipRequest.Body).ConfigureAwait(false); m_uas = uas; m_uas.Answer(m_sdpContentType, sdpAnswer, null, SIPDialogueTransferModesEnum.Default); Dialogue.DialogueState = SIPDialogueStateEnum.Confirmed; }
/// <summary> /// Answers the call request contained in the user agent server parameter. Note the /// <see cref="AcceptCall(SIPRequest)"/> method should be used to create the user agent server. /// Any existing call will be hungup. /// </summary> /// <param name="uas">The user agent server holding the pending call to answer.</param> /// <param name="mediaSession">The media session used for this call</param> /// <param name="customHeaders">Custom SIP-Headers to use in Answer.</param> public async Task Answer(SIPServerUserAgent uas, IMediaSession mediaSession, string[] customHeaders) { // This call is now taking over any existing call. if (IsCallActive) { Hangup(); } else if (uas.IsCancelled) { logger.LogDebug("The incoming call has been cancelled."); mediaSession?.Close("call cancelled"); } else { m_cts = new CancellationTokenSource(); var sipRequest = uas.ClientTransaction.TransactionRequest; MediaSession = mediaSession; MediaSession.OnRtpEvent += OnRemoteRtpEvent; //MediaSession.OnRtpClosed += (reason) => Hangup(); MediaSession.OnRtpClosed += (reason) => { if (!MediaSession.IsClosed) { logger.LogWarning($"RTP channel was closed with reason {reason}."); } }; string sdp = null; if (!String.IsNullOrEmpty(sipRequest.Body)) { // The SDP offer was included in the INVITE request. SDP remoteSdp = SDP.ParseSDPDescription(sipRequest.Body); MediaSession.setRemoteDescription(new RTCSessionDescription { sdp = remoteSdp, type = RTCSdpType.offer }); var sdpAnswer = await MediaSession.createAnswer(null).ConfigureAwait(false); if (sdpAnswer == null) { logger.LogWarning($"Could not generate an SDP answer."); m_uas.Reject(SIPResponseStatusCodesEnum.NotAcceptable, null); return; } else { MediaSession.setLocalDescription(new RTCSessionDescription { sdp = sdpAnswer, type = RTCSdpType.answer }); sdp = sdpAnswer.ToString(); } } else { // No SDP offer was included in the INVITE request need to wait for the ACK. RTCOfferOptions offerOptions = new RTCOfferOptions { RemoteSignallingAddress = sipRequest.RemoteSIPEndPoint.GetIPEndPoint().Address }; var sdpOffer = await MediaSession.createOffer(offerOptions).ConfigureAwait(false); if (sdpOffer == null) { // This shouldn't occur unless we're unable to create an audio/video track. logger.LogWarning($"Could not generate an SDP answer."); m_uas.Reject(SIPResponseStatusCodesEnum.NotAcceptable, null); return; } else { MediaSession.setLocalDescription(new RTCSessionDescription { sdp = sdpOffer, type = RTCSdpType.offer }); sdp = sdpOffer.ToString(); } } m_uas = uas; // In cases where the initial INVITE did not contain an SDP offer the action sequence is: // - INVITE with no SDP offer received, // - Reply with OK and an SDP offer, // - Wait for ACK with SDP answer. TaskCompletionSource <SIPDialogue> dialogueCreatedTcs = new TaskCompletionSource <SIPDialogue>(TaskCreationOptions.RunContinuationsAsynchronously); m_uas.OnDialogueCreated += (dialogue) => dialogueCreatedTcs.TrySetResult(dialogue); m_uas.Answer(m_sdpContentType, sdp, null, SIPDialogueTransferModesEnum.Default, customHeaders); await Task.WhenAny(dialogueCreatedTcs.Task, Task.Delay(WAIT_DIALOG_TIMEOUT)).ConfigureAwait(false); if (Dialogue != null) { if (MediaSession.remoteDescription == null || MediaSession.remoteDescription.sdp == null) { // If the initial INVITE did not contain an offer then the remote description will not yet be set. var remoteSDP = SDP.ParseSDPDescription(Dialogue.RemoteSDP); MediaSession.setRemoteDescription(new RTCSessionDescription { sdp = remoteSDP, type = RTCSdpType.answer }); } Dialogue.DialogueState = SIPDialogueStateEnum.Confirmed; await MediaSession.Start().ConfigureAwait(false); } else { logger.LogWarning("The attempt to answer a call failed as the dialog was not created. The likely cause is the ACK not being received in time."); MediaSession.Close("dialog creation failed"); Hangup(); } } }
private static void SIPTransportRequestReceived(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPRequest sipRequest) { if (sipRequest.Method == SIPMethodsEnum.BYE) { var rtpJob = (from job in m_rtpJobs.Values where job.UAS.CallRequest.Header.CallId == sipRequest.Header.CallId select job).FirstOrDefault(); if (rtpJob != null) { rtpJob.Stop(); // Call has been hungup by remote end. Console.WriteLine("Call hungup by client: " + localSIPEndPoint + "<-" + remoteEndPoint + " " + sipRequest.URI.ToString() + ".\n"); Publish(rtpJob.QueueName, "BYE request received from " + remoteEndPoint + " for " + sipRequest.URI.ToString() + "."); //Console.WriteLine("Request Received " + localSIPEndPoint + "<-" + remoteEndPoint + "\n" + sipRequest.ToString()); //m_uas.SIPDialogue.Hangup(m_sipTransport, null); SIPResponse okResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Ok, null); m_sipTransport.SendResponse(okResponse); } else { Console.WriteLine("Unmatched BYE request received for " + sipRequest.URI.ToString() + ".\n"); SIPResponse noCallLegResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.CallLegTransactionDoesNotExist, null); m_sipTransport.SendResponse(noCallLegResponse); } } else if (sipRequest.Method == SIPMethodsEnum.INVITE) { Console.WriteLine("Incoming call request: " + localSIPEndPoint + "<-" + remoteEndPoint + " " + sipRequest.URI.ToString() + ".\n"); Publish(sipRequest.URI.User, "INVITE request received from " + remoteEndPoint + " for " + sipRequest.URI.ToString() + "."); Console.WriteLine(sipRequest.Body); SIPPacketMangler.MangleSIPRequest(SIPMonitorServerTypesEnum.Unknown, sipRequest, null, LogTraceMessage); UASInviteTransaction uasTransaction = m_sipTransport.CreateUASTransaction(sipRequest, remoteEndPoint, localSIPEndPoint, null); var uas = new SIPServerUserAgent(m_sipTransport, null, null, null, SIPCallDirection.In, null, null, LogTraceMessage, uasTransaction); uas.CallCancelled += UASCallCancelled; RTPDiagnosticsJob rtpJob = new RTPDiagnosticsJob(m_rtpListenIPAddress, m_publicIPAddress, uas, sipRequest); string sdpAddress = SDP.GetSDPRTPEndPoint(sipRequest.Body).Address.ToString(); // Only mangle if there is something to change. For example the server could be on the same private subnet in which case it can't help. IPEndPoint expectedRTPEndPoint = new IPEndPoint(rtpJob.RemoteRTPEndPoint.Address, rtpJob.RemoteRTPEndPoint.Port); if (IPSocket.IsPrivateAddress(rtpJob.RemoteRTPEndPoint.Address.ToString())) { expectedRTPEndPoint.Address = remoteEndPoint.Address; } Publish(sipRequest.URI.User, "Advertised RTP remote socket " + rtpJob.RemoteRTPEndPoint + ", expecting from " + expectedRTPEndPoint + "."); m_rtpJobs.Add(rtpJob.RTPListenEndPoint.Port, rtpJob); //ThreadPool.QueueUserWorkItem(delegate { StartRTPListener(rtpJob); }); Console.WriteLine(rtpJob.LocalSDP.ToString()); uas.Answer("application/sdp", rtpJob.LocalSDP.ToString(), CallProperties.CreateNewTag(), null, SIPDialogueTransferModesEnum.NotAllowed); var hangupTimer = new Timer(delegate { if (!rtpJob.StopJob) { if (uas != null && uas.SIPDialogue != null) { if(rtpJob.RTPPacketReceived && !rtpJob.ErrorOnRTPSend) { Publish(sipRequest.URI.User, "Test completed. There were no RTP send or receive errors."); } else if (!rtpJob.RTPPacketReceived) { Publish(sipRequest.URI.User, "Test completed. An error was identified, no RTP packets were received."); } else { Publish(sipRequest.URI.User, "Test completed. An error was identified, there was a problem when attempting to send an RTP packet."); } rtpJob.Stop(); uas.SIPDialogue.Hangup(m_sipTransport, null); } } }, null, HANGUP_TIMEOUT, Timeout.Infinite); } else if (sipRequest.Method == SIPMethodsEnum.CANCEL) { UASInviteTransaction inviteTransaction = (UASInviteTransaction)m_sipTransport.GetTransaction(SIPTransaction.GetRequestTransactionId(sipRequest.Header.Vias.TopViaHeader.Branch, SIPMethodsEnum.INVITE)); if (inviteTransaction != null) { Console.WriteLine("Matching CANCEL request received " + sipRequest.URI.ToString() + ".\n"); Publish(sipRequest.URI.User, "CANCEL request received from " + remoteEndPoint + " for " + sipRequest.URI.ToString() + "."); SIPCancelTransaction cancelTransaction = m_sipTransport.CreateCancelTransaction(sipRequest, remoteEndPoint, localSIPEndPoint, inviteTransaction); cancelTransaction.GotRequest(localSIPEndPoint, remoteEndPoint, sipRequest); } else { Console.WriteLine("No matching transaction was found for CANCEL to " + sipRequest.URI.ToString() + ".\n"); SIPResponse noCallLegResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.CallLegTransactionDoesNotExist, null); m_sipTransport.SendResponse(noCallLegResponse); } } else { Console.WriteLine("SIP " + sipRequest.Method + " request received but no processing has been set up for it, rejecting.\n"); Publish(sipRequest.URI.User, sipRequest.Method + " request received from " + remoteEndPoint + " for " + sipRequest.URI.ToString() + "."); SIPResponse notAllowedResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.MethodNotAllowed, null); m_sipTransport.SendResponse(notAllowedResponse); } }
/// <summary> /// Answers the call request contained in the user agent server parameter. Note the /// <see cref="AcceptCall(SIPRequest)"/> method should be used to create the user agent server. /// Any existing call will be hungup. /// </summary> /// <param name="uas">The user agent server holding the pending call to answer.</param> /// <param name="mediaSession">The media session used for this call</param> /// <param name="customHeaders">Custom SIP-Headers to use in Answer.</param> /// <returns>True if the call was successfully answered or false if there was a problem /// such as incompatible codecs.</returns> public async Task <bool> Answer(SIPServerUserAgent uas, IMediaSession mediaSession, string[] customHeaders) { // This call is now taking over any existing call. if (IsCallActive) { Hangup(); } if (uas.IsCancelled) { logger.LogDebug("The incoming call has been cancelled."); mediaSession?.Close("call cancelled"); return(false); } else { m_cts = new CancellationTokenSource(); var sipRequest = uas.ClientTransaction.TransactionRequest; MediaSession = mediaSession; MediaSession.OnRtpEvent += OnRemoteRtpEvent; MediaSession.OnRtpClosed += (reason) => { if (!MediaSession.IsClosed) { logger.LogWarning($"RTP channel was closed with reason {reason}."); } }; string sdp = null; if (!String.IsNullOrEmpty(sipRequest.Body)) { // The SDP offer was included in the INVITE request. SDP remoteSdp = SDP.ParseSDPDescription(sipRequest.Body); var setRemoteResult = MediaSession.SetRemoteDescription(remoteSdp); if (setRemoteResult != SetDescriptionResultEnum.OK) { logger.LogWarning($"Error setting remote description from INVITE {setRemoteResult}."); uas.Reject(SIPResponseStatusCodesEnum.NotAcceptable, setRemoteResult.ToString()); MediaSession.Close("sdp offer not acceptable"); Hangup(); return(false); } else { var sdpAnswer = MediaSession.CreateAnswer(null); sdp = sdpAnswer.ToString(); } } else { // No SDP offer was included in the INVITE request need to wait for the ACK. var sdpAnnounceAddress = NetServices.GetLocalAddressForRemote(sipRequest.RemoteSIPEndPoint.GetIPEndPoint().Address); var sdpOffer = MediaSession.CreateOffer(sdpAnnounceAddress); sdp = sdpOffer.ToString(); } m_uas = uas; // In cases where the initial INVITE did not contain an SDP offer the action sequence is: // - INVITE with no SDP offer received, // - Reply with OK and an SDP offer, // - Wait for ACK with SDP answer. TaskCompletionSource <SIPDialogue> dialogueCreatedTcs = new TaskCompletionSource <SIPDialogue>(TaskCreationOptions.RunContinuationsAsynchronously); m_uas.OnDialogueCreated += (dialogue) => dialogueCreatedTcs.TrySetResult(dialogue); m_uas.Answer(m_sdpContentType, sdp, null, SIPDialogueTransferModesEnum.Default, customHeaders); await Task.WhenAny(dialogueCreatedTcs.Task, Task.Delay(WAIT_DIALOG_TIMEOUT)).ConfigureAwait(false); if (Dialogue != null) { if (MediaSession.RemoteDescription == null) { // If the initial INVITE did not contain an offer then the remote description will not yet be set. var remoteSDP = SDP.ParseSDPDescription(Dialogue.RemoteSDP); var setRemoteResult = MediaSession.SetRemoteDescription(remoteSDP); if (setRemoteResult != SetDescriptionResultEnum.OK) { // Failed to set the remote SDP from the ACK request. Only option is to hangup. logger.LogWarning($"Error setting remote description from ACK {setRemoteResult}."); MediaSession.Close(setRemoteResult.ToString()); Hangup(); return(false); } else { // SDP from the ACK request was accepted. Start the RTP session. Dialogue.DialogueState = SIPDialogueStateEnum.Confirmed; await MediaSession.Start().ConfigureAwait(false); return(true); } } else { Dialogue.DialogueState = SIPDialogueStateEnum.Confirmed; await MediaSession.Start().ConfigureAwait(false); return(true); } } else { logger.LogWarning("The attempt to answer a call failed as the dialog was not created. The likely cause is the ACK not being received in time."); MediaSession.Close("dialog creation failed"); Hangup(); return(false); } } }
/// <summary> /// Answers the currently accepted call. The call must have been previously accepted using /// AcceptCall. The user agent is acting asd a server for this operation. /// </summary> /// <param name="sdp">The session description payload to send to the remote call party /// when answering.</param> public void Answer(SDP sdp) { m_uas.Answer(m_sdpContentType, sdp.ToString(), null, SIPDialogueTransferModesEnum.Default); }