Course 1 / Lecture 11

How to develop a softphone in C#

How to create a ring group to improve your softphone

The principle of ring group creation is to create a softphone, which tries to transfer the incoming call to one of the other extensions set, when it's called. To be able to do it, the softphone should be registered to a PBX, since it should be able to be called.

These examples will introduce how to create ring group extensions, which are able to transfer calls to one of the selected extensions by different strategies.

What knowledge would you need?

To fully understand this guide, you might need to study the following chapters first:

  • SIP registration: you can learn how to begin softphone developing and how to be able to register to a pbx with a sip account.
    Learn more...
  • Managing media handlers: you can find here examples and a simple guide about how to be able to use, connect and manage different media handlers, and how can you attach them to calls.
    Learn more...
  • Making and accepting calls: from this guide you can learn how can your softphone make and accept calls, and handle the calls' states.
    Learn more...
  • Controlling the call: you can learn how to control calls, for example: how to transfer them to another party.
    Learn more...
  • Parallel call management: from this guide you can learn how can you develop softphones, which are able to manage multiple calls simultenaously.
    Learn more...

What is a ring strategy?

Before transfering a call, the ring group needs to notify the group's members about the intent of the call's transfering, first; after a member accepts the call, the extension transfers it by using the AttendedTransfer() method. To select the member which will be called within a group, there are several strategies, for example:

  • RingAll: all extensions in the ring group will be called simultenaously.
  • RingOneByOne: the ring group extension will call the members one by one, with a previously set order. This example will call the extansions in the same order as they were added to the ring group.
  • RingGroupRandom: the extensions of the ring group will be called in a randomized order.

Ring group source code in C#

To create a ring group example in C#, the best option is to define 3 classes. The first class illustrates how to create a softphone in C#, the second will demonstrate how to add ring group code to this sip softphone, the third will demonstrate a simple way to use the sip register C# method to register to the voip pbx.

  • Softphone.cs: represents a softphone, which is able to register to a pbx.
  • RingGroupCallHandler.cs: the function of the class is to implement one of the previously introduced ringing strategies. Since it's separated into a separate class, the application can handle multiple incoming calls simultenaously with its instances.
  • Program.cs: a class to handle the user events, such as asking for sip register informations, asking for the ring group's members' phone numbers, notifying the user about the results and states etc.

What is Softphone.cs used for?

Represents a very simple softphone, which can register to a pbx and is able to handle some of the key events (notifies the application, when there is an incoming call etc), which are really simple but key tasks.

Registration: It's highly recommended to visit the SIP Registration chapter to understand the meaning of "SIP Registration".

Events of the class: the Softphone class provides two events to be subscribed to:

  • phone line's state: notifies the subscriber about the changes of the phone line.
  • incoming call: notifies the subscriber if there is an incoming call.

Call objects: creates a call object, when it's called. (It is being called by the RingGroupCallHandler class).

public IPhoneCall CreateCall(string member)
    {
        return _softphone.CreateCallObject(_phoneLine, member);
    }

What is Program.cs used for?

This class communicates with the user through the console window; it simply shows an introduction message, asks the user about the necessary information for the sip register period, and if the phone line is successfully registered, also asks for the ring group's phone number.
Also, the Program class will use up the other two classes to be able to manage incoming and outgoing calls.

Registering to a PBX: the class is using a Softphone object. After showing a short introduction message, to register the extension to a pbx it asks the user about sip account and pbx information, than tries to register to it by calling the softphone object's Register() method.

Adding members to the ring group: since the class is also subscribed to the phone line's events, it knows when the phone line's state is successfully registered, and it starts to ask the user about the ring group: number of the members, and the phone numbers of them, which are being stored in a list of Strings.

After these steps, the Ring Group extension is ready to accept incoming calls:

Handling incoming calls: when an incoming call occurs, the application notifies the user about that, creates a new RingGroupCallHandler object for the call, subscribes to it's Completed method, and adds the object to the List of the handlers. If these steps are dones, it calls the object's Start() method:

var callHandler = new RingGroupCallHandler(e.Item, _softphone, _members);
callHandler.Completed += callHandler_Completed;

lock (_callHandlers)
    _callHandlers.Add(callHandler);

callHandler.Start();

Stepping out from the call: if the incoming call is no longer available (for example: it was transfered, the caller hanged up the call etc.), the RingGroupCallHandler object of that call should be removed from the call handlers' List:

static void callHandler_Completed(object sender, EventArgs e)
    {
        lock (_callHandlers)
            _callHandlers.Remove((RingGroupCallHandler) sender);
    }

What is RingGroupCallHandler.cs used for?

The class is dedicated to handle the key tasks and features of the application, which means:

  1. waits for an incoming call
  2. starts to call the ring group's members by one of the ringing strategies
  3. if the call is accepted by one of the members, transfers the incoming call to that member, and steps out from the call.

The steps are simple and mostly common, but all three strategies are managing the groups in different ways.

How does the RingAll strategy work?

Very member of the group is being called simultenaously, so the ring group extension will call them in the same time. If a member accepts the call, it transfers the call to that member, finishes to ring the others, than steps out from the call:

  • when there is an incoming call, the Start() method is going to be called by the program class. This method accepts the call, and starts to create the outgoing calls by calling the StartOutgoingCalls().
public void Start()
{
    _incomingCall.Answer();
    _incomingCall.CallStateChanged += call_CallStateChanged;
    StartOutgoingCalls();
}
  • the StartOutgoingCalls() method creats call objects to all of the members, and makes the calls while also storing them in a list. After the method has done its job, all of the ring group members' phone should ring (if that's available, reachable):
void StartOutgoingCalls()
    {
        foreach (var member in _members)
        {
            var call = _softPhone.CreateCall(member);
            call.CallStateChanged += OutgoingCallStateChanged;
            _calls.Add(call);
        }

        lock (_sync)
        {
            foreach (var call in _calls)
            {
                call.Start();
            }
        }
    }
  • as you can see, the application is subscribed to all of the calls' CallStateChanged event, and will call the OutgoingCallStateChanged method if the even occurs. If one of the calls is being answered, this method transfers that original caller to the member (by using the AttendedTransfer() method), removes the call from the calls' list, than calls the OnCompleted() method. If there are calls which are made, but the destinations aren't available, the calls to them should be hanged up, and removed from the calls' list. If there is no active outgoing call anymore, the incoming call will be hanged up.
void OutgoingCallStateChanged(object sender, CallStateChangedArgs e)
    {
        var call = (IPhoneCall)sender;

        if (e.State == CallState.Answered)
        {
            _incomingCall.AttendedTransfer(call);

            lock (_sync)
            {
                _calls.Remove(call);
                OnCompleted();
            }
        }
        
        if (e.State == CallState.Busy || e.State == CallState.Error)
            {
                lock (_sync)
                {
                    call.HangUp();
                    _calls.Remove(call);
                    if (_calls.Count == 0)
                    {
                        Console.WriteLine("No available destination.");
                        _incomingCall.HangUp();
                    }
                }
            }
    }
  • the OnCompleted() method calls the HangupOutgoingCalls() method, and sets the Completed event to indicate; the transfer has been complated, or the caller hanged up the call before the transfer. The Completed event indicated to the application, that the call handler have nothing to do anymore, and can be removed from the call handlers' list.
    The OnCompleted() method would be also called, when the incoming call ends before the transferring.
  • the HangupOutgoingCalls() method hangs up all of the outgoing calls, than clears the list of them.
void HangupOutgoingCalls()
    {
        foreach (var call in _calls)
        {
            call.HangUp();
        }
        _calls.Clear();
    }

How do the RingOneByOne and RingGroupRandom strategies work?

There are only a few little differences between the implementation of the RingOneByOne and the RingGroupRandom strategies, the main principle of these strategies is:

  • if one of them picks up the call, the others won't even be ringed,
  • if one of them is busy or cannot be reached, the next member will be called.

The main steps, that should both of the strategies should follow:

  • after an incoming call has been noticed, the StartOutgoingCalls() method is being called. This does the same job as in the case of the RingAll strategy, except after the calls have been added to the calls' list, those won't be made here. Instead of that, the CallSequencer() method is being called.
  • if there are no calls in the calls' list, the CallSequencer() method hangs up the incoming call. If there are calls in the list, it starts to ring the members one by one:
  • if the RingOneByOne strategy is being used, the method tries to make a call to the first member of the list.
private void CallSequencer()
    {
        if (_calls.Count > 0)
        {
            var call = _calls[0];
            call.Start();
        }
        else
        {
            _incomingCall.HangUp();
        }
    }
  • if the RingGroupRandom strategy is being used, the method selects a random member from the list , and tries to make a call to it:
private void CallSequencer()
    {
        if (_calls.Count > 0)
        {
            _randomMemberToDial = _randomInt.Next(_calls.Count);
            var call = _calls[_randomMemberToDial];
            call.Start();
        }
        else
        {
            _incomingCall.HangUp();
        }
    }
  • when the called ring group member is busy or cannot be reached, the application won't hang up the incoming call, instead it calls the CallSequencer() method again (since that method checks if it should try to make a new call, or it should hang up the incoming call).
  • the other parts of the implementation are the same as they were introduced at the RingAll strategy.

Conclusion

As you could see, to create ring group handler applications with Ozeki VoIP SIP SDK is really simple, and you also have several options to configure them; from the example you could learn how to implement three type of the most popular ringing strategies etc.

Related Pages

More information