Files
shaka-player/tutorials/player.html
T
Timothy Drews 1c47e1e09b Add license pre/post processing tutorial.
Closes #137

Change-Id: I092a6152d8c9c2f3809f3ef27e237c06ff0a6490
2015-09-08 21:49:03 +00:00

985 lines
41 KiB
HTML

<!--
Copyright 2014 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<h3 class="tutorial-heading">
Prerequisites
</h3>
<p>
If you have not familiarized yourself with the build process, please start with
{@tutorial dev}.
</p>
<p>
If you are not already familiar with how DASH and DASH content work, please see
the {@tutorial intro} tutorial. We will not cover the encoding of DASH videos,
but {@tutorial intro} links to external tools which could be used to encode and
package content.
</p>
<h3 class="tutorial-heading">
Streaming Content
</h3>
<p>
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.
</p>
<h3 class="tutorial-heading">
The Shaka Player
</h3>
<p>
This library relies on polyfills for certain browser functionality. If you are
not familiar with the concept, please browse the {@tutorial polyfills} tutorial
before continuing this one.
</p>
<p>
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 {@link shaka.player.IVideoSource}.
The library provides {@link shaka.player.DashVideoSource} for DASH content. We
will be using this in the tutorial.
</p>
<p>
First, create a page with a {@link http://goo.gl/NwDFQ video tag}. Install the
polyfills before you do anything else with the library. Then, instantiate your
{@link shaka.player.Player} object to manage the video element. For simplicty,
we will use the video tag's built-in controls in this example.
</p>
<p>
Next, construct a {@link shaka.player.DashVideoSource} object. This object will
manage DASH streaming and adaptation. To pass the video source into the player,
call {@link shaka.player.Player#load player.load}.
</p>
<p>
Here is a simple page which demonstrates basic DASH playback:
</p>
<pre class="prettyprint source"><code id="sample1">&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset="utf-8"&gt;
&lt;title&gt;TurtleTube - Basic Test&lt;/title&gt;
&lt;!-- Load the Shaka Player library. --&gt;
&lt;script src="shaka-player.compiled.js"&gt;&lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;video id="video"
width="640" height="480"
crossorigin="anonymous"
controls&gt;
Your browser does not support HTML5 video.
&lt;/video&gt;
&lt;/body&gt;
&lt;script&gt;
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);
&lt;/script&gt;
&lt;/html&gt;
</code></pre>
<h3 class="tutorial-heading">
Autoplay vs Load
</h3>
<p>
There are two ways to start a video playing right away. The simplest is to use
the {@link http://goo.gl/McU4Rh autoplay attribute} on the video tag.
</p>
<pre class="prettyprint source"><code id="sample2">&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset="utf-8"&gt;
<span class="newCode"> &lt;title&gt;TurtleTube - Autoplay&lt;/title&gt;</span>
&lt;!-- Load the Shaka Player library. --&gt;
&lt;script src="shaka-player.compiled.js"&gt;&lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;video id="video"
width="640" height="480"
crossorigin="anonymous"
<span class="newCode"> controls</span>
<span class="newCode"> autoplay&gt;&lt;!-- Start playing right away on load. --&gt;</span>
Your browser does not support HTML5 video.
&lt;/video&gt;
&lt;/body&gt;
&lt;script&gt;
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);
&lt;/script&gt;
&lt;/html&gt;
</code></pre>
<p>
If you want to do more than start the video playing, you can execute a function
asynchronously after {@link shaka.player.Player#load player.load} completes. It
returns a {@link http://goo.gl/8NHkC1 Promise} which is resolved once the video
is loaded. You can execute any arbitrary code when load completes.
</p>
<pre class="prettyprint source"><code id="sample3">&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset="utf-8"&gt;
<span class="newCode"> &lt;title&gt;TurtleTube - Async Load&lt;/title&gt;</span>
&lt;!-- Load the Shaka Player library. --&gt;
&lt;script src="shaka-player.compiled.js"&gt;&lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;
<span class="newCode"> &lt;ul id="videoTracks"&gt;&lt;/ul&gt;</span>
&lt;video id="video"
width="640" height="480"
crossorigin="anonymous"
<span class="newCode"> controls&gt;&lt;!-- No autoplay attribute. --&gt;</span>
Your browser does not support HTML5 video.
&lt;/video&gt;
&lt;/body&gt;
&lt;script&gt;
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.
<span class="newCode"> // Then query the video tracks to display in the videoTracks list element.</span>
<span class="newCode"> // Resize the video element to match the aspect ratio of the active track.</span>
<span class="newCode"> // Finally, begin playback.</span>
<span class="newCode"> player.load(source).then(function() {</span>
<span class="newCode"> var videoTracks = player.getVideoTracks();</span>
<span class="newCode"> var activeTrack;</span>
<span class="newCode"></span>
<span class="newCode"> // Add track info to the DOM.</span>
<span class="newCode"> var ul = document.getElementById('videoTracks');</span>
<span class="newCode"> for (var i = 0; i &lt; videoTracks.length; ++i) {</span>
<span class="newCode"> var track = videoTracks[i];</span>
<span class="newCode"> if (track.active) activeTrack = track;</span>
<span class="newCode"></span>
<span class="newCode"> var text = track.width + ' x ' + track.height;</span>
<span class="newCode"> text += ' ' + (track.bandwidth / 1024).toFixed(0) + ' kbits/s';</span>
<span class="newCode"></span>
<span class="newCode"> var li = document.createElement('li');</span>
<span class="newCode"> li.textContent = text;</span>
<span class="newCode"> ul.appendChild(li);</span>
<span class="newCode"> }</span>
<span class="newCode"></span>
<span class="newCode"> // Correct aspect ratio.</span>
<span class="newCode"> if (activeTrack) {</span>
<span class="newCode"> var aspectRatio = activeTrack.width / activeTrack.height;</span>
<span class="newCode"> video.width = video.height * aspectRatio;</span>
<span class="newCode"> } else {</span>
<span class="newCode"> console.error('Unable to query aspect ratio!');</span>
<span class="newCode"> }</span>
<span class="newCode"></span>
<span class="newCode"> // Begin playback, since autoplay is not enabled on the video tag.</span>
<span class="newCode"> video.play();</span>
<span class="newCode"> });</span>
}
document.addEventListener('DOMContentLoaded', initPlayer);
&lt;/script&gt;
&lt;/html&gt;
</code></pre>
<p>
Autoplay on the video tag does have one distinct advantage. If autoplay is set,
{@link shaka.player.Stats#playbackLatency playbackLatency stats} are collected.
If this information is important to you, please use autoplay.
</p>
<p>
(The use of the autoplay attribute and the execution of asynchronous code after
load are not mutually exclusive.)
</p>
<p>
(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
<a href="http://crbug.com/159336">http://crbug.com/159336</a> for background.)
</p>
<h3 class="tutorial-heading">
Protected Content
</h3>
<p>
Some of our TurtleTube content is worth a lot of money, so we are going to make
use of Widevine to protect it.
</p>
<p>
The Shaka Player uses the {@link http://goo.gl/o9Guuu 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.
</p>
<p>
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 {@link shaka.player.DrmInfo.Config} objects, which
each specify DRM configuration options (e.g., the key system and license server
URL).
</p>
<p>
Here is how {@link shaka.player.DashVideoSource#ContentProtectionCallback} can be
used to enable encrypted content:
</p>
<pre class="prettyprint source"><code id="sample4">&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset="utf-8"&gt;
<span class="newCode"> &lt;title&gt;TurtleTube - Encrypted Content&lt;/title&gt;</span>
&lt;!-- Load the Shaka Player library. --&gt;
&lt;script src="shaka-player.compiled.js"&gt;&lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;video id="video"
width="640" height="480"
crossorigin="anonymous"
<span class="newCode"> controls autoplay&gt;</span>
Your browser does not support HTML5 video.
&lt;/video&gt;
&lt;/body&gt;
&lt;script&gt;
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);
});
<span class="newCode"> // Construct a DashVideoSource to represent the DASH manifest and provide</span>
<span class="newCode"> // a callback to interpret the ContentProtection elements.</span>
<span class="newCode"> var mpdUrl = 'https://turtle-tube.appspot.com/t/e6/dash.mpd';</span>
var estimator = new shaka.util.EWMABandwidthEstimator();
<span class="newCode"> var source = new shaka.player.DashVideoSource(mpdUrl,</span>
<span class="newCode"> interpretContentProtection,</span>
<span class="newCode"> estimator);</span>
// Load the source into the Player.
<span class="newCode"> player.load(source);</span>
<span class="newCode"> }</span>
<span class="newCode"> /**</span>
<span class="newCode"> * @param {string} schemeIdUri The ContentProtection's scheme ID URI.</span>
<span class="newCode"> * @param {!Element} contentProtection The ContentProtection element.</span>
<span class="newCode"> * @return {!Array.&lt;shaka.player.DrmInfo.Config&gt;} An array of Config</span>
<span class="newCode"> * objects or null if the element is not understood by this application.</span>
<span class="newCode"> */</span>
<span class="newCode"> function interpretContentProtection(schemeIdUri, contentProtection) {</span>
<span class="newCode"> // This is the UUID which is used by edash-packager to represent</span>
<span class="newCode"> // Widevine. This is the only scheme we are expecting for this</span>
<span class="newCode"> // application.</span>
<span class="newCode"> if (schemeIdUri == 'urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed') {</span>
<span class="newCode"> // We will use Widevine's testing license server. In a real app,</span>
<span class="newCode"> // you would run your own front-end service for this.</span>
<span class="newCode"> var licenseServerUrl = 'https://widevine-proxy.appspot.com/proxy';</span>
<span class="newCode"> // The EME key system identifier for Widevine.</span>
<span class="newCode"> var keySystem = 'com.widevine.alpha';</span>
<span class="newCode"> return [{</span>
<span class="newCode"> 'keySystem': keySystem,</span>
<span class="newCode"> 'licenseServerUrl': licenseServerUrl</span>
<span class="newCode"> }];</span>
<span class="newCode"> }</span>
<span class="newCode"> console.warn('Unrecognized scheme: ' + schemeIdUri);</span>
<span class="newCode"> return null;</span>
<span class="newCode"> }</span>
document.addEventListener('DOMContentLoaded', initPlayer);
&lt;/script&gt;
&lt;/html&gt;
</code></pre>
<p>
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.
</p>
<h3 class="tutorial-heading">
A Full Site
</h3>
<p>
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.
</p>
<pre class="prettyprint source"><code id="sample5">&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset="utf-8"&gt;
<span class="newCode"> &lt;title&gt;TurtleTube - Beta!&lt;/title&gt;</span>
&lt;!-- Load the Shaka Player library. --&gt;
&lt;script src="shaka-player.compiled.js"&gt;&lt;/script&gt;
<span class="newCode"> &lt;style&gt;</span>
<span class="newCode"> body {</span>
<span class="newCode"> background-color: #4a8;</span>
<span class="newCode"> color: #000;</span>
<span class="newCode"> }</span>
<span class="newCode"> h1, h2 {</span>
<span class="newCode"> text-align: center;</span>
<span class="newCode"> }</span>
<span class="newCode"> #thumbContainer {</span>
<span class="newCode"> display: table;</span>
<span class="newCode"> margin: auto;</span>
<span class="newCode"> }</span>
<span class="newCode"> .thumbRow {</span>
<span class="newCode"> display: table-row;</span>
<span class="newCode"> }</span>
<span class="newCode"> .thumbCell {</span>
<span class="newCode"> display: table-cell;</span>
<span class="newCode"> width: 270px;</span>
<span class="newCode"> padding: 10px;</span>
<span class="newCode"> }</span>
<span class="newCode"> .thumbCell img {</span>
<span class="newCode"> width: 270px;</span>
<span class="newCode"> height: 180px;</span>
<span class="newCode"> border: 5px ridge #07a;</span>
<span class="newCode"> margin: 0;</span>
<span class="newCode"> }</span>
<span class="newCode"> #videoOverlay {</span>
<span class="newCode"> background-color: rgba(0, 0, 0, 0.5);</span>
<span class="newCode"> position: fixed;</span>
<span class="newCode"> top: 2px;</span>
<span class="newCode"> left: 2px;</span>
<span class="newCode"> right: 2px;</span>
<span class="newCode"> bottom: 2px;</span>
<span class="newCode"> z-index: 1;</span>
<span class="newCode"> overflow: hidden;</span>
<span class="newCode"> text-align: center;</span>
<span class="newCode"> /* Hidden until needed. */</span>
<span class="newCode"> display: none;</span>
<span class="newCode"> }</span>
<span class="newCode"> #closeButton {</span>
<span class="newCode"> position: relative;</span>
<span class="newCode"> margin-top: 10px;</span>
<span class="newCode"> z-index: 2;</span>
<span class="newCode"> }</span>
<span class="newCode"> #vcenterWrapper {</span>
<span class="newCode"> position: absolute;</span>
<span class="newCode"> width: 0;</span>
<span class="newCode"> height: 0;</span>
<span class="newCode"> /* Move the top-left corner of this div to the center. */</span>
<span class="newCode"> top: 50%;</span>
<span class="newCode"> left: 50%;</span>
<span class="newCode"> }</span>
<span class="newCode"> #video {</span>
<span class="newCode"> width: 640px;</span>
<span class="newCode"> height: 480px;</span>
<span class="newCode"> position: relative;</span>
<span class="newCode"> /* Center the video inside the overlay. */</span>
<span class="newCode"> top: -240px;</span>
<span class="newCode"> left: -320px;</span>
<span class="newCode"> }</span>
<span class="newCode"> &lt;/style&gt;</span>
&lt;/head&gt;
&lt;body&gt;
<span class="newCode"> &lt;h1&gt;TurtleTube!&lt;/h1&gt;</span>
<span class="newCode"> &lt;h2&gt;Choose a video:&lt;/h2&gt;</span>
<span class="newCode"></span>
<span class="newCode"> &lt;div id="thumbContainer"&gt;</span>
<span class="newCode"> &lt;div class="thumbRow"&gt;</span>
<span class="newCode"> &lt;div class="thumbCell"&gt;</span>
<span class="newCode"> &lt;img id="t1"</span>
<span class="newCode"> src="https://turtle-tube.appspot.com/t/t1/thumb.png"</span>
<span class="newCode"> onclick="onImageClick(this)"&gt;&lt;br&gt;</span>
<span class="newCode"> &lt;i&gt;cute green sea turtle in Ko'olina Hawai'i&lt;/i&gt;&lt;br&gt;</span>
<span class="newCode"> (MP4, WebM)</span>
<span class="newCode"> &lt;/div&gt;</span>
<span class="newCode"> &lt;div class="thumbCell"&gt;</span>
<span class="newCode"> &lt;img id="t2"</span>
<span class="newCode"> src="https://turtle-tube.appspot.com/t/t2/thumb.png"</span>
<span class="newCode"> onclick="onImageClick(this)"&gt;&lt;br&gt;</span>
<span class="newCode"> &lt;i&gt;Endangered Ocean: Sea Turtles&lt;/i&gt;&lt;br&gt;</span>
<span class="newCode"> (MP4, WebM)</span>
<span class="newCode"> &lt;/div&gt;</span>
<span class="newCode"> &lt;/div&gt;</span>
<span class="newCode"> &lt;div class="thumbRow"&gt;</span>
<span class="newCode"> &lt;div class="thumbCell"&gt;</span>
<span class="newCode"> &lt;img id="t3"</span>
<span class="newCode"> src="https://turtle-tube.appspot.com/t/t3/thumb.png"</span>
<span class="newCode"> onclick="onImageClick(this)"&gt;&lt;br&gt;</span>
<span class="newCode"> &lt;i&gt;sea turtles exercise: bent arms&lt;/i&gt;&lt;br&gt;</span>
<span class="newCode"> (WebM only)</span>
<span class="newCode"> &lt;/div&gt;</span>
<span class="newCode"> &lt;div class="thumbCell"&gt;</span>
<span class="newCode"> &lt;img id="t4"</span>
<span class="newCode"> src="https://turtle-tube.appspot.com/t/t4/thumb.png"</span>
<span class="newCode"> onclick="onImageClick(this)"&gt;&lt;br&gt;</span>
<span class="newCode"> &lt;i&gt;sea turtles exercise: straight arms&lt;/i&gt;&lt;br&gt;</span>
<span class="newCode"> (WebM only)</span>
<span class="newCode"> &lt;/div&gt;</span>
<span class="newCode"> &lt;/div&gt;</span>
<span class="newCode"> &lt;div class="thumbRow"&gt;</span>
<span class="newCode"> &lt;div class="thumbCell"&gt;</span>
<span class="newCode"> &lt;img id="t5"</span>
<span class="newCode"> src="https://turtle-tube.appspot.com/t/t5/thumb.png"</span>
<span class="newCode"> onclick="onImageClick(this)"&gt;&lt;br&gt;</span>
<span class="newCode"> &lt;i&gt;Using robots to reveal secrets of walking baby sea turtles&lt;/i&gt;&lt;br&gt;</span>
<span class="newCode"> (MP4, WebM)</span>
<span class="newCode"> &lt;/div&gt;</span>
<span class="newCode"> &lt;div class="thumbCell"&gt;</span>
<span class="newCode"> &lt;img id="e6"</span>
<span class="newCode"> src="https://turtle-tube.appspot.com/t/e6/thumb.png"</span>
<span class="newCode"> onclick="onImageClick(this)"&gt;&lt;br&gt;</span>
<span class="newCode"> &lt;i&gt;kitten vs sea turtle&lt;/i&gt;&lt;br&gt;</span>
<span class="newCode"> (MP4 only, encrypted)</span>
<span class="newCode"> &lt;/div&gt;</span>
<span class="newCode"> &lt;/div&gt;</span>
<span class="newCode"> &lt;/div&gt;</span>
<span class="newCode"> &lt;div id="videoOverlay"&gt;</span>
<span class="newCode"> &lt;div id="vcenterWrapper"&gt;</span>
<span class="newCode"> &lt;video id="video"</span>
<span class="newCode"> poster="https://turtle-tube.appspot.com/poster.jpg"</span>
<span class="newCode"> crossorigin="anonymous"</span>
<span class="newCode"> controls autoplay&gt;</span>
<span class="newCode"> Your browser does not support HTML5 video.</span>
<span class="newCode"> &lt;/video&gt;</span>
<span class="newCode"> &lt;/div&gt;</span>
<span class="newCode"> &lt;button id="closeButton" onclick="closeVideo()"&gt;Close Video&lt;/button&gt;</span>
<span class="newCode"> &lt;/div&gt;</span>
&lt;/body&gt;
&lt;script&gt;
<span class="newCode"> var video;</span>
<span class="newCode"> var player;</span>
<span class="newCode"> var estimator;</span>
<span class="newCode"></span>
function initPlayer() {
// Install polyfills.
shaka.polyfill.installAll();
<span class="newCode"> // Get the video element.</span>
<span class="newCode"> video = document.getElementById('video');</span>
<span class="newCode"></span>
<span class="newCode"> // Construct the Player to wrap around it.</span>
<span class="newCode"> player = new shaka.player.Player(video);</span>
// 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);
});
<span class="newCode"> // Construct a persistent bandwidth estimator to pass to video sources.</span>
<span class="newCode"> // This will allow second and subsequent playbacks to benefit from</span>
<span class="newCode"> // earlier bandwidth estimations and avoid starting at a low-quality</span>
<span class="newCode"> // stream.</span>
<span class="newCode"> estimator = new shaka.util.EWMABandwidthEstimator();</span>
<span class="newCode"> }</span>
<span class="newCode"></span>
<span class="newCode"> /**</span>
<span class="newCode"> * @param {!HTMLImageElement} image</span>
<span class="newCode"> */</span>
<span class="newCode"> function onImageClick(image) {</span>
<span class="newCode"> // Disregard any bandwidth data older than one hour. The user may have</span>
<span class="newCode"> // changed networks if they are on a laptop or mobile device.</span>
<span class="newCode"> if (estimator.getDataAge() &gt;= 3600) {</span>
<span class="newCode"> estimator = new shaka.util.EWMABandwidthEstimator();</span>
<span class="newCode"> }</span>
<span class="newCode"></span>
// Construct a DashVideoSource to represent the DASH manifest and provide
<span class="newCode"> // a callback to interpret the ContentProtection elements (if any).</span>
<span class="newCode"> var mpdUrl = 'https://turtle-tube.appspot.com/t/' + image.id + '/dash.mpd';</span>
var source = new shaka.player.DashVideoSource(mpdUrl,
interpretContentProtection,
estimator);
<span class="newCode"></span>
<span class="newCode"> // Show the video player overlay.</span>
<span class="newCode"> var overlay = document.getElementById('videoOverlay');</span>
<span class="newCode"> overlay.style.display = 'block';</span>
// 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.&lt;shaka.player.DrmInfo.Config&gt;} 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;
}
<span class="newCode"> function closeVideo() {</span>
<span class="newCode"> // Unload the video source.</span>
<span class="newCode"> player.unload();</span>
<span class="newCode"></span>
<span class="newCode"> // Hide the video player overlay.</span>
<span class="newCode"> var overlay = document.getElementById('videoOverlay');</span>
<span class="newCode"> overlay.style.display = 'none';</span>
<span class="newCode"> }</span>
<span class="newCode"></span>
document.addEventListener('DOMContentLoaded', initPlayer);
&lt;/script&gt;
&lt;/html&gt;
</code></pre>
<h3 class="tutorial-heading">
Bonus: Event Handling
</h3>
<p>
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.
</p>
<pre class="prettyprint source"><code id="sample6">&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset="utf-8"&gt;
<span class="newCode"> &lt;title&gt;TurtleTube - Beta (HD)!&lt;/title&gt;</span>
&lt;!-- Load the Shaka Player library. --&gt;
&lt;script src="shaka-player.compiled.js"&gt;&lt;/script&gt;
&lt;style&gt;
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;
<span class="newCode"> height: 426px;</span>
position: relative;
/* Center the video inside the overlay. */
left: -320px;
<span class="newCode"> top: -213px;</span>
<span class="newCode"> }</span>
<span class="newCode"> #errorOverlay {</span>
<span class="newCode"> border: 2px solid black;</span>
<span class="newCode"> background-color: rgba(100, 0, 0, 0.5);</span>
<span class="newCode"> font-size: 175%;</span>
<span class="newCode"> white-space: pre-line;</span>
<span class="newCode"> width: 350px;</span>
<span class="newCode"> height: 200px;</span>
<span class="newCode"> position: absolute;</span>
<span class="newCode"> /* Center the error inside the video overlay. */</span>
<span class="newCode"> left: -175px;</span>
<span class="newCode"> top: -100px;</span>
<span class="newCode"> /* Hidden until needed. */</span>
<span class="newCode"> display: none;</span>
<span class="newCode"> }</span>
<span class="newCode"> #hd {</span>
<span class="newCode"> position: absolute;</span>
<span class="newCode"> opacity: 0.6;</span>
<span class="newCode"> /* Hidden until needed. */</span>
<span class="newCode"> display: none;</span>
}
&lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;h1&gt;TurtleTube!&lt;/h1&gt;
&lt;h2&gt;Choose a video:&lt;/h2&gt;
&lt;div id="thumbContainer"&gt;
&lt;div class="thumbRow"&gt;
&lt;div class="thumbCell"&gt;
&lt;img id="t1"
src="https://turtle-tube.appspot.com/t/t1/thumb.png"
onclick="onImageClick(this)"&gt;&lt;br&gt;
&lt;i&gt;cute green sea turtle in Ko'olina Hawai'i&lt;/i&gt;&lt;br&gt;
(MP4, WebM)
&lt;/div&gt;
&lt;div class="thumbCell"&gt;
&lt;img id="t2"
src="https://turtle-tube.appspot.com/t/t2/thumb.png"
onclick="onImageClick(this)"&gt;&lt;br&gt;
&lt;i&gt;Endangered Ocean: Sea Turtles&lt;/i&gt;&lt;br&gt;
(MP4, WebM)
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="thumbRow"&gt;
&lt;div class="thumbCell"&gt;
&lt;img id="t3"
src="https://turtle-tube.appspot.com/t/t3/thumb.png"
onclick="onImageClick(this)"&gt;&lt;br&gt;
&lt;i&gt;sea turtles exercise: bent arms&lt;/i&gt;&lt;br&gt;
(WebM only)
&lt;/div&gt;
&lt;div class="thumbCell"&gt;
&lt;img id="t4"
src="https://turtle-tube.appspot.com/t/t4/thumb.png"
onclick="onImageClick(this)"&gt;&lt;br&gt;
&lt;i&gt;sea turtles exercise: straight arms&lt;/i&gt;&lt;br&gt;
(WebM only)
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="thumbRow"&gt;
&lt;div class="thumbCell"&gt;
&lt;img id="t5"
src="https://turtle-tube.appspot.com/t/t5/thumb.png"
onclick="onImageClick(this)"&gt;&lt;br&gt;
&lt;i&gt;Using robots to reveal secrets of walking baby sea turtles&lt;/i&gt;&lt;br&gt;
(MP4, WebM)
&lt;/div&gt;
&lt;div class="thumbCell"&gt;
&lt;img id="e6"
src="https://turtle-tube.appspot.com/t/e6/thumb.png"
onclick="onImageClick(this)"&gt;&lt;br&gt;
&lt;i&gt;kitten vs sea turtle&lt;/i&gt;&lt;br&gt;
(MP4 only, encrypted)
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="videoOverlay"&gt;
&lt;div id="vcenterWrapper"&gt;
&lt;video id="video"
poster="https://turtle-tube.appspot.com/poster.jpg"
crossorigin="anonymous"
controls autoplay&gt;
Your browser does not support HTML5 video.
&lt;/video&gt;
<span class="newCode"> &lt;img id="hd" src="https://turtle-tube.appspot.com/hd.png"&gt;</span>
<span class="newCode"> &lt;div id="errorOverlay"&gt;&lt;/div&gt;</span>
&lt;/div&gt;
&lt;button id="closeButton" onclick="closeVideo()"&gt;Close Video&lt;/button&gt;
&lt;/div&gt;
&lt;/body&gt;
&lt;script&gt;
var video;
<span class="newCode"> var hd;</span>
var player;
var estimator;
function initPlayer() {
// Install polyfills.
shaka.polyfill.installAll();
<span class="newCode"> // Get important elements.</span>
video = document.getElementById('video');
<span class="newCode"> hd = document.getElementById('hd');</span>
// 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;
<span class="newCode"> // Listen for adaptation events.</span>
<span class="newCode"> player.addEventListener('adaptation', onAdaptation);</span>
<span class="newCode"></span>
// Listen for errors from the Player.
<span class="newCode"> player.addEventListener('error', onError);</span>
// 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();
<span class="newCode"> }</span>
<span class="newCode"></span>
<span class="newCode"> /**</span>
<span class="newCode"> * @param {!Event} event</span>
<span class="newCode"> */</span>
<span class="newCode"> function onAdaptation(event) {</span>
<span class="newCode"> // Ignore non-video adaptation events.</span>
<span class="newCode"> if (event.contentType != 'video') {</span>
<span class="newCode"> return;</span>
<span class="newCode"> }</span>
<span class="newCode"></span>
<span class="newCode"> // Resize the video element to match the content's aspect ratio.</span>
<span class="newCode"> var aspect = event.size.width / event.size.height;</span>
<span class="newCode"> var w = video.offsetWidth;</span>
<span class="newCode"> var h = w / aspect;</span>
<span class="newCode"> video.style.width = w + 'px';</span>
<span class="newCode"> video.style.height = h + 'px';</span>
<span class="newCode"> video.style.left = (-w / 2) + 'px';</span>
<span class="newCode"> video.style.top = (-h / 2) + 'px';</span>
<span class="newCode"></span>
<span class="newCode"> // Position the HD icon in the top-right of the video element.</span>
<span class="newCode"> // 0,0 for this icon is the center of the video element.</span>
<span class="newCode"> hd.style.top = ((-h / 2) + 5) + 'px';</span>
<span class="newCode"> hd.style.right = ((-w / 2) + 5) + 'px';</span>
<span class="newCode"></span>
<span class="newCode"> // If the video is 720p or above, show the HD icon.</span>
<span class="newCode"> if (event.size.height &gt;= 720) {</span>
<span class="newCode"> hd.style.display = 'block';</span>
<span class="newCode"> } else {</span>
<span class="newCode"> hd.style.display = 'none';</span>
<span class="newCode"> }</span>
<span class="newCode"> }</span>
<span class="newCode"></span>
<span class="newCode"> /**</span>
<span class="newCode"> * @param {!Event} event</span>
<span class="newCode"> */</span>
<span class="newCode"> function onError(event) {</span>
<span class="newCode"> var overlay = document.getElementById('errorOverlay');</span>
<span class="newCode"> // This contains details about the error.</span>
<span class="newCode"> var error = event.detail;</span>
<span class="newCode"></span>
<span class="newCode"> // Format a message to show to the user in the overlay.</span>
<span class="newCode"> var text = 'Error (' + error.type + '):\n';</span>
<span class="newCode"> text += error.message;</span>
<span class="newCode"></span>
<span class="newCode"> // Display it.</span>
<span class="newCode"> overlay.textContent = text;</span>
<span class="newCode"> overlay.style.display = 'block';</span>
<span class="newCode"></span>
<span class="newCode"> // It would also be a good idea to log an anonymized version of the error</span>
<span class="newCode"> // object to the server.</span>
}
/**
* @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() &gt;= 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.&lt;shaka.player.DrmInfo.Config&gt;} 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
}];
}
<span class="newCode"> // See app.interpretContentProtection_() in app.js for more examples of</span>
<span class="newCode"> // what this callback can do.</span>
<span class="newCode"></span>
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';
<span class="newCode"></span>
<span class="newCode"> // Hide the error overlay.</span>
<span class="newCode"> overlay = document.getElementById('errorOverlay');</span>
<span class="newCode"> overlay.style.display = 'none';</span>
}
document.addEventListener('DOMContentLoaded', initPlayer);
&lt;/script&gt;
&lt;/html&gt;
</code></pre>