More Books
Flash Communication Server
Flash Communication Server
Table of Contents
Copyright
About the Authors
Brian Lesser
Giacomo
Joey Lott
Robert Reinhardt
Justin Watkins
Foreword
Preface
What Does FlashCom Offer?
What's in This Book?
How to Use This Book
Audience
ActionScript 1.0 Versus ActionScript 2.0
Server-Side ActionScript
The flash-communications.net Site
Director, Breeze, and Other Options
Flash Video Options
Licensing and Hosting Options
Conventions Used in This Book
Voice
Using Code Examples
Safari Enabled
Comments and Questions
Acknowledgments
Part I:  FlashCom Foundation
Chapter 1.  Introducing the Flash Communication Server
Section 1.1.  Clients and Servers
Section 1.2.  Creating an Application
Section 1.3.  Real-Time Messaging Protocol
Section 1.4.  The Communication Classes
Section 1.5.  Communicating with Application Servers, Databases, and Directory Servers
Section 1.6.  Firewalls and Security
Section 1.7.  Getting Started
Section 1.8.  Hello Video!
Section 1.9.  Conclusion
Chapter 2.  Communication Components
Section 2.1.  Overview of Communication Components
Section 2.2.  Summary of Communication Components
Section 2.3.  Creating an Application that Monitorsa Connection
Section 2.4.  Building a Simple Chat Room
Section 2.5.  Adding Audio and Video to the Chat Room
Section 2.6.  Forgoing the SimpleConnect Component
Section 2.7.  Conclusion
Chapter 3.  Managing Connections
Section 3.1.  Making a Connection
Section 3.2.  Managing a Connection
Section 3.3.  Reusing a NetConnection Object
Section 3.4.  Multiple Simultaneous NetConnection Objects
Section 3.5.  Testing and Debugging Network Connections
Section 3.6.  Subclassing the NetConnection Class
Section 3.7.  Communication Components Without SimpleConnect
Section 3.8.  Conclusion
Chapter 4.  Applications, Instances, and Server-Side ActionScript
Section 4.1.  Scripting Application Instances
Section 4.2.  Differences Between Flash ActionScript and Server-Side ActionScript
Section 4.3.  The Life of an Application Instance
Section 4.4.  Running a Simple Hello World Test Script
Section 4.5.  A More Realistic Example
Section 4.6.  Instance-to-Instance Communications
Section 4.7.  Script Filenames and Locations in Detail
Section 4.8.  Testing and Debugging Server-SideScript Files
Section 4.9.  Designing Communication Applications
Section 4.10.  Conclusion
Part II:  Audio, Video, and Data Streams
Chapter 5.  Managing Streams
Section 5.1.  A Simple Publisher/Subscriber Example
Section 5.2.  Stream Names
Section 5.3.  Publishing Streams in Detail
Section 5.4.  Playing Streams in Detail
Section 5.5.  The Stream Class
Section 5.6.  Publishing and Playing ActionScript Data
Section 5.7.  Creating Synchronized Presentations
Section 5.8.  The NetStream and Stream Information Objects
Section 5.9.  Stream Enhancements and Limitations
Section 5.10.  Conclusion
Chapter 6.  Microphone and Camera
Section 6.1.  Working with Microphone/Audio Input
Section 6.2.  Working with Camera Input
Section 6.3.  Building a Message-Taking Application
Section 6.4.  Building a Surveillance Application
Section 6.5.  Conclusion
Chapter 7.  Media Preparation and Delivery
Section 7.1.  Audio and Video Compression
Section 7.2.  Converting Prerecorded Materialto FLV Format
Section 7.3.  Using Flash Pro's Media Components
Section 7.4.  Enabling Multiple Bit Rate FLVsWithin an Application
Section 7.5.  Streaming MP3 Audio
Section 7.6.  Conclusion
Part III:  Remote Connectivity and Communication
Chapter 8.  Shared Objects
Section 8.1.  Objects and Shared Objects
Section 8.2.  Getting a Shared Object in Flash
Section 8.3.  Updates and Frame Rates
Section 8.4.  Scripting Shared Objects on the Server
Section 8.5.  Temporary and Persistent Shared Objects
Section 8.6.  Proxied Shared Objects
Section 8.7.  Shared Objects and Custom Classes
Section 8.8.  Avoiding Collisions
Section 8.9.  Optimizing Shared Object Performance
Section 8.10.  Broadcasting Remote Method Callswith send( )
Section 8.11.  A Simple Video and Text Chat Application
Section 8.12.  Conclusion
Chapter 9.  Remote Methods
Section 9.1.  Why Use Calls?
Section 9.2.  The send( ) and call( ) Methods
Section 9.3.  Client-to-Server Calls
Section 9.4.  Server-to-Client Calls
Section 9.5.  Server-to-Server Calls
Section 9.6.  A Simple Lobby/Rooms Application
Section 9.7.  Debugging Calls
Section 9.8.  Advanced Topics
Section 9.9.  Conclusion
Chapter 10.  Server Management API
Section 10.1.  Connecting to the Admin Service
Section 10.2.  Using the Server Management API
Section 10.3.  Server Management API Uses
Section 10.4.  Conclusion
Chapter 11.  Flash Remoting
Section 11.1.  The Remoting Gateway
Section 11.2.  Remoting Basics
Section 11.3.  Role of Remoting in FlashCom Applications
Section 11.4.  Securing Access
Section 11.5.  Conclusion
Chapter 12.  ColdFusion MX and FlashCom
Section 12.1.  Understanding ColdFusion MXand Flash Remoting
Section 12.2.  Using Flash Remoting to Log Events
Section 12.3.  Getting a List of Streams
Section 12.4.  Using ColdFusion and FTP to Mirror Streams
Section 12.5.  Conclusion
Part IV:  Design and Deployment
Chapter 13.  Building Communication Components
Section 13.1.  Source Files
Section 13.2.  People Lists
Section 13.3.  A Simple People List
Section 13.4.  Listenable Shared Objects
Section 13.5.  Status and People List
Section 13.6.  Text Chat
Section 13.7.  Shared Text
Section 13.8.  Video Conference and Video Window
Section 13.9.  PeopleGrid
Section 13.10.  Summary
Section 13.11.  Conclusion
Chapter 14.  Understanding the Macromedia Component Framework
Section 14.1.  The Component Framework
Section 14.2.  Under the Hood of the Chat Component
Section 14.3.  Creating a Simple Component from Scratch: SharedTextInput
Section 14.4.  Creating a Container Component: SharedAddressForm
Section 14.5.  Creating an Authenticating Component
Section 14.6.  Integrating Components with Your Existing Applications
Section 14.7.  Understanding the Framework
Section 14.8.  Conclusion
Chapter 15.  Application Design Patterns and Best Practices
Section 15.1.  Shared Object Management
Section 15.2.  Moving Code to the Server
Section 15.3.  Building Façades on the Server
Section 15.4.  Server-Side Client Queues
Section 15.5.  A Framework for Recording and Playing Back Componentized Applications
Section 15.6.  Components and Component Frameworks
Section 15.7.  Conclusion
Chapter 16.  Building Scalable Applications
Section 16.1.  Coordinating Instances
Section 16.2.  Scalability and Load Balancing
Section 16.3.  Conclusion
Chapter 17.  Network Performance, Latency,and Concurrency
Section 17.1.  Latency
Section 17.2.  Bandwidth
Section 17.3.  Concurrency
Section 17.4.  Conclusion
Chapter 18.  Securing Applications
Section 18.1.  The Three A's: Authentication, Authorization, and Accounting
Section 18.2.  Authentication
Section 18.3.  Authorization
Section 18.4.  Accounting
Section 18.5.  Suggestions and References
Section 18.6.  Conclusion
Index
SYMBOL
A
B
C
D
E
F
G
H
I
J
K
L
M
N
O
P
R
S
T
U
V
W

5.5. The Stream Class

The server-side Stream class is designed to provide a proxy and control mechanism for live and recorded streams. It is unlike the client-side NetStream class in that it cannot create a new and original audio/video stream because it cannot be used to attach video or audio data sources to a stream. Instead, the server-side Stream object can be used to create new streams that incorporate already existing live and recorded streams, manage existing streams, or create new data-only streams. It can be used to:

  • Create a single stream on the server that sequentially plays a list of other streams. Flash movies have to subscribe to only the one server-side-generated stream to receive the contents of a sequence of streams played from the server.

  • Create a single stream that acts as a switcher between other streams. Flash movies subscribe to the one stream while the source of the stream is switched on the server. The effect is similar to a video switcher used in a television studio to control which camera signal is broadcast to the world. The stream can also be recorded, creating a new edited stream.

  • Chain streams between application instances on one server or between many servers. Chaining streams between servers is one way to provide the capacity to serve a very large number of subscribers to a single live stream.

  • Control access to individual streams. The Stream class can be used to provide very fine-grained access control for each user.

  • Record ActionScript data streams that can contain very large amounts of data, for example, a log file.

5.5.1. Getting a Recorded Stream's Length

The Stream class has one static method, length( ), which, before the release of FLV 1.1 files, was the only way to directly determine the length of a recorded stream from the stream file itself. See Chapter 7 for more information on determining the stream length from FLV 1.1 files. To determine the length of a recorded stream, call length( ) as follows:

len = Stream.length("lectures/vectors03");
trace("The stream is " + len + " seconds long");

If the stream is not found, length( ) returns 0. It is often useful to provide a function that will calculate the length of a number of streams, as shown in Example 5-9.

Example 5-9. Getting the length of a list of streams (server-side script)
function sumStreamLengths (  ) {
  var sum = 0;
  var i = arguments.length;
  while (i--) {
    sum += Stream.length(arguments[i]);
  }
  return sum;
}

The sumStreamLengths( ) function in Example 5-9 can be used to retrieve the length of more than one stream (as in a playlist) this way:

len = sumStreamLengths("lectures/vector01", "lectures/vector02");

The sumStreamLengths( ) function can also be called to get the length of only one stream:

len = sumStreamLengths("public/recordedStream");

The simplest way to access the Stream.length( ) method from a Flash movie requires calling a method on the server, which in turn calls Stream.length( ) and returns the result to the client. Example 5-10 shows a segment of a client-side script that requests the length of a single recorded stream using a remote method call. Note the use of NetConnection.call( ) to invoke a server-side script from the client.

Example 5-10. Retrieving the length of a stream from the server using a client-side script
function doGetStreamLength (  ) {
  nc.call("getStreamLength", new ResultObject( ), streamName_txt.text);
}

function ResultObject ( ) {
}

ResultObject.prototype.onResult = function (len) {
  trace( "The stream length is: " + len);
};

The doGetStreamLength( ) function is called after the user types a stream URI into the streamName_txt field and clicks a button. Within the doGetStreamLength( ) function, NetConnection.call( ) calls a remote method named getStreamLength( ) on the server. Example 5-11 shows the server-side script and how the Client.getStreamLength( ) method is defined on the server that responds to the request.

Example 5-11. Defining the client.getStreamLength( ) method on the server
application.onConnect = function (client) {
  // Define client.getStreamLength( ) on the server to be invoked from the client.
  client.getStreamLength = function (streamName) {
    return Stream.length(streamName);
  }
  return true; // Accept the client connection.
};

Remote method calls and how they work are described in detail in Chapter 9. If the total length of all the streams in a playlist is required, a Client object can be given a method that calls sumStreamLengths( ), as shown in this server-side code:

client.getPlaylistLength = function (  ) {
  return sumStreamLengths.apply(null, arguments);
};

The server-side client.getPlaylistLength( ) method can be called using this client-side code:

nc.call("getPlaylistLength", new ResultObject(  ),
        "lectures/vector01", "lectures/vector02");

Don't get confused by the remote method invocation. The Client instance is a server-side entity that represents the client connected to the server. The client-side NetConnection.call( ) method is used to invoke a method (in this case, getPlaylistLength( )) attached to the server-side Client instance.


5.5.2. Republishing with Stream Objects

Often, the server-side Stream class is used to create a new Stream object. A newly created stream can be used to republish another stream under the new stream's name. For example, the following server-side script creates a new stream at the URI public/TVChannel_1 and republishes the prerecorded stream hidden/promo_97:

channel1Stream = Stream.get("public/TVChannel_1");
channel1Stream.play("hidden/promo_97", 0, -1);

Assuming that a stream at the URI public/TVChannel_1 does not already exist, the Stream.get( ) method creates a new stream. Every Flash movie that subscribes to public/TVChannel_1 will see the hidden/promo_97 stream being played. Even though the server-side Stream.play( ) method has similar parameters to the client-side NetConnection.play( ) method, the former provides a different service. It plays a live or recorded stream within the Stream object. When a recorded stream is played into another stream, subscribers will see the recorded stream as it is being played. In effect, the recorded stream is "streamed live" so that subscribers will not be able to seek within it.

The name of the stream specified in the Stream.get( ) method is the name of the stream clients must subscribe to in order to play it.

5.5.2.1 Server-side playlists

A mixture of live and recorded streams can be treated as a playlist and streamed in a preprogrammed sequence as one live stream, as shown in Example 5-12.

Example 5-12. Creating a server-side playlist
channel_1Stream = Stream.get("public/TVChannel_1");
channel_1Stream.onStatus = function (info) {
  trace("code: " + info.code);
  trace("details: " + info.details);
};
channel_1Stream.play("hidden/promo_97", 0, -1);
channel_1Stream.play("hidden/camera_1", -1, 300, false);
channel_1Stream.play("hidden/camera_2", -1, 300, false);
channel_1Stream.play("hidden/promo_98", 0, -1, false);

In Example 5-12, subscribers to the public/TVChannel_1 stream will see the hidden/promo_97 stream play from the beginning (start set to 0) through to the end (length set to -1), watch 300 seconds (5 minutes) of each of two live streams, and then see the final recorded stream play from the beginning to the end. The fourth parameter of the Stream.play( ) method, reset, plays the same role as the flushPlaylists parameter of NetStream.play( ) (namely, whether to begin a new playlist or append to the existing playlist).

Like client-side NetStream objects, server-side Stream objects may also have an onStatus( ) handler defined and will receive information objects that are very similar to NetStream information objects. Example 5-12 produces the following output in the app_inspector.swf movie:

code: NetStream.Publish.Start
details:
code: NetStream.Play.Reset
details: hidden/promo_97
code: NetStream.Play.Start
details: hidden/promo_97
code: NetStream.Play.Start
details: hidden/camera_1
code: NetStream.Play.Start
details: hidden/camera_2
code: NetStream.Play.Start
details: hidden/promo_98

In some circumstances, it may be necessary to prevent access to recorded streams except when they are played live by a server-side stream. In Example 5-12, two pathspublic and hiddenare used. If read access is provided to the public path only, the recorded stream in the hidden path cannot be accessed directly:

application.onConnect = function (client) {
  client.readAccess = "/public";  // Allow access to the public directory only.
  client.writeAccess = "";        // Stop all write access.
  return true;
};

5.5.2.2 Switching streams

When the reset parameter is either omitted or set to true when calling Stream.play( ), the currently playing stream is closed and the Stream object immediately plays the new stream. This feature can be used to dynamically switch the source of a Stream object at any time. The server-side code segment in Example 5-13 shows how a simple remote method can be defined for clients that need to be able to switch streams. The getUserRole( ) function was omitted to reduce the size of the listing but is included on the book's web site.

Example 5-13. Defining a switchStream( ) method for a client object (server-side code)
channel_1Stream = Stream.get("public/TVChannel_1");
channel_1Stream.play("hidden/camera_1");

application.onConnect = function (client, userName, password) {
  var userRole = getUserRole(client, userName, password);
  if (userRole == "CONTROLLER") {       // User has the control panel.
    client.switchStream = function (streamName) {
      channel_1Stream.play(streamName);
    };
  }
  else {                              // Assume user is an anonymous viewer.
    client.readAccess = "/public";    // Provide read access to the public
    client.writeAccess = "";          // directory and no write access.
  }
  return true;                        // Accept all connections.
};

A client movie designed to act as a video switcher will call Client.switchStream( ) on the server side using the client-side NetConnection.call( ) method:

function doShowCamera2 (  ) {
  nc.call("switchStream", null, "hidden/camera_2");
}

5.5.2.3 Chaining streams

The server-side Stream.play( ) method differs from the client-side NetStream.play( ) method in that it takes a fifth parameter, remoteConnection. A remoteConnection parameter must be a NetConnection object that has connected to another application instance. Example 5-14 shows a simple server-side script that connects to another application instance on another FlashCom Server and plays a stream from it.

Example 5-14. Chaining a stream from one server to another
nc = new NetConnection(  );
nc.onStatus = function (info) {
  trace("NetConnection> code: " + info.code);
  if (info.code == "NetConnection.Connect.Success") {
    rebroadcast_s = new Stream.get("rebroadcast");
    rebroadcast_s.onStatus = function (info) {
      trace("Stream> code: " + info.code);
      trace("Stream> details: " + info.details);
    };
    rebroadcast_s.play("public/Ryerson_56K", 0, -1, true, nc)
  }
};
nc.connect("rtmp://echo.ryerson.ca/campusCameras/connector");

5.5.2.4 Recording streams

A Stream object can also record the streams it plays. The Stream.record( ) method takes a single parameter, as follows, that determines how recording proceeds:


"record"

Deletes any existing recorded stream and begins recording


"append"

Appends onto an existing recorded stream or begins a new one if none exists


false

Stops recording

When a stream is recorded, the string URI that was passed into the Stream.get( ) method is used to determine the stream filename and path to which to record. The following example records a playlist as well as makes it available as a live stream. The filename of the recorded stream in this example will be TVChannel_1.flv:

channel_1Stream = Stream.get("public/TVChannel_1");
channel_1Stream.play("hidden/promo_97", 0, -1);
channel_1Stream.play("hidden/camera_1", -1, 300, false);
channel_1Stream.play("hidden/camera_2", -1, 300, false);
channel_1Stream.play("hidden/promo_98", 0, -1, false);
channel_1Stream.record("record");

Recording can be stopped at any time by calling:

channel_1Stream.record(false);

5.5.3. Deleting Streams

A Flash Video format (FLV) file can be deleted using a NetStream or Stream object. Batches of filesincluding MP3 filescan be deleted using the server-side application object.

5.5.3.1 Deleting an FLV file with a NetStream object

To delete a recorded FLV stream using a Flash movie, publish a live stream with the same relative URI as the recorded stream. For example, to delete the file junk.flv at the URI public/junk, you can use this client-side code:

ns = new NetStream(nc);
ns.onStatus = function (info) {
  for (var p in info) {
    trace(p + ": " + info[p]);
  }
};
ns.publish("public/junk", "live");
ns.close( );

The onStatus( ) information object will not indicate that a stream has been deleted. It will show only that the stream has been successfully published:

clientid: 64144408
description: Publishing public/junk.
code: NetStream.Publish.Start
level: status

clientid: 64144408
description: public/junk is now unpublished.
code: NetStream.Unpublish.Success
level: status

The second message with a code of "NetStream.Unpublish.Success" results from closing the stream. MP3 files cannot be deleted this way, as a client cannot publish MP3 files.

5.5.3.2 Deleting an FLV file with a Stream object

You can also delete a recorded FLV stream using the server-side Stream class. A Stream instance can get( ) a stream and then delete it by calling its clear( ) method. Here is another way to delete the same junk.flv file from the preceding example, this time using server-side code:

junkStream = Stream.get("public/junk");
junkStream.onStatus = function (info) {
  for (var p in info) {
    trace(p + ": " + info[p]);
  }
};
junkStream.clear( );

When a server-side Stream object clears a stream this way, FlashCom invokes onStatus( ) with an information object whose code value is "NetStream.Clear.Success", which indicates the stream file has been deleted:

code: NetStream.Clear.Success
level: status

MP3 files cannot be deleted this way, because a server-side Stream object cannot publish, and therefore cannot get( ), an MP3 URI.

5.5.3.3 Deleting MP3 and FLV files with the application object

The only way to programmatically delete MP3 files (which also works for FLV files) and to delete more than one file at a time using a URI pattern as with application.clearStreams( ). Using clearStreams( ), you can delete either a single MP3 or FLV file, a batch of either MP3 or FLV files using a wildcard pattern, or all the MP3 or FLV files belonging to an application instance. To delete an individual file, pass the relative URI of the file to the clearStreams( ) method:

if (application.clearStreams("flv:/public/junk")) {
  trace("Stream was successfully deleted or did not exist.");
}
else {
  trace("Stream was not deleted.");
}

The URI does not require the first forward slashflv:public/junkwill also work. The application.onStatus( ) method is not invoked when clearStreams( ) is called. Instead, clearStreams( ) returns true if the file was deleted or no file was found. If a file is found but could not be deleted, it returns false. The flv: prefix at the beginning of the URI makes it clear that an FLV file is to be deleted. However, if the flv: prefix is not included, it is assumed. To delete an MP3 file, mp3: must be prefixed to the URI. For example:

application.clearStreams("mp3:/music/boringTrack");

will delete boringTrack.mp3 from the music directory. If mp3: is not prefixed, the clearStreams( ) method will attempt to delete an FLV file named boringTrack.flv. If it doesn't find one, it will still return TRue.

Two wildcard characters, * and ?, can be used to delete files matching a wildcard pattern within a directory and all its subdirectories. For example, the following statement will delete all the files beginning with the string "junk" in the public directory and all its subdirectories:

application.clearStreams("/public/junk*");

An asterisk (*) can be used on its own to indicate that either all FLV or all MP3 files in a directory and its subdirectories should be deleted. For example, to delete all FLV files in the public directory and all its subdirectories use:

application.clearStreams("/public/*");

The asterisk is not really necessary. For example, you can delete all the files in the public directory and all its subdirectories this way:

application.clearStreams("/public/");

If all files in a directory, such as public, and its subdirectories are deleted as a result of a clearStreams( ) call, the directory (in this case, public) and all its subdirectories will also be deleted.

The ? character can also be used to match a single character. For example, to delete all the files that start with "log_" followed by exactly four characters, use the following:

application.clearStreams("/private/log_????");

The preceding examples delete FLV files because the file type was not specified. To delete MP3 files, prefix the stream URI with the string mp3: as in this statement:

application.clearStreams("mp3:/music/*");

Unfortunately, wildcard patterns do not work in FlashCom versions 1.5 through 1.5.2 but do work in version 1.0. You can still clear all the files in a directory this way:


 application.clearStreams("/public/");

To delete all the FLV stream files of an instance, use a single forward slash as the stream URI. For example:

application.clearStreams("/");

Or you can prefix the slash with flv:

application.clearStreams("flv:/");

To delete all the MP3 stream files of an instance, use:

application.clearStreams("mp3:/");