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:/");
|