mirror of
https://github.com/shaka-project/shaka-player.git
synced 2026-06-24 17:35:10 +03:00
37753aaa9a
We moved TextEngine to a new namespace between v2.1 and v2.2, but forgot to update the relevant docs. Closes #1046 Change-Id: Ifddbe03931b2e76cc4778aaa4c3c70695a2dce62
489 lines
15 KiB
Markdown
489 lines
15 KiB
Markdown
# Shaka Upgrade Guide, v2.0 => v2.2
|
|
|
|
This is a detailed guide for upgrading from Shaka Player v2.0 to v2.2.
|
|
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 and v2.2?
|
|
|
|
Shaka v2.2 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
|
|
- Allowing applications to render their own text tracks
|
|
- Allowing applications to define their own retry logic after streaming
|
|
failures
|
|
- Making the default ABR manager more configurable
|
|
- Adding channel count and bandwidth info to variant tracks
|
|
- Xlink support in DASH
|
|
- Stricter runtime type-checking of EME cert configuration
|
|
- New option for offline protected content without persistent licensing
|
|
|
|
|
|
#### 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.2, 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.2:
|
|
var variantTracks = player.getVariantTracks();
|
|
var i = /* choose an index somehow */;
|
|
player.selectVariantTrack(variantTracks[i]);
|
|
```
|
|
|
|
See also the {@link shakaExtern.Track} structure which is used for all track
|
|
types (variant and text).
|
|
|
|
|
|
#### Setting and configuring ABR manager
|
|
|
|
In Shaka v2.0, a custom ABR manager could be set through:
|
|
|
|
```js
|
|
player.configure({
|
|
abr.manager: customAbrManager
|
|
});
|
|
```
|
|
|
|
In v2.2, it's done through:
|
|
|
|
```js
|
|
player.configure({
|
|
abrFactory: customAbrManager
|
|
});
|
|
```
|
|
|
|
The API for AbrManager has also changed.
|
|
|
|
In v2.0, default bandwidth estimate and restrictions were set through
|
|
`setDefaultEstimate()` and `setRestrictions()` methods.
|
|
|
|
In v2.2, they are set through `configure()` method which accepts a
|
|
{@link shakaExtern.AbrConfiguration} structure. The new method is more general,
|
|
and allows for the configuration of bandwidth upgrade and downgrade targets
|
|
as well.
|
|
|
|
```js
|
|
// v2.0:
|
|
abrManager.setDefaultEstimate(defaultBandwidthEstimate);
|
|
abrManager.setRestrictions(restrictions);
|
|
|
|
// v2.2:
|
|
abrManager.configure(abrConfigurations);
|
|
```
|
|
|
|
In v2.0, AbrManager had a `chooseStreams()` method for the player to prompt for
|
|
a stream selection, and a `switch()` callback to send unsolicited changes from
|
|
AbrManager to player. In v2.2, `chooseStreams()` has been replaced with
|
|
`chooseVariant()`, and the `switch()` callback takes a variant instead of a map
|
|
of streams.
|
|
|
|
```js
|
|
// v2.0:
|
|
var map = abrManager.chooseStreams(['audio', 'video']);
|
|
console.log(map['video'], map['audio']);
|
|
|
|
MyAbrManager.prototype.makeDecision_ = function() {
|
|
var video = this.computeBestVideo_(this.bandwidth_);
|
|
var audio = this.computeBestAudio_(this.bandwidth_);
|
|
var map = {
|
|
'audio': audio,
|
|
'video': video
|
|
};
|
|
this.switch_(map);
|
|
};
|
|
|
|
// v2.2:
|
|
var variant = abrManager.chooseVariant();
|
|
console.log(variant, variant.video, variant.audio);
|
|
|
|
MyAbrManager.prototype.makeDecision_ = function() {
|
|
var variant = this.computeBestVariant_(this.bandwidth_);
|
|
this.switch_(variant);
|
|
};
|
|
```
|
|
|
|
In v2.2, the v2.0 interfaces are still supported, but are deprecated. Support
|
|
will be removed in v2.3.
|
|
|
|
|
|
#### Selecting tracks and adaptation settings
|
|
|
|
In v2.0, selecting a new video or audio track would implicitly disable
|
|
adaptation.
|
|
|
|
```js
|
|
// v2.0
|
|
player.selectTrack(videoTracks[i]);
|
|
// Adaptation has been implicitly disabled.
|
|
// To explicitly re-enable:
|
|
player.configure({abr: {enabled: true}});
|
|
```
|
|
|
|
In v2.2, any change in ABR state must be made explicitly if desired.
|
|
|
|
```js
|
|
// v2.2
|
|
// To explicitly disable:
|
|
player.configure({abr: {enabled: false}});
|
|
// Now select the track, which does not change adaptation state!
|
|
player.selectVariantTrack(variantTracks[i]);
|
|
```
|
|
|
|
|
|
#### 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.2, 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.2:
|
|
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.
|
|
|
|
The v2.0 interfaces for text and manifest are no longer supported.
|
|
|
|
|
|
#### New "text" namespace
|
|
|
|
In Shaka v2.1, `TextEngine` was part of the `shaka.media` namespace. In v2.2,
|
|
this was moved to the new `shaka.text` namespace. Text-parsing plugins should
|
|
now be registered with {@link shaka.text.TextEngine.registerParser}.
|
|
|
|
|
|
#### 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.<!TextTrackCue>}
|
|
*/
|
|
function MyTextParser(data, periodOffset, segmentStartTime, segmentEndTime) {
|
|
if (segmentStartTime == null) {
|
|
checkInitSegmentOrThrow(data);
|
|
return [];
|
|
}
|
|
|
|
var cues = [];
|
|
var parserState = new MyInternalParser(data);
|
|
while (parserState.more()) {
|
|
cues.push(new VTTCue(...));
|
|
}
|
|
return cues;
|
|
}
|
|
```
|
|
|
|
In Shaka v2.2, 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.
|
|
Also text parser plugins now return shaka.text.Cue objects instead of VTTCue or
|
|
TextTrackCue objects.
|
|
|
|
```js
|
|
// v2.2
|
|
/** @constructor */
|
|
function MyTextParser() {}
|
|
|
|
/** @param {!ArrayBuffer} data */
|
|
MyTextParser.prototype.parseInit = function(data) {
|
|
checkInitSegmentOrThrow(data);
|
|
};
|
|
|
|
|
|
/**
|
|
* @param {!ArrayBuffer} data
|
|
* @param {shakaExtern.TextParser.TimeContext} timeContext
|
|
* @return {!Array.<!shaka.text.Cue>}
|
|
*/
|
|
MyTextParser.prototype.parseMedia = function(data, timeContext) {
|
|
var cues = [];
|
|
var parserState = new MyInternalParser(data);
|
|
while (parserState.more()) {
|
|
cues.push(new shaka.text.Cue(...));
|
|
}
|
|
return cues;
|
|
};
|
|
```
|
|
|
|
All application-specific text-parsing plugins MUST to be updated,
|
|
v2.2 does not have backward compatibility on this!
|
|
The `Shaka.text.Cue` class contains the same information about a text cue as
|
|
the VTTCue class, plus extra information about text style.
|
|
|
|
For more information, see the {@link shakaExtern.TextParser.TimeContext},
|
|
{@link shaka.text.Cue} and {@link shakaExtern.TextParser} definitions in
|
|
the API docs.
|
|
|
|
|
|
#### Customizing subtitle display
|
|
|
|
Shaka v2 gave applications an opportunity to have a custom text parser, but
|
|
all the displaying was handled by the browser. Shaka v2.2 adds the
|
|
possibility to have custom logic for displaying text. By default the
|
|
rendering will still be done by the {@linksource shaka.text.SimpleTextDisplayer}
|
|
class.
|
|
|
|
A custom text display factory can be specified by calling player.configure().
|
|
|
|
```js
|
|
player.configure({
|
|
textDisplayFactory: customTextDisplayerClass
|
|
});
|
|
```
|
|
|
|
See {@linksource shakaExtern.TextDisplayer} for details.
|
|
|
|
|
|
#### 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.<shakaExtern.Manifest>}
|
|
*/
|
|
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.<shakaExtern.Manifest>}
|
|
*/
|
|
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.2 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.2
|
|
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.2
|
|
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.
|
|
|
|
|
|
#### Retry after streaming failure
|
|
|
|
In v2.0, after a network error and all network retries were exhausted, streaming
|
|
would continue to retry those requests. The only way to stop this process was
|
|
to `unload()` or `destroy()` the Player.
|
|
|
|
In v2.2, we introduced new retry behavior. The default is as it was in v2.1.3
|
|
(retry on live, but not VOD), but applications can now customize the behavior
|
|
through a callback:
|
|
|
|
```js
|
|
player.configure({
|
|
streaming: {
|
|
failureCallback: function(error) {
|
|
// Always retry, as in v2.0.0 - v2.1.2:
|
|
player.retryStreaming();
|
|
}
|
|
}
|
|
});
|
|
```
|
|
|
|
The new `player.retryStreaming()` method can be used to retry after a failure.
|
|
You can base the decision on `player.isLive()`, `error.code`, or anything else.
|
|
Because you can call `retryStreaming()` at any time, you can also delay the
|
|
decision until you get feedback from the user, the browser is back online, etc.
|
|
|
|
A few more examples of possible failure callbacks:
|
|
|
|
```js
|
|
function neverRetryCallback(error) {}
|
|
|
|
function retryLiveOnFailureCallback(error) {
|
|
if (player.isLive()) {
|
|
player.retryStreaming();
|
|
}
|
|
}
|
|
|
|
function retryOnSpecificHttpErrorsCallback(error) {
|
|
if (error.code == shaka.util.Error.Code.BAD_HTTP_STATUS) {
|
|
var statusCode = error.data[1];
|
|
var retryCodes = [ 502, 503, 504, 520 ];
|
|
if (retryCodes.indexOf(statusCode >= 0)) {
|
|
player.retryStreaming();
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
If you choose to react to `error` events instead of the failure callback, you
|
|
can use `event.preventDefault()` to avoid the callback completely:
|
|
|
|
```js
|
|
player.addEventListener('error', function(event) {
|
|
// Custom logic for error events
|
|
if (player.isLive() &&
|
|
event.error.code == shaka.util.Error.Code.BAD_HTTP_STATUS) {
|
|
player.retryStreaming();
|
|
}
|
|
|
|
// Do not invoke the failure callback for this event
|
|
event.preventDefault();
|
|
});
|
|
```
|