P2P Multicast in Flash Player 10!

P2PMulticast communication flow

P2PMulticast communication flow

P2P multicast in Flash Player 10, yeap… ready? This method creates n:n model of p2p communicators. So broadcasting data goes from broadcaster directly to all receivers. All users are broadcasters and receivers. There is no multilevel logic in it, so keep in mind the upload bandwidth of broadcaster. Feel free to use it in your flash applications, just hit me when you decide.

Core classes and files:

P2PMulticast application


P2PMulticast scheme

P2PMulticast scheme

sk.yoz.net.P2PMulticast.as is core class that cares about p2p clients and dispatches important events, such as connecting peer, disconnecting peer, incomming data:

package sk.yoz.net
{
    import flash.events.EventDispatcher;
    import flash.events.TimerEvent;
    import flash.utils.Timer;
    
    import sk.yoz.events.P2PMulticastEvent;
    import sk.yoz.events.P2PMulticastPersistenceEvent;
    import sk.yoz.events.P2PMulticastStreamEvent;
    
    public class P2PMulticast extends EventDispatcher
    {
        private var stream:P2PMulticastStream;
        private var persistence:IP2PMulticastPersistence;
        
        private var clientReloadTimer:Timer;
        
        public function P2PMulticast(persistence:IP2PMulticastPersistence, developerKey:String = "", 
            publishName:String = "", maxPeerConnections:uint = 0, handshakeURL:String = "", 
            clientConnectionTimeout:uint = 0)
        {
            super();
            
            stream = new P2PMulticastStream(developerKey, publishName, maxPeerConnections, 
                handshakeURL, clientConnectionTimeout);
            stream.addEventListener(P2PMulticastStreamEvent.CLIENT_ID_ASSIGNED, clientIdAssignedHandler);
            stream.addEventListener(P2PMulticastStreamEvent.PEER_CONNECTED, peerConnectedHandler);
            stream.addEventListener(P2PMulticastStreamEvent.PEER_DISCONNECTED, peerDisconnectedHandler);
            stream.addEventListener(P2PMulticastStreamEvent.PEER_DATA, peerDataHandler);
            
            this.persistence = persistence;
            persistence.addEventListener(P2PMulticastPersistenceEvent.CLIENTS_DEFINED, clientsDefinedHandler);
        }
        
        public function get connected():Boolean
        {
            return stream.connected;
        }
        
        public function set debug(value:Boolean):void
        {
            if(stream)
                stream.debug = value;
        }
        
        public function get onlineClients():Array
        {
            return stream.onlineClients;
        }
        
        public function broadcast(data:Object):Boolean
        {
            return stream.broadcast(data);
        }
        
        private function clientIdAssignedHandler(event:P2PMulticastStreamEvent):void
        {
            persistence.getClientList();
            dispatchEvent(new P2PMulticastEvent(P2PMulticastEvent.STREAM_CONNECTED));
        }
        
        private function clientsDefinedHandler(event:P2PMulticastPersistenceEvent):void
        {
            var list:Array = event.data.list;
            for each(var farID:String in list)
                stream.connectClient(farID);
            if(list.indexOf(stream.nearID) == -1)
                list.push(stream.nearID);
            persistence.saveClientList(list);
            clientReloadTimer = new Timer(10000 + Math.random() * 5000, 1);
            clientReloadTimer.addEventListener(TimerEvent.TIMER_COMPLETE, clientReloadTimerComplete);
            clientReloadTimer.start();
        }
        
        private function clientReloadTimerComplete(event:TimerEvent):void
        {
            persistence.getClientList();
            
            var timer:Timer = event.target as Timer;
            timer.removeEventListener(TimerEvent.TIMER_COMPLETE, clientReloadTimerComplete);
            timer = null;
        }
        
        private function peerDataHandler(event:P2PMulticastStreamEvent):void
        {
            dispatchEvent(new P2PMulticastEvent(P2PMulticastEvent.PEER_DATA, false, false, event.data));
        }
        
        private function peerConnectedHandler(event:P2PMulticastStreamEvent):void
        {
            var farID:String = event.data.farID;
            verifyClient(farID);
            dispatchEvent(new P2PMulticastEvent(P2PMulticastEvent.PEER_CONNECTED, false, true, event.data));
        }
        
        private function peerDisconnectedHandler(event:P2PMulticastStreamEvent):void
        {
            var farID:String = event.data.farID;
            verifyClient(farID);
            dispatchEvent(new P2PMulticastEvent(P2PMulticastEvent.PEER_DISCONNECTED, false, false, event.data));
        }
        
        private function verifyClient(farID:String):void
        {
            if(!stream.allConnected)
                return;
            var list:Array = stream.getClientList();
            list.push(stream.nearID);
            persistence.saveClientList(list);
        }
    }
}

sk.yoz.net.P2PMulticastPersistence.as class works as database of online clients. Before peer connection can be established, clients need to know each other ID. This class works as persistence where all active IDs are stored. This one is based on .xml (data) and .php (processor), but you can create your own persistence lets say based on database…:

package sk.yoz.net
{
    import com.adobe.crypto.MD5;
    
    import flash.events.Event;
    import flash.events.EventDispatcher;
    import flash.events.HTTPStatusEvent;
    import flash.events.IOErrorEvent;
    import flash.events.ProgressEvent;
    import flash.events.SecurityErrorEvent;
    import flash.net.URLLoader;
    import flash.net.URLRequest;
    import flash.net.URLRequestMethod;
    import flash.utils.ByteArray;
    
    import sk.yoz.Log;
    import sk.yoz.events.P2PMulticastPersistenceEvent;
    
    public class P2PMulticastPersistence extends EventDispatcher implements IP2PMulticastPersistence
    {
        private var signSalt:String = "StratusIsCoool";
        
        private var loader:URLLoader = new URLLoader();
        public var scriptURL:String = "";   // http://mydomain/script.php? or ...
                                            // http://mydomain/script.php?id=123
                                            // "&sign={HASH}" is added
        
        public var debug:Boolean = false;
        private var log:Log = Log.instance;
        
        public function P2PMulticastPersistence(scriptURL:String, signSalt:String = "")
        {
            super();
            this.scriptURL = scriptURL;
            this.signSalt = signSalt;
            loader.addEventListener(Event.COMPLETE, loaderComplete);
            loader.addEventListener(Event.OPEN, openHandler);
            loader.addEventListener(ProgressEvent.PROGRESS, progressHandler);
            loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
            loader.addEventListener(HTTPStatusEvent.HTTP_STATUS, httpStatusHandler);
            loader.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
        }
        
        public function saveClientList(list:Array):void
        {
            var data:String = listToMessage(list);
            var sign:String = signData(data);
            var request:URLRequest = new URLRequest(scriptURL + "&sign=" + sign);
            var saver:URLLoader = new URLLoader();
            request.contentType = "text/xml";
            request.data = data;
            request.method = URLRequestMethod.POST;
            saver.load(request);
        }
        
        public function getClientList():void
        {
            var request:URLRequest = new URLRequest(scriptURL);
            request.method = URLRequestMethod.GET;
            
            try {
                loader.load(request);
            } catch (error:Error) {
                if(debug)
                    log.write("P2PMulticastPersistence.getClientList() load() Error");
            }
        }
        
        private function loaderComplete(event:Event):void
        {
            var list:Array = messageToList(loader.data);
            dispatchEvent(new P2PMulticastPersistenceEvent(P2PMulticastPersistenceEvent.CLIENTS_DEFINED, true, true, {list:list}));
        }
        
        private function messageToList(message:String):Array
        {
            var xml:XML = XML(message);
            var list:Array = [];
            for each(var id:String in xml.client.@['id'])
                list.push(id);
            return list;
        }
        
        private function listToMessage(list:Array):String
        {
            var xml:XML = <clients />
            var xmlNode:XML;
            for each(var id:String in list)
            {
                xmlNode = <client />;
                xmlNode.@id = id;
                xml.appendChild(xmlNode);
            }
            return xml.toXMLString();
        }
        
        private function openHandler(event:Event):void
        {
            if(debug)
                log.write("P2PMulticastPersistence.openHandler(" + event.toString() + ")");
        }
        
        private function progressHandler(event:ProgressEvent):void
        {
            if(debug)
                log.write("P2PMulticastPersistence.progressHandler() " + 
                        "// loaded:" + event.bytesLoaded + " total: " + event.bytesTotal);
        }
        
        private function securityErrorHandler(event:SecurityErrorEvent):void
        {
            if(debug)
                log.write("P2PMulticastPersistence.securityErrorHandler(" + event.toString() + ")");
        }
        
        private function httpStatusHandler(event:HTTPStatusEvent):void
        {
            if(debug)
                log.write("P2PMulticastPersistence.httpStatusHandler(" + event.toString() + ")");
        }
        
        private function ioErrorHandler(event:IOErrorEvent):void
        {
            if(debug)
                log.write("P2PMulticastPersistence.ioErrorHandler(" + event.toString() + ")");
        }
        
        private function signData(data:String):String
        {
            return MD5.hash(signSalt + MD5.hash(data));
        }
    }
}

sk.yoz.net.P2PMulticastStream.as class provides stratus handshake (assigned ID) and directly communicates via NetStream

package sk.yoz.net</p>
{
    import flash.events.AsyncErrorEvent;
    import flash.events.EventDispatcher;
    import flash.events.IOErrorEvent;
    import flash.events.NetStatusEvent;
    import flash.events.SecurityErrorEvent;
    import flash.events.TimerEvent;
    import flash.net.NetConnection;
    import flash.net.NetStream;
    
    import sk.yoz.utils.DataTimer;
    import sk.yoz.Log;
    import sk.yoz.events.P2PMulticastStreamEvent;
    
    public class P2PMulticastStream extends EventDispatcher
    {
        private var publishName:String = "com";
        private var maxPeerConnections:uint = 100;
        private var handshakeURL:String = "rtmfp://stratus.adobe.com";   // eg: rtmfp://stratus.adobe.com
        private var developerKey:String = "";
        private var clientConnectionTimeout:uint = 15000;
        
        private var stratus:NetConnection;
        private var publish:NetStream;
        private var clients:Object = {};
        
        public var debug:Boolean = false;
        private var log:Log = Log.instance;
        
        public function P2PMulticastStream(developerKey:String = "", publishName:String = "", 
            maxPeerConnections:uint = 0, handshakeURL:String = "", clientConnectionTimeout:uint = 0)
        {
            super();
            if(publishName)
                this.publishName = publishName;
            if(maxPeerConnections)
                this.maxPeerConnections = maxPeerConnections;
            if(handshakeURL)
                this.handshakeURL = handshakeURL;
            if(developerKey)
                this.developerKey = developerKey;
            if(clientConnectionTimeout)
                this.clientConnectionTimeout = clientConnectionTimeout;
                
            stratus = new NetConnection();
            stratus.maxPeerConnections = this.maxPeerConnections;
            stratus.addEventListener(NetStatusEvent.NET_STATUS, stratusStatusHandler);
            stratus.addEventListener(AsyncErrorEvent.ASYNC_ERROR, stratusAsyncErrorHandler);
            stratus.addEventListener(IOErrorEvent.IO_ERROR, stratusIOErrorHandler);
            stratus.addEventListener(SecurityErrorEvent.SECURITY_ERROR, stratusSecurityErrorHandler);
            stratus.connect(this.handshakeURL + "/" + this.developerKey + "/");
        }
        
        public function get connected():Boolean
        {
            return nearID ? true : false;
        }
        
        public function get onlineClients():Array
        {
            var list:Array = [];
            for(var farID:String in clients)
                if(client(farID).connected)
                    list.push(farID);
            return list;
        }
        
        public function get allConnected():Boolean
        {
            for(var farID:String in clients)
                if(!client(farID).connected)
                    return false;
            return true;
        }
        
        private function initPublish():void
        {
            if(debug)
                log.write("P2PMulticastStream.initPublish()");
            publish = new NetStream(stratus, NetStream.DIRECT_CONNECTIONS);
            publish.addEventListener(NetStatusEvent.NET_STATUS, publishStatusHandler);
            publish.addEventListener(AsyncErrorEvent.ASYNC_ERROR, publishAsyncErrorHandler);
            publish.addEventListener(IOErrorEvent.IO_ERROR, publishIOErrorHandler);
            publish.publish(publishName);
            publish.client = {
                onPeerConnect: function(caller:NetStream):Boolean
                {
                    connectClient(caller.farID);
                    return true;
                }
            };
        }
        
        public function get nearID():String
        {
            return stratus && stratus.connected ? stratus.nearID : '';
        }
        
        private function client(farID:String):StratusStream
        {
            return clients.hasOwnProperty(farID) ? clients[farID] : null;
        }
        
        public function connectClient(farID:String):void
        {
            if(farID == nearID || clients.hasOwnProperty(farID))
                return;
                
            if(debug)
                log.write("P2PMulticastStream.connectClient(" + farID + ")");
            var c:StratusStream = new StratusStream(stratus, farID);
            c.addEventListener(NetStatusEvent.NET_STATUS, clientStatusHandler);
            c.play(publishName);
            c.client = {
                onData: function(data:Object):void
                {
                    if(debug)
                        log.write("P2PMulticastStream.client(" + farID + ").onData(" + data.toString() + ")");
                    dispatchEvent(new P2PMulticastStreamEvent(P2PMulticastStreamEvent.PEER_DATA, false, false, {farID:farID, data:data}));
                }
            };
            clients[farID] = c;
            
            var timer:DataTimer = new DataTimer(clientConnectionTimeout, 1, {farID:farID});
            timer.addEventListener(TimerEvent.TIMER_COMPLETE, clientConnectionTimeoutHandler);
            timer.start();
        }
        
        private function clientConnectionTimeoutHandler(event:TimerEvent):void
        {
            var timer:DataTimer = event.target as DataTimer;
            var farID:String = timer.data.farID;
            if(client(farID) && !client(farID).connected)
                disconnectClient(farID);
            timer.removeEventListener(TimerEvent.TIMER_COMPLETE, clientConnectionTimeoutHandler);
            timer = null;
        }
        
        private function disconnectClient(farID:String):void
        {
            if(farID == nearID || !client(farID))
                return;
            clients[farID] = null;
            delete clients[farID];
            if(debug)
                log.write("P2PMulticastStream.disconnectClient(" + farID + ")");
            dispatchEvent(new P2PMulticastStreamEvent(P2PMulticastStreamEvent.PEER_DISCONNECTED, true, true, {farID:farID}));
        }
        
        public function broadcast(data:Object):Boolean
        {
            if(!connected)
                return false;
            try
            {
                publish.send("onData", data);
            }
            catch(error:Error)
            {
                if(debug)
                    log.write("P2PMulticastStream.sendData(" + data.toString() + ") // ERROR: " + error.message);
                return false;
            }
            return true;
        }
        
        private function clientStatusHandler(event:NetStatusEvent):void
        {
            var farID:String = event.target.farID;
            if(debug)
                log.write("P2PMulticastStream.clientStatusHandler(" + event.info.code + ")");
            switch(event.info.code)
            {
                case "NetStream.Play.Start":
                    if(debug)
                        log.write("P2PMulticastStream.clientStatusHandler(" + event.info.code + ") " + 
                                "// client(" + farID + ") connected");
                    client(farID).connected = true;
                    dispatchEvent(new P2PMulticastStreamEvent(P2PMulticastStreamEvent.PEER_CONNECTED, true, true, {farID:farID}));
                    break;
            }
        }
        
        private function stratusStatusHandler(event:NetStatusEvent):void
        {
            if(debug)
                log.write("P2PMulticastStream.stratusStatusHandler(" + event.info.code + ")");
            switch(event.info.code)
            {
                case "NetConnection.Connect.Success":
                    if(debug)
                        log.write("P2PMulticastStream.stratusStatusHandler(" + event.info.code + ") " + 
                                "// p2p connection ready (" + nearID + ")");
                    initPublish();
                    dispatchEvent(new P2PMulticastStreamEvent(P2PMulticastStreamEvent.CLIENT_ID_ASSIGNED, true, true, {nearID:nearID}));
                    break;
                    
                case "NetStream.Connect.Closed":
                    if(debug)
                        log.write("P2PMulticastStream.stratusStatusHandler(" + event.info.code + ") " + 
                                "// p2p disconnected (" + event.info.stream.farID + ")");
                    disconnectClient(event.info.stream.farID);
                    break;
            }
        }
        
        private function stratusAsyncErrorHandler(event:AsyncErrorEvent):void
        {
            if(debug)
                log.write("P2PMulticastStream.stratusAsyncErrorHandler(" + event.text + ")");
        }
        
        private function stratusIOErrorHandler(event:IOErrorEvent):void
        {
            if(debug)
                log.write("P2PMulticastStream.stratusIOErrorHandler(" + event.text + ")");
        }
        
        private function stratusSecurityErrorHandler(event:SecurityErrorEvent):void
        {
            if(debug)
                log.write("P2PMulticastStream.stratusSecurityErrorHandler(" + event.text + ")");
        }
        
        private function publishStatusHandler(event:NetStatusEvent):void
        {
            if(debug)
                log.write("P2PMulticastStream.publishStatusHandler(" + event.info.code + ")");
            switch(event.info.code)
            {
                case "NetStream.Publish.Start":
                    if(debug)
                        log.write("P2PMulticastStream.publishStatusHandler(" + event.info.code + ") " + 
                                "// publishing started (" + nearID + ")");
                    break;
            }
        }
        
        private function publishAsyncErrorHandler(event:AsyncErrorEvent):void
        {
            if(debug)
                log.write("P2PMulticastStream.publishAsyncErrorHandler(" + event.text + ")");
        }
        
        private function publishIOErrorHandler(event:IOErrorEvent):void
        {
            if(debug)
                log.write("P2PMulticastStream.publishIOErrorHandler(" + event.text + ")");
        }
        
        public function getClientList():Array
        {
            var list:Array = [];
            for(var farID:String in clients)
                list.push(farID);
            return list;
        }
    }
}

Application.mxml is demo application, export it and open in 2 (or more) different windows. When connected (online text appear), you can communicate to other instances via p2p. Do not forget to insert your stratus developer key:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
    applicationComplete="init()" viewSourceURL="srcview/index.html">
<mx:Script>
<![CDATA[
    import sk.yoz.events.P2PMulticastEvent;
    import sk.yoz.net.P2PMulticastPersistence;
    import sk.yoz.net.P2PMulticast;
    import sk.yoz.Log;
    
    private var log:Log = Log.instance;
    private var p2p:P2PMulticast;
    
    private function init():void
    {
        log.output = logArea;
        
        var scriptURL:String = "http://localhost/p2p/clients.php?";
        var signSalt:String = "StratusIsCoool";
        var developerKey:String = "***"; // HERE COMES YOUR STRATUS DEVELOPER KEY
        
        var persistence:P2PMulticastPersistence = new P2PMulticastPersistence(scriptURL, signSalt);
        p2p = new P2PMulticast(persistence, developerKey);
        p2p.debug = true;
        persistence.debug = true;
        
        p2p.addEventListener(P2PMulticastEvent.STREAM_CONNECTED, streamConnectedHandler);
        p2p.addEventListener(P2PMulticastEvent.PEER_CONNECTED, peerChangeHandler);
        p2p.addEventListener(P2PMulticastEvent.PEER_DISCONNECTED, peerChangeHandler);
        p2p.addEventListener(P2PMulticastEvent.PEER_DATA, peerDataHandler);
    }
    
    private function streamConnectedHandler(event:P2PMulticastEvent):void
    {
        info.text = "online";
    }
    
    private function peerChangeHandler(event:P2PMulticastEvent):void
    {
        peers.dataProvider = p2p.onlineClients;
    }
    
    private function peerDataHandler(event:P2PMulticastEvent):void
    {
        dataArea.text += event.data.farID + ":" + event.data.data;
    }
    
    private function broadcast():void
    {
        p2p.broadcast(message.text);
        callLater(function():void
        {
            message.text = "";
        });
    }
]]>
</mx:Script>
<mx:HBox width="100%" height="100%">
    <mx:VBox width="100%" height="100%">
        <mx:TextArea id="logArea" width="100%" height="100%" />
        <mx:TextArea id="dataArea" width="100%" height="100%" />
        <mx:HBox width="100%">
            <mx:TextInput id="message" width="100%" enter="broadcast()"/>
            <mx:Button click="broadcast()" label="send"/>
        </mx:HBox>
    </mx:VBox>
    
    <mx:VBox width="400">
        <mx:Text id="info" text="offline"/>
        <mx:List id="peers" width="100%"/>
    </mx:VBox>
</mx:HBox>
</mx:Application>

clients.php file reads and writes clients.xml. It uses hash to validate data. Make sure you set sign salt “StratusIsCoool” in .php file same as signSalt in Application.mxml

<?php
function signData($data){
	return md5("StratusIsCoool".md5($data));
}

function defaultize(&$key, $default=null){
   return is_null($key)?$key=$default:$key;
}

$file=dirname(__FILE__)."/clients.xml";
$bytes=defaultize($GLOBALS['HTTP_RAW_POST_DATA'], null);
$sign=defaultize($_GET['sign'], null);

if($bytes){
	if(!$sign || $sign !== signData($bytes))
		exit;
	
	$fd=fopen($file, 'w');
	fwrite($fd, $bytes);
	fclose($fd);
	exit;
}

if(is_file($file))
	readfile($file);

Other required files can be found here: P2PMulticast source. Don not forget to export your application into Flash Player 10. P2P Multicast is used in onBoard collaborative painting.

21 comments so far

  1. [...] If you are looking for P2P Multicast in Flash Player 10, see here. [...]

  2. Polprav October 15, 2009 22:19

    Hello from Russia!
    Can I quote a post in your blog with the link to you?

  3. Jozef Chúťka October 16, 2009 09:39

    Yes, sure

  4. Oscar April 24, 2010 16:14

    Hello, wanted to know if you can use C #. Net on the server instead of php, I think so. And to know how much you could receive receptors simultaneously.

    Thanks and sorry for my English.

  5. Jozef Chúťka April 24, 2010 20:13

    Hi Oscar, sure you can use any backend. Its all about sharing/persisting farID for each connected user, however It may be desired to handle incomming/ougoing farIDs different ways for different apps.

  6. Olayode Ezekiel April 26, 2010 18:54

    Hi,

    1. Please can there be a way to create a simple p2p app in Flash 10.1 without Stratus.

    2. How can i use LAN Discovery in local network for local Peers without Stratus.

  7. Jozef Chúťka April 26, 2010 23:13

    Hi Olayode,
    in fact, stratus is the key service that enables p2p for flash. As for now, there is only adobe cloud service available on rtmfp://stratus.adobe.com to be used for handshakes, but I guess later version of flash media server will enable this handshake feature on your own server… While you aim your product at 10.1, you can use flash player built-in technology for multiuser p2p (read more here http://www.flashrealtime.com/p2p-groupspecifier-explained-1/ ). If you are willing to create p2p channels over your LAN (without beeing online), you possibly could do that using your own flash media server installed on one of the LAN connected server, but I am sory to tell you, as far as I know there is no public release of fms that allows it. Try sincerely ask adobe for thair latest beta with rtmfp.

  8. Olayode Ezekiel April 27, 2010 17:48

    Thanks so much for your reply.

    I will like you to take a look at this and please answer my question.

    I tried this sample page [ http://milkreations.com/projects/chatroom/ ] and worked perfectly on 2 different browsers. and intentionally i off the Internet of the computer and the 2 pages on different browsers were still exchanging data but after some seconds, they both said “Disconnected”.

    With this i think the NetConnection, NetGroup and the GroupSpecifier should work for an Offline P2P app without Stratus on LAN.

    My question is can you look into this a come up with a solution to this.

    Thanks,
    Olayode

  9. Jozef Chúťka April 28, 2010 09:32

    Olayode,
    nothing appears on the website you sent me – just empty flash is all I see. I am not working with rtmfp on protocol level but my best guesses is, fp throws some pings on handshake server form time to time, in order to maintain connection. In your case, you need your own flash media server with rtmfp on LAN so connections can be established even offline the internet. NetGroup and GroupSpecifier are just some utils built over NetConnection, those will work only when NetConnection is ok. In this case, you will get the best answers on adobe forums http://forums.adobe.com/community/webplayers/flash_player – login and start a new discussion. If you succeed with your project please let me know.
    Have a nice one
    Yoz

  10. Abdo April 30, 2010 00:48

    Hello there, great article :)

    Quick stratus question:
    Does stratus somehow disconnect you after a little when connecting two clients running on localhost? I’m getting a NetStream.Connect.Closed event after about a minute or so. (using my own code)

    Thanks =)

  11. Jozef Chúťka April 30, 2010 12:52

    Hi Abdo,

    I have just tested how NetConnection behaves on localhost, but it does not seem to disconnect me even after a minutes. It should not be disconnecting, because your NetStream than has no connection to communicate over. Does your internet connection work correctly?

    Jozo

  12. calvin May 14, 2010 23:37

    Hi there Jozef!

    Your work is fantastic. Is there a link where I can download the source for your onBoard Collorborative Painting software? I would like to use it in a remote art project for me and my friends to work togeter from different cities.

    Thanks for having this cool blog!
    Calvin.

  13. Jozef Chúťka May 15, 2010 09:27

    Hi Calvin,
    thank you, but unfortunately onBoard code is not open, this one as well as onConference and projects for clients, everything other what you see on the blog is…
    anyway you can embed onboard into your page via iframe (use embed button to generate the code)

  14. calvin May 16, 2010 07:35

    Thanks Jozef, I will use your embed code then. I will send you the URL once I set it up.

    Calvin

  15. Rahul August 17, 2010 10:58

    can we receive multiple videos and play them same time..? trying to do this from long time but not sure whether its possible with stratus multicast..

  16. Jozef Chúťka August 17, 2010 12:02

    @Rahul, yes you can publish/attach (identifed by name) any number of NetStreams over one NetConnection

  17. Simon October 7, 2010 11:01

    this is interesting. is it possible to build a simple PONG game?
    like one player sites in USA and one in Sweden? this would be awesome!
    are there any p2p flash player 10.1 games out there?

  18. Jozef Chúťka October 7, 2010 13:25

    hi Simon, sure you can usit for games…
    you can use unicast (faster responses, upload to everyone) or multicast (fp 10.1, slower)
    I use multicast with http://onboard.yoz.sk

  19. Simon October 7, 2010 19:41

    thanks!
    so do we need to pay for a FLASH MEDIA SERVER in future to use the p2p feature over the internet?

    i don’t understand who takes the costs on traffic and everything for using this great feature.

    btw: onBoard is really cool! congrats!

  20. Jozef Chúťka October 8, 2010 13:24

    @Simon,
    the hosted service is not meant to be used for commercial projects, there are some restrictions, thats why you have to use your own fms… traffic is minimal while its p2p! the costs for hosted service takes adobe

Leave a comment

Please be polite and on topic. Your e-mail will never be published.