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.
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"> </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.
8 thoughts on “One Player to Rule Them All…”
Thanks for the information on how to do this with JW player. I was doing some research on if we could switch to either a skinned version of Flowplayer or JW player at work and continue tracking various events in Omniture.
Much appreciated.
Chris,
Thanks for the comments. I’m glad that this was helpful
-Rudi
Thanks Rudi, for this article.
On my site we have many videos which we want to track in Omniture SiteCatalyst. As those videos are in flash and to work with Omniture ActionSource implemenatiton is bit difficult, I was searching for some way like this.
I still have some queries and would be really thankful to you if you can guide me –
1. We have the videos in flash and the player we use (rather which is there on site is Adobe Flash Player). To use the above method, should we now replace the player with JW player?
2. You have used Omniture Media Tracking events in the above code. As I understand, Omniture media tracking will only work with SiteCatalyst version H.15 & above. Do you have any idea on this? We have H14 implemented on our site.
The questions might be very basic but I would be really thankfu to you if you could giveme some guidelines on these questions.
Many thanks again for the article and sharing the knowledge.
Best regards,
Amol
Amol,
1) I would recommend going with the JW Player. It has been very stable and well supported.
2) Using my code above as a guide you should be able to create JS functions that populate any combination of eVars or sProps. that you are currently using. You would need to use the s.tl() function as to not inflate page views.
-Rudi
Good job…. Thanks for the info.
Rudi- thank you so much for sharing this! I have made a few customizations to the code to try to simplify it as much as possible. Changes: Pulled out all ColdFusion. Minimized JavaScript. I hope others find this useful. http://topcweb.com/content/multimedia-tracking-analytics-omniture-jwplayer