MPEG-DASH Tutorial: Embedding an adaptive streaming video within your HTML5 application


Poor quality streaming video solutions resulted in an estimated $2.16 Billion of lost revenue in 2012 (according to the 2013 Conviva Viewer Experience Report). That’s a LOT of zeros!

Since we at Microsoft Open technologies, Inc. (MS Open Tech) believe this is simply unacceptable, we’d like to share some ways in which developers can leverage open source code to ensure their own delivery of video is of the highest possible standard.

For this tutorial, we have chosen to use the dash.js player to deliver MPEG-DASH video to any browser that supports the W3C Media Source Extensions (MSE).

What is MPEG-DASH and dash.js?

MPEG-DASH is an ISO standard for the adaptive streaming of video content, which offers significant benefits for those who wish to deliver high-quality, adaptive video streaming output. Many of these benefits directly address opportunities for improving user engagement identified in the Conviva report, such as:

  • 226% increase in video consumption given a buffer-less experience
  • Fourfold increase in likelihood of watching the video if start-up is less than two seconds
  • 25% increase in consumption for higher quality streams

With MPEG-DASH, the video stream will automatically drop to a lower definition when the network becomes congested. This reduces the likelihood of the viewer seeing a “paused” video while the player downloads the next few seconds to play (aka buffering). As network congestion reduces, the video player will in turn return to a higher quality stream. This ability to adapt the bandwidth required also results in a faster start time for video. That means that the first few seconds can be played in a fast-to-download lower quality segment and then step up to a higher quality once sufficient content has been buffered.

Dash.js is an open source MPEG-DASH video player written in JavaScript. Its goal is to provide a robust, cross-platform player that can be freely reused in applications that require video playback. It provides MPEG-DASH playback in any browser that supports the W3C Media Source Extensions (MSE), today that is Chrome and IE11 (other browsers have indicated their intent to support MSE).

Creating a browser-based streaming video player

In simple terms, the intention of the below example is to demonstrate how easy it can be to build an MPEG-DASH player into your website. If you have any problems applying this example to your own real world use case, pop over to the dash.js community mailing list, where we will be happy to help you out.

To create a simple web page that displays a video player with the expected controls such a play, pause, rewind etc., you will need to:

  • Create an HTML page
    • Add the video tag
  • Add the dash.js player
  • Initialize the player
  • Add some CSS style
  • View the results in a browser that implements MSE

The only part of this process that may be new to most of you is the “Initialize the player” step. This step can be completed in just a handful of lines of JavaScript code. Using dash.js, it really is that simple to embed MPEG-DASH video in your browser based application – including native applications that use HTML and JavaScript!

Creating the HTML page

The first step is to create a standard HTML page containing the <video> element, save this file as basicPlayer.html. You will tell the player to display its controls (by including the “control”), but won’t need to initialize any other aspects of the player in the HTML. This configuration could be managed completely in JavaScript, if desired.

Here is the HTML you should have in basicPlayer.html:

<!DOCTYPE html>
<html>
  <head><title>Adaptive Streaming in HTML5</title></head>
  <body>
    <h1>Adaptive Streaming with HTML5</h1>
    <video id="videoplayer" controls></video>
  </body>
</html>

Since there is nothing unusual about this HTML. let’s move quickly on to the dash.js player code.

Adding the dash.js player

To add the dash.js reference implementation to the application, you’ll need to grab the dash.all.js file from the 1.0 release of dash.js project. This should be saved in the JavaScript folder of your application. This file is a convenience file that pulls together all the necessary dash.js code into a single file. If you have a look around the dash.js repository, you will find the individual files, test code and much more, but if all you want to do is use dash.js, then the dash.all.js file is what you need.

If you prefer you could use the code from the master branch. The master branch contains the latest fully tested version of the code. At the time of writing this tutorial will work with the mater branch, and this should always be the case. The adventurous might want to use the development branch, which contains all the latest changes that have been accepted by the project community. This is the code that will go into the next release of dash.js. While there has been some testing on this branch, it is development code to be used at your own risk.

Should you encounter any problems with any version of the code, please discuss them on the projects mailing list. If you uncover any bugs please report them via our issue tracker. In addition, as an open source project, we welcome appropriate code contributions, please fork the project on GitHub and issue a pull request.

Whichever version of dash.all.js you choose to use, you will need to be load it into your application, to do this add a script tag to the head section of basicPlayer.html:

<!– DASH-AVC/265 reference implementation –>
<script src=”js/dash.all.js”></script>

Next, create a function to initialize the player when the page loads. Add the following script after the line in which you load dash.all.js:

<script>
// setup the video element and attach it to the Dash player
function setupVideo() {
  var url = "http://wams.edgesuite.net/media/MPTExpressionData02/BigBuckBunny_1080p24_IYUV_2ch.ism/manifest(format=mpd-time-csf)";
  var context = new Dash.di.DashContext();
  var player = new MediaPlayer(context);
                  player.startup();
                  player.attachView(document.querySelector("#videoplayer"));
                  player.attachSource(url);
}
</script>

This function first creates a DashContext. This is used to configure the application for a specific runtime environment. From a technical point of view, it defines the classes that the dependency injection framework should use when constructing the application. In most cases, you will use Dash.di.DashContext.

Next, instantiate the primary class of the dash.js framework MediaPlayer. This class contains the core methods needed such as play and pause, manages the relationship with the video element and also manages the interpretation of the Media Presentation Description (MPD) file which describes the video to be played. You will be working with this MediaPlayer from now on.

The startup() function of the MediaPlayer is called to ensure that the player is ready to play video. Amongst other things this function ensures that all the necessary classes (as defined by the context) have been loaded. Once the player is ready, you can attach the video element to it using the attachView() function. This enables the MediaPlayer to inject the video stream into the element and also control playback as necessary. Finally, pass the URL of the MPD file to the MediaPlayer so that it knows about the video it is expected to play.

The setupVideo() function just created will need to be executed once the page has fully loaded. Do this by using the onload event of the body element. Change your <body> element to:

<body onload="setupVideo()">

Finally, set the size of the video element using CSS. In an adaptive streaming environment, this is especially important because the size of the video being played may change as playback adapts to changing network conditions. In this simple demo simply force the video element to be 80% of the available browser window by adding the following CSS to the head section of the page:

<style>
video {
  width: 80%;
  height: 80%;
}
</style>

Playing video

That’s it. You now have a fully functional JavaScript MPEG-DASH player that will work in any browser that supports MSE. Point your browser at your basicPlayback.html file, click play on the video controls and watch your video in all its adaptive streaming glory.

From here it is a relatively small step to create, for example, a Windows Store application using dash.js. Or you could create a module for Drupal, Joomla, WordPress or some other content management system.

Our goal with dash.js is to make it as reusable as possible. If you need any help getting it to work for you contact us through the project mailing list. We will be happy to help!

Comments (16)

  1. NumbStill says:

    Thank you for your post.

    Technical notes, though –

    Please, use best practice instead of <body onload> and inline scripts. Put the code in a separate JavaScript file and add an 'window.addEventListener("load", setupVideo, false);' statement instead.

    Same for the inline style (or perhaps just put ' width="80%" height="80%"' to <video> and be done with it, if it works for you).

    Stop encouraging bad practice and ugly code.

  2. JayM [MSFT] says:

    Good suggestion NumbStill. Alternatively you can put your script below the HTML content and not use Load at all.

  3. Chris Allen says:

    Good tut- but as an edit, you forgot to close your <head>  tag

  4. Steve Hartzog says:

    I've been trying to use this code or the DASH 1.0 player to play media from my WAMS account. Unfortunately Chrome 33 (which supports Media Source Extensions, and thus DASH) gives me a standard CORS error:

    XMLHttpRequest cannot load totallyrad.origin.mediaservices.windows.net/…/manifest(format=mpd-time-csf). No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000&#39; is therefore not allowed access.

    Unfortunately, you can't just upload a crossdomain.xml to WAMS (it's a disallowed type for some reason) so the browser will allow the request. I found an article on how to enable CORS for Azure Storage but this is to enable an upload scenario from a browser: gauravmantri.com/…/windows-azure-storage-and-cors-lets-have-some-fun. This is the opposite of what I need.

    It seems that Azure doesn't really support dash if this is not possible… so I must have done something wrong because someone has done this, as this media link works in the dash player: wams.edgesuite.net/…/manifest(format=mpd-time-csf).

  5. Nick Drouin says:

    Hi Steve,

    I'm the Program Manager in charge of the DASH server features in WAMS.

    I'm getting a 404 on your URL:

    totallyrad.origin.mediaservices.windows.net/…/manifest(format=mpd-time-csf)

    Also, I get a 404 for the Smooth version of the same manifest:

    totallyrad.origin.mediaservices.windows.net/…/manifest

    This tells me there is probably an issue with the either the asset/filenames, or the locator.  

    The reason I checked for a Smooth manifest is if you don't have a 'streaming reserved unit' there will be no 'on the fly' conversion (aka Dynamic-Packaging) of your source files to DASH/CSF for your account.

    Also, if you don't have a streaming reserved unit, then the word 'Premium' will be missing from the Pragma header below.

    Here is some content straight out of my WAMS production account:

    dashdemo.origin.mediaservices.windows.net/…/Manifest(format=mpd-time-csf)

    Here is the response header:

    HTTP/1.1 200 OK

    Cache-Control: max-age=259200

    Pragma: IISMS/6.0,IIS Media Services Premium by Microsoft

    Content-Type: application/dash+xml

    Content-Encoding: gzip

    Expires: Sun, 02 Feb 2014 17:39:22 GMT

    ETag: "0x8D0165D5D5EDB93"

    Vary: Accept-Encoding

    Server: Microsoft-IIS/7.5 IISMS/6.0

    X-Powered-By: ASP.NET

    X-Content-Type-Options: nosniff

    Access-Control-Allow-Origin: *

    Date: Thu, 30 Jan 2014 17:39:22 GMT

    Content-Length: 1110

    As you can see, we have:

    Access-Control-Allow-Origin: *

    So, next steps for you would be:

    Check the streaming URL, perhaps hit Publish again if you've Unpublished it.

    Check that you can get a Smooth manifest for you file.

    Check that the smooth plays (smf.cloudapp.net/healthmonitor).

    Check that you have a streaming RU.

    Check that the DASH manifest downloads.

    Try it again in the DASH-IF sample player.

    Regards,

    -Nick

  6. Steve Hartzog says:

    I received feedback on the MPEG-DASH github issue I opened (github.com/…/118), and after I followed their suggestions, I replied:

    Thanks for the quick response. I got it working based on the feedback. Here's what I found:

    1. My WAMS account didn’t have a Reserved Unit (RU). Although I believe this is for encoding, I assigned one.

    2. My content was unpublished, so I published it.

    3. Finally, my encoding job apparently wasn’t using smooth streaming – the default in the MediaServicesGettingStarted project is “H264 Broadband 720p”.

    I had tested my uploaded video in a Flash player with the Microsoft OSMF smooth streaming plugin… and it had worked. So I'm completely confused as to why it became unpublished, or how the smooth streaming plugin played it previously. But, it works now… so thank you for the tips!

  7. Tércio says:

    Hi Steve,

    How I can make a MPEG-DASH Server and Client?

    Thank you.

  8. Ross Gardler says:

    Tércio,

    This post tells you how to build a HTML MPEG-DASH client. If you have more specific questions about using dash.js in a client I would encourage you to ask on the projects mailing list (see the last link in the post above).

    With respect to building an MPEG-DASH server it really depends on your specific needs, there are a number of different ways you could do this. John Deutscher has a blog on how to use Windows Azure Media Services for this purpose. blog.johndeutscher.com/…/mpeg-dash-preview-from-windows-azure-media-services

  9. urvashi says:

    i am writing my master thesis  abt the different adaptive methods for videos.  i have a questions. how is dynamic bitrate adaption implemented for DASH videos?

  10. Ross Gardler says:

    @urvashi you can see the implementation at github.com/…/dash.js

    This is a player build against the Dash/AVC-264 implementation guidelines which you can read at dashif.org/…/DASH-AVC-264-base-v1.03.pdf

  11. Boyd Badten says:

    I'm using a CMS (DotNetNuke) and don't have direct access to the <body> tag….so I could not make this work. I'm not a programmer–but I'm smart enough to make a lot of things like this work…your <body> tag trick stopped me.

  12. Ross Gardler says:

    I'm not familiar with DotNetNuke but if you can embed scripts in the page then you can do as NumbStill correctly suggests in the first comment – use 'window.addEventListener("load", setupVideo, false);' instead.

  13. Danny says:

    Thanks for dash.js player.  My only complaint is that I can't seem to find any documentation anywhere.  Specifically I would like to be able to toggle logging.

    Trying to dig through minified dash.all.js is not fun, can't figure out how to disable all this verbose logging.

  14. dan says:

    Using dash.js, but can't figure out how to disable logging.  Anyone know if there's a way to toggle logging off?

  15. Ross Gardler says:

    Dan, the best place to ask your question is within the dash.js project community – see github.com/…/How-to-Contribute for a link to their issue tracker and discussion forums

  16. Veeru says:

    There are different ways to create MPD, I use MP4box to create video segments and MPD.

    My MPD is like the below. Using the MPD when I play it using Dash.js it is not playing but downloading the content but the index list is not moving from 0.

    MPD:

    <MPD type="static" xmlns="urn:mpeg:DASH:schema:MPD:2011" profiles="urn:mpeg:dash:profile:full:2011" minBufferTime="PT1.5S" mediaPresentationDuration="PT0H1M0.29S">

    <ProgramInformation moreInformationURL="http://gpac.sourceforge.net"&gt;

    </ProgramInformation>

    <Period start="PT0S" duration="PT0H1M0.29S">

     <AdaptationSet>

      <ContentComponent id="1" contentType="video"/>

      <ContentComponent id="2" contentType="audio" lang="und"/>

      <Representation id="1" mimeType="video/mp4" codecs="avc1.640028,mp4a.40.02" width="1280" height="1024" sampleRate="44100" numChannels="2" lang="und" startWithSAP="1" bandwidth="317599">

       <BaseURL>Recorded_gopi_dash.mp4</BaseURL>

       <SegmentList timescale="1000" duration="13915">

        <Initialization range="0-1264"/>

        <SegmentURL mediaRange="1265-512125" indexRange="1265-1465"/>

        <SegmentURL mediaRange="512126-774261" indexRange="512126-512254"/>

        <SegmentURL mediaRange="774262-1165553" indexRange="774262-774390"/>

        <SegmentURL mediaRange="1165554-1484521" indexRange="1165554-1165682"/>

        <SegmentURL mediaRange="1484522-1819145" indexRange="1484522-1484626"/>

        <SegmentURL mediaRange="1819146-2122548" indexRange="1819146-1819310"/>

        <SegmentURL mediaRange="2122549-2375382" indexRange="2122577-2122705"/>

       </SegmentList>

      </Representation>

     </AdaptationSet>

    </Period>

    </MPD>