mirror of
https://github.com/shaka-project/shaka-player.git
synced 2026-06-15 16:06:41 +03:00
1c47e1e09b
Closes #137 Change-Id: I092a6152d8c9c2f3809f3ef27e237c06ff0a6490
985 lines
41 KiB
HTML
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"><!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>
|
|
</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"><!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<span class="newCode"> <title>TurtleTube - Autoplay</title></span>
|
|
<!-- Load the Shaka Player library. -->
|
|
<script src="shaka-player.compiled.js"></script>
|
|
</head>
|
|
<body>
|
|
<video id="video"
|
|
width="640" height="480"
|
|
crossorigin="anonymous"
|
|
<span class="newCode"> controls</span>
|
|
<span class="newCode"> autoplay><!-- Start playing right away on load. --></span>
|
|
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>
|
|
</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"><!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<span class="newCode"> <title>TurtleTube - Async Load</title></span>
|
|
<!-- Load the Shaka Player library. -->
|
|
<script src="shaka-player.compiled.js"></script>
|
|
</head>
|
|
<body>
|
|
<span class="newCode"> <ul id="videoTracks"></ul></span>
|
|
<video id="video"
|
|
width="640" height="480"
|
|
crossorigin="anonymous"
|
|
<span class="newCode"> controls><!-- No autoplay attribute. --></span>
|
|
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.
|
|
<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 < 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);
|
|
</script>
|
|
</html>
|
|
</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"><!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<span class="newCode"> <title>TurtleTube - Encrypted Content</title></span>
|
|
<!-- Load the Shaka Player library. -->
|
|
<script src="shaka-player.compiled.js"></script>
|
|
</head>
|
|
<body>
|
|
<video id="video"
|
|
width="640" height="480"
|
|
crossorigin="anonymous"
|
|
<span class="newCode"> controls autoplay></span>
|
|
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);
|
|
});
|
|
|
|
<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.<shaka.player.DrmInfo.Config>} 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);
|
|
</script>
|
|
</html>
|
|
</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"><!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<span class="newCode"> <title>TurtleTube - Beta!</title></span>
|
|
<!-- Load the Shaka Player library. -->
|
|
<script src="shaka-player.compiled.js"></script>
|
|
<span class="newCode"> <style></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"> </style></span>
|
|
</head>
|
|
<body>
|
|
<span class="newCode"> <h1>TurtleTube!</h1></span>
|
|
<span class="newCode"> <h2>Choose a video:</h2></span>
|
|
<span class="newCode"></span>
|
|
<span class="newCode"> <div id="thumbContainer"></span>
|
|
<span class="newCode"> <div class="thumbRow"></span>
|
|
<span class="newCode"> <div class="thumbCell"></span>
|
|
<span class="newCode"> <img id="t1"</span>
|
|
<span class="newCode"> src="https://turtle-tube.appspot.com/t/t1/thumb.png"</span>
|
|
<span class="newCode"> onclick="onImageClick(this)"><br></span>
|
|
<span class="newCode"> <i>cute green sea turtle in Ko'olina Hawai'i</i><br></span>
|
|
<span class="newCode"> (MP4, WebM)</span>
|
|
<span class="newCode"> </div></span>
|
|
<span class="newCode"> <div class="thumbCell"></span>
|
|
<span class="newCode"> <img id="t2"</span>
|
|
<span class="newCode"> src="https://turtle-tube.appspot.com/t/t2/thumb.png"</span>
|
|
<span class="newCode"> onclick="onImageClick(this)"><br></span>
|
|
<span class="newCode"> <i>Endangered Ocean: Sea Turtles</i><br></span>
|
|
<span class="newCode"> (MP4, WebM)</span>
|
|
<span class="newCode"> </div></span>
|
|
<span class="newCode"> </div></span>
|
|
<span class="newCode"> <div class="thumbRow"></span>
|
|
<span class="newCode"> <div class="thumbCell"></span>
|
|
<span class="newCode"> <img id="t3"</span>
|
|
<span class="newCode"> src="https://turtle-tube.appspot.com/t/t3/thumb.png"</span>
|
|
<span class="newCode"> onclick="onImageClick(this)"><br></span>
|
|
<span class="newCode"> <i>sea turtles exercise: bent arms</i><br></span>
|
|
<span class="newCode"> (WebM only)</span>
|
|
<span class="newCode"> </div></span>
|
|
<span class="newCode"> <div class="thumbCell"></span>
|
|
<span class="newCode"> <img id="t4"</span>
|
|
<span class="newCode"> src="https://turtle-tube.appspot.com/t/t4/thumb.png"</span>
|
|
<span class="newCode"> onclick="onImageClick(this)"><br></span>
|
|
<span class="newCode"> <i>sea turtles exercise: straight arms</i><br></span>
|
|
<span class="newCode"> (WebM only)</span>
|
|
<span class="newCode"> </div></span>
|
|
<span class="newCode"> </div></span>
|
|
<span class="newCode"> <div class="thumbRow"></span>
|
|
<span class="newCode"> <div class="thumbCell"></span>
|
|
<span class="newCode"> <img id="t5"</span>
|
|
<span class="newCode"> src="https://turtle-tube.appspot.com/t/t5/thumb.png"</span>
|
|
<span class="newCode"> onclick="onImageClick(this)"><br></span>
|
|
<span class="newCode"> <i>Using robots to reveal secrets of walking baby sea turtles</i><br></span>
|
|
<span class="newCode"> (MP4, WebM)</span>
|
|
<span class="newCode"> </div></span>
|
|
<span class="newCode"> <div class="thumbCell"></span>
|
|
<span class="newCode"> <img id="e6"</span>
|
|
<span class="newCode"> src="https://turtle-tube.appspot.com/t/e6/thumb.png"</span>
|
|
<span class="newCode"> onclick="onImageClick(this)"><br></span>
|
|
<span class="newCode"> <i>kitten vs sea turtle</i><br></span>
|
|
<span class="newCode"> (MP4 only, encrypted)</span>
|
|
<span class="newCode"> </div></span>
|
|
<span class="newCode"> </div></span>
|
|
<span class="newCode"> </div></span>
|
|
<span class="newCode"> <div id="videoOverlay"></span>
|
|
<span class="newCode"> <div id="vcenterWrapper"></span>
|
|
<span class="newCode"> <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></span>
|
|
<span class="newCode"> Your browser does not support HTML5 video.</span>
|
|
<span class="newCode"> </video></span>
|
|
<span class="newCode"> </div></span>
|
|
<span class="newCode"> <button id="closeButton" onclick="closeVideo()">Close Video</button></span>
|
|
<span class="newCode"> </div></span>
|
|
</body>
|
|
<script>
|
|
<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() >= 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.<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;
|
|
}
|
|
|
|
<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);
|
|
</script>
|
|
</html>
|
|
</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"><!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<span class="newCode"> <title>TurtleTube - Beta (HD)!</title></span>
|
|
<!-- 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;
|
|
<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>
|
|
}
|
|
</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>
|
|
<span class="newCode"> <img id="hd" src="https://turtle-tube.appspot.com/hd.png"></span>
|
|
<span class="newCode"> <div id="errorOverlay"></div></span>
|
|
</div>
|
|
<button id="closeButton" onclick="closeVideo()">Close Video</button>
|
|
</div>
|
|
</body>
|
|
<script>
|
|
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 >= 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() >= 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
|
|
}];
|
|
}
|
|
|
|
<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);
|
|
</script>
|
|
</html>
|
|
</code></pre>
|
|
|