diff --git a/README.md b/README.md index 89de1f9eb..d923a0ef2 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # Shaka Player # Shaka Player is a JavaScript library for adaptive video streaming. -It plays [DASH][] content without plugins using [MediaSource Extensions][] and -[Encrypted Media Extensions][]. +It plays [DASH][] content without browser plugins using +[MediaSource Extensions][] and [Encrypted Media Extensions][]. We are currently testing on the latest stable releases of Chrome, Firefox, and Edge, as well as IE 11 and Safari 9. We test using both Widevine and PlayReady, @@ -29,9 +29,7 @@ and multilingual content. And best of all, it's free! * [mailing list](https://groups.google.com/forum/#!forum/shaka-player-users) (join for release announcements or to discuss development) * [hosted API docs](http://shaka-player-demo.appspot.com/docs/api/index.html) - *__(HOSTED DOCS FOR v2 COMING SOON)__* - * [tutorials](http://shaka-player-demo.appspot.com/docs/tutorials/index.html) - *__(HOSTED DOCS FOR v2 COMING SOON)__* + * [tutorials](http://shaka-player-demo.appspot.com/docs/api/tutorial-welcome.html) ## Compiled Mode ## @@ -43,7 +41,7 @@ to compile the sources and documentation are included in the sources: If you installed Shaka Player via npm, the sources have already been compiled for you. -In order to build, you simply need python v2.7+ (for the build scripts) and +In order to build, you simply need python v2.7 (for the build scripts) and JRE 7+ (for the compiler). Just run `./build/all.py` and look for the output in `dist/shaka-player.compiled.js`. The output can be included directly in a ` + + + + + + + +``` + +```js +// myapp.js + +var manifestUri = '//storage.googleapis.com/shaka-demo-assets/angel-one/dash.mpd'; + +function initApp() { + // Install built-in polyfills to patch browser incompatibilities. + shaka.polyfill.installAll(); + + // Check to see if the browser supports the basic APIs Shaka needs. + shaka.Player.support().then(function(support) { + if (support.supported) { + // Everything looks good! + initPlayer(); + } else { + // This browser does not have the minimum set of APIs we need. + alert('Browser not supported!'); + } + }); +} + +function initPlayer() { + // Create a Player instance. + var video = document.getElementById('video'); + var player = new shaka.Player(video); + + // Attach player to the window to make it easy to access in the JS console. + window.player = player; + + // Listen for error events. + player.addEventListener('error', onErrorEvent); + + // Try to load a manifest. + player.load(manifestUri).then(function() { + // The video has now been loaded! + }).catch(onError); +} + +function onErrorEvent(event) { + // Extract the shaka.util.Error object from the event. + onError(event.detail); +} + +function onError(error) { + // Log the error. + console.error(error); + alert('Error code ' + error.code); +} + +document.addEventListener('DOMContentLoaded', initApp); +``` + +That's it! + + +#### Continue the Tutorials + +Next, check out {@tutorial debugging}. diff --git a/docs/tutorials/config.md b/docs/tutorials/config.md new file mode 100644 index 000000000..67d066c16 --- /dev/null +++ b/docs/tutorials/config.md @@ -0,0 +1,91 @@ +# Configuration + +The goal of this tutorial is to introduce Shaka's configuration system and the +concepts on which it is built. More detail can be found in the API docs. + +Shaka's Player object has a hierarchical configuration. The overall player +config contains sub-configs for various parts of the system, such as manifests, +streaming, and DRM. + +To see the current config, you can use `player.getConfiguration()`. If you run +this without setting anything first, you get the default configuration. + +Player also has a `configure()` method that takes a plain, anonymous object as +an argument. Any fields you leave out of the config object will retain their +existing values, and any fields you explicitly set as `undefined` will be +reverted to their default value. + +You can use the code from {@tutorial basic-usage} and try these examples in +the JS console: + +```js +player.getConfiguration(); + +=> Object + abrManager: SimpleAbrManager + drm: Object + advanced: Object + clearKeys: Object + retryParameters: Object + backoffFactor: 2 + baseDelay: 1000 + fuzzFactor: 0.5 + maxAttempts: 1 + timeout: 0 + servers: Object + enableAdaptation: true + manifest: Object + dash: Object + retryParameters: Object + preferredAudioLanguage: "" + preferredTextLanguage: "" + streaming: Object + bufferBehind: 30 + bufferingGoal: 30 + rebufferingGoal: 15 + retryParameters: Object + + +// set audio language preference to Canadian French: +player.configure({ preferredAudioLanguage: 'fr-CA' }); + +// set text language preference to Greek and buffering goal to 2 minutes: +player.configure({ + preferredTextLanguage: 'el', + streaming: { + bufferingGoal: 120 + } +}); + +// check audio language preference, which is still Canadian French: +player.getConfiguration().preferredAudioLanguage + +// check buffering goal, which is 2 minutes: +player.getConfiguration().streaming.bufferingGoal + +// check rebuffering goal, which is still the default of 15 seconds: +player.getConfiguration().streaming.rebufferingGoal + +// set the rebuffering goal to 2 seconds and revert buffering goal to default: +player.configure({ + streaming: { + bufferingGoal: undefined, + rebufferingGoal: 2 + } +}); +``` + +Some of these fields have immediate effects (such as language-related settings, +networking settings, and buffering settings) while some will not have any +effect until the next call to `load()` (such as DRM and manifest settings). + + +#### Detailed API Docs + +For more detail on individual configuration options, please see the API docs for +{@link shakaExtern.PlayerConfiguration} and {@link shaka.Player#configure}. + + +#### Continue the Tutorials + +Next, check out {@tutorial network-and-buffering-config}. diff --git a/docs/tutorials/debugging.md b/docs/tutorials/debugging.md new file mode 100644 index 000000000..95da5c26a --- /dev/null +++ b/docs/tutorials/debugging.md @@ -0,0 +1,137 @@ +# Debugging the Uncompiled Library + +#### Create an error + +Let's say that we have some kind of error, and we want to debug it. +To simulate this, we'll start with the code from {@tutorial basic-usage} +and make a bad change. + +First, let's make `manifestUri` blank. + +```js +var manifestUri = ''; +``` + +This causes an alert that says "Error code 4000". What now? + + +#### Use the JS console + +Open the JavaScript console and look at the shaka.util.Error object we logged. +It has `category: 4`, `code: 4000`, and `data: [""]`. To find out what that +means, let's open lib/util/error.js and search for the error code: + +```js + /** + * The Player was unable to guess the manifest type based on file extension + * or MIME type. To fix, try one of the following: + *
+ *
error.data[0] is the manifest URI. + */ + 'UNABLE_TO_GUESS_MANIFEST_TYPE': 4000, +``` + +So the player could not determine what type of manifest that is, and `data[0]` +is the manifest URI. Looking at `data[0]`, we see a blank string, which is +what we told the player to load. + +But it's still not clear *why* that error code is being sent. To find out more, +we should look at the logs. Logging is turned off during compilation, so we +need to load the uncompiled library. + + +#### Loading the uncompiled library + +Instead of the single-file compiled library, we need to load three scripts in +our HTML file: + +1. Closure's base library +2. A dependency file which maps Shaka's class names to source files +3. The uncompiled library's bootstrap file, "shaka-player.uncompiled.js" + +The uncompiled library's bootstrap file will use Closure to "require" each of +the exported classes in the library. This causes the entire library to be +loaded, one uncompiled source file at a time. + +```html + + + + + + + + + + + +``` + +Reload the page and look in the JavaScript console. Now we see: + +```js +Unable to guess manifest type by file extension or by MIME type. + undefined text/html player.js:297 + +s…a.u…l.Error {category: 4, code: 4000, data: Array[1], + message: "Shaka Error MANIFEST.UNABLE_TO_GUESS_MANIFEST_TYPE ()", + stack: "Error: Shaka Error… at http://localhost/shaka/lib/player.js:300:35"} +``` + +So much more information! The uncompiled library includes a log from Player +(player.js, line 297) right before the error was dispatched, and the error +includes a message that gives the full human-readable name of the error: +`MANIFEST.UNABLE_TO_GUESS_MANIFEST_TYPE`. + +There's also a `stack` field showing the context in which it was generated: + +``` +Error: Shaka Error MANIFEST.UNABLE_TO_GUESS_MANIFEST_TYPE () + at new shaka.util.Error (http://localhost/shaka/lib/util/error.js:77:13) + at http://localhost/shaka/lib/player.js:300:35 +``` + +So now we know player.js line 300 is the source of the error. + + +#### Setting the log level + +Sometimes the error and stack trace isn't enough. Sometimes you need to see a +long sequence of events leading up to an error. For this, you want to set the +log level. The log level lets you control what logs are shown by the uncompiled +library. To set the log level: + +```js +// Debug logs, when the default of INFO isn't enough: +shaka.log.setLevel(shaka.log.Level.DEBUG); + +// Verbose logs, which can generate a lot of output: +shaka.log.setLevel(shaka.log.Level.V1); + +// Verbose 2, which is extremely noisy: +shaka.log.setLevel(shaka.log.Level.V2); +``` + +Please note that this method is not available from the compiled library. + + +#### Okay, but why *that* error? + +To keep the API simple, Shaka tries to guess what type of manifest you want to +load. It does this first based on extension, and if that fails, it makes a HEAD +request and checks the MIME type. + +A request for "" is interpretted as a relative URL. What we actually requested +was the index page for the folder the HTML is in. + +Since the file extension of "" was `undefined`, and the MIME type was +`text/html`, neither of those matched a registered manifest parser. + + +#### Continue the Tutorials + +Next, check out {@tutorial config}. diff --git a/docs/tutorials/drm-config.md b/docs/tutorials/drm-config.md new file mode 100644 index 000000000..27fdc696e --- /dev/null +++ b/docs/tutorials/drm-config.md @@ -0,0 +1,124 @@ +# DRM Configuration + +#### License Servers + +Without DRM configuration, Shaka only plays clear content. To play protected +content, the application only needs to tell Shaka one basic thing: the URL(s) +of its license server(s). + +We've made this simple through `player.configure()`. The field `drm.servers` is +an object mapping key system IDs to server URLs. For example, to set license +servers for both Widevine and Playready: + +```js +player.configure({ + drm: { + servers: { + 'com.widevine.alpha': 'https://foo.bar/drm/widevine', + 'com.microsoft.playready': 'https://foo.bar/drm/playready' + } + } +}); +``` + +Assuming your manifest uses the standard UUIDs for those key systems, that's +all you need to do. + + +#### Choosing a Key System + +Shaka Player is key-system-agnostic, meaning we don't prefer any key systems +over any others. We use EME to ask the browser what it supports, and make no +assumptions. If your browser supports multiple key systems, the first supported +key system in the manifest is used. + +The interoperable encryption standard that DRM vendors are implementing is +called Common Encryption (CENC). Some DASH manifests don't specify any +particular key system at all, but instead state that any CENC system will do: + +```xml + +``` + +If this is the only `` element in the manifest, Shaka will +try {@link shaka.dash.ContentProtection.defaultKeySystems\_ all key systems +it knows}. If the browser supports it and you configured a license server URL +for it, we'll use it. + + +#### Clear Key + +The EME spec requires browsers to support a common key system called "Clear +Key". *(At the time of this writing (April 2016), only Chrome and Firefox +have implemented "Clear Key".)* +Clear Key uses unencrypted keys to decrypt CENC content, and can be useful +for diagnosing problems and testing integrations. To configure Clear Key, +use the configuration field `drm.clearKeys` and provide a map of key IDs to +content keys (both in hex): + +```js +player.configure({ + drm: { + clearKeys: { + 'deadbeefdeadbeefdeadbeefdeadbeef': '18675309186753091867530918675309', + '02030507011013017019023029031037': '03050701302303204201080425098033' + } + } +}); +``` + +This will force the use of Clear Key for decryption, regardless of what is in +your manifest. Use this when you need to confirm that your keys are correct. + + +#### Clear Key Licenses + +If your manifest actually specifies Clear Key, you can also use the normal +license request mechanism to retrieve keys based on key IDs. The EME spec +defines a JSON-based [license request format] and [license format] for the +Clear Key CDM. If you have a server that understands these, just configure +a license server as normal: + +```js +player.configure({ + drm: { + servers: { + 'org.w3.clearkey': 'http://foo.bar/drm/clearkey' + } + } +}); +``` + +[license request format]: https://w3c.github.io/encrypted-media/#clear-key-request-format +[license format]: https://w3c.github.io/encrypted-media/#clear-key-license-format + + +#### Advanced DRM Configuration + +We have several {@link shakaExtern.AdvancedDrmConfiguration advanced options} +available to give you access to the full EME configuration. The config field +`drm.advanced` is an object mapping key system IDs to their advanced settings. +For example, to require hardware security in Widevine: + +```js +player.configure({ + drm: { + servers: { + 'com.widevine.alpha': 'https://foo.bar/drm/widevine' + }, + advanced: { + 'com.widevine.alpha': { + 'videoRobustness': 'HW_SECURE_ALL', + 'audioRobustness': 'HW_SECURE_ALL' + } + } + } +}); +``` + +If you don't need them, you can leave these at their default settings. + + +#### Continue the Tutorials + +Next, check out {@tutorial license-server-auth}. diff --git a/docs/tutorials/index.json b/docs/tutorials/index.json new file mode 100644 index 000000000..a861f4084 --- /dev/null +++ b/docs/tutorials/index.json @@ -0,0 +1,15 @@ +[ + { "welcome": { "title": "Welcome to Shaka Player" } }, + { "basic-usage": { "title": "Basic Usage" } }, + { "debugging": { "title": "Debugging the Uncompiled Library" } }, + { "config": { "title": "Configuration" } }, + { "network-and-buffering-config": { "title": "Network and Buffering Configuration" } }, + { "drm-config": { "title": "DRM Configuration" } }, + { "license-server-auth": { "title": "License Server Authentication" } }, + { "Coming Soon": { "children": [ + { "license-headers": { "title": "License Headers and Track Restrictions" } }, + { "live": { "title": "Special Considerations for Live Streaming" } }, + { "offline": { "title": "Offline Storage and Playback" } } + ] } }, + { "plugins": { "title": "Plugins and Customizing the Build" } } +] diff --git a/docs/tutorials/license-server-auth.md b/docs/tutorials/license-server-auth.md new file mode 100644 index 000000000..2e3c3ec59 --- /dev/null +++ b/docs/tutorials/license-server-auth.md @@ -0,0 +1,175 @@ +# License Server Authentication + +Your application's license server may require some form of authentication so +that it only delivers licenses to paying users. In this tutorial, we're going +to use different license server endpoints that require different forms of +authentication. + +*Please note that the license server we are using in this tutorial is a +Widevine license server, so you will need to use Chrome to follow along.* + +To start, we're going to use the code from {@link basic-usage}, but use this +manifest and license server: + +```js +var manifestUri = '//storage.googleapis.com/shaka-demo-assets/sintel-widevine/dash.mpd'; +var licenseServer = '//cwip-shaka-proxy.appspot.com/no_auth'; +``` + +We'll also need to configure the player to use this license server before it +loads the manifest: + +```js + player.configure({ + drm: { + servers: { 'com.widevine.alpha': licenseServer } + } + }); + + // Try to load a manifest. + player.load(manifestUri).then(function() { + // The video should now be playing! + }).catch(onError); +``` + +Since the endpoint is `/no_auth`, this should play without authentication. + + +#### Header authentication + +First, we'll try authentication using headers. Change the license server to: + +```js +var licenseServer = '//cwip-shaka-proxy.appspot.com/header_auth'; +``` + +This endpoint requires a specific header value to deliver a license. If you +try to use it without setting the authentication header, you will see `Error +code 6007`, which means `LICENSE_REQUEST_FAILED`. The JavaScript console will +show you a failed HTTP request with HTTP status code `401 (Unauthorized)`, and +playback will hang when you get to the encrypted part of the stream (10 seconds +in). + +To authenticate to this endpoint, we must send a special header. You can add +arbitrary headers to Shaka's requests through a request filter callback. +Register the filter before calling `player.load()`: + +```js + player.getNetworkingEngine().registerRequestFilter(function(type, request) { + // Only add headers to license requests: + if (type == shaka.net.NetworkingEngine.RequestType.LICENSE) { + // This is the specific header name and value the server wants: + request.headers['CWIP-Auth-Header'] = 'VGhpc0lzQVRlc3QK'; + } + }); +``` + +Load the page again, and the license request will succeed. Although we are +using a fixed value for the purposes of this tutorial, your application can +derive appropriate authentication header(s) before or during the callback. + + +#### Parameter Authentication + +Next, we'll try authentication using URL parameters. Change the license server +to: + +```js +var licenseServer = '//cwip-shaka-proxy.appspot.com/param_auth'; +``` + +This endpoint requires a specific URL parameter to deliver a license. If you +try to use it without setting the parameter, you will see `Error code 6007` +(`LICENSE_REQUEST_FAILED`) just as before with header authentication. + +We can use a request filter to modify the URL and add the required parameter: + +```js + player.getNetworkingEngine().registerRequestFilter(function(type, request) { + // Only add headers to license requests: + if (type == shaka.net.NetworkingEngine.RequestType.LICENSE) { + // This is the specific parameter name and value the server wants: + request.uris[0] += '?CWIP-Auth-Param=VGhpc0lzQVRlc3QK'; + } + }); +``` + +Load the page again, and the license request will succeed. + + +#### Cookie Authentication + +Finally, let's try using cookies for authentication. Change the license server +to: + +```js +var licenseServer = '//cwip-shaka-proxy.appspot.com/cookie_auth'; +``` + +This endpoint requires a specific cookie to deliver a license. If you try to +use it without setting the parameter, you will see `Error code 6007` +(`LICENSE_REQUEST_FAILED`) just as with the other endpoints. + +Cookies are set by a server to be returned to that server, and are not sent by +the JavaScript application. So to set the required cookie value, point your +browser to the server's [set\_cookie][] page. + +Open the JavaScript console and check the value of `document.cookie` to confirm +that you have the cookie. You should see `"CWIP-Auth-Cookie=VGhpc0lzQVRlc3QK"`. + +Now load the Shaka page again, and ... we still get error code 6007. What +happened? + +Cookies are considered "credentials" by the browser's XmlHttpRequest API, and +credentials may not be sent cross-origin unless: + +1. [explicitly requested by the application](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials) AND +2. [explicitly allowed by the destination server](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Access-Control-Allow-Credentials) + +Our `cookie_auth` endpoint sends back headers that allow credentialed requests, +so we set a flag in our request filter to send credentials cross-site: + +```js + player.getNetworkingEngine().registerRequestFilter(function(type, request) { + if (type == shaka.net.NetworkingEngine.RequestType.LICENSE) { + request.allowCrossSiteCredentials = true; + } + }); +``` + +Load the page again, and the license request will succeed. + +[set\_cookie]: http://cwip-shaka-proxy.appspot.com/set_cookie + + +#### Always Sending Credentials + +Now, you may be asking yourself: "Why not just make `true` the default and +always send credentials when we have them?" + +If a server does not explicitly allow credentials to be sent cross-origin, +setting this flag would cause the request to fail *even if the client has no +cookies to send*. If you'd like to try this out, clear your cookies by +pointing your browser to the server's [delete\_cookie][] page. Then set your +license server back to: + +```js +var licenseServer = '//cwip-shaka-proxy.appspot.com/no_auth'; +``` + +Since `allowCrossSiteCredentials` is `true` and that endpoint doesn't +explicitly allow credentials, you'll get a failure. The JavaScript console +will show a message from the browser that says something like: + +```html +Credentials flag is 'true', but the 'Access-Control-Allow-Credentials' header +is ''. It must be 'true' to allow credentials. Origin 'http://localhost' is +therefore not allowed access. +``` + +[delete\_cookie]: http://cwip-shaka-proxy.appspot.com/delete_cookie + + +#### Continue the Tutorials + +Next, check out {@tutorial plugins}. diff --git a/docs/tutorials/network-and-buffering-config.md b/docs/tutorials/network-and-buffering-config.md new file mode 100644 index 000000000..70ee88907 --- /dev/null +++ b/docs/tutorials/network-and-buffering-config.md @@ -0,0 +1,86 @@ +# Network and Buffering Configuration + +#### Networking Configuration + +Shaka Player has separate network retry settings for each of the different +types of requests: manifest, license, and segment requests. For example: you +may want a failed license request to be retried differently from a failed +segment request. + +The three separate retry settings are found under `drm.retryParameters` (for +license requests), `manifest.retryParameters` (for manifest requests), and +`streaming.retryParameters` (for segment requests). + +They all look the same, though: + +```js +retryParameters: { + timeout: 0, // timeout in ms, after which we abort a request; 0 means never + maxAttempts: 1, // the maximum number of requests before we fail + baseDelay: 1000, // the base delay in ms between retries + backoffFactor: 2, // the multiplicative backoff factor between retries + fuzzFactor: 0.5, // the fuzz factor to apply to each retry delay +} +``` + +Each time we retry, the backoff factor is applied to the delay between retries. +So, for example, if the base delay is 1s, and the backoff factor is 2: + +1. initial request at time t = 0 seconds +2. delay of 1, retry at t = (0 + 1) = 1 +3. delay of 2, retry at t = (1 + 2) = 3 +4. delay of 4, retry at t = (3 + 4) = 7 +5. delay of 8, retry at t = (7 + 8) = 15 + +and so on. To avoid many clients hammering a server at the same exact time, +we also apply a fuzz factor. A fuzz factor of 0.5 means we fuzz the delay +50% in either direction. So if the ideal delay is 8, the actual delay will be +randomly chosen between 4 and 12. To extend our earlier example: + +1. initial request +2. delay of 1±50% (0.5 to 1.5), retry +3. delay of 2±50% (1 to 3), retry +4. delay of 4±50% (2 to 6), retry +5. delay of 8±50% (4 to 12), retry + +You should consider the default backoff and fuzz factors as a recommendation of +best practice. The base delay, timeout, and maximum number of attempts should +be customized for your application's requirements. + + +#### Buffering Configuration + +Shaka Player's buffering system has three parameters, all of which are nested +under `streaming` in the config object: `bufferingGoal`, `rebufferingGoal`, and +`bufferBehind`. All are expressed in seconds. + +`bufferingGoal` is the amount of content we try to buffer. For example, if +this is set to 30, we fetch segments until we have at least 30 seconds buffered. + +`rebufferingGoal` is the amount of content we have to have buffered before we +can play. For example, if this is 15, we stay in a buffering state until we +have at least 15 seconds buffered. This affects both buffering at startup +and rebuffering later during playback. + +`bufferBehind` is the amount of content we keep in buffer behind the playhead. +For example, if this is 30, we keep 30 seconds of content buffered behind the +video's `currentTime`. When we have more than 30 seconds buffered behind, +content will be removed from the start of the buffer to save memory. + +*NOTES:* + - *`rebufferingGoal` should always be less than `bufferingGoal`.* + - *A DASH manifest's `minBufferTime`, if greater, overrides `rebufferingGoal`.* + +All of these settings should be customized for your application. The default +values are very conservative. + + +#### Try it out + +Use the code from {@tutorial basic-usage} and try configuring some of these +parameters in `playerInit()` to see how they affect playback. + + +#### Continue the Tutorials + +Next, check out {@tutorial drm-config}. diff --git a/docs/tutorials/plugins.md b/docs/tutorials/plugins.md new file mode 100644 index 000000000..a00ee44b1 --- /dev/null +++ b/docs/tutorials/plugins.md @@ -0,0 +1,153 @@ +# Plugins and Customizing the Build + +Shaka has a plugin system to make it easier to extend and customize the +library. Plugins can be written outside the library (in your application), or +they can be built into the library to take advantage of the [Closure compiler]. + +[Closure compiler]: https://github.com/google/closure-compiler + +Manifest parsing, subtitle and caption parsing, networking, ABR, and polyfills +are all done through plugins. Even our "built-in" parsers, such as DASH and +WebVTT, are actually just default plugins. + + +#### Plugins + +A plugin registers itself with a "core" component. These are the various +plugin interfaces and the default plugins that Shaka provides: + +__Manifest parsers__ + - Selected by file extension, with a fall back to manifest MIME type + - Register with {@link shaka.media.ManifestParser.registerParserByExtension} + and {@link shaka.media.ManifestParser.registerParserByMime} + - Default manifest parser plugins: + - DASH: {@link shaka.dash.DashParser} + +__Subtitle/caption parsers__ + - Selected by MIME type + - Register with {@link shaka.media.TextEngine.registerParser} + - Default text parser plugins: + - WebVTT: {@link shaka.media.VttTextParser} + +__Networking plugins__ + - Selected by URI scheme (http, https, etc.) + - Register with {@link shaka.net.NetworkingEngine.registerScheme} + - Default networking plugins: + - HTTP(S): {@link shaka.net.HttpPlugin} + - data URIs: {@link shaka.net.DataUriPlugin} + +__ABR plugins__ + - Configured at runtime on a Player instance + - Use {@link player.configure} and set the `abrManager` field + - Must implement the {@link shakaExtern.AbrManager} interface + - Default AbrManager implementation: {@link shaka.abr.SimpleAbrManager} + +__Polyfills__ + - All polyfills are installed by {@link shaka.polyfill.installAll} + - Register with {@link shaka.polyfill.register} + - Default polyfills: + - prefixed fullscreen implementations: {@link shaka.polyfill.Fullscreen} + - prefixed video QoE metrics: {@link shaka.polyfill.VideoPlaybackQuality} + - prefixed EME implementations for IE 11 and very old versions of embedded + Chrome/Chromium: {@link shaka.polyfill.MediaKeys} + - Promise implementation for IE 11: {@link shaka.polyfill.Promise} + + +#### Excluding Default Plugins + +Core components cannot be removed from the build, but everything else is +technically optional. For example, if you don't need WebVTT, you can exclude +our VTT parser from the build to save space. Any VTT text streams found in a +manifest would then be ignored. + +*(At the time of this writing, our default plugins account for 43% of the size +of our compiled library.)* + +Because each plugin's source file ends with a call to register itself with the +core system, a plugin can simply be excluded from the build without changing +any of the source code. + +You can start with the complete library (`+@complete`) and exclude any +individual source file with a minus sign and a path: + +```sh +python build/build.py +@complete -lib/net/data_uri_plugin.js +``` + +You can also exclude an entire category of plugins: + +```sh +# Build without polyfills: +python build/build.py +@complete -@polyfill +# Build without polyfills or text parsers: +python build/build.py +@complete -@polyfill -@text +``` + + +#### Build Configs + +Each of these arguments that starts with an '@' sign is a build config file in +`build/types/` containing a list sources or other configs to include. Each +line in these files is treated as an argument to `build.py`. For example, +this is what `build/types/networking` looks like: + +```sh +# All standard networking scheme plugins. ++../../lib/net/http_plugin.js ++../../lib/net/data_uri_plugin.js +``` + + +#### Adding Your Own Plugins + +If you want to take advantage of the [Closure compiler], you can add your own +sources to the build. Your plugins, like ours, should register themselves at +the bottom of the source file. + +To add a single source file, prefix it with a plus sign: + +```sh +python build/build.py +@complete +my_plugin.js +``` + +You can add multiple sources as well: + +```sh +python build/build.py +@complete +my_plugin.js +/path/to/my_other_plugin.js +``` + + +#### Custom Build Configs + +If you have a long list of customizations, you may want to create your own +group file. For example: + +```sh +# Start with a complete library ++@complete +# Drop subtitle support +-@text +# Remove default networking plugins +-@networking +# Add my custom HTTP implementation ++/path/to/my_http_plugin.js +# Add an additional polyfill for some odd platform I'm targetting ++/path/to/my_platform_polyfill.js +``` + + +#### Plugins in Your Application + +Every plugin interface is exported from the compiled library so that you don't +have to customize the build to create a plugin. Just register your plugins +with the appropriate interfaces after the library is loaded. + + +#### Giving Back + +If you have a great plugin that you'd like to contribute back to the community, +we'd love to hear from you. You can get in touch via our [mailing list][] to +discuss it, and once it's ready, you can send a [pull request][] on github. + +[mailing list]: https://groups.google.com/forum/#!forum/shaka-player-users +[pull request]: https://github.com/google/shaka-player/pull/new/master diff --git a/docs/tutorials/welcome.md b/docs/tutorials/welcome.md new file mode 100644 index 000000000..d3494559e --- /dev/null +++ b/docs/tutorials/welcome.md @@ -0,0 +1,38 @@ +# Welcome to Shaka Player + +Shaka Player is a JavaScript library for adaptive video streaming. +It plays [DASH][] content without browser plugins using +[MediaSource Extensions][] and [Encrypted Media Extensions][]. + +[DASH]: http://dashif.org/ +[MediaSource Extensions]: http://w3c.github.io/media-source/ +[Encrypted Media Extensions]: https://w3c.github.io/encrypted-media/ + +## Getting Started + +#### Get the source + +```sh +git clone https://github.com/google/shaka-player.git +cd shaka-player +``` + + +#### Compile the library + +```sh +python build/all.py +``` + + +#### Join the mailing list + +If you want to discuss Shaka Player developement or receive notifications when +a new version is released, you should join our [mailing list]. + +[mailing list]: https://groups.google.com/forum/#!forum/shaka-player-users + + +#### Continue the Tutorials + +Next, check out {@tutorial basic-usage}. diff --git a/externs/shaka/player.js b/externs/shaka/player.js index fb3d1a7bf..086dfb03b 100644 --- a/externs/shaka/player.js +++ b/externs/shaka/player.js @@ -318,10 +318,14 @@ shakaExtern.StreamingConfiguration; * @property {string} preferredAudioLanguage * The preferred language to use for audio tracks. If not given it will use * the 'main' track. + * Changing this during playback will cause the language selection algorithm + * to run again, and may change the active audio track. * @property {string} preferredTextLanguage - * The preferred language to use for text tracks. If the audio and text - * tracks have different languages, the text track will be enabled. - * + * The preferred language to use for text tracks. If a matching text track + * is found, and the selected audio and text tracks have different languages, + * the text track will be shown. + * Changing this during playback will cause the language selection algorithm + * to run again, and may change the active text track. * @exportDoc */ shakaExtern.PlayerConfiguration; diff --git a/third_party/jsdoc/templates/default/static/styles/jsdoc-default.css b/third_party/jsdoc/templates/default/static/styles/jsdoc-default.css index 5b84b05bd..7d7016b5b 100644 --- a/third_party/jsdoc/templates/default/static/styles/jsdoc-default.css +++ b/third_party/jsdoc/templates/default/static/styles/jsdoc-default.css @@ -56,6 +56,7 @@ header tt, code, kbd, samp { font-family: Consolas, Monaco, 'Andale Mono', monospace; + background-color: #eee; } .class-description { diff --git a/third_party/jsdoc/templates/default/tmpl/layout.tmpl b/third_party/jsdoc/templates/default/tmpl/layout.tmpl index 4ef2273ce..4eb9cfc8d 100644 --- a/third_party/jsdoc/templates/default/tmpl/layout.tmpl +++ b/third_party/jsdoc/templates/default/tmpl/layout.tmpl @@ -18,7 +18,9 @@
+

+