17 November 2009
Intermediate
Adobe offers two different services for building and managing peer-to-peer (P2P) applications: Adobe Cirrus (previously codenamed Stratus) and Adobe LiveCycle Collaboration Service. The two serve different purposes. Cirrus is a service on Adobe Labs that allows you to preview the latest prerelease capabilities of Flash. LiveCycle Collaboration Service is a commercially supported service with an easy-to-use development framework.
This article will help you understand the two different platforms and teach you how to migrate applications you have developed on Cirrus to a commercial service with LiveCycle Collaboration Service (LCCS).
Note: The Cirrus service discussed in this article is not to be confused with the "Cirrus" code name for Adobe Flash Builder for Force.com – Developer Preview, which is a development tool for building cloud-based RIAs.
The basic differences between using Adobe Cirrus (a P2P rendezvous service) and LiveCycle Collaboration Service boil down to control and support. With Cirrus, you have limited control over managing your P2P applications from the server, because the service is solely a rendezvous service responsible for helping applications running in Adobe Flash Player connect with one another. Moreover, because it is an Adobe Labs technology, Cirrus will always have the latest pre-release capabilities of Adobe Flash built in. Our goal with Cirrus is to provide you with access to these new features so we can collect your collective feedback. Cirrus is for non-commercial use only.
Adobe LiveCycle Collaboration Service is a commercial service that provides an easy-to-use development model supported by Adobe's hosted service. It includes a framework to se P2P—so you don't have to care whether it's using P2P or hub-and-spoke communication. Together with this, it has automatic failover from P2P to hub-and-spoke for clients who don't have a UDP-enabled network.
protocol="rtmfp" on ConnectSession or ConnectSessionContainer (even if you don't have a UDP-enabled network, it will try and then fall back to RTMPS)LiveCycle Collaboration Service is very efficient when using P2P and uses it whenever possible. Together with this, it has automatic failover to hub-and-spoke when P2P is unavailable. On the other hand, when using LiveCycle Collaboration Service, only audio and video streams are sent using P2P—no byte/text data. In a video chat scenario, audio and video streams are transferred using P2P when possible, but text chat, whiteboard, or mouse position data are sent using hub-and-spoke.
This is the main difference between LiveCycle Collaboration Service and Cirrus in the area of data transfer:
Use LiveCycle Collaboration Service if you need an easy-to-use, tested implementation with failover, support, and monitoring with a commercial service. LCCS will let you use guaranteed P2P commercially in your product.
In addition to Cirrus and LiveCycle Collaboration Service, the RTMFP protocol will also be available in a future release of Flash Media Server.
OddCast, which provides and builds VoIP solutions on the Adobe Flash Platform, recently switched from Cirrus to LiveCycle Collaboration Service. As David Segal, Team Leader, Front-End Development, explained it:
The main reason we switched from Status to LCCS was we needed to make our application commercially available to our customers. Our goal was to create a P2P tool to connect small businesses with their clients using voice and text chat. I found LCCS to be a comprehensive, feature-rich set of tools for P2P communication. In comparison to Cirrus, I found working with the LCCS framework to be a huge upgrade both in terms of its feature set and its ease of implementation. Much of the code I had written to manage NetStreams with Status became redundant with the LCCS framework, as it was already built-in. I ended up with a more readable, usable, and easily expandable application. In addition, the admin tool is a nice addition that has been quite helpful for debugging and monitoring usage of the service.
Figure 1 depicts two aspects of the SitePal Communication Center. The two browser windows at the top of the image are the client-facing apps (SWFs). They can be embedded in the web pages of any of OddCast's clients and allow visitors of the site to voice- or text-chat with the site owner or his/her help desk. On the bottom right is the AIR application that the site owner runs to receive the connections from the SWFs above, as well as monitor traffic and usage of the site.
Given the differences between Cirrus and LiveCycle Collaboration Service and the different levels of control they offer the developer, connecting to these services for P2P applications is also very different. Following are two examples of different connection routines between LCCS and Cirrus. You can use these examples if you choose to migrate from Cirrus to LCCS.
Following is an example based on Adobe Flex 3 of a full-featured video chat application for multiple participants:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
minWidth="1024" minHeight="768"
xmlns:rtc="AfcsNameSpace">
<rtc:AdobeHSAuthenticator
id="auth" userName="SOME-USERNAME" protocol="rtmfp" />
<rtc:ConnectSessionContainer
authenticator="{auth}" width="100%" height="100%" id="session"
roomURL="http://connectnow.acrobat.com/YOUR-ACCOUNT/YOUR-ROOM">
<rtc:WebCamera x="10" y="10"/>
<rtc:AudioPublisher />
<rtc:AudioSubscriber />
<rtc:SimpleChat x="10" y="218" width="250"/>
</rtc:ConnectSessionContainer>
</mx:Application>
For more information about LiveCycle Collaboration Service, see the Flash Platform Collaboration Services page. Sign up for a developer account at afcs.acrobat.com.
Here is an example based on Adobe Flash Builder 4 illustrating a similar, basic video chat for two people. Notice in this example that you need to transfer P2P fingerprints manually. Automatic fingerprint exchange needs server-side scripting and database, and the app gets even more complicated (and it's not available with Cirrus). However, you have full control over the connection routine implementation at the ActionScript level:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/halo" minWidth="1024"
minHeight="768">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[
/**
*
* P2P
*
*/
public const SERVER_ADDRESS:String = "rtmfp://stratus.adobe.com/";
public const DEVELOPER_KEY:String = "YOUR-DEVELOPER-KEY";
private var conn:NetConnection;
private var streamOut:NetStream;
private var streamIn:NetStream;
[Bindable]
private var myPeerID:String;
private var farID:String;
public function connect():void{
conn = new NetConnection();
conn.addEventListener(NetStatusEvent.NET_STATUS,connStatus);
conn.addEventListener(AsyncErrorEvent.ASYNC_ERROR,asyncErr);
conn.connect(SERVER_ADDRESS+DEVELOPER_KEY);
}
private function connStatus(event:NetStatusEvent):void{
trace(event.info.code);
txtStatus.text = event.info.code;
if(event.info.code=="NetConnection.Connect.Success"){
myPeerID = conn.nearID;
initOutStream();
}
}
private function asyncErr(event:AsyncErrorEvent):void{
}
private function initOutStream():void{
trace("initOutStream");
streamOut = new NetStream(conn,NetStream.DIRECT_CONNECTIONS);
streamOut.addEventListener(NetStatusEvent.NET_STATUS,streamStatus);
streamOut.publish("media");
// CAMERA
var cam:Camera = Camera.getCamera();
camOut.videoObject.attachCamera(cam);
streamOut.attachCamera(cam);
streamOut.attachAudio(Microphone.getMicrophone());
var streamOutClient:Object = new Object();
streamOutClient.onPeerConnect = function(farStream:NetStream):Boolean{
// initInStream(farStream.farID);
return true;
}
}
private function initInStream(farID:String):void{
trace("initInStream: ")
streamIn = new NetStream(conn,farID);
streamIn.addEventListener(NetStatusEvent.NET_STATUS,streamStatus);
streamIn.play("media");
streamIn.client = this;
camIn.videoObject.attachNetStream(streamIn);
}
private function streamStatus(event:NetStatusEvent):void{
trace("streamStatus: "+event.info.code);
}
public function receiveMessage(msg:String):void{
trace("receiveMessage: "+msg)
txtHistory.text = msg+"\n"+txtHistory.text;
}
public function sendMessage(msg:String):void{
streamOut.send("receiveMessage",msg);
receiveMessage(msg);
}
protected function btnSend_clickHandler(event:MouseEvent):void{
trace("sendMessage: "+txtMessage.text)
sendMessage(txtMessage.text);
}
]]>
</fx:Script>
<s:TextInput x="10" y="10" width="201" id="txtStatus"/>
<s:TextInput x="10" y="40" width="201" id="txtStatusStream"/>
<s:TextInput x="9" y="151" width="157" id="txtFarPeerID"/>
<s:TextInput x="9" y="124" width="250" id="txtMyPeerID" text="{myPeerID}"/>
<s:Button x="10" y="75" label="Connect" click="connect()" id="btnConnect"/>
<s:Button x="174" y="152" label="initInStream" click="initInStream(txtFarPeerID.text)" id="btnConnect0"/>
<s:TextArea x="9" y="181" width="285" height="160" id="txtHistory"/>
<s:Button x="232" y="351" label="Send" id="btnSend" click="btnSend_clickHandler(event)" width="62"/>
<s:TextInput x="9" y="349" id="txtMessage" width="215" enter="{btnSend_clickHandler(null)}"/>
<s:VideoElement id="camOut" width="141" height="112" x="221" y="8"/>
<s:VideoElement id="camIn" width="141" height="112" x="365" y="8"/>
</s:Application>
Dive deeper into P2P by visiting the following resources:
Tutorials & Samples |