Home » Code Samples, Flash, FMS, Tutorials, Uncategorized

Flash Media Server Streaming Speed Testing [Part 1] – Detect Upload, Download, and Latency Speeds

12 May 2009 | 18,932 views | 10 Comments


This tutorial is the first in a series of three that builds upon the first Flash Bandwidth & Port Detection tutorial I created a while back. These three articles look to make it even easier than the original post, and go more in depth on some of the streaming options, and have better structured server side code.

If you are interested in the original article, you can find it here.

Bandwidth detection is most important when connecting your users to the correctly compressed media to be streamed, recorded, or delivered via a Flash Media Server. This example will walk you through the process of being able to detect a users download bandwidth, upload bandwidth, and the latency between the client computer and the host server. All of the code is Actionscript 2.0, and I’ve setup a Flash Media Server 3.5 to host the server side scripting. I use Influxis as my Flash Media Server host for all of these tutorials and smaller examples.

Now, onto the code.

First, lets put together our Flash Actionscript. Here our goal is to setup our connection manager, and create a BandwidthInfo object that will call to our main.asc file (this is explained next) for sending packets to the Flash Media server to the client, and vice versa.

function connectMe() {
	rec_nc = new NetConnection();
	rec_nc.onStatus = function(info) {
		trace("Level: " + info.level + " Code: " + info.code);
		if (info.code == "NetConnection.Connect.Success") {
			trace("connected to: "+this.uri);
			conn_stat.text += "connected to: "+this.uri+newline;
			startTest(rec_nc);
		} else if (info.code == "NetConnection.Connect.Failed" || info.code == "NetConnection.Connect.Closed") {
			trace("no connection to app");
			conn_stat.text += "error connection failed"+newline;
		}
	};
	rec_nc.connect("rtmp://your.host.com/application_name/");
}

function startTest(nc) {
	_global.bwInfo = new BandwidthInfo(nc);
	_global.bwInfo.start();
}

for (i=0; i<1000; i++) {
	data += "C->S";
}
function BandwidthInfo(nc) {
	this.nc = nc;
	this.maxLength = 10;
	this.bwInHistory = new Array(this.maxLength);
	this.bwInCtr = 0;
	this.bwOutHistory = new Array(this.maxLength);
	this.bwOutCtr = 0;
	this.pingHistory = new Array(this.maxLength);
	this.headIn = 0;
	this.headOut = 0;
	this.headPing = 0;
	this.bBWOutStop = false;
	this.bBWInStop = false;
	this.onBWInTimeout = function() {
		clearInterval(this.bwInfoTimeout);
		this.bwInfoTimeout = null;
		delete this.bwInfoTimeout;
		this.bBWInStop = true;
		
		if (this.bwInCtr == 0) {
			conn_stat.text += "unable to receive data from server."+newline;
			this.abort("unable to receive data from server.");
			return;
		}
		
		this.stop();
	};
	this.onBWOutTimeout = function() {
		clearInterval(this.bwInfoTimeout);
		this.bwInfoTimeout = null;
		delete this.bwInfoTimeout;
		this.bBWOutStop = true;
		
		if (this.bwOutCtr == 0) {
			conn_stat.text += "unable to receive data from server"+newline;
			this.abort("unable to send data to server");
			return;
		}
		
		trace("testing bandwidth from server");
		conn_stat.text += "testing bandwidth from server"+newline;
		this.bwInfoTimeout = setInterval(this, "onBWInTimeout", 5*1000);
		this.serverToClient();
	};
	this.clientToServer = function() {
		this.time = getTimer();
		size = 0;
		bwinfo = this;
		this.nc.ack = function(pingVal) {
			if (!bwinfo.bBWOutStop) {
				bwinfo.bwOutHistory[bwinfo.headOut++%bwinfo.maxLength] = Math.floor(size/(getTimer()-bwinfo.time)*1000);
				bwinfo.pingHistory[bwinfo.headPing++%bwinfo.maxLength] = pingVal;
				bwinfo.nc.call("recData", 0, data);
				size += 4000;
				bwinfo.bwOutCtr++;
			}
		};
		this.nc.call("recData", 0, data);
		this.nc.call("recData", 0, data);
	};
	this.serverToClient = function() {
		this.time = getTimer();
		size = 0;
		bwinfo = this;
		nc.onEcho = function() {
			if (!bwinfo.bBWInStop) {
				bwinfo.bwInHistory[bwinfo.headIn++%bwinfo.maxLength] = Math.floor(size/(getTimer()-bwinfo.time)*1000);
				this.call("echoData", 0, 0);
				size += 4000;
				bwinfo.bwInCtr++;
			}
		};
		nc.call("echoData", 0, 0);
		nc.call("echoData", 0, 0);
	};
	this.start = function() {
		conn_stat.text += "testing upload speed"+newline;
		trace("testing upload speed");
		clearInterval(this.bwInfoTimeout);
		this.bwInfoTimeout = null;
		delete this.bwInfoTimeout;
		this.bwInfoTimeout = setInterval(this, "onBWOutTimeout", 5*1000);
		this.clientToServer();
	};
	this.stop = function() {
		this.nc = null;
		var ping_rtt = 0;
		var bw_out = 0;
		var bw_in = 0;
		for (var i = 0; i<this.maxLength && i<this.bwOutCtr; i++) {
			ping_rtt = Math.max(ping_rtt, this.pingHistory[i]);
		}
		for (var i = 0; i<this.maxLength && i<this.bwOutCtr; i++) {
			bw_out += this.bwOutHistory[i];
		}
		bw_out /= Math.min(this.maxLength, this.bwOutCtr);
		bw_out = Math.round((bw_out/1024)*8);
		for (var i = 0; i<this.maxLength && i<this.bwInCtr; i++) {
			bw_in += this.bwInHistory[i];
		}
		bw_in /= Math.min(this.maxLength, this.bwInCtr);
		bw_in = Math.round((bw_in/1024)*8);
		var s;
		s += "bandwidth\n";
		s += "   upstream: "+bw_out+" kbps\n";
		s += "   downstream: "+bw_in+" kbps\n";
		s += "   latency: "+ping_rtt+" ms\n";
		if (ping_rtt>1000) {
			s += "network appears to have a very high delay\n\n";
		}
		if ((bw_in>500) && (bw_out>200)) {
			s += "connection supports high quality speed\n\n";
		} else if ((bw_in>=200) && (bw_out>=100)) {
			s += "connection supports good quality speed\n\n";
		} else if ((bw_in>100) && (bw_out>80)) {//(bw_in<250) && (bw_out<80) && 
			s += "connection supports mid quality speed\n\n";
		} else if ((bw_in>20) && (bw_out>15)) {
			s += "connection supports slow quality speed\n\n";
		} else {
			s += "connection supports very slow quality speed\n\n";
		}
		trace(s);
		conn_stat.text += s+newline;
	};
	this.abort = function(reason) {
		conn_stat.text += "failed "+reason+newline;
		trace("failed "+reason);
	};
}

connectMe();

In order to get the above to work correctly with your own Flash Media Server, you simply need to edit the following line.

rec_nc.connect("rtmp://your.host.com/application_name/");

We now have our Flash file ready, but we still need our application setup on the Flash Server. Create a new application on your Flash Media Server, and name it however you want. That application name then goes into the connect function above, along with the url for the server.

Next, we want to create the server side code that will allow our final swf to call server side functions in order to return the correct upload, download, and latency values.

application.onAppStart = function (info){
	for ( i = 0; i < 500; i++ ) {
		data += "S->C";
	}
	Client.prototype.recData = function(data) {
		this.ping();
		var v = this.getStats();
		this.call("ack", 0, v.ping_rtt);
	}
	Client.prototype.echoData = function() {
		this.call("onEcho", 0, data);
	};
	Client.prototype.getBWInfo = function() {
		return this.getStats();
	};
	Client.prototype.onConnTimeout = function(){
		clearInterval( this.connTimeout );
		this.connTimeout = null;
		application.disconnect(this);
	}
}
application.onConnect = function(client_obj, id) {
	application.acceptConnection(client_obj);
}

Download the source

Next, take a look at how you can use the above code along with Port Detection to be sure that your users can connect to your Flash Media Server applications from behind certain firewall setting in the part two of this three part tutorial series.

Here are the links for this tutorial in its entirety:

Flash Media Server Streaming Speed Testing [Part 1] – Detect Upload, Download, and Latency Speeds

Flash Media Server Streaming Speed Testing [Part 2] – Detect Upload, Download, and Latency Speeds, and Port Connection

Flash Media Server Streaming Speed Testing [Part 3] – Compare Multiple Server Resources, Port Connections, Detect Upload, Download, and Latency Speed

  • Venkat B

    can u give me in as3 ?

  • SandraMillhouse

    Can't wait to read the second part…

  • http://www.derekentringer.com Derek J Entringer

    @Ajith

    Right now, this tutorial is only geared for Flash and AS2.

  • http://www.derekentringer.com Derek J Entringer

    @Ajith

    Right now, this tutorial is only geared for Flash and AS2.

  • Ajith

    Hi,
    Thanks for the tutorial.
    Do you have flex code for the same.

    Regards,
    Ajith

  • Pingback: Flash Bandwidth & Port Detection | Derek J Entringer | Interactive Media & Web Application Developer

  • http://www.derekentringer.com Derek J Entringer

    @Jack (Flash Scope)

    Thanks! I’ll take a look at getting a membership. Great looking site by the way. Looks like it is shaping up to be a good resource for the community.

  • http://www.derekentringer.com/ Derek J Entringer

    @Jack (Flash Scope)

    Thanks! I’ll take a look at getting a membership. Great looking site by the way. Looks like it is shaping up to be a good resource for the community.

  • http://www.flashscope.com Flash Scope

    Thanks for the tutorial.
    We submitted a link to your tutorial to FlashScope.com and promoted it the front page
    (http://www.flashscope.com/All/Flash-Media-Server-Streaming-Speed-Testing-Part-1)
    If you are interested in promoting your articles on Flash Scope – Flash components and Flash developers resource, you are welcome to join the community!

    Regards,
    Jack (Flash Scope)

  • http://www.flashscope.com/ Flash Scope

    Thanks for the tutorial.
    We submitted a link to your tutorial to FlashScope.com and promoted it the front page
    (http://www.flashscope.com/All/Flash-Media-Server-Streaming-Speed-Testing-Part-1)
    If you are interested in promoting your articles on Flash Scope – Flash components and Flash developers resource, you are welcome to join the community!

    Regards,
    Jack (Flash Scope)