5.6. Publishing and Playing ActionScript Data
ActionScript data can
be added to a published stream at any time. The data is always added
as part of a method invocation request. For example, we
can't just add the text string "Hi
Robert, how are you?" to a stream. Instead, we have
to name a method that will receive our text. In Example 5-1, we chose to have each client publishing an
out_ns stream specify the method name
"showMessage" and the text to pass
to it:
out_ns.send("showMessage", input_txt.text);
The NetStream.send( ) method sends a method
name, parameters, and timestamp to the server along with the
published stream.
The out_ns.send(
) method invocation adds the name of the
method ("showMessage"), the data to
be passed into the method (the text from the
input_txt.text field), and a stream timestamp to
the published stream.
In the case of a live stream, the method name, parameters, and
timestamp will be sent to the server along with any audio and video.
The server forwards the ActionScript data on to any clients
subscribed to the stream. When each client receives the ActionScript
data from the server, nothing will happen unless the
NetStream object playing the stream has a
showMessage( ) method defined on it. From Example 5-1:
in_ns.showMessage = function (msg) {
writeln(msg);
};
Since the showMessage(
) method is usually invoked on a client
other than the sender, it is referred to as a remote
method.
Not only does the server send on ActionScript data to live
subscribers of a stream, but it also stores the ActionScript data in
the stream's FLV file if the stream is being
recorded. Clients that subscribe to a recorded stream will also
receive the recorded ActionScript data in sync with stream time and
any audio or video that was recorded.
 |
Sending ActionScript data in a stream can be tremendously useful. The
data can be used to record all sorts of events, such as someone
adding text to a chat, showing a slide during a presentation, or
starting to publish a different stream.
|
|
Chapter 15 includes an entire session recording
and playback framework based on recording and playing back
ActionScript data in a stream. However, there are other ways to send
a remote method request to other clients that are often more
practical than sending data in a stream. Chapter 9 covers all the options for invoking remote
methods in detail.
The client-side NetStream.send( ) method and the
server-side Stream.send( ) method both allow you
to send ActionScript data in a stream. There are two essential steps
to making remote method invocation work: defining a method on the
subscribing NetStream object and sending the
request to the method within a published stream.
5.6.1. Defining a Remote Method
Defining a remote method to be executed on a
NetStream object is no different than defining a
method on any ActionScript object. The method can be defined on
NetStream.prototype, which makes it available to
every NetStream object, or as an individual
method of a particular NetStream instance. The
following code snippet shows two simple method definitions. Both
methods rely on a function or object (not shown) to do the real work:
// Defining a method on the prototype object for all instances.
NetStream.prototype.showMessage = function (msg) {
writeln(msg);
};
ns = new NetStream(nc);
// Defining a method for a single instance.
ns.doCommand = function (command) {
commandHandler.doCommand(command);
};
Both methods can be invoked only if the
NetStream object ns is
playing a stream. Publishing streams cannot invoke their own methods
using send( ). When a
NetStream method such as showMessage(
) or doCommand( ) is invoked, it
cannot return a value the way a normal ActionScript function can. If
a remote method does attempt to use a return
statement to return a value, the value is discarded.
5.6.2. Sending a Request
A request to invoke a remote method can be sent by a publishing
NetStream object or by a subscribed server-side
Stream object. Both objects use the
send( ) method to make a request. The first
parameter of the send( ) method is always the
name of the remote method (handler name) to invoke. Any optional
parameters passed after the method name are passed as parameters into
the remote method when it is invoked:
// Passing a single string parameter.
out_ns.send("showMessage", input_txt.text);
// Passing a complex object.
cObj = {command: "rewriteField", text: "Sample Text.", scroll: 1};
out_ns.send("doCommand", cObj);
On the server, method invocation requests can also be made by a
Stream object:
s = Stream.get("monitorStream");
s.send("showMessage", "New client logged in from " + client.ip);
5.6.2.1 Stream logs
The send( ) method can be used to write
log data into a recorded stream file. In
fact, this is the way FlashCom stores all its log files.
 |
The advantage to using streams to store log data is that streams,
unlike shared objects, can store very large amounts of data with
little impact on client or server memory.
|
|
The data can be read back by defining a method on a
NetStream object and then playing the stream.
In the simple homegrown Example 5-15, a server-side
Stream object is used to
record
events as they occur.
Example 5-15. Simple application logging
log_s = Stream.get("log");
log_s.record("append");
application.onAppStart = function ( ) {
log_s.send("onLog", {event: "onAppStart",
time: new Date( ),
appInstance: application.name});
};
application.onConnect = function (client, userName, password) {
log_s.send("onLog", {event: "onConnect",
time: new Date( ),
ip: client.ip,
userName: userName});
return true;
};
application.onDisconnect = function (client) {
log_s.send("onLog", {event: "onDisconnect",
time: new Date( ),
ip: client.ip,
userName: userName});
return true;
};
Playing the log file back from a
remote client is done by connecting to the instance and then playing
the log stream. However, since this is a log file that may have taken
many hours to record, we don't want to wait for
hours to get back all the ActionScript data. To play all the
ActionScript data immediately, we can set the
flushPlaylists parameter to a value of 2 or 3:
ns = new NetStream(nc);
ns.onLog = function (info) {
for (var p in info) {
trace(p + ": " + info[p]);
}
};
ns.play("log", 0, -1, 3);
In this example, the value 3 is specified as the
NetStream.play( ) method's
flushPlaylists
parameter. If a value of 2 or 3 is passed in, all remote method call
requests are retrieved immediately before any stream audio or video
begins to play. A value of 3 resets the playlist while a value of 2
maintains the current playlist.
Here is some sample output:
time: Sat May 24 22:42:51 GMT-0400 2003
event: onAppStart
appInstance: customLog/_definst_
userName: Guest
time: Sat May 24 22:42:51 GMT-0400 2003
ip: 127.0.0.1
event: onConnect
userName: blesser
time: Sat May 24 22:43:04 GMT-0400 2003
ip: 127.0.0.1
event: onDisconnect
In fact, you don't have to go to all this work to
log events. FlashCom can write out trace( )
messages to an application log for you if you ask it to. The messages
are saved in a stream file as onLogMsg( )
requests and can be read back with a utility such as the one cited
shortly. To turn on application logging, use the
<RecordAppLog> tag in any
Application.xml file.
Replace the default false value with
true:
<RecordAppLog>true</RecordAppLog>
Logging can use a great deal of disk space, so you may want to place
individual Application.xml files with the
<RecordAppLog> tag set to
true in the home directory of the applications you
want to log. The log file will be saved to the
.../admin/streams/logs/application/appName
directory and will be named after the application instance name, for
example, _definst_.flv. FlashCom can also create
a server access log that records information about when users logged
in and out of the system.
A utility to read log files and more information on
FlashCom logs are available at:
- http://www.macromedia.com/support/flashcom/ts/documents/flashcom_logging.htm
See Chapter 12 for another approach to
application-level logging and Chapter 18 for a
discussion of logging options.
5.6.2.2 Sending and recording events
The send( ) method can be
used to send and record
information to be acted on in sync with audio and video. For example,
during an online lecture, a professor may wish to bold a section of
text in a text field in order to focus attention on it. When the
professor clicks a Send button, each student will see the section of
text bolded in the Flash movie she is watching. Later, when the
recorded version of the stream is played back, the bolding of text
will again occur, synchronized to the audio stream of the
professor's voice.
Making a system like this work requires building a Flash movie or
movies that capture audio and events from the professor, translate
the events into some form of data, such as an object, and send them
in the stream. When the data in the stream arrives at the subscribing
movie, it must be acted on to produce the required effect.
When an object is used to send an instructionfor example to
tell another movie to bold text in a text fieldit is often
called a command object. See the
Command design pattern in the book
Design Patterns by Erich Gamma et al. (Addison
Wesley).
Example 5-16 shows a partial listing of code that
creates an object containing information about the
formatting of text in a text
field. (The complete example is available on the
book's web site.) The
formatTextCommand
object is an instance of the
FormatTextCommandClass class and contains the
starting and ending positions of the text to select and bold. It is
created after the user highlights some text and clicks a Send button.
Example 5-16. Sending a command to set selected text in bold
function FormatTextCommandClass (start, end) {
this.startIndex = start; // Beginning of selection
this.endIndex = end; // End of selection
}
FormatTextCommandClass.prototype.destination = "code_mc";
FormatTextCommandClass.prototype.command = "formatText";
FormatTextCommandClass.prototype.repaint = false;
// Called when the Send button is clicked.
function doSend ( ) {
if (lastTextobj.focusedField == code_txt) {
// Show the professor the text set in bold.
code_txt.setTextFormat(normalFormat);
code_txt.setTextFormat(lastTextObj.startIndex,
lastTextObj.endIndex,
highlightFormat);
// Create a command object and queue it up.
var formatTextCommand = new FormatTextCommandClass(lastTextObj.startIndex,
lastTextObj.endIndex);
sender.queueCommand(formatTextCommand);
}
sender.send( );
}
The lastTextObj and sender
objects are not shown. The sender object contains
a queue of events and sends them in a stream using the following
statement (where obj is a command already in the
sender's queue):
ns.send("doCommand", obj);
When the stream is playing (live or recorded), command objects are
passed into the doCommand( ) method of
subscribing streams:
ns.doCommand = function (command) {
command.time = this.time;
commandHandler.doCommand(command);
};
In this case, the
commandHandler object passes the command on to an
object that finds the destination movie clip (in this case named
code_mc) and calls its formatText(
) method. The formatText(
) method, shown in Example 5-17, sets the formatting of the
code_txt field and then uses the information in
the command to bold a section of the text.
Example 5-17. Processing the command to bold a text selection
highlightFormat = new TextFormat( );
highlightFormat.bold = true;
highlightFormat.color = 0x004433;
normalFormat = new TextFormat( );
normalFormat.bold = false;
normalFormat.color = 0x006655;
function formatText (command) {
code_txt.setTextFormat(normalFormat);
code_txt.setTextFormat(command.startIndex, command.endIndex, highlightFormat);
}
|