Note: The standard BWCheck module measures server-to-client bandwidth.
There are two parts:
- Wowza Media Server application module
- Adobe® client applications (Adobe Flex 4 ActionScript 3 and Flash® ActionScript 2 versions)
Server-side application module
Code:
package com.wowza.wms.plugin.bwcheck;
import com.wowza.util.IOPerformanceCounter;
import com.wowza.wms.amf.*;
import com.wowza.wms.client.*;
import com.wowza.wms.module.*;
import com.wowza.wms.request.*;
public class ModuleClientBWCheck extends ModuleBase {
static public void onClientBWCheck(IClient p_client, RequestFunction function, AMFDataList params) {
getLogger().info("onClientBWCheck");
AMFDataObj statValues = new AMFDataObj();;
IOPerformanceCounter stats = p_client.getTotalIOPerformanceCounter();
statValues.put("cOutBytes", new AMFDataItem(stats.getMessagesInBytes()));
statValues.put("cInBytes", new AMFDataItem(stats.getMessagesOutBytes()));
statValues.put("time", new AMFDataItem(params.getLong(PARAM1)));
sendResult(p_client, params, statValues);
}
}
To install the module
- Download wms-plugin-collection.zip and extract the contents from the compressed (zipped) folder.
- Copy the /lib/wms-plugin-collection.jar file in the package to the [install-dir]/lib folder.
- If Wowza Media Server is running, restart the server.
To configure the module
- Create a folder named [install-dir]/applications/[app-name].
- Create a folder named [install-dir]/conf/[app-name].
- Copy the [install-dir]/conf/Application.xml file to the new [install-dir]/conf/[app-name] folder.
- Open the [install-dir]/conf/[app-name]/Application.xml file in a text editor and add the following module reference as the last entry in the <Modules> section (it is important that it be the last entry):
Code:<Module> <Name>ClientBWCheck</Name> <Description>ClientBWCheck</Description> <Class>com.wowza.wms.plugin.collection.module.ModuleClientBWCheck</Class> </Module>
Client-side applications
Variables in the following client code can be accessed via res.kbitUp, res.deltaUp, res.deltaTime, and res.latency.
Flex 4 AS3 version
A built version of this client is included in clientBWCheck.zip.
ClientBWCheck.mxml
Code:
<?xml version="1.0" encoding="utf-8"?>
<local:cbwc xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:local="*"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" width="100%" height="100%">
<mx:Image source="http://www.wowzamedia.com/img/header.gif" x="0" y="0"/>
<s:VGroup x="10" y="70" horizontalAlign="left">
<s:HGroup>
<s:Button label="Connect: " click="mainInit()"/>
<mx:TextInput id="wowzaAddress" text="rtmp://laketk.com:1935/upbwcheck" width="380"/>
</s:HGroup>
<mx:Button label="Run" id="runButton" click="start()" enabled="{isConnected}"/>
<mx:TextArea id="output" width="400" height="400" editable="false" contentBackgroundColor="#E7E2E2"/>
</s:VGroup>
<mx:TabNavigator x="540" y="120" >
<mx:VBox label="Graph" horizontalAlign="center">
<mx:ColumnChart id="chart" dataProvider="{chartData}">
<mx:horizontalAxis>
<mx:CategoryAxis
dataProvider="{chartData}"
categoryField="testNumber"/>
</mx:horizontalAxis>
<mx:series>
<mx:ColumnSeries
yField="kbitUp"
displayName="kbitUp"
labelField="kbitUp"
labelPosition = "inside"
/>
</mx:series>
</mx:ColumnChart>
<mx:Legend direction="horizontal" dataProvider="{chart}"/>
</mx:VBox>
<mx:Box label="Data">
<mx:DataGrid dataProvider="{chartData}" width="600" height="400">
<mx:columns>
<mx:DataGridColumn dataField="testNumber"/>
<mx:DataGridColumn dataField="kbitUp"/>
<mx:DataGridColumn dataField="KBytes"/>
<mx:DataGridColumn dataField="latency"/>
<mx:DataGridColumn dataField="deltaUp"/>
<mx:DataGridColumn dataField="deltaTime"/>
</mx:columns>
</mx:DataGrid>
</mx:Box>
</mx:TabNavigator>
</local:cbwc>
Code:
package
{
import flash.events.MouseEvent;
import flash.events.NetStatusEvent;
import flash.net.NetConnection;
import flash.net.Responder;
import flash.system.Security;
import mx.charts.ColumnChart;
import mx.collections.ArrayCollection;
import mx.controls.Button;
import mx.controls.DataGrid;
import mx.controls.Text;
import mx.controls.TextArea;
import mx.controls.TextInput;
import mx.events.FlexEvent;
import spark.components.Application;
public class cbwc extends Application
{
Security.LOCAL_TRUSTED;
public var nc:NetConnection;
private var data:Object = new Object();
private var testNumber:int = 0;
public var output:TextArea;
public var wowzaAddress:TextInput;
public var runButton:Button;
private var run:Boolean = false;
[Bindable]
public var isConnected:Boolean = false;
public var chart:ColumnChart = new ColumnChart();
[Bindable]
public var chartData:ArrayCollection = new ArrayCollection();
public function mainInit():void
{
nc = new NetConnection();
nc.addEventListener(NetStatusEvent.NET_STATUS,function(infoObject:NetStatusEvent):void
{
output.text = infoObject.info.code;
if (infoObject.info.code == "NetConnection.Connect.Success")
{
isConnected = true;
testNumber = 0;
chartData = new ArrayCollection()
}
});
var clientObj:Object = new Object();
nc.addEventListener(NetStatusEvent.NET_STATUS, function(infoObject:NetStatusEvent):void
{
trace (infoObject.info.code);
if (infoObject.info.code == "NetConnection.Connect.Success") {
isConnected = true;
}
});
nc.connect(wowzaAddress.text);
}
public function start():void
{
if (run)
{
run = false;
runButton.label = "Run";
}
else
{
runButton.label = "Stop";
run = true;
doClientBWCheck();
}
}
private function doClientBWCheck():void
{
output.text = "";
data.latency = 0;
data.cumLatency = 1;
data.bwTime = 0;
data.count = 0;
data.sent = 0;
data.kbitUp = 0;
data.deltaUp = 0;
data.deltaTime = 0;
data.pakSent = new Array();
data.pakRecv = new Array();
data.beginningValues = {};
nc.call("onClientBWCheck", new Responder(bwResponse));
}
private function bwResponse(p_res:Object):void
{
var deltaTime:Number;
var deltaUp:Number;
var kbitUp:Number;
var payload:Array = new Array();
for (var i:int=0; i<1200; i++){
payload[i] = Math.random(); //16K approx
}
var now:Number = (new Date()).getTime()/1;
if(data.sent == 0) {
data.beginningValues = p_res;
data.beginningValues.time = now;
data.pakSent[data.sent++] = now;
nc.call("onClientBWCheck", new Responder(bwResponse), now);
} else {
data.pakRecv[data.count] = now;
trace( "Packet interval = " + (data.pakRecv[data.count] - data.pakSent[data.count])*1 );
output.text += "Packet interval = " + (data.pakRecv[data.count] - data.pakSent[data.count])*1 ;
data.count++;
var timePassed:Number = (now - data.beginningValues.time);
if (data.count == 1) {
data.latency = Math.min(timePassed, 800);
data.latency = Math.max(data.latency, 10);
data.overhead = p_res.cOutBytes - data.beginningValues.cOutBytes;
trace("overhead: "+data.overhead);
data.pakSent[data.sent++] = now;
nc.call("onClientBWCheck", new Responder(bwResponse), now, payload);
}
trace("count: "+data.count+ " sent: "+data.sent+" timePassed: "+timePassed+" latency: "+data.latency);
output.text += "count: "+data.count+ " sent: "+data.sent+" timePassed: "+timePassed+" latency: "+data.latency;
// If we have a hi-speed network with low latency send more to determine
// better bandwidth numbers, send no more than 6 packets
if ( (data.count >= 1) && (timePassed<1000))
{
data.pakSent[data.sent++] = now;
data.cumLatency++;
nc.call("onClientBWCheck", new Responder(bwResponse), now, payload);
} else if ( data.sent == data.count ) {
// See if we need to normalize latency
if ( data.latency >= 100 )
{ // make sure we detect sattelite and modem correctly
if ( data.pakRecv[1] - data.pakRecv[0] > 1000 )
{
data.latency = 100;
}
}
payload = new Array();
// Got back responses for all the packets compute the bandwidth.
var stats:Object = p_res;
deltaUp = (stats.cOutBytes - data.beginningValues.cOutBytes)*8/1000;
deltaTime = ((now - data.beginningValues.time) - (data.latency * data.cumLatency) )/1000;
if ( deltaTime <= 0 )
deltaTime = (now - data.beginningValues.time)/1000;
kbitUp = Math.round(deltaUp/deltaTime);
trace("onBWDone: kbitUp = " + kbitUp + ", deltaUp= " + deltaUp + ", deltaTime = " + deltaTime + ", latency = " + data.latency + " KBytes " + (stats.cOutBytes - data.beginningValues.cOutBytes)/1024) ;
output.text = "kbitUp = " + kbitUp + ", deltaUp= " + deltaUp + ", deltaTime = " + deltaTime + ", latency = " + data.latency + " KBytes " + (stats.cOutBytes - data.beginningValues.cOutBytes)/1024 + "/n/n" + output.text;
var obj:Object = new Object();
obj.kbitUp = kbitUp;
obj.deltaUp = deltaUp;
obj.deltaTime = deltaTime;
obj.latency = data.latency;
obj.KBytes = (stats.cOutBytes - data.beginningValues.cOutBytes)/1024;
obj.testNumber = "Test # " + ++testNumber;
chartData.addItemAt(obj,0);
if (run)
doClientBWCheck();
}
}
}
}
}
Flash AS2 version
Code:
function doClientBWCheck() {
nc.call("onClientBWCheck", res, null);
trace("testing CtoS performance...");
}
var nc:NetConnection = new NetConnection();
nc.onStatus = function(info) {
trace (info.code);
if (info.code == "NetConnection.Connect.Success") {
doClientBWCheck();
}
}
nc.connect("rtmp://localhost/bwcheck");
var payload = new Array();
for (var i=0; i<1200; i++){
payload[i] = Math.random(); //16K approx
}
var res = new Object();
res.latency = 0;
res.cumLatency = 1;
res.bwTime = 0;
res.count = 0;
res.sent = 0;
res.kbitUp = 0;
res.deltaUp = 0;
res.deltaTime = 0;
//res.client = p_client;
//var stats = p_client.getStats();
res.pakSent = new Array();
res.pakRecv = new Array();
res.beginningValues = {};
res.onResult = function(p_res) {
trace ("ClientBWResult: ");
var now = (new Date()).getTime()/1;
if(this.sent == 0) {
this.beginningValues = p_res;
this.beginningValues.time = now;
this.pakSent[res.sent++] = now;
nc.call("onClientBWCheck", this, now);
} else {
this.pakRecv[this.count] = now;
trace( "Packet interval = " + (this.pakRecv[this.count] - this.pakSent[this.count])*1 );
this.count++;
var timePassed = (now - this.beginningValues.time);
if (this.count == 1) {
this.latency = Math.min(timePassed, 800);
this.latency = Math.max(this.latency, 10);
this.overhead = p_res.cOutBytes - this.beginningValues.cOutBytes;
trace("overhead: "+this.overhead);
this.pakSent[res.sent++] = now;
nc.call("onClientBWCheck", res, now, payload);
}
trace("count: "+this.count+ " sent: "+this.sent+" timePassed: "+timePassed+" latency: "+this.latency);
// If we have a hi-speed network with low latency send more to determine
// better bandwidth numbers, send no more than 6 packets
if ( (this.count >= 1) && (timePassed<1000))
{
this.pakSent[res.sent++] = now;
this.cumLatency++;
nc.call("onClientBWCheck", res, now, payload);
} else if ( this.sent == this.count ) {
// See if we need to normalize latency
if ( this.latency >= 100 )
{ // make sure we detect sattelite and modem correctly
if ( this.pakRecv[1] - this.pakRecv[0] > 1000 )
{
this.latency = 100;
}
}
delete payload;
// Got back responses for all the packets compute the bandwidth.
var stats = p_res;
deltaUp = (stats.cOutBytes - this.beginningValues.cOutBytes)*8/1000;
deltaTime = ((now - this.beginningValues.time) - (this.latency * this.cumLatency) )/1000;
if ( deltaTime <= 0 )
deltaTime = (now - this.beginningValues.time)/1000;
kbitUp = Math.round(deltaUp/deltaTime);
trace("onBWDone: kbitUp = " + kbitUp + ", deltaUp= " + deltaUp + ", deltaTime = " + deltaTime + ", latency = " + this.latency + " KBytes " + (stats.cOutBytes - this.beginningValues.cOutBytes)/1024) ;
}
}
}


Article List
Categories
Wowza Media