Posts tagged ‘Flash Media Server’

Silverlight and Flash Media Server integration with the FluorineFx Silverlight library

This sample provides an overview of creating a Silverlight project and connecting it with Flash Media Server using the FluorineFx Silverlight library (Visual Studio 2008 with Silverlight extensions is required).

The sample starts with the following FMS web chat application (sotest.asc):

application.onAppStart = function()
{
	this.usersID = 1;
	this.usersSO = SharedObject.get(“users”, false); 

	// Initialize the history of the text share
	application.history = “”;
} 

application.onConnect = function(clientObj)
{
	clientObj.uniqueUserID = this.usersID;
	clientObj.connectStartTime = new Date();
	//set the shared object
	this.usersSO.setProperty(“user”+ this.usersID, clientObj);
	this.usersID++;
	// Accept the connection.
	application.acceptConnection( clientObj ); 

	// Call the client function ’setHistory,’ and pass 
	// the initial history
 	clientObj.call(“setHistory”, null, application.history); 

	// The client will call this function to get the server
	// to accept the message, add the user’s name to it, and
	// send it back out to all connected clients.
	clientObj.msgFromClient = function(msg) {
		msg = “user”+ this.uniqueUserID + “: ” + msg + “\n”;
		application.history += msg;
		application.usersSO.send(“msgFromSrvr”, msg);
	} 	
} 

application.onDisconnect = function(clientObj)
{
	this.usersSO.setProperty(“user”+ clientObj.uniqueUserID, null);
}

When the application is started a shared object is created on the server side. Every user connecting to the application will get a unique identity. Clients will call the “msgFromClient” function to send messages to the other connected clients.

Copy the above code to the FMS\applications\sotest directory.

Network Security Access Restrictions
Before going forward to the Silverlight client one must be aware of the Network Security Access Restrictions in Silverlight 2:
1) When a socket request is made to the site (cross-domain or site of origin), the Silverlight 2 runtime opens a connection using TCP to a well-known port (port 943) on the target site. Then the runtime sends a special string <policy-file-request/> to the server to request a Silverlight policy file. The Silverlight 2 runtime then waits to receive a reply from the target site that contains a Silverlight policy file.
The policy file retrieved will have to grant permission to have a connection to the target host opened.

2) Port range that a Silverlight 2 network application is allowed to connect to must be within the range of 4502-4534

For (1) you can use the FluorineFx Silverlight Policy Server provided. The application comes with a clientaccesspolicy.xml file you can change accordingly:


<socket-resource port=”4502-4534″ protocol=”tcp”/>

Our sample uses only the port 4502 so it is recommended to change the range.

For (2) we must configure the Flash Media Server to listen on the port we can access from our Silverlight application. Open the fms.ini file and change the entry
ADAPTOR.HOSTPORT = :1935 (if previously used 1935)
to
ADAPTOR.HOSTPORT = :4502

Creating the Silverlight project

Start Visual Studio 2008, create a new Silverlight Application (I’ve named it SilverlightChatApplication) and the web application to host the Silverlight client (I’ve named it SilverlightApplicationWeb).

Put the following XAML markup in the Page.xaml file:

<UserControl x:Class=”SilverlightChatApplication.Page”
xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
Width=”800″ Height=”500″ xmlns:d=”http://schemas.microsoft.com/expression/blend/2008″
xmlns:mc=”http://schemas.openxmlformats.org/markup-compatibility/2006″ mc:Ignorable=”d”>
<Grid x:Name=”LayoutRoot” Width=”800″ Height=”500″ Background=”Black”>
<ScrollViewer x:Name=”ScrollBar” Background=”White” HorizontalScrollBarVisibility=”Auto” Canvas.Top=”60″ Canvas.Left=”340″ Margin=”8,8,8,50″>
<TextBlock x:Name=”ChatConsole” HorizontalAlignment=”Stretch” Margin=”8,8,8,47.4630012512207″ VerticalAlignment=”Stretch” Text=”" TextWrapping=”Wrap”/>
</ScrollViewer>
<TextBox x:Name=”InputTextBox” HorizontalAlignment=”Stretch” Margin=”8,0,59.6339988708496,8″ VerticalAlignment=”Bottom” Text=”Sample Text”/>
<Button Height=”35.463″ HorizontalAlignment=”Right” Margin=”0,0,8,8″ VerticalAlignment=”Bottom” Width=”47.634″ Content=”Send” d:LayoutOverrides=”Width, Height” Click=”Button_Click”/>
</Grid>
</UserControl>

In the Solution Explorer add a reference to FluorineFx.dll (note: we are adding the FluorineFx Silverlight client library to the Silverlight application and not the remoting gateway!). The assembly should be located in the “Bin\net\Silverlight2.0” folder.

Add the following code to the Page.xaml.cs code file:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes; 

using FluorineFx;
using FluorineFx.Net;
using FluorineFx.Messaging.Api;
using FluorineFx.Messaging.Api.Service;
using FluorineFx.AMF3; 

namespace SilverlightChatApplication
{
    public partial class Page : UserControl
    {
        NetConnection _netConnection;
        RemoteSharedObject _sharedObject; 

        public Page()
        {
            InitializeComponent(); 

            // Create NetConnection client
            _netConnection = new NetConnection();
            _netConnection.ObjectEncoding = ObjectEncoding.AMF0;
            _netConnection.OnConnect += new ConnectHandler(_netConnection_OnConnect);
            _netConnection.OnDisconnect += new DisconnectHandler(_netConnection_OnDisconnect);
            _netConnection.NetStatus += new NetStatusHandler(_netConnection_NetStatus);
            _netConnection.Client = this;
            //FMS test
            _netConnection.Connect(“rtmp://localhost:4502/sotest”);
        } 

        void _netConnection_NetStatus(object sender, NetStatusEventArgs e)
        {
            string level = null;
            if (e.Info.ContainsKey(“level”))
                level = e.Info[“level”] as string;
            if (level == “error”)
            {
                //received an error
                Log(“Error: ” + e.Info[“code”] as string);
                Log(“Client not connected.”);
            }
            if (level == “status”)
            {
                Log(“Status: ” + e.Info[“code”] as string);
            } 

        } 

        void _netConnection_OnDisconnect(object sender, EventArgs e)
        {
            Log(“Connection disconnected.”);
        } 

        void _netConnection_OnConnect(object sender, EventArgs e)
        {
            Log(“Connected to server. Connecting to RSO…”);
            _sharedObject = RemoteSharedObject.GetRemote(typeof(UsersRSO), “users”, _netConnection.Uri.ToString(), false);
            (_sharedObject as UsersRSO).Page = this; 

            _sharedObject.OnConnect += new ConnectHandler(_sharedObject_OnConnect);
            _sharedObject.OnDisconnect += new DisconnectHandler(_sharedObject_OnDisconnect);
            _sharedObject.NetStatus += new NetStatusHandler(_sharedObject_NetStatus);
            _sharedObject.Sync += new SyncHandler(_sharedObject_Sync);
            _sharedObject.Connect(_netConnection);            
        } 

        void _sharedObject_Sync(object sender, SyncEventArgs e)
        {
            ASObject[] changeList = e.ChangeList;
            for (int i = 0; i < changeList.Length; i++)
            {
                ASObject info = changeList[i];
                if (info.ContainsKey(“name”) && info[“name”] != null)
                    Log(info[“name”].ToString() + ” - ” + info[“code”].ToString());
                else
                    Log(info[“code”].ToString());
            }
        } 

        void _sharedObject_NetStatus(object sender, NetStatusEventArgs e)
        {
            string level = e.Info[“level”] as string;
            if (level == “error”)
            {
                //received an error
                Log(“Error: ” + e.Info[“code”] as string);
            }
            if (level == “status”)
            {
                Log(“Status: ” + e.Info[“code”] as string);
            }
        } 

        void _sharedObject_OnDisconnect(object sender, EventArgs e)
        {
            Log(“Disconnected RSO.”);
        } 

        void _sharedObject_OnConnect(object sender, EventArgs e)
        {
            Log(“Connected to RSO.”);
        } 

        public void setHistory(string history)
        {
            Log(history);
        } 

        /// <summary>
        /// Submit text to the server
        /// </summary>
        /// <param name=“sender”></param>
        /// <param name=“e”></param>
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            if (_sharedObject != null && _sharedObject.Connected)
            {
                _netConnection.Call(“msgFromClient”, null, InputTextBox.Text);
            }
            else
            {
                Log(“Not connected to the server, reload page and try again.”);
            }
        } 

        public void Log(string msg)
        {
            Dispatcher.BeginInvoke(delegate()
            {
                ChatConsole.Text += msg + “\n”;
                ScrollBar.ScrollToVerticalOffset(ScrollBar.ScrollableHeight);
            });
        } 

        public void LogMessage(string msg)
        {
            Dispatcher.BeginInvoke(delegate()
            {
                ChatConsole.Text += msg;
                ScrollBar.ScrollToVerticalOffset(ScrollBar.ScrollableHeight);
            });
        }
    }
}

Additionally add a new UsersRSO class to the Silverlight application

using System;
using FluorineFx;
using FluorineFx.Net; 

namespace SilverlightChatApplication
{
    public class UsersRSO : RemoteSharedObject
    {
        Page _page; 

        //This class must have parameterless constructor!
        public UsersRSO()
        {
        } 

        public Page Page
        {
            get { return _page; }
            set { _page = value; }
        } 

        public void msgFromSrvr(string msg)
        {
            _page.LogMessage(msg);
        }
    }
}

This class is our custom RemoteSharedObject class and it is required as FMS will notify us by calling the “msgFromSrvr” method on RSO.

Run FMS
Run the FluorineFx Silverlight Policy Server
Run the Silverlight client application (by running the host web application)

Here is a screenshot of the running application

Get the latest FluorineFx code and binaries from the SVN repository

Update: the Silverlight library is available for download in the latest FluorineFx version