HTML 5 Player Goodness

Times are a changin’ and my department knew that we needed something more than the  Flash based players we had been using for quite some time. One of the bigger forces that drove us to need an HTML 5 player was the rise in ‘iDevices’, such as the iPhone and iPad by Apple. The charge was given to me to create a player that would work nice with the existing infrastructure and able to deliver media in both video and audio formats.

I chose to use jPlayer, a jQuery plugin. The reasons that I chose it was because it could work with both video and audio, it is skinable, and had hooks that I could use to my advantage. I then wrote a wrapper around jPlayer that added the missing functionality that was necessary for my department, with included keyboard controls, links to transcripts, and captioning. To help keep things consistent, the HTML 5 player uses the same transcripts and captions that the Flash player does.

I first created created a mock-up of the controls bar in Photoshop. The player was inspired by a custom player audio skin at Premium Pixels. I then took the design and ran with it to make it work for our needs. Other than the play and speaker icons, the icons where created for this purpose.

Mock-up of the ELC HTML 5 player

I then wrote the HTML, CSS, and JavaScript to implement the mock-up. I then created a sprite file that allowed all of the elements to be located on one PNG file. This helps avoid any loading flash with elements that change state when rolled over.

Sprites used in the player

The final results look like the following:

Shows the video player in HTML form
Video player in HTML form
Screen shot showing the audio variant of the HTML 5 player
Audio variant of the HTML 5 player

Here is a snippet of the JavaScript wrapper used to control the player:

// create a self extrating function to run everything in and keep all of my code out of the global scope
// bring in the global window object by default
(function(window,undefined){
  var document = window.document; // get the global document object.
  var $ = window.$; // get the jQuery object

  if(window.vp5 == undefined)
  { // vp5 has not been defined yet
    var vp5 = {}; // the vp5 player namespace
  }
  else
  { // we have already loaded this script, lets not load and run it again.
    return true;
  }

  vp5.defaults = {
    // default options for our players
    playerRoot: '/~d-ctel/mediaPlayerTest/',
    fileRoot: this.mediaRoot + 'scripts/JavaScript/',
    mediaRoot: '/~d-ctel/filesForMedia/',
    coursesFolder: 'courses/',
    captionXmlName: 'captions.xml',
    pNumber: 1 // for unique number for the players
  };

  vp5.mediaTags = new Array(); // make mediaTags an array to store our players, kinda like a catalog

  // create our media player object to store our stuff in
  var mp5 = function() {
    //var me = this;
    //this._init();
    //alert(me);
  };

  // this fires when jQuery is ready
  $(document).ready(function(){
    $('.mediaContainer_1').each(function(index, element) {

      vp5.mediaTags[vp5.defaults.pNumber] = new mp5; // each player gets its a mp5 object so we can build its own jplayer object. The array allows us to catalog each player on the page
      vp5.mediaTags[vp5.defaults.pNumber].pNumber = vp5.defaults.pNumber; // capture the player number for this perticular player
      vp5.mediaTags[vp5.defaults.pNumber].href = location.pathname; // the current url. This will possible change if/when multiple players are implemented on a page, or if the player is embeded
      //alert($(this).find('div').is('.video-jp-box'));
      if($(this).find('div').is('.video-jp-box'))
      { // we have a video tag
        vp5.mediaTags[vp5.defaults.pNumber].findMediaInfo(this, 'video');
        vp5.mediaTags[vp5.defaults.pNumber].buildVideoInterface(); // build the jPlayer video user interface html
      }
      else if($(this).find('div').is('.audio-jp-box'))
      { // we have an audio tag
        vp5.mediaTags[vp5.defaults.pNumber].findMediaInfo(this, 'audio');
        vp5.mediaTags[vp5.defaults.pNumber].buildAudioInterface(); // build the jPlayer audio user interface html
      }

      vp5.mediaTags[vp5.defaults.pNumber]._init(); // initialize the current player object so the events will work

      vp5.defaults.pNumber++; // incrament pNumber by one
    });

    /***** Generic Events *****/
    // enables using the keyboard to control the players
    // we have to find which player was in focus and generated the event, so we can pass the event to it
    $('.mediaContainer_1').keydown(function(event) {
      //vp5.keyEvents(event);
      var nodeName = event.target.nodeName;
      var mediaContainerId = $(nodeName + ' div[id^="jp_container_"]').attr('id');
      var num = mediaContainerId.substr(mediaContainerId.lastIndexOf('_') + 1);
      vp5.mediaTags[num].keyEvents(event);
    });

    // this helps the user understand which player has focus
    $('.mediaAnchor').focus(function(event) {
      $(this).addClass('activePlayer');
    }).blur(function(event){
      $(this).removeClass('activePlayer');
    });
    /***** End Generic Events *****/
  });