dash.js – How to use DASH events

Http Live Streaming (HLS) and Dynamic Adaptive Streaming over HTTP (MPEG-DASH) are the two main formats for adaptive streaming. While HLS is natively supported on most of its target platforms (iOS and MacOSX), we need external players for MPEG-DASH. For browser based environments there are two great open-source options, namely shaka-player and dash.js.  Both are written in JavaScript and use the MediaSourceExtensions (MSE) and the EncryptedMediaExtensions (EME) to enable playback directly in the browser without the need for external plugins. Both offer a wide set of features and have an active community. Shaka-player is maintained and developed by Google, dash.js is the official reference player of the DASH Industry Forum

In this series of blog posts I will focus on dash.js. I will explain how certain features are implemented and how they can be used within applications. To start off we focus on DASH events and how they can be used for features like metadata signaling and ad-insertion. But first we need to clarify what DASH events are and how they work.

What are DASH events?

You can think of DASH events as a mean to signal additional information to the DASH client or the underlying application. Events are timed and therefor have a start time and a duration. They can be part of the manifest file or be embedded in the ISOBMFF based media files as an EMSG box. For means of simplicity we focus on the manifest variant. Lets take a closer look on the event specific parts of such a manifest file:

<EventStream schemeIdUri="urn:custom-data" value="1">
  <Event presentationTime="5" duration="1" id="1">
      Hello world
   </Event>
  <Event presentationTime="10" duration="1" id="2">
      Hello world 2
   </Event>
</EventStream>

We introduce two new tags in our manifest file. The <EventStream> and the <Event> element. Think of EventStreams as a container for a specific type of event. Each has a @schemeIdUri which identifies the type of event and an optional @value to distinguish between events of the same type. Certain DASH-specific events with a reserved @schemeIdUri are directly processed by the DASH client and not dispatched to the underlying application.
Inside the <Eventstream> container we can specify multiple events. In the example above we defined two events. Each <Event> has a @presentationTime and a @duration. The presentation time is relative to the start of the parent <Period> element. Both, @presentationTime and @duration, have to be divided by the @timescale attribute to get the respective values in seconds. In our case no @timescale is defined, so we can assume a default value of 1.  The payload of the event is wrapped within the <Event> tags.

 DASH events in dash.js

In order to handle DASH events within the player, dash.js has separate controller called EventController.js. Without going into too much detail, we can take a closer look at the part in which the events are actually dispatched:

if (curr.eventStream.schemeIdUri == MPD_RELOAD_SCHEME && curr.eventStream.value == MPD_RELOAD_VALUE) {
   if (curr.duration !== 0 || curr.presentationTimeDelta !== 0) { 
      refreshManifest();
   }
 } else {                           
   eventBus.trigger(curr.eventStream.schemeIdUri, {event: curr});
  }

DASH events with a schemeIdUri set to “urn:mpeg:dash:event:2012” (MPD_RELOAD_SCHEME) and a value of “1” (MPD_RELOAD_VALUE) are directly processed by dash.js. In that case an aperiodic MPD reload is triggered. In any other case the event is dispatched to the underlying application using the internal event bus.

How to use DASH events in an application

Now that we know what DASH events are and how they are handled within dash.js we can start using them. Let us create a small application which shows the payload of our events in a <div> container below our video element. The corresponding index.html is very straight forward and looks like the following:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8"/>
    <title>DASH events example</title>
        
    <style>
        video {
            width: 640px;
            height: 360px;
        }
    </style>
</head>
<body>
<div class="code">
    <video controls="true">
    </video>
</div>
<div >
   Event output: <span id="event-output"></span>   
</div>
<script>
    document.addEventListener("DOMContentLoaded", function () {
        var app = new App();
        app.init();
    });
</script>
<script src="dash.all.debug.js"></script>
<script src="main.js"></script>
</body>
</html>

We simply add all our dependencies and initialize or sample application. For the latter we only need a few lines of code:

var CUSTOM_EVENT_SCHEME = 'urn:custom-data';

var MPD_URL = 'events.mpd';

var App = function () {
 this.outputDiv = document.getElementById('event-output');
};

App.prototype.init = function () {
    this.video = document.querySelector("video");
    this.player = dashjs.MediaPlayer().create();
    this.player.initialize(this.video, MPD_URL, true);
    this.player.on(
     CUSTOM_EVENT_SCHEME, this.customEventHandler.bind(this));
};

App.prototype.customEventHandler = function (payload) {
    this.outputDiv.innerText = payload.event.messageData;
};

The crucial part is the last line of the init function in which we register for our custom events. The dash.js player will dispatch the events at the appropriate time and we output the content of the messageData attribute in the index.html. The complete example code can be found here.

This concludes our small example of DASH events and how they are handled within dash.js Obviously there are much more sophisticated use cases for those types of events. For example, a switch to an advertisement could be signaled. Morever, DASH events are great to provide additional information about current objects like buildings or an actor/actress to the viewer.