/// <summary> /// Event handler for RTP events from the remote call party. Fires /// when the event is completed. /// </summary> /// <param name="rtpEvent">The received RTP event.</param> private void OnRemoteRtpEvent(RTPEvent rtpEvent) { if (rtpEvent.EndOfEvent) { OnDtmfTone?.Invoke(rtpEvent.EventID, rtpEvent.Duration); } }
/// <summary> /// Event handler for RTP events from the remote call party. Fires /// when the event is completed. /// </summary> /// <param name="rtpEvent">The received RTP event.</param> /// <param name="rtpHeader">THe RTP header on the packet that the event was received in.</param> private void OnRemoteRtpEvent(RTPEvent rtpEvent, RTPHeader rtpHeader) { OnRtpEvent?.Invoke(rtpEvent, rtpHeader); if (rtpEvent.EndOfEvent) { OnDtmfTone?.Invoke(rtpEvent.EventID, rtpEvent.Duration); } }
/// <summary> /// Event handler for RTP events from the remote call party. Fires /// when the event is completed. /// </summary> /// <param name="rtpEvent">The received RTP event.</param> private void OnRemoteRtpEvent(RTPEvent rtpEvent) { if (lastRtpEventEndTimeStamp != rtpEvent.Source.Header.Timestamp && rtpEvent.EndOfEvent) { lastRtpEventEndTimeStamp = rtpEvent.Source.Header.Timestamp; OnDtmfTone?.Invoke(rtpEvent.EventID, rtpEvent.Duration); } }
/// <summary> /// Event handler for RTP events from the remote call party. /// </summary> /// <param name="rtpEvent">The received RTP event.</param> private void OnRemoteRtpEvent(RTPEvent rtpEvent) { if (rtpEvent.EndOfEvent) { remoteDtmfDuration = 0; } else if (remoteDtmfDuration == 0) { remoteDtmfDuration = rtpEvent.Duration; DtmfCompleted?.Invoke(rtpEvent.EventID); } }
public Task SendDtmf(byte key, CancellationToken cancellationToken = default) { var dtmfEvent = new RTPEvent(key, false, RTPEvent.DEFAULT_VOLUME, DTMF_EVENT_DURATION, DTMF_EVENT_PAYLOAD_ID); return(SendDtmfEvent(dtmfEvent, cancellationToken)); }
static void Main() { Console.WriteLine("SIPSorcery client user agent example."); Console.WriteLine("Press ctrl-c to exit."); // Plumbing code to facilitate a graceful exit. CancellationTokenSource rtpCts = new CancellationTokenSource(); // Cancellation token to stop the RTP stream. bool isCallHungup = false; bool hasCallFailed = false; AddConsoleLogger(); SIPURI callUri = SIPURI.ParseSIPURI(DEFAULT_DESTINATION_SIP_URI); Log.LogInformation($"Call destination {callUri}."); // Set up a default SIP transport. var sipTransport = new SIPTransport(); sipTransport.AddSIPChannel(new SIPUDPChannel(new IPEndPoint(IPAddress.Any, 0))); // Un/comment this line to see/hide each SIP message sent and received. EnableTraceLogs(sipTransport); // Note this relies on the callURI host being an IP address. If it's a hostname a DNS lookup is required. IPAddress localIPAddress = NetServices.GetLocalAddressForRemote(callUri.ToSIPEndPoint().Address); // Initialise an RTP session to receive the RTP packets from the remote SIP server. var rtpSession = new RTPSession((int)SDPMediaFormatsEnum.PCMU, null, null, true, localIPAddress.AddressFamily); var offerSDP = rtpSession.GetSDP(localIPAddress); // Create a client user agent to place a call to a remote SIP server along with event handlers for the different stages of the call. var uac = new SIPClientUserAgent(sipTransport); uac.CallTrying += (uac, resp) => { Log.LogInformation($"{uac.CallDescriptor.To} Trying: {resp.StatusCode} {resp.ReasonPhrase}."); }; uac.CallRinging += (uac, resp) => Log.LogInformation($"{uac.CallDescriptor.To} Ringing: {resp.StatusCode} {resp.ReasonPhrase}."); uac.CallFailed += (uac, err) => { Log.LogWarning($"{uac.CallDescriptor.To} Failed: {err}"); hasCallFailed = true; }; uac.CallAnswered += (uac, resp) => { if (resp.Status == SIPResponseStatusCodesEnum.Ok) { Log.LogInformation($"{uac.CallDescriptor.To} Answered: {resp.StatusCode} {resp.ReasonPhrase}."); rtpSession.DestinationEndPoint = SDP.GetSDPRTPEndPoint(resp.Body); Log.LogDebug($"Remote RTP socket {rtpSession.DestinationEndPoint}."); } else { Log.LogWarning($"{uac.CallDescriptor.To} Answered: {resp.StatusCode} {resp.ReasonPhrase}."); } }; // The only incoming request that needs to be explicitly handled for this example is if the remote end hangs up the call. sipTransport.SIPTransportRequestReceived += async(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPRequest sipRequest) => { if (sipRequest.Method == SIPMethodsEnum.BYE) { SIPResponse okResponse = SIPResponse.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Ok, null); await sipTransport.SendResponseAsync(okResponse); if (uac.IsUACAnswered) { Log.LogInformation("Call was hungup by remote server."); isCallHungup = true; rtpCts.Cancel(); } } }; // Wire up the RTP receive session to the default speaker. var(audioOutEvent, audioOutProvider) = GetAudioOutputDevice(); rtpSession.OnReceivedSampleReady += (sample) => { for (int index = 0; index < sample.Length; index++) { short pcm = NAudio.Codecs.MuLawDecoder.MuLawToLinearSample(sample[index]); byte[] pcmSample = new byte[] { (byte)(pcm & 0xFF), (byte)(pcm >> 8) }; audioOutProvider.AddSamples(pcmSample, 0, 2); } }; // Send audio packets (in this case silence) to the callee. Task.Run(() => SendSilence(rtpSession, rtpCts)); // Start the thread that places the call. SIPCallDescriptor callDescriptor = new SIPCallDescriptor( SIPConstants.SIP_DEFAULT_USERNAME, null, callUri.ToString(), SIPConstants.SIP_DEFAULT_FROMURI, null, null, null, null, SIPCallDirection.Out, SDP.SDP_MIME_CONTENTTYPE, offerSDP.ToString(), null); uac.Call(callDescriptor); // Ctrl-c will gracefully exit the call at any point. Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e) { e.Cancel = true; rtpCts.Cancel(); }; // Give the call some time to answer. Task.Delay(3000).Wait(); // Send some DTMF key presses via RTP events. var dtmf5 = new RTPEvent(0x05, false, RTPEvent.DEFAULT_VOLUME, 1200, RTPSession.DTMF_EVENT_PAYLOAD_ID); rtpSession.SendDtmfEvent(dtmf5, rtpCts.Token).Wait(); Task.Delay(2000, rtpCts.Token).Wait(); var dtmf9 = new RTPEvent(0x09, false, RTPEvent.DEFAULT_VOLUME, 1200, RTPSession.DTMF_EVENT_PAYLOAD_ID); rtpSession.SendDtmfEvent(dtmf9, rtpCts.Token).Wait(); Task.Delay(2000, rtpCts.Token).Wait(); var dtmf2 = new RTPEvent(0x02, false, RTPEvent.DEFAULT_VOLUME, 1200, RTPSession.DTMF_EVENT_PAYLOAD_ID); rtpSession.SendDtmfEvent(dtmf2, rtpCts.Token).Wait(); Task.Delay(2000, rtpCts.Token).ContinueWith((task) => { }).Wait(); // Don't care about the exception if the cancellation token is set. Log.LogInformation("Exiting..."); rtpCts.Cancel(); audioOutEvent?.Stop(); rtpSession.CloseSession(null); if (!isCallHungup && uac != null) { if (uac.IsUACAnswered) { Log.LogInformation($"Hanging up call to {uac.CallDescriptor.To}."); uac.Hangup(); } else if (!hasCallFailed) { Log.LogInformation($"Cancelling call to {uac.CallDescriptor.To}."); uac.Cancel(); } // Give the BYE or CANCEL request time to be transmitted. Log.LogInformation("Waiting 1s for call to clean up..."); Task.Delay(1000).Wait(); } SIPSorcery.Net.DNSManager.Stop(); if (sipTransport != null) { Log.LogInformation("Shutting down SIP transport..."); sipTransport.Shutdown(); } }