FLV Player (Streaming) using Dynamic Buffering, Port Detection, and Bandwidth Checks
This FLV player has quite a few advanced options included. The video is streamed from a Flash Media server, dynamically buffered, and playable. The server side scripts allow for dynamic port detection, and you can setup the player to serve differently encoded flv files based on the users bandwidth.
First let’s start with our external classes.
stop(); /*--------------------------------------------------------------------------------*/ //external classes /*--------------------------------------------------------------------------------*/ import NCManager; import mx.transitions.Tween;
We’re using the NCManager.as file as provided by Adobe. This external class allows us to manage our port connection list as well as detect a users bandwidth when they load our swf. I have included this file in the downloadable source, but we won’t be going over it in detail here.
Next we have our dynamic buffer animation. This function is used whenever the user may have caught up to the buffer available for the streaming video. There is a timer object that monitors the buffer level, and uses this animation as needed.
/*--------------------------------------------------------------------------------*/
//dynamic buffering
/*--------------------------------------------------------------------------------*/
function startBufferAnimation() {
if (ns.time>"0") {
//hide animation
clearInterval(startBufferAnimation_interval);
buffer_graphic._alpha = 0;
buffer_graphic._x = 5000;
} else {
//show animation
buffer_graphic._alpha = 100;
//display the buffer progress
totalBuffer = ns.bufferTime;
currentBuffer = ns.bufferLength;
loadingText = Math.round((currentBuffer/totalBuffer)*100);
if (loadingText<=100) {
buffer_graphic.loading_txt.text = loadingText+"%";
} else {
buffer_graphic.loading_txt.text = "100%";
clearInterval(startBufferAnimation_interval);
}
}
}
Let’s load the video from the Flash Media Server. Here we will be connecting to our server using the NCManager class, looking for the bandwidth available for our current user, and recording some information about the video to be used in our controls such as scrubbing. Something that is available to us within this next bit is the ability to use the bandwidth returned by the bwcheck.as on the FMS. You could setup different variables for the k_FILENAME to be loaded based on the returned bandwidth checks. Different data rates for the same video can be served to your site visitors to make sure they have the best viewing experience possible.
/*--------------------------------------------------------------------------------*/
//video loading/controls
/*--------------------------------------------------------------------------------*/
//variables
var firstFramePending:Boolean = true;
var firstPlayPending:Boolean = true;
var ns:NetStream;
var fileName:String;
//constants
var k_STARTTIME:Number = getTimer();
//server info
var k_SERVERNAME:String = "your-server-ip";
var k_APPNAME:String = "app-name";
var k_FILENAME:String = "flv-file-name";
var k_ASC_PATH:String = "rtmp://your-fms-server-ip/app-name/bwcheck.asc";
//connect to the bwcheck.asc
nc = new NetConnection();
//connect to the bwcheck.asc
nc.connect(k_ASC_PATH,true);
//let us know that the connection was successful
nc.onStatus = function(info) {
trace("Level: "+info.level+" Code: "+info.code);
if (info.code == "NetConnection.Connect.Success") {
trace("loaded: "+this.uri);
}
};
//the variable p_bw returned by bwcheck.asc holds a value equal to the detected download speed in kilobits per second
//for best results we should serve video that is encoded at a rate less than or equal to kbitsDown
NetConnection.prototype.onBWDone = function(p_bw) {
trace("BANDWIDTH= "+p_bw);
//save the detected bandwidth
detected_bw = p_bw;
//close the Netconnection to bwcheck
this.close();
};
//calling the serverside script
NetConnection.prototype.onBWCheck = function() {
return ++counter;
};
//define an instance of our NCManager class
var ncm:NCManager = new NCManager();
//create the listener object
var ncmListener:Object = new Object();
//fires when it has found a successfull connection
ncmListener.ncConnected = function(evt:Object) {
trace("["+Math.round(getTimer()-k_STARTTIME)+"ms] Successfully connected using "+evt.protocol+":"+evt.port);
//as soon as we have a good connection, play the first frame
playFirstFrame(evt.nc);
};
//fires when it has timed-out trying to find a good connection
ncmListener.ncFailedToConnect = function(evt:Object) {
trace("Failed to connect after "+evt.timeout+" milliseconds.");
};
//fires when bandwdith has been detected
ncmListener.ncBandWidth = function(evt:Object) {
//--not working--> //trace("["+Math.round(getTimer()-k_STARTTIME)+"ms] Measured bandwidth at "+evt.kbps+" kbps.");
fileName = k_FILENAME;
ncm.getStreamLength(fileName);
trace("LOADED FILE= "+fileName);
};
//fires when streamlength has been detected
ncmListener.ncStreamLength = function(evt:Object) {
trace("["+Math.round(getTimer()-k_STARTTIME)+"ms] Streamlength of "+evt.name+" is "+evt.length+" seconds.");
//save stream length info
//vid_length = evt.length;
};
//set listeners on the NCM instance
ncm.addEventListener("ncConnected",ncmListener);
ncm.addEventListener("ncFailedToConnect",ncmListener);
ncm.addEventListener("ncBandWidth",ncmListener);
ncm.addEventListener("ncStreamLength",ncmListener);
//call the connect method on NCM instance
ncm.connect(k_SERVERNAME,k_APPNAME);
//set up the NetStream and plays the first frame of the stream
function playFirstFrame(nc:NetConnection):Void {
ns = new NetStream(nc);
ns.onStatus = function(info) {
if (info.code == "NetStream.Play.Stop" && firstFramePending) {
firstFramePending = false;
trace("["+Math.round(getTimer()-k_STARTTIME)+"ms] First frame loaded");
//once the first frame has loaded, measure the bandwidth
nc.call("checkBandwidth",null);
}
};
//vid length
ns.onMetaData = function(infoObject:Object) {
my_duration = infoObject["duration"];
//trace(my_duration);
timing(my_duration);
};
//display the first frame of the best quality flv
video.attachVideo(ns);
//buffer the video
//set buffer parameters
startBufferLength = 10;
expandedBufferLength = 20;
//attach onStatus function
ns.onStatus = function(infoObject:Object) {
if (infoObject["code"] == "NetStream.Buffer.Full") {
ns.setBufferTime(expandedBufferLength);
trace("SET EXPANDED BUFFER");
buffer_graphic._alpha = 0;
}
if (infoObject["code"] == "NetStream.Buffer.Empty") {
ns.setBufferTime(startBufferLength);
trace("SET START BUFFER");
buffer_graphic._alpha = 100;
}
};
//start the buffer
ns.setBufferTime(startBufferLength);
//display buffer graphic
startBufferAnimation_interval = setInterval(startBufferAnimation, 150);
ns.play(k_FILENAME);
}
Now that we have our video loaded, we can use some simple controls. Below I have setup a scrub bar, volume, play/pause, rewind, and download buttons.
/*--------------------------------------------------------------------------------*/
//scrubber functions
/*--------------------------------------------------------------------------------*/
function timing(fTime:Number) {
//the length of the movie
var finalTime:Number = fTime;
var time_interval:Number = setInterval(checkTime, 10, ns);
//function for the interval
function checkTime(ns:NetStream) {
//movie time
var ns_seconds:Number = ns.time;
//parameters for the scrubber
var finalWidth:Number = timer_bar.slider._width-timer_bar.scrubber._width;
var movVar:Number = finalWidth/finalTime;
timer_bar.scrubber._x = movVar*ns_seconds;
//values for the scrubber position
var pTop:Number = timer_bar.scrubber._y;
var pLeft:Number = 0;
var pBottom:Number = timer_bar.scrubber._y;
var pRight:Number = timer_bar.slider._width-timer_bar.scrubber._width;
//scrubber is pressed
timer_bar.scrubber.onPress = function() {
clearInterval(time_interval);
//dragging
startDrag("timer_bar.scrubber", false, pLeft, pTop, pRight, pBottom);
dragging = true;
//seek as scrubber is moved
this.onEnterFrame = function() {
ns_seconds = this._x/movVar;
ns.seek(ns_seconds);
};
};
timer_bar.scrubber.onRelease = function() {
dragging = false;
stopDrag();
delete this.onEnterFrame;
time_interval = setInterval(checkTime, 10, ns);
};
timer_bar.scrubber.onReleaseOutside = function() {
dragging = false;
stopDrag();
delete this.onEnterFrame;
time_interval = setInterval(checkTime, 10, ns);
};
timer_bar.scrubber.onRollOver = function(){
timer_bar.scrubber.gotoAndStop(2);
};
timer_bar.scrubber.onRollOut = function(){
timer_bar.scrubber.gotoAndStop(1);
};
}
}
/*--------------------------------------------------------------------------------*/
//sound settings/controls
/*--------------------------------------------------------------------------------*/
globalsound = new Sound();
globalsound.setVolume(100);
volume_slide.volume_slider._x = volume_slide.volume_bar._width-volume_slide.volume_slider._width;
b_volume.onRelease = function() {
if (globalsound.getVolume() == 100) {
b_volume.gotoAndStop(2);
globalsound.setVolume(0);
} else if (globalsound.getVolume() == 0) {
b_volume.gotoAndStop(1);
globalsound.setVolume(100);
}
};
b_volume.onRollOver = function() {
b_volume.b_sound_a.gotoAndStop(2);
b_volume.b_sound_b.gotoAndStop(2);
}
b_volume.onRollOut = function() {
b_volume.b_sound_a.gotoAndStop(1);
b_volume.b_sound_b.gotoAndStop(1);
}
/*--------------------------------------------------------------------------------*/
//play/pause controls
/*--------------------------------------------------------------------------------*/
b_play.onRelease = function() {
if (b_play._currentframe == 1) {
ns.pause();
b_play.gotoAndStop(2);
} else {
ns.pause();
b_play.gotoAndStop(1);
}
};
b_play.onRollOver = function(){
b_play.b_play_a.gotoAndStop(2);
b_play.b_play_b.gotoAndStop(2);
};
b_play.onRollOut = function(){
b_play.b_play_a.gotoAndStop(1);
b_play.b_play_b.gotoAndStop(1);
};
b_rewind.onRelease = function() {
ns.seek(0);
};
b_rewind.onRollOver = function(){
b_rewind.gotoAndStop(2);
}
b_rewind.onRollOut = function(){
b_rewind.gotoAndStop(1);
};
download.onRelease = function() {
getURL("http://path/to/downloadable/movie", "_blank");
};
download.onRollOver = function(){
download.gotoAndStop(2);
}
download.onRollOut = function(){
download.gotoAndStop(1);
}
Here’s a screenshot of the end result:






