static void Main() { Console.WriteLine("SIPSorcery Call Hold and Blind Transfer example."); Console.WriteLine("Press 'c' to initiate a call to the default destination."); Console.WriteLine("Press 'h' to place an established call on and off hold."); Console.WriteLine("Press 'H' to hangup an established call."); Console.WriteLine("Press 't' to request a blind transfer on an established call."); Console.WriteLine("Press 'q' or ctrl-c to exit."); // Plumbing code to facilitate a graceful exit. CancellationTokenSource exitCts = new CancellationTokenSource(); // Cancellation token to stop the SIP transport and RTP stream. Log = AddConsoleLogger(); // Set up a default SIP transport. var sipTransport = new SIPTransport(); sipTransport.AddSIPChannel(new SIPUDPChannel(new IPEndPoint(IPAddress.Any, SIP_LISTEN_PORT))); Console.WriteLine($"Listening for incoming calls on: {sipTransport.GetSIPChannels().First().ListeningEndPoint}."); EnableTraceLogs(sipTransport); var winAudio = new WindowsAudioEndPoint(new AudioEncoder()); winAudio.RestrictCodecs(new List <AudioCodecsEnum> { AudioCodecsEnum.PCMU }); // Create a client/server user agent to place a call to a remote SIP server along with event handlers for the different stages of the call. var userAgent = new SIPUserAgent(sipTransport, null, true); userAgent.RemotePutOnHold += () => Log.LogInformation("Remote call party has placed us on hold."); userAgent.RemoteTookOffHold += () => Log.LogInformation("Remote call party took us off hold."); userAgent.OnIncomingCall += async(ua, req) => { Log.LogInformation($"Incoming call from {req.Header.From.FriendlyDescription()} at {req.RemoteSIPEndPoint}."); var uas = userAgent.AcceptCall(req); if (userAgent?.IsCallActive == true) { // If we are already on a call return a busy response. Log.LogWarning($"Busy response returned for incoming call request."); uas.Reject(SIPResponseStatusCodesEnum.BusyHere, null); } else { var voipSession = new VoIPMediaSession(winAudio.ToMediaEndPoints()); voipSession.AcceptRtpFromAny = true; var answerResult = await userAgent.Answer(uas, voipSession); } }; // At this point the call has been initiated and everything will be handled in an event handler. Task.Run(async() => { try { while (!exitCts.Token.WaitHandle.WaitOne(0)) { var keyProps = Console.ReadKey(); if (keyProps.KeyChar == 'c') { if (!userAgent.IsCallActive) { var voipSession = new VoIPMediaSession(winAudio.ToMediaEndPoints()); voipSession.AcceptRtpFromAny = true; bool callResult = await userAgent.Call(DEFAULT_DESTINATION_SIP_URI, SIP_USERNAME, SIP_PASSWORD, voipSession); Log.LogInformation($"Call attempt {((callResult) ? "successfull" : "failed")}."); } else { Log.LogWarning("There is already an active call."); } } else if (keyProps.KeyChar == 'h') { // Place call on/off hold. if (userAgent.IsCallActive) { if (userAgent.IsOnLocalHold) { Log.LogInformation("Taking the remote call party off hold."); (userAgent.MediaSession as VoIPMediaSession).TakeOffHold(); userAgent.TakeOffHold(); } else { Log.LogInformation("Placing the remote call party on hold."); await(userAgent.MediaSession as VoIPMediaSession).PutOnHold(); userAgent.PutOnHold(); } } else { Log.LogWarning("There is no active call to put on hold."); } } else if (keyProps.KeyChar == 'H') { if (userAgent.IsCallActive) { Log.LogInformation("Hanging up call."); userAgent.Hangup(); } } else if (keyProps.KeyChar == 't') { // Initiate a blind transfer to the remote call party. if (userAgent.IsCallActive) { var transferURI = SIPURI.ParseSIPURI(TRANSFER_DESTINATION_SIP_URI); bool result = await userAgent.BlindTransfer(transferURI, TimeSpan.FromSeconds(TRANSFER_TIMEOUT_SECONDS), exitCts.Token); if (result) { // If the transfer was accepted the original call will already have been hungup. // Wait a second for the transfer NOTIFY request to arrive. await Task.Delay(1000); exitCts.Cancel(); } else { Log.LogWarning($"Transfer to {TRANSFER_DESTINATION_SIP_URI} failed."); } } else { Log.LogWarning("There is no active call to transfer."); } } else if (keyProps.KeyChar == 'q') { // Quit application. exitCts.Cancel(); } } } catch (Exception excp) { Log.LogError($"Exception Key Press listener. {excp.Message}."); } }); // Ctrl-c will gracefully exit the call at any point. Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e) { e.Cancel = true; exitCts.Cancel(); }; // Wait for a signal saying the call failed, was cancelled with ctrl-c or completed. exitCts.Token.WaitHandle.WaitOne(); #region Cleanup. Log.LogInformation("Exiting..."); if (userAgent != null) { if (userAgent.IsCallActive) { Log.LogInformation($"Hanging up call to {userAgent?.CallDescriptor?.To}."); userAgent.Hangup(); } // 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(); } #endregion }
static void Main() { Console.WriteLine("SIPSorcery Getting Started Video Call Demo"); Console.WriteLine("Press ctrl-c to exit."); Log = AddConsoleLogger(); _sipTransport = new SIPTransport(); EnableTraceLogs(_sipTransport); // Open a window to display the video feed from the remote SIP party. _form = new Form(); _form.AutoSize = true; _form.BackgroundImageLayout = ImageLayout.Center; _picBox = new PictureBox { Size = new Size(640, 480), Location = new Point(0, 0), Visible = true }; _form.Controls.Add(_picBox); Application.EnableVisualStyles(); ThreadPool.QueueUserWorkItem(delegate { Application.Run(_form); }); ManualResetEvent formMre = new ManualResetEvent(false); _form.Activated += (object sender, EventArgs e) => formMre.Set(); Console.WriteLine("Waiting for form activation."); formMre.WaitOne(); _sipTransport.SIPTransportRequestReceived += OnSIPTransportRequestReceived; string executableDir = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); var userAgent = new SIPUserAgent(_sipTransport, null); var windowsAudioEndPoint = new WindowsAudioEndPoint(new AudioEncoder()); windowsAudioEndPoint.RestrictCodecs(new List <AudioCodecsEnum> { AudioCodecsEnum.PCMU }); var windowsVideoEndPoint = new WindowsVideoEndPoint(); MediaEndPoints mediaEndPoints = new MediaEndPoints { AudioSink = windowsAudioEndPoint, AudioSource = windowsAudioEndPoint, VideoSink = windowsVideoEndPoint, VideoSource = windowsVideoEndPoint, }; var voipMediaSession = new VoIPMediaSession(mediaEndPoints); voipMediaSession.AcceptRtpFromAny = true; // Place the call and wait for the result. Task <bool> callTask = userAgent.Call(DESTINATION, null, null, voipMediaSession); callTask.Wait(CALL_TIMEOUT_SECONDS * 1000); ManualResetEvent exitMRE = new ManualResetEvent(false); if (callTask.Result) { Log.LogInformation("Call attempt successful."); windowsVideoEndPoint.OnVideoSinkDecodedSample += (byte[] bmp, uint width, uint height, int stride, VideoPixelFormatsEnum pixelFormat) => { _picBox.BeginInvoke(new Action(() => { unsafe { fixed(byte *s = bmp) { System.Drawing.Bitmap bmpImage = new System.Drawing.Bitmap((int)width, (int)height, stride, System.Drawing.Imaging.PixelFormat.Format24bppRgb, (IntPtr)s); _picBox.Image = bmpImage; } } })); }; windowsAudioEndPoint.PauseAudio().Wait(); voipMediaSession.AudioExtrasSource.SetSource(AudioSourcesEnum.Music); } else { Log.LogWarning("Call attempt failed."); Console.WriteLine("Press ctrl-c to exit."); } Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e) { e.Cancel = true; Log.LogInformation("Exiting..."); exitMRE.Set(); }; exitMRE.WaitOne(); if (userAgent.IsCallActive) { Log.LogInformation("Hanging up."); userAgent.Hangup(); Task.Delay(1000).Wait(); } // Clean up. _form.BeginInvoke(new Action(() => _form.Close())); _sipTransport.Shutdown(); }