From 395cc1745e9c51f3c0b66dfc7d3f5eee545a7e74 Mon Sep 17 00:00:00 2001 From: Joey Parrish Date: Fri, 7 Apr 2017 13:27:53 -0700 Subject: [PATCH] Update the upgrade guides for v2.1 Also changes the jsdoc template for tutorials with children, to give the tutorial content itself control over how the links to children are formatted. Also fixes annotations for the TextParser interface. Change-Id: I99502f38bf711b74a596ad804d8abdddee5d6f4d --- docs/tutorials/index.json | 12 +- docs/tutorials/plugins.md | 10 +- docs/tutorials/upgrade-v1.md | 727 +++++++++++++++++ docs/tutorials/upgrade-v2-0.md | 291 +++++++ docs/tutorials/upgrade.md | 728 +----------------- externs/shaka/text.js | 11 +- .../templates/default/tmpl/tutorial.tmpl | 10 - 7 files changed, 1041 insertions(+), 748 deletions(-) create mode 100644 docs/tutorials/upgrade-v1.md create mode 100644 docs/tutorials/upgrade-v2-0.md diff --git a/docs/tutorials/index.json b/docs/tutorials/index.json index 913a87aef9..548f2bc456 100644 --- a/docs/tutorials/index.json +++ b/docs/tutorials/index.json @@ -7,13 +7,15 @@ { "drm-config": { "title": "DRM Configuration" } }, { "license-server-auth": { "title": "License Server Authentication" } }, { "license-wrapping": { "title": "License Wrapping" } }, - { "Coming Soon": { "children": [ - { "live": { "title": "Special Considerations for Live Streaming" } }, - { "offline": { "title": "Offline Storage and Playback" } } - ] } }, { "plugins": { "title": "Plugins and Customizing the Build" } }, { "manifest-parser": { "title": "Manifest Parser Plugins" } }, { "architecture": { "title": "Architecture Diagrams" } }, { "service-worker": { "title": "Service Worker Caching" } }, - { "upgrade": { "title": "Shaka v2 Upgrade Guide" } } + { "upgrade": { + "title": "Upgrade Guide", + "children": { + "upgrade-v1": { "title": "Upgrade Guide, v1 => v2.1" }, + "upgrade-v2-0": { "title": "Upgrade Guide, v2.0 => v2.1" } + } + } } ] diff --git a/docs/tutorials/plugins.md b/docs/tutorials/plugins.md index e934ee1e39..798209c45f 100644 --- a/docs/tutorials/plugins.md +++ b/docs/tutorials/plugins.md @@ -28,12 +28,16 @@ __Manifest parsers__ and {@link shaka.media.ManifestParser.registerParserByMime} - Default manifest parser plugins: - DASH: {@linksource shaka.dash.DashParser} + - HLS: {@linksource shaka.hls.HlsParser} __Subtitle/caption parsers__ - Selected by MIME type - Register with {@link shaka.media.TextEngine.registerParser} - Default text parser plugins: - - WebVTT: {@linksource shaka.media.VttTextParser} + - WebVTT: {@linksource shaka.media.VttTextParser} and + {@linksource shaka.media.Mp4VttParser} + - TTML: {@linksource shaka.media.TtmlTextParser} and + {@linksource shaka.media.Mp4TtmlParser} __Networking plugins__ - Selected by URI scheme (http, https, etc.) @@ -59,6 +63,8 @@ __Polyfills__ - prefixed EME implementations for IE 11 and very old versions of embedded Chrome/Chromium: {@linksource shaka.polyfill.MediaKeys} - Promise implementation for IE 11: {@linksource shaka.polyfill.Promise} + - variants of VTTCue and TextTrackCue constructors: + {@linksource shaka.polyfill.VTTCue} #### Excluding Default Plugins @@ -68,7 +74,7 @@ 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 +*(At the time of this writing, our default plugins account for 54% of the size of our compiled library.)* Because each plugin's source file ends with a call to register itself with the diff --git a/docs/tutorials/upgrade-v1.md b/docs/tutorials/upgrade-v1.md new file mode 100644 index 0000000000..cb35456f45 --- /dev/null +++ b/docs/tutorials/upgrade-v1.md @@ -0,0 +1,727 @@ +# Shaka Upgrade Guide, v1.x => v2 + +This is a detailed guide for upgrading from Shaka Player v1 to v2. It is a bit +long to read from beginning to end, so feel free to skim or to search for the +class and method names you are using in your application. + + +#### What's New in v2? + +Shaka v2 has several improvements over v1, including: + - Support for multiple DASH Periods + - Support for DASH Location elements + - Support for DASH UTCTiming elements for clock synchronization + - Lower-latency startup + - Simplified API + - Better browser compatibility + - More detailed browser support test + - Numerical error code system + - Clears old data from the buffer to conserve memory + - Buffering state is independent of play/pause + - Distinguishes between subtitle and caption tracks + - Separate audio & text language preferences + - New plugin and build system to extend Shaka + - Cache-friendly networking + - Simpler, mobile-friendly demo app + + +#### Shaka Plugins + +Shaka v2 has a new, cleaner architecture than v1 based on plugins. In v2, +networking, manifest parsing, and subtitle/caption parsing are all plugins. + +We bundle some default plugins (HTTP support, DASH support, and WebVTT), and +we plan to expand this list in future releases. Application developers can +write their own plugins as well. Plugins can either be compiled into the +library, or they can live outside the library in the application. Application +developers can also customize the build to exclude any default plugins they +don't need. + +For a more in-depth discussion of plugins, check out {@tutorial plugins}. + + +#### Namespace + +In v1, the `Player` class was namespaced as `shaka.player.Player`. In v2, this +has been simplified to `shaka.Player`. + + +#### load() + +Before, you needed a `DashVideoSource` or other `IVideoSource` subclass to pass +to `player.load()`. The video source was constructed with a manifest URL: + +```js +// v1: +var player = new shaka.player.Player(video); +var videoSource = new shaka.player.DashVideoSource(manifestUri); +player.load(videoSource); +``` + +In v2, the entire video source concept is gone from the API. Now, you pass the +URL directly to the player, and it decides which manifest parser plugin to use +based on the file extension or MIME type: + +```js +// v2: +var player = new shaka.Player(video); +player.load(manifestUri); +``` + + +#### ContentProtection callbacks + +Shaka v1's `DashVideoSource` had a parameter for a `ContentProtection` callback. +This callback was required to play protected content because Shaka did not +interpret `ContentProtection` elements in the DASH manifest and could not +derive the license server URI automatically: + +```js +// v1: +function interpretContentProtection(schemeIdUri, contentProtectionElement) { + if (schemeIdUri.toLowerCase() == + 'urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed') { + // This is the UUID which represents Widevine. + return [{ + 'keySystem': 'com.widevine.alpha', + 'licenseServerUrl': '//widevine-proxy.appspot.com/proxy' + }]; + } else if (schemeIdUri.toLowerCase() == + 'urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95') { + // This is the UUID which represents PlayReady. + return [{ + 'keySystem': 'com.microsoft.playready', + 'licenseServerUrl': '//playready.directtaps.net/pr/svc/rightsmanager.asmx' + }]; + } else { + return null; + } +} +var player = new shaka.player.Player(video); +var videoSource = new shaka.player.DashVideoSource( + manifestUri, interpretContentProtection); +player.load(videoSource); +``` + +In v2, these callbacks are *only* required for *non-standard* ContentProtection +schemes, such as that used by YouTube's demo assets. For the 99% of you who are +using standard schemes, no callback is required. Simply `configure()` the +player with your license servers: + +```js +// v2: +var player = new shaka.Player(video); +player.configure({ + drm: { + servers: { + 'com.widevine.alpha': '//widevine-proxy.appspot.com/proxy' + 'com.microsoft.playready': '//playready.directtaps.net/pr/svc/rightsmanager.asmx' + } + } +}); +player.load(manifestUri); +``` + +For a more in-depth discussion of DRM configuration, see {@tutorial drm-config}. + +If you need to support a custom ContentProtection scheme, you can still do so +with a callback set through `player.configure()`: + +```js +// v2: +function interpretContentProtection(contentProtectionElement) { + if (contentProtectionElement.getAttribute('schemeIdUri') == + 'http://youtube.com/drm/2012/10/10') { + var configs = []; + for (....) { + configs.push({ + 'keySystem': keySystem, + // WATCH OUT: now called URI not URL + 'licenseServerUri': licenseServerUri + }); + } + return configs; + } +} + +var player = new shaka.Player(video); +player.configure({ + manifest: { + dash: { + customScheme: interpretContentProtection + } + } +}); +player.load(manifestUri); +``` + +For more on what you can specify for a custom scheme, see the docs for +{@link shakaExtern.DrmInfo}. + + +#### Detailed DrmInfo + +Shaka v1's ContentProtection callbacks could return a detailed DrmInfo object +with lots of EME-related and license-request-related settings: + +```js +// v1: +function interpretContentProtection(schemeIdUri, contentProtectionElement) { + if (schemeIdUri.toLowerCase() == + 'urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed') { + return [{ + 'keySystem': 'com.widevine.alpha', + 'licenseServerUrl': '//widevine-proxy.appspot.com/proxy', + + 'distinctiveIdentifierRequired': true, + 'persistentStateRequired': false, + 'serverCertificate': certificateUint8Array, + 'audioRobustness': 'HW_SECURE_ALL', + 'videoRobustness': 'HW_SECURE_ALL', + 'initData': { 'initDataType': 'cenc', 'initData': initDataUint8Array }, + + 'licensePreProcessor': licensePreProcessor, + 'licensePostProcessor': licensePostProcessor, + 'withCredentials': true + }]; + } else { + return null; + } +} +function licensePreProcessor(requestInfo) { + // Only called for license requests. + + // Wrap the body, which is an ArrayBuffer: + var newBody = wrapLicenseRequest(requestInfo.body); + requestInfo.body = newBody; + + // Add a header: + requestInfo.headers['foo'] = 'bar'; +} +function licensePostProcessor(license) { + // Only called for license responses. + // Unwrap the license, which is a Uint8Array, use/store the extra data: + var rawLicense = unwrapLicense(license); + // Now return the raw license, which is also a Uint8Array: + return rawLicense; +} +``` + +In v2, the EME settings have moved to the `drm.advanced` field of the config +object: + +```js +// v2: +player.configure({ + drm: { + advanced: { + 'com.widevine.alpha': { + distinctiveIdentifierRequired: true, + persistentStateRequired: true, + serverCertificate: certificateUint8Array, + audioRobustness: 'HW_SECURE_ALL', + videoRobustness: 'HW_SECURE_ALL', + // NOTE: initData is now an array of one or more overrides: + initData: [{ initDataType: 'cenc', initData: initDataUint8Array }] + } + } + } +}); +``` + +For a discussion of advanced DRM configuration, see {@tutorial drm-config}. + +Shaka v1's license-request-releated settings have moved to v2's network filters. + +Network filters are a generic filtering system for all networking, including +license requests and responses. They are more general and flexible, so they +take slightly more effort than the old preprocessor/postprocessor system. +However, in v2, you only need to filter your license traffic for two reasons: + - if you use cross-site credentials (v1's "withCredentials" flag) + - if you wrap/unwrap license requests and responses into some other format + +```js +// v2: +player.getNetworkingEngine().registerRequestFilter(licensePreProcessor); +player.getNetworkingEngine().registerResponseFilter(licensePostProcessor); + +function licensePreProcessor(type, request) { + // A generic filter for all requests, so filter on type LICENSE: + if (type != shaka.net.NetworkingEngine.RequestType.LICENSE) return; + + // Equivalent to v1's 'withCredentials': true + request.allowCrossSiteCredentials = true; + + // Wrap the request data, which is an ArrayBuffer: + var newData = wrapLicenseRequest(request.data); + request.data = newData; + + // Add a header: + request.headers['foo'] = 'bar'; +} +function licensePostProcessor(type, response) { + // A generic filter for all responses, so filter on type LICENSE: + if (type != shaka.net.NetworkingEngine.RequestType.LICENSE) return; + + // Unwrap the response, which is an ArrayBuffer, use/store the extra data: + var rawLicense = unwrapLicense(response.data); + // Instead of returning the raw license, store it back to the response: + response.data = rawLicense; + // Return nothing. +} +``` + +For more on request filters, see the docs for +{@link shaka.net.NetworkingEngine.RequestFilter}, {@link shakaExtern.Request}, +{@link shaka.net.NetworkingEngine.ResponseFilter}, {@link shakaExtern.Response}. + + +#### ClearKey configuration + +Shaka v1's `ContentProtection` callbacks could be used for ClearKey, but it +required you to craft both a data URI and fake init data in the correct format: + +```js +// v1: +function interpretContentProtection(schemeIdUri, contentProtectionElement) { + var keyid; // as Uint8Array + var key; // as Uint8Array + var keyObj = { + kty: 'oct', + kid: Uint8ArrayUtils.toBase64(keyid, false), + k: Uint8ArrayUtils.toBase64(key, false) + }; + var jwkSet = {keys: [keyObj]}; + var license = JSON.stringify(jwkSet); + var initData = { + 'initData': keyid, + 'initDataType': 'webm' + }; + var licenseServerUrl = 'data:application/json;base64,' + + window.btoa(license); + return [{ + 'keySystem': 'org.w3.clearkey', + 'licenseServerUrl': licenseServerUrl, + 'initData': initData + }]; +} +``` + +In v2, this has been simplified through `player.configure()`: + +```js +// v2: +player.configure({ + drm: { + clearKeys: { + 'deadbeefdeadbeefdeadbeefdeadbeef': '18675309186753091867530918675309', + '02030507011013017019023029031037': '03050701302303204201080425098033' + } + } +}); +``` + +For more on ClearKey setup, see {@tutorial drm-config}. + + +#### BandwidthEstimator and AbrManager + +Shaka v1's `DashVideoSource` had parameters for applications to inject custom +BandwidthEstimator and AbrManager implementations. We even recommended +injecting BandwithEstimator to get persisted estimates across playbacks: + +```js +// v1: +var player = new shaka.player.Player(video); +var bandwidthEstimator = new shaka.util.EWMABandwidthEstimator(); +var abrManager = new shaka.media.SimpleAbrManager(); +var videoSource = new shaka.player.DashVideoSource( + manifestUri, /* interpretContentProtection */ null, estimator, abrManager); +player.load(videoSource); +``` + +In v2, we rolled the BandwidthEstimator concept into AbrManager. It is no +longer necessary to inject an instance to persist estimates across playbacks, +and custom AbrManagers are now provided via `player.configure()`: + +```js +// v2: +var player = new shaka.Player(video); +var customAbrManager = new MyCustomAbrManager(); +player.configure({ + abr: { + manager: customAbrManager + } +}); +player.load(manifestUri); +``` + +For more on the AbrManager interface, see the docs for +{@link shakaExtern.AbrManager}. + + +#### Selecting tracks + +Shaka v1 had separate methods for each type of tracks: `getVideoTracks()`, +`getAudioTracks()`, and `getTextTracks()`, as well as `selectVideoTrack()`, +`selectAudioTrack()`, and `selectTextTrack()`. Tracks were selected by ID. + +```js +// v1: +var videoTracks = player.getVideoTracks(); +var i = /* choose an index somehow */; +player.selectVideoTrack(videoTracks[i].id); // id, specifically video +``` + +In Shaka v2, audio and video tracks are combined into a variant track. It is +not possible to select individual audio/video streams, you can only select a +specific variant as specified by the manifest. + +You can get the currently available tracks using `getVariantTracks()` and +`getTextTracks()`. To switch tracks, use `selectVariantTrack()` and +`selectTextTrack()`, passing in the whole track object. + +```js +// v2: +var variantTracks = player.getVariantTracks(); +var i = /* choose an index somehow */; +player.selectVariantTrack(variantTracks[i]); // whole track +``` + +In v1, you could show or hide text tracks with `player.enableTextTrack()`: + +```js +// v1: +player.enableTextTrack(true); +``` + +In v2, this becomes `player.setTextTrackVisibility()`: + +```js +// v2: +player.setTextTrackVisibility(true); +``` + +See also the {@link shakaExtern.Track} structure which is used for all track +types (variant and text). + + +#### Side-loading captions/subtitles + +In Shaka v1, you could side-load subtitles that were not present in the manifest +by calling `addExternalCaptions()` on the `DashVideoSource` before `load()`: + +```js +// v1: +var player = new shaka.player.Player(video); +var videoSource = new shaka.player.DashVideoSource(manifestUri); +videoSource.addExternalCaptions(textStreamUri, 'fr-CA', 'text/vtt'); +player.load(videoSource); +``` + +In v2, this is done on with `player.addTextTrack()` after load() is complete: + +```js +// v2: +var player = new shaka.Player(video); +player.load(manifestUri).then(function() { + player.addTextTrack(textStreamUri, 'fr-CA', 'caption', 'text/vtt'); +}); +``` + + +#### Playback start time + +Shaka v1's `player.setPlaybackStartTime()` would let you start playback at an +arbitrary timestamp. It had to be called before load(): + +```js +// v1: +player.setPlaybackStartTime(123.45); +player.load(manifestUri); +``` + +In v2, this is done through an optional parameter on `load()`: + +```js +// v2: +player.load(manifestUri, 123.45); +``` + + +#### Trick play + +Shaka v1 had `player.setPlaybackRate()` that could be used for trick play by +emulating negative rate support in `video.playbackRate`. If you used +v1's `setPlaybackRate()` for trick play, use v2's `player.trickPlay()`. For +other purposes, use `video.playbackRate` directly. + + +#### configure() + +Shaka v1 and v2 both have a `player.configure()` method. Here is a map of +settings in v1 and their equivalents in v2 (most of which are at a different +level of the configuration hierarchy): + + - `enableAdaptation` => `abr.enabled` + - `streamBufferSize` => `streaming.bufferingGoal` + - `licenseRequestTimeout` => `drm.retryParameters.timeout` + - `mpdRequestTimeout` => `manifest.retryParameters.timeout` + - `segmentRequestTimeout` => `streaming.retryParameters.timeout` + - `preferredLanguage` => split into `preferredAudioLanguage` and + `preferredTextLanguage` + - `restrictions` => (same name, see below) + - `liveStreamEndTimeout` => (not needed in v2) + - `disableCacheBustingEvenThoughItMayAffectBandwidthEstimation` => + (not needed, always cache-friendly) + +The `shaka.player.Restriction` type was replaced by a simple record type. So +instead of constructing an object, simply create an anonymous JavaScript object. +`minPixels`/`maxPixels` were added to limit total pixels. Also `minBandwidth` +and `maxBandwidth` were split into `minAudioBandwidth`, `maxAudioBandwidth`, +`minVideoBandwidth`, and `maxVideoBandwidth`, see +{@link shakaExtern.Restrictions}. + +For more information on configuration in v2, see {@tutorial config}, +{@tutorial network-and-buffering-config}, and {@tutorial drm-config}. + + +#### getStats() + +Shaka v1 had `player.getStats()`. Shaka v2 has a similar method, but it returns +a somewhat different structure. + +```js +// v1: +player.getStats() + +=> Object + streamStats: StreamStats // refers to currently selected video stream + videoWidth: number // pixels + videoHeight: number // pixels + videoMimeType: string + videoBandwidth: number // bits/sec + decodedFrames: number + droppedFrames: number + estimatedBandwidth: number // bits/sec + playTime: number // seconds + bufferingTime: number // seconds + playbackLatency: number // seconds + bufferingHistory: Array // of timestamps when we started buffering + bandwidthHistory: Array of Objects + timestamp: number // seconds, when bandwidth estimate was made + value: number // bandwidth estimate, bits/sec + streamHistory: Array of Objects + timestamp: number // seconds, when video stream changed + value: StreamStats // information about the selected stream, video only +``` + +Shaka v2 does not expose playback latency or a history of bandwidth estimates. +v2's `switchHistory` is more general than v1's `streamHistory`, and covers all +stream types: + +```js +// v2: +player.getStats() + +=> Object + width: number // pixels, current video track + height: number // pixels, current video track + streamBandwidth: number // bits/sec, total for all current streams + decodedFrames: number // same as v1 + droppedFrames: number // same as v1 + estimatedBandwidth: number // bits/sec, same as v1 + loadLatency: number, // seconds between load() and the video's 'loadend' event + playTime: number // seconds, same as v1 + bufferingTime: number // seconds, same as v1 + switchHistory: Array of Objects // replaces v1's streamHistory + timestamp: number // seconds, when the stream was selected + id: number // stream ID + type: string // 'variant' or 'text' + fromAdaptation: boolean // distinguishes between ABR and manual choices + stateChange: Array of Objects + timestamp: number // seconds, when the state changed + state: string // 'buffering', 'playing', 'paused', or 'ended' + duration: number // seconds in this state +``` + +For more on stats in v2, see {@link shakaExtern.Stats}. + + +#### Player events + +In both Shaka v1 and v2, `Player` emits events. Here is a map of events and +their properties in v1 and their equivalents in v2: + + - `error` => `error` + - `detail` (various types) => `detail` ({@link shaka.util.Error}) + - `type` (vague string) => `category` + (number, unambiguous {@link shaka.util.Error.Category error category}) + - `message` (vague string) => `code` + (number, unambiguous {@link shaka.util.Error.Code error code}) + - `bufferingStart` and `bufferingEnd` => combined into `buffering` + - (no v1 equivalent) => `buffering` + (boolean, true when we enter buffering state, false when we leave) + - (no v1 equivalent) => + `{@link shaka.Player.TextTrackVisibilityEvent texttrackvisibility}` + - `trackschanged` => `trackschanged` + - `seekrangechanged` => + (no v2 equivalent, use `{@link player.seekRange}()` when updating the UI) + - `adaptation` => `adaptation` + - `contentType` (string) => (no v2 equivalent) + - `size` (width:number, height:number) => (no v2 equivalent) + - `bandwidth` (number) => (no v2 equivalent) + +For more information on events, see the Events section of {@link shaka.Player}. + + +#### Browser support testing + +In Shaka v1, you could check if a browser was supported or not using +`shaka.player.Player.isBrowserSupported()`: + +```js +// v1: +if (!shaka.player.Player.isBrowserSupported()) { + // Show an error and abort. +} +``` + +In v2, the same method exists to detect support. For diagnostics there is a new +method that will get more detailed information about the browser. This will +involve making a number of queries to EME which may result in user prompts, +so it is suggested this only be used for diagnostics: + +```js +// v2: +if (!shaka.Player.isBrowserSupported()) { + // Show an error and abort. +} else { + // Only call this method if the browser is supported. + shaka.Player.probeSupport().then(function(support) { + // The check is asynchronous because the EME API is asynchronous. + // The support object contains much more information about what the browser + // offers, if you need it. For example, if you require both Widevine and + // WebM/VP9: + if (!support.drm['com.widevine.alpha'] || + !support.media['video/webm; codecs="vp9"']) { + // Show an error and abort. + } + }); +} +``` + +For more on the support object, check out {@link shakaExtern.SupportType}. +You can also see the full `probeSupport()` report for your browser at: +{@link http://shaka-player-demo.appspot.com/support.html} + + +#### HttpVideoSource + +There is no equivalent to v1's `HttpVideoSource`. Shaka v2 only supports +playback via MSE. This is much simpler and allowed us to remove many special +cases from the code. + + +#### Offline storage + +In v1, to store offline content you used an `OfflineVideoSource`. + +```js +// v1: +function chooseStreams() { + return Promise.resolve(trackIds); +} + +function onProgress(evt) { + console.log('Stored ' + evt.detail + '%'); +} + +var videoSource = new shaka.player.OfflineVideoSource(null, null); +videoSource.addEventListener('progress', onProgress); + +var promise = videoSource.store('https://url', 'en-US', null, chooseStreams); +promise.then(function(groupId) { + console.log('Stored at group ID ' + groupId); +}); +``` + +In v2 you use the `Storage` class to store offline content. + +```js +// v2: +function chooseTracks(allTracks) { + return filteredTracks; +} + +function onProgress(storedContent, percent) { + console.log('Stored ' + percent + '%'); +} + +var storage = new shaka.offline.Storage(player); +// Optional +storage.configure({ + trackSelectionCallback: chooseTracks, + progressCallback: onProgress +}); + +var promise = storage.store('https://url', {extra: 'data'}); +promise.then(function(storedContent) { + console.log('Can be loaded using url: ' + storedContent.offlineUri); +}); +``` + +#### Offline playback + +In v1, you also used `OfflineVideoSource` to load the stored content. + +```js +// v1: +var videoSource = new shaka.player.OfflineVideoSource(groupId, null); +player.load(videoSource); +``` + +In v2, you don't have to use any special types so long as you know the URL to +use. When storing the content, you get a special URL that is simply passed to +`load` like any other URL. You can also get the info by listing all stored +content. + +```js +// v2: +player.load(offlineUri); +``` + +#### Offline listing and deleting + +In v1, you used `OfflineVideoSource` to list and delete content. + +```js +// v1: +var videoSource = new shaka.player.OfflineVideoSource(null, null); + +videoSource.retrieveGroupIds().then(function(groupIds) { + console.log(groupIds[0]); +}); + +var sourceToDelete = new shaka.player.OfflineVideoSource(groupId, null); +sourceToDelete.deleteGroup().then(function() { + console.log('Done'); +}); +``` + +In v2, you use `Storage` to list and delete stored content. + +```js +// v2: +var storage = new shaka.offline.Storage(player); + +storage.list().then(function(storedContents) { + var firstInfo = storedContents[0]; + var url = firstInfo.offlineUri; + player.load(url); +}); + +storage.delete(storedContent).then(function() { + console.log('Done'); +}); +``` diff --git a/docs/tutorials/upgrade-v2-0.md b/docs/tutorials/upgrade-v2-0.md new file mode 100644 index 0000000000..2ad26a9880 --- /dev/null +++ b/docs/tutorials/upgrade-v2-0.md @@ -0,0 +1,291 @@ +# Shaka Upgrade Guide, v2.0 => v2.1 + +This is a detailed guide for upgrading from Shaka Player v2.0 to v2.1. +Feel free to skim or to search for the class and method names you are using in +your application. + + +#### What's New in v2.1? + +Shaka v2.1 introduces several improvements over v2.0, including: + - Basic HLS support + - DASH trick mode support + - Support for jumping gaps in the timeline + - Asynchronous network filters + - Additional stats and events from Player + - Indication of critical errors vs recoverable errors + + +#### Selecting tracks + +Shaka v2.0 had one method for listing tracks (`getTracks()`) and one method for +selecting tracks (`selectTrack()`). Audio, video, and text could all be +independently selected. + +```js +// v2.0: +var allTracks = player.getTracks(); +var videoTracks = allTracks.filter(function(t) { t.type == 'video'; }); +var i = /* choose an index somehow */; +player.selectTrack(videoTracks[i]); +``` + +In Shaka v2.1, audio and video tracks are combined into a variant track. It is +not possible to select individual audio/video streams, you can only select a +specific variant as specified by the manifest. This was necessary for us to +support HLS. Text tracks are independent of variant tracks. + +You can get the currently available tracks using `getVariantTracks()` and +`getTextTracks()`. To switch tracks, use `selectVariantTrack()` and +`selectTextTrack()`. + +```js +// v2.1: +var variantTracks = player.getVariantTracks(); +var i = /* choose an index somehow */; +player.selectVariantTrack(variantTracks[i]); +``` + +The v2.0 methods `getTracks()` and `selectTrack()` are still present in v2.1, +but they are deprecated and will be removed in v2.2. However, they are not +completely backward compatible because of the `type` field. If you are looking +for `'video'` or `'audio'` in the `type` field, your application will need to +be updated to handle `'variant'` instead. + +See also the {@link shakaExtern.Track} structure which is used for all track +types (variant and text). + + +#### Changing languages + +With Shaka v2.0, you could change languages using `configure()` and the +`preferredAudioLanguage` and `preferredTextLanguage` fields. This would affect +both the initial choice of language and the current language during playback. + +```js +// v2.0: +player.configure({ preferredAudioLanguage: 'fr-CA' }); +player.load(manifestUri); // Canadian French preferred for initial playback +player.configure({ preferredAudioLanguage: 'el' }); // switch to Greek +``` + +In Shaka v2.1, language selection during playback is explicit and separate from +the configuration. Configuration only affects the next call to `load()`, and +will not change languages during playback. + +To list available languages, we provide the `getAudioLanguages()` and +`getTextLanguages()` methods. To change languages during playback, use +`selectAudioLanguage()` and `selectTextLanguage()`. + +```js +// v2.1: +player.configure({ preferredAudioLanguage: 'fr-CA' }); +player.load(manifestUri); // Canadian French preferred for initial playback + +player.configure({ preferredAudioLanguage: 'el' }); // Greek, does nothing now +player.selectAudioLanguage('fa'); // switch to Farsi right now + +player.load(secondManifestUri); // Greek preferred for initial playback +``` + + +#### Interpretation of Segmented WebVTT Text + +Segmented WebVTT text is not well-defined by any spec. Consensus in the +community seems to be that timestamps should be relative to the segment start. + +In Shaka v2.0, we offered an option called `useRelativeCueTimestamps`. When +set, WebVTT text timestamps were interpreted as relative to the segment. When +not set, WebVTT text timestamps were intepreted as relative to the period. + +In Shaka v2.1, this option was removed. WebVTT text timestamps are now always +interpreted as relative to the segment start time. + +Non-segmented WebVTT text, MP4-embedded VTT, and TTML are not affected by this +change. + +For more information, see discussions here: + - {@link https://github.com/google/shaka-player/issues/480} + - {@link https://github.com/google/shaka-player/issues/726} + + +#### Plugin interface changes + +If you have taken advantage of Shaka v2's plugin APIs, you may need to update +your plugins to the new interfaces. + +In v2.1, the v2.0 interfaces for text and manifest parsers are still supported, +but are deprecated. Support will be removed in v2.2. + + +#### Text parser plugin changes + +Text parser plugins have a new interface. The old interface was a single +function that took many parameters and handled both initialization segments and +media segments. Initialization segments were indicated by null segment times. + +```js +// v2.0 +/** + * @param {ArrayBuffer} data + * @param {number} periodOffset + * @param {?number} segmentStartTime + * @param {?number} segmentEndTime + * @param {boolean} useRelativeCueTimestamps Only used by the VTT parser + * @return {!Array.} + */ +function MyTextParser(data, periodOffset, segmentStartTime, segmentEndTime) { + if (segmentStartTime == null) { + checkInitSegmentOrThrow(data); + return []; + } + + var cues = []; + var parserState = new MyInternalParser(data); + while (parserState.more()) { + cues.push(parserState.nextCueOrThrow(periodOffset)); + } + return cues; +} +``` + +In Shaka v2.1, the text parser interface is now a constructor. The interface +now has explicit methods for init segments and media segments, and parameters +related to time offsets have been grouped together into one `TimeContext` +parameter. + +```js +// v2.1 +/** @constructor */ +function MyTextParser() {} + +/** @param {!ArrayBuffer} data */ +MyTextParser.prototype.parseInit = function(data) { + checkInitSegmentOrThrow(data); +}; + +/** + * @param {!ArrayBuffer} data + * @param {shakaExtern.TextParser.TimeContext} timeContext + * @return {!Array.} + */ +MyTextParser.prototype.parseMedia = function(data, timeContext) { + var cues = []; + var parserState = new MyInternalParser(data); + while (parserState.more()) { + cues.push(parserState.nextCueOrThrow(timeContext.periodStart)); + } + return cues; +}; +``` + +For more information, see the {@link shakaExtern.TextParser.TimeContext} and +{@link shakaExtern.TextParser} definitions in the API docs. + + +#### Manifest parser plugin changes + +Manifest parsers also have a new interface. The old interface had a `start()` +method that took many parameters. + +```js +// v2.0 +/** @constructor */ +function MyManifestParser() {} + +/** @param {shakaExtern.ManifestConfiguration} config */ +MyManifestParser.configure = function(config) { + this.config_ = config; +}; + +/** + * @param {string} uri + * @param {!shaka.net.NetworkingEngine} networkingEngine + * @param {function(shakaExtern.Period)} filterPeriod + * @param {function(!shaka.util.Error)} onError + * @param {function(!Event)} onEvent + * @return {!Promise.} +*/ +MyManifestParser.prototype.start = + function(networkingEngine, filterPeriod, onError, onEvent) { + this.networkingEngine_ = networkingEngine; + this.filterPeriod_ = filterPeriod; + this.onError_ = onError; + this.onEvent_ = onEvent; + + var type = shaka.net.NetworkingEngine.RequestType.MANIFEST; + var request = shaka.net.NetworkingEngine.makeRequest( + [uri], this.config_.retryParameters); + return this.networkingEngine_.request(type, request).then(function(response) { + this.manifest_ = this.parseInternal_(response.data); + this.updateInterval_ = setInterval(this.updateManifest_.bind(this), 5000); + return this.manifest_; + }); +}; + +/** @return {!Promise} */ +MyManifestParser.prototype.stop = function() { + clearInterval(this.updateInterval_); + return Promise.resolve(); +}; +``` + +In Shaka v2.1, the parameters to `start()`, which were all tied back to the +`Player` object, have been grouped into a one `PlayerInterface` parameter. +This will allow us to add features to the interface without breaking plugins. + +```js +// v2.1 +/** + * @param {string} uri The URI of the manifest. + * @param {shakaExtern.ManifestParser.PlayerInterface} playerInterface Contains + * the interface to the Player. + * @return {!Promise.} + */ +MyManifestParser.prototype.start = function(uri, playerInterface) { + this.playerInterface_ = playerInterface; + + var type = shaka.net.NetworkingEngine.RequestType.MANIFEST; + var request = shaka.net.NetworkingEngine.makeRequest( + [uri], this.config_.retryParameters); + return this.playerInterface_.networkingEngine.request(type, request).then( + function(response) { + this.manifest_ = this.parseInternal_(response.data); + this.updateInterval_ = setInterval(this.updateManifest_.bind(this), 5000); + return this.manifest_; + }); +}; +``` + +Shaka v2.1 also adds two new methods to the manifest parser interface: +`update()` and `onExpirationUpdated()`. + +The `update()` method allows `StreamingEngine` to ask for an explicit manifest +update. This is used, for example, to support `emsg` boxes in MP4 content, +which can be used by the stream to indicate that a manifest update is needed. + +```js +// v2.1 +MyManifestParser.prototype.update = function() { + // Trigger an update now! + this.updateManifest_(); +}; +``` + +The `onExpirationUpdated` method is optional. It is used by `DrmEngine` to +inform the manifest parser that the expiration time of an EME session has +changed. We use this internally in our offline support, so that we can keep +track of expiring licenses for stored content. + +```js +// v2.1 +MyManifestParser.prototype.onExpirationUpdated = + function(sessionId, expiration) { + var oldExpiration = this.database_.getExpiration(this.contentId_); + expiration = Math.min(expiration, oldExpiration); + this.database_.setExpiration(this.contentId_, expiration); +}; +``` + +For more information, see the {@link shakaExtern.ManifestParser.PlayerInterface} +and {@link shakaExtern.ManifestParser} definitions in the API docs. diff --git a/docs/tutorials/upgrade.md b/docs/tutorials/upgrade.md index 30b0423567..f14d950b96 100644 --- a/docs/tutorials/upgrade.md +++ b/docs/tutorials/upgrade.md @@ -1,727 +1,5 @@ -# Shaka v2 Upgrade Guide +# Shaka Player Upgrade Guide -This is a detailed guide for upgrading from Shaka Player v1 to v2. It is a bit -long to read from beginning to end, so feel free to skim or to search for the -class and method names you are using in your application. +If you are upgrading from v1.x, please see {@tutorial upgrade-v1}. - -#### What's New in v2? - -Shaka v2 has several improvements over v1, including: - - Support for multiple DASH Periods - - Support for DASH Location elements - - Support for DASH UTCTiming elements for clock synchronization - - Lower-latency startup - - Simplified API - - Better browser compatibility - - More detailed browser support test - - Numerical error code system - - Clears old data from the buffer to conserve memory - - Buffering state is independent of play/pause - - Distinguishes between subtitle and caption tracks - - Separate audio & text language preferences - - New plugin and build system to extend Shaka - - Cache-friendly networking - - Simpler, mobile-friendly demo app - - -#### Shaka Plugins - -Shaka v2 has a new, cleaner architecture than v1 based on plugins. In v2, -networking, manifest parsing, and subtitle/caption parsing are all plugins. - -We bundle some default plugins (HTTP support, DASH support, and WebVTT), and -we plan to expand this list in future releases. Application developers can -write their own plugins as well. Plugins can either be compiled into the -library, or they can live outside the library in the application. Application -developers can also customize the build to exclude any default plugins they -don't need. - -For a more in-depth discussion of plugins, check out {@tutorial plugins}. - - -#### Namespace - -In v1, the `Player` class was namespaced as `shaka.player.Player`. In v2, this -has been simplified to `shaka.Player`. - - -#### load() - -Before, you needed a `DashVideoSource` or other `IVideoSource` subclass to pass -to `player.load()`. The video source was constructed with a manifest URL: - -```js -// v1: -var player = new shaka.player.Player(video); -var videoSource = new shaka.player.DashVideoSource(manifestUri); -player.load(videoSource); -``` - -In v2, the entire video source concept is gone from the API. Now, you pass the -URL directly to the player, and it decides which manifest parser plugin to use -based on the file extension or MIME type: - -```js -// v2: -var player = new shaka.Player(video); -player.load(manifestUri); -``` - - -#### ContentProtection callbacks - -Shaka v1's `DashVideoSource` had a parameter for a `ContentProtection` callback. -This callback was required to play protected content because Shaka did not -interpret `ContentProtection` elements in the DASH manifest and could not -derive the license server URI automatically: - -```js -// v1: -function interpretContentProtection(schemeIdUri, contentProtectionElement) { - if (schemeIdUri.toLowerCase() == - 'urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed') { - // This is the UUID which represents Widevine. - return [{ - 'keySystem': 'com.widevine.alpha', - 'licenseServerUrl': '//widevine-proxy.appspot.com/proxy' - }]; - } else if (schemeIdUri.toLowerCase() == - 'urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95') { - // This is the UUID which represents PlayReady. - return [{ - 'keySystem': 'com.microsoft.playready', - 'licenseServerUrl': '//playready.directtaps.net/pr/svc/rightsmanager.asmx' - }]; - } else { - return null; - } -} -var player = new shaka.player.Player(video); -var videoSource = new shaka.player.DashVideoSource( - manifestUri, interpretContentProtection); -player.load(videoSource); -``` - -In v2, these callbacks are *only* required for *non-standard* ContentProtection -schemes, such as that used by YouTube's demo assets. For the 99% of you who are -using standard schemes, no callback is required. Simply `configure()` the -player with your license servers: - -```js -// v2: -var player = new shaka.Player(video); -player.configure({ - drm: { - servers: { - 'com.widevine.alpha': '//widevine-proxy.appspot.com/proxy' - 'com.microsoft.playready': '//playready.directtaps.net/pr/svc/rightsmanager.asmx' - } - } -}); -player.load(manifestUri); -``` - -For a more in-depth discussion of DRM configuration, see {@tutorial drm-config}. - -If you need to support a custom ContentProtection scheme, you can still do so -with a callback set through `player.configure()`: - -```js -// v2: -function interpretContentProtection(contentProtectionElement) { - if (contentProtectionElement.getAttribute('schemeIdUri') == - 'http://youtube.com/drm/2012/10/10') { - var configs = []; - for (....) { - configs.push({ - 'keySystem': keySystem, - // WATCH OUT: now called URI not URL - 'licenseServerUri': licenseServerUri - }); - } - return configs; - } -} - -var player = new shaka.Player(video); -player.configure({ - manifest: { - dash: { - customScheme: interpretContentProtection - } - } -}); -player.load(manifestUri); -``` - -For more on what you can specify for a custom scheme, see the docs for -{@link shakaExtern.DrmInfo}. - - -#### Detailed DrmInfo - -Shaka v1's ContentProtection callbacks could return a detailed DrmInfo object -with lots of EME-related and license-request-related settings: - -```js -// v1: -function interpretContentProtection(schemeIdUri, contentProtectionElement) { - if (schemeIdUri.toLowerCase() == - 'urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed') { - return [{ - 'keySystem': 'com.widevine.alpha', - 'licenseServerUrl': '//widevine-proxy.appspot.com/proxy', - - 'distinctiveIdentifierRequired': true, - 'persistentStateRequired': false, - 'serverCertificate': certificateUint8Array, - 'audioRobustness': 'HW_SECURE_ALL', - 'videoRobustness': 'HW_SECURE_ALL', - 'initData': { 'initDataType': 'cenc', 'initData': initDataUint8Array }, - - 'licensePreProcessor': licensePreProcessor, - 'licensePostProcessor': licensePostProcessor, - 'withCredentials': true - }]; - } else { - return null; - } -} -function licensePreProcessor(requestInfo) { - // Only called for license requests. - - // Wrap the body, which is an ArrayBuffer: - var newBody = wrapLicenseRequest(requestInfo.body); - requestInfo.body = newBody; - - // Add a header: - requestInfo.headers['foo'] = 'bar'; -} -function licensePostProcessor(license) { - // Only called for license responses. - // Unwrap the license, which is a Uint8Array, use/store the extra data: - var rawLicense = unwrapLicense(license); - // Now return the raw license, which is also a Uint8Array: - return rawLicense; -} -``` - -In v2, the EME settings have moved to the `drm.advanced` field of the config -object: - -```js -// v2: -player.configure({ - drm: { - advanced: { - 'com.widevine.alpha': { - distinctiveIdentifierRequired: true, - persistentStateRequired: true, - serverCertificate: certificateUint8Array, - audioRobustness: 'HW_SECURE_ALL', - videoRobustness: 'HW_SECURE_ALL', - // NOTE: initData is now an array of one or more overrides: - initData: [{ initDataType: 'cenc', initData: initDataUint8Array }] - } - } - } -}); -``` - -For a discussion of advanced DRM configuration, see {@tutorial drm-config}. - -Shaka v1's license-request-releated settings have moved to v2's network filters. - -Network filters are a generic filtering system for all networking, including -license requests and responses. They are more general and flexible, so they -take slightly more effort than the old preprocessor/postprocessor system. -However, in v2, you only need to filter your license traffic for two reasons: - - if you use cross-site credentials (v1's "withCredentials" flag) - - if you wrap/unwrap license requests and responses into some other format - -```js -// v2: -player.getNetworkingEngine().registerRequestFilter(licensePreProcessor); -player.getNetworkingEngine().registerResponseFilter(licensePostProcessor); - -function licensePreProcessor(type, request) { - // A generic filter for all requests, so filter on type LICENSE: - if (type != shaka.net.NetworkingEngine.RequestType.LICENSE) return; - - // Equivalent to v1's 'withCredentials': true - request.allowCrossSiteCredentials = true; - - // Wrap the request data, which is an ArrayBuffer: - var newData = wrapLicenseRequest(request.data); - request.data = newData; - - // Add a header: - request.headers['foo'] = 'bar'; -} -function licensePostProcessor(type, response) { - // A generic filter for all responses, so filter on type LICENSE: - if (type != shaka.net.NetworkingEngine.RequestType.LICENSE) return; - - // Unwrap the response, which is an ArrayBuffer, use/store the extra data: - var rawLicense = unwrapLicense(response.data); - // Instead of returning the raw license, store it back to the response: - response.data = rawLicense; - // Return nothing. -} -``` - -For more on request filters, see the docs for -{@link shaka.net.NetworkingEngine.RequestFilter}, {@link shakaExtern.Request}, -{@link shaka.net.NetworkingEngine.ResponseFilter}, {@link shakaExtern.Response}. - - -#### ClearKey configuration - -Shaka v1's `ContentProtection` callbacks could be used for ClearKey, but it -required you to craft both a data URI and fake init data in the correct format: - -```js -// v1: -function interpretContentProtection(schemeIdUri, contentProtectionElement) { - var keyid; // as Uint8Array - var key; // as Uint8Array - var keyObj = { - kty: 'oct', - kid: Uint8ArrayUtils.toBase64(keyid, false), - k: Uint8ArrayUtils.toBase64(key, false) - }; - var jwkSet = {keys: [keyObj]}; - var license = JSON.stringify(jwkSet); - var initData = { - 'initData': keyid, - 'initDataType': 'webm' - }; - var licenseServerUrl = 'data:application/json;base64,' + - window.btoa(license); - return [{ - 'keySystem': 'org.w3.clearkey', - 'licenseServerUrl': licenseServerUrl, - 'initData': initData - }]; -} -``` - -In v2, this has been simplified through `player.configure()`: - -```js -// v2: -player.configure({ - drm: { - clearKeys: { - 'deadbeefdeadbeefdeadbeefdeadbeef': '18675309186753091867530918675309', - '02030507011013017019023029031037': '03050701302303204201080425098033' - } - } -}); -``` - -For more on ClearKey setup, see {@tutorial drm-config}. - - -#### BandwidthEstimator and AbrManager - -Shaka v1's `DashVideoSource` had parameters for applications to inject custom -BandwidthEstimator and AbrManager implementations. We even recommended -injecting BandwithEstimator to get persisted estimates across playbacks: - -```js -// v1: -var player = new shaka.player.Player(video); -var bandwidthEstimator = new shaka.util.EWMABandwidthEstimator(); -var abrManager = new shaka.media.SimpleAbrManager(); -var videoSource = new shaka.player.DashVideoSource( - manifestUri, /* interpretContentProtection */ null, estimator, abrManager); -player.load(videoSource); -``` - -In v2, we rolled the BandwidthEstimator concept into AbrManager. It is no -longer necessary to inject an instance to persist estimates across playbacks, -and custom AbrManagers are now provided via `player.configure()`: - -```js -// v2: -var player = new shaka.Player(video); -var customAbrManager = new MyCustomAbrManager(); -player.configure({ - abr: { - manager: customAbrManager - } -}); -player.load(manifestUri); -``` - -For more on the AbrManager interface, see the docs for -{@link shakaExtern.AbrManager}. - - -#### Selecting tracks - -Shaka v1 had separate methods for each type of tracks: `getVideoTracks()`, -`getAudioTracks()`, and `getTextTracks()`, as well as `selectVideoTrack()`, -`selectAudioTrack()`, and `selectTextTrack()`. Tracks were selected by ID. - -```js -// v1: -var videoTracks = player.getVideoTracks(); -var i = /* choose an index somehow */; -player.selectVideoTrack(videoTracks[i].id); // id, specifically video -``` - -In Shaka v2, audio and video tracks are combined into a variant track. It is -not possible to select individual audio/video streams, you can only select a -specific variant as specified by the manifest. - -You can get the currently available tracks using `getVariantTracks()` and -`getTextTracks()`. To switch tracks, use `selectVariantTrack()` and -`selectTextTrack()`, passing in the whole track object. - -```js -// v2: -var variantTracks = player.getVariantTracks(); -var i = /* choose an index somehow */; -player.selectVariantTrack(variantTracks[i]); // whole track -``` - -In v1, you could show or hide text tracks with `player.enableTextTrack()`: - -```js -// v1: -player.enableTextTrack(true); -``` - -In v2, this becomes `player.setTextTrackVisibility()`: - -```js -// v2: -player.setTextTrackVisibility(true); -``` - -See also the {@link shakaExtern.Track} structure which is used for all track -types (variant and text). - - -#### Side-loading captions/subtitles - -In Shaka v1, you could side-load subtitles that were not present in the manifest -by calling `addExternalCaptions()` on the `DashVideoSource` before `load()`: - -```js -// v1: -var player = new shaka.player.Player(video); -var videoSource = new shaka.player.DashVideoSource(manifestUri); -videoSource.addExternalCaptions(textStreamUri, 'fr-CA', 'text/vtt'); -player.load(videoSource); -``` - -In v2, this is done on with `player.addTextTrack()` after load() is complete: - -```js -// v2: -var player = new shaka.Player(video); -player.load(manifestUri).then(function() { - player.addTextTrack(textStreamUri, 'fr-CA', 'caption', 'text/vtt'); -}); -``` - - -#### Playback start time - -Shaka v1's `player.setPlaybackStartTime()` would let you start playback at an -arbitrary timestamp. It had to be called before load(): - -```js -// v1: -player.setPlaybackStartTime(123.45); -player.load(manifestUri); -``` - -In v2, this is done through an optional parameter on `load()`: - -```js -// v2: -player.load(manifestUri, 123.45); -``` - - -#### Trick play - -Shaka v1 had `player.setPlaybackRate()` that could be used for trick play by -emulating negative rate support in `video.playbackRate`. If you used -v1's `setPlaybackRate()` for trick play, use v2's `player.trickPlay()`. For -other purposes, use `video.playbackRate` directly. - - -#### configure() - -Shaka v1 and v2 both have a `player.configure()` method. Here is a map of -settings in v1 and their equivalents in v2 (most of which are at a different -level of the configuration hierarchy): - - - `enableAdaptation` => `abr.enabled` - - `streamBufferSize` => `streaming.bufferingGoal` - - `licenseRequestTimeout` => `drm.retryParameters.timeout` - - `mpdRequestTimeout` => `manifest.retryParameters.timeout` - - `segmentRequestTimeout` => `streaming.retryParameters.timeout` - - `preferredLanguage` => split into `preferredAudioLanguage` and - `preferredTextLanguage` - - `restrictions` => (same name, see below) - - `liveStreamEndTimeout` => (not needed in v2) - - `disableCacheBustingEvenThoughItMayAffectBandwidthEstimation` => - (not needed, always cache-friendly) - -The `shaka.player.Restriction` type was replaced by a simple record type. So -instead of constructing an object, simply create an anonymous JavaScript object. -`minPixels`/`maxPixels` were added to limit total pixels. Also `minBandwidth` -and `maxBandwidth` were split into `minAudioBandwidth`, `maxAudioBandwidth`, -`minVideoBandwidth`, and `maxVideoBandwidth`, see -{@link shakaExtern.Restrictions}. - -For more information on configuration in v2, see {@tutorial config}, -{@tutorial network-and-buffering-config}, and {@tutorial drm-config}. - - -#### getStats() - -Shaka v1 had `player.getStats()`. Shaka v2 has a similar method, but it returns -a somewhat different structure. - -```js -// v1: -player.getStats() - -=> Object - streamStats: StreamStats // refers to currently selected video stream - videoWidth: number // pixels - videoHeight: number // pixels - videoMimeType: string - videoBandwidth: number // bits/sec - decodedFrames: number - droppedFrames: number - estimatedBandwidth: number // bits/sec - playTime: number // seconds - bufferingTime: number // seconds - playbackLatency: number // seconds - bufferingHistory: Array // of timestamps when we started buffering - bandwidthHistory: Array of Objects - timestamp: number // seconds, when bandwidth estimate was made - value: number // bandwidth estimate, bits/sec - streamHistory: Array of Objects - timestamp: number // seconds, when video stream changed - value: StreamStats // information about the selected stream, video only -``` - -Shaka v2 does not expose playback latency or a history of bandwidth estimates. -v2's `switchHistory` is more general than v1's `streamHistory`, and covers all -stream types: - -```js -// v2: -player.getStats() - -=> Object - width: number // pixels, current video track - height: number // pixels, current video track - streamBandwidth: number // bits/sec, total for all current streams - decodedFrames: number // same as v1 - droppedFrames: number // same as v1 - estimatedBandwidth: number // bits/sec, same as v1 - loadLatency: number, // seconds between load() and the video's 'loadend' event - playTime: number // seconds, same as v1 - bufferingTime: number // seconds, same as v1 - switchHistory: Array of Objects // replaces v1's streamHistory - timestamp: number // seconds, when the stream was selected - id: number // stream ID - type: string // 'variant' or 'text' - fromAdaptation: boolean // distinguishes between ABR and manual choices - stateChange: Array of Objects - timestamp: number // seconds, when the state changed - state: string // 'buffering', 'playing', 'paused', or 'ended' - duration: number // seconds in this state -``` - -For more on stats in v2, see {@link shakaExtern.Stats}. - - -#### Player events - -In both Shaka v1 and v2, `Player` emits events. Here is a map of events and -their properties in v1 and their equivalents in v2: - - - `error` => `error` - - `detail` (various types) => `detail` ({@link shaka.util.Error}) - - `type` (vague string) => `category` - (number, unambiguous {@link shaka.util.Error.Category error category}) - - `message` (vague string) => `code` - (number, unambiguous {@link shaka.util.Error.Code error code}) - - `bufferingStart` and `bufferingEnd` => combined into `buffering` - - (no v1 equivalent) => `buffering` - (boolean, true when we enter buffering state, false when we leave) - - (no v1 equivalent) => - `{@link shaka.Player.TextTrackVisibilityEvent texttrackvisibility}` - - `trackschanged` => `trackschanged` - - `seekrangechanged` => - (no v2 equivalent, use `{@link player.seekRange}()` when updating the UI) - - `adaptation` => `adaptation` - - `contentType` (string) => (no v2 equivalent) - - `size` (width:number, height:number) => (no v2 equivalent) - - `bandwidth` (number) => (no v2 equivalent) - -For more information on events, see the Events section of {@link shaka.Player}. - - -#### Browser support testing - -In Shaka v1, you could check if a browser was supported or not using -`shaka.player.Player.isBrowserSupported()`: - -```js -// v1: -if (!shaka.player.Player.isBrowserSupported()) { - // Show an error and abort. -} -``` - -In v2, the same method exists to detect support. For diagnostics there is a new -method that will get more detailed information about the browser. This will -involve making a number of queries to EME which may result in user prompts, -so it is suggested this only be used for diagnostics: - -```js -// v2: -if (!shaka.Player.isBrowserSupported()) { - // Show an error and abort. -} else { - // Only call this method if the browser is supported. - shaka.Player.probeSupport().then(function(support) { - // The check is asynchronous because the EME API is asynchronous. - // The support object contains much more information about what the browser - // offers, if you need it. For example, if you require both Widevine and - // WebM/VP9: - if (!support.drm['com.widevine.alpha'] || - !support.media['video/webm; codecs="vp9"']) { - // Show an error and abort. - } - }); -} -``` - -For more on the support object, check out {@link shakaExtern.SupportType}. -You can also see the full `probeSupport()` report for your browser at: -{@link http://shaka-player-demo.appspot.com/support.html} - - -#### HttpVideoSource - -There is no equivalent to v1's `HttpVideoSource`. Shaka v2 only supports -playback via MSE. This is much simpler and allowed us to remove many special -cases from the code. - - -#### Offline storage - -In v1, to store offline content you used an `OfflineVideoSource`. - -```js -// v1: -function chooseStreams() { - return Promise.resolve(trackIds); -} - -function onProgress(evt) { - console.log('Stored ' + evt.detail + '%'); -} - -var videoSource = new shaka.player.OfflineVideoSource(null, null); -videoSource.addEventListener('progress', onProgress); - -var promise = videoSource.store('https://url', 'en-US', null, chooseStreams); -promise.then(function(groupId) { - console.log('Stored at group ID ' + groupId); -}); -``` - -In v2 you use the `Storage` class to store offline content. - -```js -// v2: -function chooseTracks(allTracks) { - return filteredTracks; -} - -function onProgress(storedContent, percent) { - console.log('Stored ' + percent + '%'); -} - -var storage = new shaka.offline.Storage(player); -// Optional -storage.configure({ - trackSelectionCallback: chooseTracks, - progressCallback: onProgress -}); - -var promise = storage.store('https://url', {extra: 'data'}); -promise.then(function(storedContent) { - console.log('Can be loaded using url: ' + storedContent.offlineUri); -}); -``` - -#### Offline playback - -In v1, you also used `OfflineVideoSource` to load the stored content. - -```js -// v1: -var videoSource = new shaka.player.OfflineVideoSource(groupId, null); -player.load(videoSource); -``` - -In v2, you don't have to use any special types so long as you know the URL to -use. When storing the content, you get a special URL that is simply passed to -`load` like any other URL. You can also get the info by listing all stored -content. - -```js -// v2: -player.load(offlineUri); -``` - -#### Offline listing and deleting - -In v1, you used `OfflineVideoSource` to list and delete content. - -```js -// v1: -var videoSource = new shaka.player.OfflineVideoSource(null, null); - -videoSource.retrieveGroupIds().then(function(groupIds) { - console.log(groupIds[0]); -}); - -var sourceToDelete = new shaka.player.OfflineVideoSource(groupId, null); -sourceToDelete.deleteGroup().then(function() { - console.log('Done'); -}); -``` - -In v2, you use `Storage` to list and delete stored content. - -```js -// v2: -var storage = new shaka.offline.Storage(player); - -storage.list().then(function(storedContents) { - var firstInfo = storedContents[0]; - var url = firstInfo.offlineUri; - player.load(url); -}); - -storage.delete(storedContent).then(function() { - console.log('Done'); -}); -``` +If you are upgrading from v2.0, please see {@tutorial upgrade-v2-0}. diff --git a/externs/shaka/text.js b/externs/shaka/text.js index 6b197ff3e7..c88fe2ac46 100644 --- a/externs/shaka/text.js +++ b/externs/shaka/text.js @@ -33,9 +33,9 @@ shakaExtern.TextParser = function() {}; * A collection of time offsets used to adjust text cue times. * * @typedef {{ - * periodStart : number, - * segmentStart : number, - * segmentEnd : number + * periodStart : number, + * segmentStart : number, + * segmentEnd : number * }} * * @property {number} periodStart @@ -59,8 +59,7 @@ shakaExtern.TextParser.TimeContext; * * @exportDoc */ - -shakaExtern.TextParser.prototype.parseInit; +shakaExtern.TextParser.prototype.parseInit = function(data) {}; /** @@ -75,7 +74,7 @@ shakaExtern.TextParser.prototype.parseInit; * * @exportDoc */ -shakaExtern.TextParser.prototype.parseMedia; +shakaExtern.TextParser.prototype.parseMedia = function(data, timeContext) {}; /** diff --git a/third_party/jsdoc/templates/default/tmpl/tutorial.tmpl b/third_party/jsdoc/templates/default/tmpl/tutorial.tmpl index 9dfe909977..382ea0fb44 100644 --- a/third_party/jsdoc/templates/default/tmpl/tutorial.tmpl +++ b/third_party/jsdoc/templates/default/tmpl/tutorial.tmpl @@ -1,15 +1,5 @@
- 0) { ?> -
-
    -
  • -
-
- -