In Chapter 4, we talked about planning the data. In this chapter, we are going to talk about how the data moves into and out of Director MX and Flash MX 2004. How this data moves into and out of the applications is through Flash Communication Server.
A good analogy for how the Flash Communication Server fits into the scheme of things is air travel. A good place to start would be Chicago O'Hare. You can hardly fly anywhere in the Midwest without a stop in Chicago. You have people flying into the airport, getting off the plane, and running through the airport to catch a connecting flight to somewhere else in the Midwest.
If you use that analogy in relation to dynamic sites, the Flash Communication Server MX is O'Hare. The people arrive via their Flash Players, and they move through the Server to connect to other people's Flash Players.
These connections, as we briefly mentioned in Chapter 3, "Planning a Dynamic Site," are facilitated using Macromedia's proprietary two-way connection protocol named RTMP or Real Time Messaging Protocol. The Flash Communication Server MX can also connect to other communication servers, J2EE, web services, and ASP.NET application servers. This means data in the form of audio, video, and text-based chat can be delivered in real time along with database connectivity.
In Director, this information is delivered through a Flash sprite located on the stage.
Until the introduction of Director MX, developers looking to add a multiuser dimension to their Director presentations used the Shockwave Multiuser Server. This technology has been discontinued and replaced, for obvious reasons, with the Flash Communication Server MX.
The key issue among developers was that the Multiuser Server could handle multiple users, but not as many as they would have liked. Depending on whether you entered your Director serial number, the number of connections available at any one time was anywhere between 50 and 2,000. At the time, up to 2,000 was thought to be acceptable. It isn't in today's Internet environment. The Flash Communication Server is designed to support a seriously heavier load. It has been tested with thousands of simultaneous connections.
What tends to be forgotten in all the hype around the Flash Communication Server MX is that it does more than simply accommodate live video, audio, and chat streams. It offers a whole new dimension to work authored in Director because it also supports Macromedia Flash Remoting, which we discussed in Chapter 9, "Flash Remoting." This opens Director to connections with application servers such as .NET and, as you saw in Chapter 9, ColdFusion MX.
Now that you understand that all you need to communicate with the Flash Communication Server MX through Director MX is a Flash sprite on the stage, a world of possibilities opens up for you. To enable you to explore those possibilities, there is a "subset" of Lingo designed to work with Flash.
The extensive Flash Communication Server MX support that has been added to Director, in addition to letting you access all of the server's features, also gives you the capability to use installed USB or Firewire cameras and microphones. We will be explaining this feature later on.
You access the Flash Communication Server MX in Director through the use of Flash sprites on the Director stage that are designed to work with the server or that are used to create NetConnection or NetStream objects in Lingo that can be used to communicate with the server.
The neat thing about this is you use exactly the same ActionScript commands and properties you would use in Flash to manipulate the Flash Object. In fact, the steps necessary to communicate with the Flash Communication Server MX are the same steps you would use, were you to be using Flash. The end result is remarkably similar workflow models.
Let's look at an example that uses Lingo to facilitate a two-way video chat that utilizes much of the functionality of the Flash Communication Server.
This example is derived from an involved tutorial by a couple of Macromedia people ?Jay Armstrong and Tom Higgins. Though many developers will claim that "code is code," we believe in acknowledging the source. The interesting thing about coding is how quickly certain bits and pieces of code become standardized and rapidly and widely adopted. Back in the early days of Flash, a developer posted to a forum the ActionScript he had written. The code made a globe spin. Within three months, hundreds of sites utilizing spinning globes driven by the posted code started appearing.
We aren't going to get into the creation of the Flash movie. It is nothing more than a 320x240 Flash movie containing the VideoChat component.
The instance name, in Flash, for the component is videoFeed. We'll get into building a very similar application in the next section of the book when we build the online meeting facility. For now, we are going to show you how Director can be used as the shell for this chat.
The chat will occur between two individuals who have web cameras and microphones connected to their computers. Each person participating in the chat will enter their name and the name of the person to whom they are talking. When they connect, the video and audio streams flow through the Flash Communication Server to the Flash sprite located on the stage. When the chat is finished, they simply disconnect.
To accomplish this, Lingo will have to set up a NetConnection object to communicate with the Flash Communication Server. Camera and Microphone objects will have to be created in order to use the cameras and microphones connected to each individual's computer, along with a NetStream object to send and receive each other's video feed.
The movie starts with the two individuals entering their names when prompted. Those names are then captured by Lingo and become a global variable named gNames. Clicking the Connect button (Figure 12.1) moves them to a frame containing the Flash sprite.
The next frame of the Director movie contains the Flash sprite, as shown in Figure 12.2. Attached to this sprite is a Lingo behavior that displays the video stream from the other user. The Lingo creates the ActionScript objects needed to create and control an incoming and outgoing video stream. Under the Flash sprite is some sort of control button that ends the session. In this case, it is a button whose purpose is to disconnect the two users from the Flash Communication Server.
It seems, when you first encounter the project, like a lot of work. It isn't. As we like to say, "Think like a computer." When you do, the code needed becomes nothing more than a series of very logical steps.
The key to what follows is to understand that Lingo and ActionScript are separate languages, sort of like English and German. The way they communicate with each other involves, among other things, being lost in Germany.
One of the authors found himself lost in Hamburg, Germany, a couple of years ago and needed to retrace his steps to a specific intersection. He saw a policeman, walked over to him, and asked in English, "How do I get to the harbor?" The policeman, not speaking English, simply looked at him with a blank stare. The author tried again, only this time, in typical North American fashion, raised his voice a bit and slowly repeated the request word-by-word. The policeman's expression didn't change, but the author suspects the policeman was thinking, "Do I arrest him now or later?"
A lady passing by caught this exchange and walked over. "I speak English," she said. "Can I help you?" The author explained his predicament to his savior, and the lady turned to the policeman and started talking to him in German. He replied, and she gave the author the directions he needed in English.
That story demonstrates how Lingo and Actionscript talk to each other. They need an intermediary to translate their commands so that each clearly understands what the other is asking. This is the purpose of the Flash sprite on the Director stage. As a Director sprite, it will understand the "Flash Lingo" sent to it by Director and communicate the request to the Flash Communication Server using ActionScript. When requests flow the other way, the process is simply reversed.
Here's the code attached to the Flash sprite:
global gNames property pCamera property pIn property pMicrophone property pNetConnect property pOut property pSprite on exitFrame (me) if voidP(pNetConnect) then me.makeConnection() end if end on makeConnection (me) pSprite = sprite(me.spriteNum) pNetConnect = pSprite.newObject("NetConnection") pSprite.setCallback(pNetConnect,"onStatus",#myOnStatus,me) ServerAddress = "rtmp://localhost/VideoChat" pNetConnect.connect(ServerAddress) end on startStreams (me) CameraObj = pSprite.newObject("Camera") pCamera = CameraObject.get() MicObject = pSprite.newObject("Microphone") pMicrophone = MicObject.get() pOut = pSprite.newObject("NetStream",pNetConnect) pOut.attachAudio(pMicrophone) pOut.attachVideo(pCamera) pOut.publish(gNames.publishName,"live") pIn = pSprite.newObject("NetStream",pNetConnect) pIn.play(gNames.subscribeName) pVideoClip = pSprite.getVariable("VideoClip",FALSE) pVideoClip.attachVideo(pInStream) end on endSprite if not( voidP(pOutStream) ) then pOutStream.close() end if if not( voidP(pInStream) ) then pInStream.close() end if if not( voidP(pNetConnect) ) then pNetConnect.close() end if end on myOnStatus me, aArg1, aArg2 if (aArg2.code = "NetConnection.Connect.Success") then me.startStreams() end if end
That's a lot of code, but it does make sense if you dissect it.
The first section of code simply declares the global variables and the properties used. The global?gNames?is nothing more than the names of the participants who identified themselves in the first frame of the movie.
The properties are:
property pCamera: The camera object that provides the video stream from the user's computer.
property pIn: The incoming NetStream object.
property pMicrophone: The audio stream from the user's computer.
property pNetConnect: The NetConnection object that will handle the incoming and outgoing streams.
property pOut: The outgoing NetStream object.
property pSprite: The sprite number of the Flash movie on the Director stage.
If you are somewhat new to Lingo, you are probably wondering why property names have the letter "p" in front of them. This is a standard practice to distinguish the names from other variables used in the code to avoid confusion. This explains why global variables have a "g." Local variables simply use the name. Thus, "pIn" means a property. The variable "gNames" tells you it is a global variable, and "ServerAddress" is simply a local variable.
Obviously, the Director playback head is going to be held on the frame holding the Flash movie. This uses the following simple Lingo script:
On exitFrame Go to the frame End
This simple frame loop keeps the playback head moving in and out of the frame. Thus, the next bit of code…
on exitFrame (me) if voidP(pNetConnect) then me.makeConnection() end if end exitFrame
…uses the Lingo function voidP() to simply check if a NetConnection object?pNetConnect?exists. If it does not exist, it heads to the makeConnection handler.
With no NetConnection object found, one has to be made, which is the purpose of the makeConnection handler. The first thing it does is identify the sprite to which the behavior is attached?pSprite = sprite(me.spriteNum).
The remaining four lines are critical:
pNetConnect = pSprite.newObject("NetConnection")
Director first has to create an ActionScript reference to the "NetConnection" object. This ActionScript object will be given the property name pNetConnect.
When the connection has been identified, the Director Flash command setCallback() can be defined for the myOnStaus handler at the bottom of the script. The syntax for the command is setCallback(ActionScript Object, ActionScript Event Name,# Lingo handler, Lingo Script Object).
This line checks to see if the connection to the server was successful and then calls the startStreams handler.
ServerAddress = "rtmp://localhost/VideoChat"
When the callback handler is defined, this line puts the address of the server in a variable name, and the next line…
…makes the connection to the Flash Communication Server with the NetConnection object's connect() method, using the address as a parameter.
When the connection is made with the Flash Communication Server, it sends an onStatus message of success or failure with the attempt to connect with the server. The myOnStatus handler will capture this message.
Having found the connection, the Flash movie on the user's machine has to start communicating with the other Flash movie. This is the purpose of the startStreams handler. The remainder of the script creates the Flash Camera and Microphone objects and starts sending and receiving the audio and video streams.
on startStreams (me)
The next four lines…
CameraObj = pSprite.newObject("Camera") pCamera = CameraObject.get() MicObject = pSprite.newObject("Microphone") pMicrophone = MicObject.get()
…create the Camera and Microphone objects. The first line uses the NewObject() function to create an ActionScript Camera object and names it CameraObj or MicObject. The next line takes those two objects and uses their get() functions to return a reference to the camera or microphone and assigns that reference either the pCamera or pMicrophone property.
With the ActionScript objects created, the video stream has to be sent to the Flash Communication Server. This is the function of this line:
pOut = pSprite.newObject("NetStream",pNetConnect)
The first parameter, "NetStream", names the type of object to create. The second, "pNetConnect", is how Director tells the Flash sprite which NetConnection object to use to connect with the Flash Communication Server. By naming it pOut, you no longer have to be writing this line of code.
Having opened the connection out to the server, you can now determine what gets sent from Director to the Flash Communication Server, namely, the audio and video streams from the user's computer to the viewer's computer. To accomplish this, Director has to tell Flash where the streams are originating. The next two lines…
…undertake this task. The ActionScript attachAudio(source) command associates a microphone with the NetStream object, just as the attachVideo(source) command attaches a camera to the NetStream object.
With the camera and the microphone attached to the outgoing NetStream object, the audio and video streams flowing out of these devices are ready to be published. This line…
…using the Actionscript publish command?publish(What getsPublished)?accomplishes this task. The two parameters are rather simple to understand. The first one is the name of the sender. The second says the stream is being published live.
Having established the data flow from the user's computer to the Flash Communication Server, the next consideration is getting that data from the server to the viewer's computer. The first line…
pIn = pSprite.newObject("NetStream",pNetConnect)
…is exactly the same as that used to send the data to the Flash Communication Server. The next line…
…uses the Lingo command sprite (nameof FlashSpriteGoesHere).play() to associate the stream from the Flash Communication Server with the newObject and make it play on the viewer's screen. The parameter identifies the user sending the stream.
The last two lines…
pVideoClip = pSprite.getVariable("VideoClip",FALSE) pVideoClip.attachVideo(pInStream)
…create a reference to the video clip object within the Flash sprite and attach the incoming video stream to the video clip.
The first line is the important one. It uses the Lingo getVariable() function to get the value of the "VideoClip" variable from the Flash sprite. The actual Lingo syntax is sprite (FlashSpriteNumberGoesHere).getVariable ("variablename", returnValueorReference). The first parameter,"variablename", is understandable. The second is one of those "Say what?" things that are so common in computer coding.
There are only two values that can be used. They are "True" or "False." If the value is True, the default, the value of the Flash variable "VideoClip" would be a string (not a good thing in this instance). If it is set to False, as it is in this case, the data is returned as the current literal value?a video stream?of the Flash variable. In this case, the value of the Flash variable is an object. Thus, the rule is really simple. When the value is an object, the parameter must be set to False.
The endSprite handler closes the two NetStream Objects?Video and Audio?and the NetConnection object. The way this occurs is actually quite simple. The sprite is ended when the Disconnect button is clicked. When the button is clicked, the user is taken back to the login page. If the Flash Movie isn't in that frame of the Director movie, it obviously has ended the connection. The Lingo that makes this happen is:
if not( voidP(pOutStream) ) then pOutStream.close() end if if not( voidP(pInStream) ) then pInStream.close() end if if not( voidP(pNetConnect) ) then pNetConnect.close() end if
Note the use of Lingo's VoidP() function. All this does is determine if the properties in the brackets?pOutstream, pInStream, pNetConnect?have a value of either True of False. The not essentially asks if the variable value is False. If it is, the handler sends the ActionScript close() method to sever the connection between Director, the Flash Player, and the Flash Communication Server. The order is important here. Shut down the video and audio streams before you close the NetConnection object. They rely on the NetConnection object for their shut?down procedures.
The final handler constantly checks the status of the connection between the Flash movie in Director and the Flash Communication Server. This is determined by the setcallBack() statement from the on makeConnection handler.
on myOnStatus me, aArg1, aArg2 if (aArg2.code = "NetConnection.Connect.Success") then me.startStreams() end if end
…looks rather mysterious. However, it is quite succinct.
When a connection to the Flash Communication Server is made, the server sends two arguments to the callback handler. The second one is the important one because it contains a property list. This handler checks the second argument property list for a code string indicating that the connection was successful. If it is successful, the handler starts executing the startStreams handler.