Skip to content

Commit

Permalink
Make Player.support synchronous and add support plugin system.
Browse files Browse the repository at this point in the history
Now there is a new synchronous method Player.isBrowserSupported.  This
will detect basic support and return a boolean.  The support() method
has been renamed to probeSupport() and should only be used for
diagnostics.

Also added a plugin system to Player support testing.  Now a plugin
(e.g. offline) can add extra info to the output of probeSupport().

Closes shaka-project#388
Closes shaka-project#389

Change-Id: I313a41d9f123871272f1395aeb99c980df1f4bae
  • Loading branch information
TheModMaker committed Jun 2, 2016
1 parent 0acc6de commit 5102f54
Show file tree
Hide file tree
Showing 12 changed files with 244 additions and 196 deletions.
42 changes: 21 additions & 21 deletions demo/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,27 +80,27 @@ shakaDemo.init = function() {

shaka.polyfill.installAll();

shaka.Player.support().then(function(support) {
shakaDemo.support_ = support;

if (shakaDemo.support_.supported == false) {
var errorDisplay = document.getElementById('errorDisplay');
var error = 'Your browser is not supported!';

// IE8 and other very old browsers don't have textContent.
if (errorDisplay.textContent === undefined) {
errorDisplay.innerText = error;
} else {
errorDisplay.textContent = error;
}

// Disable the load button.
var loadButton = document.getElementById('loadButton');
loadButton.disabled = true;
if (!shaka.Player.isBrowserSupported()) {
var errorDisplay = document.getElementById('errorDisplay');
var error = 'Your browser is not supported!';

// Make sure the error is seen.
errorDisplay.style.fontSize = '250%';
// IE8 and other very old browsers don't have textContent.
if (errorDisplay.textContent === undefined) {
errorDisplay.innerText = error;
} else {
errorDisplay.textContent = error;
}

// Disable the load button.
var loadButton = document.getElementById('loadButton');
loadButton.disabled = true;

// Make sure the error is seen.
errorDisplay.style.fontSize = '250%';
} else {
shaka.Player.probeSupport().then(function(support) {
shakaDemo.support_ = support;

shakaDemo.video_ =
/** @type {!HTMLVideoElement} */(document.getElementById('video'));
shakaDemo.player_ = new shaka.Player(shakaDemo.video_);
Expand All @@ -124,8 +124,8 @@ shakaDemo.init = function() {
if ('play' in params) {
shakaDemo.load();
}
}
});
});
}
};


Expand Down
23 changes: 13 additions & 10 deletions docs/tutorials/upgrade.md
Original file line number Diff line number Diff line change
Expand Up @@ -571,29 +571,32 @@ if (!shaka.player.Player.isBrowserSupported()) {
}
```

The check in v1 was not very detailed and only did a surface examination of
browser APIs. In v2, the check is much more detailed:
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:
shaka.Player.support().then(function(support) {
// The check is asynchronous because the EME API is asynchronous.
if (!support.supported) {
// Show an error and abort.
} else {
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 `support()` report for your browser at:
You can also see the full `probeSupport()` report for your browser at:
{@link http://shaka-player-demo.appspot.com/support.html}


Expand Down
31 changes: 20 additions & 11 deletions externs/shaka/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -191,26 +191,35 @@ shakaExtern.Restrictions;

/**
* @typedef {{
* manifest: Object.<string, boolean>,
* media: Object.<string, boolean>,
* drm: Object.<string, boolean>,
* supported: boolean
* persistentState: boolean
* }}
*
* @property {boolean} persistentState
* Whether this key system supports persistent state.
*/
shakaExtern.DrmSupportType;


/**
* @typedef {{
* manifest: !Object.<string, boolean>,
* media: !Object.<string, boolean>,
* drm: !Object.<string, ?shakaExtern.DrmSupportType>
* }}
*
* @description
* An object detailing browser support for various features.
*
* @property {Object.<string, boolean>} manifest
* @property {!Object.<string, boolean>} manifest
* A map of supported manifest types.
* The keys are manifest MIME types and file extensions.
* @property {Object.<string, boolean>} media
* @property {!Object.<string, boolean>} media
* A map of supported media types.
* The keys are media MIME types.
* @property {Object.<string, boolean>} drm
* A map of DRM support.
* The keys are well-known key system IDs.
* @property {boolean} supported
* True if the library is usable at all.
* @property {!Object.<string, ?shakaExtern.DrmSupportType>} drm
* A map of supported key systems.
* The keys are the key system names. The value is null if it is not
* supported. Key systems not probed will not be in this dictionary.
*
* @exportDoc
*/
Expand Down
96 changes: 58 additions & 38 deletions lib/media/drm_engine.js
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ shaka.media.DrmEngine.prototype.prepareMediaKeyConfigs_ =
var config = configsByKeySystem[drmInfo.keySystem];
if (!config) {
config = {
initDataTypes: undefined, // don't care.
// ignore initDataTypes
audioCapabilities: [],
videoCapabilities: [],
distinctiveIdentifier: 'optional',
Expand Down Expand Up @@ -405,13 +405,13 @@ shaka.media.DrmEngine.prototype.queryMediaKeys_ =
if (!hasLicenseServer)
return;

// If there are no tracks of a type, these should be undefined, not empty.
// If there are no tracks of a type, these should be not present.
// Otherwise the query will fail.
if (config.audioCapabilities.length == 0) {
config.audioCapabilities = undefined;
delete config.audioCapabilities;
}
if (config.videoCapabilities.length == 0) {
config.videoCapabilities = undefined;
delete config.videoCapabilities;
}

p = p.catch(function() {
Expand Down Expand Up @@ -439,6 +439,7 @@ shaka.media.DrmEngine.prototype.queryMediaKeys_ =
// Edge 13 does not report capabilities. To work around this, set the
// supported types to null, which Player will use as a signal that the
// information is not available.
// See: https://goo.gl/0cSuT2
this.supportedTypes_ = null;
}

Expand Down Expand Up @@ -966,51 +967,70 @@ shaka.media.DrmEngine.prototype.onKeyStatusesChange_ = function(event) {


/**
* Returns a Promise to a map of EME support for well-known key systems.
* Returns true if the browser has recent EME APIs.
*
* @param {boolean} persistentStateRequired
* @return {!Promise.<!Object.<string, boolean>>}
* @return {boolean}
*/
shaka.media.DrmEngine.support = function(persistentStateRequired) {
// Every object in the support hierarchy has a "basic" member.
// All "basic" members must be true for the library to be usable.
shaka.media.DrmEngine.isBrowserSupported = function() {
var basic =
!!window.MediaKeys &&
!!window.navigator &&
!!window.navigator.requestMediaKeySystemAccess &&
!!window.MediaKeySystemAccess &&
!!window.MediaKeySystemAccess.prototype.getConfiguration;

var support = {'basic': basic};
return basic;
};

var tests = [];
if (support['basic']) {
var testKeySystems = [
'org.w3.clearkey',
'com.widevine.alpha',
'com.microsoft.playready',
'com.apple.fps.2_0',
'com.apple.fps.1_0',
'com.apple.fps',
'com.adobe.primetime'
];

var config = {};
if (persistentStateRequired) {
config.persistentState = 'required';
config.sessionTypes = ['persistent-license'];
}

testKeySystems.forEach(function(keySystem) {
var p = navigator.requestMediaKeySystemAccess(keySystem, [config])
.then(function() {
support[keySystem] = true;
}, function() {
support[keySystem] = false;
});
tests.push(p);
});
}
/**
* Returns a Promise to a map of EME support for well-known key systems.
*
* @return {!Promise.<!Object.<string, ?shakaExtern.DrmSupportType>>}
*/
shaka.media.DrmEngine.probeSupport = function() {
goog.asserts.assert(shaka.media.DrmEngine.isBrowserSupported(),
'Must have basic EME support');

var tests = [];
var testKeySystems = [
'org.w3.clearkey',
'com.widevine.alpha',
'com.microsoft.playready',
'com.apple.fps.2_0',
'com.apple.fps.1_0',
'com.apple.fps',
'com.adobe.primetime'
];

var config = {
persistentState: 'required',
sessionTypes: ['persistent-license']
};
var support = {};
testKeySystems.forEach(function(keySystem) {
var p = navigator.requestMediaKeySystemAccess(keySystem, [config, {}])
.then(function(access) {
// Create a media keys object and try to create an offline session.
// This is used to detect offline license support for browsers that
// do not correctly report it in the configuration.
// https://goo.gl/gtYT3z, https://goo.gl/rvnB1g, https://goo.gl/z0URJ0
return access.createMediaKeys();
})
.then(function(mediaKeys) {
var persistentState = false;
try {
// This will throw if persistent licenses are not supported.
mediaKeys.createSession('persistent-license');
persistentState = true;
} catch (e) {}

support[keySystem] = {persistentState: persistentState};
}, function() {
support[keySystem] = null;
});
tests.push(p);
});

return Promise.all(tests).then(function() {
return support;
Expand Down
7 changes: 2 additions & 5 deletions lib/media/manifest_parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,9 @@ shaka.media.ManifestParser.registerParserByMime = function(
*
* @return {!Object.<string, boolean>}
*/
shaka.media.ManifestParser.support = function() {
// Every object in the support hierarchy has a "basic" member.
// All "basic" members must be true for the library to be usable.
var support = {'basic': true};

shaka.media.ManifestParser.probeSupport = function() {
// Make sure all registered parsers are shown.
var support = {};
for (var type in shaka.media.ManifestParser.parsersByMime) {
support[type] = true;
}
Expand Down
77 changes: 42 additions & 35 deletions lib/media/media_source_engine.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,15 +105,11 @@ shaka.media.MediaSourceEngine.isTypeSupported = function(mimeType) {


/**
* Returns a map of MediaSource support for well-known types.
* Returns true if the browser has the basic APIs we need.
*
* @return {!Object.<string, boolean>}
* @return {boolean}
*/
shaka.media.MediaSourceEngine.support = function() {
// Every object in the support hierarchy has a "basic" member.
// All "basic" members must be true for the library to be usable.
var support = {'basic': !!window.MediaSource};

shaka.media.MediaSourceEngine.isBrowserSupported = function() {
// This is ugly, but Safari 8 does not implement appendWindowEnd.
// Detecting this missing feature directly is too complex, since that would
// involve creating a video element, MediaSource, and a SourceBuffer.
Expand All @@ -124,36 +120,47 @@ shaka.media.MediaSourceEngine.support = function() {
var version = navigator.appVersion;
if (vendor && vendor.indexOf('Apple') >= 0 &&
version && version.indexOf('Version/8') >= 0) {
support['basic'] = false;
return false;
}

if (support['basic']) {
var testMimeTypes = [
// MP4 types
'video/mp4; codecs="avc1.42E01E"',
'audio/mp4; codecs="mp4a.40.2"',
// WebM types
'video/webm; codecs="vp8"',
'video/webm; codecs="vp9"',
'audio/webm; codecs="vorbis"',
'audio/webm; codecs="opus"',
// MPEG2 TS types (video/ is also used for audio: http://goo.gl/tYHXiS)
'video/mp2t; codecs="avc1.42E01E"',
'video/mp2t; codecs="mp4a.40.2"',
// WebVTT types
'text/vtt',
'application/mp4; codecs="wvtt"',
// TTML types
'application/ttml+xml',
'application/mp4; codecs="stpp"'
];

testMimeTypes.forEach(function(type) {
support[type] = shaka.media.MediaSourceEngine.isTypeSupported(type);
var basicType = type.split(';')[0];
support[basicType] = support[basicType] || support[type];
});
}
return !!window.MediaSource;
};


/**
* Returns a map of MediaSource support for well-known types.
*
* @return {!Object.<string, boolean>}
*/
shaka.media.MediaSourceEngine.probeSupport = function() {
goog.asserts.assert(shaka.media.MediaSourceEngine.isBrowserSupported(),
'Requires basic support');
var support = {};
var testMimeTypes = [
// MP4 types
'video/mp4; codecs="avc1.42E01E"',
'audio/mp4; codecs="mp4a.40.2"',
// WebM types
'video/webm; codecs="vp8"',
'video/webm; codecs="vp9"',
'audio/webm; codecs="vorbis"',
'audio/webm; codecs="opus"',
// MPEG2 TS types (video/ is also used for audio: http://goo.gl/tYHXiS)
'video/mp2t; codecs="avc1.42E01E"',
'video/mp2t; codecs="mp4a.40.2"',
// WebVTT types
'text/vtt',
'application/mp4; codecs="wvtt"',
// TTML types
'application/ttml+xml',
'application/mp4; codecs="stpp"'
];

testMimeTypes.forEach(function(type) {
support[type] = shaka.media.MediaSourceEngine.isTypeSupported(type);
var basicType = type.split(';')[0];
support[basicType] = support[basicType] || support[type];
});

return support;
};
Expand Down
Loading

0 comments on commit 5102f54

Please sign in to comment.