One Player to Rule Them All…

Last week I read a great post by Vaugn Denny about the JW Video Player. And being of the curious sort I had to investigate further. So I visited the JW Player web site. And I was greeted with a page that claimed that they had a video player that would not only play flv files, but wmv files as well. And with further reading the player claims to have an open API with access to the player events. Could it be true?

One Player to rule them all, One Player to track them,
One Player to bring them all and in the darkness correlate them
In the Land of Analytics where the Shadows lie.

theringvx6

Almost one player. There are slightly different versions for the wmv player and the flv players, but it’s close enough. So now the question was, could I take these players and have a unified look and feel to the player on the site and be able to track them the way I wanted/needed to with Omniture?

This is important to me since we have tons of content already created for our site that we need to track and we have very limited access to action source tools to be able to leverage the action source code that Omniture has.

I downloaded both versions and set to work. The wmv version requires that Silverlight be installed on your web server. This was a pain free process on my local workstation, but provided a small hicup when testing the code on my dev and prod servers. You have to add the silverlight extensions to the available MIME types on the server to get it to run. Once you do that, it is smooth sailing. Using the demos provided I had a working version of the JW Player running a wmv file and I had access to the event listeners in Firefox, IE, and Safari.

The FLV player was even easier to set up. As I was working with the flv player I discovered that you could put in a link to your favorite YouTube video in the call, and it would pull the video into the player and let you track it as if it was any other media file on your server.

So using the demo sites I found on the LongTail video site I put together this code sample that is ColdFusion based to pull in the information about the file and then serve up the correct player.


<cfparam name="variables.mediaFileExt" default="">
<cfparam name="url.mediaFilePath" default="">
<cfparam name="variables.mediaFileName" default="">
<cfparam name="variables.mediaLocation" default="">
<cfparam name="variables.mediaFilePath" default="">
<cfparam name="variables.mediaFileType" default="">
<cfparam name="variables.mediaPreviewImage" default="preview.png">
<cfparam name="variables.mediaHeight" default="500">
<cfparam name="variables.mediaWidth" default="700">

<div id="videoPlayerDiv">&nbsp;</div>

<cfif variables.mediaFileExt EQ "wmv">

	<cfoutput>
	<!--- windows media files --->
	<script type="text/javascript" src="silverlight.js"></script>
	<script type="text/javascript"  src="wmvplayer.js"></script>
	<script type="text/javascript">

		// some variables to save
		var currentPosition = 0;
		var currentBuffer = 0;
		var currentVolume = 80;
		var currentState = "NONE";
		var defaultState = "NONE";
		var currentLoad = 0;
		var player;

		// This creates the player after the page has finished loading (onload).
		function createPlayer() {
			var cnt = document.getElementById('videoPlayerDiv');
			var src = "/includes/mediaplayers/wmvplayer.xaml";
			var cfg = {height:"#variables.mediaHeight#", width:"#variables.mediaWidth#", file:"#variables.mediaFilePath#", autostart:"false",image:'#variables.mediaPreviewImage#'};
			player = new jeroenwijering.Player(cnt,src,cfg);
			addListeners();
		};
		function addListeners() {
			if(player.view) {
				player.addListener('STATE',stateUpdate);
				player.addListener('TIME',timeUpdate);
			} else {
				setTimeout("addListeners()",100);
			}
		};
		// These are the event listeners
		function stateUpdate(ost,nst) {
			currentState = nst;

		    if (defaultState == "NONE") {
		    	//alert("started");
		    	defaultState = "started";
		    	getTimeValue();
		    }

		     currentTime = getPosValue();
		    if (currentState == "Completed")
		 	  {
		  		omniMediaTrackingDone('#variables.mediaFileName#');
		  	  }
		    if (currentState == "Playing")
		 	  {
		  		omniMediaTrackingResume('#variables.mediaFileName#',currentTime);
		  	  }
		    if (currentState == "Paused")
		 	  {
		  		omniMediaTrackingStop('#variables.mediaFileName#',currentTime);
		  	  }

		};

		function timeUpdate(pos,dur) {
			currentPosition = pos;
			var tmp = document.getElementById("time");
			if (tmp) { tmp.innerHTML = pos; }
			var tmp = document.getElementById("dur");
			if (tmp) { tmp.innerHTML = dur; }
		};
		// you can not combine the listener events, so the functions below are a workaround to get the length/pos of the video file
		function getTimeValue(){
			var tmp = document.getElementById("dur");
			var tv = tmp.innerHTML;
			if (tv == "0")
				{
				setTimeout("getTimeValue()",100);
				}
			else
			{
				omniInitMediaTracking('#variables.mediaFileName#',tv,'#variables.playerName#');
			}
		}

		function getPosValue() {
			var tmp = document.getElementById("time");
			var pv = tmp.innerHTML;
			return pv;
		}
	</script>
	</cfoutput>
	<div style="display:none;" id="buffer"></div>
	<div style="display:none;"  id="state"></div>
	<div style="display:none;"  id="time"></div>
	<div style="display:none;"  id="load"></div>
	<div style="display:none;"  id="dur"></div>
<script>
	setTimeout("createPlayer()",500);
</script>
<cfelse>

<!--- flv & youtube use the same js script code --->
	<script type="text/javascript" src="/includes/mediaplayers/swfobject.js"></script>

	<cfoutput>
	<div id="timevalue" style="display:none;">0</div>
	<div id="posValue" style="display:none;">0</div>
	<div id="flashbanner">this will be replaced by the SWF.</div>

	<cfif variables.mediaFileExt EQ "yt">
		<script type='text/javascript'>
		  var so = new SWFObject('/includes/mediaplayers/player.swf','ply','#variables.mediaWidth#','#variables.mediaHeight#','9','##ffffff');
		  so.addParam('allowfullscreen','true');
		  so.addParam('allowscriptaccess','always');
		  so.addParam('wmode','opaque');
		  so.addVariable('file','#variables.mediaFilePath#');
		  so.write('flashbanner');
		</script>
	<cfelse>
		<script type="text/javascript">
		  var so = new SWFObject('/includes/mediaplayers/player.swf','aribaFLVVideoPlayer','#variables.mediaWidth#','#variables.mediaHeight#','9');
		  so.addParam('allowfullscreen','true');
		  so.addParam('flashvars','file=#variables.mediaFilePath#&image=#variables.mediaPreviewImage#');
		  so.write('flashbanner');
		</script>
	</cfif>

	<script>
		playerReady();

		var currentPosition = 0;
		var currentVolume = 0;
		var currentMute = false;
		var currentState = "NONE";
		var defaultState = "NONE";
		var clipduration = 0;

		var player = document.getElementById('aribaFLVVideoPlayer');

		function playerReady() {
		   var player = document.getElementById('aribaFLVVideoPlayer');
		    addListeners();
		}

		function addListeners() {
		    if (player) {
		        addAllModelListeners();
		    } else {
		        setTimeout("addListeners()",100);
		    }
		}

		function addAllModelListeners() {
			 if (typeof player.addModelListener == "function") {
		         player.addModelListener("BUFFER", "doNothing"); //{percentage,id,client,version}.
		         player.addModelListener("ERROR", "doNothing"); //{message,id,client,version}.
		         player.addModelListener("LOADED", "doNothing"); //{loaded,total,offset,id,client,version}.
		         player.addModelListener("META", "doNothing"); //{variable1,variable2,variable3,...,id,client,version}.
		         player.addModelListener("STATE", "stateListener");//{newstate,oldstate,id,client,version}.
		         player.addModelListener("TIME", "positionListener"); //{position,duration,id,client,version}.
			}

		}

		function doNothing(obj) { //nothing
		}

		function positionListener(obj) {
		    currentPosition = obj.position;
		     clipduration = obj.duration;
		    var tmp = document.getElementById("posValue");
		    if (tmp) { tmp.innerHTML = currentPosition; }
		      var tmp2 = document.getElementById("timevalue");
		    if (tmp2) { tmp2.innerHTML = clipduration; }
		}

		function stateListener(obj) {
			oldState = obj.oldstate;

		    if (defaultState == "NONE") {
		    	//alert("started");
		    	defaultState = "started";
		    	getTimeValue();
		    }
		     currentState = obj.newstate;
		     currentTime = getPosValue();
		    if (currentState == "COMPLETED")
		 	  {
		  		omniMediaTrackingDone('#variables.mediaFileName#');
		  	  }
		    if (currentState == "PLAYING")
		 	  {
		  		omniMediaTrackingResume('#variables.mediaFileName#',currentTime);
		  	  }
		    if (currentState == "PAUSED")
		 	  {
		  		omniMediaTrackingStop('#variables.mediaFileName#',currentTime);
		  	  }

		}

		// you can not combine the listener events, so the functions below are a workaround to get the length/pos of the video file
		function getTimeValue(){
			var tmp = document.getElementById("timevalue");
			var tv = tmp.innerHTML;
			if (tv == "0")
				{
				setTimeout("getTimeValue()",100);
				}
			else
			{
				omniInitMediaTracking('#variables.mediaFileName#',tv,'#variables.playerName#');
			}
		}

		function getPosValue() {
			var tmp = document.getElementById("posValue");
			var pv = tmp.innerHTML;
			return pv;
		}

	</script>
	</cfoutput>
</cfif>

The only workaround the event listeners I had to deal with was tracking the initial play of the video. The listener events can not be combined in a function, or at least I could not get that to work, so I came up with the solution to let the listeners track the position and duration of the file in a hidden div that I could pull the value from there when I needed it. Then it is a matter of just calling the JS functions I added to my tracking / omniture page code to pass the data to Omniture.

I am very pleased with this player and the ability I have to track all the video formats we use. The player also claims that it is skinable and seems easy enough to to that, but I have not tried that yet.

Demo sites:

Be sure to read this article by Brian Thopsey on how he used the JW player with Omniture too.

Leave a comment

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

Are you human? Or are you Dancer? *

8 thoughts on “One Player to Rule Them All…”