info@voip-sip-sdk.com Tel.: 00 36 52 532 731

Windows Phone 7 Development

Download: 01_Mobile2Web_voice.zip
Download: 02_Mobile2Web_video.zip
Download: 03_Mobile2SIP_calls.zip

This article is a detailed guide for Windows Phone 7 mobile platform programming in relation with Ozeki VoIP SIP SDK. After reading through this page you will be fully familiar with all the essential terms concerning Windows Phone 7 application programming and what you will need for creating your own solution using Ozeki VoIP SIP SDK.

Introduction

Windows Phone 7 is the Microsoft mobile platform for smartphones. The VoIP programs for this platform are of a limited number, but Ozeki VoIP SIP SDK now provides you the solution of making professional Voice over IP applications, even for video calls easily. This is a unique possibility with which you can now integrate the Microsoft Mobile Platform into your communication network (Figure 1).


Figure 1 - Windows Phone VoIP application with Ozeki VoIP SDK

The following example codes need some essential tools installed on your computer. Please check the prerequisites to be sure that the example programs will run properly.

Client-side code

The client application needs to be a Windows Phone application project in the Visual Studio environment. You will need to set the WPClientSDK.dll as a reference in your project. This .dll is part of the Ozeki VoIP SIP SDK that you can download from here.

When you have created a Windows Phone application project in Visual Studio, you will need to set the user interface layout for your application. Code 1 shows a possible layout setting.

<Grid x:Name="LayoutRoot" Background="Transparent">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            #lt;RowDefinition Height="*"/>
        #lt;/Grid.RowDefinitions>

        <!--TitlePanel contains the name of the application and page title-->
        <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
            <TextBlock x:Name="ApplicationTitle" Text="Ozeki VoIP SIP SDK" Style="{StaticResource PhoneTextNormalStyle}"/>
            <TextBlock x:Name="PageTitle" Text="Mobile2Web" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
        </StackPanel>

        <!--ContentPanel - place additional content here-->
        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <Button Content="Call" Height="70" x:Name="btnCall" VerticalAlignment="Top" Click="btnCall_Click" Margin="57,156,223,0" IsEnabled="False" />
            <TextBlock x:Name="txtboxInfo" TextWrapping="Wrap" Text="Offline" TextAlignment="Center" VerticalAlignment="Top" FontSize="21.333" Margin="0,86,0,0"/>
            <Button Content="Stop Call" Height="70" Margin="215,156,66,0" Name="btnStopCall" VerticalAlignment="Top" Click="btnStopCall_Click" IsEnabled="False" />
            <TextBlock Height="23" HorizontalAlignment="Center" Margin="0,27,0,0" Name="txtBDebug" Text="DebugLine" VerticalAlignment="Top" Visibility="Collapsed" />
            <TextBlock Height="23" HorizontalAlignment="Left" Margin="356,27,0,0" Name="txtBClientID" Text="ClientID" VerticalAlignment="Top" />
            <TextBox x:Name="txtLog" Height="348" HorizontalAlignment="Left" Margin="0,253,0,0"  Text="" VerticalAlignment="Top" Width="450" IsEnabled="False" TextWrapping="Wrap" VerticalScrollBarVisibility="Visible" />
        </Grid>
    </Grid>

Code 1 - GUI definition

Figure 2 shows the GUI layout defined in Code 1 above. This layout contains the basic features for the voice communication and it displays the basic information about the connection and calls made by the program.


Figure 2 - The GUI layout for the Windows Phone 7 voice communication program

The App.xaml.cs file only needs an extra code line (Code 2) in its constructor. Otherwise by default the project settings can be used in the program.

The client application needs to use some packages for the Windows Phone 7 and the VoIP communication support. If you want to use these tools without namespace labeling, you will need to set some precompilation directives in your program (Code 3).

using System;
using System.Diagnostics;
using System.Windows;
using Microsoft.Phone.Controls;
using Ozeki.MediaGateway;
using WPClientSDK;
using WPClientSDK.Log;

Code 3 - Using lines for precompilation

The client class is a standard phone application derived from the PhoneApplicationPage class provided by the Windows Phone 7 SDK. As for the VoIP communication, you will need to use some basic tools in your application. These are shown in Code 4.

public partial class MainPage : PhoneApplicationPage
    {
        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 4 - Class definition and properties

The initialization of the phone application is defined in the method shown in Code 5. Basically the GUI settings are done when the program is ready to be used.

public void OnSetReadyStatus(bool isReady, string name)
{
    InvokeGUIThread(()=>
                        {
                            clientID = name;
                            btnStopCall.IsEnabled = isReady;
                            btnCall.IsEnabled = isReady;
                            txtBClientID.Text = name;
                            txtboxInfo.Text = isReady ? "Ready to call." : "Waiting for other client.";
                        });
}

Code 5 - Basic settings for the program

When the program receives an incoming call, the method in Code 6 invokes on the client side. The call is specified by the remote party (caller) SIP account (number) that is also displayed on the GUI.

public void OnCallRequest(string remotpartyId)
{
    callProcess = true;
    IncomingCallOwner = remotpartyId;
    InvokeGUIThread(() => { txtboxInfo.Text = "Call received from " + remotpartyId; });
}

Code 6 - Incoming call notification

In case of an established call the OnInCall method is invoked on the client side (Code 7). The program here sets the media sender for the call and attaches the microphone as the primary voice capture device to the studio sender object.

public void OnInCall()
{
    InvokeGUIThread(()=>txtboxInfo.Text = "Incall");


    streamSender = new MediaStreamSender(connection);

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

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

    streamSender.Publish(clientID);
}

Code 7 - Audio stream sender setup

In the case of a VoIP communication, the programs need to play the remote party media streams. For this purpose, you need to use a stream receiver object and a player object too. The usage and setup instructions of these are shown in Code 8.

public void OnPlayRemoteStream(string remoteparty)
{
    streamReceiver = new MediaStreamReceiver(connection);
    streamReceiver.StreamStateChanged += new EventHandler<GenericEventArgs<StreamState>>(streamReceiver_StreamStateChanged);
    audioPlayer.AttachMediaStreamReceiver(streamReceiver);
    streamReceiver.Play(remoteparty);
}

Code 8 - Media stream receiving

The media stream sender and receiver objects need to be subscribed for the stream state change events. For this purpose there is a need for two event handler methods. These are shown in Code 9. They are basically for debugging functionalities but in case of the sender, there is an actual remote method invocation, too.

void streamReceiver_StreamStateChanged(object sender, GenericEventArgs<StreamState> e)
{
    Debug.WriteLine("receiver play {0}", e.Item);
    InvokeGUIThread(() => { txtBDebug.Text = e.Item.ToString(); });
}

void streamSender_StreamStateChanged(object sender, GenericEventArgs<StreamState> e)
{
    InvokeGUIThread(() => { txtBDebug.Text = e.Item.ToString(); });
    switch (e.Item)
    {
        case StreamState.PublishingSuccess:
            connection.InvokeOnConnection("PlayRemoteStream");
            break;
        default:
            Debug.WriteLine(e.Item);
            break;
    }
}

Code 9 - Basic event handlers for the media sender and receiver

When the call stops the method shown in Code 10 runs on the client side. It mainly releases the incoming and outgoing media streams but also sets the GUI so that it will resemble the new phone state.

public void OnCallStop()
{
    InvokeGUIThread(()=>
                        {
                            txtboxInfo.Text = "Remoteparty finished the call.";
                            btnCall.IsEnabled = true;
                        } );

    ReleaseStreams();
}

Code 10 - You need to release the media streams when the call stops

When the Windows Phone 7 application starts, the Loaded event invokes. If you want to set some basic tools for your application, you are suggested to do in the event handler of this event (Code 11). In this case the media connection and the microphone settings are done in this first running step.

void MainPage_Loaded(object sender, RoutedEventArgs e)
{
    Logger.Instance.LogMessageReceived += new EventHandler<GenericEventArgs<string>>(Instance_LogMessageReceived);
    connection = new MediaConnection("localhost:6888");
    connection.ConnectionStateChanged += new EventHandler<GenericEventArgs<ConnectionState>>(connection_ConnectionStateChanged);
    connection.Connect();
    microphone = Microphone.GetMicrophone();
}

Code 11 - Application initialization

The connection state is essential to know in a communication program. The state change is handled in this application in the method shown in Code 12. In a succeeded connection, the program sets the audio player for the voice calls and also registers the client to the connection.

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

Code 12 - Connection state handling

The user interface functionalities are implemented simply in two methods. This communication program is only capable of calling another client through the webphone server. Code 13 shows the handler method for the Call button. This button is used for starting and accepting a call.

private void btnCall_Click(object sender, RoutedEventArgs e)
{
    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 13 - Handling the Call button functionalities

The Stop button is handled similar to the Call button. The event handler is shown in Code 14. This button stops an established call or rejects an incoming one.

private void btnStopCall_Click(object sender, RoutedEventArgs e)
{
    if (callProcess)
    {
        txtboxInfo.Text = "Call stop, ready to call.";
        connection.InvokeOnConnection("CallStop");
        ReleaseStreams();
        btnCall.IsEnabled = true;
    }
}

Code 14 - Event handler for the Stop Call button

Server-side code

The server program is a console application that uses the Ozeki VoIP SIP SDK .dll file as a reference.

The server program of the example applications can handle more client types. You only need to set the client type you want to use with the server in the application code or in the configuration file. In this example the App.config file contains the client settings for the server. Code 15 shows the line that defines the Windows Phone 7 client support on the server side.

<add type="windowsphone" serviceName="Web2WebServer" listenedPort="6888"  policyServiceEnabled="true"/>

Code 15 - Server-side settings for the Windows Phone client support

The server application is a standard Ozeki MediaGateway class that uses some basic tools. Code 16 shows the class definition and the properties it needs for the proper working.

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

Code 16 - Server class definition

The Ozeki VoIP SDK provided MediaGateway class defines some methods for the server functionalities. In this server application, these methods are overridden for logging purposes. Code 17 shows the method that runs when the server starts.

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

Code 17 - Starting the server

The server can handle the client connection within the OnClientConnect method that is also one of the MediaGateway class methods. Code 18 shows how the example server implements this method. It only extends the provided functionality with the log printing on the console and a notification method call that informs the other connected clients about the new client connection.

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 18 - Handling the client connections

The client disconnection is handled in the method shown in Code 19. The basic concept is the same as in case of the client connection. The other connected clients are notified about the disconnection and the client that invoked the method is removed from the server dictionary of connected clients.

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 19 - Client disconnection handling

When a client starts publishing its stream the server handles it in the method shown in Code 20. This method only extends the MediaGateway implemented version with logging on the console.

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 20 - Handling the client stream publishing

The client calls are handled in the server program via the Call method shown in Code 21. The call is set between two clients. Both need to be set as the remote party of each other in the call.

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 21 - Server-side call handling

When the call between the clients is established, all the clients get a notification about the new call state changes (Code 22).

public void ChangeToIncall(IClient client)
{
    Clients[client].OnInCall();
    foreach (MyClient c in Clients.Values)
    {
        NotifyClientsAboutTheirCallStatus();
    }

}

Code 22 - Changing the client's call state into InCall

When a client starts communication with another, the server needs to invoke the method on the client side that will start playing the other client's media stream. The method that implements this functionality is shown in Code 23.

public void PlayRemoteStream(IClient client)
{
        Clients[client].OnPlayRemoteStream();

}

Code 23 - Invoking the client-side OnPlayRemoteStream method

When a client stops a call, the server sends notification to the other communicating client about the call stop by invoking the OnCallStop client-side method (Code 24).

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

Code 24 - Notifying the remote client about the end of the call

Any client state change - connection or disconnection - can affect the behavior of the other clients, therefore all the connected clients need to be notified about any client state change. The method shown in code 25 is used in the server code for this purpose.

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 25 - All the clients need to know about the others' state changes

The server application also contains a class for the client representation. The methods shown in Code 26 are used for invoking the client-side methods. When the server wants to send an invocation to a client, it calls one of these methods (the proper one for the given purpose) and these make the actual client-side method invocations. This class (called MyClient) is an encapsulation for the clients.

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 26 - The MyClient class' methods

This documentation only covered the voice calling example program. The other two examples work similarly to this one. If you have read this manual page carefully and checked the source code of the example, you can surely understand all the three examples.

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

Related Pages