Tutorial: DASH Playback with the Shaka Player

Prerequisites

If you have not familiarized yourself with the build process, please start with Shaka Player Development.

If you are not already familiar with how DASH and DASH content work, please see the Intro to DASH Streams tutorial. We will not cover the encoding of DASH videos, but Intro to DASH Streams links to external tools which could be used to encode and package content.

Streaming Content

If you want to start streaming with HTML5 and DASH, the first thing you need is source material. For this tutorial, we'll be using DASH-packaged turtle videos to create the world's next big streaming site: TurtleTube. The DASH videos for this tutorial have already been uploaded to appspot, so the URLs used are real.

The Shaka Player

This library relies on polyfills for certain browser functionality. If you are not familiar with the concept, please browse the Polyfills tutorial before continuing this one.

The Shaka Player is composed of two parts: the player and the video source. To start, you instantiate a player object, then create and load a video source. A video source can be anything that implements shaka.player.IVideoSource. The library provides shaka.player.DashVideoSource for DASH content. We will be using this in the tutorial.

First, create a page with a video tag. Install the polyfills before you do anything else with the library. Then, instantiate your shaka.player.Player object to manage the video element. For simplicty, we will use the video tag's built-in controls in this example.

Next, construct a shaka.player.DashVideoSource object. This object will manage DASH streaming and adaptation. To pass the video source into the player, call player.load.

Here is a simple page which demonstrates basic DASH playback:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>TurtleTube - Basic Test</title>
    <!-- Load the Shaka Player library. -->
    <script src="shaka-player.compiled.js"></script>
  </head>
  <body>
    <video id="video"
           width="640" height="480"
           crossorigin="anonymous"
           controls>
      Your browser does not support HTML5 video.
    </video>
  </body>
  <script>
    function initPlayer() {
      // Install polyfills.
      shaka.polyfill.installAll();

      // Find the video element.
      var video = document.getElementById('video');

      // Construct a Player to wrap around it.
      var player = new shaka.player.Player(video);

      // Attach the player to the window so that it can be easily debugged.
      window.player = player;

      // Listen for errors from the Player.
      player.addEventListener('error', function(event) {
        console.error(event);
      });

      // Construct a DashVideoSource to represent the DASH manifest.
      var mpdUrl = 'https://turtle-tube.appspot.com/t/t2/dash.mpd';
      var estimator = new shaka.util.EWMABandwidthEstimator();
      var source = new shaka.player.DashVideoSource(mpdUrl, null, estimator);

      // Load the source into the Player.
      player.load(source);
    }
    document.addEventListener('DOMContentLoaded', initPlayer);
  </script>
</html>

Autoplay vs Load

There are two ways to start a video playing right away. The simplest is to use the autoplay attribute on the video tag.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>TurtleTube - Autoplay</title>
    <!-- Load the Shaka Player library. -->
    <script src="shaka-player.compiled.js"></script>
  </head>
  <body>
    <video id="video"
           width="640" height="480"
           crossorigin="anonymous"
           controls
           autoplay><!-- Start playing right away on load. -->
      Your browser does not support HTML5 video.
    </video>
  </body>
  <script>
    function initPlayer() {
      // Install polyfills.
      shaka.polyfill.installAll();

      // Find the video element.
      var video = document.getElementById('video');

      // Construct a Player to wrap around it.
      var player = new shaka.player.Player(video);

      // Attach the player to the window so that it can be easily debugged.
      window.player = player;

      // Listen for errors from the Player.
      player.addEventListener('error', function(event) {
        console.error(event);
      });

      // Construct a DashVideoSource to represent the DASH manifest.
      var mpdUrl = 'https://turtle-tube.appspot.com/t/t2/dash.mpd';
      var estimator = new shaka.util.EWMABandwidthEstimator();
      var source = new shaka.player.DashVideoSource(mpdUrl, null, estimator);

      // Load the source into the Player.
      player.load(source);
    }
    document.addEventListener('DOMContentLoaded', initPlayer);
  </script>
</html>

If you want to do more than start the video playing, you can execute a function asynchronously after player.load completes. It returns a Promise which is resolved once the video is loaded. You can execute any arbitrary code when load completes.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>TurtleTube - Async Load</title>
    <!-- Load the Shaka Player library. -->
    <script src="shaka-player.compiled.js"></script>
  </head>
  <body>
    <ul id="videoTracks"></ul>
    <video id="video"
           width="640" height="480"
           crossorigin="anonymous"
           controls><!-- No autoplay attribute. -->
      Your browser does not support HTML5 video.
    </video>
  </body>
  <script>
    function initPlayer() {
      // Install polyfills.
      shaka.polyfill.installAll();

      // Find the video element.
      var video = document.getElementById('video');

      // Construct a Player to wrap around it.
      var player = new shaka.player.Player(video);

      // Attach the player to the window so that it can be easily debugged.
      window.player = player;

      // Listen for errors from the Player.
      player.addEventListener('error', function(event) {
        console.error(event);
      });

      // Construct a DashVideoSource to represent the DASH manifest.
      var mpdUrl = 'https://turtle-tube.appspot.com/t/t2/dash.mpd';
      var estimator = new shaka.util.EWMABandwidthEstimator();
      var source = new shaka.player.DashVideoSource(mpdUrl, null, estimator);

      // Load the source into the Player.
      // Then query the video tracks to display in the videoTracks list element.
      // Resize the video element to match the aspect ratio of the active track.
      // Finally, begin playback.
      player.load(source).then(function() {
        var videoTracks = player.getVideoTracks();
        var activeTrack;

        // Add track info to the DOM.
        var ul = document.getElementById('videoTracks');
        for (var i = 0; i < videoTracks.length; ++i) {
          var track = videoTracks[i];
          if (track.active) activeTrack = track;

          var text = track.width + ' x ' + track.height;
          text += ' ' + (track.bandwidth / 1024).toFixed(0) + ' kbits/s';

          var li = document.createElement('li');
          li.textContent = text;
          ul.appendChild(li);
        }

        // Correct aspect ratio.
        if (activeTrack) {
          var aspectRatio = activeTrack.width / activeTrack.height;
          video.width = video.height * aspectRatio;
        } else {
          console.error('Unable to query aspect ratio!');
        }

        // Begin playback, since autoplay is not enabled on the video tag.
        video.play();
      });
    }
    document.addEventListener('DOMContentLoaded', initPlayer);
  </script>
</html>

Autoplay on the video tag does have one distinct advantage. If autoplay is set, playbackLatency stats are collected. If this information is important to you, please use autoplay.

(The use of the autoplay attribute and the execution of asynchronous code after load are not mutually exclusive.)

(NOTE: Autoplay is disabled on some mobile browsers, including Chrome. Both the autoplay attribute and programmatic calling of video.play() from outside a user interaction event are disabled. This is something the library cannot fix. See http://crbug.com/159336 for background.)

Protected Content

Some of our TurtleTube content is worth a lot of money, so we are going to make use of Widevine to protect it.

The Shaka Player uses the EME APIs to get licenses and decrypt protected content. However, many details needed to do this are not part of the DASH spec. Although there is a ContentProtection element specified, its contents and interpretation are application-specific.

To bridge the gap between DASH and EME with application-specific details, Shaka makes a callback to the application to interpret ContentProtection elements from the DASH manifest. The application receives the ContentProtection's scheme ID URI (a string) and the entire ContentProtection element, and the app then returns one or more shaka.player.DrmInfo.Config objects, which each specify DRM configuration options (e.g., the key system and license server URL).

Here is how shaka.player.DashVideoSource#ContentProtectionCallback can be used to enable encrypted content:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>TurtleTube - Encrypted Content</title>
    <!-- Load the Shaka Player library. -->
    <script src="shaka-player.compiled.js"></script>
  </head>
  <body>
    <video id="video"
           width="640" height="480"
           crossorigin="anonymous"
           controls autoplay>
      Your browser does not support HTML5 video.
    </video>
  </body>
  <script>
    function initPlayer() {
      // Install polyfills.
      shaka.polyfill.installAll();

      // Find the video element.
      var video = document.getElementById('video');

      // Construct a Player to wrap around it.
      var player = new shaka.player.Player(video);

      // Attach the player to the window so that it can be easily debugged.
      window.player = player;

      // Listen for errors from the Player.
      player.addEventListener('error', function(event) {
        console.error(event);
      });

      // Construct a DashVideoSource to represent the DASH manifest and provide
      // a callback to interpret the ContentProtection elements.
      var mpdUrl = 'https://turtle-tube.appspot.com/t/e6/dash.mpd';
      var estimator = new shaka.util.EWMABandwidthEstimator();
      var source = new shaka.player.DashVideoSource(mpdUrl,
                                                    interpretContentProtection,
                                                    estimator);

      // Load the source into the Player.
      player.load(source);
    }

    /**
     * @param {string} schemeIdUri The ContentProtection's scheme ID URI.
     * @param {!Element} contentProtection The ContentProtection element.
     * @return {!Array.<shaka.player.DrmInfo.Config>} An array of Config
     *     objects or null if the element is not understood by this application.
     */
    function interpretContentProtection(schemeIdUri, contentProtection) {
      // This is the UUID which is used by edash-packager to represent
      // Widevine.  This is the only scheme we are expecting for this
      // application.
      if (schemeIdUri == 'urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed') {
        // We will use Widevine's testing license server.  In a real app,
        // you would run your own front-end service for this.
        var licenseServerUrl = 'https://widevine-proxy.appspot.com/proxy';

        // The EME key system identifier for Widevine.
        var keySystem = 'com.widevine.alpha';

        return [{
          'keySystem': keySystem,
          'licenseServerUrl': licenseServerUrl
        }];
      }

      console.warn('Unrecognized scheme: ' + schemeIdUri);
      return null;
    }

    document.addEventListener('DOMContentLoaded', initPlayer);
  </script>
</html>

This example uses Widevine, but the Shaka Player library queries the browser to find out what key systems are supported. So you should be able to protect your content with multiple key systems for different browsers. The only requirement is that the browser has a standards compliant implementation of EME. If that's the case, you can put multiple ContentProtection tags in your MPD, and once the tags are resolved as DrmInfo.Config objects, the player will discard those which cannot be used by the browser. This way, there's no need to detect the browser vendor and serve different content based on that.

A Full Site

This is a sample demonstrating a complete TurtleTube site. Just click on one of the video thumbnails, and the appropriate video source will be constructed. The video will play back in an overlay on top of the thumbnails.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>TurtleTube - Beta!</title>
    <!-- Load the Shaka Player library. -->
    <script src="shaka-player.compiled.js"></script>
    <style>
      body {
        background-color: #4a8;
        color: #000;
      }
      h1, h2 {
        text-align: center;
      }
      #thumbContainer {
        display: table;
        margin: auto;
      }
      .thumbRow {
        display: table-row;
      }
      .thumbCell {
        display: table-cell;
        width: 270px;
        padding: 10px;
      }
      .thumbCell img {
        width: 270px;
        height: 180px;
        border: 5px ridge #07a;
        margin: 0;
      }
      #videoOverlay {
        background-color: rgba(0, 0, 0, 0.5);
        position: fixed;
        top: 2px;
        left: 2px;
        right: 2px;
        bottom: 2px;
        z-index: 1;
        overflow: hidden;
        text-align: center;
        /* Hidden until needed. */
        display: none;
      }
      #closeButton {
        position: relative;
        margin-top: 10px;
        z-index: 2;
      }
      #vcenterWrapper {
        position: absolute;
        width: 0;
        height: 0;
        /* Move the top-left corner of this div to the center. */
        top: 50%;
        left: 50%;
      }
      #video {
        width: 640px;
        height: 480px;
        position: relative;
        /* Center the video inside the overlay. */
        top: -240px;
        left: -320px;
      }
    </style>
  </head>
  <body>
    <h1>TurtleTube!</h1>
    <h2>Choose a video:</h2>

    <div id="thumbContainer">
      <div class="thumbRow">
        <div class="thumbCell">
          <img id="t1"
               src="https://turtle-tube.appspot.com/t/t1/thumb.png"
               onclick="onImageClick(this)"><br>
          <i>cute green sea turtle in Ko'olina Hawai'i</i><br>
          (MP4, WebM)
        </div>
        <div class="thumbCell">
          <img id="t2"
               src="https://turtle-tube.appspot.com/t/t2/thumb.png"
               onclick="onImageClick(this)"><br>
          <i>Endangered Ocean: Sea Turtles</i><br>
          (MP4, WebM)
        </div>
      </div>
      <div class="thumbRow">
        <div class="thumbCell">
          <img id="t3"
               src="https://turtle-tube.appspot.com/t/t3/thumb.png"
               onclick="onImageClick(this)"><br>
          <i>sea turtles exercise: bent arms</i><br>
          (WebM only)
        </div>
        <div class="thumbCell">
          <img id="t4"
               src="https://turtle-tube.appspot.com/t/t4/thumb.png"
               onclick="onImageClick(this)"><br>
          <i>sea turtles exercise: straight arms</i><br>
          (WebM only)
        </div>
      </div>
      <div class="thumbRow">
        <div class="thumbCell">
          <img id="t5"
               src="https://turtle-tube.appspot.com/t/t5/thumb.png"
               onclick="onImageClick(this)"><br>
          <i>Using robots to reveal secrets of walking baby sea turtles</i><br>
          (MP4, WebM)
        </div>
        <div class="thumbCell">
          <img id="e6"
               src="https://turtle-tube.appspot.com/t/e6/thumb.png"
               onclick="onImageClick(this)"><br>
          <i>kitten vs sea turtle</i><br>
          (MP4 only, encrypted)
        </div>
      </div>
    </div>
    <div id="videoOverlay">
      <div id="vcenterWrapper">
        <video id="video"
               poster="https://turtle-tube.appspot.com/poster.jpg"
               crossorigin="anonymous"
               controls autoplay>
          Your browser does not support HTML5 video.
        </video>
      </div>
      <button id="closeButton" onclick="closeVideo()">Close Video</button>
    </div>
  </body>
  <script>
    var video;
    var player;
    var estimator;

    function initPlayer() {
      // Install polyfills.
      shaka.polyfill.installAll();

      // Get the video element.
      video = document.getElementById('video');

      // Construct the Player to wrap around it.
      player = new shaka.player.Player(video);

      // Attach the player to the window so that it can be easily debugged.
      window.player = player;

      // Listen for errors from the Player.
      player.addEventListener('error', function(event) {
        console.error(event);
      });

      // Construct a persistent bandwidth estimator to pass to video sources.
      // This will allow second and subsequent playbacks to benefit from
      // earlier bandwidth estimations and avoid starting at a low-quality
      // stream.
      estimator = new shaka.util.EWMABandwidthEstimator();
    }

    /**
     * @param {!HTMLImageElement} image
     */
    function onImageClick(image) {
      // Disregard any bandwidth data older than one hour.  The user may have
      // changed networks if they are on a laptop or mobile device.
      if (estimator.getDataAge() >= 3600) {
        estimator = new shaka.util.EWMABandwidthEstimator();
      }

      // Construct a DashVideoSource to represent the DASH manifest and provide
      // a callback to interpret the ContentProtection elements (if any).
      var mpdUrl = 'https://turtle-tube.appspot.com/t/' + image.id + '/dash.mpd';
      var source = new shaka.player.DashVideoSource(mpdUrl,
                                                    interpretContentProtection,
                                                    estimator);

      // Show the video player overlay.
      var overlay = document.getElementById('videoOverlay');
      overlay.style.display = 'block';

      // Load the source into the Player.
      player.load(source);
    }

    /**
     * @param {string} schemeIdUri The ContentProtection's scheme ID URI.
     * @param {!Element} contentProtection The ContentProtection element.
     * @return {!Array.<shaka.player.DrmInfo.Config>} An array of Config
     *     objects or null if the element is not understood by this application.
     */
    function interpretContentProtection(schemeIdUri, contentProtection) {
      // This is the UUID which is used by edash-packager to represent
      // Widevine.  This is the only scheme we are expecting for this
      // application.
      if (schemeIdUri == 'urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed') {
        // We will use Widevine's testing license server.  In a real app,
        // you would run your own front-end service for this.
        var licenseServerUrl = 'https://widevine-proxy.appspot.com/proxy';

        // The EME key system identifier for Widevine.
        var keySystem = 'com.widevine.alpha';

        return [{
          'keySystem': keySystem,
          'licenseServerUrl': licenseServerUrl
        }];
      }

      console.warn('Unrecognized scheme: ' + schemeIdUri);
      return null;
    }

    function closeVideo() {
      // Unload the video source.
      player.unload();

      // Hide the video player overlay.
      var overlay = document.getElementById('videoOverlay');
      overlay.style.display = 'none';
    }

    document.addEventListener('DOMContentLoaded', initPlayer);
  </script>
</html>

Bonus: Event Handling

There a few events which the Player will dispatch. This sample demonstrates how you can listen for the adaptation event and use to it indicate when an HD video is being played. It also shows an error dialog if/when error events occur.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>TurtleTube - Beta (HD)!</title>
    <!-- Load the Shaka Player library. -->
    <script src="shaka-player.compiled.js"></script>
    <style>
      body {
        background-color: #4a8;
        color: #000;
      }
      h1, h2 {
        text-align: center;
      }
      #thumbContainer {
        display: table;
        margin: auto;
      }
      .thumbRow {
        display: table-row;
      }
      .thumbCell {
        display: table-cell;
        width: 270px;
        padding: 10px;
      }
      .thumbCell img {
        width: 270px;
        height: 180px;
        border: 5px ridge #07a;
        margin: 0;
      }
      #videoOverlay {
        background-color: rgba(0, 0, 0, 0.5);
        position: fixed;
        top: 2px;
        left: 2px;
        right: 2px;
        bottom: 2px;
        z-index: 1;
        overflow: hidden;
        text-align: center;
        /* Hidden until needed. */
        display: none;
      }
      #closeButton {
        position: relative;
        margin-top: 10px;
        z-index: 2;
      }
      #vcenterWrapper {
        position: absolute;
        width: 0;
        height: 0;
        /* Move the top-left corner of this div to the center. */
        top: 50%;
        left: 50%;
      }
      #video {
        width: 640px;
        height: 426px;
        position: relative;
        /* Center the video inside the overlay. */
        left: -320px;
        top: -213px;
      }
      #errorOverlay {
        border: 2px solid black;
        background-color: rgba(100, 0, 0, 0.5);
        font-size: 175%;
        white-space: pre-line;
        width: 350px;
        height: 200px;
        position: absolute;
        /* Center the error inside the video overlay. */
        left: -175px;
        top: -100px;
        /* Hidden until needed. */
        display: none;
      }
      #hd {
        position: absolute;
        opacity: 0.6;
        /* Hidden until needed. */
        display: none;
      }
    </style>
  </head>
  <body>
    <h1>TurtleTube!</h1>
    <h2>Choose a video:</h2>

    <div id="thumbContainer">
      <div class="thumbRow">
        <div class="thumbCell">
          <img id="t1"
               src="https://turtle-tube.appspot.com/t/t1/thumb.png"
               onclick="onImageClick(this)"><br>
          <i>cute green sea turtle in Ko'olina Hawai'i</i><br>
          (MP4, WebM)
        </div>
        <div class="thumbCell">
          <img id="t2"
               src="https://turtle-tube.appspot.com/t/t2/thumb.png"
               onclick="onImageClick(this)"><br>
          <i>Endangered Ocean: Sea Turtles</i><br>
          (MP4, WebM)
        </div>
      </div>
      <div class="thumbRow">
        <div class="thumbCell">
          <img id="t3"
               src="https://turtle-tube.appspot.com/t/t3/thumb.png"
               onclick="onImageClick(this)"><br>
          <i>sea turtles exercise: bent arms</i><br>
          (WebM only)
        </div>
        <div class="thumbCell">
          <img id="t4"
               src="https://turtle-tube.appspot.com/t/t4/thumb.png"
               onclick="onImageClick(this)"><br>
          <i>sea turtles exercise: straight arms</i><br>
          (WebM only)
        </div>
      </div>
      <div class="thumbRow">
        <div class="thumbCell">
          <img id="t5"
               src="https://turtle-tube.appspot.com/t/t5/thumb.png"
               onclick="onImageClick(this)"><br>
          <i>Using robots to reveal secrets of walking baby sea turtles</i><br>
          (MP4, WebM)
        </div>
        <div class="thumbCell">
          <img id="e6"
               src="https://turtle-tube.appspot.com/t/e6/thumb.png"
               onclick="onImageClick(this)"><br>
          <i>kitten vs sea turtle</i><br>
          (MP4 only, encrypted)
        </div>
      </div>
    </div>
    <div id="videoOverlay">
      <div id="vcenterWrapper">
        <video id="video"
               poster="https://turtle-tube.appspot.com/poster.jpg"
               crossorigin="anonymous"
               controls autoplay>
          Your browser does not support HTML5 video.
        </video>
        <img id="hd" src="https://turtle-tube.appspot.com/hd.png">
        <div id="errorOverlay"></div>
      </div>
      <button id="closeButton" onclick="closeVideo()">Close Video</button>
    </div>
  </body>
  <script>
    var video;
    var hd;
    var player;
    var estimator;

    function initPlayer() {
      // Install polyfills.
      shaka.polyfill.installAll();

      // Get important elements.
      video = document.getElementById('video');
      hd = document.getElementById('hd');

      // Construct the Player to wrap around it.
      player = new shaka.player.Player(video);

      // Attach the player to the window so that it can be easily debugged.
      window.player = player;

      // Listen for adaptation events.
      player.addEventListener('adaptation', onAdaptation);

      // Listen for errors from the Player.
      player.addEventListener('error', onError);

      // Construct a persistent bandwidth estimator to pass to video sources.
      // This will allow second and subsequent playbacks to benefit from
      // earlier bandwidth estimations and avoid starting at a low-quality
      // stream.
      estimator = new shaka.util.EWMABandwidthEstimator();
    }

    /**
     * @param {!Event} event
     */
    function onAdaptation(event) {
      // Ignore non-video adaptation events.
      if (event.contentType != 'video') {
        return;
      }

      // Resize the video element to match the content's aspect ratio.
      var aspect = event.size.width / event.size.height;
      var w = video.offsetWidth;
      var h = w / aspect;
      video.style.width = w + 'px';
      video.style.height = h + 'px';
      video.style.left = (-w / 2) + 'px';
      video.style.top = (-h / 2) + 'px';

      // Position the HD icon in the top-right of the video element.
      // 0,0 for this icon is the center of the video element.
      hd.style.top = ((-h / 2) + 5) + 'px';
      hd.style.right = ((-w / 2) + 5) + 'px';

      // If the video is 720p or above, show the HD icon.
      if (event.size.height >= 720) {
        hd.style.display = 'block';
      } else {
        hd.style.display = 'none';
      }
    }

    /**
     * @param {!Event} event
     */
    function onError(event) {
      var overlay = document.getElementById('errorOverlay');
      // This contains details about the error.
      var error = event.detail;

      // Format a message to show to the user in the overlay.
      var text = 'Error (' + error.type + '):\n';
      text += error.message;

      // Display it.
      overlay.textContent = text;
      overlay.style.display = 'block';

      // It would also be a good idea to log an anonymized version of the error
      // object to the server.
    }

    /**
     * @param {!HTMLImageElement} image
     */
    function onImageClick(image) {
      // Disregard any bandwidth data older than one hour.  The user may have
      // changed networks if they are on a laptop or mobile device.
      if (estimator.getDataAge() >= 3600) {
        estimator = new shaka.util.EWMABandwidthEstimator();
      }

      // Construct a DashVideoSource to represent the DASH manifest and provide
      // a callback to interpret the ContentProtection elements (if any).
      var mpdUrl = 'https://turtle-tube.appspot.com/t/' + image.id + '/dash.mpd';
      var source = new shaka.player.DashVideoSource(mpdUrl,
                                                    interpretContentProtection,
                                                    estimator);

      // Show the video player overlay.
      var overlay = document.getElementById('videoOverlay');
      overlay.style.display = 'block';

      // Load the source into the Player.
      player.load(source);
    }

    /**
     * @param {string} schemeIdUri The ContentProtection's scheme ID URI.
     * @param {!Element} contentProtection The ContentProtection element.
     * @return {!Array.<shaka.player.DrmInfo.Config>} An array of Config
     *     objects or null if the element is not understood by this application.
     */
    function interpretContentProtection(schemeIdUri, contentProtection) {
      // This is the UUID which is used by edash-packager to represent
      // Widevine.  This is the only scheme we are expecting for this
      // application.
      if (schemeIdUri == 'urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed') {
        // We will use Widevine's testing license server.  In a real app,
        // you would run your own front-end service for this.
        var licenseServerUrl = 'https://widevine-proxy.appspot.com/proxy';

        // The EME key system identifier for Widevine.
        var keySystem = 'com.widevine.alpha';

        return [{
          'keySystem': keySystem,
          'licenseServerUrl': licenseServerUrl
        }];
      }

      // See app.interpretContentProtection_() in app.js for more examples of
      // what this callback can do.

      console.warn('Unrecognized scheme: ' + schemeIdUri);
      return null;
    }

    function closeVideo() {
      // Unload the video source.
      player.unload();

      // Hide the video player overlay.
      var overlay = document.getElementById('videoOverlay');
      overlay.style.display = 'none';

      // Hide the error overlay.
      overlay = document.getElementById('errorOverlay');
      overlay.style.display = 'none';
    }

    document.addEventListener('DOMContentLoaded', initPlayer);
  </script>
</html>