Ozeki VoIP SDK - Product Guide
Developers Guide
Source code explanation for MediaGateway SIP Example for Ozeki VoIP SIP SDK
![]() |
Download: | 06_WEB2SIP_DesignedGUI.zip |
This page is an explanation article for the source code of MediaGateway SIP Example for Ozeki VoIP SIP SDK to provide an overall view. Please find the source code parts and their explanation below this page.
Server implementation
In this sample program, the server is responsible for keeping connection
with the SIP PBX, and also for setting up/accepting calls, hanging up calls, forwarding
media data between the PBX/SIP device and the Silverlight client and distributing accounts
that are required for logging into the PBX. The server also makes these accounts logged into
the PBX for the Silverlight clients.
These tasks will be realized by the SIPGateway class in order to exploit the
functions provided by Ozeki VoIP SIP SDK. It has been derived from the MediaGateway
class. Furthermore, in order to implement SIP functionality quickly and easily, it
uses the tools ensured by Ozeki VoIP SIP SDK.
SIPGateway Constructor
A SIPAccountPool is built in the class constructor that includes a certain number of accounts that belongs to a SIP PBX (in this example, it is Asterisk PBX). All connected clients are demonstrated by a SIPClient object, they are recorded in a ConcurrentDictionary. In order to exploit the implementation of the easy-to-handle, two-way communication between the client and the server, the stream service is got with the MediaGateway GetService<IStreamService>(); command (Code 1).
public SIPGateway()
{
sipClients = new ConcurrentDictionary<IClient, SIPClient>();
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);
sipAccountManager.AddSIPAcount(sipAccount);
}
streamService = GetService<IStreamService>();
}
Code 1 - The server class constructor method
The softphone object that can be seen in the code is provided by the Ozeki VoIP SIP SDK. It represents a telephone with full functionality. Based on the SDK licensing, it is possible to add even more telephone lines through which calls are handled. The softphone can be created with the use of SoftPhoneFactory.CreateSoftPhone method call, in this example, the IP address and port range of Asterisk PBX is also used.
SipGateWay Public methods
OnClientConnect is an overridden method (Code 2). It can originally be found in SIPGateway class. Via this method the VoIP SIP SDK notifies if a client tries to connect.
public override void OnClientConnect(IClient client, object[] parameters)
{
Console.WriteLine("New client connected");
var sipAccount = sipAccountPool.GetSIPAccount();
if(sipAccount == null)
{
client.InvokeMethod("OnRegistrationStateChanged", RegistrationState.ConnectionLimit);
client.Close();
return;
}
var sipClient = new SIPClient(client, streamService, softPhone, sipAccount);
sipClients.TryAdd(client, sipClient);
sipClient.Register();
}
Code 2 - This method is invoked when a client requests connection to the server
If there is a free SIP account in the SipAccountPool for the client that
tries to connect, then the SipClient object of the client is created with this
account and the necessary parameters. In case there is no free account, the client that
tries to connect will be notified and the connection is closed.
OnClientDisconnect method (Code 3) is only responsible for removing the connected
client's reference from ConcurrentDictionary and for releasing the
SIP account that has been used by the client and for unregistering the account from the PBX.
public override void OnClientDisconnect(IClient client)
{
Console.WriteLine("Client disconnected");
SIPClient sipClient;
if (sipClients.TryRemove(client, out sipClient))
{
sipClient.Unregister();
sipAccountPool.AddSIPAcount(sipClient.Account);
}
}
Code 3 - This method handles client disconnection from the server
OnSteamPublishStart (Code 4) will be invoked when the given client published its MediaStreamSender object, this way, it will be subscribed its stream MediaDataReceived event for the MediaDataReceived event handler of the SIPClient. This will forward the received data to the proper direction. The code implements these issues as follows:
public override void OnStreamPublishStart(IClient client, IMediaStream mediaStream)
{
Console.WriteLine("Media stream published. Stream name {0}", mediaStream.Name);
SIPClient sipClient;
if (sipClients.TryGetValue(client, out sipClient))
mediaStream.MediaDataReceived += sipClient.MediaDataReceived;
}
Code 4 - The OnStreamPublishStart method
OnStreamClose (Code 5) makes the opposite of OnSteamPublishStart. OnStreamClose finishes the data transfer of the published stream by unsubscribing the event handler of the previously subscribed event.
public override void OnStreamClose(IClient client, IMediaStream mediaStream)
{
Console.WriteLine("Media stream closed.");
SIPClient sipClient;
if (sipClients.TryGetValue(client, out sipClient))
mediaStream.MediaDataReceived -= sipClient.MediaDataReceived;
}
Code 5 - This method invokes when the clients stop streaming
The described methods above can be considered as the basic methods of Ozeki VoIP SIP
SDK. They have been overridden according to the current implementation in order to
provide them the appropriate functionality.
The next 3 methods have the following functionality: Call initiation, HangUP - rejecting incoming calls/hangup
active call - and the Accept call functionality.
These methods extend the built-in services of Ozeki VoIP SIP SDK. They can be
invoked via the mechanisms of the SDK that are related to these functions. This is
the follows: the InvokeOnConnection method of the created MediaConnection in the
client (Code 6). For example, in the following way in case of Call:
mediaConnection.InvokeOnConnection("Call", dialNumber);
Code 6 - This instruction invokes the Call method from the client-side
That will result the following method call on the server side (Code 7).
public void Call(IClient client, string phoneNumber)
{
SIPClient sipClient;
if (sipClients.TryGetValue(client, out sipClient))
sipClient.Call(phoneNumber);
}
Code 7 - The Call method on the server-side
This will forward the request to the SIPClient class. Hangup and Accept methods work similarly to the Call method. This way it is not detailed here, but it can be checked in the source code available for download on this webpage.
The SIPClient class for representing the clients on the server-side
A server-side SIPClient object will be created for
each connected client and handle the status of the phone line belonging to the client.
SIPClient constructor (Code 8) gets its client's reference, server stream service's reference,
the softphone that is responsible for representing the telephone and an
account derived from a SIPAccountPool in its constructor.
public SIPClient(IClient client, IStreamService streamService, ISoftPhone softPhone, SIPAccount account)
{
mediaConnector = new MediaConnector();
myMediaSender = new MyMediaSender();
phoneCallMediaSender = new PhoneCallMediaSender(VoIPMediaType.Audio);
codecConverter = CodecConverter.Instance;
mediaConnector.Connect(myMediaSender, phoneCallMediaSender);
this.streamService = streamService;
this.softPhone = softPhone;
this.client = client;
Account = account;
}
Code 9 - The SIPClient class constructor
The class has a Mediaconnector object that is a tool provided by Ozeki VoIP SIP SDK. It is responsible for connecting the media data receiving from the client to the PhoneCallMediaSender object provided by the Ozeki VoIP SIP SDK. The media data that is received from the client is matched with the mentioned PhoneCallMediaSender via a MyMediaSender wrapper class with the following command shown in Code 10.
mediaConnector.Connect(myMediaSender, phoneCallMediaSender);
Code 10 - Connection establishment
Register() method (Code 11) will create the phone line that belongs to the client and it will also subscribe to the necessary events.
public virtual void Register()
{
phoneLine = softPhone.CreatePhoneLine(Account);
if (phoneLine.RegisteredInfo == PhoneLineState.RegistrationSucceded)
OnPhoneLineStateChanged(phoneLine.RegisteredInfo);
phoneLine.PhoneLineStateChanged += PhoneLine_PhoneLineInformationCanged;
softPhone.IncommingCall += SoftPhone_IncommingCall;
softPhone.RegisterPhoneLine(phoneLine);
}
Code 11 - Registering to the SIP PBX
OnPhoneLineStateChanged event is responsible for indicating the changes in the telephone line status. The IncommingCall event of Softphone is for indicating incoming calls. The Unregister() method unregisters the phoneline and unsubscribes from the previously subscribed events. Call(string dialNumber) will dial the telephone number provided in the parameter via the Call object provided by Ozeki VoIP SIP SDK, furthermore, it subscribes to the events of this object that are necessary for making the call (Code 12).
public virtual void Call(string dialNumber)
{
if (phoneLine == null)
{
System.Diagnostics.Debug.Fail("PhoneLine null");
return;
}
if ((phoneLine.RegisteredInfo == PhoneLineState.RegistrationSucceded || phoneLine.RegisteredInfo == PhoneLineState.NoRegNeeded) && call == null)
{
call = softPhone.CreateCallObject(phoneLine, dialNumber);
call.CallErrorOccured += Call_ErrorOccured;
call.CallStateChanged += Call_StateChanged;
call.MediaDataReceived += Call_MediaDataReceived;
call.Start();
}
}
Code 12 - The setup of the call object
The Accept() method accepts the request for an incoming call, while the HangUp() method rejects
the incoming call or hangs up active calls.
The Call_StateChanged event handler is for indicating call status during call setup
and the call progress. It helps identify in which status the call is (Busy, Cancelled,
Ringing, Incall, Completed...). InCall status is handled here. InCall status shows that
point of the call when the media data sending and receiving can be started.
protected virtual void Call_StateChanged(object sender, VoIPEventArgs<CallState> e)
{
switch (e.Item)
{
case CallState.InCall:
phoneCallAudioSender.AttachToCall(call);
phoneCallAudioReceiver.AttachToCall(call);
publishStreamName = Guid.NewGuid().ToString(); // Ez az amit a kliens publikál
playStreamName = Guid.NewGuid().ToString(); // Ez az amit a szerver publikál
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 13 - The EventHandler method for the call state changing
Two unique stream identifiers are created for the client in InCall status. One ID
is for sending voice data, the other ID is for receiving voice data from the remote end. This
will be indicated for the client via OnInCall call. Callstate.Completed status
indicates that the actual call has been completed, this way, the created streams will be
removed from the service and the call. The method notifies the client in its last line.
The connection between the Mediagateway and the SIP SDK is made by
two MediaHandler objects (Code 14). These objects are connected to the
PhoneCallAudioSender and PhoneCallAudioReceiver in the Call_StateChanged method.
MediaGatewayAudioReceiver mediaGatewayAudioReceiver; MediaGatewayAudioSender mediaGatewayAudioSender;
Code 14 - The MediaHandlers that are used for connecting the MediaGateway and the SIP tools
Two-way communication is built up between the server and the client, so the server can invoke certain methods of the client. It is similar to the case when the client can invoke the server's extended service functions. It differs only in that the InvokMethod of the IClient object is invoked in this case (Code 15).
void OnIncomingCall(string phoneNumber)
{
client.InvokeMethod("OnIncomingCall", phoneNumber);
}
Code 15 - The server-side method call for invoking client-side methods
Where the server indicates to the client that there is an incoming call from the phone number that has been specified in the parameter. The InvokeMethod is the follows shown in Code 16.
public void InvokeMethod(string methoName, params object[] parameters)
Code 16 - The definition of method invocation method
The SIPClient also sends call status changes to the client similarly to this method via OnCallStateChanged and OnCallErrorOccured methods.
Client implementation
The Silverlight client demonstrates a one-line webphone with making/accepting/hanging up calls and DTMF handling functionality.
User InterfaceThe graphical user interface is built up using 3 main GUI elements: NumPad, Display and MainPage. The Display panel only displays the actual status of the application with using Silverlight Binding Engine. The NumPad includes the necessary buttons of the phone, and sends the functionality of these buttons to the MainPage GUI element. The MainPage is responsible for transferring commands arriving from NumPad to the logic that is described by SIPClient in this case. Besides this, the MainPage also published the changes occurred in logic (so the data displayed on Display). After loading the interface, controls are created for recording and playing the audio for the SIPClient object. Then it is necessary to subscribe to the events that display certain information. Then it needs to be connected to the server (Code 17).
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
phoneState = PhoneState.Normal;
numpad.owner = this;
display.DataContext = this;
UserName = "unregistered";
microphone = Microphone.GetMicrophone();
audioPlayer = new AudioPlayer();
sipClient = new SIPClient(microphone, audioPlayer);
CallInfo = "Registering...";
sipClient.ConnectionStateChanged += sipClient_ConnectionStateChanged;
sipClient.RegistrationStateChanged += sipClient_RegistrationStateChanged;
sipClient.PhoneLineStateChanged += sipClient_PhoneLineStateChanged;
sipClient.CallStateChanged += sipClient_CallStateChanged;
sipClient.CallErrorOccurred += sipClient_CallErrorOccurred;
sipClient.SIPUserNameReceived += sipClient_SIPUserNameReceived;
sipClient.IncomingCall += sipClient_IncomingCall;
sipClient.DTMFReceived += sipClient_DTMFReceived;
sipClient.Connect();
}
Code 17 - Initialization of the MainPage object
The client-side SIPClient classThe SIPClient is responsible for providing an interface to the server for the two-way client-server communication. This interface can be created with the use of the MediaConnection class of Ozeki MediaGateway SLCLient SDK easily. An object needs to be created with the given server's IP address, then the Connect method is invited with which it is connected to the server that has the IP address previously defined in constructor (Code 18).
public SIPClient(Microphone microphone, AudioPlayer audioPlayer)
{
this.microphone = microphone;
this.audioPlayer = audioPlayer;
mediaConnection = new MediaConnection("localhost:4502/SIPGateway");
mediaConnection.Client = this;
mediaConnection.ConnectionStateChanged += mediaConnection_ConnectionStateChanged;
}
Code 18 - The SIPClient constructor on the client-side
A Microphone and Audioplayer objects are provided in the SIPClient constructor
by the VoIP SIP SDK in order to make audio recording
and playing functions of Silverlight version 4 easier.
Subscription to ConnectionStateChanged event in the constructor ensures a notification
about the results of the connection to the server.
To allow the server to call the public methods of the SIPClient
object, it needs to be specified for the Client Property of the MediaConnection class
in which SIPClient object it needs to search for the method (that has been invited by the server)
in this case. (mediaConnection.Client = this;)
Since the communication logic related to SIP can be found in the server-side solution, this
class is only responsible for forwarding data to the GUI and the server and displaying
the data received from the server on the GUI. Of course, media data is an exception
because it will be provided by the Microphone class and it will be attached to a
MediaStreamSender object as an input device as the phone call is setup. It needs
to be mentioned that besides attaching there are no further tasks because the appropriate
audio compression is made by the Microphone object. Data arriving from the server are also
excpeptions because they will be played by the AudioPlayer object that is attached to the
MediaStreamReceiver.
The SIPClient ensures simple telephone functionality with the following methods: Call, HangUp, Accept.
public void Call(string dialNumber)
{
mediaConnection.InvokeOnConnection("Call", dialNumber);
}
Code 19 - The Call client-side method that invokes the server-side Call method
The Call method (Code 19) dials the telephone number that has been specified in parameter on the server side. Since the server side Call is not the part of the basic server side methods of Ozeki VoIP SIP SDK, it will be invited via the InvokeOnConnection mechanism of the MediaConnection (Code 20).
MediaConnection.IvnvokeOnConnection(string methodname, params object[] parameters )
Code 20 - The client-side method invokation instruction for calling server-side methods
HangUp rejects incoming calls or hangs up actual calls. Accept
accepts incoming call requests and it reaches the necessary method of the server side
via the InvokOnConnection similarly to Call.
OnCallStateChanged is a further method that is invoked from the server-side (Code 21). It
displays the changes of the actual call status on the interface.
public void OnCallStateChanged(CallState callState)
{
if (callState == CallState.Completed)
CloseStreams();
var handler = CallStateChanged;
if(handler != null)
handler(this, new GenericEventArgs<CallState>(callState));
}
Code 21 - The method invoked from server-side when tha call state changes
OnCallErrorOccurred, OnRegistrationStateChanged, OnPhoneLineStateChanged,
OnRegisteredUserNameReceived methods have similar tasks as the above mentioned
methods, so they are not detailed here.
The OnInCall method (Code 22) is responsible for notifying the client when the actual
call has been accepted by the remote end; and the client should start playing the
media data and should send the outgoing media data (that arrives from the microphone)
to the server.
public void OnInCall(string publisStreamName, string playStreamName)
{
mediaStreamSender = new MediaStreamSender(mediaConnection);
mediaStreamSender.AttachMicrophone(microphone);
mediaStreamSender.Publish(publisStreamName);
mediaStreamReceiver = new MediaStreamReceiver(mediaConnection);
mediaStreamReceiver.AttachAudioPlayer(audioPlayer);
mediaStreamReceiver.Play(playStreamName);
}
Code 22 - The method that is invoked from the server-side in case of an incoming call
Based on the above mentioned it is done as follows: a MediaStreamSender object
is created for sending the incoming voice data (that arrive from the microphone). The
Microphone object (that is provided by Ozeki VoIP SIP SDK) has been attached
to this created MediaStreamSender object. This way the server starts playing
the outgoing media stream to the remote end. It then needs to be published with a
unique name that will be received by the remote end via its MediaStreamReciver
object after it has invited the Play method with the unique ID (that has been
published by the given client). In this case, both stream identifiers are generated by
the server and then it forwards the IDs to the clients, as it can be seen from the
source code above.
The voice data that arrive to the MediaStreamReceiver object is handled by
the Audioplayer provided by Ozeki VoIP SIP SDK. It will play the received voice data
via the speakers of the computer (Code 23).
mediaStreamReceiver = new MediaStreamReceiver(mediaConnection);
mediaStreamReceiver.AttachAudioPlayer(audioPlayer);
mediaStreamReceiver.Play(playStreamName);
Code 23 - The basic settings for the media stream receiver object
It can be seen that the voice data is started to be received by inviting the Play method on the stream
with the unique ID parameter of the stream to be played.
For more information, please contact us at: info@voip-sip-sdk.com!
INTERMEDIATE
VoIP technology walkthrough
Softphone development
Webphone development
Mobile development
Voice recording
GETTING AROUND
Sitemap
Search the manual
API documentation
FAQ
Appendix


