.NET Tutorials, Forums, Interview Questions And Answers
Welcome :Guest
 
Sign In
Register
 
Win Surprise Gifts!!!
Congratulations!!!


Top 5 Contributors of the Month
david stephan

Home >> Articles >> XML >> Post New Resource Bookmark and Share   

 Subscribe to Articles

How to build a web-to-web VoIP application for Android mobile phones

Posted By:Simon Robert VoIP       Posted Date: February 14, 2014    Points: 25    Category: XML    URL: http://www.dotnetspark.com  

This article explains how to develop a webphone in XML that works in web browsers and it can run in the browser of Android mobile phones, too. It is communicating through RTMP protocol, this way bypassing the fact that many mobile service providers block the usage of SIP protocol inhibiting VoIP communication. This guide presents how to build the flash-based client side and the server side of the VoIP application.
 

How to build a web-to-web VoIP application for Android mobile phones

Introduction

Today we can't talk about the internet without mentioning the VoIP technology, because it is widely used by many applications, for example Skype and Viber uses VoIP for transmitting the voice between the communicating partners and many corporate solutions use it too e.g.: PBX-es and webphones. Nowadays most mobile phones have an internet connection and it is wise to use this for communication because we can spare money with it. If you are speaking through the internet you don't have to pay minute charges, just the price of the internet service.

So I have created a webphone that works in web browsers and it can run in the browser of Android mobile phones too. It is communicating through RTMP protocol, this way bypassing the fact that many mobile service providers block the usage of SIP protocol that is the basis of VoIP communications. My application stands of two parts a flash based client and a server.

Background

For building the server side of the application I have used Microsoft Visual Studio 2010 that needs at least the .NET Framework 3.5 version to work, along with the Ozeki VoIP SIP SDK. As for the client side I have used the Adobe Flash Builder together with Flex 4.5.1 SDK version and the Adobe flashplayer_11_7_plugin_debug for debugging the project. I have written the server side in C# and the client side in MXML language.

Building the client

To start building the client you will need the Adobe Flash builder installed on your computer together with Flex 4.5.1 SDK which has to be extracted to the SDK folder of Flash builder. You are going to need a debugger for the flash player too as I mentioned earlier.

When you have everything ready, start with creating a new Flex project in the Flash Builder. You will have to add the 4.5.1 Flex SDK version at the first page of the new Flex project creation by clicking on the "Configure Flex SDKs." button. A new window will appear where you have to click on the "Add." button and in the next window click on "Browse." and select the folder of the extracted Flex 4.5.1 SDK then tick in the checkbox before the Flex 4.5.1 and click on OK and on Next. You don't have to make changes in the "Server Settings" tab, just click on "Next". On the last page you have to click on "Add SWC." and navigate to the SDK folder or Ozeki VoIP SIP SDK and select "FlashClientSDK.swc". With this you can finish the creation of the project and you can start to write the application.

 

First of all I have created the flash Graphical User Interface (GUI) of the application. You can build it by either putting it together with your mouse from the components of the design tab or you can write it as code. You can see the code of the GUI below.

<s:BorderContainer width="100%" height="125" cornerRadius="10">
	<s:backgroundFill>
		<s:LinearGradient rotation="90">
			<s:GradientEntry color="#FFFFFF" ratio="0"/>
			<s:GradientEntry color="#BEBDBD" ratio="0.1"/>
			<s:GradientEntry color="#BEBDBD" ratio="0.9"/>
			<s:GradientEntry color="#FFFFFF" ratio="1"/>
		</s:LinearGradient>
	</s:backgroundFill>
	<s:Label id="lblClientId" x="237" y="10" text="ClientID"/>
	<s:Label id="lblInfo" width="100%" fontSize="15" horizontalCenter="0" text="Offline"
			 textAlign="center" verticalAlign="middle" verticalCenter="-10"/>
	<s:Button id="btnCall" bottom="10" label="Call" click="btnCall_clickHandler(event)"
			  horizontalCenter="-38"/>
	<s:Button id="btnCallStop" y="92" label="Call stop" click="btnCallStop_clickHandler(event)"
			  horizontalCenter="38"/>
		
</s:BorderContainer>

Code Example 1- The code of the GUI

Figure 1 - The GUI

You can see that the client is very simple, it only has two buttons which are for calling/answering a call and stopping a call and it has two labels which show the state of the client and its name.

I have specified the namespaces of those Ozeki VoIP SIP SDK classes that I am going to use in the flash based client application. You can see the import lines below.

import mx.events.FlexEvent;
			
import ozeki.mediaGateway.ConnectionEvent;
import ozeki.mediaGateway.ConnectionState;
import ozeki.mediaGateway.MediaConnection;
import ozeki.mediaGateway.MediaStreamReceiver;
import ozeki.mediaGateway.MediaStreamSender;
import ozeki.mediaGateway.StreamState;
import ozeki.mediaGateway.StreamStateChangedEvent;

Code Example 2 - The namespace specifications

I have declared the variables I needed for the application, according to the flash syntax, meaning that the type of the variable is at the end of the lines.

private var connection:MediaConnection;
private var streamSender:MediaStreamSender;
private var streamReceiver:MediaStreamReceiver;
private var clientID:String;
private var IncomingCallOwner:String;
private var microphone:Microphone;
private var callProcess:Boolean; 

Code Example 3 - Declaring the variables in Flash

You can see that I have created a variable for the connection of the client, two variables for sending and receiving the stream media of the clients, one variable for identifying the client itself and one for identifying the caller. Lastly I have created a variable to handle the microphone and one for handling the process of the call.

You can see the code responsible for connecting the clients to the server next.

protected function application1_creationCompleteHandler(event:FlexEvent):void
	{
		connection = new MediaConnection("localhost:1935/Web2WebServer");
		connection.client=this;
		connection.addEventListener(ConnectionEvent.CONNECTION_STATE_CHANGED,connecionStateChanged);
		connection.connect();
	}

Code Example 4 - The function of connecting to the server

After the clients have connected to the server their states are going to change from "Offline" to "Online" or in case the connection failed to "Connection failed". You can see the code responsible for this below.

private function connecionStateChanged(event:ConnectionEvent):void
	{
		switch (event.connectionState)
		{
			case ConnectionState.SUCCESS:
				if (lblInfo.text=="Offline")
					lblInfo.text = "Online";
				break;
			case ConnectionState.FAILED:
				lblInfo.text = "Connection failed";
				break;
			case ConnectionState.CLOSED:
				lblInfo.text = "Closed";
				break;
		}
	}

Code Example 5 - Handling the states of the connection

As soon as the client is connected to the server it will search for another client whom it can call. If there is no other client the "Waiting fo other client" message will appear on the phone otherwise the "Ready to call" message will be seen. Basically you won't see the "Online" message after the client connects to the server because one of these two messages will be shown.

public function OnSetReadyStatus(isReady:Boolean, name:String):void
	{
		clientID = name;
		btnCallStop.enabled = isReady;
		btnCall.enabled = isReady;
		lblClientId.text = name;
		lblInfo.text = isReady ? "Ready to call." : "Waiting for other client.";
	}

Code Example 6 - The ready status function

Figure 2 - Ready to call state

After two clients have connected to the server they are ready to make/accept a call, for this I have made the following code.

protected function btnCall_clickHandler(event:MouseEvent):void
	{
		ReleaseStreams();
		callProcess = true;
		if (IncomingCallOwner && IncomingCallOwner.length>0)
		{
			connection.invokeOnConnection("ChangeToIncall");
			
			IncomingCallOwner = "";
			btnCall.enabled = false;
		}
		else
		{
			connection.invokeOnConnection("Call", clientID);
			lblInfo.text = "Outgoing call progress.";
			btnCall.enabled = false;
		}
	}

Code Example 7 - The call/accept call button

Figure 3 - Outgoing call state

You can see that if our client receives a call by clicking on the call button it will accept it otherwise it will be able to make a call.

Every call must be ended once, for this I have created the code responsible for hanging up the calls.

protected function btnCallStop_clickHandler(event:MouseEvent):void
	{
		if (callProcess)
		{
			lblInfo.text = "Call stop, ready to call.";
			connection.invokeOnConnection("CallStop");
			ReleaseStreams();
			btnCall.enabled = true;
		}
	}

Code Example 8 - The call stop button

Figure 4 - Call stopped state

When the other client calls ours we have to be notified of it, this is why I have made the OnCallRequest method.

public function OnCallRequest(remotpartyId:String):void
	{
		callProcess = true;
		IncomingCallOwner = remotpartyId;
		lblInfo.text = "Call received from " + remotpartyId; 
	}

Code Example 9 - The method that notifies about the incoming call

As you can see the "Call received from" + 'the clients name' will appear on our client when we receive an incoming call.

Figure 5 - Call received state

After the call is answered the state of the clients will change to "Incall" and the client will get access to the microphone, moreover a streamSender will initialized for the communication.

public function OnInCall():void
	{
		lblInfo.text = "Incall";
		microphone = Microphone.getMicrophone();
		streamSender = new MediaStreamSender(connection);
		streamSender.attachMicrophone(microphone);
		streamSender.addEventListener(StreamStateChangedEvent.STREAM_STATE_CHANGED, streamSender_StreamStateChanged);
		
		streamSender.publish(clientID);
	}

Code Example 10 - The clients in InCall state

The web browser will ask you to grant access to the microphone for the application, just click on "Allow".

Figure 6 - Granting access to the microphone

After you have granted access the clients state will change to Incall.

Figure 7 - Both clients in "Incall" state

When the call is established the clients will need to stream their media for the other and play the received media. I made the following code for this.

public function OnPlayRemoteStream(remoteparty:String):void
	{
		streamReceiver = new MediaStreamReceiver(connection);
		streamReceiver.addEventListener(StreamStateChangedEvent.STREAM_STATE_CHANGED, streamReceiver_StreamStateChanged);
		streamReceiver.play(remoteparty);
	}

Code Example 11 - The code for playing the received media

When the call is stopped by the other client the following method will be invoked.

public function OnCallStop():void
	{
		lblInfo.text = "Remoteparty finished the call.";
		btnCall.enabled = true;
		ReleaseStreams();
	}

Code Example 12 - Stopping the calls

Figure 8 - A stopped call by the other client

The streamSender and streamReceiver need some minor debugging which I have made with the following code.

private function streamSender_StreamStateChanged(event:StreamStateChangedEvent):void
	{
		switch (event.streamState)
		{
			case StreamState.PUBLISHING_SUCCESS:
				connection.invokeOnConnection("PlayRemoteStream");
				break;
			default: 
				trace(event.streamState);
				break;
		}
	}
	
	private function streamReceiver_StreamStateChanged(event:StreamStateChangedEvent):void
	{
		trace(event.streamState);
	}

Code Example 13 - The debugging of streamSender and streamReceiver

Finally the MediaStreamSender and MediaStreamReceiver object have to be released when the call is stopped and when a new call is made. You can see the code snippet responsible for this below.

private function ReleaseStreams():void
	{
		if (streamSender != null)
		{;
			streamSender.removeEventListener(StreamStateChangedEvent.STREAM_STATE_CHANGED, streamSender_StreamStateChanged);
			streamSender.close();
		}
		if (streamReceiver != null)
		{
			streamReceiver.removeEventListener(StreamStateChangedEvent.STREAM_STATE_CHANGED, streamReceiver_StreamStateChanged);
			streamReceiver.close();
		}
	}

Code Example 14 - Releasing the streams

 

Building the server

The server part of the application is made as a project in Microsoft Visual Studio 2010. It is mainly written in C# language and partly in XML as you will see in the followings. The server is a consol application that can handle different client types. In our case it's going to be a flash type client.

You can see the setting of the client type in the following XML code which can be found in the App.config file of the project.

<?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" listenedPort="1935" serviceName="Web2WebServer"/>
      </providers>
    </mediaGatewayConfig>
  </mediaGateway>
</configuration>

Code Example 15 - Setting the client type

As you can see in the code below the server is basically an Ozeki MediaGateway class.

class Web2WebGateway : MediaGateway
{
    private Dictionary<IClient,MyClient> Clients;
    private int clientCounter;
    private int busyClients;

Code Example 16 - The definition of Mobile2Gateway

When the server is started the following two methods will be invoked.

public Web2WebGateway()
    {
        Console.WriteLine("Web2Web Gateway starting...");
    }

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

Code Example 17 - Starting the server (C#)

Figure 9 - The started server

You can see that the server shows how many days are left from the trial of Ozeki VoIP SIP SDK that I am using for this application, moreover you can see the maximum number of SilverLight and Flash client and that the server was successfully started.

The server has to handle the connection of clients, for this purpose I have written the following method which writes the IP address of the connected client to the consol and notifies the other clients about the connected client.

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 Example 18 - The method for handling the connection of clients

You can see the connection of a client on the figure below.

Figure 10 - A connected client

The server also has to handle the disconnection of the clients which works similar to the connection as you can see in the code below.

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

Code Example 19 - The method for handling the disconnection of clients

You can see how the disconnection of a client looks like in the server consol on the following figure.

Figure 11 - A disconnected client

When a call is made between two clients the server has to start handle the streamed media of the clients and it does with the following code.

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 Example 20 - Handling the streamed media of the clients

If two clients have successfully connected to the server they can call each other with the code below.

public void Call(IClient invokerClient, string requestOwner)
    {
        foreach (KeyValuePair<IClient, MyClient> keyValuePair in Clients)
        {
            //Searchs the first not busy connected client and sets theirs remote party.
            if (keyValuePair.Key != invokerClient && !keyValuePair.Value.IsBusy)
            {
                MyClient invoker = Clients[invokerClient];
                MyClient callee = keyValuePair.Value;
                invoker.RemoteParty = callee;
                callee.RemoteParty = invoker;
                callee.OnCallRequest(requestOwner);
                return;
            }
        }
    }

Code Example 21 - The method for calling

You can see the call in the consol of the server on the following figure.

Figure 11 - A call between the clients

After the call is answered by the called client the state of the clients will change to Incall and all the other clients will be notified so they won't be able to call up these clients.

When the call is established the server will indicate to the client that they can start streaming their media with the code below.

public void PlayRemoteStream(IClient client)
    {
        //foreach (MyClient c in Clients.Values)
        {
            Clients[client].OnPlayRemoteStream();
        }
    }

Code Example 22 - The server indicates to the clients to start playing their streams

Figure 12 - The clients publishing their streams

As you can see the clients have started to publish their streams after the call is made between them.

When the call is stopped between the clients their streams will stop and their states are going to change back to available.

public void CallStop(IClient invokerClient)
{
    if (Clients.ContainsKey(invokerClient))
    {
        MyClient invoker = Clients[invokerClient];
        invoker.RemoteParty.OnCallStop();
    }
}

Code Example 23 - Stopping the call

The server notifies the clients about their call states whenever a client connects to the server or is disconnected from it. You can see the code responsible for this below.

private void NotifyClientsAboutTheirCallStatus()
   {
        busyClients = 0;
        foreach (MyClient c in Clients.Values)
        {
            if (c.IsBusy)
                busyClients++;
        }
        bool isReady = Clients.Count > 1 && Clients.Count-busyClients > 1;

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

Code example 24 - Client notification

The server has invoked many methods inside it's owns. You can see the list of these methods below which can be found in the MyClient.cs file of the server project.

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

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

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);
}

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

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

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

Code Example 25 - The methods of MyClient

Summary

I have created a webphone which is useful for companies to get in contact with their customers easily. The customer only has to open the webphone in a browser, let it be a normal web browser for PC or one for android phones then she/he only has to click on call and an agent will answer it. It was quite simple to create this application thanks to the Ozeki VoIP SIP SDK, because it has implemented every communication protocol I used and so I didn't have to implement them myself. I could concentrate on the development on the application and it made my work much faster. Thanks to this I can recommend Ozeki VoIP SIP SKD to anyone who is wishing to create VoIP applications without the need of implementing the communication protocols herself/himself.

References


 Subscribe to Articles

     

Further Readings:

Responses

No response found. Be the first to respond this post

Post Comment

You must Sign In To post reply
Find More Articles on C#, ASP.Net, Vb.Net, SQL Server and more Here

Hall of Fame    Twitter   Terms of Service    Privacy Policy    Contact Us    Archives   Tell A Friend