This article introduces you the basic concepts of a web page embedded softphone
solution with the use of Silverlight technology. After reading through this material
you will be able to create your own softphone application that
you can put on your web site so as your customers could use it for calling you any
time they want only by visiting your site.
Prerequisites:
Operating system:
Windows 8, Windows 7, Windows Vista, Windows 200x, Windows XP
Development environment:
Visual Studio 2010 (Recommended), Visual Studio 2008, Visual Studio 2005
Webphone technology makes it possible for your customers to be able to call you
using your web site as the webphone can a web page embedded solution with all
the functions of a fully operable softphone (Figure 1).
Figure 1 - Ozeki VoIP SIP SDK supports web to SIP communication using Silverlight
Assuming that you are familiar with the main terms concerning a webphone and you have
created your first own click-to-call webphone solution before, this guide will show you how
to implement a fully functional softphone application that can be embedded in you web page.
This guide will show the basic steps of implementing a fully featured Silverlight
softphone client-server application, however, it may not be as detailed as the
other two sites mentioned before. So, it is strongly suggested to check those before you
start to write the sample program that is described on this page.
The webphone application you will create with this guide is a C# application. You will
need to have Visual Studio 2010, Silverlight 4 and Ozeki VoIP SIP SDK in order to be able
to get through this guide. If you do not have some of these you will need to download and
install them before starting the programming stage.
As the Silverlight environment is not capable of establishing TCP or UDP communication,
the webphone application will need the help of a webphone server that will provide the
channel between the clients. The solution you will see establishes the connection between
a Silverlight client and a SIP gateway that will connect to a SIP telephone network.
At first you will need to write the server-side code and after that you can create the
client-side application. This order can be changed, however, you will not be able to test the client
without a server.
The server functionality will need some basic changes
If you have checked the webphone server on the above mentioned sites, you will see that
you need to make some basic changes to be able to establish a SIP connection. It may be
good if you read through the pages about softphone technology in order to see the
fundament of this new server.
The basic softphone programming guide can be found on
Ozeki VoIP SIP Softphone page. It is strongly recommended to become
familiar with the basic terms of softphones if you want to create a Silverlight softphone
solution.
If you have read all the above suggested material, you will find it easy to create the
softphone application. If you have any doubts, however, about some parts of the program,
feel free to recheck the introduction pages to be sure you understand everything.
The MyClient class will define the client-side functions within the server
As you surely know the MyClient class in the previous click-to-call example programs
was only a storage class that had an IClient object and a name field defined in it.
In this case, the MyClient class will represent a more complex function as it will store
the whole SIP support and background for the webphone clients.
Code 1 shows the tools you will use when having a webphone client. You can see
a lot of familiar tools as, for example, there is a need for a MediaConnector as always
There are some softphone specific definitions here too like the ISoftPhone or the
IPhoneLine objects. You will see how easy is to merge these two sets of tools together
in a web site embedded softphone.
In order to be able to use the names without labeling them with the namespaces, you will
need to use some new using lines in your code like in Code 2.
using Ozeki.Common;
using Ozeki.Media;
using Ozeki.Media.MediaHandlers;
using Ozeki.MediaGateway;
using Ozeki.MediaGateway.Data;
using Ozeki.MediaGateway.Service;
using Ozeki.VoIP;
using Ozeki.VoIP.Media;
using Ozeki.VoIP.SDK;
Code 2 - Some useful using lines will be needed too
Of course, you can only use these tools and namespaces if you have registered your Ozeki VoIP SIP SDK
to your server project on the Solution Explorer panel.
When creating a new client, the program initializes the basic tools for the connection.
At this time the server itself will provide an available SIP account for the clients.
The constructor of the MyClient class initializes the basic tools and sets the
field values according to the parameters (Code 3).
This constructor shows how the two basic concepts of the softphone and the
webphone technologies are merged together. You create a softphone but at the same time
you need a StreamService object for the webphone functionality too.
The MediaConnector has to register the MediaSender object to the phoneCallMediaSender
that makes the transmission between the two solutions. The MediaSender object is a new
type that is defined in this project with the name of MyMediaSender.
public MyClient(IClient client, IStreamService streamService, ISoftPhone softPhone, SIPAccount account)
{
mediaConnector = new MediaConnector();
phoneCallAudioSender = new PhoneCallAudioSender();
phoneCallAudioReceiver = new PhoneCallAudioReceiver();
this.streamService = streamService;
this.softPhone = softPhone;
this.client = client;
Account = account;
}
Code 3 - MyClient class constructor
The MyClient class implements the basic softphone function of registering to and
unregistering from the SIP PBX. This is actually made the same way as in case of an
ordinary softphone (Code 4).
The phoneLine is created with the SIP account the client gets from the server and
it must be registered to the state change event. The softphone object also has to be
registered to the incoming call event in order to be able to notice an incoming call.
The last step is to register the phone line to the softphone. This establishes the PXB registration.
Code 4 - The MyClient class registers the softphone to a SIP PBX
Unregistering the softphone from the PBX has to contain the opposite instructions. The
call has to be hung up and the EventHandlers has to be removed, and at last
the phone line has to be unregistered from the softphone.
You will need to write the two EventHandler methods of the incoming call and the
call state change. These methods are slightly different from the ones you
have created for a simple softphone as at this time the server has to make sure
that all the clients got the right messages about the events.
The incoming call can be handled with the simple method shown in Code 5. You need
to register the call to the basic events and you will need to write those methods too
in this class.
The method consists of one conditional instruction that ensures that only in case
of the proper phone line will it do anything. In any other cases the incoming call
does not belong to this client and there is nothing to do with it.
Code 5 - In case of an incoming call you will register to some events of the call
The call state change event is one of the most important events to notice. This informs
both the server and the client about a change in the call state (Code 6).
The method can be divided into two sections. The first section is a great switch instruction
that decides about the type of the change and runs the proper functions. In case of an incoming
call the phoneCallMediaSender object has to be attached to the call and the server and
the client have to publish the stream names.
The publishStreamName is the name that the client publishes and the playStreamName
is the one that the server will publish.
This method also sets the mediaStream object with the information that the server gives
(playStreamName) and it also calls the OnInCall method that invokes the proper method
on the client's side to inform the client application about the incoming call.
In case of a completed call that means that one of the clients hung up the phone, the
method will detach the phoneCallMediaSender from the call, remove the streamService
object from the mediaStream and free the call object.
The second section of this method has only one instruction (last line of Code 6)
that is for informing the remote client about the state change of the call. This
method call will invoke the call state change method of the remote client.
switch (e.Item)
{
case CallState.InCall:
phoneCallAudioSender.AttachToCall(call);
phoneCallAudioReceiver.AttachToCall(call);
publishStreamName = Guid.NewGuid().ToString(); // This is the ID the client publishes
playStreamName = Guid.NewGuid().ToString(); // This is the ID the server publishes
var mediaStream = streamService.CreateStream(playStreamName);
mediaGatewayAudioReceiver = new MediaGatewayAudioReceiver(mediaStream);
mediaConnector.Connect(phoneCallAudioReceiver, mediaGatewayAudioReceiver);
OnInCall(publishStreamName, playStreamName);
break;
case CallState.Completed:
phoneCallAudioSender.Detach();
phoneCallAudioReceiver.Detach();
streamService.RemoveStream(mediaGatewayAudioReceiver.MediaStream);
mediaConnector.Disconnect(phoneCallAudioReceiver, mediaGatewayAudioReceiver);
mediaGatewayAudioReceiver.Dispose();
mediaGatewayAudioReceiver = null;
call = null;
break;
}
OnCallStateChanged(e.Item);
}
Code 6 - The call state change event has to reach the server and both connected clients
The MyClient class contains the methods that invoke the client-side methods according
to the messages from the other clients. These methods are OnCallStateChanged, OnCallErrorOccurred,
OnSetReadyStatus, OnCallRequest and OnInCall. The client-side application has to have
the proper methods that can be invoked in case of these method calls.
The method invocation code line is shown in Code 7. The server can invoke the client-side method
by giving the method's name as the first parameter of the invoke call and then it has to
set all the parameters the client-side method needs.
Code 7 - The server can invoke the client's proper method with this instruction
The MyClient class also contains the basic methods for making, accepting, rejecting and
hanging up the call. These are the basic softphone methods and they can be implemented similar to the
ones you did in case of the softphone application.
Code 8 shows the main functionality of the Call method that is for starting a call towards
a remote client. The call object has to be set with the proper parameters and it has to be
registers to the basic events mentioned before. After all these settings, the server only has to start the call.
Code 8 - Starting a call is similar to the softphone method
The MyClient class used a SIP account in several cased and it was mentioned that
the server itself gives a SIP account to each clients. For this purpose you will need to
define a new class.
You will need to know the usable SIP accounts
The SIPAccountPool class is a helping storage that registers the SIP accounts that the server can
give to the clients. The server registers some SIP accounts when started and the
SIPAccountPool object will handle these.
When a client connects to the server it gets an unused SIP account from the SIPAccountPool
and when it disconnects, the account is put back into the pool to be able to given to
another client. The client itself has no doubt about the SIP account it got and it cannot
modify it in any ways. With this setting you can ensure that the webphone clients will
work properly and they will use a valid and fully handled SIP account.
The SIPAccountPool class only contains two methods and a constructor. It has a
property that is a Queue object that contains SIP accounts. The constructor only
initializes this object.
The two methods in the SIP AccountPool class are GetSIPAccount and AddSIPAcount.
The GetSIPAccount method will provide a SIP account to the connected client. It gets one
account from the Queue object and returns with it. The account will be dequeued from the
pool.
The AddSIPAcount method will add a SIP account to the pool Queue object. This method is called
when the server fills up the pool when it starts and it is also called when a client disconnects and gives
back the SIP account it got from the server.
The AddSIPaccount method only puts the parameter SIP account to the pool Queue.
The main server functionality will be implemented in the SIPGateway class
The server functionality is implemented in the SIPGateway class that is to be added to the project.
This class looks similar to the one that was defined in case of the click-to-call
webphone solution but it contains some new properties to be able to handle the softphone
functionalities and the SIP accounts.
This webphone server will need the properties of a softphone and a SIPAccountPool that
will store the SIP accounts the server can give to the clients. When the server starts
and the OnStart method is called the softphone is initialized and the usable SIP accounts
will be set (Code 9).
The server can write some information or notification on the console window then it
initializes the basic properties.
In this sample the server will generate 18 different SIP accounts that can be registered
with a cycle instruction. Note that you have to
set your own proper SIP accounts you got from your PBX provider in order to have your webphone work.
When the softphone is created, the SoftPhoneFactory class sets the main settings
of the PBX. Note that you need to set your PBX IP
and port information in order to have your webphone work. If you do not know these data,
please ask for them from your PBX provider.
Console.WriteLine("SIP Gateway started");
MyClients = new Dictionary<IClient, MyClient>();
softPhone = SoftPhoneFactory.CreateSoftPhone("192.168.113.6", 5060, 5800, 5060);
sipAccountPool = new SIPAccountPool();
for (int i = 80; i < 99; i++)
{
var name = "oz8" + i;
var sipAccount = new SIPAccount(true, name, name, name, name, "192.168.115.1", 5060);
sipAccountPool.AddSIPAcount(sipAccount);
}
streamService = GetService<IStreamService>();
Code 9 - This method runs in case of the server start
When a client connects to the server, the OnClientConnect method gets called
(Code 10). The server gives a SIP account to the client or in case of no available SIP accounts
it invokes the client's OnRegistrationStateChanged method with the parameter "Connection limit"
that indicates that there is no SIP account at the moment.
If there is at least one available SIP account the client is initialized and put
in the register Dictionary of the server.
Console.WriteLine("New client connected. Remote address: {0}", client.RemoteAddress);
var sipAccount = sipAccountPool.GetSIPAccount();
if (sipAccount == null)
{
client.InvokeMethod("OnRegistrationStateChanged", "Connection limit");
client.Close();
return;
}
var MyClient = new MyClient(client, streamService, softPhone, sipAccount);
MyClients.Add(client, MyClient);
MyClient.Register();
Code 10 - When a client connects to the server it gets a SIP account to use
When a client disconnects the server gets back the SIP account ant puts it back
to the pool of available SIP accounts.
You will need to implement the basic events for the call that are
Call, HangUp, Accept, Reject and the stream that are OnStreamPublishStart and
OnStreamClose. These methods are really similar to the ones you used for the
click-to-call webphone.
After having all these methods, you can start implementing the client-side application with the use of Silverlight.
You will need a new project for the Silverlight client
In order to get all the background support you will need to download and install
Ozeki MediaGateway_SLClientSDK. This SDK provides the background support for
the Silverlight webphone client applications.
Make sure you are using Silverlight 4 for your application. If you don't you can
download and install it to your Visual Studio before starting.
The first step when implementing the webphone client is to create a new Silverlight application.
You can use the File->New Project menu and choose Silverlight application from the list
on the New Project window.
Your Silverlight project will be divided to two parts. One for the actual application
and another for a testing html page where you can run the project. You will not have to do anything with
the TestHTLM page though; you will only have to implement the application.
First step should be building a functional GUI
When building a graphical application, the first step is always about the GUI. You can
build the exact look you want and after that you can write the code that will make
the GUI work.
You can build the GUI on the Design tab using the GUI elements that are provided on
the Toolbox floating panel. For a fully featured softphone application that supports
voice calls, you will need a keypad and some notification labels and, of course, a textbox
that shows the dialed number.
You can set the position and the size of the GUI elements with your mouse or
you can use the Properties panel to set all the properties you want to change.
After setting all the GUI elements, you can have a Silverlight softphone GUI like
the one in Figure 2.
Figure 2 - This is a simple GUI sample for a Silverlight softphone
After having an impressive GUI, it is time to start implementing the functionality
of the client-side solution, but at first, do not forget to register the
Ozeki MediaGateway_SLClientSDK to your project.
Two brand new classes for state and error definition
The client application will contain three classes, two of what will be Enum definitions
for call states and errors. These enums contain the flags that indicate the
events that occur during a call.
The CallState enum has the following values:
Created - the call object has been created
Setup - the call has started
Error - an error occurred during the call initialization
Ringing - the softphone is ringing (incoming call)
RingingWithEarlyMedia - the softphone is ringing with early media
LocalHeld - the local call pary does not want to call for a while
RemoteHeld - the remot call party does not want to call for a while
InCall - the call paries are talking
Completed - the phone was hanged up
Rejected - the call has been rejected
Cancelled - the call has been cancelled
Busy - the remote party is busy
The CallError enum can have the following values:
NotFound - the called number has not been found
Unavailable - the callee is not available
UnknownError - some other error occurred
These two enums are used for flaging the messages that are sent to the server from
the client and they are for indicating some state or error data that will be transferred
to the other client too.
After defining these basic enum values, it is time to implement the client functionality
itself. For this purpose you will need to work in the main class that is generated
in connection with the GUI.
The softphone functionality is attached to the GUI buttons
The GUI buttons has to have the required functionality that must be attached to
the buttons' Click event on the Event panel in Visual Studio.
As you can use the same EventHandler method for all keypad buttons, it is
recommended to write this method and attach it to all buttons on the GUI.
The numeric keypad buttons (including the * and # buttons) only write the
proper character onto the textbox that shows the number to dial. These can have
the same EventHandler method shown in Code 11.
Code 11 - The EventHandler method for all numeric keypad buttons
After writing this single method, you will need to attach it to all keypad buttons' Click event
on the Design tab.
The Call and Stop Call buttons have their own well defined functionality that
can be written in two separate methods.
The EventHandler for the Call button can be created by double clicking on the button
on the Design tab. Code 12 shows how this method can be written.
The method checks the call state according to the CallState enum values and chooses the
right functionality. In case of an incoming call, the Call button accepts the call
while if there is no incoming call, it tries to start an outgoing one.
If the Silverlight does not have permission for using the microphone, it asks for permission before it can do anything
about the call.
if (Microphone.GetPermissionToMicrophone())
{
ReleaseStreams();
switch (phoneState)
{
case PhoneState.InRinging:
connection.InvokeOnConnection("Accept");
setPhoneState(PhoneState.InCall);
break;
case PhoneState.Ready:
connection.InvokeOnConnection("Call", txtDial.Text);
txtboxInfo.Text = "Outgoing call progress.";
setPhoneState(PhoneState.OutRinging);
break;
}
}
else
{
txtboxInfo.Text = "Please, add permission to access microphone.";
}
Code 12 - The default EventHandler for the Call button
The Stop Call button also has more than one functionalities. In case of an incoming
call the Stop Call button can reject the call while in case of an
established call it can hang up the phone. Code 13 shows the method that does this
activity.
This method also decides on the phone states about the proper activity to perform.
switch (phoneState)
{
case PhoneState.InRinging:
connection.InvokeOnConnection("Reject");
setPhoneState(PhoneState.Ready);
break;
case PhoneState.OutRinging:
case PhoneState.InCall:
txtboxInfo.Text = "Call stop, ready to call.";
connection.InvokeOnConnection("HangUp");
setPhoneState(PhoneState.Ready);
break;
}
txtDial.Text = "";
Code 13 - The EventHandler for the Stop Call button
The client application also has to implement the methods that can be invoked by
the server. These are OnSetReadyStatus, OnCallRequest, OnInCall, OnCallErrorOccurred and
OnCallStateChanged.
The OnInCall method defines the activity the client performs in case of a call. It
has to start the microphone and the audioPlayer and it has to attach the
sender and receiver objects to the proper tool (Code 14).
streamSender = new MediaStreamSender(connection);
microphone = Microphone.GetMicrophone();
streamSender.AttachMicrophone(microphone);
streamSender.Publish(publisStreamName);
streamReceiver = new MediaStreamReceiver(connection);
streamReceiver.AttachAudioPlayer(audioPlayer);
streamReceiver.Play(playStreamName);
Code 14 - The method the server invokes in case of a call
The other invoking methods mainly set the call status according to the events that occurred and
the set the notification text on the GUI to inform the user about the actual event.
The constructor of the client class only establishes the connection to the server by
giving the IP and port data as parameters (Code 15).
Note that you have to set your server IP address, port number
and name in order to have your webphone work properly. Make sure this information
are set to the same data as in the App.config XML file of the server.
InitializeComponent();
connection = new MediaConnection("localhost:4502/SIPGateway");
connection.ConnectionStateChanged += new EventHandler<GenericEventArgs<ConnectionState>>(connection_ConnectionStateChanged);
connection.Connect();
Code 15 - The client constructor establishes the connection
Now you have a fully featured Silverlight softphone application and if you set the
IP addresses, ports, and SIP accounts properly, you can use it right now.
Further development possibilities
If the above mentioned functions have called your attention
contact us at info@voip-sip-sdk.com.
You can check licensing information about Ozeki VoIP SIP SDK on How to buy page.
Summary
This articles showed you how to develop a Silverlight softphone application with the support of
the Ozeki VoIP SIP SDK. If you followed the guide properly you already have your fully operational
webphone solution that you can embed in your web site. Now it is time to take a step forward and
explore the other outstanding features provided by Ozeki VoIP SIP SDK.