Adobe
In this article, you will learn how to build a basic video switcher using Flash MX Professional 2004 and Flash Communication Server MX 1.5. This video switcher enables multiple people to broadcast video and audio to Flash Communication Server, and lets you control what the end user watches. You will learn how to use server-side ActionScript and Flash Communication Server messaging easily to control the source of the stream that your audience watches.
As a bonus, you will even incorporate switching between live and prerecorded video sources—all with nothing more than Flash Player, a camera, and some real personality.
In this article, you will create three Flash movies and one ActionScript communications file:
To help you get started, I created the user interfaces for this article. You can download the basic interface and the completed solution with some bonus material below. The basic interface contains all visual elements with the key ActionScript removed. To follow along with this tutorial, use the files in the fcsBroadcaster_start folder. The completed solution is located within the fcsBroadcaster folder. Both folders should be placed within the flashcom/applications folder on your server.
For this article, you will need the most recent versions and updaters of these Macromedia products:
This article makes significant use of ActionScript and server-side ActionScript. You should be comfortable with the ActionScript language. The user interface for this article is already complete, so you don't need to be a designer!
Before you start building the video switching application, let's first take a look at how the switch will happen and how you can deliver the best possible experience to your users.
There are two ways to make a video switch with Flash Communication Server:
There are three key problems with the messaging option:
This article focuses on using server-side ActionScript to produce the best possible experience for receiving live video with absolute synchronization, no rebuffering, and optimal Flash Communication Server performance.
All Flash Player clients will subscribe to a single "program" stream. Server-side ActionScript will perform the switch on the server. Sustaining the playback stream on the Flash Player means it will not need to rebuffer and everyone will see the switch at the proper time in the video. This solution also produces less CPU load on the server.
There are three applications that make up this solution:
I explain each part of the application separately so you can understand how each interface operates and how server-side ActionScript interacts with each client. Let's start with the Broadcaster application.
File: simpleBroadcaster.fla
The Broadcaster application is used to publish input from a webcam or other video source to Flash Communication Server. This section of the article shows you how to build the functionality required to publish a stream. It also demonstrates how to use a slider component to set the Camera and Microphone device properties both locally and remotely. First, let's review the user interface for the Broadcaster application (see Figure 1).
Figure 1. The finished Live Broadcaster application
To build this application following the steps in this article, start with the prebuilt files in the fcsBroadcast_start folder. This folder is located in the ZIP package you downloaded. This folder contains three prebuilt interfaces and a server-side ActionScript file.
In this section of the article I show you how to calculate the bandwidth you need automatically, and give your users a very simple interface to control their video feed.
First, create an instance of the NetConnection class and then connect the Flash client to your Flash Communication Server using the connect method. Place this ActionScript (and all ActionScript in this article) on Frame 1 of the Actions layer:
nc = new NetConnection();
nc.onStatus= ncStatus;
nc.connect("rtmp://localhost/fcsBroadcast_start", "BROADCASTER");
The connect method sends the string "BROADCASTER" as the second parameter. This triggers special processing in server-side ActionScript and sets the connection as a Broadcast client. Each of the three applications identify themselves as either BROADCASTER, PLAYER or SWITCHER.
The NetConnection.onStatus handler (ncStatus) performs three operations:
I talk more about the broadcaster shared object in Broadcaster Step 4. The shared object monitors changes to the broadcast stream—for example, if the switcher makes the stream live or remotely sets the Camera or Microphone properties.
The NetConnection call to getClientObj returns the Client object (information about the connection and the stream) from Flash Communication Server to Flash Player. The returned Client object contains an important property called streamName that is created for broadcasters when they connect. Figure 2 shows the full Client object structure:
function ncStatus(nsObj) {
switch(nsObj.code) {
case "NetConnection.Connect.Success":
// initialize the local Capture Devices
initDevices();
// initialize the SharedObject
doInitSO();
// Call Flash Communication Server to return the Client Object
this.call("getClientObj", new onFCSData());
break;
}
}
The onResult handler receives the Client object in the object called onFCSData. The handler for the server call to onFCSData performs the following two operations:
Here is the onFCSData function:
function onFCSData() {
// Handle the call back from the server
this.onResult = function(clientObj) {
//1) Copy the returned Client Object into the _global scope
_global.clientObj = clientObj;
//2) Initialize the broadcaster streams
initStreams(clientObj.streamName);
}
}
Figure 2. The Client object stored in the _global scope of Flash Player. The four new properties—streamName, broadcasterName, status, and userID—are created in server-side ActionScript when the Broadcaster connects.
Note: I have not included any connection error handling for this article but you should consider monitoring other information objects from the NetConnection onStatus event specifically for connection failures.
The initDevices() function performs two operations:
Here is the initDevices() function:
function initDevices() {
// Reference the devices
source_cam = Camera.get();
source_mic = Microphone.get();
source_mic.setRate(11);
// Set the initial properties of the devices
//(we'll build these two functions in step 5)
doSetCamera();
doSetMicLevel();
// attach the camera to the video UI Object
local_video.attachVideo(source_cam);
The NetConnection.onStatus handler calls the initDevices() function once the connection has been established. The capture devices are set up using reusable functions that you will set up in Step 5.
The initStreams() function performs three operations:
This function is called in the onFCSData handler after the server has successfully returned the server-side ActionScript Client object back to Flash Player.
Here is the initStream() function:
function initStreams(streamName) {
// instance the NetStream object
out_ns = new NetStream(nc);
// attach the capture devices
out_ns.attachAudio(source_mic);
out_ns.attachVideo(source_cam);
// start publishing the stream
out_ns.publish(streamName, "live");
}
The App Inspector (Streams tab) shows the broadcaster's stream publishing to the server (see Figure 3). A nice extension to this solution would be to make the broadcaster stream publish only to the server when requested by the switcher. This technique would manage the stream more efficiently on the server but would require some additional functionality to be built.
Figure 3. A broadcaster stream (stream_61) shown in the App Inspector
The doInitSO() function connects Flash to the remote shared object called "broadcaster." It also assigns the event handlers used later for synchronization and remote control of the Microphone and Camera:
function doInitSO() {
broadcaster_so = SharedObject.getRemote("broadcaster",nc.uri,false);
broadcaster_so.onSync = syncBroadcaster;
// Custom Event handlers (listeners) to remotely manage the capture devices
broadcaster_so.onCameraSet = onCameraSet;
broadcaster_so.onMicSet = onMicSet;
// Connect the SharedObject to the Server
broadcaster_so.connect(nc);
}
The syncBroadcaster() function (below) is assigned as the onSync event handler (above). The function is called each time a change is made to (or by) any broadcaster sending video to the server. This handler monitors only the changes to the slot associated with its current user ID. It has three operations:
Here is the syncBroadcaster() function:
function syncBroadcaster(syncObj) {
//1) Copy the Client Object
var mySlot = this.data[_global.clientObj.userID];
//2) Change the background colour of the camera
cameraBG_mc.gotoAndStop(mySlot.status);
//3) Sets a UI Status message informing the user that the camera is live
statusMsg_txt.text = mySlot.status;
}
These final two functions, onCameraSet and onMicSet, were assigned earlier in this step as event handlers. They respond to changes made remotely by the switcher to the Camera or Microphone properties. They each receive two arguments: targetUserID and newValue. The targetUserID argument contains the user ID of the broadcaster being changed. The newValue argument contains the numerical position of the slider component. By setting the value, the component automatically changes and the component's change handler is called. You will develop the change handler and the device settings in Step 5.
Both functions are assigned to handlers in the initSO() function. They listen for messages sent over the shared object::
function onCameraSet(targetUserID, newValue) {
if (targetUserID == clientObj.userID)
camQuality_slide.value = newValue;
}
function onMicSet(targetUserID, newValue) {
if (targetUserID == clientObj.userID)
micLevel_slide.value = newValue;
}
The Camera Quality properties are set by the user interface slider component (included in the sample files). The slider allows users easily to change their quality settings. When called, this function has three operations:
Here is the doSetCamera() function:
function doSetCamera() {
var camSet = new Object();
switch(camQuality_slide.value) {
case 1:
camSet.w = 80;
camSet.h = 60;
camSet.fps = 8;
camSet.quality = 75;
break;
case 2:
camSet.w = 192;
camSet.h = 144;
camSet.fps = 7;
camSet.quality = 80;
break;
case 3:
camSet.w = 320;
camSet.h = 240;
camSet.fps = 15;
camSet.quality = 90;
break;
}
// calculated KeyFrame and Bandwidth
camSet.kfi = camSet.fps * 4;
camSet.bw = (camSet.w * camSet.h * camSet.fps) / 8;
// Set the Camera
source_cam.setMode(camSet.w,camSet.h,camSet.fps,false);
source_cam.setQuality(camSet.bw,camSet.quality);
source_cam.setKeyFrameInterval(camSet.kfi);
}
Finally, set the event handler for the "change" event of the Camera slider component (camQuality_slide) to the doSetCamera function. The change handler will be called if the user adjusts the slider or if the switcher changes the value remotely (as I mentioned in Step 4):
camQuality_slide.changeHandler = doSetCamera;
To illustrate further what is being set, Figure 4 shows the three settings as they relate to each case.
Figure 4. Bandwidth targets used in this solution: 38 Kbps dial-up (left); 194 Kbps DSL (middle); 1.2 Mbps LAN (right)
The Microphone volume is set by the change handler of the volume slider. It feeds the slider component's value property (0 to 100) directly into the Microphone.setGain() method:
function doSetMicLevel() {
source_mic.setGain(micLevel_slide.value);
micLevel_txt.text = source_mic.gain;
}
Now assign the doSetMicLevel function to the change event handler for the Microphone volume slider component:
micLevel_slide.changeHandler = doSetMicLevel;
The preview mode uses the loopback property of the Camera object to show users how other people see their streams. When set to true the display shows the stream filtered through the encoder. A false setting lets the broadcaster see the raw camera feed:
function previewMode() {
var isLoopBack = previewMode_cb.selectedItem.data;
source_cam.setLoopback(isLoopBack);
}
Assign this function to the change event handler for the ComboBox component:
previewMode_cb.changeHandler = previewMode;
Each broadcaster can set its name by filling in a text area. The updateSystem() function sets the broadcaster's name by sending the text property of the textArea component to the server by calling the updateBroadcaster Client method. I discuss the updateBroadcaster function later in the section on server-side ActionScript.
The deltaObject object (which stores changes) is loaded with properties that have changed. When received by the server, it resets the clientObject and the SharedObject slots with the changed information. The implementation below is a good starting point for addressing multiple property changes. However, for this article, I've only included a single property, broadcasterName:
function updateSystem() {
var deltaObj = new Object();
deltaObj.broadcasterName = broadcasterName_txt.text;
nc.call("updateBroadcaster", new onFCSUpdate, deltaObj);
}
This NetConnection call to updateBroadcaster returns the updated Client object back to Flash Player. It is handled by the onFCSUpdate() function, which updates the Client object in the _global scope:
function onFCSUpdate() {
this.onResult = function(clientObj) {
_global.clientObj = clientObj;u
}
}
setName_btn.clickHandler = broadcasterName_txt.onKillFocus = updateSystem;
File: simplePlayer.fla
The Live Video Player application plays a live video stream from Flash Communication Server. The interface (see Figure 5) includes only what is required to display the video and some additional information:
Figure 5. The Live Video Player application
All that you need to play a live video stream from Flash Communication Server is an embedded video object and some ActionScript. The Media Display and Media Controller media components are intended only for prerecorded video (both streaming and progressive download); they will not work with live video solutions.
Set up the connection between Flash Player and Flash Communication Server. As you did with the Broadcaster application, place this ActionScript on Frame 1 of the Actions layer.
Also similar to the Broadcaster application, the NetConnection.connect method receives a parameter identifying this connection request as PLAYER:
nc = new NetConnection();
nc.onStatus= ncStatus;
nc.connect("rtmp://localhost/fcsBroadcast_start/", "PLAYER");
Handle the NetConnection.onStatus event using the ncStatus function. Once the connection has been accepted, call the doInitStreams() function to set up the incoming stream and the doInitSO function to start listening to the "public" shared object:
function ncStatus(nsObj) {
switch(nsObj.code) {
// If the connection is successful, start the stream
case "NetConnection.Connect.Success":
// start the stream
doInitStreams();
// initialize the SharedObject (Public)
doInitSO();
break;
}
}
The doInitStreams() function will create an instance of the NetStream class attached to the NetConnection. It is called only after a connection request has been successful. This function performs four operations:
Here is the doInitStreams() function:
function doInitStreams() {
// 1) instance the NetStream Class and set the buffer
program_ns = new NetStream(nc);
program_ns.setBufferTime(2);
// 2) attach the streams to the Objects
live_video.attachVideo(program_ns);
live_sound = new Sound();
live_sound.attachSound(program_ns);
// set the default volume level
vol_slide.value=70;
// 3) set up an interval to monitor the buffer / and time
playTimer_int = setInterval(monPlayback, 250);
// 4) Subscribe to the stream "programStream"
program_ns.play("programStream",-1,-1,true);
}
When a single video player connects to Flash Communication Server, you will see this activity in the Streams panel (Figure 6) of the App Inspector.
Figure 6. The App Inspector showing a single player connected, playing the programStream stream. No broadcasters are sending video at this point.
Note: The player is "playing live" the programStream stream. As you will see later, Flash Communication Server is publishing an empty stream, called programStream, using the server-side ActionScript Stream object. This is the key to the system.
The monPlayback() function monitors the buffer and time properties of the NetStream object. It is called by an interval created when the stream starts. It calculates the buffer size and writes its value as a percentage to the buffer_txt text field. The stream time is sent to the playTime_txt text field (see Figure 7):
function monPlayback() {
// Calculate the buffer percentage
currentBuffer = (program_ns.bufferLength/program_ns.bufferTime)*100;
if (currentBuffer>100) currentBuffer = 100;
// Write the values to the Interface
buffer_txt.text = "BUFFER: " + Math.round(currentBuffer)+"%";
playTime_txt.text = program_ns.time;
}
Figure 7. Examples of dynamic text fields in the video player
A single shared object is used to monitor information about the current live stream. The player only monitors broadcasterName but you can use this to build additional monitoring and communication for the players:
function doInitSO() {
public_so = SharedObject.getRemote("public",nc.uri,false);
public_so.onSync = syncPublic;
public_so.connect(nc);
}
function syncPublic(syncObj) {
broadcasterName_txt.text = this.data.currentStream.broadcasterName;
}
The volume slider controls the gain property of the sound object. The live_sound object manages the stream audio. This onVolChange() function is assigned to the change handler for the vol_slide slider component:
function onVolChange() {
live_sound.setVolume(vol_slide.value);
volLevel_txt.text = live_sound.getVolume();
}
vol_slide.changeHandler = onVolChange;
That's it for the Live Video Player application. Now let's build the Video Switcher application.
File: simpleSwitcher.fla
The Video Switcher application is the key to this whole solution (see Figure 8). Its interface operates similarly to a television control room (except with a better-looking interface and less radiation).
Figure 8. The Video Switcher application interface
The principal interface for the Video Switcher application consists of a control panel and two video panels:
When you click the Cut button, the "switch" happens: The live window becomes the preview and the preview becomes the live. This technique makes it easier to switch between the two sources. The director can then select another live or prerecorded source to preview and then cut to the live stream.
In the case of prerecorded video, the preview window can be used to preview a prerecorded (FLV) video before sending it to the live stream.
Let's examine the key ActionScript used in this application. As with the other two solutions, the ActionScript is located in Frame 1 of the Actions layer.
As you did with the two other applications, identify the connection as SWITCHER in the connect method after you set up the connection:
nc = new NetConnection(); nc.onStatus= ncStatus; nc.connect(connString, "SWITCHER");
The success handler for the switcher performs two operations:
Here is the ncStatus() function:
function ncStatus(nsObj) {
switch(nsObj.code) {
case "NetConnection.Connect.Success":
// 1) initialize 2 Streams
doInitStreams();
// 2) Initialize the SharedObject
doInitSO();
break;
}
}
The doInitStreams() function sets up two incoming streams:
The function performs five operations:
Here is the doInitStreams() function:
function doInitStreams() {
// 1) Instance the NetStream Objects
program_ns = new NetStream(nc);
preview_ns = new NetStream(nc);
// 2) Set the incoming buffer (2 seconds)
program_ns.setBufferTime(2);
preview_ns.setBufferTime(2);
// 3) attach the Video to the embedded video objects
prog_video.attachVideo(program_ns);
prev_video.attachVideo(preview_ns);
// 4) play the program stream and a null stream
program_ns.play("programStream",-1,-1,true);
preview_ns.play("null",-1,-1,true);
// 5) startup the streams monitor (interval 250ms)
setInterval(monStreams,250);
}
All SharedObjects are managed by server-side ActionScript. There are two shared objects used for the application:
Here is the doInitSO() function:
function doInitSO() {
broadcaster_so = SharedObject.getRemote("broadcaster",nc.uri,false);
broadcaster_so.onSync = syncBroadcaster;
broadcaster_so.connect(nc);
public_so = SharedObject.getRemote("public",nc.uri,false);
public_so.onSync = syncPublic;
public_so.connect(nc);
}
The synchronization handlers for each shared object are built in Step 4.
The SharedObject handlers monitor changes to the shared objects. In this example, we're only using it as a trigger when the shared object changes. Normally, you would loop through the information object and handle each change.
Flash Communication Server is the only part of the application that writes to either of the shared objects, so we can keep the onSync handlers simple.
The first handler (syncBroadcaster) is responsible for populating the DataGrid component that contains a list of all broadcast clients currently streaming to the server. It performs three operations:
Here is the syncBroadcaster() function:
function syncBroadcaster(syncObj) {
// initialize temporary variables (syncObj is not used)
var soData = broadcaster_so.data;
var gridObj;
var currentPublishers = new Array();
// Loop through the soData and copy only the columns to display in the DG
for (i in soData) {
gridObj = new Object();
gridObj.userID = soData[i].userID;
gridObj.streamName = soData[i].streamName;
gridObj.broadcasterName = soData[i].broadcasterName;
// push the new object to the array used for the Datagrid
currentPublishers.push(gridObj);
}
// set the datagrid's dataprovider to the array of objects
broadcasterList_dg.dataProvider = currentPublishers;
}
The second handler (syncPublic) monitors the public shared object and updates the name of the broadcaster in the user interface:
function syncPublic(syncObj) {
// display the broadcaster name for the live (program) stream
prog_BroadcasterName_txt.text = this.data.currentStream.broadcasterName;
}
This function subscribes the NetStream object preview_ns to the selected stream in the DataGrid component. First it retrieves the selectedItem property of the data grid and stores it in the _global.stream_prev variable. Next it sets the stream property to the selected stream name. Finally, using the NetStream.play() method, it subscribes to the stream. Note the absence of the start and duration parameters in the play() method. This allows you ultimately to use this application to stream both live and prerecorded FLV files using the same system!
Here is the doLoadPreview() function:
function doLoadPreview() {
_global.stream.preview = broadcasterList_dg.selectedItem;
prev_BroadcasterName_txt.text = stream.preview.broadcasterName;
preview_ns.play(stream.preview.streamName);
}
Finally, assign this function to the data grid's change event handler:
broadcasterList_dg.changeHandler = doLoadPreview;
This last function switches the preview and play streams. It has three operations:
Here is the doSwitchVideo() function:
function doSwitchVideo() {
// Save the program and preview stream data into the _global scope
_global.stream.program = _global.stream.preview;
_global.stream.preview = public_so.data.currentStream;
// play the current live stream in the preview window
preview_ns.play(stream.preview.streamName);
prev_BroadcasterName_txt.text = stream.preview.broadcasterName;
// send the User ID to the server for switching
nc.call("switchStream",null,stream.program.userID);
}
These next three functions let the switcher remotely control the Camera and Microphone properties of the broadcaster clients. The first two functions are assigned to the slider components for both the program and preview streams. Each function determines which component called it and sets the targetUserID parameter.
Using the broadcaster shared object, they send a message with the user ID and the new value to set. You developed the onCameraSet() and onMicSet() handlers earlier in this article:
function setCamera() {
var targetUserID = stream.preview.userID;
if (this._name == "prog_camQuality_slide") targetUserID = stream.program.userID;
broadcaster_so.send("onCameraSet",targetUserID, this.value)
}
prev_camQuality_slide.changeHandler = setCamera;
prog_camQuality_slide.changeHandler = setCamera;
function setMicrophone() {
var targetUserID = stream.preview.userID;
if (this._name == "prog_micLevel_slide") targetUserID = stream.program.userID;
broadcaster_so.send("onMicSet",targetUserID, this.value);
}
prog_micLevel_slide.changeHandler = setMicrophone;
prev_micLevel_slide.changeHandler = setMicrophone;
The onMute() function is used by both mute buttons on the interface to enable/disable the audio stream. Although this method works fine, you might consider using a sound object and the setGain() method as you saw earlier when you built the Live Video Player application:
function onMute() {
var targetStream = "program_ns";
if (this._name == "mutePrev_ch") targetStream = "preview_ns";
this._parent[targetStream].receiveAudio(this.value);
}
mutePrev_ch.clickHandler = onMute;
muteProg_ch.clickHandler = onMute;
That's it for all three Flash interfaces. Now, let's build the final piece, the server-side ActionScript.
File: fcsBroadcast.asc
The server-side ActionScript stitches everything together. It is the key to stream management and managing the connections. We use three key events on the server to manage users connecting and disconnecting:
We also extend the Client prototype with three additional methods:
Two additional functions are used by these new client methods and the application event handlers. I cover them in the following steps.
This first event handler is called the first time the application is instanced (usually at the first connection request). It performs three key functions:
Here is the onAppStart function:
application.onAppStart = function() {
// set initial Values
application.currentStreamID = undefined;
userID = 0;
// setup the BROADCAST SharedObject
broadcaster_so = SharedObject.get("broadcaster",false);
// setup the PUBLIC SharedObject
public_so = SharedObject.get("public",false);
public_so.setProperty("currentStream", ({broadcaster: "No Broadcaster"}));
// start the program stream all viewers will play
prog_stream = Stream.get("programStream");
prog_stream.play("null");
}
When you play a stream from Flash Communication Server, you are in fact publishing a stream to the server (see Figure 9). The stream name set as the first parameter is how you ultimately connect to other streams being published to the server.
Figure 9. The App Inspector showing Flash Communication Server publishing the programStream stream with no players or broadcasters connected.
This second event handler is called each time an instance of Flash Player requests a connection to the server. This is where any authentication or user counter functions would go. For this article, the onConnect function will just do the basics:
The Client object is modified only for broadcasters. In the next step, you'll see the definition for the initBroadcasters() function.
Here is the onConnect() function:
application.onConnect = function(clientObj, userType) {
// 1) increment the userID counter
userID++;
// 2) Setup the Broadcaster (if userType is correct)
if(userType == "BROADCASTER")
clientObj = initBroadcaster(clientObj);
// 3) Accept All Connections
application.acceptConnection(clientObj);
}
This function sets three additional properties inside the Client object only for BROADCASTER connections. It performs three tasks:
Here is the initBroadcasters() function:
initBroadcaster = function(clientObj) {
// Set new properties in the Client Object
clientObj.streamName = "stream_" + userID;
clientObj.broadcasterName = "Broadcaster #" + userID;
clientObj.userID = userID;
clientObj.status = "READY"
// Add the client to the broadcasters SharedObject
broadcaster_so.setProperty(userID, clientObj);
// Return the Client Object
return clientObj;
}
The switchStream function sets the programStream source to the required stream (see Figure 10). This function is called by the switcher application and requires the userID argument to be passed. It uses the user ID to retrieve the streamName value and the Client object from the broadcasters shared object.
The application.currentStreamID variable stores the user ID of the broadcaster currently set to the program feed. There are two occasions when application.currentStreamID is undefined: if there is no previous live stream or if the current broadcaster suddenly disconnects. The if statement within this method skips the resetting routines in case there is no current live broadcaster:
Client.prototype.switchStream = function(userID) {
var oldVal = new Object();
var currentStream;
// get the PREVIOUS Stream Object
if (application.currentStreamID != undefined){
oldVal = broadcaster_so.getProperty(application.currentStreamID);
oldVal.status = "READY";
broadcaster_so.setProperty(oldVal.userID, oldVal);
}
// get the NEW Stream Object
currentStream = broadcaster_so.getProperty(userID);
currentStream.status = "LIVE";
// update the slots
broadcaster_so.setProperty(userID, currentStream);
public_so.setProperty("currentStream", currentStream);
// set the currentStreamID to the new Stream ID
application.currentStreamID = userID;
// connect the new stream with the programStream
prog_stream.play(currentStream.streamName);
}
Figure 10. The App Inspector showing two broadcasters publishing, one player, one switcher with one live stream, and one preview stream.
This function returns the Client object to the Broadcaster application. The Client object contains the stream name and user ID, which are required by the broadcaster to start publishing a video to the server:
Client.prototype.getClientObj = function() {
return this;
}
As I mentioned earlier, we made the decision to let only server-side ActionScript perform any changes to shared objects. This Client method is called by a broadcaster client when a change is made to the information. It requires the deltaObj parameter containing Client properties that have changed. This method has four operations:
Here is the Client method:
Client.prototype.updateBroadcaster = function(deltaObj) {
// 1) set the changed properties in the clientObject
for (i in deltaObj) {
this[i] = deltaObj[i];
}
// 2)_update the Broadcaster SharedObject
broadcaster_so.setProperty(this.userID, this);
// 3) update the Public SharedObject
if (application.currentStreamID == this.userID) {
public_so.setProperty("currentStream", this);
}
// 4) return the updated Client Object back to the Flash player
return this;
}
Typically when any user disconnects, you should do garbage collection (cleaning up variables). This is where strategically using the Client object comes in real handy. Each time a Flash client disconnects from Flash Communication Server, the onDisconnect event handler is called. You can use the Client object (automatically passed as a parameter) to get the user ID (the Slot name) in the shared object and delete it. It's important to flush the shared object after you have deleted a slot to destroy it completely.
This handler also resets application.currentStreamID, should the disconnected user be the one currently broadcasting on the live program stream:
application.onDisconnect = function(clientObj) {
broadcaster_so.setProperty(clientObj.userID, undefined);
broadcaster_so.flush();
if(application.currentStreamID == clientObj.userID) {
application.currentStreamID = undefined;
public_so.setProperty("currentStream", ({broadcasterName: "Please Stand By..."}));
}
}
That's it for the server-side ActionScript and fcsBroadcast.asc file. At this point you have everything you need to build your first Internet television station using Flash. Inside the sample code, I've added some functionality not mentioned in this article that will help you complete this solution.
As an alternative to building your own live video encoding solution with Flash Communication Server would be to use Flash Video Streaming Service, a hosted service for streaming on-demand and live Flash Video from a reliable content delivery network partner.
VitalStream provides a live streaming service and a live encoder application as a part of its implementation of Flash Video Streaming Service (see Figure 11). You can use it to stream video to as many people as you want.
Figure 11. Flash Video Streaming Service powered by VitalStream
VitalStream provides you with the encoder and live player. Their HTML embed code makes it simple to get your live streaming project up and going quickly. For more information, read Tim Napoleon's article, Delivering High-Quality Video with Flash Video Streaming Service.
Read more about Flash Video Streaming Service ›
As more and more people push Flash Video to the next level, solutions like the one I've presented in this article function as building blocks for creating powerful live video solutions. Audiences just need Flash Player 6, broadcasters just need a little onscreen personality, and you've got a winning project. For additional features and functionality, refer to the extra Actions layer in the completed sample files you downloaded at the beginning of this article.
If you are interested in more live video switching solutions, check out my session Streaming Live Video with Flash at Macromedia MAX 2004 conference in New Orleans.
Good luck with your live video project, Mr. Director!