Bandwidth Detection with AS3

Bandwidth detection can be used in a flash site to load optimized assets for the client based on the detected bandwidth. Unfortunately, the Flash Player does not have a native bandwidth detection system, so we can’t accurately predict the users bandwidth. Bandwidth test results vary greatly due to many factors like latency, but, we can get an idea of the users download bandwidth by using flash to download an image, take the total bytes of the image and divide it by the total download time. In order to get a reasonable accuracy on bandwidth, experts suggest that you test at least three times and obtain an average from the results. Another thing to keep in mind is that the larger the file that the user has to download the more accurate the results. I would advise the use of a moderate file size around 200kb, especially if you don’t want your visitors to wait for a long period of time before viewing your content.

Get Adobe Flash player

The following is the class which determines the users download speed. I do not need to go into detail, it simply downloads an image 3 times, figures out the average speed, stores the results and dispatches a Complete event.

DownloadBandwidthTest.as
/****************************
* Manuel Gonzalez           *
* design@stheory.com        *
* www.stheory.com           *
* www.codingcolor.com       *
*****************************/

package
{
 
  import flash.net.URLRequest;
  import flash.display.Loader;
  import flash.events.EventDispatcher;
  import flash.events.Event;
  import flash.events.IOErrorEvent;
  import flash.utils.getTimer;

  public class DownloadBandwidthTest extends EventDispatcher
  {
    private var _imagePath:String;
    private var _bandwidthTests:Array;
    private var _numOfTests:uint;
    private var _downloadSpeed:Number;
    private var _startTime:uint;
    private var _imageLoader:Loader;

    public function DownloadBandwidthTest(inPath:String=null):void
    {
      _imagePath = inPath;
    }
    //////////////////// setters & getters //////////////////////
    public function set imagePath(inStr:String):void
    {
      _imagePath = inStr;
    }
    public function get imagePath():String
    {
      return _imagePath;
    }
    public function set downloadSpeed(inNum:Number):void{
      _downloadSpeed = inNum;
    }
    public function get downloadSpeed():Number{
      return _downloadSpeed;
    }
    public function set numOfTests(inNum:uint):void{
      _numOfTests = inNum;
    }
    public function get numOfTests():uint{
      return _numOfTests;
    }
    /////////////////////////////////////////////////////////////
    /*
    Method: startBandwidthCheck
    Paramters:
    Returns:
    */

    public function startBandwidthCheck():void
    {
      _numOfTests = 3;
      _downloadSpeed = 0;
      _bandwidthTests=[];
      loadTestFile();
    }
    /*
    Method: loadTestFile
    Paramters:
    Returns:
    */

    private function loadTestFile():void
    {
      var filepath:String = _imagePath += "?" + uniqueId();

      _imageLoader = new Loader();
      _imageLoader.contentLoaderInfo.addEventListener(Event.OPEN, startTimer,false,0,true);
      _imageLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, downloadCompleteHandler,false,0,true);
      _imageLoader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler,false,0,true);
      _imageLoader.load( new URLRequest(filepath) );
    }
    /*
    Method: startTimer
    Paramters:
    event:Event
    Returns:
    */

    private function startTimer(event:Event):void
    {
      _startTime = getTimer();
    }
    /*
    Method: downloadCompleteHandler
    Paramters:
    event:Event
    Returns:
    */

    private function downloadCompleteHandler(event:Event):void
    {
      logResults(event.currentTarget.bytesTotal);
      _imageLoader.unload();
    }
    /*
    Method: logResults
    Paramters:
    inBytesTotal:Number
    Returns:
    */

    private function logResults(inBytesTotal:Number):void
    {
      var fileTotalBytes:Number = inBytesTotal;
      var endTime:uint = getTimer();
      var totalDownloadTime:Number = ( endTime - _startTime ) / 1000;
      var kilobits:Number = fileTotalBytes / 1000 * 8;
      var kbps = Math.floor(kilobits / totalDownloadTime);
      _bandwidthTests.push(kbps);
      if(_bandwidthTests.length >= _numOfTests)
      {
        reportResults();
      }else{
        loadTestFile();
      }
     
    }
    /*
    Method: reportResults
    Paramters:
    Returns:
    */

    private function reportResults():void
    {
      var detectedSpeed:Number=0;
      for(var i:uint=0; i < _numOfTests; i++)
      {
        detectedSpeed += _bandwidthTests[i];
      }
      _downloadSpeed = Math.floor(detectedSpeed /_numOfTests);
      dispatchEvent(new Event(Event.COMPLETE));
      dispose();
    }
    /*
    Method: ioErrorHandler
    Paramters:
    event:IOErrorEvent
    Returns:
    */

    private function ioErrorHandler(event:IOErrorEvent):void
    {
     
      _downloadSpeed = -1
      dispatchEvent(event);
      dispose();
    }
    /*
    Method: dispose
    Paramters:
    Returns:
    */

    private function dispose():void
    {
      _imageLoader.contentLoaderInfo.removeEventListener(Event.OPEN, startTimer);
      _imageLoader.contentLoaderInfo.removeEventListener(Event.COMPLETE, downloadCompleteHandler);
      _imageLoader.contentLoaderInfo.removeEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
    }
    /*
    Method: uniqueId
    Paramters:
    Returns:
    Number
    */

    private function uniqueId():Number
    {
      var id:Number = getTimer();
      return id;
    }
  }
}

You can instantiate this class from any of your classes, for this example I used the document class to instantiate the bandwidth detection.
Main.as
/****************************
* Manuel Gonzalez           *
* design@stheory.com        *
* www.stheory.com           *
* www.codingcolor.com       *
*****************************/

package{
 
  import flash.display.Sprite;
  import flash.display.MovieClip;
  import flash.events.Event;
  import flash.events.IOErrorEvent;
  import flash.text.TextField;
  import flash.text.TextFormat;
 
  public class Main extends Sprite{
    private var _downloadTest:DownloadBandwidthTest;
    private var _testFile:String = "path to your 200kb file";
    private var _circleAnimation:CircleAnimation;
    public function Main()
    {
      testBandwidth();
    }
    /*
    Method: testBandwidth
    Parameters:
    Returns:
    */

    private function testBandwidth():void
    {
      _downloadTest = new DownloadBandwidthTest(_testFile);
      _downloadTest.addEventListener(Event.COMPLETE,completeEventHandler,false,0,true);
      _downloadTest.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler,false,0,true);
      _downloadTest.startBandwidthCheck();
      addLoadingAnimation();
     
    }
    /*
    Method: addLoadingAnimation
    Parameters:
    Returns:
    */

    private function addLoadingAnimation():void {
      _circleAnimation = new CircleAnimation();
      _circleAnimation.x = (resultBkg.x + resultBkg.width/2) - _circleAnimation.width/2;
      _circleAnimation.y = (resultBkg.y + resultBkg.height/2) - _circleAnimation.height/2 + 10;
      addChild(_circleAnimation);
    }
    /*
    Method: removeLoadingAnimation
    Parameters:
    Returns:
    */

    private function removeLoadingAnimation():void {
      removeChild(_circleAnimation);
    }
    /*
    Method: completeEventHandler
    Parameters:
    event:Event
    Returns:
    */

    private function completeEventHandler(event:Event):void
    {
      removeLoadingAnimation();
      _downloadTest.removeEventListener( Event.COMPLETE, completeEventHandler );
      _downloadTest.removeEventListener( IOErrorEvent.IO_ERROR, ioErrorHandler );
      var detectedSpeed:Number = event.target.downloadSpeed;
      tField.text = String(detectedSpeed) + " kbps";
    }
    /*
    Method: ioErrorHandler
    Parameters:
    event:IOErrorEvent
    Returns:
    */

    private function ioErrorHandler(event:IOErrorEvent):void
    {
      removeLoadingAnimation();
      _downloadTest.removeEventListener( Event.COMPLETE, completeEventHandler );
      _downloadTest.removeEventListener( IOErrorEvent.IO_ERROR, ioErrorHandler );
      var errorFormat:TextFormat = new TextFormat();
            errorFormat.color = 0xFF0000;
      tField.text = event.text;
      tField.setTextFormat(errorFormat)
    }
 
 
 
  }
}

The completeEventHandler() method is where you would add logic or call a method which would tailor your application based off the users bandwidth. Now, detecting the users upload speed is almost similar to this method except, you have to use the fileReference class, but due to Adobe’s security features its not an ideal situation since the user needs to initiate an upload by selecting an image and starting the upload with a Mousevent. That maybe a future post. Till then, Enjoy!

Download Source:
Bandwidth Detection Application (890)

One thought on “Bandwidth Detection with AS3

Leave a Reply

Your email address will not be published. Required fields are marked *

%d bloggers like this: