info@voip-sip-sdk.com Tel: +36 1 371 0150

How to implement web to web voice calls using Silverlight microphone access

Explanation

Prerequisities

Download: 01_Web2Web_Voice.zip

If you want to implement video calls, please check How to implement web to web video calls using Silverlight camera access >>>

This article leads you through the main steps of implementing a webphone solution for web to web voice calls using Silverlight technology for the client-side implementation. If you read this article carefully, you will be familiar with all tools and methods that are necessary to know when you want to build your own webphone solution.

Introduction

Web to web communication is one of the main purposes of webphone technologies. This solution means that two remote client can communicate with each other by using the web page embedded webphone applications (Figure 1).


Figure 1 - Web to web communication means that two embedded webphones communicate with each other

A call establishment between two webphone clients invokes a lot of background activity as the server has to synchronize both clients' status and settings before the actual voice streaming process can start (Figure 2).

When the caller client that is Client "A" in Figure 2 sends a Call message to the server in order to call Client "B", the server calls the OnCallRequest method for Client "B" to initiate the call.

As the Client "B" gets the OnCallRequest call, it sends the ChangeToInCall message to the server that sends the OnInCall invoke method to both clients.

When both clients register the OnInCall message they send the virtually parallel calls of PublishStream methods to the server. If the stream publishing succeeded, the server sends back an answer to each client with the reassurance of the success.

After the clients got the message about the succeeded stream publish, they send the PlayRemoteStream message to the server that invokes the OnPlayRemoteStream method of the other remote client with the information of the sender's ID.

When both clients received the OnPlayRemoteStream message from the server, the call has been established and the voice transmission can be started.


Figure 2 - The server has to synchronize the clients for establishing the call

The simplest version of a webphone is the click-to-call webphone type when the webphone client can only call a previously set contact, in this case another webphone. These click-to-call clients usually contain one or two buttons for calling and ending the call and a textbox that can show notifications and information about the call.

As the webphone solutions are always client-server applications, you will need to have both sides implemented to be able to use your webphone.

This guide shows you how to create a simple webphone server for voice transmission and how to implement a click-to-call webphone client using Microsoft Silverlight. The sample program, this guide shows is written in C# language and for being able to use it, you will need to have some tools installed on your computer.

Requirements for the sample program:

  • Visual Studio 2010 or Visual Studio 2010 Express
  • Microsoft Silverlight 4 Tools for Visual Studio 2010
  • Ozeki VoIP SIP SDK
  • Ozeki SLClientSDK.dll

When implementing a webphone solution, you will need to write the server-side application first. This is essential because the client application will need to connect to the server so before being able to run the client, you will need a fully operable server application.

Step 1. How to create a new webphone server?

After starting your Visual Studio, you will need to create a new project for your webphone server. As the server is only a service provider that runs in the background it can be a console application that means that it will not have a GUI.

You can create a new project by choosing the File->New Project menu from the menu bar (Figure 3). This menu opens the New Project window where you can choose the project type from a template list.


Figure 3 - File->New Project menu helps you to create a new project

You should choose Console Application from the list on the New Project window shown in Figure 4. You can rename your project and change the project location according to your wish on the bottom panel of this window. You will be able to do these changes later, but it is easier to do them before starting the programming stage.


Figure 4 - You can choose the project type and set the project properties on the New Project window

Step 2. How to get background support from Ozeki VoIP SIP SDK?

The webphone server application needs the background support of the MediaGateway SDK that is part of Ozeki VoIP SIP SDK. You will need to register this SDK to your server project in order to use its functionality support.

You can register your SDK to your project on the Solution Explorer panel that is usually on the right side of the Visual Studio window. You need to right click on the References label and choose Add Reference from the appearing list (Figure 5).


Figure 5 - You can register your Ozeki VoIP SIP SDK on the Solution Explorer panel

You can browse your hard drive for ozeki.dll file and press OK when you have found it (Figure 6). If you have installed Ozeki VoIP SIP SDK without interaction, the .dll will be in "C:\Program Files\Ozeki\SDK" folder.


Figure 6 - You can choose ozeki.dll by browsing your file system

After registering Ozeki VoIP SIP SDK you will be able to use the MediaGateway SDK that provides the background support for the webphone server application.

Step 3. How to implement the server functionality?

You can add new classes to your project on the Solution Explorer panel. You should right click on the project name and choose Add->Class from the floating menu that appears or you can simply press Shift+Alt+C key combination to create a new class (Figure 7).

In the Add New Item window, you can specify the item you want to add to your project. In this case it will be a Class but you can also choose for example Interface, Windows Form, UML diagrams, XML files, etc. You can specify the Name of the new item and press OK to create it.


Figure 7 - You can define a new class on the Solution Explorer panel

After having your brand new class, you will need to be able to reach the support Ozeki VoIP SIP SDK provides and this is more comfortable without using namespace labels all the time, so you will need to add a new line (Code 1) in the using section of the class.

using Ozeki.MediaGateway;

Code 1 - You have to specify a new namespace usage in order to avoid labeling all the tools it provides

This single code line will save you a lot of tying and also allows you to inherit the methods and tools that are implemented in Ozeki.MediaGateway.MediaGateway class. You have to specify this inheritance by expanding the class definition line to look like as the one in Code 2.

class Web2WebGateway : MediaGateway

Code 2 - This line ensures that your server class inherits the MediaGateway class' methods

You will need two fields in your class, one for the clients and the other for counting the clients. As for the clients, you can use the System.Collections.Generic.Dictionary@lt;TKey, TValue> class. This class is a hash table that stores the values with a specific key for identification.

The Dictionary class will be more comfortable to use if you append another line to your using section (Code 3).

using System.Collections.Generic;

Code 3 - With this line in your using section you can refer to the Dictionary class without labeling it with System.Collections.Generic

The Dictionary class is a generic class, so you can specify the type of the key and the value of the objects that are stored in it. In this case the key type will be IClient that is a built-in type in Ozeki VoIP SIP SDK and the value type is MyClient that is a class you will have to define yourself (Code 4).

private Dictionary<IClient,MyClient> Clients;

Code 4 - You can specify the type of the key and the value elements of the Client Dictionary

MyClient class is another self-defined class that you should add to your project the same way as you did in case of the Web2WebGateway class that is the main class of this project.

The MyClient class is basically for defining the invoke methods for performing the client-side methods. The server has to be able to make the clients start some of their operations in order to ensure the proper work of the webphone.

The MyClient class stores three basic information fields that are Name, IsBusy and RemoteParty. These give you the basic knowledge about the state of the represented client and the other client it is connected to if there is any.

MyClient class also implements the client-side method calls that are OnStartPlay, OnSetReadyStatus, OnCallRequest, OnInCall, OnPlayRemoteStream, OnCallStop. These methods define the invoke calls for the clients that were shown in Figure 2 above.

The client-side solution has to have a proper method for all these invoke methods that will be called when the server calls these methods with the specific client ID.

The OnStartPlay method implements an asynchronous message from the server to the remote client about that the other client started sending a voice stream. On client side the OnPlay method will be called to perform the proper functionality.

public void OnStartPlay(string remotpartyId)
{
    Client.InvokeMethod("OnPlay", remotpartyId);
}

Code 5 - The asynchronous message from the server to the client about the start of an incoming voice stream

The OnSetReadyStatus method invokes the client side method OnSetReadyStatus that will make the basic setting for the client to be ready for incoming and outgoing calls (Code 6).

public void OnSetReadyStatus(bool isReady, string name)
{
    try
    {
        Client.InvokeMethod("OnSetReadyStatus", isReady, name);
    }
    catch (Exception)
    {}
}

Code 6 - The client has to be ready for calls and this method settles it

When a call request comes to the server from one of the clients, it will send an OnCallRequest to the other client with the information about the caller and sets both clients busy. This is done because all new clients that connect to the server can only call another not busy client, and in order to avoid collision, the server instantly sets both connected parties busy (Code 7).

public void OnCallRequest(string requestOwner)
{
    Console.WriteLine("Call request received from {0} to {1}",requestOwner, Name);
    RemoteParty.IsBusy = true;
    IsBusy = true;
    Client.InvokeMethod("OnCallRequest", requestOwner);
}

Code 7 - In case of a call request the server sends the calling information to the other client

The OnIncall invoke method makes both clients start their OnIcall method that will ensure the synchronization of the clients to the call (Code 8).

public void OnInCall()
{
    Console.WriteLine("Sends 'start publishing' sign to the clients.");
    Client.InvokeMethod("OnInCall");
    RemoteParty.Client.InvokeMethod("OnInCall");
}

Code 8 - The OnInCall method invokes the same functionality of both connected clients

The OnPlayRemoteStream method is a message to the clients that the other party is ready for sending voice streams to them. The voice call is established properly when both clients get the OnPlayRemoteStrem message from the server (Code 9).

public void OnPlayRemoteStream()
{
    Console.WriteLine("PlayRemoteStream - client Name : {0} starts to play remoteStream: {1}", RemoteParty.Name, Name);
    RemoteParty.Client.InvokeMethod("OnPlayRemoteStream", Name);
}

Code 9 - The OnPlayRemoteStream call is the last step when establishing a line between two clients

The server calls the OnCallStop method when one of the clients sent a CallStop message to the server. In this case both clients will be free to start and accept new calls as their IsBusy flag will be set to false (Code 10).

public void OnCallStop()
{
    IsBusy = false;
    RemoteParty.IsBusy = false;
    Client.InvokeMethod("OnCallStop");
}

Code 10 - The end of the call is made properly by setting the IsBusy flags to false

After writing these essential client invocation methods and having some getters and setters for the basic fields of MyClient class you can return to your main server class and finish it properly.

The main server functionality contains the actions that have to be done when the server starts, when a client connects or disconnects to or from the server and when a stream publishing starts. These methods are written in MediaGateway SDK and you need to override them for defining your own server behavior.

As the server is a console application, it will use the Console.WriteLine method to write some notifications or information about the current actions that are in process.

You will need to override the OnStart, OnClientConnect, OnClientDisconnect and OnStreamPublishStart methods of the MediaGateway.

The OnStart method defines all the activities that invoke when the webphone server starts. In this case the server only initializes the Clients table and writes a notification onto the console about the start (Code 11).

public override void OnStart()
{
    Clients = new Dictionary<IClient, MyClient>();
    Console.WriteLine("Web2Web Gateway started.");
}

Code 11 - The server start mean that the Clients table has to be initialized

When a client sends a connection request to the server, the OnClientConnect method will be called. If the client has not been added to the Clients table before, the server adds it to the table and calls the NotifyClientsAboutTheirCallStatus() method that also has to be written in this class (Code 12).

public override void OnClientConnect(IClient client, object[] parameters)
{
    Console.WriteLine( "{0} client connected to the server.",client.RemoteAddress);
    if (!Clients.ContainsKey(client))
    {
        Clients.Add(client, new MyClient(client, string.Format("client{0}", clientCounter++)));
        NotifyClientsAboutTheirCallStatus();
    }
}

Code 12 - The server has to do the basic setting every time a client connects

The NotifyClientsAboutTheirCallStatus() method should contain a repetitive instruction that is a foreach block seen in Code 13 that sets the ready status of all the connected clients.

foreach (KeyValuePair<IClient, MyClient> keyValuePair in Clients)
                {
                    keyValuePair.Value.OnSetReadyStatus(isReady, keyValuePair.Value.Name);
                }

Code 13 - The NotifyClientsAboutTheirCallStatus method should set the ready status of all clients

When a client is disconnected from the server it has to be removed from the Clients table and all clients has to be notified about the status change. If the disconnected client had an established call line at the time of disconnection, the remote connected client has to get an OnCallStop message in order to stop the call properly. The OnClientDisconnect method that defines this functionality is seen in Code 14.

public override void OnClientDisconnect(IClient client)
{
    Console.WriteLine("{0} client disconnected from the server.", client.RemoteAddress);
    if (Clients.ContainsKey(client))
    {
        MyClient disconnectedClient = Clients[client];
        if (disconnectedClient.IsBusy && disconnectedClient.RemoteParty!=null)
            disconnectedClient.RemoteParty.OnCallStop();
        Clients.Remove(client);
        NotifyClientsAboutTheirCallStatus();
    }
}

Code 14 - When a client disconnects, all the lines has to be cut properly

The OnStreamPublisStart method is for performing the voice stream start towards the connected clients. It only contains a notification line written on the console and a method call that starts publishing the stream that is represented by the mediaStream object to the client (Code 15).

public override void OnStreamPublishStart(IClient client, IMediaStream mediaStream)
{
    Console.WriteLine("client : {0} publish his stream : {1}",client.RemoteAddress,mediaStream.Name);
    base.OnStreamPublishStart(client, mediaStream);
}

Code 15 - The server has to be able to start voice streaming towards the clients

You will need to write Call, ChangeToIncall, PlayRemoteStream, CallStop methods in your server class that are mainly used for notifying the clients about other side actions and for setting some client data on the server side.

The last step for the server implementation is to create a configuration file that will store the basic information about the server such as the IP address, the port number it uses, etc. For this purpose you will need to add a New Item (as it was shown above in Figure 7) to your project and this time it should be an XML file with the name of App.config.

The App.config XML file is shown in Code 16. You can use similar setting for your server but note that you have to set your proper IP address and port number in order to have a properly operable server.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <sectionGroup name="mediaGateway">
      <section name="mediaGatewayConfig" type="Ozeki.MediaGateway.Config.MediaGatewayAppConfig, VoIPSDK"/>
    </sectionGroup>
  </configSections>
  <mediaGateway>
    <mediaGatewayConfig>
      <providers>
        <add type="silverlight" localIPAddress="0.0.0.0" listenedPort="4502" serviceName="Web2WebServer"/>
        <add type="flash" serviceName="Web2WebServer"/>
      </providers>
    </mediaGatewayConfig>
  </mediaGateway>
</configuration>

Code 16 - The configuration XML file of the webphone server

After having a fully configured server-side application, it is time to start making the client side solution with Silverlight.

Step 4. How to implement the client?

The Silverlight client-side application needs the MediaGateway_SLClient.dll to be able to reach the support Ozeki provides for a Silverlight webphone client.

You will need to create a new Visual studio solution and have a new Silverlight Application for your client. You will need to be sure that the Silverlight version is correct that is Silverlight 4 and the web project type is ASP.NET Web Application Project (Figure 8).


Figure 8 - You have to make sure your setting are proper for the client

You will need to register the Ozeki MediaGateway_SLClient.dll to your project on the Solution Explorer panel.

The Silverlight Visual Studio project has two main parts. One for the application itself that contains the GUI and the background functionalities and another that is a test html page that will contain your Silverlight application to be able to be tested. You will not need to do anything with this html, it is only for testing purposes.

First you will need to create a graphical user interface (GUI) for your webphone. As this example is a click-to-call webphone, you will only need to have two buttons and some notification textboxes.

You can create the GUI as easily as in case of a Windows Forms Application only that there are different GUI elements you can choose from this time. After some properties to be set your GUI should look similar to the one in Figure 9.


Figure 9 - A simple click-to-call webphone GUI using Silverlight

The client has to have the following functionalities implemented:

  • make connection to the server
  • send notification about a connection state change
  • start and accept a call
  • stop a call
  • play incoming audio stream
  • set the client status
  • detect an incoming call
  • establish the MediaStreamSender
  • release the used tools

The above mentioned functions will be implemented in separate methods, some of which will contain the button handlers and the textboxes' settings and the others will be the actual client-server communication and setting methods.

The first step as in case of every class is to add the useful namespace specifications to the using section of the code (Code 17).

The using.System lines are used for the C# built-in tools and the last two using lines are the ones that are namespaces provided by the Ozeki VoIP SIP SDK. These using commands are essential for the client application if you do not want to label all the Ozeki MediaGateway tools with the full namespace path.

using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using Ozeki.MediaGateway;
using Ozeki.MediaGateway.SilverlightService;

Code 17 - You need to extend the using section with the Ozeki MediaGateway namespaces

You will need to define some fields in your class that are used for the connection and the communication, and some of them are for storing the local settings of the client (Code 18).

The MediaConnection, MediaStreamSender, MediaStreamReceiver, AudioPlayer and Microphone classes are provided by the Ozeki VoIP SIP SDK. The other tools are mainly for standard setting storages within the class.

The MediaConnection object is the one that is for establishing a connection between the client and the server. It needs at least the server address as parameter, but you can also add a value for the connection timeout as an optional second parameter.

The MediaStreamSender and MediaStreamReceiver objects are for sending and receiving media data, in this case audio streams to and from the server. The AudioPlayer object is the tool that can actually play the received audio stream.

private MediaConnection connection;
private MediaStreamSender streamSender;
private MediaStreamReceiver streamReceiver;
private AudioPlayer audioPlayer;
private Microphone microphone;
private string clientID;
private string IncomingCallOwner;
private bool callProcess;

Code 18 - There are some essential tools to be defined for the client class

The connection_ConnectionStateChanged method is mainly a switch block that initializes the AudioPlayer in case of a succeeded connection to the server and sets the information textbox Text to the proper state that can be Online for a succeeded connection or Connection failed (Code 19).

switch (e.Item)
{
case ConnectionState.Success:
    txtboxInfo.Text = "Online";
    audioPlayer = new AudioPlayer();
    connection.Client = this;
    break;
default:
    InvokeGUIThread(() => {
        txtboxInfo.Text = "Connection failed.";
        btnStopCall.IsEnabled = btnCall.IsEnabled = false;
    });
    break;
}

Code 19 - The client has to be up-to-date about the connection status

The Call button has double functionality that needs to be implemented in its handler method. You can get the default handler method of the button by double clicking on it on the Design tab.

You will need to check if there is any incoming call as in that case the call button will accept the call. In case of no incoming call, the client will start a call and send a Call message to the server that will forward the call request to an available connected client (Code 20).

You will also have to check if the Silverlight has the right to access the microphone that is connected to your computer. It is essential as the microphone is the device that will capture your speaking for the audio stream that will be delivered to the other party.

if (Microphone.GetPermissionToMicrophone())
{
ReleaseStreams();
callProcess = true;
if (!string.IsNullOrEmpty(IncomingCallOwner))
{
    connection.InvokeOnConnection("ChangeToIncall");

    IncomingCallOwner = "";
    btnCall.IsEnabled = false;

}
else
{
    connection.InvokeOnConnection("Call", clientID);
    txtboxInfo.Text = "Outgoing call progress.";
    btnCall.IsEnabled = false;
}
}
else
{
txtboxInfo.Text = "Please, add permission to access microphone.";
}

Code 20 - You need to implement double functionality for the Call button

The Stop Call button has a simple task to perform that is stopping an existing call. If there is no call established, the Stop Call button will do nothing. In case of an existing call the client will stop the call by invoking the connection's CallStop method (Code 21).

Stopping a call also means that you have to free the Streams you have used. This is done by the method call ReleaseStreams(). This method has to be written in this class too. After stopping a call you will be able to start or accept a new one, so you will need to be able to use the Call button, this is why it is enabled.

if (callProcess)
{
txtboxInfo.Text = "Call stopped, ready to call.";
connection.InvokeOnConnection("CallStop");
ReleaseStreams();
btnCall.IsEnabled = true;
}

Code 21 - The Stop call button stops the call if it exists

You will also need to write some helping method for the main purposes of the client that can enable, disable or simply initiate the fields in case of need.

The call establishment needs the basic initializations of the used tools that should be written in the OnInCall method (Code 22).

In order to be able to establish and use a call, you need to set the microphone and attach it to the MediaSender. Ozeki MediaGatewy SDK gives you the support for these tasks, so you will only need to call some methods as it is shown in Code 22.

The MediaStreamSender object also has to be signed up to the StreamStateChanged event as it is essential to know if the stream can be sent or not.

The last step in case of a call is to publish the client ID to indicate that the client is ready for the media sending and receiving.

txtboxInfo.Text = "Incall";
    microphone = Microphone.GetMicrophone();
    streamSender = new MediaStreamSender(connection);

    try
    {
        streamSender.AttachMicrophone(microphone);
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }

    streamSender.StreamStateChanged += new EventHandler<GenericEventArgs<Ozeki.MediaGateway.SilverlightService.StreamState>>(streamSender_StreamStateChanged);

    streamSender.Publish(clientID);

Code 22 - The main initialization and connection settings for the call

In order to be able to receive audio data, you will need to write the OnPlayRemoteStream method (Code 23) that will initiate and attach the MediaStreamReceiver object to the connection. You will also need to start the stream playing to be able to hear what the other side tells.

streamReceiver = new MediaStreamReceiver(connection);
streamReceiver.StreamStateChanged += new EventHandler<GenericEventArgs<StreamState>>(streamReceiver_StreamStateChanged);
streamReceiver.AttachAudioPlayer(audioPlayer);
streamReceiver.Play(remoteparty);

Code 23 - The receiver also has to be initialized and set in order to get audio information

At this point, you will only have to implement some EventHandler methods with basic functionality and your webphone client will be perfect.

Step 5. How to use your webphone?

In order to be able to use your webphone, you will need to start your server (you can do this from Visual Studio if you like) and you will need to have two computers with the access to the server application.

You will need to run the webphone client on both computers. Make sure that the server address has been set properly in the clients. The sample clients automatically connect to the server and you will start the call when there are two available (not busy) clients connected. This is the time when the Call button will be enabled.

You can start a client from Visual Studio by running it or you can use the web browser where you will need to add the html page on which your client is set.

When there are two clients connected to the server, you will be able to start a call between them and if you have done all the programming steps properly, your webphones will communicate properly.

Ozeki web to web Silverlight webphone sample Visual Studio project can be obtained by
opening the download page:
Download Ozeki web to web Silverlight webphone sample project now

This article gave you a detailed step-by-step guide about how to create your first webphone application in Visual Studio using Silverlight technology for the client-side application. If you followed this guide properly, now you are fully familiar of all the tools and term in relation with a click-to-call webphone application and you are ready to write your own, fully operable webphone solution.

If you have any questions or need assistance, please contact us at info@voip-sip-sdk.com

You can select a suitable Ozeki VoIP SIP SDK license for your project on licensing page

Related Pages

Operating system: Windows 10 Windows 8, Windows 7, Vista, 200x, XP
Development environment: Visual Studio 2012, Visual Studio 2010, Visual Studio 2008, Visual Studio 2005
Programming language: C#.NET
Supported .NET framework: .NET Framework 4.5, .NET Framework 4.0, .NET Framework 3.5 SP1
Software development kit: OZEKI VoIP SIP SDK (Download)
VoIP connection: 1 SIP account
System memory: 512 MB+
Free disk space: 100 MB+

Ozeki Cookie Policy
Ozeki Informatics Ltd uses cookies to provide you the best experience on this website. The further use of the website will be considered as an agreement to the use of cookies. For more information read this website.

Cookies are enabled You are browsing the optimized version of this website. For more information read this website.