コード例 #1
0
ファイル: Program.cs プロジェクト: zanzo420/sipsorcery
        static void Main()
        {
            Console.WriteLine("SIPSorcery Notification Client Demo");

            // Use verbose to get display the full SIP messages.
            AddConsoleLogger(LogEventLevel.Verbose);

            var sipTransport = new SIPTransport();

            sipTransport.EnableTraceLogs();

            var mwiURI = SIPURI.ParseSIPURIRelaxed($"{USERNAME}@{SERVER}");
            int expiry = 180;

            SIPNotifierClient mwiSubscriber = new SIPNotifierClient(sipTransport, null, SIPEventPackagesEnum.MessageSummary, mwiURI, USERNAME, null, PASSWORD, expiry, null);

            mwiSubscriber.SubscriptionFailed     += (uri, failureStatus, errorMessage) => Console.WriteLine($"MWI failed for {uri}, {errorMessage}");
            mwiSubscriber.SubscriptionSuccessful += (uri) => Console.WriteLine($"MWI subscription successful for {uri}");
            mwiSubscriber.NotificationReceived   += (evt, msg) => Console.WriteLine($"MWI notification, type {evt}, message {msg}.");

            mwiSubscriber.Start();

            Console.WriteLine("press any key to exit...");
            Console.ReadLine();

            Console.WriteLine("Exiting...");

            // Clean up.
            mwiSubscriber.Stop();
            Thread.Sleep(1000);
            sipTransport.Shutdown();
        }
コード例 #2
0
ファイル: Program.cs プロジェクト: zanzo420/sipsorcery
        static async Task Main()
        {
            Console.WriteLine("SIPSorcery SIP Call Server example.");
            Console.WriteLine("Press 'c' to place a call to the default destination.");
            Console.WriteLine("Press 'd' to send a random DTMF tone to the newest call.");
            Console.WriteLine("Press 'h' to hangup the oldest call.");
            Console.WriteLine("Press 'H' to hangup all calls.");
            Console.WriteLine("Press 'l' to list current calls.");
            Console.WriteLine("Press 'r' to list current registrations.");
            Console.WriteLine("Press 't' to transfer the newest call to the default destination.");
            Console.WriteLine("Press 'q' to quit.");

            Log = AddConsoleLogger();

            // Set up a default SIP transport.
            _sipTransport = new SIPTransport();
            _sipTransport.AddSIPChannel(new SIPUDPChannel(new IPEndPoint(IPAddress.Any, SIP_LISTEN_PORT)));
            _sipTransport.AddSIPChannel(new SIPUDPChannel(new IPEndPoint(IPAddress.IPv6Any, SIP_LISTEN_PORT)));
            _sipTransport.AddSIPChannel(new SIPTCPChannel(new IPEndPoint(IPAddress.Any, SIP_LISTEN_PORT)));
            var localhostCertificate = new X509Certificate2(SIPS_CERTIFICATE_PATH);

            _sipTransport.AddSIPChannel(new SIPTLSChannel(localhostCertificate, new IPEndPoint(IPAddress.Any, SIPS_LISTEN_PORT)));
            // If it's desired to listen on a single IP address use the equivalent of:
            //_sipTransport.AddSIPChannel(new SIPUDPChannel(new IPEndPoint(IPAddress.Parse("192.168.11.50"), SIP_LISTEN_PORT)));
            _sipTransport.EnableTraceLogs();

            _sipTransport.SIPTransportRequestReceived += OnRequest;

            // Uncomment to enable registrations.
            //StartRegistrations(_sipTransport, _sipAccounts);

            CancellationTokenSource exitCts = new CancellationTokenSource();
            await Task.Run(() => OnKeyPress(exitCts.Token));

            Log.LogInformation("Exiting...");

            if (_sipTransport != null)
            {
                Log.LogInformation("Shutting down SIP transport...");
                _sipTransport.Shutdown();
            }
        }
コード例 #3
0
        static void Main()
        {
            Console.WriteLine("SIPSorcery registration user agent example.");
            Console.WriteLine("Press ctrl-c to exit.");

            Log = AddConsoleLogger(LogEventLevel.Verbose);

            // Set up a default SIP transport.
            var sipTransport = new SIPTransport();

            sipTransport.EnableTraceLogs();

            // Create a client user agent to maintain a periodic registration with a SIP server.
            var regUserAgent = new SIPRegistrationUserAgent(sipTransport, USERNAME, PASSWORD, DOMAIN, EXPIRY);

            // Event handlers for the different stages of the registration.
            regUserAgent.RegistrationFailed           += (uri, err) => Log.LogWarning($"{uri}: {err}");
            regUserAgent.RegistrationTemporaryFailure += (uri, msg) => Log.LogWarning($"{uri}: {msg}");
            regUserAgent.RegistrationRemoved          += (uri) => Log.LogWarning($"{uri} registration failed.");
            regUserAgent.RegistrationSuccessful       += (uri) => Log.LogInformation($"{uri} registration succeeded.");

            // Start the thread to perform the initial registration and then periodically resend it.
            regUserAgent.Start();

            ManualResetEvent exitMRE = new ManualResetEvent(false);

            Console.CancelKeyPress += (object sender, ConsoleCancelEventArgs e) =>
            {
                e.Cancel = true;
                Log.LogInformation("Exiting...");
                exitMRE.Set();
            };

            exitMRE.WaitOne();

            regUserAgent.Stop();
            sipTransport.Shutdown();
        }
コード例 #4
0
ファイル: Program.cs プロジェクト: zanzo420/sipsorcery
        static async Task Main()
        {
            Console.WriteLine("SIPSorcery Getting Started Video Call Demo");
            Console.WriteLine("Press ctrl-c to exit.");

            Log = AddConsoleLogger();
            ManualResetEvent exitMRE = new ManualResetEvent(false);

            var sipTransport = new SIPTransport();

            sipTransport.EnableTraceLogs();
            var userAgent = new SIPUserAgent(sipTransport, null, true);

            #region Set up a simple Windows Form with two picture boxes.

            _form          = new Form();
            _form.AutoSize = true;
            _form.BackgroundImageLayout = ImageLayout.Center;
            _localVideoPicBox           = new PictureBox
            {
                Size     = new Size(VIDEO_FRAME_WIDTH, VIDEO_FRAME_HEIGHT),
                Location = new Point(0, 0),
                Visible  = true
            };
            _remoteVideoPicBox = new PictureBox
            {
                Size     = new Size(VIDEO_FRAME_WIDTH, VIDEO_FRAME_HEIGHT),
                Location = new Point(0, VIDEO_FRAME_HEIGHT),
                Visible  = true
            };
            _form.Controls.Add(_localVideoPicBox);
            _form.Controls.Add(_remoteVideoPicBox);

            #endregion

            Application.EnableVisualStyles();
            ThreadPool.QueueUserWorkItem(delegate { Application.Run(_form); });
            _form.FormClosing      += (sender, e) => _isFormActivated = false;
            _form.Activated        += (sender, e) => _isFormActivated = true;
            _form.FormClosed       += (sender, e) => userAgent.Hangup();
            userAgent.OnCallHungup += (dialog) =>
            {
                if (_isFormActivated)
                {
                    _form.Close();
                }
            };

            // Video sink and source to generate and consume VP8 video streams.
            var testPattern  = new VideoTestPatternSource(new VpxVideoEncoder());
            var vp8VideoSink = new VideoEncoderEndPoint();

            // Add the video sink and source to the media session.
            MediaEndPoints mediaEndPoints = new MediaEndPoints
            {
                VideoSink   = vp8VideoSink,
                VideoSource = testPattern,
            };
            var voipMediaSession = new VoIPMediaSession(mediaEndPoints);
            voipMediaSession.AcceptRtpFromAny = true;

            #region Connect the video frames generate from the sink and source to the Windows form.

            testPattern.OnVideoSourceRawSample += (uint durationMilliseconds, int width, int height, byte[] sample, VideoPixelFormatsEnum pixelFormat) =>
            {
                if (_isFormActivated)
                {
                    _form?.BeginInvoke(new Action(() =>
                    {
                        if (_form.Handle != IntPtr.Zero)
                        {
                            unsafe
                            {
                                fixed(byte *s = sample)
                                {
                                    var bmpImage            = new Bitmap(width, height, width * 3, System.Drawing.Imaging.PixelFormat.Format24bppRgb, (IntPtr)s);
                                    _localVideoPicBox.Image = bmpImage;
                                }
                            }
                        }
                    }));
                }
            };

            vp8VideoSink.OnVideoSinkDecodedSample += (byte[] bmp, uint width, uint height, int stride, VideoPixelFormatsEnum pixelFormat) =>
            {
                if (_isFormActivated)
                {
                    _form?.BeginInvoke(new Action(() =>
                    {
                        if (_form.Handle != IntPtr.Zero)
                        {
                            unsafe
                            {
                                fixed(byte *s = bmp)
                                {
                                    var bmpImage             = new Bitmap((int)width, (int)height, stride, PixelFormat.Format24bppRgb, (IntPtr)s);
                                    _remoteVideoPicBox.Image = bmpImage;
                                }
                            }
                        }
                    }));
                }
            };

            #endregion

            // Place the call.
            var callResult = await userAgent.Call(DESTINATION, null, null, voipMediaSession).ConfigureAwait(false);

            Console.WriteLine($"Call result {((callResult) ? "success" : "failure")}.");

            Console.WriteLine("Press any key to hangup and exit.");
            Console.ReadLine();

            if (userAgent.IsCallActive)
            {
                _isFormActivated = false;
                userAgent.Hangup();
                await Task.Delay(1000).ConfigureAwait(false);
            }

            sipTransport.Shutdown();
        }
コード例 #5
0
ファイル: Program.cs プロジェクト: zanzo420/sipsorcery
        static async Task Main()
        {
            Console.WriteLine("SIPSorcery Play Sounds Demo");

            AddConsoleLogger();
            CancellationTokenSource exitCts = new CancellationTokenSource();

            var sipTransport = new SIPTransport();

            sipTransport.EnableTraceLogs();

            var userAgent = new SIPUserAgent(sipTransport, OUTBOUND_PROXY);

            userAgent.ClientCallFailed += (uac, error, sipResponse) => Console.WriteLine($"Call failed {error}.");
            userAgent.ClientCallFailed += (uac, error, sipResponse) => exitCts.Cancel();
            userAgent.OnCallHungup     += (dialog) => exitCts.Cancel();

            var windowsAudio = new WindowsAudioEndPoint(new AudioEncoder());
            //windowsAudio.RestrictFormats(format => format.Codec == AudioCodecsEnum.G722);
            var voipMediaSession = new VoIPMediaSession(windowsAudio.ToMediaEndPoints());

            voipMediaSession.AcceptRtpFromAny = true;
            //voipMediaSession.AudioExtrasSource.AudioSamplePeriodMilliseconds = 20;
            //voipMediaSession.AudioLocalTrack.Capabilities.Clear();
            //voipMediaSession.AudioLocalTrack.Capabilities.Add(
            //    new SDPAudioVideoMediaFormat(new AudioFormat(AudioCodecsEnum.L16, 118, 8000)));

            Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e)
            {
                e.Cancel = true;

                if (userAgent != null)
                {
                    if (userAgent.IsCalling || userAgent.IsRinging)
                    {
                        Console.WriteLine("Cancelling in progress call.");
                        userAgent.Cancel();
                    }
                    else if (userAgent.IsCallActive)
                    {
                        Console.WriteLine("Hanging up established call.");
                        userAgent.Hangup();
                    }
                }
                ;

                exitCts.Cancel();
            };

            // Place the call and wait for the result.
            var callTask = userAgent.Call(DESTINATION, null, null, voipMediaSession);

            Console.WriteLine("press ctrl-c to exit...");

            bool callResult = await callTask;

            if (callResult)
            {
                Console.WriteLine($"Call to {DESTINATION} succeeded.");

                await windowsAudio.PauseAudio();

                try
                {
                    await voipMediaSession.AudioExtrasSource.StartAudio();

                    //Console.WriteLine("Sending welcome message from 8KHz sample.");
                    await voipMediaSession.AudioExtrasSource.SendAudioFromStream(new FileStream(WELCOME_8K, FileMode.Open), AudioSamplingRatesEnum.Rate8KHz);

                    await Task.Delay(200, exitCts.Token);

                    Console.WriteLine("Sending sine wave.");
                    voipMediaSession.AudioExtrasSource.SetSource(AudioSourcesEnum.SineWave);

                    await Task.Delay(5000, exitCts.Token);

                    Console.WriteLine("Sending white noise signal.");
                    voipMediaSession.AudioExtrasSource.SetSource(AudioSourcesEnum.WhiteNoise);
                    await Task.Delay(2000, exitCts.Token);

                    Console.WriteLine("Sending pink noise signal.");
                    voipMediaSession.AudioExtrasSource.SetSource(AudioSourcesEnum.PinkNoise);
                    await Task.Delay(2000, exitCts.Token);

                    Console.WriteLine("Sending silence.");
                    voipMediaSession.AudioExtrasSource.SetSource(AudioSourcesEnum.Silence);

                    await Task.Delay(2000, exitCts.Token);

                    Console.WriteLine("Playing music.");
                    voipMediaSession.AudioExtrasSource.SetSource(AudioSourcesEnum.Music);

                    await Task.Delay(5000, exitCts.Token);

                    Console.WriteLine("Sending goodbye message from 16KHz sample.");
                    await voipMediaSession.AudioExtrasSource.SendAudioFromStream(new FileStream(GOODBYE_16K, FileMode.Open), AudioSamplingRatesEnum.Rate16KHz);

                    voipMediaSession.AudioExtrasSource.SetSource(AudioSourcesEnum.None);

                    await voipMediaSession.AudioExtrasSource.PauseAudio();

                    await Task.Delay(200, exitCts.Token);
                }
                catch (TaskCanceledException)
                { }

                // Switch to the external microphone input source.
                await windowsAudio.ResumeAudio();

                exitCts.Token.WaitHandle.WaitOne();
            }
            else
            {
                Console.WriteLine($"Call to {DESTINATION} failed.");
            }

            Console.WriteLine("Exiting...");

            if (userAgent?.IsHangingUp == true)
            {
                Console.WriteLine("Waiting 1s for the call hangup or cancel to complete...");
                await Task.Delay(1000);
            }

            // Clean up.
            sipTransport.Shutdown();
        }
コード例 #6
0
        static void Main(string[] args)
        {
            Console.WriteLine("SIPSorcery registration user agent example.");
            Console.WriteLine("Press ctrl-c to exit.");

            Log = AddConsoleLogger(LogEventLevel.Verbose);

            string server   = DEFAULT_SERVER;
            string username = DEFAULT_USERNAME;
            string password = DEFAULT_PASSWORD;
            int    expiry   = DEFAULT_EXPIRY;

            int posn = 0;

            while (posn < args?.Length && posn <= 3)
            {
                switch (posn)
                {
                case 0:
                    server = args[posn++].Trim();
                    break;

                case 1:
                    username = args[posn++].Trim();
                    break;

                case 2:
                    password = args[posn++].Trim();
                    break;

                case 3:
                    int.TryParse(args[posn++], out expiry);
                    break;
                }
            }

            Console.WriteLine("Attempting registration with:");
            Console.WriteLine($" server: {server}");
            Console.WriteLine($" username: {username}");
            Console.WriteLine($" expiry: {expiry}");

            // Set up a default SIP transport.
            var sipTransport = new SIPTransport();

            sipTransport.EnableTraceLogs();

            // Create a client user agent to maintain a periodic registration with a SIP server.
            var regUserAgent = new SIPRegistrationUserAgent(sipTransport, username, password, server, expiry);

            // Event handlers for the different stages of the registration.
            regUserAgent.RegistrationFailed           += (uri, err) => Log.LogWarning($"{uri}: {err}");
            regUserAgent.RegistrationTemporaryFailure += (uri, msg) => Log.LogWarning($"{uri}: {msg}");
            regUserAgent.RegistrationRemoved          += (uri) => Log.LogWarning($"{uri} registration failed.");
            regUserAgent.RegistrationSuccessful       += (uri) => Log.LogInformation($"{uri} registration succeeded.");

            // Start the thread to perform the initial registration and then periodically resend it.
            regUserAgent.Start();

            ManualResetEvent exitMRE = new ManualResetEvent(false);

            Console.CancelKeyPress += (object sender, ConsoleCancelEventArgs e) =>
            {
                e.Cancel = true;
                Log.LogInformation("Exiting...");
                exitMRE.Set();
            };

            exitMRE.WaitOne();

            regUserAgent.Stop();

            // Allow for unregister request to be sent (REGISTER with 0 expiry)
            Task.Delay(1500).Wait();

            sipTransport.Shutdown();
        }
コード例 #7
0
        /// <summary>
        /// Runs a single task as part of the overall job.
        /// </summary>
        /// <param name="options">The options that dictate the type of task to run.</param>
        /// <param name="taskNumber">The number assigned to this task.</param>
        /// <returns>A boolean indicating whether this single task succeeded or not.</returns>
        private static async Task <bool> RunTask(Options options, int taskNumber)
        {
            SIPTransport sipTransport = new SIPTransport();

            sipTransport.PreferIPv6NameResolution = options.PreferIPv6;

            if (options.Verbose)
            {
                sipTransport.EnableTraceLogs();
            }

            try
            {
                DateTime startTime = DateTime.Now;

                var dstUri = ParseDestination(options.Destination);

                logger.LogDebug($"Destination SIP URI {dstUri}");

                if (options.SourcePort != 0)
                {
                    var sipChannel = sipTransport.CreateChannel(dstUri.Protocol,
                                                                options.PreferIPv6 ? AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork,
                                                                options.SourcePort);
                    sipTransport.AddSIPChannel(sipChannel);
                }

                Task <bool> task = null;

                switch (options.Scenario)
                {
                case Scenarios.reg:
                    task = InitiateRegisterTaskAsync(sipTransport, dstUri);
                    break;

                case Scenarios.uac:
                case Scenarios.uacw:
                    task = InitiateCallTaskAsync(sipTransport, dstUri, options.Scenario);
                    break;

                case Scenarios.opt:
                default:
                    task = SendOptionsTaskAsync(sipTransport, dstUri);
                    break;
                }

                var result = await Task.WhenAny(task, Task.Delay(options.Timeout * 1000));

                TimeSpan duration = DateTime.Now.Subtract(startTime);
                bool     failed   = false;

                if (!task.IsCompleted)
                {
                    logger.LogWarning($"=> Request to {dstUri} did not get a response on task {taskNumber} after {duration.TotalMilliseconds.ToString("0")}ms.");
                    failed = true;
                }
                else if (!task.Result)
                {
                    logger.LogWarning($"=> Request to {dstUri} did not get the expected response on task {taskNumber} after {duration.TotalMilliseconds.ToString("0")}ms.");
                    failed = true;
                }
                else
                {
                    logger.LogInformation($"=> Got correct response on send {taskNumber} in {duration.TotalMilliseconds.ToString("0")}ms.");
                }

                return(!failed);
            }
            finally
            {
                logger.LogDebug("Shutting down the SIP transport...");
                sipTransport.Shutdown();
            }
        }
コード例 #8
0
ファイル: Program.cs プロジェクト: zanzo420/sipsorcery
        static void Main(string[] args)
        {
            Console.WriteLine("SIPSorcery client user agent example.");
            Console.WriteLine("Press ctrl-c to exit.");

            // Plumbing code to facilitate a graceful exit.
            ManualResetEvent exitMre       = new ManualResetEvent(false);
            bool             preferIPv6    = false;
            bool             isCallHungup  = false;
            bool             hasCallFailed = false;

            Log = AddConsoleLogger(LogEventLevel.Verbose);

            SIPURI callUri = SIPURI.ParseSIPURI(DEFAULT_DESTINATION_SIP_URI);

            if (args?.Length > 0)
            {
                if (!SIPURI.TryParse(args[0], out callUri))
                {
                    Log.LogWarning($"Command line argument could not be parsed as a SIP URI {args[0]}");
                }
            }
            if (args?.Length > 1 && args[1] == "ipv6")
            {
                preferIPv6 = true;
            }

            if (preferIPv6)
            {
                Log.LogInformation($"Call destination {callUri}, preferencing IPv6.");
            }
            else
            {
                Log.LogInformation($"Call destination {callUri}.");
            }

            // Set up a default SIP transport.
            var sipTransport = new SIPTransport();

            sipTransport.PreferIPv6NameResolution = preferIPv6;
            sipTransport.EnableTraceLogs();

            var audioSession = new WindowsAudioEndPoint(new AudioEncoder());

            audioSession.RestrictFormats(x => x.Codec == AudioCodecsEnum.PCMA || x.Codec == AudioCodecsEnum.PCMU);
            //audioSession.RestrictFormats(x => x.Codec == AudioCodecsEnum.G722);
            var rtpSession = new VoIPMediaSession(audioSession.ToMediaEndPoints());

            var offerSDP = rtpSession.CreateOffer(preferIPv6 ? IPAddress.IPv6Any : IPAddress.Any);

            // 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 += async(uac, resp) =>
            {
                Log.LogInformation($"{uac.CallDescriptor.To} Ringing: {resp.StatusCode} {resp.ReasonPhrase}.");
                if (resp.Status == SIPResponseStatusCodesEnum.SessionProgress)
                {
                    if (resp.Body != null)
                    {
                        var result = rtpSession.SetRemoteDescription(SdpType.answer, SDP.ParseSDPDescription(resp.Body));
                        if (result == SetDescriptionResultEnum.OK)
                        {
                            await rtpSession.Start();

                            Log.LogInformation($"Remote SDP set from in progress response. RTP session started.");
                        }
                    }
                }
            };
            uac.CallFailed += (uac, err, resp) =>
            {
                Log.LogWarning($"Call attempt to {uac.CallDescriptor.To} Failed: {err}");
                hasCallFailed = true;
            };
            uac.CallAnswered += async(iuac, resp) =>
            {
                if (resp.Status == SIPResponseStatusCodesEnum.Ok)
                {
                    Log.LogInformation($"{uac.CallDescriptor.To} Answered: {resp.StatusCode} {resp.ReasonPhrase}.");

                    if (resp.Body != null)
                    {
                        var result = rtpSession.SetRemoteDescription(SdpType.answer, SDP.ParseSDPDescription(resp.Body));
                        if (result == SetDescriptionResultEnum.OK)
                        {
                            await rtpSession.Start();
                        }
                        else
                        {
                            Log.LogWarning($"Failed to set remote description {result}.");
                            uac.Hangup();
                        }
                    }
                    else if (!rtpSession.IsStarted)
                    {
                        Log.LogWarning($"Failed to set get remote description in session progress or final response.");
                        uac.Hangup();
                    }
                }
                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;
                        exitMre.Set();
                    }
                }
            };

            // Start the thread that places the call.
            SIPCallDescriptor callDescriptor = new SIPCallDescriptor(
                SIPConstants.SIP_DEFAULT_USERNAME,
                null,
                callUri.ToString(),
                SIPConstants.SIP_DEFAULT_FROMURI,
                callUri.CanonicalAddress,
                null, null, null,
                SIPCallDirection.Out,
                SDP.SDP_MIME_CONTENTTYPE,
                offerSDP.ToString(),
                null);

            uac.Call(callDescriptor, null);

            // Ctrl-c will gracefully exit the call at any point.
            Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e)
            {
                e.Cancel = true;
                exitMre.Set();
            };

            // Wait for a signal saying the call failed, was cancelled with ctrl-c or completed.
            exitMre.WaitOne();

            Log.LogInformation("Exiting...");

            rtpSession.Close(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();
            }

            if (sipTransport != null)
            {
                Log.LogInformation("Shutting down SIP transport...");
                sipTransport.Shutdown();
            }
        }