C# VoIP Softphone Video Tutorial Part 2
Making and receiving calls

Download: my-first-soft-phone-two.zip (Tutorial part 2)

This video presents some of the main steps concerning to how to build a softphone by using Ozeki VoIP SIP SDK. This tutorial is the 2. part of a 3-part video tutorial series, in which you can learn how to connect audio devices (microphone, speaker) to your softphone, then how to create a simple GUI and how to make some incoming and outgoing test calls.

From the first part of this tutorial you could learn how to create a softphone, how to implement the main events and methods etc. Your softphone can also register to a PBX, if you set every attribute correctly at the phone line's creation. During this guide, you will continue to develop that application, and you will be able to make and accept calls with the usage of a simple GUI (Graphical User Interface).

You can learn from the video how to connect audio devices, how to create a simple gui, how to accept and make calls, and much more:


Step 1 - Finishing the softPhone_inComingCall method

When you have an incoming call, the application needs to set the call object, and needs to subscribe to the call's events. To subscribe to those events, you have already created the WireUpCallEvents() method during the first tutorial, so you just have to call it now. To get notified about incoming calls waiting to be accepted, you have created a boolean variable (called inComingCall) during the first tutorial, as well. If this variable's value is "true", that means there is an incoming call waiting to be accepted:

call = e.Item;
WireUpCallEvents();
inComingCall = true;

Step 2 - Creating the GUI

If you would like to be able to handle calls throught the GUI, you have to add some buttons to it.
You have to switch to the Design View, and from the Toolbox, drag and drop 14 Buttons onto your Form, and change their "Text" field to the followings:

  • Pick Up: for picking up the call, making call etc.
  • Hang Up: for hanging up or rejecting the call, clearing the actual dialing etc.
  • Numeric buttons from 0 to 9: usually used for dialing phone numbers.
  • Special buttons: these buttons are signed with the "*" and the "#" characters, and usually used for special tasks.

To place the buttons, you might have to incrase the Form's size. It's also a good idea to place those buttons to be similar to an ordinary phone's keypad.

Please note that, these buttons aren't functioning yet, since you will set their Click event during later steps.

Step 3 - Setting the audio devices

You can start and stop the media devices (the microphone and the speaker in this example), and this example is using helper methods for these purposes. These methods check first if there is a device to be started or stopped, than call the device's Start() or Stop() methods:

private void StartDevices()
{
    if (microphone != null)
    {
        microphone.Start();
        InvokeGUIThread(() => { lb_Log.Items.Add("Microphone Started."); });
    }

    if (speaker != null)
    {
        speaker.Start();
        InvokeGUIThread(() => { lb_Log.Items.Add("Speaker Started."); });
    }
}


private void StopDevices()
{
    if (microphone != null)
    {
        microphone.Stop();
        InvokeGUIThread(() => { lb_Log.Items.Add("Microphone Stopped."); });
    }

    if (speaker != null)
    {
        speaker.Stop();
        InvokeGUIThread(() => { lb_Log.Items.Add("Speaker Stopped."); });
    }
}

Step 4 - Setting the audio devices part 2

Even if you have a started microphone and the called party has a started speaker, you have to send the voice data from your device, to the other party's. To do this, you have to connect your microphone object to a PhoneCallAudioSender object, and if you would like to receive voice data as well, you have to connect your speaker object to a PhoneCallAudioReceiver object. The senders and receivers can be attached to the call itself, so you will be able to send voice data through the call, from your microphone, to an another speaker.
To connect the devices to the senders and receivers, you have to use a MediaConnector object:

        private void ConnectMedia()
        {
            if (microphone != null)
            {
                connector.Connect(microphone, mediaSender);
            }

            if (speaker != null)
            {
                connector.Connect(mediaReceiver, speaker);
            }
        }

        private void DisconnectMedia()
        {
            if (microphone != null)
            {
                connector.Disconnect(microphone, mediaSender);
            }

            if (speaker != null)
            {
                connector.Disconnect(mediaReceiver, speaker);
            }
        }

The DisconnectMedia() method closes the connections between MediaHandlers. You could also close all of the connections by using:

connector.Dispose();

There are several places, where you could call the ConnectMedia() method; the example calls it at the end of the InitializeSoftPhone() method, since it's enough to connect the devices only once, becouse you won't need to use the DisconnectMedia() method, yet.

Step 5 - Finishing the call_CallStateChanged method

Since the application is following the active call's state, you will know if there is an active communication between two parties.

  • if the call's state is "Answered", you need to start the devices with the help of the previously written StartDevices() method. You also need to set the mediaReceiver object to be able to receive audio data from the call, and the mediaSender object to be able to send audio data to the call. You can do these by attaching these objects to the call.
    You can also tell the application to create a new entry in the lb_Log ListBox, when a call is being answered.
    if (e.State == CallState.Answered)
        {
            StartDevices();
    
            mediaReceiver.AttachToCall(call);
            mediaSender.AttachToCall(call);
    
            InvokeGUIThread(() => { lb_Log.Items.Add("Call started."); });
        }
    
    Please note that, the application is already listening to the call's events, since you have already called the WireUpCallEvents() method when you noticed the incoming call (at Step 1), and you will also call this method a few steps later, when you make a call (at Step 8).
  • when the call ends, the application will stop the devices by calling the previously written StopDevices() method. Since there will be no audio data to send and receive, you need to detach the mediaReceiver and mediaSender objects from the call, and you won't need to listen to the call's events either, so you can call the WireDownCallEvents() method too.
    Finally, you have to set the call object to be "null", and you can also log the ending of the call in the lb_Log ListBox:
    if (e.State.IsCallEnded() == true)
        {
            StopDevices();
    
            mediaReceiver.Detach();
            mediaSender.Detach();
    
            WireDownCallEvents();
    
            call = null;
    
            InvokeGUIThread(() => { lb_Log.Items.Add("Call ended."); });
        }
    

Step 6 - Adding event to the Pick Up button

To add Click event to a button, the easiest way is to double click on the button at the Design View. After that, you will be switched to the Code view, and a Click event and method will be generated to the selected button.
To be able to accept incoming calls, you have to use the call object's Answer() method. However, there are several other steps and cases to be handled; you have to check first, if there is an incoming call, and you have to set the inComingCall boolean variable's value to "false", since the call is being answered. You can also log that there is an accepted call, to the lb_Log ListBox:

if (inComingCall)
    {
        inComingCall = false;
        call.Answer();

        InvokeGUIThread(() => { lb_Log.Items.Add("Call accepted."); });
        return;
    }

    if (call != null)
    {
        return;
    }

As you can see, if you press the Pick Up button during a call, it'll do nothing.

Please note that, you will be able to use this button for more functions later (for example: to make a call, in this example).

Step 7 - Adding event to the Hang Up button

To be able to hang up active calls, you have to make a Click event to the Hang Up button as well.
This button will handle two cases, for now:

  • if there is an incoming call and the phone is ringing (so, the call's state is "Ringing"), you have to use the call object's Reject() method, to reject the call. You can also log this event to the lb_Log ListBox.
  • if there is an active call, you have to use the call object's HangUp() method, to hang up the call, and you have to set the inComingCall boolean variable's value to "false" .
if (call != null)
    {
        if (inComingCall && call.CallState == CallState.Ringing)
        {
            call.Reject();
            InvokeGUIThread(() => { lb_Log.Items.Add("Call rejected."); });
        }
        else
        {
            call.HangUp();
            inComingCall = false;
            InvokeGUIThread(() => { lb_Log.Items.Add("Call hanged up."); });
        }

        call = null;

    }

In both cases, the call object needs to be set to "null".

If you have followed these steps, your softphone is ready to accept, reject and hang up calls.

Step 8 - Making calls - creating a Call object

At the Pick Up button's Click event you need to handle a new case; the case, when the registration was required, but was unsuccessful.
If this happens, the softphone will do nothing, but you can log the unsuccessful registration to the lb_Log ListBox.

if (phoneLineInformation != RegState.NoRegNeeded && phoneLineInformation != RegState.RegistrationSucceeded)
    {
        InvokeGUIThread(() => { lb_Log.Items.Add("Registration Failed!"); });
        return;
    }

At the Pick Up button's Click event, the softphone is handling three cases now. If non of these cases occurs when the user presses the Pick Up button, it will try to make a call.
To make a call, you should do three steps:

  1. create a call object with the help of the softPhone object's CreateCallObject() method, which waits a phoneLine object and the number to be dialed as String (which will be received from the lbl_NumberToDial Label's Text field).
  2. subscribe to the call's events, with the previously written WireUpCallEvents() method.
  3. start the call with the call objec't Start() method.
call = softPhone.CreateCallObject(phoneLine, lbl_NumberToDial.Text);
WireUpCallEvents();
call.Start();

Step 9 - Adding a new Label for the dialed number

With the help of a new Label, you can display the dialed number, which u'll receive by clicking on the numeric buttons.
To add a new label to the Form, you have to switch to the Design View, and from the Toolbox, drag and drop a Label onto the form. If you check the example source code, you will see, this label is named "lbl_NumberToDial".

Please note that, if you are using a TextBox instead of a Label, you can also edit the dialed number from your computer's keyboard. In the next example, this Label will be also replaced with a TextBox.

Step 10 - Adding event to the numeric and the special buttons

You do not have to write event's for all of these buttons, since they were created for a common goal: to add a character to the dialed number.

  • first, you have to write a method, which will handle the sender object as a Button within a variable, and will add the button's Text (the exact number or the special character) to the lbl_NumberToDial Label's Text field (without whitespaces).
    If you would like to allow to use these buttons only when there is no active call, you can also set that here:
    private void buttonKeyPadButton_Click(object sender, EventArgs e)
    {
        var btn = sender as Button;
    
        if (call != null) { return; }
    
        if (btn == null) return;
    
        lbl_NumberToDial.Text += btn.Text.Trim();
    }
    

    Please note that, if you would like to avoid NullReferenceExceptions, you have to check if the sender object is not null.

  • after you have created the method, you have to switch to the Design View, and select all of the numeric and special buttons, by holding the Ctrl button on your keyboard, and left clicking on them one by one.
    If all of the 12 buttons are selected, within the Properties tab, click on the Events tab (indicated with a lightning sign), and at the Click event, select the "buttonKeyPadButton_Click" method from the drop-down menu.

If you are done with these steps, you are able to dial numbers with your softphone, as well.

Step 11 - Finishing the source code

There are a few things which are recommended to do, before you think you are done.

  • at the softphone's initialization, the lbl_NumberToDial Label's Text field should be empty:
    lbl_NumberToDial.Text = string.Empty;
    
  • every time, when the Hang Up button is being clicked, the dialed number should be cleared. To do this, insert this line to the Hang Up button's Click event's method's end as well.

Conclusion

You are ready to try your softphone, which is able to register with a SIP account to a PBX, and can make and accept calls.

Next tutorial

From the third part of the tutorial, you can learn how to

  • manage and display the phone's states
  • transfer the call
  • put the call on hold and take the call off hold
  • redial the last called number (or the last caller)

More information