about
history
syntax
visu
arcade
goodies
wrappers
efficiency
examples
experiments
tabageos
tutorials
actiontad logo white
A Simple FLV Player
 


The Code:    

/* A basic flv player that loads specs from xml, loads external graphics and plays one flv file. 
   The flv to be played in this case is just a basic video overview of using a mxmlc compiler on windows. */

package
{


	import flash.display.*;
	import flash.events.*;
	import flash.geom.Rectangle;
	import flash.media.SoundTransform;
	import flash.media.Video;
	import flash.net.NetConnection;
	import flash.net.NetStream;
	import flash.net.URLRequest;
	import flash.utils.Timer;
	import flash.text.TextField;

	/* These two classes are custom classes found here http://www.actiontad.com/experiments/domex/wrappermousefollow/ */
	import LoadSomeXML;
	import Checker;

	/* (t)ads Simple FLV Player */

	public class SimpleFLVPlayer extends Sprite
	{


		private var pipeline:NetConnection;
		private var channel:NetStream;
		private var theVideo:Video;
		private var soundControl:SoundTransform;

		private var playPause:Sprite;
		private var muteUnmute:Sprite;
		private var playerSkin:Sprite;
		private var bufferText:TextField;

		private var check:Checker;

		private var playerSettings:XML;

		private var scrubber:Sprite;
		private var loadBar:Sprite;
		private var loadBarHolder:Sprite;

		private var duration:Number;
		private var keepTrack:Timer;

		private var seeking:Boolean;
		private var scrubSpace:Number;


		public function SimpleFLVPlayer() {
			stage.align = "topLeft";
			stage.scaleMode = "noScale";
	
			LoadSomeXML.freshXMLFile("videoSettings.xml", setUpControls, XMLLoadError);

		}

		private function XMLLoadError(e:IOErrorEvent):void  {
			//handle xml loading error
		}

		private function setUpControls(e:Event):void {

			seeking = false;
			duration = 0;

			playerSettings = new XML(e.target.data);

			/* we initiate a new Checker instance with playerSettings as the object
			the variable check is assigned as it */
			check = new Checker(playerSettings);

			/* statSpeed is the speed of the timer in milliseconds
			keepTrack will control the scrubber and loading movements */
			var statSpeed:Number = Number(check.property("processSpeed", "10", "justNumbers"));
			keepTrack = new Timer(statSpeed, 0);


			/* Here is a use of the Checker class,
			it is used to make sure that one: the property videoHeight exists and,
			two: that the value for said property consists of only numbers.
			If one or the other is not true the value will be 240.
			The ability to check for justNumbers or justAlphas is optional. */

			var videoHeight:Number = check.property("videoHeight", "240", "justNumbers");
			var videoWidth:Number = check.property("videoWidth", "320", "justNumbers");
			theVideo = new Video(videoWidth, videoHeight);

			var playButton:Loader = new Loader();
			playButton.load(new URLRequest(check.property("playButton", "play.png")));

			var pauseButton:Loader = new Loader();
			pauseButton.load(new URLRequest(check.property("pauseButton", "pause.png")));

			pauseButton.visible = false;

			playPause = new Sprite();
			playPause.addChild(playButton);
			playPause.addChild(pauseButton);
			playPause.addEventListener(MouseEvent.CLICK, togglePlay);

			var muteButton:Loader = new Loader();
			muteButton.load(new URLRequest(check.property("muteButton", "mute.png")));

			var unmuteButton:Loader = new Loader();
			unmuteButton.load(new URLRequest(check.property("unmuteButton", "unmute.png")));

			unmuteButton.visible = false;
			muteUnmute = new Sprite();
			muteUnmute.addChild(muteButton);
			muteUnmute.addChild(unmuteButton);
			muteUnmute.addEventListener(MouseEvent.CLICK, toggleMute);

			playerSkin = new Sprite();
			var skinLoader:Loader = new Loader();
			playerSkin.addChild(skinLoader);
			skinLoader.load(new URLRequest(check.property("playerSkin", "skin.png")));
			skinLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, finishSetup);


		}

		private function finishSetup(e:Event):void {

			if (e.target.hasOwnProperty("currentCount")) {e.target.removeEventListener(TimerEvent.TIMER_COMPLETE, finishSetup);}

			if (Loader(playPause.getChildAt(1)).content != null && Loader(muteUnmute.getChildAt(1)).content != null)
			{

				/* Before we can know the width of playPause and the width of playerSkin,
				the external files must be loaded.
				This function is also set up as a loop to wait until the files are loaded.
				In this case we assume that the files are in the right place and exist. */

				soundControl = new SoundTransform(1, 0);

				addChild(playerSkin);

				theVideo.x = 0 + ((playerSkin.width - theVideo.width)/2);
				theVideo.y = 0 + ((playerSkin.width - theVideo.width)/2);
				addChild(theVideo);

				bufferText = new TextField();
				bufferText.x = theVideo.x;bufferText.y = theVideo.y;
				addChild(bufferText);

				playPause.x = theVideo.x;// - playPause.width; you may need to adjust these values to suit your images
				playPause.y = theVideo.y + theVideo.height + Number(check.property("buttonYOffset", "16", "justNumbers"));
				addChild(playPause);

				var buttonWidth:Number = Loader(muteUnmute.getChildAt(0)).width;
				muteUnmute.x = theVideo.x + theVideo.width - buttonWidth;
				muteUnmute.y = playPause.y;
				addChild(muteUnmute);

				scrubber = new Sprite();
				scrubber.graphics.beginFill(0x000000);
				scrubber.graphics.drawRect(0, 0, 5, 14);

				/* This number will be used often so we put it in the scrubSpace variable */
				/* It is the total width the load bar and scrubber can occupy */
				/* The loadBarHolder horizontal space */
				scrubSpace = Number(theVideo.width-(playPause.width*2)-
				Number(check.property("scrubSpaceNumber", "5", "justNumbers")));

				loadBar = new Sprite();
				loadBar.graphics.beginFill(0xc8c8c8);
				loadBar.graphics.drawRect(0,0, scrubSpace, 7);
				loadBar.y = 4;

				loadBarHolder = new Sprite();
				loadBarHolder.graphics.lineStyle(1, 0x000000);
				loadBarHolder.graphics.drawRect(0, 0, scrubSpace, 14);
				loadBarHolder.addChild(loadBar);
				loadBarHolder.addChild(scrubber);

				loadBarHolder.x = playPause.x + playPause.width +3;
				loadBarHolder.y = theVideo.y + theVideo.height +
				Number(check.property("loadBarYOffset", "25", "justNumbers"));


				addChild(loadBarHolder);

				loadBar.width = 1;

				establishPipeline();

			} else {

				/* this timer can take this same function because a TimerEvent is also a plain Event */


				if (e.target.hasOwnProperty("currentCount"))
				{
					e.target.stop();e.target.reset();
					e.target.addEventListener(TimerEvent.TIMER_COMPLETE, finishSetup, false, 0, true);
					e.target.start();
				}
				else
				{
					var loadLoop:Timer = new Timer(300, 1);
					loadLoop.addEventListener(TimerEvent.TIMER_COMPLETE, finishSetup, false, 0, true);
					loadLoop.start();
	
				}


			}

		}

		private function toggleMute(e:MouseEvent):void {
			if (soundControl.volume == 0)
			{
				muteUnmute.getChildAt(0).visible = true;
				muteUnmute.getChildAt(1).visible = false;
				soundControl.volume = 1;channel.soundTransform = soundControl;
			} else {
				muteUnmute.getChildAt(0).visible = false;
				muteUnmute.getChildAt(1).visible = true;
				soundControl.volume = 0;channel.soundTransform = soundControl;
			}
		}


		private function togglePlay(e:MouseEvent):void {
			if (playPause.getChildAt(0).visible == false)
			{
				playPause.getChildAt(0).visible = true;
				playPause.getChildAt(1).visible = false;
				channel.togglePause();
			} else {
				playPause.getChildAt(0).visible = false;
				playPause.getChildAt(1).visible = true;
				channel.togglePause();
			}
		}


		private function establishPipeline():void {

			pipeline = new NetConnection();
			pipeline.addEventListener(NetStatusEvent.NET_STATUS, channelConnect);
			pipeline.addEventListener(SecurityErrorEvent.SECURITY_ERROR, handleSecErrors);

			pipeline.connect(null);

		}



		private function channelConnect(event:NetStatusEvent):void {

			if (event.info.code == "NetConnection.Connect.Success") {

				channel = new NetStream(pipeline);
				var videoClient:Object = new Object();
				channel.client = videoClient;
				videoClient.onMetaData = doOnMetaData;
				channel.bufferTime = Number(check.property("bufferTime", "3", "justNumbers"));
				channel.addEventListener(NetStatusEvent.NET_STATUS, channelStatus);
				channel.addEventListener(AsyncErrorEvent.ASYNC_ERROR, function(e:Event):void {});
				theVideo.attachNetStream(channel);

				soundControl.volume = 0;
				channel.soundTransform = soundControl;
				channel.play(""+check.property("videoToPlay", "default.flv")+"");
				var justAbit:Timer = new Timer(500, 1);
				justAbit.addEventListener(TimerEvent.TIMER_COMPLETE, getToAtLeastFirstFrame, false, 0, true);
				justAbit.start();

			}

		}


		private function channelStatus(e:NetStatusEvent):void {

			var currentStat:String = e.info.code;

			if (currentStat == "NetStream.Buffer.Full")
			{
				bufferText.text = "";
			}

			if (currentStat == "NetStream.Buffer.Empty")
			{
				bufferText.text = "buffering";
			}

			/* NetStream.Play.Stop will be the code when the flv has finished playing
			o we use it to rewind the video and adjust the playPause button */
	
			if (currentStat == "NetStream.Play.Stop")
			{
				if (playPause.getChildAt(0).visible == false)
				{
					playPause.getChildAt(0).visible = true;
					playPause.getChildAt(1).visible = false;
				}

				soundControl.volume = 0;
				channel.soundTransform = soundControl;
				/* use seek to go to a keyframe of the video, in this case the first one 0 */
				channel.seek(0);
				channel.play(""+check.property("videoToPlay", "default.flv")+"");
				var justAbitTwo:Timer = new Timer(500, 1);
				justAbitTwo.addEventListener(TimerEvent.TIMER_COMPLETE, getToAtLeastFirstFrame, false, 0, true);
				justAbitTwo.start()
			}

		}


		private function getToAtLeastFirstFrame(e:TimerEvent):void {

			channel.togglePause();
			soundControl.volume = 1;
			channel.soundTransform = soundControl;
			e.target.removeEventListener(TimerEvent.TIMER_COMPLETE, getToAtLeastFirstFrame);
	
		}

		private function handleSecErrors(e:SecurityErrorEvent):void {

			//security error handler

		}

		private function handleAsyncErrors(e:AsyncErrorEvent):void {

			//async error handler

		}

		public function doOnMetaData(info:Object):void {

			/* Because this function may happen more than once,
			we wrap the statements in an if to ensure they don't happen more than once */

			if (duration == 0) {
				duration = info.duration;
				keepTrack.addEventListener(TimerEvent.TIMER, doScrubb);
				keepTrack.start();

				scrubber.addEventListener(MouseEvent.MOUSE_DOWN, startSeek);
				/* notice that the stage gets the mouse up hadler, not the scrubber */
				stage.addEventListener(MouseEvent.MOUSE_UP, endSeek);
			}

		}

		private function startSeek(e:MouseEvent):void {
			var rect:Rectangle = new Rectangle(0, scrubber.y, (loadBarHolder.width - scrubber.width), 0);
			scrubber.startDrag(false, rect);
			seeking = true;

		}

		private function endSeek(e:MouseEvent):void {

			scrubber.stopDrag();
			seeking = false;

		}

		private function doScrubb(e:TimerEvent):void {

			if (seeking == false) {
				if ((channel.time * (scrubSpace / duration )) > scrubber.width )
				{scrubber.x = (channel.time * (scrubSpace / duration )) - scrubber.width;}
			} else {
				channel.seek((scrubber.x * duration / scrubSpace));
			}

			loadBar.width = (channel.bytesLoaded / channel.bytesTotal) * scrubSpace;
		}


	}
}
The XML used with this example:

<?xml version="1.0" encoding="UTF-8"?>
<videoSettings>



<playButton>images/play.png</playButton>

<pauseButton>images/pause.png</pauseButton>

<muteButton>images/mute.png</muteButton>

<unmuteButton>images/unmute.png</unmuteButton>

<playerSkin>images/skin.png</playerSkin>



<videoWidth>675</videoWidth>

<videoHeight>356</videoHeight>



<videoToPlay>simplemxmlcuse.flv</videoToPlay>



</videoSettings>





Compiled Result:










actiontad twitter.com/actiontad terms