Code:Server bandwidthdetection
From Red5Tutorials
Port of bandwidth detection from FMS to Red5 by Dan Rossi (2007/02/08)
Dan writes: Hi there ive taken the initiative because i cant seem to find any other solution out there to port the FMS code. Ive prob made alot of mistakes in terms of the HashMap and arrays. I had to also port the client.getStats method from FMS which returns information in methods returned from red5 but not easily accessible via variables, maybe there is a better way of accessing this until the stats API is avail ?
Basically all it does is send a packet array and calculate the result time to work out the download rate or something like that ? Ive yet to manage to get the right calculation for the download rate as i was testing it locally, i dont think that one is right. If someone wants to clean it up go ahead :)
Port of bandwidth detection from FMS to Red5 by Dan Rossi (2007/03/23)
Dan Writes: "Hi it seems there has been an update on the wowza list to the bandwidth detection source, the current implementation sends packets 3 times which creates a tiny lag on startup, the calculations are more correct, so ive migrated and merged some of the code. Full credits here
http://www.wowzamedia.com/forums/showthread.php?t=169
Ive supplied a quick flex example and tarball source code aswell as the flex source, it would be good to find out how to optimise it a little better, maybe a better way to send AMF packet byte data, aswell as storing the hashmaps with the stats information for the beginning and end. Finally it would be good to implement into a bean you just add into the xml config and it does the bandwidth detection in the onconnect method ?
If you keep refreshing you may see that the numbers vary so its not enitely exact. It would be good to know if there is a network framework that is capable of doing these kind of calculations ? As this code still seems abit clunky and can be better as it was a port from the FMS code to begin with anyway.
http://www.electroteque.org:5080/bwcheck/bwcheck.html http://www.electroteque.org:5080/bwcheck/bwcheck.mxml http://www.electroteque.org:5080/bwcheck/bwcheck.tar.gz "
Calling example
public boolean appConnect(IConnection conn) {
BandwidthDetection detect = new BandwidthDetection();
detect.checkBandwidth(conn);
}
Class IBandwidthDetection.java
import org.red5.server.api.IConnection;
public interface IBandwidthDetection {
public void checkBandwidth(IConnection p_client);
public void calculateClientBw(IConnection p_client);
}
Class BandwidthDetection.java:
import org.red5.server.api.IConnection;
import org.red5.server.api.Red5;
import org.red5.server.api.service.IPendingServiceCall;
import org.red5.server.api.service.IPendingServiceCallback;
import org.red5.server.api.service.IServiceCapableConnection;
import org.red5.server.api.stream.IStreamCapableConnection;
import org.apache.log4j.Logger;
import java.util.*;
import java.util.Map;
import java.util.HashMap;
public class BandwidthDetection implements IPendingServiceCallback, IBandwidthDetection {
IConnection client = null;
double latency = 0;
double cumLatency = 1;
int count = 0;
int sent = 0;
double kbitDown = 0;
double deltaDown = 0;
double deltaTime = 0;
List<Long> pakSent = new ArrayList<Long>();
List<Long> pakRecv = new ArrayList<Long>();
private Map<String, Long> beginningValues;
private double[] payload = new double[1200];
private double[] payload_1 = new double[12000];
private double[] payload_2 = new double[12000];
private static final Logger log = Logger.getLogger(BandwidthDetection.class.getName());
public BandwidthDetection()
{
}
public void checkBandwidth(IConnection p_client)
{
this.calculateClientBw(p_client);
}
public void calculateClientBw(IConnection p_client)
{
for (int i=0; i<1200; i++){
payload[i] = Math.random();
}
p_client.setAttribute("payload", payload);
for (int i=0; i<12000; i++){
payload_1[i] = Math.random();
}
p_client.setAttribute("payload_1", payload_1);
for (int i=0; i<12000; i++){
payload_2[i] = Math.random();
}
p_client.setAttribute("payload_2", payload_2);
final IStreamCapableConnection beginningStats = this.getStats();
final Long start = new Long(System.nanoTime()/1000000); //new Long(System.currentTimeMillis());
this.client = p_client;
beginningValues = new HashMap<String, Long>();
beginningValues.put("b_down", beginningStats.getWrittenBytes());
beginningValues.put("b_up", beginningStats.getReadBytes());
beginningValues.put("time", start);
this.pakSent.add(start);
this.sent++;
this.callBWCheck("");
}
/**
* Handle callback from service call.
*/
public void resultReceived(IPendingServiceCall call) {
Long now = new Long(System.nanoTime()/1000000); //new Long(System.currentTimeMillis());
this.pakRecv.add(now);
Long timePassed = (now - this.beginningValues.get("time"));
this.count++;
if (count == 1) {
latency = Math.min(timePassed, 800);
latency = Math.max(latency, 10);
log.info("count: "+count+ " sent: "+sent+" timePassed: "+timePassed+" latency: "+latency);
// We now have a latency figure so can start sending test data.
// Second call. 1st packet sent
pakSent.add(now);
sent++;
this.callBWCheck(this.client.getAttribute("payload"));
}
// To run a very quick test, uncomment the following if statement and comment out the next 3 if statements.
/*
else if (count == 2 && (timePassed < 2000)) {
pakSent.add(now1);
sent++;
cumLatency++;
this.callBWCheck(this.client.getAttribute("payload"));
}
*/
// The following will progressivly increase the size of the packets been sent until 1 second has elapsed.
else if ((count > 1 && count < 3) && (timePassed < 1000)) {
pakSent.add(now);
sent++;
cumLatency++;
this.callBWCheck(this.client.getAttribute("payload"));
} else if ((count >=3 && count < 6) && (timePassed < 1000)) {
pakSent.add(now);
sent++;
cumLatency++;
this.callBWCheck(this.client.getAttribute("payload_1"));
} else if (count >= 6 && (timePassed < 1000)) {
pakSent.add(now);
sent++;
cumLatency++;
this.callBWCheck(this.client.getAttribute("payload_2"));
}
// Time elapsed now do the calcs
else if (sent == count) {
// see if we need to normalize latency
if (latency >= 100) {
// make sure satelite and modem is detected properly
if (pakRecv.get(1) - pakRecv.get(0) > 1000) {
latency = 100;
}
}
this.client.removeAttribute("payload");
this.client.removeAttribute("payload_1");
this.client.removeAttribute("payload_2");
final IStreamCapableConnection endStats = this.getStats();
deltaDown = (endStats.getWrittenBytes() - beginningValues.get("b_down")) * 8 / 1000; // bytes to kbits
deltaTime = ((now - beginningValues.get("time")) - (latency * cumLatency)) / 1000; // total dl time - latency for each packet sent in secs
if (deltaTime <= 0) {
deltaTime = (now - beginningValues.get("time")) / 1000;
}
kbitDown = Math.round(deltaDown / deltaTime); // kbits / sec
log.info("onBWDone: kbitDown = " + kbitDown + ", deltaDown= " + deltaDown + ", deltaTime = " + deltaTime + ", latency = " + this.latency);
this.callBWDone(this.kbitDown, this.deltaDown, this.deltaTime, this.latency);
}
}
private void callBWCheck(Object params)
{
IConnection conn = Red5.getConnectionLocal();
if (conn instanceof IServiceCapableConnection) {
((IServiceCapableConnection) conn).invoke("onBWCheck", new Object[]{params}, this);
}
}
private void callBWDone(double kbitDown, double deltaDown, double deltaTime, double latency)
{
IConnection conn = Red5.getConnectionLocal();
if (conn instanceof IServiceCapableConnection) {
((IServiceCapableConnection) conn).invoke("onBWDone", new Object[]{kbitDown, deltaDown, deltaTime, latency});
}
}
private IStreamCapableConnection getStats()
{
IConnection conn = Red5.getConnectionLocal();
if (conn instanceof IStreamCapableConnection) {
return (IStreamCapableConnection) conn;
}
return null;
}
}
Client Side:
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="onAppComplete(event)" width="602" height="211"> <mx:Script> <![CDATA[ import mx.events.FlexEvent; import flash.net.NetConnection; private var nc:NetConnection; private var serverURL:String = "rtmp://www.electroteque.org/bwcheck"; public function onAppComplete(event:FlexEvent):void { nc = new NetConnection(); nc.client = this; nc.connect(serverURL); } public function onBWCheck(obj:Object):void { trace("Checking Bandwidth"); log.data = "Checking Bandwidth ..... \n\n"; } public function onBWDone(kBDown:String, deltaDown:String, deltaTime:String, latency:String):void { trace("KBDown: " + kBDown + " Delta Down: " + deltaDown + " Delta Time: " + deltaTime + " Latency: " + latency); log.data += "KBDown: " + kBDown + " Delta Down: " + deltaDown + " Delta Time: " + deltaTime + " Latency: " + latency; } ]]> </mx:Script> <mx:TextArea x="0" y="0" width="602" height="211" id="log"/> </mx:Application>

