5.1. A Simple Publisher/Subscriber Example
Example 5-1 is a somewhat
oversimplified but complete example
script that shows the essential steps in publishing one live stream
and subscribing to a second live stream. The example shows how video,
audio, and ActionScript data can be sent via a published stream and
received by subscribing to another stream.
Example 5-1. A very basic two-way messaging client
// writeln( ) replaces trace( ) and writes messages into the trace_txt field.
function writeln (msg) {
trace_txt.text += msg + "\n";
trace_txt.scroll = trace_txt.maxscroll;
}
// onStatus( ) sets up the streams and buttons when a connection is
// established or lost.
NetConnection.prototype.onStatus = function (info) {
writeln("Level: " + info.level + ", code: " + info.code);
if (info.code == "NetConnection.Connect.Success") {
out_ns = new NetStream(this);
out_ns.attachAudio(Microphone.get( ));
out_ns.attachVideo(Camera.get( ));
in_ns = new NetStream(this);
in_ns.showMessage = function (msg) {
writeln(msg);
};
// remote_video is the instance name of an embedded video object.
remote_video.attachVideo(in_ns);
in_ns.play("public/" + remoteUserName);
connect_pb.setLabel("Disconnect");
send_pb.setEnabled(true);
}
else if (!this.isConnected) {
if (out_ns) out_ns.close( );
if (in_ns) in_ns.close( );
connect_pb.setLabel("Connect");
send_pb.setEnabled(false);
}
};
NetStream.prototype.onStatus = function (info) {
for (var p in info) {
writeln("info." + p + ": " + info[p]);
}
};
// doSend( ) is called when the send_pb is clicked.
function doSend (msg) {
out_ns.send("showMessage", input_txt.text);
input_txt.text = "";
}
/* doConnect( ) is called when the connect_pb is clicked.
* It stores the text in the userName_txt and remoteUserName_txt
* fields in global variables for later use if a connection is
* attempted. See the onStatus( ) method.
*/
function doConnect (pb) {
if (pb.getLabel( ) == "Connect") {
userName = userName_txt.text;
remoteUserName = remoteUserName_txt.text;
nc.connect("rtmp:/courseChat/algebra101", userName);
}
else {
nc.close( );
}
}
function doSendStream (pb) {
if (pb.getLabel( ) == "Send Stream" && nc.isConnected) {
pb.setLabel("Stop Stream");
out_ns.publish("public/" + userName);
}
else {
pb.setLabel("Send Stream");
out_ns.publish(false);
}
}
send_pb.setEnabled(false);
nc = new NetConnection( );
To create a NetStream object within a network
connection, the NetConnection object must
already exist and a connection attempt to FlashCom must have already
been made by calling the connect( ) method. In
Example 5-1, a new
NetConnection object is created and then the
user must enter both his username and the name of the remote user he
wants to talk to. Then he must click the Connect button. When the
connection is established, the
NetConnection.onStatus( )
handler is called and two NetStream objects are
created and initialized to send or receive data. The
NetStream object that is designed to receive
data tries to subscribe to a stream immediately, but the outgoing
stream does not attempt to send anything until the user clicks the
Send Stream button.
5.1.1. Publishing a Live Stream
In order to publish
a stream, a NetStream object has to be created.
In Example 5-1, the out_ns object
is created within a connection by passing it a
NetConnection object. The keyword
this, which represents the current
NetConnection object, is passed in this example
because the NetStream object is created within
the NetConnection.onStatus( ) method:
out_ns = new NetStream(this);
This statement could also have been written using the
nc variable defined in the last line of Example 5-1:
out_ns = new NetStream(nc);
Once the NetStream object
out_ns exists, audio and video data sources can be
attached to it:
out_ns.attachAudio(Microphone.get( ));
out_ns.attachVideo(Camera.get( ));
The Microphone and Camera
classes are available only within Flash movies (not on the server)
and provide a number of static methods, such as get(
). In the example, Microphone.get( )
returns the
Microphone object last selected in the
movie's Player Settings dialog box. Passing the
Microphone object to the
out_ns.attachAudio( ) method makes the sound
data captured from the microphone available to the
out_ns stream. The Camera.get(
) method returns a
Camera object and works in the same way.
Attaching the Camera object to the stream using
the NetStream.attachVideo(
) method makes compressed video data from the camera
available to the stream. Although it isn't a best
practice to pass the return value from the get(
) methods directly to the attachAudio(
) and attachVideo( ) methods, we use
this approach for demonstration purposes. Chapter 6 considers other alternatives.
The Camera and Microphone
classes provide other methods for managing video and audio sources
and events, and are described in detail in Chapter 6.
Before actually publishing the stream, it is a good idea to set up an
onStatus( ) event handler that will receive
notifications when stream events occur. In Example 5-1, a default onStatus( )
handler is attached to NetStream.prototype, making
it available to all NetStream instances. Any
NetStream object that is not assigned its own
handler will use the one assigned to the prototype. Defining an
onStatus( ) method on the
NetStream.prototype is convenient for testing and
exploration but is not normally used in production code. In Example 5-1, the onStatus( ) handler
simply displays the information object's properties
in the trace_txt field.
The outbound stream is published to the server using
NetStream.publish( ) after the user clicks the
Send Stream button:
out_ns.publish("public/" + userName);
The required parameter is the relative URI that identifies the
stream. In this example, the stream name will depend on the username
provided. If the username is
"brian," the URI will be
public/brian, which becomes the address of the
published stream within the application instance to which the movie
is connected. If another movie is already publishing a stream with
the URI public/brian, the attempt to publish the
stream will fail and a
"NetStream.Publish.BadName" error
message will be returned in the info.code property
passed to onStatus( ). The directory name
public has no special meaning in this example
and was used to emphasize that stream names are relative URIs.
The publish( ) method has a second, optional parameter
(discussed later) that determines whether the stream is recorded. If
the parameter is omitted, the stream is not recorded and is available
as a live stream only.
Once the stream has been published, ActionScript data can also be
sent on the stream at any time using the NetStream.send(
) method. In Example 5-1,
NetStream.send( ) is used to create a simple
text messaging feature. When one user enters some text in the
input_txt field and clicks the Send button, the
other user will see the text appear in her
trace_txt field.
ActionScript data that is sent on a stream is always sent as part of
a request to invoke a method. In Example 5-1, when
one user enters text in the input_txt field and
clicks the Send button, the doSend( ) function
is executed, which in turn calls out_ns.send( )
to send the text data. The NetStream.send( )
method is always passed the name of a remote method to call as the
first parameter, as follows:
out_ns.send("showMessage", input_txt.text);
In Example 5-1, the remote method
showMessage( ) will be called on any
NetStream objects subscribed to the stream and
will receive the text the user typed into the
input_txt field.
The showMessage( ) method should be defined on
the subscribing NetStream, for example:
in_ns.showMessage = function (msg) {
writeln(msg);
};
Even if the publishing stream defines a showMessage(
) method, it will not receive any remote method calls it
sends on the stream. Only subscribed streams receive remote method
calls.
In summary, the steps to publish a live stream are:
Create a NetConnection object: nc = new NetConnection( );
Use NetConnection.connect( ) to attempt to
connect to an application instance. The connect(
) method must be called before attempting to create a
NetStream object: nc.connect("rtmp:/courseChat/algebra101", userName);
Create a new NetStream object on a
NetConnection object by passing the latter as a
parameter to the NetStream constructor: out_ns = new NetStream(nc);
Attach any audio or video sources to the stream using
attachAudio( ) and attachVideo(
) methods of the NetStream class: out_ns.attachAudio(Microphone.get( ));
out_ns.attachVideo(Camera.get( ));
Make sure the NetStream object has an
onStatus( ) handler, either by defining one on
NetStream.prototype or by creating a method for
the individual NetStream object. This step is
not required but is recommended. Publish the stream using NetStream.publish( ):
out_ns.publish("public/" + userName);
5.1.2. Subscribing to a Live Stream
Subscribing to a stream is a little different than
publishing one. To publish audio and video in a stream, we attached a
Camera and a Microphone
object to the stream. However, to play a stream and display it in the
Flash client, we have to attach the stream to a Video object. We also
have the option of attaching the stream to a movie clip to control
the stream's audio. We always start by creating a
NetStream object within a
NetConnection and then attach objects that
display video or control audio. Methods can also be added to the
stream to receive remote method calls. Here, we create a new
NetStream, attach it to a Video object, and play
the stream. Text messages received by the stream will be displayed
using the showMessage( ) method of the
NetStream object:
in_ns = new NetStream(nc);
remote_video.attachVideo(in_ns);
in_ns.showMessage = function (msg) {
writeln(msg);
};
in_ns.play("public/" + remoteUserName);
In Example 5-1, the in_ns stream
is attached to an embedded Video object named
remote_video. Embedded Video objects are similar
to movie clips. They can be placed on the Stage to display video and
have some of the same properties as movie clips. For example, you can
manipulate the height and width of an embedded Video object by
setting its _height and _width
properties. To place a Video object on the Stage, use the Library
panel's pop-up Options menu and choose New Video.
Once an embedded Video object is available in the Library, it can be
dragged to the Stage and resized, or it can be placed within a movie
clip. Once on stage, a Video object must be given an instance name
using the Properties panel in order to refer to it via ActionScript.
By default, audio arriving within a stream is automatically forwarded
to a sound device on the local computer so that it can be heard. It
is not necessary to attach a stream to anything in order to hear
audio. However, to control audio
volume,
the stream can be attached to a movie clip and then a
Sound object can be created on the movie clip to
control the volume of the audio. For example, to set the volume to
50, an existing movie clip instance named
stream_mc could be utilized this way:
stream_mc.attachAudio(in_ns);
soundControl = new Sound(stream_mc);
soundControl.setVolume(50);
 |
Although the Sound object did not work properly
in the Flash Player 6 initially distributed with the Flash MX
development environment, later versions properly control stream
volume. The Player used in the authoring environment is not
automatically upgraded when the browser Player plugin is upgraded. To
upgrade the Player used in the Flash development environment, visit:
http://www.macromedia.com/support/flash/downloads.html
|
|
In summary, NetStream objects have
attachVideo( ) and attachAudio(
) methods that are used to attach video and audio sources
when publishing a stream. Video objects have an
attachVideo( ) method that is used to attach
video arriving within a stream to an embedded
Video object. Similarly, the
MovieClip class has an attachAudio(
) method that attaches incoming audio to a clip.
If a publishing stream calls a
remote method, that method must exist on the subscribing stream or
nothing will happen. In Example 5-1, the
NetStream.send( ) method is called and passed
the remote method named showMessage( ) and a
text string to pass into it. Defining a showMessage(
) method on the subscribing stream, as shown earlier, is
all that is required to respond to the remote method call.
Once the in_ns stream is ready to receive audio,
video, and remote method calls, you can subscribe it to a stream on
the server using NetStream.play( ):
in_ns.play("public/" + remoteUserName);
Following through on the stream names used in Figure 5-1, if the remoteUserName
variable contains the string
"robert", then the relative URI of
the stream being requested via play( ) will be
public/robert. If no stream of that name exists,
no error will be returned. Instead, the application will simply
return data from a stream of that name if and when one is published.
This short demonstration script can be used by two people to have a
video, audio, and text conversation, but it does suffer from some
serious limitations. For example, text messages appear only in the
subscriber's text field and there is no way for
either user to know the other is available and wants to chat. Also,
the program should not rely on both users entering both usernames in
order to chat. Some additional programming, most often using remote
method calls and/or shared objects, is required to overcome these
limitations. Keeping track of and managing streams using shared
objects was first introduced in the helloVideo
application in Chapter 1 and is described in
detail in Chapter 8.
 |