Accessibility

Flash Communication Server Article

 

Delivering Flash Video: Dynamic Bandwidth Detection with Macromedia Flash Communication Server

Stefan Richter

FlashComGuru
POPview
Team Macromedia

In this tutorial you will learn how to detect a specific user's download speed using Flash Communication Server MX. You will learn how to set up the detection routine and use the test results to serve a video clip that is suitable for the user's connection speed.

The sample file bwcheck.fla contains a simple video streaming application as your starting point (I refer to it as your main application) and during the course of this article you will extend it to use the speed test results that the detection routine gathers. This article focuses on implementing the detection routine, not the video playback application.

This article is based on a script by Pritham Shetty, director of engineering for Flash Communication Server, and was published by Giacomo "Peldi" Guilizzoni on his blog at www.peldi.com/blog/.

Requirements

To complete this tutorial you will need to install the following software and files:

Flash Communication Server (version 1.5 updater 2)

Note: If you need to obtain the updated version, download it from the Macromedia Flash Communication Server Support Center.

Flash MX Professional 2004

Tutorials and sample files:

Prerequisite knowledge:

You should be familiar with ActionScript, including basic server-side ActionScript. You should also have a basic understanding of Flash Communication Server MX. This tutorial does not require you to write much code; instead, it encourages you to modify the provided source files to suit your needs.

Setting Up the Server-Side Application

First create the bwcheck folder:

  1. Create a folder by the name of bwcheck inside your Flash Communication Server application folder. On Windows, this folder's default location is:

    C:\Program Files\Macromedia\Flash Communication Server MX\applications

  2. Copy bwcheck.asc into the bwcheck folder.

Next create the videoplayer folder:

  1. Create a folder called videoplayer in the same location inside the applications folder.
  2. Inside the videoplayer folder, create a folder called streams.
  3. Inside the streams folder, create another folder called _definst_.
  4. Copy the two FLV files (100kbps_Stream.flv and 256kbps_Stream.flv) included in the sample file download into the _definst_ folder.

Your folder structure should now look like Figure 1.

Server-side folder structure

Figure 1. Server-side folder structure

The reason why you deploy to separate applications is obvious: It leaves you more flexible when other applications also want to access the bwcheck application. Rather than integrating the bandwidth-check code into each and every new application, you simply connect to bwcheck, gather the test results, and disconnect again. Your SWF file holds the data and connects to your main application, leaving you with only minimal scripting to do on the client side and no server-side scripting left to do at all.

Creating the Client-Side Application

Now you can create the application to run on the client using the Macromedia Flash authoring tool:

  1. Create a new folder for your application; I have named mine "bwcheck" but you can name yours differently if you like.
  2. Copy bwcheck.fla into the newly created folder and open the file for editing in Flash.
  3. Highlight Frame 1 of the Actions layer and bring up the Actions panel.

You will see the following code:

1 nc = new NetConnection();
2 nc.onStatus = function(info) {
3 	trace("Level: "+info.level+" Code: "+info.code);
4	if (info.code == "NetConnection.Connect.Success") {
5		trace("--- connected to: " + this.uri);
6		initStream();
7	}
8 };
9 nc.connect("rtmp:/videoplayer");
10
11 initStream = function(){
12	// create a NetStream
13	video_ns = new NetStream(nc);
14	// optional onStatus handler
15	video_ns.onStatus = function(info) {
16		trace("Level: " + info.level + " Code: " + info.code);
17	};
18	// attach the NetStream to a video object called videoObj on stage
19	videoObj.attachVideo(video_ns);
20	video_ns.play("100kbps_Stream", 0); // the parameter 0 will tell the server to play a recorded stream
21 }
22 stop();

This piece of code is what I referred to earlier as the main application. It establishes a NetConnection to the videoplayer application (Lines 1–9). Once connected (Lines 4–7), it calls the initStream method, which in turn creates a new NetStream (Line 13), attaches it to the video object on the Stage (Line 19), and starts playback of the file 100kbps_Stream (Line 20). Note that I omit the .flv extension when referring to the 100kbps_Stream.flv file.

To test the video playback, select Control > Test Movie from the menu or press Control+Enter. The video should play. Should you have problems playing the video, the traces in the Output window may give you some clues to help debugging.

Adding the bwcheck Application to the Mix

Configure this client-side videoplayer using the bandwidth test results from the bwcheck application. Create a new keyframe on Frame 2 and move the existing code into it (see Figure 2).

Actions moved to Frame 2

Figure 2. Actions moved to Frame 2

Now add the following code to Frame 1 of the Actions layer:

1  nc = new NetConnection();
2  nc.onStatus = function(info) {
3  	trace("Level: " + info.level + " Code: " + info.code);
4 	if (info.code == "NetConnection.Connect.Success") {
5 		trace("--- connected to: " + this.uri);
7 	} 
8  };
9  NetConnection.prototype.onBWDone = function(p_bw) {
10 	trace("onBWDone: "+p_bw);
11 }
12 NetConnection.prototype.onBWCheck = function() {
13 	return ++counter; // Serverside, just ignore any return value and return the call count
14 }
15 nc.connect("rtmp:/bwcheck", true);
16 stop();

This code creates a new NetConnection (Line 1) and adds an onStatus handler to it (Lines 2–8). The actual connect call happens on Line 15. Notice that I pass an additional parameter of true in my connect method—this triggers the bandwidth check on the server side. Should you not want to run the speed test upon connection, you should pass false instead. If you pass false, however, the onBWDone method will still be invoked with a value of undefined for p_bw, indicating that no speed test took place. You may simply ignore this value and proceed with your application logic.

Triggering the Speed Test

You may opt to run the speed test at a later time in your application; maybe the download speed is only important to you after a user has proceeded past a certain point in your application. To trigger the speed test, you can invoke the server-side checkBandwidth method like this:

nc.call("checkBandwidth", null);

The corresponding method on the server-side client object that gets triggered by this is the following:

Client.prototype.checkBandwidth = function() {
	application.calculateClientBw(this);
}

In any case, you must establish a NetConnection to the bwcheck application before attempting to run the speed test.

You are now ready to test the movie. Select Control > Test Movie from the menu or press Control+Enter. You should see some traces in the Output window. About two seconds after the successful connection, the onBWCheck method is triggered and the speed test is complete. You can now proceed and use the returned speed results to configure your existing application.

Integrating the Detection Routine into Existing Applications

As you have already learned, the speed test application returns the variable p_bw. This variable holds a value equal to the detected download speed in kilobits per second. Your next step is to use this value to configure your existing application.

Add the following code after Line 10 of Frame 1 in your FLA file:

detected_bw = p_bw;
// close the NetConnection to bwcheck
this.close();
gotoAndStop(2);

This code makes the test results available to your main Timeline inside the variable named detected_bw, closes your NetConnection, and advances the playhead to Frame 2 where the actual video playback occurs.

For best results, your application should serve video that is encoded at a rate less than or equal to kbitsDown. You can also use this value to set up your buffer time. For example, if you have a high-bandwidth connection, you can set a two-second buffer—this gives a quick start. If bandwidth is close to the encoded rate, picking a larger buffer results in a smoother experience even if there is a network hiccup.

For this article I have encoded a video clip into two different bit rates using Sorenson Squeeze (one at 100kbps, the other at 256kbps). I ended up with two FLV files called 100kbps_Stream.flv and 256kbps_Stream.flv.

To request the appropriate stream, you need to modify the code of the initStream method on Frame 2. Add the following code after Line 11:

if(p_bw >= 256){
	useVideo = "256kbps_Stream";
	bufferlength = 2;
} else if (p_bw <256 && p_bw >=100){
	useVideo = "100kbps_Stream";
	bufferlength = 3;
} else {
	// bandwidth is too low, you best alert the user at this stage
	trace("bandwidth too low");
}

After the onstatus handler, add the following:

video_ns.onStatus = function(info) {
	trace("Level: " + info.level + " Code: " + info.code);
};

Add this code to set the bufferlength:

video_ns.setBufferTime(bufferlength);

Also modify the play action by changing that line from this:

video_ns.play("100kbps_Stream", 0);

to this:

video_ns.play(useVideo, 0);

That's it! Press Control+Enter again and you will see the detected bandwidth speed in your Output window. This number will be pretty high if you are playing this locally, so sit back and enjoy some top-quality skateboard action (see Figure 3). You deserve it!

Screen shot of the completed application

Figure 3. Screen shot of the completed application

The approach I describe for detecting the client download speed can also be used to detect client upload speed for a multiway application such as a video chat. I won't go into detail here but you can find the original code on Peldi's blog. The exact URL is: www.peldi.com/blog/archives/2004/01/automatically_c.html.

Where to Go from Here

As you can see from the examples in this article, you can greatly enhance the user experience by gathering and using data acquired by a speed test—and this one takes less than two seconds to complete. Keep in mind that test results can be affected by other applications accessing the Internet. Therefore, the results may be much lower than the actual connection speed. Remember this when planning your applications.

Another point to note is that this test does not return the actual or theoretical speed of your user's line. Instead, it determines how fast data can be transferred through the user's machine and the Flash Communication Server to which the SWF is connecting. That's a small but important difference.

Feel free to contact me with any questions you might have, or visit my forum at www.flashcomguru.com/forum, where you will also find a thread accompanying this article.

About the author

Stefan Richter is a Certified Flash Developer who has been involved with Flash Media Server since its very early days, when it was Flash Communication Server. Shortly after relocating from Germany to the UK, he became the driving force behind Europe's first FCS-centric hosting company, flashcomstudio.com. Stefan now runs his own popular community at flashcomguru.com. Having moved on from his role as developer and webmaster at Monster Worldwide, he now holds the position of VP of application development at Dallas-based POPview. From his home office in the UK he is handling projects for a variety of clients including McAfee, USA Network, and Unilever.