Dash.js

Dash js

In a recent project I was asked to add a video player that supports the following features:

  • Streaming video
  • .mpd based video display
  • Quality selection
  • Subtitles

It reminded me somewhat of youtube 🙂

When looking for a suitable solution, I’ve found Dash.js. Dash.js is a library that is meant to run MPEG DASH (you can read more about it here).  In short, it takes care of streaming and .mpd.  

My aim was to customize the control buttons (add the quality and subtitles selection in addition to combine the play/pause buttons).  In addition, I had to get the .mpd from a WOWZA server.  You can read more about WOWZA here.

In this post I’ll describe how I’ve implemented the whole setup to fit the specs above. 

The final outcome is in this github project: https://github.com/DoronMaman/DashVideoJs

Hook up with WOWZA

WOWZA server generates the MPD file.  This .mpd file was served to the client.

The MPD file holds all the of the video configuration: quality, translation, and audio config.  The video is segmented into small parts, and this is reflected in the .mpd file.  Here’s an .mpd example:

<?xml version="1.0" encoding="UTF-8"?>

<MPD type="static" xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:dvb="urn:dvb:dash-extensions:2014-1"
     profiles="urn:dvb:dash:profile:dvb-dash:2014,urn:dvb:dash:profile:dvb-dash:isoff-ext-live:2014"
     minBufferTime="PT2.049S" maxSegmentDuration="PT3.84S" mediaPresentationDuration="PT1H0.040S">

</ProgramInformation>

<BaseURL serviceLocation="A" dvb:priority="1" dvb:weight="1">http://rdmedia.bbc.co.uk/dash/ondemand/testcard/1/
</BaseURL>

<Period duration="PT1H0.040S" start="PT0S">

<AdaptationSet startWithSAP="2" segmentAlignment="true" id="1" sar="1:1" mimeType="video/mp4">

    <InbandEventStream schemeIdUri="tag:rdmedia.bbc.co.uk,2014:events/ballposition" value="1"/>

    <Role schemeIdUri="urn:mpeg:dash:role:2011" value="main"/>

    <BaseURL>avc3-events/</BaseURL>

    <SegmentTemplate startNumber="1" timescale="1000" duration="3840" media="$RepresentationID$/$Number%06d$.m4s"
                     initialization="$RepresentationID$/IS.mp4"/>

    <Representation id="960x540p50" codecs="avc3.64001f" height="540" width="960" frameRate="50" scanType="progressive"
                    bandwidth="2814440"/>

    <Representation id="256x144p25" codecs="avc3.42c015" height="144" width="256" frameRate="25" scanType="progressive"
                    bandwidth="158128"/>

    <Representation id="704x396p50" codecs="avc3.64001f" height="396" width="704" frameRate="50" scanType="progressive"
                    bandwidth="1572456"/>

    <Representation id="1920x1080i25" codecs="avc3.640028" height="1080" width="1920" frameRate="25"
                    scanType="interlaced" bandwidth="8060152"/>

    <Representation id="512x288p25" codecs="avc3.4d4015" height="288" width="512" frameRate="25" scanType="progressive"
                    bandwidth="440664"/>

    <Representation id="640x360p25" codecs="avc3.42c01e" height="360" width="640" frameRate="25" scanType="progressive"
                    bandwidth="166680"/>

    <Representation id="384x216p25" codecs="avc3.42c015" height="216" width="384" frameRate="25" scanType="progressive"
                    bandwidth="283320"/>

    <Representation id="896x504p25" codecs="avc3.64001f" height="504" width="896" frameRate="25" scanType="progressive"
                    bandwidth="1375216"/>

    <Representation id="1280x720p50" codecs="avc3.640020" height="720" width="1280" frameRate="50"
                    scanType="progressive" bandwidth="5072376"/>

    <Representation id="704x396p25" codecs="avc3.4d401e" height="396" width="704" frameRate="25" scanType="progressive"
                    bandwidth="834352"/>

    <Representation id="192x108p25" codecs="avc3.42c015" height="108" width="192" frameRate="25" scanType="progressive"
                    bandwidth="88648"/>

    <Representation id="448x252p25" codecs="avc3.42c015" height="252" width="448" frameRate="25" scanType="progressive"
                    bandwidth="437856"/>

    <Representation id="192x108p6_25" codecs="avc3.42c015" height="108" width="192" frameRate="25/4"
                    scanType="progressive" bandwidth="31368"/>

</AdaptationSet>

</MPD>

Using Dash.js API, I’ve loaded the .mpd file into the player:

player.load(‘mpdFileName.mpd’);

Controls

The controls section of the Dash.js player had to be modified according to the project’s specs.  As mentioned above, I had to add subtitles and quality selection. In addition:

  • the play and pause buttons are separate.  I had to combine both as well.
  • The “full screen” was giving the video the size of half the screen for some reason. I had to change that as well.

 

The customization steps are as follows:

  • Add the controls you want to the Player’s control HTML template (see code)

Adding the controls is very easy. The control code is wrapped up inside a div with the class ‘video-controller’.  Here’s the full control code with the changes from the origin highlighted:

<div id="videoController" class="video-controller unselectable">

    <div id="playPauseBtn" class="btn-play-pause" title="Play/Pause">

        <span id="iconPlayPause" class="icon-play"></span>

    </div>

    <span id="videoTime" class="time-display">00:00:00</span>

    <div id="fullscreenBtn" class="btn-fullscreen" title="Fullscreen">

        <span class="icon-fullscreen-enter"></span>

    </div>

    <input type="range" id="volumebar" class="volumebar" value="1" min="0" max="1" step=".01"/>

    <div id="muteBtn" class="btn-mute" title="Mute">

        <span id="iconMute" class="icon-mute-off"></span>

    </div>

    <div title="Setting" id="qualitybutton" class="btn-setting" id="muteBtn1">

        <span class="icon-setting" id="iconMute1"></span>

    </div>

    <div id="captionBtn" class="btn-caption" title="Closed Caption">

        <span class="icon-caption"></span>

    </div>

    <span id="videoDuration" class="duration-display">00:00:00</span>

    <div class="seekContainer">

        <input type="range" id="seekbar" value="0" class="seekbar" min//9/="0" step="0.01"/>

    </div>

</div>
  • Get the relevant elements in javascript:

playPauseBtn = document.getElementById(“playPauseBtn”);

captionBtn = document.getElementById(“captionBtn”);

fullscreenBtn = document.getElementById(“fullscreenBtn”);

qualitybutton= document.getElementById(“qualitybutton”);

  • Hook the relevant events (mostly “click”) to the buttons and setup the event handler methods:

fullscreenBtn.addEventListener(“click”, onFullscreenClick);

playPauseBtn.addEventListener(“click”, onPlayPauseClick);

For the captions and quality buttons, it’s a bit more complex, since we need the meta data in order to construct the selection menu. So we hook up with the plugin’s data load events:

player.on(dashjs.MediaPlayer.events.TEXT_TRACKS_ADDED, onTracksAdded);

player.on(dashjs.MediaPlayer.events.PLAYBACK_METADATA_LOADED, onQualitiesAdded);

You can look at the code in order to understand how both types (simple button and select menu) work and how to build the select menus from the meta data.

The methods are contained by comments:

FullScreen – FULLSCREEN

PlayPause – PLAYBACK

Captions – CAPTION MENU

Quality selector – QUALITY MENU

 

Leave a Reply