empanalytics-devdeprecated

This component collects playback events and sends them every 3s to EMP Exposure backend.

Usage no npm install needed!

<script type="module">
  import empanalyticsDev from 'https://cdn.skypack.dev/empanalytics-dev';
</script>

README

General Information

Analytics is now served as a standalone service and you can integrate this component into any javascript-based project other than the empPlayer. Android/iOS libraries already send analytics by themselves. Also, for the empPlayer, analytics are sent automatically aswell. If your project is using an old analytics setup: for instance via plugin or calling the method setAnalytics, you can remove that code from your project as it is no longer necessary. Being a standalone component, EMPAnalytics can now be integrated with more products other than EMPPlayer. This tutorial will show you how to use EMP Analytics. When using the new component, please do not forget to remove the old video.js analytics plugin from your setup, otherwise the EMPPlayer might send duplicated analytics events. The new analytics service is delivered with some important bug fixes. We will no longer deliver new features or bug fixes to the old video.js analytics plugin.

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.

Usage

Installation

npm install empanalytics

In browser, node or react-native


// ES modules
import EMPAnalytics from 'EMPAnalytics';
// CommonJS
var EMPAnalytics = require('EMPAnalytics');


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

Constructor

var deviceInfoData = {
  deviceModel: DEVICE_MODEL_STRING, // default: DESKTOP
  userAgent: USER_AGENT_STRING, // default: window.navigator.userAgent
  screenHeight: SCREEN_HEIGHT,  // default: window.screen.height
  screenWidth: SCREEN_WIDTH,  // default: window.screen.width
  deviceName: DEVICE_NAME_STRING, // default: window.navigator.product
  deviceOS: DEVICE_OS_STRING, // default: platform.os.family
  deviceOSVersion: DEVICE_OS_VERSION_STRING,  // default: platform.os.version
  type: DEVICE_TYPE_STRING, // default: WEB
  deviceManufacturer: DEVICE_MANUFACTURER_STRING,
  deviceId: DEVICE_ID_STRING,
};
var properties = {
  disableWebWorkers: DISABLE_BOOLEAN  // default: false
}
constructor(serverURL, customer, businessUnit, sessionToken, userId, deviceInfoData, properties)

Init

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

Clear

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

Debug

// This property is called to enable/disable debug.
debug = true | false;

Created

// This method is called to add an event for a new plackback
// Event: Playback.Created
var params = {
  autoplay: true
  techName: TECH_NAME,
  version: TECH_VERSION,
  player: PLAYER_IDENTIFIER,
  deviceAppInfo: APP_INFO_STRING, // optional
  playMode: PLAY_MODE_STRING  // optional
};
created(sessionId, params);

Play

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

Playing

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

Paused

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

Seek

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

Start Casting

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

Stop Casting

// This method is called when the player stops casting to an external device.
// Event: Playback.StopCasting
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.
// Event: Playback.HandshakeStarted
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.
// Event: Playback.Resumed
resume(sessionId, startTime, params);

Bitrate Changed

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

DRM Session Update

// Method for when DRM Session Update
// Event: Playback.DRM
var params = {
  message: [WIDEVINE_CERTIFICATE_REQUEST, FAIRPLAY_CERTIFICATE_REQUEST 
            WIDEVINE_CERTIFICATE_RESPONS, FAIRPLAY_CERTIFICATE_RESPONSE
            WIDEVINE_LICENSE_REQUEST, PLAYREADY_LICENSE_REQUEST, FAIRPLAY_LICENSE_REQUEST 
            WIDEVINE_LICENSE_RESPONS, PLAYREADY_LICENSE_RESPONSE, FAIRPLAY_LICENSE_RESPONSE
            WIDEVINE_LICENSE_ERROR, PLAYREADY_LICENSE_ERROR, FAIRPLAY_LICENSE_ERROR]
  code: 0,
  info: 'info',
};
drmSessionUpdate(sessionId, params);

End Of Stream

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

Error

// This method is called when an error occurs during a playback.
// Event: Playback.Error
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.
// Event: Playback.Aborted
dispose(sessionId, nowTime, params = {});

Waiting

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

Waiting Ended

// This method is called when the player leaves a waiting/buffering/loading state.
// Event: Playback.BufferingEnded
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);
};