Home » Code Samples, Flash, FMS, Tutorials

FLV Player (Streaming) using Dynamic Buffering, Port Detection, and Bandwidth Checks

21 August 2008 | 8,807 views | 3 Comments

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:

Download the source

  • Rajivsharma0723

    Code ia not worked

  • Joe

    The scrubber doesn't seem to want to be dragged to an earlier point in the movie than the current position.

  • Joe

    The scrubber doesn't seem to want to be dragged to an earlier point in the movie than the current position.