Section 12.4. Using ColdFusion and FTP to Mirror Streams

12.4. Using ColdFusion and FTP to Mirror Streams

If your FlashCom application gets enough traffic, you may need to have more than one FlashCom Server running. Depending on what your application does, you may need to make sure that the lists of streams on each of the servers are synchronized. For example, users may have the option of recording videos that are viewable by anyone else using the application. However, if the user who records the video is connected to FlashCom Server A and another user is connected to FlashCom Server B, the user connected to Server B won't be able to view the video from Server A. The following example shows one way to use ColdFusion, Flash Remoting, and FTP to keep the streams on multiple servers in sync. The application consists of five basic parts :

  • A Server-Side ActionScript class to manage streams

  • A simple Server-Side ActionScript class to represent an FTP server

  • A CFC to manage the FTP operations

  • The FlashCom application file

  • A simple Flash client movie to record new streams

Additionally, in order to successfully run the application, you'll need access to at least one FTP server. Even though the general idea of the example is to mirror streams on a cluster of FlashCom Servers, you don't need to have more than one FlashCom Server to run the basic example and see how it works. In fact, its usefulness isn't limited to synchronizing files on other FlashCom Servers. The same system can be used to back up streams to another server; you also can use it to copy streams from a dedicated recording server to individual distribution servers or to a storage area network.

12.4.1. Creating a Stream Manager

The example assumes that users can record new streams and, as soon as the stream has finished publishing, you want to make copies to one or more other servers. Therefore, you'll need a stream manager class on the server that can generate unique stream names and listen to get notified when the stream has finished publishing.

The PFCSStreamManager class does three basic things:

  • Generates a new, unique stream name for each stream

  • Connects to the stream

  • Once the client finishes publishing to the stream, the manager dispatches an event to listeners, notifying them that the stream is ready to copy

You can create the PFCSStreamManager class within a new FlashCom application named fcs_ftp . In the fcs_ftp directory, create a new file named PFCSStreamManager.asc , and add the following code to the file:

 function PFCSStreamManager(  ) {   // Create a new associative array to store the streams that are publishing.   this.streams_publishing = new Object(  ); } PFCSStreamManager.prototype.getNewStreamID = function ( ) (   // Create two numbers---one based on epoch milliseconds and one based on a    // random number from 0 to 100,000. Then, concatenate those to form a unique    // stream identifier (the chance that the ID isn't unique is very low).   var number_1 = (new Date( )).getTime( );   var number_2 = Math.round(Math.random( ) * 100000);   var id = "stream_" + number_1 + "_" + number_2;   // Create a new Stream object.   this.streams_publishing[id] = Stream.get("playback_" + id);   // Tell the stream its ID.   this.streams_publishing[id].id = id;   this.streams_publishing[id].parent = this;   this.streams_publishing[id].onStatus = function (info) {     // The "NetStream.Play.UnpublishNotify" code is recevied when the client stops      // recording the stream. Dispatch a   publish_done   event to notify listeners.     if (info.code == "NetStream.Play.UnpublishNotify") {       // Dispatch a   publish_done   event, and pass the stream ID in the event object.       this.parent.dispatchEvent({type: "publish_done", id: this.id});       // Delete the stream.       delete this.parent.streams_publishing[id];     }   };   // Start playing the stream.   this.streams_publishing[id].play(id);   // Return the new stream's ID.   return id; }; // Define a   dispatchEvent( )   method to notify listeners. PFCSStreamManager.prototype.dispatchEvent = function (event) {   // Loop through each of the listeners for the specific event type. Call the   // method of the listener that matches the event name. Pass it the   event   object.   for (var i = 0; i < this["listeners_" + event.type].length; i++) {     this["listeners_" + event.type][i][event.type](event);   } }; // Add new listeners. PFCSStreamManager.prototype.addEventListener = function (type, listener) {   if (this["listeners_" + type] == undefined) {     this["listeners_" + type] = new Array( );   }   this["listeners_" + type].push(listener); }; 

The preceding code is quite straightforward and doesn't require much further explanation. However, a few items are worth discussing in more detail.

The getNewStreamID( ) method generates a new, unique stream ID. Since potentially many FlashCom Servers will allow users to record streams and copy them to other servers, it's important to generate IDs that are likely to be unique across servers. Although it doesn't absolutely guarantee that the same ID won't be generated twice, concatenating the epoch milliseconds with a random number makes duplicate stream IDs exceedingly unlikely :

 var number_1 = (new Date()).getTime(  ); var number_2 = Math.round(Math.random(  ) * 100000); var id = "stream_" + number_1 + "_" + number_2; 

You can next create a new stream to which to subscribe and listen for events of the stream to which the client will publish. The onStatus( ) method is called with a "NetStream.Play.UnpublishNotify" code when the user finishes publishing to the stream. It simply dispatches a publish_done event to listeners:

 this.streams_publishing[id] = Stream.get("playback_" + id); this.streams_publishing[id].id = id; this.streams_publishing[id].parent = this; this.streams_publishing[id].onStatus = function (info) {   if (info.code == "NetStream.Play.UnpublishNotify") {     this.parent.dispatchEvent({type: "publish_done", id: this.id});     delete this.parent.streams_publishing[id];   } }; this.streams_publishing[id].play(id); 

Now that the stream manager class is complete, let's tackle the FTP server class.

12.4.2. Creating an FTP Server Class

The next step is very simple. You want to create a basic class that represents the data for an FTP server, including:


url

The URL of the FTP server, such as "publicstreams.flashcommunications-net", excluding the ftp : protocol.


username

A username that has write access to the FTP server.


password

The password corresponding to the username for access to the FTP server.


directory

The initial (root) directory to which to copy files.


passive_mode

Whether to use passive mode for the FTP transfer. Passive FTP (a.k.a. PASV FTP) allows a connection to be initiated from the client rather than from the server. Passive mode may be required for users behind some gateways or router-based firewalls. However, some FTP servers support active transfers only. ColdFusion supports both, which is why we leave it as a configurable option that is specified for each FTP server individually.

You can create a new file named FTPServer.asc in the fcs_ftp directory, and add the following SSAS code to it:

 function FTPServer (url, username, password, directory, passive_mode) {   this.url = url;   this.username = username;   this.password = password;   this.directory = directory;   // Make   passive_mode   an optional parameter. It can have values of   // either "yes" or "no" (defaults to "no" if no value is passed in).   if (passive_mode == undefined) {     this.passive = "no";   }   else {     this.passive = passive_mode;   } } 

We'll use FTPServer instances to describe each FTP server to which to copy the stream. The CFC that we'll write next uses this data to perform the FTP transfers.

12.4.3. Using ColdFusion with FTP

You next want to create a CFC that will use FTP to copy a file from one server to one or more other servers. The method should accept four parameters:


ftp_servers

An array of FTPServer objects


file

The name of the file to copy, such as an .flv stream


path

The path to the file on the local machine


transfermode

An optional parameter indicating the transfer mode (ASCII or binary); defaults to binary, as should be used for .flv files

Create a new CFC document named FTPUtilities.cfc , and save it to a directory named fcs_ftp in the web root. Add the following code to the file:

 <cfcomponent>   <cffunction name="copyFile" access="remote">     <cfargument name="ftp_servers" type="array" />     <cfargument name="file" type="string" />     <cfargument name="path" type="string" />     <cfargument name="transfermode" type="string"                  required="false" default="binary" />     //Loop through   FTPServer   objects, and use   <cfftp>   to copy the file.     <cfloop from="1" to="#ArrayLen(ftp_servers)#" index="i">       //Create the connection to the server.       <cfftp action="open" server="#ftp_servers[i].url#"              username="#ftp_servers[i].username#"              password="#ftp_servers[i].password#" connection="ftp_connection" />       //Copy the file.       <cfftp action="putfile" transfermode="#transfermode#"              localfile="#path##file#" remotefile="#ftp_servers[i].directory##file#"               passive="#ftp_servers[i].passive#" connection="ftp_connection" />       //Close the connection to the server.       <cfftp action="close" connection="ftp_connection" />     </cfloop>   </cffunction> </cfcomponent> 

Our ColdFusion component implements a copyFile( ) method that copies the specified file to all the FTP servers specified by ftp_servers , an array of FTPServer objects (one describing each FTP server).

12.4.4. Making the FlashCom Application

The next step is to assemble the FlashCom application. The application should allow a client to connect, request a stream ID, and publish to the stream. Once the client stops publishing, the FlashCom application should automatically copy the file to one or more servers.

Create a new file named fcs_ftp.asc (or main.asc ) and save it to the fcs_ftp subdirectory in the FlashCom applications directory. Add the following code to the file:

 load("PFCSStreamManager.asc"); load("FTPServer.asc"); load("NetServices.asc"); application.onAppStart = function ( ) (   // Create a Flash Remoting connection.   NetServices.setDefaultGatewayUrl("http://localhost/flashservices/gateway");   var ncFlashRemoting = NetServices.createGatewayConnection( );   this.frservice = ncFlashRemoting.getService("fcs_ftp.FTPUtilities", this);   // Create a connection to the Admin Service to get the current    // application's physical path.   this.ncAdmin = new NetConnection( );   this.ncAdmin.onStatus = function (info) {     if (info.code == "NetConnection.Connect.Success") {       var key = "Adaptor:_defaultRoot_/VirtualHost:_defaultVhost_/AppsDir";       this.call("getConfig", application, key, "/");     }   };   // Replace the username and password with those you've set in your Admin Service.   this.ncAdmin.connect("rtmp://localhost:1111/admin",                        "adminusername", "adminpassword");   // Create a new stream manager.   this.stream_manager = new PFCSStreamManager( );   // Create a listener to listen for   publish_done   events.   this.stream_manager_listener = new Object( );   // When the stream is done publishing, the stream manager dispatches a    //   publish_done   event. The event handler calls the   copyFile( )   method.   this.stream_manager_listener.publish_done = function (event) {     application.copyFile(event.id);   };   this.stream_manager.addEventListener("publish_done",                                        this.stream_manager_listener);      // Create an array of FTP servers to which the files should be copied.   this.servers = new Array( );   // Add one or more   FTPServer   instances to the array. You need to add trailing    // slashes after the   streams   directory but exclude the   ftp   protocol. For example:   //    "   fcs/some_application/streams/_definst_/   ".   this.servers.push(new FTPServer("ftp.somefcsserver1.com", "username", "password",                      "streams_directory", "yes"));   this.servers.push(new FTPServer("ftp.somefcsserver2.com", "username", "password",                     "streams_directory", "no")); }; //   onResult( )   is called when the response is returned from the Admin Service. // Calculate the physical path to the streams. application.onResult = function (value) {   var application_path = application.name.split("/");   this.streams_path = value.data + "\" + application_path[0] + "\streams\" +                                           application_path[1] + "\"; }; // Create an empty method to handle the response from the   copyFile( )   method. // Otherwise, the   onResult( )   method would get called. application.copyFile_Result = function ( ) (}; // When the   copyFile( )   method is called, invoke the   copyFile( )   method of the CFC. // This always copies   .flv   files. application.copyFile = function (id) {   this.frservice.copyFile(this.servers, id + ".flv", this.streams_path, "binary"); }; // Add a   getNewStreamID( )   method to the client objects that simply proxies  // a call to the stream manager's   getNewStreamID( )   . Client.prototype.getNewStreamID = function ( ) (   return application.stream_manager.getNewStreamID( ); }; 

Much of the FlashCom application code is either straightforward or very similar to code used in the previous example application. Note the ftp:// protocol need not be included in the FTPServer instance's url parameter (although FTP servers often have names that begin with "ftp" such as "ftp.somedomain.com"). This example shows two FTP servers, one for which we used passive transfers and one for which we did not.

Also note that the application.copyFile( ) method automatically appends the .flv extension to the filename, so you should not include it when specifying a stream's filename. It always uses the binary transfer mode, as is appropriate for .flv streams. To transfer other types of files, you could modify application.copyFile( ) to accept the transfer mode as a second optional parameter and to not automatically append .flv to the filename.

12.4.5. Creating the Publishing Client

In order to test the FlashCom stream-copying application, you'll need a simple client that publishes a stream. The client should connect to the FlashCom application. When the user clicks a button to start publishing, the client should send a request to the FlashCom application for a stream ID. Once it gets the new stream ID, it can start publishing. Then, when the user clicks the button to stop publishing, the stream manager on the server will get notified automatically, and the FTP operations will occur.

Create a new Flash document named streamCopy.fla . Add a Button component instance to the Stage, and give it an instance name of cbt_publish . Then, add the following code to the first frame of the Actions layer on the main timeline:

 // Define a response object to handle response from the   getNewStreamID(  )   call. var response = new Object(  ); // Once the FlashCom application returns a response with the stream ID,  // attach the camera and start publishing. response.onResult = function (id) {   ns.attachVideo(Camera.get( ));   ns.publish(id, "record"); }; // Create a NetConnection to connect to the FlashCom application. var nc = new NetConnection( ); nc.connect("rtmp:/fcs_ftp"); // Create a NetStream over which to publish the stream. var ns = new NetStream(nc); // Define a listener to handle the click events from the button. var listener = new Object( ); listener.click = function (event) {   // If the button is selected, get a new stream ID. Otherwise, stop    // publishing the stream.   if (event.target.selected) {     nc.call("getNewStreamID", response, null);   }   else {     ns.publish(false);   } }; // Register the listener to handle click events. cbt_publish.addEventListener("click", listener); // Add a label and set the button to toggle. cbt_publish.label = "Publish"; cbt_publish.toggle = true; 

The client code is basic enough that it doesn't require any further explanation. Simply run the client movie; to test it, publish a stream and then stop publishing. Then, check on the FTP server in the correct directory, and you should see the copy of the stream. Other applications running on secondary servers should then be able to access the streams.



Programming Flash Communication Server
Programming Flash Communication Server
ISBN: 0596005040
EAN: 2147483647
Year: 2003
Pages: 203

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net