Experimenting with DASH, MSE and EME

In this article, I will drive you through different open source tools that can be used to build an MPEG DASH CENC encrypted video stream from scratch In a second phase, I will then explain how it can be played out in web browser.

Nowadays, browsers incorporate the capabilities of rendering encrypted video fragments thanks to two APIs: the MSE (Media Source Extensions- that enables one to feed video fragments from JavaScript), and EME (Encrypted Media Extensions - that enables to access the browsers CDM - content decryption module).


Building an encrypted MPEG DASH stream

First of all, you need to get hold of a video stream we will use for our experiment. You can use one of your own personal videos, or you can download an open source one, typically from:


Right-click and save as to a folder in which we will be creating our final stream, this will become your working folder.

MPEG DASH is an adaptive streaming format, therefore, we will have to prepare several different bitrate variants from the video source file previously downloaded. In our case, we will generate three variants: 500KB/s, 1MB/s and 2MB/s. To perform this task, we will use FFMPEG. You can use a simple command to perform this, however, I will give one here that will enable us to burn the corresponding bitrate in each bitrate variant; this is very practical when testing an adaptive streaming format. We will also burn in a timecode counter. First of all, you need to make sure you have an ffmpeg install that includes the right options. To do so, and assuming you are using a mac, you can use the following homebrew command:

brew install ffmpeg --with-fdk-aac --with-ffplay --with-freetype --with-frei0r --with-libass --with-libvo-aacenc --with-libvorbis --with-libvpx --with-opencore-amr --with-openjpeg --with-opus --with-rtmpdump --with-schroedinger --with-speex --with-theora --with-tools

Your FFMPEG installation should now be successfully with all required options we will be using. (this install is actually a pretty fat one and will have more than the minimal options we will be requiring here...)

As we will be burning in a timecode counter as well as the actual stream bitrate, you will need to have a font library handy in your working directory. You can typically choose a true type font such as Orkney here:


Copy the "Orkney Light.ttf to your working folder in which you have placed your video source file.


STEP 1 : Generating three video streams at different bitrates

ffmpeg -i ed_hd.mp4 -vf "scale=640:-1,drawtext=fontfile=Orkney Light.ttf:text="500Kb":fontcolor=white:fontsize=24:box=1:boxcolor=black:x=20:y=80,drawtext=fontsize=21:fontfile=Orkney Light.ttf:timecode='00\:00\:00\:00':rate=24:fontsize=32:fontcolor='white':box=1:boxcolor=black:x=20:y=50" -r 24 -g 50 -codec:v libx264 -s 1280x720 -b:v 500k output_500k.mp4 -vf "scale=640:-1, drawtext=Orkney Light.ttf:text="1000Kb":fontcolor=white:fontsize=24:box=1:boxcolor=black:x=20:y=80,drawtext=fontsize=21:fontfile=Orkney Light.ttf:timecode='00\:00\:00\:00':rate=24:fontsize=32:fontcolor='white':box=1:boxcolor=black:x=20:y=50" -r 24 -g 50 -codec:v libx264 -s 1280x720 -b:v 1000k output_1000k.mp4 -vf "scale=640:-1,drawtext=Orkney Light.ttf:text="2000Kb":fontcolor=white:fontsize=24:box=1:boxcolor=black: x=20:y=80,drawtext=fontsize=21:fontfile=Orkney Light.ttf:timecode='00\:00\:00\:00':rate=24:fontsize=32:fontcolor='white':box=1:boxcolor=black:x=20:y=50" -r 24 -g 50 -codec:v libx264 -s 1280x720 -b:v 2000k output_2000k.mp4

Providing the command was successfully completed, you should now have three video files corresponding to three different bitrates available in your folder.


STEP 2 : Stripping out audio from the video files and creating an audio only stream

The next step we now need to perform is to strip out the audio from the three video files we obtained in the previous step, and generate a stream that contains only the audio from one of them. Indeed, in MPEG DASH, audio is not contained in the same container and has to be delivered separately to the video player. The following FFMPEG commands will do the trick for us:

ffmpeg -i output_2000k.mp4 -an -c copy output_2000k_v.mp4 
ffmpeg -i output_1000k.mp4 -an -c copy output_1000k_v.mp4 
ffmpeg -i output_500k.mp4 -an -c copy output_500k_v.mp4 
ffmpeg -i output_2000k.mp4 -map 0:1 -c copy audio_only.m4a


STEP 3 : Encrypting the video and audio files with CENC

In this step, we are going to encrypt the video and audio files using CENC (Common encryption) in AES-CTR mode.

To do so, we will use the GPAC MP4Box tool, so you will first have to install it. Assuming you have a mac, you simply need to type the following command in a shell:

brew install MP4Box

This tool is a very nice open source MP4 packager toolbox that we will use for the remaining tasks.

Before we launch the adequate MP4Box command to encrypt our audio and video file, MP4Box requires a encryption descriptor file. We will take one that is already ready and available on internet:

  <DRMInfo type="pssh" version="1">
    <BS ID128="1077efecc0b24d02ace33c1e52e2fb4b"/>
    <BS bits="32" value="1"/>
    <BS ID128="cd7eb9ff88f34caeb06185b00024e4c2"/>
  <Cryptrack IV_Size="8" first_IV="0xbb5738fe08f11341" isEncrypted="1" saiSavedBox="senc" trackid="1">
    <key KID="0xcd7eb9ff88f34caeb06185b00024e4c2" value="0x63cb5f7184dd4b689a5c5ff11ee6a328"/>

Simply copy this xml snippet to a file, and name it drm.xml, then place this file in our working folder, alongside the video streams.

We are now ready to launch encryption of our streams with MP4Box commands:

MP4Box -crypt drm.xml output_2000k_v.mp4 -out output_2000k_v_enc.mp4 MP4Box -crypt drm.xml output_1000k_v.mp4 -out output_1000k_v_enc.mp4 MP4Box -crypt drm.xml output_500k_v.mp4 -out output_500k_v_enc.mp4 MP4Box -crypt drm.xml audio_only.m4a -out audio_only_encrypted.m4a

Providing these four commands were successfully completed, you should now have 4 additional files that cannot be played as they are encrypted.


STEP 4: Packaging the video and audio in DASH format

We have now reached the final step in our MPEG DASH stream creation. Indeed, we now need to split each video and audio file into multiple segments and generate the corresponding manifest file that will refer to each of these generated segments.

Again, MP4Box comes in handy, simply launch the following command:

MP4Box -dash 5000 -segment-name "output/outputseg-%s" -url-template -bs-switching no -out output.mpd -rap audio_only_encrypted.m4a output_2000k_v_enc.mp4 output_1000k_v_enc.mp4 output_500k_v_enc.mp4


STEP 5: Playing out the DASH stream with Chrome and DASH-IF JS player

In this step, we are going to use the DASH IF player and the Chrome browser to play the video stream we have generated.

First of all, we need to perform a build of the DASH IF player in order to be able to run DASH IF sample player locally.

You basically need to follow the steps described here, in the "Quickstart for Developers" section:


Once you have managed to Git checkout and build the DASH IF player, copy the entire dash.js folder to your current working folder (the one in which we have been working up to now and that contains your video files).

We will now set up a webserver that will serve both the DASH IF player and also the DASH stream we generated. Since Chrome 50, in order to use EME/MSE with CENC (Common Encryption), all encrypted video and audio fragments need to be served over HTTPS. Our server will therefore have to be set up to use SSL.

As we will be serving both the player and video/audio segments from the same localhost webserver, we will not be facing any CORS related issues that would require specific web server configurations. However, if you want to host your DASH IF player on a different web server (typically online) and want to be able to playout streams that are hosted on your localhost server, then a CORS configuration will be required.

To set up a simple webserver, we will use a python SimpleHTTPServer, that we will enable for SSL and also for CORS in the case you have your player running on a different server.

First of all, we need to generate a self-signed certificate with OpenSSL, simply launch this command in your working directory:

openssl req -new -x509 -keyout server.pem -out server.pem -days 365 -nodes

You need to follow the console that will prompt for some inputs. Once you are done, you should have a server.pem in your working repository.

We now need to "build" our basic python server. To do so, create a file in your working directory and name it server.py. Then add the following code to it:

# taken from http://www.piware.de/2011/01/creating-an-https-server-in-python/ # added CORS header management for the purpose of this article (Marc Rochat) import BaseHTTPServer, SimpleHTTPServer import ssl from SimpleHTTPServer import SimpleHTTPRequestHandler class CORSRequestHandler (SimpleHTTPRequestHandler): def end_headers (self): self.send_header('Access-Control-Allow-Origin', '*') SimpleHTTPRequestHandler.end_headers(self) httpd = BaseHTTPServer.HTTPServer(('localhost', 4443), CORSRequestHandler) httpd.socket = ssl.wrap_socket (httpd.socket, certfile='./server.pem', server_side=True) httpd.serve_forever()

You now can run the server with :

python server.py

Before launching the server, open Chrome and load chrome://flags/#allow-insecure-localhost in the URL entry bar. Click on enable and relaunch the browser to encounter this change. As we self-signed our SSL certificate, we need to add this exception if we want to avoid the exception to fire up in the browser. Please note that this step is optional, it just avoids one clicking on the exception link.

If you now launch Chrome and navigate to : https://localhost:4443, you should now see a directory listing of your working directory. We are close to being able to launch our video stream (at last!), however, there is one additional and last change we need to perform. Indeed, we need to add the URL of our DASH manifest within the DASH IF player sources.json. This configuration file populates the "stream" drop-down box located on the top left-hand side corner (blue), but most importantly, it actually configures the DASH IF player with the information of where to find the actual encryption key in order to decrypt the video and audio fragments. The sources.json file can be found in the dash-if-reference-player/app folder contained in the dash.js folder. Simply add the following json entry:

... { "url": "https://localhost:4443/output.mpd", "name": "Test Stream", "protData":{ "org.w3.clearkey":{ "clearkeys":{ "zX65_4jzTK6wYYWwACTkwg" : "Y8tfcYTdS2iaXF_xHuajKA" } } } }, ...


Playing your freshly created video stream

You can now navigate to : https://localhost:4443/dash.js/samples/dash-if-reference-player/index.html

If you now load your DASH IF player page located at https://localhost:4443/dash.js/samples/dash-if-reference-player/index.html, you will see your stream entry in the blue "streams" drop down menu. Select it and click the blue "load" button located on the top right hand side of the screen to initiate the playback of your video.

Here is what your video stream should look like:

Your final working directory should contain the exact following files:

I hope you have enjoyed this article and that it will be useful for you.

Please note this is just a compilation of some experiments I did for fun when I worked in the video streaming field, it is not guaranteed that all of this will work out of the box without pain.. :)

Marc Rochat