• How to track users using a SharedObject

    The following is the basic code to track connected users in a shared object:

    package com.wowza.wms.plugin.test.module;
    
    import com.wowza.wms.module.*; 
    import com.wowza.wms.client.*;
    import com.wowza.wms.application.*;
    import com.wowza.wms.sharedobject.*;
    
    public class ModuleConnectedUserSO extends ModuleBase
    {
    	static public final String CONNECTEDUSERS_SONAME = "connectedUsersSO";
    
    	public void onAppStart(IApplicationInstance appInstance)
    	{
    		getLogger().info("onAppStart: ");
    
    		// Create a shared object to keep track of the connected users
    		ISharedObjects sharedObjects = appInstance.getSharedObjects(false);
    		ISharedObject connectedUsersSO = new SharedObject(CONNECTEDUSERS_SONAME);
    		sharedObjects.put(CONNECTEDUSERS_SONAME, connectedUsersSO);
    		
    		connectedUsersSO.lock();
    		try
    		{
    			connectedUsersSO.acquire();
    		}
    		catch (Exception e)
    		{
    			
    		}
    		finally
    		{
    			connectedUsersSO.unlock();
    		}
    		getLogger().info("onAppStart: create shared object: "+CONNECTEDUSERS_SONAME);
    	}
    
    	public void onAppStop(IApplicationInstance appInstance)
    	{
    		getLogger().info("onAppStop: ");
    
    		// release the shared object when the application stops
    		ISharedObjects sharedObjects = appInstance.getSharedObjects(false);
    		ISharedObject connectedUsersSO = sharedObjects.get(CONNECTEDUSERS_SONAME);
    		if (connectedUsersSO != null)
    		{
    			getLogger().info("onAppStart: release shared object: "+CONNECTEDUSERS_SONAME);
    			connectedUsersSO.lock();
    			try
    			{
    				connectedUsersSO.release();
    			}
    			catch (Exception e)
    			{
    				
    			}
    			finally
    			{
    				connectedUsersSO.unlock();
    			}
    		}
    	}
    
    	public void onConnect(IClient client, RequestFunction function, AMFDataList params) 
    	{
    		
    		String userName = getParamString(params, PARAM1);
    		client.getProperties().setProperty("userName", userName);
    
    	}
    	public void onConnectAccept(IClient client)
    	{
    		// when a client connection is accepted add a property to the shared object
    		// that notifies all connections of new client
    		getLogger().info("onConnectAccept: "+client.getClientId());
    		ISharedObjects sharedObjects = getAppInstance(client).getSharedObjects(false);
    		ISharedObject connectedUsersSO = sharedObjects.get(CONNECTEDUSERS_SONAME);
    		if (connectedUsersSO != null)
    		{
    			connectedUsersSO.lock();
    			try
    			{
    				getLogger().info("onConnectAccept: add property: "+client.getClientId());
    				String userName = client.getProperties().getPropertyStr("userName");
    				String propName = userName+ ": " + client.getClientId();
    				connectedUsersSO.setProperty(propName, "connected");
    			}
    			catch (Exception e)
    			{
    				
    			}
    			finally
    			{
    				connectedUsersSO.unlock();
    			}
    		}
    	}
    
    	public void onDisconnect(IClient client)
    	{
    		// When the client is disconnected remove the property from the shared object
    		getLogger().info("onDisconnect: "+client.getClientId());
    		ISharedObjects sharedObjects = getAppInstance(client).getSharedObjects(false);
    		ISharedObject connectedUsersSO = sharedObjects.get(CONNECTEDUSERS_SONAME);
    		if (connectedUsersSO != null)
    		{
    			synchronized(connectedUsersSO)
    			{
    				connectedUsersSO.lock();
    				try
    				{
    					getLogger().info("onDisconnect: remove property: "+client.getClientId());
    					String propName = "c"+client.getClientId();
    					connectedUsersSO.deleteSlot(propName);
    					connectedUsersSO.purge(connectedUsersSO.getVersion()-200);
    				}
    				catch (Exception e)
    				{
    					
    				}
    				finally
    				{
    					connectedUsersSO.unlock();
    				}
    			}
    		}
    	}
    }
    Note: On May 23, 2008 I removed the pinging code since it is no longer needed based on new features in Wowza Pro 1.5.0.

    This is the essential client-side Actionscript. This includes an binadable ArrayCollection that would only be useful in a Flex application:

    import flash.events.NetStatusEvent;
    import flash.net.NetConnection;
    import flash.net.SharedObject;
    // for Flex
    import mx.collections.ArrayCollection;
    
    
    public function doConnect(event:Event = null):void 
    {
    	nc = new NetConnection();
    	nc.addEventListener(NetStatusEvent.NET_STATUS,netStatusHandler);
    	nc.connect("rtmp://[wowza-address]/[appName],"Charlie");
    }
    
    
    function netStatusHandler(infoObject:NetStatusEvent):void 
    {
    	connectedUsersSO = SharedObject.getRemote("connectedUsersSO", nc.uri, false);
    	connectedUsersSO.addEventListener(SyncEvent.SYNC, streamSynch);
    	connectedUsersSO.connect(nc);		
    
    }
    
    // for Flex
    [Bindable]
    public var streamList:ArrayCollection;
    
    private function streamSynch(event:SyncEvent):void
    {
    	var results:Object = event.target.data;
            var array:Array = new Array();
            for( var item:String in results ) 
    	{              
    		var obj:Object = new Object();
    		obj.label = results[ item ];
    		obj.value = item;
    		array.push( obj );
    	}
    	// for Flex
    	streamList = new ArrayCollection(array);
    }

    If you're having problems or want to discuss this article, post in our forum.