This technique makes use of the URLLoader's close method. Therefore, the code is only available for AS3.

You know what would be nice? If we could serve every user the same 1024×768, 512 kilobits per second video. Unfortunately, some selfish people still refuse to pay us developers the simple courtesy of buying that T1 line (I'm looking at you, Mom). To make matters even worse, our bosses want these selfish losers to be able to watch our videos. Luckily, Flash's FLVPlayback component supports a subset of SMIL that allows us to serve different videos to users with different bandwidths.* For those who don't know, a SMIL file looks something like this:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE smil PUBLIC "-//W3C//DTD SMIL 2.0//EN"
    "http://www.w3.org/2001/SMIL20/SMIL20.dtd">
<smil xmlns="http://www.w3.org/2001/SMIL20/Language">
    <body>
        <switch>
            <video src="video2.flv" system-bitrate="512000" />
            <video src="video1.flv" system-bitrate="256000" />
            <video src="video0.flv" />
        </switch>
    </body>
</smil>

Given the above SMIL file, users with connections transferring at a rate of 512kbps or higher should see "video2.flv," users with transfer speeds between 256kbps and 512kbps should see "video1.flv," and everybody else should get "video0.flv." But they won't — unless you let the FLVPlayback component know what their transfer rate actually is. Generally, this means having to download a file of known size and estimate the user's bandwidth based on how long the download takes to complete. However, with the advent of AS3 (and a little clever actionscripting), we can automate this process entirely.

See the NCManagerAuto Example and then download the NCManagerAuto Source and Example Files to try it out for yourself. (Or grab it from my as3 subversion repository.)

Most of the work of the FLVPlayback component is actually done by the VideoPlayer object that it wraps. The VideoPlayer class, in turn, uses a helper class called NCManager to (of all things) manage NetConnections. Therefore, we could make some pretty significant changes to how our FLVPlayback components operate if we could only substitute our own versions of NCManager for the default. If you haven't already guessed, Adobe provides us with a simple method to do exactly that. By setting the static iNCManagerClass property of the VideoPlayer class, we can change how VideoPlayers (and by extension FLVPlayback components) handle NetConnections. By setting the property to the xa NCManagerAuto class, we can enable automatic (and completely transparent) bandwidth detection for our FLVPlayback components. But enough babbling, here's how you do it:

import fl.video.FLVPlayback;
import fl.video.VideoPlayer
import com.exanimo.video.NCManagerAuto;

VideoPlayer.iNCManagerClass = NCManagerAuto;
var myFLVPlayback:FLVPlayback = new FLVPlayback();
myFLVPlayback.source = 'example.smil';
this.addChild(myFLVPlayback);

Except for the addition of one line (okay, one line and two import statements), everything here is business as usual. You can create your FLVPlayback component with ActionScript on a frame, in a document class, or even drag it onto the stage. Just add VideoPlayer.iNCManagerClass = NCManagerAuto; and the FLVPlayback component will be able to parse your SMIL file and play the video appropriate for your user's internet connection.

So How Does it Work?

The NCManagerAuto class uses a variation of the same trick people have always used to estimate a user's bandwidth. Historically, this required a small file of known size on your server. This file would then be downloaded and, based on the time the download took, the bandwidth could then be estimated. However, NCManagerAuto doesn't require any extra files on your server. Instead, it creates an instance of the xa BandwidthChecker, which it uses to download the first 35K of one of the FLVs in your SMIL file — an uncached version of course. It then sets the bitrate of your component using the data returned by the BandwidthChecker.

Of course, this method of determining bandwidth isn't entirely foolproof. Its drawbacks have been documented by almost everybody who's written on the subject so I won't go into it. However, since the actual detection is encapsulated in the BandwidthChecker, it'll be a simple switch if a more accurate method arises. Both the NCManagerAuto and the BandwidthChecker are available under the MIT License. Enjoy the free code.

*There appears to be a bug in how Adobe's FLVPlayback component parses SMIL files: the SMILManager 's parseVideo method throws away the system-bitrate attributes of your video nodes. Therefore, the component will think that the first node is your default video, and use it every time. The source code that Adobe includes with Flash 9 (located in the "Component Source" directory) does not exhibit this problem, so I can only guess that the FLVPlayback component was compiled with an earlier version of the SMILManager class.

This technique addresses the problem transparently by using a different SMILManager class. Even if you do not want to use automatic bandwidth checking (and instead want to detect bandwidth using your own method), you can still use the NCManagerAuto class to work around this bug. Simply set the bitrate property of your FLVPlayback instance and the component will use that value instead of trying to determine the user's bandwidth itself.


Comments

Gravatar Jeremy

This looks cool. If I have my flv's hosted on an rtmp server should I be able to link to different versions of this content by changing the src path in the SMIL file? I have tried with the URL I usually use when linking from flash movies but to no avail.
rtmp://xx.xx.xx.xx/test_video/_definst_/movies/myfilename.flv
Is this possible and if so what do I need to do?

Gravatar matthew

Actually, when you use a SMIL file with an RTMP server, you need to set the application (rtmp://xx.xx.xx.xx/test_video) in the meta tag. Try this:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE smil PUBLIC "-//W3C//DTD SMIL 2.0//EN"
    "http://www.w3.org/2001/SMIL20/SMIL20.dtd">
<smil xmlns="http://www.w3.org/2001/SMIL20/Language">
    <head>
        <meta base="rtmp://xx.xx.xx.xx/test_video" />
    </head>
    <body>
        <switch>
            <video src="_definst_/movies/video2.flv" system-bitrate="512000" />
            <video src="_definst_/movies/video1.flv" system-bitrate="256000" />
            <video src="_definst_/movies/video0.flv" />
        </switch>
    </body>
</smil>
Gravatar Luke

Hey there,

This is almost what I was looking for, pretty transparent way to detect bandwidth. But what about simply turning videos on or off based on connection speed. For our dialup users, we want to disable videos entirely. Could I use something like ContentPath = null, and myFLVPlayback._visible=false?

Cheers,
Luke

Gravatar PDL

I understand the structure of the smil , my question is how do you actually embed this into your html/php code? We are looking to have 2, possibly 3 bitrate/size versions of each of our videos and I don't understand how to link to this in a page.

Thanks

Gravatar Jim Jeffers

This is a bit odd. Probably a novice AS3 issue. When I try to compile the example with a supplied set of FLVs and SMIL I get this error in my ouput:

ArgumentError: Error #2012: LoaderInfo class cannot be instantiated.
at com.exanimo.video::NCManagerAuto/helperDone()
at fl.video::SMILManager/http://www.adobe.com/2007/flash/flvplayback/internal::xmlLoadEventHandler()
at flash.events::EventDispatcher/dispatchEventFunction()
at flash.events::EventDispatcher/dispatchEvent()
at flash.net::URLLoader/onComplete()

Gravatar mteguh

Yes I am also getting the same error message.
ArgumentError: Error #2012: LoaderInfo class cannot be instantiated.
at com.exanimo.video::NCManagerAuto/helperDone()
at fl.video::SMILManager/http://www.adobe.com/2007/flash/flvplayback/internal::xmlLoadEventHandler()
at flash.events::EventDispatcher/dispatchEventFunction()
at flash.events::EventDispatcher/dispatchEvent()
at flash.net::URLLoader/onComplete()

And seems like it only happens in IE, which is weird coz flash is supposed to be crossbrowser?
In Firefox and in the IDE it works fine.

Gravatar mteguh

Just confirmed that the example file given is also hosed in IE.
Making this change seems to fix it tho. Anyhow this is cool stuff.
Thx

var serverPath:String = "";

try {
serverPath = new LoaderInfo().url.split('/').slice(0, -1).join('/');
} catch (e:Error) {
serverPath = ExternalInterface.call( 'function(){ return window.location.toString(); }' );
serverPath = (serverPath)? serverPath : "";
}

//var myBandwidthChecker = new BandwidthChecker(URLUtil.getFullURL(new LoaderInfo().url.split('/').slice(0, -1).join('/'), URLUtil.getFullURL(_streamName, _streams0.src)));
var myBandwidthChecker = new BandwidthChecker(URLUtil.getFullURL(serverPath, URLUtil.getFullURL(_streamName, _streams0.src)));

Gravatar matthew

@Luke:
Check out the BandwidthChecker class. It's included in the download.

@PDL:
This is a Flash solution. Just embed a SWF like in the example. If you want to load different videos using the same SWF, just pass the SWF the URL of your SMIL file.

@jim, mteguh:
That was an error on my part. I've replaced new LoaderInfo().url with new Loader().contentLoaderInfo.loaderURL. Your solution is neat, mteguh, but I think it's off the mark for two reasons: 1) it requires JavaScript and 2) I believe we actually want the URL of the SWF (not the html container). The confusion is that (for some stupid reason) Flash expects FLV sources to be relative to the SWF, even though Loader and URLLoader treat URLs as being relative to the HTML container. Since we're using URLLoader to check the bandwidth (BandwidthChecker wraps URLLoader), we must account for the fact that the provided FLV source is relative to the location of the SWF. We do this by resolving the URL to the directory of the SWF. The ZIP file has been updated and the new code is checked into subversion.

Gravatar Jim Jeffers

Oh man! That makes sense I don't know why I didn't even realize that was the problem. We had another guy in the office who had an install of flash 9 that had yet to be updated. It compiled for him. Pretty crazy that Adobe modified the language in Flash CS3 after it had been officially released for quite some time.

Gravatar Jim W

I'm trying to use your detection once for two sets of videos – high bandwidth and low bandwidth. Each set is contained in two arrays, but from the info above, it looks like the src value must point to an FLV in order for the checking to occur – am I correct?

If so, is there a way that I can just pass a value based on the bitrate and then determine the FLV for playback? Currently I'm trying this:

// Function determining which Array to use

function setBandwidth():Array
{

var bandwidth:String = 'example.smil';

switch (bandwidth)
{

case "HI":
return(HiBandArray);
break;

case "LO":
return(LoBandArray);
break;

default:
trace ("defaulted!");
return(LoBandArray);
break;

}

}

Gravatar Jim W

As a quick solution, I've used the FLV pathname to switch – but this would become cumbersome with larger directory paths and perhaps dynamic paths.

Gravatar Bandwidth Detection AS3 « Thomas J. Webb’s Ecology Blog

[...] ex animo » Blog Archive » Automatic Bandwidth Detection for SMIL + FLVPlayback + Progressive Downl… [...]

Gravatar Jesse K

Does anyone know how you would go about getting a playback controller integrated into the movies using this method?

Gravatar Mike Fox

I'm stumped. Things are working fine if I test the files locally, but once I upload the SMIL file, html and .swf file to my server, and point the FLVplayback component's source to the same SMIL on an http server, I don't get any video, just endless barber pole.

Any suggestions?

Gravatar Mike Fox

Interesting. Your example code doesn't work for me either unless I open it locally on my Mac.

Your link above ("See the NCManagerAuto Example ") brings up nothing but a blank page.

If I download the sample source files and run them, voila! I see content.

But if I upload the exact same files to my server and try to run the html remotely, no content.

So, whatssupwiddat? ; ) Inquiring minds wanna know.

Gravatar Mike Fox

Never mind. I figured it out.

For those following behind…

Apparently, in order for the smil file approach to work remotely, the smil file can NOT end with .smi or .smil as I had been informed elsewhere.

The file must end with .xml . (Or at least the way our http servers are configured, maybe this might work under different configurations, but it wouldn't work for me until I changed .smil to .xml )

I'm outtahere.

Gravatar matthew

@Mike Sorry I didn't respond sooner but thanks for reporting back with your findings!

Gravatar Anthony

Hi man, nice code, work good but in Flex 3 and using SDK 4.0.x I' have problem on final compiling.

I use this additional compliler arguments: -locale en_US -static-link-runtime-shared-libraries=true

I solve the problem with next way…

edit next class: com.exanimo.video.NCManagerAuto.as

– error line: 175 -> var myBandwidthChecker = new BandwidthChecker(URLUtil.getFullURL(n…
– solution : you need declarate your variable, simply add ":*"

var myBandwidthChecker:* = new BandwidthChecker(URLUtil.getFullURL(new Loader().contentLoaderInfo.loaderURL.split('/').slice(0, -1).join('/'), URLUtil.getFullURL(_streamName, _streams[0].src)));

Good Luck

Gravatar matthew

Thanks for the comment Anthony, but I think you're using an old version. Try grabbing it from the SVN repository. I'll update the ZIP…someday.

Gravatar FLVPlayback Bandwidth Detection?? » free icons download

[...] you trying to do? ||| Originally, I was trying to detect the bandwidth using the method outlined here. Then, depending on either Lo or Hi bandwidth, define an array of associated FLVs to play. The [...]

Gravatar FealaFep

I am sure you will love ugg boots cheap cGfhwOOr [URL=http://www.socialimba.com/uggsusa/ – uggs cheap[/URL – and check coupon code available MHKmAOyN http://www.socialimba.com/uggsusa/

Gravatar sok noni

I love your blog.. very nice colors & theme. Did you make this website yourself or did you hire someone to do it for you? Plz respond as I'm looking to construct my own blog and would like to know where u got this from. thank you

Gravatar matthew

Thanks! I made it myself a long, long time ago.