Analytics

Analytics is served as a standalone service and you can integrate this component into any project other than the empPlayer. However, for the empPlayer, analytics are automatically sent.
Being a standalone component, EMPAnalytics can now be integrated with more products other than EMPPlayer.
This component collects playback events and sends them every 3s to EMP Exposure backend.
In case no event has been generated and a playback is taking place, a Heartbeat event is sent every 60 seconds.

Note: emp-player.js or emp-player-all.js include Analytics module.


This tutorial will show you how to use EMP Analytics outside of EMP Player.

Configuration

The following configuration values are required when the plugin is created:

  • customer / businessUnit values provided by EMP project team
  • serverURL EMP exposure API analytics endpoint
  • sessionToken token returned by EMP exposure login API
  • userId unique user identifier

If you use the EntitlmentEngine provided by EMP, in order to have analytics, all you have to do is pass these values in as options when instantiating the player:

 var exposureOptions = {
   'customer': customer.customer,
   'businessUnit': customer.businessUnit,
   'sessionToken': session.sessionToken,
   'exposureApiURL': apiUrl
 };

 var options = {
   'ericssonexposure': exposureOptions,
   //...
}

If you use your own entitlment engine, just include these properties directly as options in your entitlement engine:

yourEMPPlayerObjec.entitlementEngine_.options_ = {
  'customer': customer.customer,
  'businessUnit': customer.businessUnit,
  'sessionToken': session.sessionToken,
  'exposureApiURL': apiUrl
}

Methods

Init

// This method is called to initialize the component.
init();

Clear

// This method is called to clear all pending analytics events.
clear();

Debug

// This method is called to enable debug.
debug(debug);

Created

// This method is called to add an event for a new plackback
var params = {
  autoplay: true // default: false
};
created(sessionId, params);

Play

// This method is called when a 'play' command occurs (automatic or user triggered)
var params = {
  techName: TECH_NAME,           // default: false
  version: TECH_VERSION,
  deviceAppInfo: APP_INFO_STRING // optional
};
play(sessionId, startTime, params);

Playing

// This method is called when the media is actually playing
var params = {
  bitrate: BITRATE_IN_BYTES,
  duration: DURATION_IN_SECONDS,
  mediaLocator: MEDIA_URL         // optional (.mpd URL p.e.)
};
playing(sessionId, currentTime, params);

Paused

// This method is called when the playback is paused.
paused(sessionId, currentTime);

Seek

// This method is called when a seek occurs.
seek(sessionId, seekTime);

Start Casting

// This method is called when the player starts casting to an external device (chromecast p.e.).
startCasting(sessionId, nowTime);

Stop Casting

// This method is called when the player stops casting to an external device.
stopCasting(sessionId, nowTime);

Set Current Time

// This method should be called regularly to update the playback current time.
setCurrentTime(sessionId, nowTime);

Handshake

// This method is called when a new asset is loaded.
var params = {
  assetId: EMP_ASSET_ID,
  programId: EMP_PROGRAM_ID // optional
};
handshake(sessionId, params);

Resume

// This method is called when the playback is resumed from a paused state.
resume(sessionId, startTime, params);

Bitrate Changed

// This method is called when there is a change in the playback bitrate.
var params = {
  bitrate: BITRATE_IN_BYTES
};
bitrateChanged(sessionId, nowTime, params);

End Of Stream

// This method is called when the playback plays all the media to the end.
endOfStream(sessionId, params = {});

Error

// This method is called when an error occurs during a playback.
var params = {
  errorCode: ERROR_CODE,  // default: N/A
  errorMessage: ERROR_MESSAGE   // default: Unknown Error
};
error(sessionId, nowTime, params = {});

Dispose

// This method is called when a user exits/disposes a playback before it reaches the end of the stream.
dispose(sessionId, nowTime, params = {});

Waiting

// This method is called when the player enters goest into a waiting/buffering/loading state.
waiting(sessionId, nowTime, params = {});

Waiting Ended

// This method is called when the player leaves a waiting/buffering/loading state.
waitingEnded(sessionId, nowTime, params = {});

Get Session State

// This getter returns the current state of an analytics session (IDLE, PLAYING, DIRTY, FINISHED).
getSessionState(sessionId);

Dispatch Now

// This method immediately dispatches all events waiting to be sent to the backend.
dispatchNow(asyncCall = true);

Exit Ongoing Session

// This method automatically disposes ongoing analytics session
exitOngoingSession(nowTime);

Set Custom Attribute

// This method allows you to register a custom attribute that will be sent on every Playback.Started event
setCustomAttribute(key, value);

Clear Custom Attributes

// This method allows you to clear all custom events previously registered
clearCustomAttributes();

Usage Example

HTML Code

index.html

<html>
  <head>
    <script src="emp-analytics.min.js"></script>
    <script src="html5player-analytics-connector.js"></script>
  </head>
  <body>
    <video id="sample-video" autoplay>
      <source src=http://techslides.com/demos/sample-videos/small.webm type=video/webm>
      <source src=http://techslides.com/demos/sample-videos/small.ogv type=video/ogg>
      <source src=http://techslides.com/demos/sample-videos/small.mp4 type=video/mp4>
      <source src=http://techslides.com/demos/sample-videos/small.3gp type=video/3gp>
    </video>
    <script>
      // Global variables
      var SERVER_URL = 'YOUR_ANALYTICS_SERVER_URL';
      var CUSTOMER  = 'YOUR_CUSTOMER';
      var BUSINESS_UNIT = 'YOUR_BUSINESS_UNIT';
      var SESSION_TOKEN = 'YOUR_SESSION_TOKEN';

      // Video element to connect analytics to
      var html5player = document.getElementById('sample-video');

      // Creating and initiating the analytics service
      var analytics = new empAnalytics (SERVER_URL, CUSTOMER, BUSINESS_UNIT,  SESSION_TOKEN);
      analytics.init();

      // Creating and initiating the connector - the constructor receives the html5 player element and the analytics component
      var connector = new HTML5PlayerAnalyticsConnector(html5player, analytics);
      connector.init();
    </script>
  </body>
</html>

Javascript Connector Code

html5player-analytics-connector.js

var HTML5PlayerAnalyticsConnector = function(html5player, analytics) {
  this.player_ = html5player;
  this.analytics_ = analytics;

  this.onLoadStartBind = this.onLoadStart.bind(this);
  this.onCanPlayBind = this.onCanPlay.bind(this);
  this.onPlayingBind = this.onPlaying.bind(this);
  this.onEndedBind = this.onEnded.bind(this);
  this.onPauseBind = this.onPause.bind(this);
  this.onSeekedBind = this.onSeeked.bind(this);
  this.onErrorBind = this.onError.bind(this);
  this.onAbortBind = this.onAbort.bind(this);
  this.onWindowUnloadBind = this.onWindowUnload.bind(this);

  this.init();
};

// Initialization function - adds events to the provided player
HTML5PlayerAnalyticsConnector.prototype.init = function() {
  this.dispose();
  this.rnd_ = Math.random();
  this.player_.addEventListener('loadstart', this.onLoadStartBind);
  this.player_.addEventListener('canplay', this.onCanPlayBind);
  this.player_.addEventListener('playing', this.onPlayingBind);
  this.player_.addEventListener('ended', this.onEndedBind);
  this.player_.addEventListener('pause', this.onPauseBind);
  this.player_.addEventListener('seeked', this.onSeekedBind);
  this.player_.addEventListener('error', this.onErrorBind);
  this.player_.addEventListener('abort', this.onAbortBind);
  window.addEventListener('beforeunload', this.onWindowUnloadBind);
};

// Returns the session ID being currently analysed
HTML5PlayerAnalyticsConnector.prototype.getSessionId = function() {
  return 'ANALYTICS-DEMO-' + this.rnd_;
};

// Function triggered when player starts to load new source
HTML5PlayerAnalyticsConnector.prototype.onLoadStart = function() {
  this.onGeneric('HandShake', this.analytics_.handshake, function() {
    var params = {
      'assetId': 'YOUR_ASSET_ID',
      'programId': 'YOUR_PROGRAM_ID'
    };
    this.analytics_.handshake(this.getSessionId(), params);
  }.bind(this), false);
};

// Sends playback current time to the analytics engine
HTML5PlayerAnalyticsConnector.prototype.sendCurrentTime = function() {
  var sessionId = this.getSessionId();
  if (this.analytics_.getSessionState &&
      this.analytics_.getSessionState(sessionId) === 'PLAYING') {
    // This is important for the heartbeats to reflect accurate playback currentTime
    this.analytics_.setCurrentTime(sessionId, this.player_.currentTime);
  }
};

// Clears all timers bound to the analytics session
HTML5PlayerAnalyticsConnector.prototype.clearTimers = function() {
  if(this.currentTimeTimer_) {
    clearInterval(this.currentTimeTimer_);
  }
  this.currentTimeTimer_ = null;
};

// Registers a timer to send to the analytics engine regular currentTime updates
HTML5PlayerAnalyticsConnector.prototype.registerCurrentTimeTimer = function() {
  this.clearTimers();
  this.currentTimeTimer_ = setInterval(function() { this.sendCurrentTime(); }.bind(this), 1000);
};

// Function triggered when player is able to start playing media
HTML5PlayerAnalyticsConnector.prototype.onCanPlay = function() {
  var currentSessionId = this.getSessionId();
  this.onGeneric('Created', this.analytics_.created, function() {
    var params = {
      'autoplay': true
    };
    this.analytics_.created(currentSessionId, params);
    this.onStartPlay();
  }.bind(this), false);
};

// Function triggered when player receives a command to start playing media
HTML5PlayerAnalyticsConnector.prototype.onStartPlay = function() {
  this.onGeneric('Play', this.analytics_.play, function() {
    var params = {
      'techName' : 'NativeHTML5',
      'version' : '1.0',
      'techVersion': '1.0'
    };
    this.analytics_.play(this.getSessionId(), 0.0, params);
    this.registerCurrentTimeTimer();
  }.bind(this), false);
};

// Function triggered when the playback is paused
HTML5PlayerAnalyticsConnector.prototype.onPause = function() {
  this.onGeneric('Pause', this.analytics_.paused, function() {
    this.analytics_.paused(this.getSessionId(), this.player_.currentTime);
  }.bind(this), true);
};

// Function triggered when player actually displays the first media elements on the screen
HTML5PlayerAnalyticsConnector.prototype.onPlaying = function() {
  this.onGeneric('Playing', this.analytics_.playing, function() {
    let params = {
      'bitrate': 'YOUR_BITRATE',
      'duration': this.player_.duration,
      'mediaLocator': 'MEDIA_URL'
    };
    this.analytics_.playing(this.getSessionId(), this.player_.currentTime, params);
  }.bind(this), false);
};

// Function triggered when the playback reaches the end of the stream
HTML5PlayerAnalyticsConnector.prototype.onEnded = function() {
  var currentSessionId = this.getSessionId();
  this.onGeneric('End of Stream', this.analytics_.endOfStream, function() {
    this.analytics_.endOfStream(currentSessionId);
  }.bind(this), true, true);
};

// Function triggered when the media is seeked
HTML5PlayerAnalyticsConnector.prototype.onSeeked = function() {
  this.onGeneric('Seeked', this.analytics_.seek, function() {
    this.analytics_.seek(this.getSessionId(), this.player_.currentTime);
  }.bind(this), true);
};

// Function triggered when an error occurs within the player's playback session
HTML5PlayerAnalyticsConnector.prototype.onError = function() {
  this.onGeneric('Error', this.analytics_.error, function() {
    const params = {
      'code': 'YOUR_ERROR_CODE',
      'errorMessage': 'YOUR_MESSAGE'
    };
    this.analytics_.error(this.getSessionId(), this.player_.currentTime, params);
  }.bind(this), false);
};

// Function triggered when the playback session is interruped
HTML5PlayerAnalyticsConnector.prototype.onAbort = function() {
  this.onGeneric('Dispose', this.analytics_.dispose, function() {
    this.analytics_.dispose(this.getSessionId());
  }.bind(this), true, true);
};

// Function triggered when the browser window is unloaded/closed
HTML5PlayerAnalyticsConnector.prototype.onWindowUnload = function() {
  this.onGeneric('WindowClose', this.analytics_.dispose, function() {
    this.analytics_.dispose(this.getSessionId(), this.player_.currentTime);
    this.analytics_.dispatchNow(false);
  }.bind(this), true);
};

HTML5PlayerAnalyticsConnector.prototype.onGeneric = function(eventName, eventFnc, callback) {
  if (eventFnc && callback) {
    callback();
  }
  else {
    console.log('\'' + eventName + '\' was discarded.');
  }
};

// Function used to unregister callbacks for player events and clear timers
HTML5PlayerAnalyticsConnector.prototype.dispose = function() {
  this.clearTimers();
  this.player_.removeEventListener('loadstart', this.onLoadStartBind);
  this.player_.removeEventListener('canplay', this.onCanPlayBind);
  this.player_.removeEventListener('playing', this.onPlayingBind);
  this.player_.removeEventListener('ended', this.onEndedBind);
  this.player_.removeEventListener('pause', this.onPauseBind);
  this.player_.removeEventListener('seeked', this.onSeekedBind);
  this.player_.removeEventListener('error', this.onErrorBind);
  this.player_.removeEventListener('abort', this.onAbortBind);
  window.removeEventListener('beforeunload', this.onWindowUnloadBind);
};