Home » Cool & Future Tech, Featured, Headline, html5, Web Experiments

Build an MPEG-DASH player from Scratch

17 July 2013 6 Comments
Build an MPEG-DASH player from Scratch

This post will show you how to build an MPEG-DASH video player using Javascript.

tl;dr? Skip to the demo (chrome only) http://francisshanahan.com/demos/mpeg-dash.

Dynamic Adaptive Streaming over HTTP (DASH) enables streaming of media content delivered from conventional HTTP web servers. Fragments served are static pieces of media so no server-side component required. This is very similar to HLS but unlike HLS MPEG-DASH is the first adaptive bit-rate HTTP-based streaming solution that is an international standard. Reference [LINK]

The recent Build conference announced Netflix’s plan for Playready DRM+HTML5 in the browser without plugins, coming in IE11 and tied to the Windows 8.1 release. Here’s the session where they announced it: LINK

Google and the Youtube team currently have a tech sandbox which explores MPEG-DASH with Playready, Widevine and various samples here: LINK

To build an MPEG-DASH player in a Browser (you can of course playback on a device/desktop) you need to rely on the MediaSource Extensions which are documented as a Draft W3C standard here [LINK] and are now emerging in Chrome, Canary and Internet Explorer 11.

The steps are fairly easy but so far the sample players I’ve looked at have been really complex. I decided to boil these down into the bare-bones and see if I could get something working. Here’s the demo: http://francisshanahan.com/demos/mpeg-dash.

The steps are as follows (mostly from slide #20 of the Build presentation)

  • Add a Video tag to your DOM
  • Create a MediaSource and set it as the src to the video tag
  • Once the SourceOpen event fires, add a SourceBuffer for video (or Audio, whichever)
  • Download the Manifest of the stream you are playing
  • Parse the Manifest – this will vary depending on the DASH profile you are implmenting and the stream protocol you are supporting. For more info on profiles see [LINK]
  • The segment name of the init segment will be contained within the manifest. This must be appended to the SourceBuffer before anything else. If it’s no good you’ll likely get the incredibly descriptive DOM Exception 15. Appending the name to the base URL of the stream will give you the full URL of the init segment.
  • Download the Init Segment and append() it to the sourcebuffer. Be sure to use TypedArrays here as this is binary data.
  • Download the Data Segments (many of them) and append() it to the sourcebuffer. Be sure to use TypedArrays here as this is binary data.

The initialization segment in the manifest might look like this –

<Initialization sourceURL="mp4-main-multi-h264bl_low-.mp4" />

You’re interested in the URL bit. Download this via XHR and append to your sourceBuffer. Repeat for all the data segments.

To keep the code as simple as possible I’ve manually parsed the Manifest and just included a static list of segments. Here’s the bit that matters.

var video = document.getElementById('myVideo');
var mse = new (window.MediaSource || window.WebKitMediaSource)();
var sb; 
    
mse.addEventListener('webkitsourceopen', onSourceOpen.bind(null, video, mse));
  
video.src = URL.createObjectURL(mse);

function onSourceOpen(video, mse, evt) {
  log("onSourceOpen()");

  // Sets up the source buffer - you parse these value out of the manifest too   
  sb = mse.addSourceBuffer('video/mp4; codecs="avc1.4d401e"');
  log('source buffer added');
  loadMovie(); 
};

// no frills - fill the source buffer with an init segment followed by data segments
var loadMovie = function(){ 
  
  downloadArrayBuffer(initUrl, 0, function( data, context) {
    log('init segment downloaded');
    if (data) {
      sb.append(data);
        
      for (var i = 0; i < segmentList.length ; i ++ ) {
        downloadArrayBuffer(baseUrl + segmentList[i], i, function( data, context) {
          log('Data segment ' + context + ' downloaded ');

          if (data) {
            sb.append(data);
          }
        });
      }
    }
  });
}

This player just plays video only (for clarity of code). If you’d like to add audio it’s the same process, just add an audio sourceBuffer to the MediaSource and init+append as before.

See the demo here: http://francisshanahan.com/demos/mpeg-dash.

This isn’t a fully fledged player I’ll admit but it does illustrate the bits that matter. MPEG-DASH is going to be kind of a big deal in the near future so I hope this helps spread the good word.

Enjoy!

6 Comments »

  • Ross Gardler said:

    Great post!

    You might be interested in the dash.js open source project which is building a platform independent reference implementation of MPEG-DASH. You can find a little more info at http://msopentech.com/blog/2013/07/17/ms-open-tech-early-contributor-to-open-source-dash-js-community-to-accelerate-advanced-video-streaming-through-mpeg-dash-standard/

  • Francis (author) said:

    Thanks Ross. I’m a big fan of Dash.JS

  • Mathew Young said:

    Thanks for the code. So the manifest is just a logical bundle that isn’t required to append segments. How did you make the individual mpd (segments) files?

  • Francis (author) said:

    The manifest is just a descriptor, but necessary to obtain the segment list. To get the list I parsed the manifest but left this out of the sample as it’s a lot of jiggery pokery with XML and the manifest varies depending on the profile of MPEG-DASH you’re implementing.

  • Matthew Young said:

    Used mp4box to create my fragmented mp4 dash files. Odd thing is (put out a question on the gpac forum) that the entry data in the trun has no segment duration. Your mpd files do. Unsure at the moment if it effects playback.

  • Matthew Young said:

    Plus, the most important part. How it you add the sequence numbers to the media segment files and the earliestPresentationTime data?

Leave your response!

Add your comment below, or trackback from your own site. You can also subscribe to these comments via RSS.

Be nice. Keep it clean. Stay on topic. No spam.

You can use these tags:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

This is a Gravatar-enabled weblog. To get your own globally-recognized-avatar, please register at Gravatar.