-
Notifications
You must be signed in to change notification settings - Fork 71
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Add CDM detection module #98
Merged
Merged
Changes from 9 commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
49762c6
add cdm detection module
alex-barstow 7d27724
allow promise polyfill for detect func, expose on player.eme, add tests
alex-barstow 0125e5d
Merge branch 'main' into cdm-detection
alex-barstow 81748c7
update logic
alex-barstow f0f677f
fix test
alex-barstow bf1b162
add temporary debug log
alex-barstow 7d495c0
temporarily change initDataTypes
alex-barstow 5680afb
switch initDataTypes back
alex-barstow 0173a16
fix tests
alex-barstow 7ac3e30
remove getSupportedCDMs, update tests, add docs
alex-barstow File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
import window from 'global/window'; | ||
import videojs from 'video.js'; | ||
|
||
// `IS_CHROMIUM` and `IS_WINDOWS` are newer Video.js features, so add fallback just in case | ||
export const IS_CHROMIUM = videojs.browser.IS_CHROMIUM || (/Chrome|CriOS/i).test(window.navigator.userAgent); | ||
export const IS_WINDOWS = videojs.browser.IS_WINDOWS || (/Windows/i).test(window.navigator.userAgent); | ||
|
||
// Use a combination of API feature and user agent detection to provide an initial | ||
// best guess as to which CDMs are supported. | ||
const hasMediaKeys = Boolean(window.MediaKeys && window.navigator.requestMediaKeySystemAccess); | ||
const isChromeOrFirefox = videojs.browser.IS_CHROME || videojs.browser.IS_FIREFOX; | ||
const isChromiumEdge = videojs.browser.IS_EDGE && IS_CHROMIUM; | ||
const isAnyEdge = videojs.browser.IS_EDGE; | ||
|
||
const bestGuessSupport = { | ||
fairplay: Boolean(window.WebKitMediaKeys) || (hasMediaKeys && videojs.browser.IS_ANY_SAFARI), | ||
playready: hasMediaKeys && (isAnyEdge && (!IS_CHROMIUM || IS_WINDOWS)), | ||
widevine: hasMediaKeys && (isChromeOrFirefox || isChromiumEdge), | ||
clearkey: hasMediaKeys && (isChromeOrFirefox || isChromiumEdge) | ||
}; | ||
|
||
let latestSupportResults = bestGuessSupport; | ||
|
||
// Synchronously return the latest list of supported CDMs returned by detectCDMSupport(). | ||
// If none is available, return the best guess | ||
export const getSupportedCDMs = () => { | ||
return latestSupportResults; | ||
}; | ||
|
||
const genericConfig = [{ | ||
initDataTypes: ['cenc'], | ||
audioCapabilities: [{ | ||
contentType: 'audio/mp4;codecs="mp4a.40.2"' | ||
}], | ||
videoCapabilities: [{ | ||
contentType: 'video/mp4;codecs="avc1.42E01E"' | ||
}] | ||
}]; | ||
|
||
const keySystems = [ | ||
// Fairplay | ||
// Needs a different config than the others | ||
{ | ||
keySystem: 'com.apple.fps', | ||
supportedConfig: [{ | ||
initDataTypes: ['sinf'], | ||
videoCapabilities: [{ | ||
contentType: 'video/mp4' | ||
}] | ||
}] | ||
}, | ||
// Playready | ||
{ | ||
keySystem: 'com.microsoft.playready.recommendation', | ||
supportedConfig: genericConfig | ||
}, | ||
// Widevine | ||
{ | ||
keySystem: 'com.widevine.alpha', | ||
supportedConfig: genericConfig | ||
}, | ||
// Clear | ||
{ | ||
keySystem: 'org.w3.clearkey', | ||
supportedConfig: genericConfig | ||
} | ||
]; | ||
|
||
// Asynchronously detect the list of supported CDMs by requesting key system access | ||
// when possible, otherwise rely on browser-specific EME API feature detection. | ||
export const detectSupportedCDMs = () => { | ||
const Promise = window.Promise; | ||
const results = { | ||
fairplay: Boolean(window.WebKitMediaKeys), | ||
playready: false, | ||
widevine: false, | ||
clearkey: false | ||
}; | ||
|
||
if (!window.MediaKeys || !window.navigator.requestMediaKeySystemAccess) { | ||
latestSupportResults = results; | ||
|
||
return Promise.resolve(results); | ||
} | ||
|
||
return Promise.all(keySystems.map(({keySystem, supportedConfig}) => { | ||
return window.navigator.requestMediaKeySystemAccess(keySystem, supportedConfig).catch(() => {}); | ||
})).then(([fairplay, playready, widevine, clearkey]) => { | ||
results.fairplay = Boolean(fairplay); | ||
results.playready = Boolean(playready); | ||
results.widevine = Boolean(widevine); | ||
results.clearkey = Boolean(clearkey); | ||
|
||
latestSupportResults = results; | ||
|
||
return results; | ||
}); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
import QUnit from 'qunit'; | ||
import videojs from 'video.js'; | ||
import {IS_CHROMIUM, IS_WINDOWS, getSupportedCDMs, detectSupportedCDMs } from '../src/cdm.js'; | ||
|
||
QUnit.module('videojs-contrib-eme CDM Module'); | ||
|
||
QUnit.test('detectSupportedCDMs() returns a Promise', function(assert) { | ||
const promise = detectSupportedCDMs(); | ||
|
||
assert.ok(promise.then); | ||
}); | ||
|
||
QUnit.test('getSupportedCDMs() returns an object with correct properties', function(assert) { | ||
const cdmResults = getSupportedCDMs(); | ||
const cdmNames = Object.keys(cdmResults); | ||
|
||
assert.equal(cdmNames.length, 4, 'object contains correct number of properties'); | ||
assert.equal(cdmNames.includes('fairplay'), true, 'object contains fairplay property'); | ||
assert.equal(cdmNames.includes('playready'), true, 'object contains playready property'); | ||
assert.equal(cdmNames.includes('widevine'), true, 'object contains widevine property'); | ||
assert.equal(cdmNames.includes('clearkey'), true, 'object contains clearkey property'); | ||
}); | ||
|
||
// NOTE: This test is not future-proof. It verifies that the CDM detect function | ||
// works as expected given browser's *current* CDM support. If that support changes, | ||
// this test may need updating. | ||
QUnit.test('detectSupportedCDMs() promise resolves correctly on different browsers', function(assert) { | ||
const done = assert.async(); | ||
const promise = detectSupportedCDMs(); | ||
|
||
promise.then((result) => { | ||
// Currently, widevine and clearkey don't work in headless Chrome, so we can't verify cdm support in | ||
// the remote Video.js test environment. However, it can be verified if testing locally in a real browser. | ||
// Headless Chrome bug: https://bugs.chromium.org/p/chromium/issues/detail?id=788662 | ||
if (videojs.browser.IS_CHROME) { | ||
assert.equal(result.fairplay, false, 'fairplay not supported in Chrome'); | ||
assert.equal(result.playready, false, 'playready not supported in Chrome'); | ||
|
||
// Uncomment if testing locally in actual browser | ||
// assert.equal(result.clearkey, true, 'clearkey is supported in Chrome'); | ||
// assert.equal(result.widevine, true, 'widevine is supported in Chrome'); | ||
} | ||
|
||
// Widevine requires a plugin in Ubuntu Firefox so it also does not work in the remote Video.js test environment | ||
if (videojs.browser.IS_FIREFOX) { | ||
assert.equal(result.fairplay, false, 'fairplay not supported in FF'); | ||
assert.equal(result.playready, false, 'playready not supported in FF'); | ||
assert.equal(result.clearkey, true, 'clearkey is supported in FF'); | ||
|
||
// Uncomment if testing locally in actual browser | ||
// assert.equal(result.widevine, true, 'widevine is supported in Chrome and FF'); | ||
} | ||
|
||
if (videojs.browser.IS_ANY_SAFARI) { | ||
assert.deepEqual(result, { | ||
fairplay: true, | ||
clearkey: true, | ||
playready: false, | ||
widevine: false | ||
}, 'fairplay support reported in Safari'); | ||
} | ||
|
||
if (videojs.browser.IS_EDGE && IS_CHROMIUM && !IS_WINDOWS) { | ||
assert.deepEqual(result, { | ||
fairplay: false, | ||
playready: false, | ||
widevine: true, | ||
clearkey: true | ||
}, 'widevine support reported in non-Windows Chromium Edge'); | ||
} | ||
|
||
if (videojs.browser.IS_EDGE && IS_CHROMIUM && IS_WINDOWS) { | ||
assert.deepEqual(result, { | ||
fairplay: false, | ||
playready: true, | ||
widevine: true, | ||
clearkey: true | ||
}, 'widevine and playready support reported in Windows Chromium Edge'); | ||
} | ||
|
||
done(); | ||
}).catch(done); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why have a synchronous function for an asynchronous process?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could just get rid of it. Curious whether others think it makes sense to remove
getSupportedCDMs()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think getting rid of
getSupportedCDMs
is the right call here.