Course 2 / Lecture 5

How to build a VoIP PBX in C#

How to create MediaGateway connection in your PBX

This article is a brief introduction about the VoIP PBX MediaGateway connection in relation with Ozeki VoIP SIP SDK. After reading through this page you will be fully familiar with all the essential terms concerning VoIP PBX and Ozeki MediaGateway connection and what you will need for creating your own solution using Ozeki VoIP SIP SDK.

This PBX solution is an extended version of the one introduced in the "VoIP PBX Voice Mail" article. The following sections will show the basic changes in the PBX code and the necessary extensions for the Ozeki MediaGateway server support.

Introduction

Please be informed that PBX development support is available in Ozeki VoIP SIP SDK from version 10.0

This article shows the easiest way to connect an Ozeki VoIP PBX system with the Ozeki MediaGateway technology, this allows you to connect to your PBX with a webphone application too. As both parts of the Ozeki VoIP SIP SDK provides great support and basic background implementations, you only need to define some objects and call some methods for having a great cooperation between these two.

The following program code uses the background support of Ozeki VoIP SIP SDK, therefore you will need to download and install the SDK on your computer before starting to use the program code. You will also need to have Visual Studio 2010 or compatible IDE and .NET Framework installed on your system, as the program code below is written in C# language.

The client side application of the example program is exactly the same as the one introduced in the Flash SIP softphone article. You can find a detailed guide for the client solution in that article. The following sections will only show the PBX implementation.

The PBX OnStart method needs to be extended with the MediaGateway support. This example program handles Flash client connections through the MediaGateway server as it is shown in Code 1.

protected override void OnStart()
{
	SetDialplanProvider();
	SetListenedPort();

	InitMediaGateway();

	Console.WriteLine("PBX started.");
	base.OnStart();
}

void SetDialplanProvider()
{
	var callManager = GetService<ICallManager>();
	var extensionContainer = GetService<IExtensionContainer>();
	callManager.DialplanProvider = new MyDialplanProvider(userInfoContainer, extensionContainer);
}
		
private void SetListenedPort()
{
	SetListenPort(localAddress, 5060, TransportType.Udp);
	Console.WriteLine("Listened port: 5060(UDP)");
}
        
void InitMediaGateway()
{
	var pbxCallFactory = GetService<IPBXCallFactory>();
	var extensionContainer = GetService<IExtensionContainer>();
	var callManager = GetService<ICallManager>();

	var mediaGatewayConfig = new MediaGatewayConfig().AddConfigElement(new FlashConfig(serviceName: "SIPGateway"));

	mediaGateway = new MyMediaGateway(mediaGatewayConfig, pbxCallFactory, extensionContainer, callManager, localAddress);
	mediaGateway.Start();
}
Code 1 - The OnStart method

The MediaGateway server that is used in the PBX is implemented in a class that is derived from the Ozeki.MediaGateway.MediaGateway class provided by the Ozeki VoIP SIP SDK. Code 2 shows the declaration line of the class. This derivation is necessary to get all the provided functionalities for the webphone support in the PBX.

class MyMediaGateway : Ozeki.MediaGateway.MediaGateway
Code 2 - Class definition

The MediaGateway implementation this PBX solution handles the calls provided by the PBX factory, therefore the constructor (Code 3) of the class gets a PBX factory as parameter.

The MediaGateway also needs to handle the PBX extensions as the MediaGateway client class is defined to be a PBX extension as this is the easiest way for handling it.

The MediaGateway clients get a SIP account from the PBX automatically during the login process. This SIP account is provided by a class called AccountPool. The SetAvailableAccounts(); instruction in the constructor code fills this pool with available SIP accounts.

public MyMediaGateway(MediaGatewayConfig mediaGateway, IPBXCallFactory pbxCallFactory, IExtensionContainer extensionContainer, ICallManager callManager, string localAddress):base(mediaGateway)
{
	Console.WriteLine("MediaGateway starting...");

	myClients = new Dictionary<IClient, MediaGatewayClient>();

	this.pbxCallFactory = pbxCallFactory;
	this.extensionContainer = extensionContainer;
	this.callManager = callManager;
	this.localAddress = localAddress;

	SetAvailableAccounts();
}
Code 3 - MediaGateway constructor

Code 4 shows the method that produces the SIP accounts for the webphone clients. In this example the accounts from the range 900 to 999 are available for the webphones that try to login to the PBX.

private void SetAvailableAccounts()
{
	accountPool = new AccountPool();

	for (int i = 900; i < 1000; i++)
	{
    	var userName = i.ToString();

		accountPool.AddAccount(userName);
	}
}
Code 4 - Setting the SIP accounts

The MediaGateway handles the client connections in the OnClientConnect method. This is a method provided by the Ozeki SDK MediaGateway class and that needs to be overridden in this class (Code 5).

When a webphone client tries to connect to the PBX, it gets a SIP account form the account pool if there is at least one available. If there are no available SIP accounts, the registration will be rejected by the PBX with the message to the client that the number of connections exceeded the limit.

The MediaGateway server stores the connected clients in a Dictionary object called myClients. When a client connected to the PBX, it will be put into this Dictionary and will be deleted from it after its disconnection.

public override void OnClientConnect(IClient client, object[] parameters)
{
	Console.WriteLine("New client connected. Remote address: {0}", client.RemoteAddress);

	lock (myClients)
	{
		var account = accountPool.GetAccount();

		if (account == null)
		{
			client.InvokeMethod("OnRegistrationStateChanged", "Connection limit");
			client.Close();
			return;
		}

		var mediaGatewayClient = new MediaGatewayClient(account, client, pbxCallFactory, streamService, callManager);
		myClients.Add(client, mediaGatewayClient);

		extensionContainer.TryAddExtension(mediaGatewayClient);

		mediaGatewayClient.OnSetReadyStatus();
	}
}
Code 5 - The OnClientConnect method

The communication between the webphones is done through the PBX MediaGateway in the following way. The webphone calls a server-side method indicating some functionalities to start and the server invokes a client-side method in the other client indicating the start of the given functionality.

The call process is handled in the server by the Call method shown in Code 6.

public void Call(IClient client, string phoneNumber)
{
	lock (myClients)
	{
		MediaGatewayClient myClient;
		if (myClients.TryGetValue(client, out myClient))
			myClient.Call(phoneNumber);
	}
}
Code 6 - The Call method

When a client acceps a call, it calls the server-side method shown in Code 7. This method sends the acceptance to the other client and the communication can be started.

public void Accept(IClient client)
{
    lock (myClients)
    {
        MediaGatewayClient myClient;
        if (myClients.TryGetValue(client, out myClient))
            myClient.Accept();
    }

}
Code 7 - The Accept method

Ending a phone call works with the same protocol as accepting one. One of the communicating clients calls the method shown in Code 8 on the server-side, and the server calls the client-side HangUp method.

public void HangUp(IClient client)
{
    lock (myClients)
    {
        MediaGatewayClient myClient;
        if (myClients.TryGetValue(client, out myClient))
            myClient.HangUp();
    }

}
Code 8 - The HangUp method

The difference between ending and rejecting a call is the method that is called in the background. Rejecting the call means that you do not accept it when ringing. On the client GUI both actions are attached to the Hang Up button. Code 9 shows the server-side method for the call rejection.

public void Reject(IClient client)
{
    lock (myClients)
    {
        MediaGatewayClient myClient;
        if (myClients.TryGetValue(client, out  myClient))
            myClient.Reject();
    }
}
Code 9 - The Reject method

The PBX MediaGateway server handles the webphone clients through a PBX extension class called MediaGatewayClient. The declaration line of the class is shown in Code 10. The MediaGatewayClient class is derived from the IExtension interface that ensures the easiest handling process in the PBX.

class MediaGatewayClient : IExtension
Code 10 - Client class definition

Creating a PBX call in the MediaGateway extension is implemented in the Client class (Code 11). This method subscribes the call for the standard call events, the sate change and the call error. The event handlers for these events need to be implemented in this class too.

public virtual void Call(string dialNumber)
{
	var dialParams = new DialParameters(dialNumber);
	var pbxCall = pbxCallFactory.CreateServerCall(this, dialParams);

	call = pbxCall;
	call.CallStateChanged += Call_StateChanged;

	callManager.RouteCall(pbxCall);
}
Code 11 - Creating a call

When a webphone wants to start a Call, this method is invoked. The call is subscribed for the call events. The call is added to the call manager in the last instruction of the code.

The methods shown in Code 12 are the client methods for the call accepting, ending and rejecting processes. These methods only call the standard methods of the call object.

public virtual void HangUp()
{
    if (call != null)
    {
        call.HangUp();
        call = null;
    }
}

public virtual void Accept()
{
    if (call != null)
        call.Answer();
}

public void Reject()
{
    if (call != null)
    {
        call.Reject();
        call = null;
    }
}
Code 12 - Call handling in the Client class

The call state handling is the most important part of the code. The implementation used is this example program is shown in Code 13.

In the case of a ringing call (incoming call), the PBX runs the OnCallRequest method that will invoke the client-side method of the same name in the client application.

In the case of an established call the same processes need to be done as in the case of a simple softphone. You need to attach the audio sender and receiver objects to the call and then you need to set the MediaGateway audio receiver parameterized with the media stream of the client.

You need to connect the phoneCallAudioReceiver with the mediaGatewayAudioReceiver. This connection makes possible the communication between the webphone solutions and any other SIP end points.

The last step of the call handling is to notify the other client about the call establishment by calling the OnInCall method.

In the case of a completed call the sender and receiver objects need to be detached from the call, the receiver connection needs to be deleted, and the mediaGatewayAudioReceiver needs to be disposed.

void Call_StateChanged(object sender, CallStateChangedArgs e)
{
	if (e.State == CallState.Ringing)
	{
		OnCallRequest(call.DialInfo.CallerID);
	}

	if (e.State == CallState.Answered)
	{
		phoneCallAudioSender.AttachToCall(call);
		phoneCallAudioReceiver.AttachToCall(call);

		publishStreamName = Guid.NewGuid().ToString();
		playStreamName = Guid.NewGuid().ToString();

		var mediaStream = streamService.CreateStream(playStreamName);
		mediaGatewayAudioReceiver = new MediaGatewayAudioReceiver(mediaStream);

		mediaConnector.Connect(phoneCallAudioReceiver, mediaGatewayAudioReceiver);

		OnInCall(publishStreamName, playStreamName);
	}

	if (e.State.IsCallEnded())
	{
		call.CallStateChanged -= Call_StateChanged;
		phoneCallAudioSender.Detach();
		phoneCallAudioReceiver.Detach();

		streamService.RemoveStream(mediaGatewayAudioReceiver.MediaStream);
		mediaConnector.Disconnect(phoneCallAudioReceiver, mediaGatewayAudioReceiver);
		mediaGatewayAudioReceiver.Dispose();

		mediaGatewayAudioReceiver = null;
		call = null;
	}

	OnCallStateChanged(e.State);
}
Code 13 - Call state handling in the client class

At this point you are familiar with the basic concepts of implementing webphone support in your PBX system. The example program can be used as a base and you can extend it with the functionalities you need. If you have read through this article carefully, you are surely capable for create your own 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 developing your PBX on Pricing and licensing information page

Related Pages

More information