Section 5.4. Playing Streams in Detail

5.4. Playing Streams in Detail

The NetStream.play( ) method can be used to play live and recorded streams, segments of recorded streams, and a playlist of streams. The play( ) method has a number of optional parameters that make this possible:

 in_ns.play(   streamURI   false, [,   start   [,   length   [,   flushPlaylists   ]]]); 

The first parameter is required and must be either a relative URI that identifies the stream to play or the value false , which stops the current stream from playing. The remaining parameters, shown inside square brackets, are optional. The nesting of the square brackets indicates that if one optional parameter is included, then so must all previous ones. For example, to use the length parameter, the start parameter must also be provided.

5.4.1. Playing Live or Recorded Streams

The start parameter can be used to indicate more than its name implies. The possible values for start are:


-2

Plays the live stream at the specified URI or plays the recorded stream at that location if the live stream is not found. If a recorded stream is also not found, the NetStream object waits for the stream to be published.


-1

Plays only a live stream at the specified URI. If a live stream is not found, the NetStream object waits for one to be published. A recorded stream at the same URI will not be played .


Plays only a recorded stream at the specified URI from its beginning. If a recorded stream is not found, an information object is returned to NetStream.onStatus( ) .


Greater than 0

Same as passing 0, but indicates how many seconds into the recorded stream to begin playing.

If a start value is not passed into the play( ) method, the default is -2 (plays a live or recorded stream).

The length parameter defines how many seconds the live or recorded stream should play. For example, to play a stream beginning 1 minute into the stream for 30 seconds, call play( ) as follows :

 in_ns = new NetStream(nc); in_ns.play("announcements", 60, 30); 

If the length parameter is -1 or omitted, the stream plays until it ends. Note that, by definition, live streams always start playing at the current time; only recorded streams can be started at some offset. But the length of time to play a stream can be limited even for live streams by specifying a time limit with the length parameter.

5.4.1.1 Creating playlists

By default, invoking play( ) overrides any previous play( ) command for that NetStream object, closing the previous stream immediately and playing the new stream. However, you can create a playlist in which the server treats a sequence of recorded streams as a continuous stream; the server provides seamless buffering so that there are no interruptions when the source stream changes.

To add a stream to a playlist, pass false as the fourth, optional parameter ( flushPlaylists ) when invoking play( ) . This tells FlashCom not to close the previous stream. Instead, the stream is queued and buffered to play after the streams already in the playlist. Here is a short example. Note that if flushPlaylists is specified as true or omitted, as shown in the first play( ) invocation, FlashCom reinitializes the playlist:

 in_ns = new NetStream(nc); in_ns.play("announcements", 60, 30); in_ns.play("live_Camera_1", -1, 15, false); in_ns.play("live_Camera_2", -1, 15, false); in_ns.play("prod_trailer",   0, -1, false); 

The preceding code creates a playlist that plays the following sequence:

  1. 30 seconds of the recorded stream named announcements , starting 60 seconds into the stream

  2. 15 seconds of the live_Camera_1 stream

  3. 15 seconds of the live_Camera_2 stream

  4. The entire recorded prod_trailer stream

The flushPlaylists parameter can also be used to control how ActionScript data is streamed. See the "Stream logs" section later in this chapter.

5.4.2. Stream Time

As a live or recorded stream plays, stream time increases . If the stream pauses, stream time stops until the stream begins to play again. Stream time is 0 at the beginning of a recorded stream or when a live stream is first published. The stream time in seconds is available from the NetStream.time property. However, if no data is being received while a stream is playing, the time property reflects the last moment that data was received on the stream. When data arrives within the stream, the time property jumps to the correct value. For example, if a stream containing video plays for 30 seconds, at which point the video stops, the stream time will remain at 30 until more data of some type arrives. If more data arrives after another 20 seconds, the time value will suddenly change to 50 and continue increasing as long as data continues to arrive . Playlists are treated as though they are a single stream. When each stream in the playlist begins, stream time is not reset to 0. Therefore, the stream time is relative to when the stream started, not when the current content started playing. For example, if each playlist video clip is 30 seconds, the beginning of the second clip will have a stream time of 30, not 0.

Internally, whenever data is attached to a stream by a publisher, a timestamp is generated and embedded within the stream at the start of the audio, video, or ActionScript message. The timestamp is used to establish correct stream time when the stream is played. After a period of time while a stream is playing but no audio, video, or ActionScript data messages have arrived, the timestamp at the beginning of the next message to arrive is used to update the stream time.

5.4.3. Playback Events

The NetStream.onStatus( ) handler, if defined, is called when errors or significant events occur. It receives an information object containing five fields that describe the event: level , code , details , description , and clientid . For example, when the chat movie described at the beginning of this chapter first attempts to subscribe to a stream named public/robert , the information object passed to onStatus( ) contains the following property values:

 info.level: status info.code: NetStream.Play.Reset info.description: Playing and resetting public/robert. info.details: public/robert info.clientid: 58996536 

The level property will contain either "error" or "status", depending on the type of event. The code property contains the most useful message for determining what has happened . In this case, the stream has been reset, which indicates that any other pending play( ) commands have been dropped. The details property contains the stream URI to which the message relates .

When a NetStream object attempts to subscribe to a live stream, its onStatus( ) handler will normally be called twice and receive the info.code values "NetStream.Play.Reset" and "NetStream.Play.Start".

If the live stream is available, it will begin to play without onStatus( ) being called again. However, the stream may not have started publishing yet, in which case no data will be received from FlashCom. The subscribing stream will therefore wait without generating any further calls to onStatus( ) . When the stream is finally published, the onStatus( ) handler will be called a third time and passed an information object with a code property of "NetStream.Play.PublishNotify".

There is no notification if a live stream is not available the moment an attempt is made to subscribe to it.

When a NetStream object attempts to connect to a recorded stream without a start parameter or with a start parameter of -2, it is not clear to FlashCom whether the NetStream is attempting to play a live or recorded stream. Consequently, even if the recorded stream does not exist, the NetStream will behave as though it is attempting to play a live stream. However, if a start parameter of 0 or greater is passed into play( ) , the NetStream looks for a recorded stream only. In such a case, if the recorded file is found, the onStatus( ) handler is called twice with the code values "NetStream.Play.Reset" followed by "NetStream.Play.Start".

If the file is not found and start is 0 or greater, FlashCom passes an information object with a code value of "NetStream.Play.StreamNotFound" to the onStatus( ) handler.

The next few sections describe typical operational sequences and the status events they generate. Later, under "Putting the User in Control," we'll explain how to use these events to modify the user interface dynamically to help the user monitor and control various operations.

5.4.4. Playing MP3 Files

FlashCom 1.5 introduced the ability to stream recorded MPEG layer 3 (MP3) compressed audio files. MP3 files can be saved into a subdirectory of an application's stream s subdirectory in order to make them available for streaming. Once available, they can be played using a stream URI that begins with the mp3 : prefix. For example, if an MP3 file is stored in the following file path :

.../applications/radio/streams/_definst_/jazz/track1.mp3

and a NetConnection is established from a Flash movie to the _definst_ instance of the radio application, then the following statement subscribes to a stream that plays the MP3 file:

 in_ns.play("mp3:jazz/track1", 0, -1); 

MP3 files can contain tagged information in the ID3 format that may contain information such as the song title, artist, album, year, genre , and a comment. To read the ID3 information, use a separate play( ) statement in which the relative URI of the stream begins with the id3 : prefix. For example:

 in_ns.play("id3:jazz/track1"); 

To actually read the ID3 data, you must define a NetStream.onId3( ) handler. Example 5-4 shows a short demonstration script that reads the ID3 information, writes it into the Flash development environment's Output panel, and then starts to play the audio stream. The ID3 tag names and values are returned as the property names and values of the information object passed into the onId3( ) handler.

Example 5-4. Reading the ID3 tags and playing MP3 audio using one stream
 NetStream.prototype.onId3 = function (info) {   trace("NetStream.onId3>");   for (var p in info) {     trace("   " + p + ": " + info[p]);   } }; nc = new NetConnection( ); nc.connect("rtmp:/radio"); ns = new NetStream(nc); ns.setBufferTime(10); ns.play("id3:jazz/track1"); ns.play("mp3:jazz/track1", 0, -1, false); 

In order to make sure that all the ID3 information is received before the audio is played, the code sets the flushPlaylists parameter to false when play( ) is called a second time. Two separate streams can also be usedone to retrieve the ID3 information and another to play the audio.

FlashCom 1.5 returns the code value of "NetStream.Play.Failed", not "NetStream.Play.StreamNotFound", when an MP3 file is not found.

5.4.5. Uploading Prerecorded Streams

Prerecorded media files in both FLV and MP3 format can be uploaded to a FlashCom Server where they can be played. FLV files that are not recorded by the FlashCom Server can be created using Sorenson Squeeze for Flash MX 2004 or other utilities, such as Squeeze Lite, which is included with the Macromedia Video Kit for Dreamweaver. There are two options as to where FLV and MP3 files can be stored on the server for playback. (FLV files recorded by FlashCom can also be copied to other locations on the server where they can be played by other instances.) One option is to place FLV or MP3 files in the streams/ instanceName subdirectory of an application's home directory where instanceName is an instance name such as _definst_ . However, the files are available only for that instance of the application. The second option is to specify a virtual directory so that every instance of every application can access the same files. A virtual directory is a pathname that is mapped to an actual directory path. The mapping is defined within the <Streams> tag of a Vhost.xml configuration file. For example, to map the common name music to the path C:\CommonFiles\music , use the following <Streams> tag:

 <Streams>music;C:\CommonFiles\music</Streams> 

A semicolon must separate the virtual pathname and the actual directory path. If multiple mappings are required, use multiple <Streams> tags.

5.4.6. Buffering When Subscribing

Live streams that support real-time chats and video conferences should not be buffered. Any delay due to buffering of a stream will only make conversation more difficult. The amount of lag, or delay, in seconds of a live stream can be determined by checking the NetStream.liveDelay property. For recorded streams, or even live streams that are not part of a real-time conversation, buffering can be used to provide a more seamless and higher-quality playback than live bufferless streaming provides.

When recorded streams are played, a small default buffer time of 10 milliseconds (0.01 second) is used even if the buffer time is set to 0. If stream data arrives more slowly than it is being played, eventually the buffer will empty and playing will stop until the buffer refills. With a fast connection and low data rates, this may never happen. However, if data arrives too slowly to be continuously played, the stream may start and stop repeatedly. Setting the buffer time to a higher value will guarantee that longer sections of a stream play continuously.

If a recorded stream is played and then the same NetStream object is used to play a live stream, a buffer time will still be set on the stream. Clear it by setting the buffer time to 0.

To set the buffer time for a stream, use NetStream.setBufferTime( ) :

 in_ns.setBufferTime(10); 

To determine the number of seconds of data currently in the buffer, use the NetStream.bufferLength property. During testing, you can watch the buffer length change with a simple test script that displays the length in a text field:

 in_ns.play(streamName); in_ns.setBufferTime(10); function onEnterFrame(  ) {   bufferLength_txt.text = in_ns.bufferLength; } 

The bufferLength property is often used to set the size of a progress bar after buffering begins. In fact, it is often a good idea to display a movie clip that shows a progress bar whenever the buffer is empty until the buffer refills and then hide the progress bar. When a stream stops, the user will see the progress bar gradually increase until it reaches 100%, and then it will disappear when the stream starts to play again. An exception occurs if the stream is buffering very near the end. It may not need to completely fill the buffer in order to play to the end. One way to determine when to show or hide a buffering progress indicator movie clip is to watch for three buffer- related code values in the information objects passed to onStatus( ) , namely "NetStream.Buffer.Empty", "NetStream.Buffer.Full", and "NetStream.Play.Stop". "NetStream.Play.Stop" indicates that buffering is complete when the stream is near the end and does not need to fill the buffer completely before starting to play.

5.4.7. Detecting the End of the Stream

When the publisher of a live stream stops publishing a stream, the onStatus( ) method of any subscribing NetStream objects are called and passed an information object with a code value of "NetStream.Play.UnpublishNotify". So there is no ambiguity about when a live stream ends. However, when a recorded stream is being played, two things happen. First, the server stops sending data to the Flash movie. Second, any data still in the stream buffer continues playing within the Flash movie until the buffer is empty. When the server stops sending data to the movie, onStatus( ) is passed a code value of "NetStream.Play.Stop". Then, when the buffer empties shortly afterward, onStatus( ) is called again with a code value of "NetStream.Buffer.Empty". The following output shows the information objects' code values during playback of a stream from start to finish:

 NetStream.Play.Reset NetStream.Play.Start NetStream.Buffer.Full NetStream.Buffer.Empty NetStream.Buffer.Full NetStream.Play.Stop NetStream.Buffer.Empty 

Someone viewing this stream does not see or hear it start to play until the first "NetStream.Buffer.Full" value occurs. The stream stops playing when the first "NetStream.Buffer.Empty" value is returned and does not start again until the second "NetStream.Buffer.Full" value is returned. Finally, the stream stops playing completely after the last "NetStream.Buffer.Empty" value occurs.

5.4.8. Seeking Within a Stream

When a recorded stream is being played, NetStream.seek( ) can be used to jump to any time within the recorded stream and begin playing the stream from that point. The seek( ) method takes one required parameterthe number of seconds from the beginning of the stream to seek to. For example, this statement stops the playing stream and restarts it 20 seconds from the beginning of the recorded stream:

 in_ns.seek(20); 

Calling seek( ) empties the buffer and restarts buffering of the stream. Consequently, the following code values will be passed in three successive onStatus( ) handler calls:

 NetStream.Seek.Notify NetStream.Play.Start NetStream.Buffer.Full 

The stream will not visibly or audibly begin to play again until the "NetStream.Buffer.Full" code value is received. If the seek cannot be carried out, a code value of "NetStream.Seek.Failed" will be returned.

To seek to another time in the stream relative to the current stream time, convert it to absolute time by adding the NetStream.time property as an offset. For example, to seek to 5 seconds before the current time, subtract 5 from the current time to get the absolute time:

 relativeSeekTime = -5; in_ns.seek(in_ns.time + relativeSeekTime); 

Compressed video data is almost always made up of keyframes separated by difference frames. Most recorded video frames are therefore not full video frames but rather updates from the last keyframe. When seeking within a stream, the requested time usually does not fall on a keyframe. When this happens, FlashCom's default behavior is to start playing the stream from the closest preceding keyframe. The alternative is to have FlashCom seek to the previous keyframe, read the difference frames up until the seek time, and construct a new video frame dynamically. This process is called enhanced seeking and can be enabled using an Application.xml file's <EnhancedSeek> flag. By default, enhanced seeking is disabled because it is a more computationally expensive operation. To turn on enhanced seek for one application, place an Application.xml file in the home directory of an application with the following tag:

 <EnhancedSeek>true</EnhancedSeek> 

Using enhanced seeking for all applications is not recommended because it requires the server to perform the extra work of reading the preceding frames to create the current frame. For a complete description of the Application.xml and other XML configuration files, consult Managing Flash Communication Server , available at:

http://www.macromedia.com/support/flashcom/documentation.html

5.4.9. Pausing and Unpausing a Stream

The NetStream.pause( ) method can be used to pause and unpause playback of a recorded stream. When called without any parameters, it toggles between pausing and playing the stream. Calling pause(true) pauses the stream; calling pause(false) resumes playback. Unfortunately, when a stream is paused, the stream's buffer empties, so that when the stream is unpaused, the stream must refill the buffer before it can begin playing again. When a stream is paused and unpaused, NetStream.onStatus( ) is called four times. The information objects' code values will be:

 NetStream.Pause.Notify NetStream.Unpause.Notify NetStream.Play.Start NetStream.Buffer.Full 

After the "NetStream.Buffer.Full" code value is received, the stream will appear to play to the user. If seek( ) is called while a stream is paused, the current stream time changes to reflect the seek( ) request and the video frame at the seek time is displayed. When the stream is unpaused, the stream plays starting from the new time. In such a case, the information objects' code values received in succession by onStatus( ) are:

 NetStream.Pause.Notify NetStream.Seek.Notify NetStream.Play.Start NetStream.Unpause.Notify NetStream.Play.Start NetStream.Buffer.Full 

In between "NetStream.Pause.Notify" and "NetStream.Unpause.Notify", the "NetStream.Seek.Notify" code indicates the seek operation succeeded and "NetStream.Play.Start" indicates that the server sent data to the subscriber. No buffer-related message occurs between the pause codes.

5.4.10. Putting the User in Control

A common task in developing Flash communication applications is creating an audio/video stream player with volume, mute, pause, stop, start, and seek/progress controls and a pop-up buffering indicator. It is also desirable to visibly disable and enable controls or otherwise change their text or appearance depending on the state of an application. To make everything work properly, an application has to know the current state of the stream, be informed when it changes, and be able to track requests made of a NetStream object. Most of the work of interpreting what a stream is doing begins in the onStatus( ) handler. The information objects that are passed into onStatus( ) provide most of the low-level information about a stream; however, they do not describe the state of the stream at the level an application may need. For example, in order to know when to pop up a movie clip that shows the buffering status of the stream, we need to know when the stream has paused in order to refill its buffer. No single information object delivered to the onStatus( ) handler indicates when the stream has paused to refill its buffer. So, before looking at writing a complete onStatus( ) handler, let's look at some common problems in writing one designed to help a stream playback application determine what a stream is doing.

5.4.10.1 NetStream.Buffer.Empty

When the code property of an information object indicates the buffer is empty, either the stream has played through to the end, or the stream has simply run out of data and must pause to refill the buffer. How do you tell the difference? A first good answer starts with the recognition that, when the stream has played to the end, the "NetStream.Play.Stop" message precedes the "NetStream.Buffer.Empty" one. We might try to differentiate between the two states by introducing a stopped property to act as a flag as follows:

 in_ns.onStatus = function (info) {   writeln(info.code);   switch (info.code) {     case "NetStream.Play.Stop":       this.stopped = true;       break;     case "NetStream.Buffer.Empty":       if (this.stopped) {         this.reportChange("playingAtEnd");       }       else {         this.reportChange("buffering");       }       break;     case "NetStream.Buffer.Full":       this.reportChange("playing");       break;   } }; 

In this code snippet, a function named reportChange( ) is passed a string that indicates the current state of the stream. In turn, reportChange( ) will pass on the message to another object that is responsible for controlling the streamsomething I'll discuss later. For now, we just want to properly inform some other object of what the stream is doing. Now imagine that the user watches the stream through to the end so that "NetStream.Play.Stop" and "NetStream.Buffer.Empty" code values are passed to onStatus( ) . The onStatus( ) handler will correctly report that the stream has played through to the end. Now, what happens if the user restarts the stream? Whenever the buffer empties from now on, onStatus( ) remembers that it has seen a "NetStream.Play.Stop" code and incorrectly reports that the stream has played to the end. The solution is to add another case clause to reset the stopped flag when the stream plays:

 case "NetStream.Play.Start":   this.stopped = false;   break; 

5.4.10.2 NetStream.Seek.Notify

When a seek occurs while a stream is playing, the buffer is emptied and the stream stops until the seek is complete and the buffer is refilled. However, there is no "NetStream.Buffer.Empty" message to indicate the buffer is empty in this situation. So, the "NetStream.Seek.Notify" message must serve the same purpose. But there is a complication! If the stream is paused when the user seeks to a new location, the stream does not start to fill the buffer (the buffer fills only when the stream is playing). So just as an empty buffer message may mean more than one thing, the notify message alone does not indicate the state of the stream. What to do? Again we can resort to tracking whether the stream is paused by setting a paused property, which we add to the NetStream object. We can therefore add case clauses as follows:

 case "NetStream.Seek.Notify":   if (!this.paused) {      this.reportChange("buffering");   }   break; case "NetStream.Pause.Notify":   this.paused = true;   this.reportChange("paused");   break; case "NetStream.Unpause.Notify":   this.paused = false;   break; 

With six case clauses in our switch statement so far, you may wonder how far we have to go to write an onStatus( ) handler that will reliably describe what a NetStream is really doing while subscribed to a recorded stream. The answer is that it depends on what your application needs to do. But rather than writing numerous onStatus( ) handlers for different applications and run the risk of mishandling some events, you should write one onStatus( ) handler that handles all the events related to playing a recorded stream. In fact, for convenience, a good way to do this is to create a NetStream subclass that can be used in many different applications. Example 5-5 shows one way to create a NetStream subclass using ActionScript 1.0-style syntax.

Example 5-5. A NetStream subclass for use in subscribing to recorded streams
 function NetStreamPlaybackClass (nc) {   this.stopped  = false;   this.paused   = false;   super(nc); } NetStreamPlaybackClass.prototype = new NetStream( ); NetStreamPlaybackClass.prototype.play = function (streamURI, start,                                                   length, flushPlaylists) {   // Preprocess parameters if necessary.   super.play.apply(this, arguments); }; NetStreamPlaybackClass.prototype.pause = function (flag) {   // Preprocess parameters if necessary.   if (flag != undefined) {     this.paused = flag;   }   else {     this.paused = !this.paused;   }   super.pause.apply(this, arguments);   // Notify immediately.   if (this.paused) {     this.onStatus({code: "NetStream.Pause.Notify", level: "status"});   } }; NetStreamPlaybackClass.prototype.reportChange = function (state) {   this.state = state;   this.obj[this.method](state, this); }; NetStreamPlaybackClass.prototype.onStatus = function (info) {   this.lastInfo = info;   if (info.level != "status") {     this.reportChange(info.level);     return;   }   switch (info.code) {     case "NetStream.Play.Start":       if (!this.paused)         this.reportChange("buffering");         this.stopped = false;       break;     case "NetStream.Play.Stop":       if (this.bufferLength == 0) {         this.reportChange("playingAtEnd");       }       else {         this.reportChange("playing");       }       this.stopped = true;       break;     case "NetStream.Buffer.Empty":       if (this.stopped) {         this.reportChange("playingAtEnd");       }       else {         this.reportChange("buffering");       }       break;     case "NetStream.Buffer.Full":       this.reportChange("playing");       break;     case "NetStream.Seek.Notify":       if (!this.paused) {         this.reportChange("buffering");       }       else {         this.reportChange("seeking");       }       break;     case "NetStream.Pause.Notify":       this.paused = true;       this.reportChange("paused");       break;     case "NetStream.Unpause.Notify":       this.paused = false;       break;   } }; //   setChangeHandler( )   must be passed a method and an object. NetStreamPlaybackClass.prototype.setChangeHandler = function (method, obj) {   this.method = method;   this.obj = obj; }; NetStreamPlaybackClass.prototype.getLastInfo = function ( ) {   return this.lastInfo; }; 

The NetStreamPlaybackClass subclass defined in Example 5-5 operates behind the scenes but does not directly affect an application's UI. On its own, it does not enable or disable buttons or move sliders; instead, it tells an object that registers itself as a change handler what the stream is doing. To tie together button components , a custom slider, or other interface elements with NetStreamPlaybackClass , a good design choice is to create an object that acts as a mediator (sometimes called a director) between the interface controls and a NetStream object or NetStream subclass. To be effective, a mediator object must register itself as the change handler for all the interface elements and for the NetStreamPlaybackClass instance. When a change notification arrives from another object, the mediator must determine how to respond by manipulating the stream or interface elements. This design has the advantage that all state management code is centralized in one place. Example 5-6 shows how a mediator object works with two movie clips buffer_mc , a clip that shows a progress bar, and seek_mc , a clip that contains a slider. The example shows a partial listing for a mediator class named PlayBackDirector and some of the code used to connect it to other objects. The complete code for this example is available on the book's web site.

Example 5-6. Partial listing of the PlayBackDirector class
 function PlayBackDirector (  ) {   this.soundOn = true;   this.lastStateName = ""; } PlayBackDirector.prototype.playing = function (obj) {   // Hide the buffer status movie clip.   delete buffer_mc.onEnterFrame;   buffer_mc._visible = false;   // Start tracking the stream time.   seek_mc.onEnterFrame = function ( ) {     this.setPosition(stream.time);   }   // Set the buttons to the right state.   play_pb.setEnabled(true);   play_pb.setLabel("Stop");   pause_pb.setEnabled(true);   pause_pb.setLabel("Pause"); }; PlayBackDirector.prototype.buffering = function (obj) {   // Show the buffer status movie clip.   buffer_mc.setMaxValue(stream.bufferTime)   buffer_mc.onEnterFrame = function ( ) {     this.setPosition(stream.bufferLength);   }   buffer_mc._visible = true;   // Stop tracking the stream time.   delete seek_mc.onEnterFrame;   // Set the buttons to the right state.   play_pb.setEnabled(true);   play_pb.setLabel("Stop");   pause_pb.setEnabled(true);   pause_pb.setLabel("Pause"); }; PlayBackDirector.prototype.onStreamChange = function (stateName, obj) {   if (stateName == this.lastStateName) return;   this.lastStateName = stateName;   this[stateName](obj); }; // This handler calls   seek( )   on the stream and disables the slider until // the stream starts to play again. PlayBackDirector.prototype.onSliderChange = function (pos, obj) {   delete seek_mc.onEnterFrame;   stream.seek(pos); }; PlayBackDirector.prototype.onPlayRequest = function (btn) {   if (btn.getLabel( ) == "Play") {     this.playing(stream);     stream.play("public/Ryerson_High_Speed");   }   else {     stream.close( );     btn.setLabel("Play");     delete seek_mc.onEnterFrame;     delete buffer_mc.onEnterFrame;     buffer_mc._visible = false;     pause_pb.setEnabled(false);   } }; 

The onStreamChange( ) method is called whenever the state of the stream object changes. For example, if onStreamChange( ) is passed a stateName value of "buffering", PlayBackDirector.buffering( ) is called. The buffering( ) method is responsible for setting everything controlled by PlayBackDirector to the correct state when the stream is buffering. The buffer_mc clip's range is adjusted to reflect the stream buffer's time, an onEnterFrame( ) method is created for the clip so that on each frame it will show the current buffer length, and finally, the buffer_mc clip is made visible. On the other hand, if a "playing" message arrives, the buffer_mc clip is hidden and its onEnterFrame( ) method is deleted. Ideally, there should be one method for each stream state, which guarantees that all the objects and movie clips controlled by PlayBackDirector always respond appropriately to changes in stream state.

As of Flash Player 6.0.79.0, "NetStream.Buffer.Empty" events are not always generated when a stream pauses to buffer before continuing to play. A version of NetStreamPlaybackClass is provided on the book's web site that fixes the problem by regularly checking whether the stream is playing and calling the onStatus( ) method with a "NetStream.Buffer.Empty" message if it has paused to buffer. Macromedia expects to fix this for Flash Player 8, due to be released in the second half of 2005.


5.4.11. Managing Bandwidth

Matching the size of the data stream to the client's available bandwidth is the most important factor in determining how well a stream plays for any user. FlashCom cannot further compress a stream to suit the bandwidth available for each client or interpolate video frames to lower resolutions to cope with lower-bandwidth connections. As will be discussed in Chapter 7, the best way to tailor a stream to different bandwidths is to create separate streams for typical connection speeds. Often this means creating one stream for modem users, another for DSL users, and possibly one for users on a LAN. However, once a stream is selected for playing by a client, there are still some things beyond buffering that can be done to improve playback performance when bandwidth is less than optimal.

5.4.11.1 Shutting off audio or video

The receiveAudio( ) and receiveVideo( ) methods of a NetStream object can be used to stop and start the server sending audio or video in a stream. Both methods are passed a Boolean value that determines whether audio or video data should be sent by the server. Example 5-7 shows a method that is called when an audio Mute button is clicked.

Example 5-7. A method for toggling sound on and off
 PlayBackDirector.prototype.onMuteAudioRequest = function (obj) {   this.soundOn = !this.soundOn;   stream.receiveAudio(this.soundOn);   if (this.soundOn) {     muteAudio_pb.setLabel("Mute Audio");   }   else {     muteAudio_pb.setLabel("Restore Audio");   } }; 

When a stream is already playing and the audio is muted by instructing the server to stop sending audio, the audio will not stop playing in the client immediately if there is still audio data in the stream's buffer. Similarly, if video data is in a buffer when a request to restart sending audio data is received, it may be some time before audio is heard on the client system. Stopping the server from sending video using receiveVideo(false) may improve stream playback performance. Without the need to download and buffer video data, audio and ActionScript data can fill the buffer faster, leading to fewer pauses for buffering.

5.4.11.2 Capping client bandwidth usage

The maximum bandwidth a client can use to send or receive data is capped by the server according to the <Bandwith> , <ServerToClient> , and <ClientToServer> tags in the Application.xml file that controls an application. The default settings allow a client to send 250,000 bytes/sec (244 KB/s) to the server as well as to receive 250,000 bytes/sec. A client connected via modem is likely to be able to send and receive much less dataoften less than 4000 bytes/second. When a client is subscribed to a stream, FlashCom attempts to monitor network traffic and drops video frames as necessary. To help the server determine the bandwidth available, each client's bandwidth limit can be set using Client.setBandwidthLimit( ) , as shown in Example 5-8.

Example 5-8. Server-side script that relies on a connection type string being passed to the server from the client when it connects
 connectionSettings = {   modem: {up: 4000, down: 4000},   dsl:   {up: 16000, down: 62000},   lan:   {up: 1250000, down: 10000000} } application.onConnect = function (client, connectionType) {   var setting = connectionSettings[connectionType];   if (setting) {     client.setBandwidthLimit(setting.up, setting.down);   }   return true; }; 

5.4.11.3 Conferences and bandwidth

Live conferences in which users see and hear one another simultaneously can create a heavy processing load on each Flash movie and the FlashCom Server hosting the conference, and tax the bandwidth available to the client and server. The processing and bandwidth load for each client scales linearly with the number of participants . The load and bandwidth demands on the server increase as the square of the number of clients . Figure 5-1 shows the outgoing stream from each client and the incoming streams for small conferences of two or three participants.

Figure 5-3. Two- and three-client conferences: the two-client conference requires four streams; the three-client conference, nine streams

Each client must send one stream to the conference instance and receive as many streams as there are other participants. In other words, if there are n participants, each will have to send one and receive n - 1 streams:

 Total client streams = 1 + n - 1 = n 

The server will receive a stream from each client and send out to each client one stream from all the other clients. Therefore, the total number of streams handled by the server will be:

 Total server streams = n * (1 + n - 1) = n  2  

Conferences with too many participantsall sending and receiving audio and videocan quickly swamp some clients with data and may contribute to bandwidth and load problems on the server. There is no magic solution to this problemit is the nature of live conferences. When designing and hosting conference systems, consider making sure that clients do not attempt to send more audio and video data than absolutely necessary, that there is a limit on the number of participants, or that a token passing system is used to restrict the number of clients actually sending audio and video at one time. Assuming each video stream requires 8 KB/s, Table 5-1 shows how bandwidth demand increases per client connection.

Table 5-1. Bandwidth demands for number of client connections

Number of clients

Client upstream demand

Client downstream demand

Server demand

2

8 KB/s

8 KB/s

32 KB/s

3

8 KB/s

16 KB/s

72 KB/s

4

8 KB/s

24 KB/s

128 KB/s

5

8 KB/s

32 KB/s

200 KB/s

10

8 KB/s

72 KB/s

800 KB/s

20

8 KB/s

152 KB/s

3.2 MB/s

50

8 KB/s

392 KB/s

20 MB/s




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