Skip to content

Commit

Permalink
IE/Edge + PlayReady PoC
Browse files Browse the repository at this point in the history
Implemented a (rough) polyfill for EME spec version 20140218 (http://www.w3.org/TR/2014/WD-encrypted-media-2014021) which IE11/Edge implement. Works with real PlayReady protected assets with a few caveats:
1. Using CustomEvent is troublesome, IE doesn't seem to completely remove it from window, as some properties like "target" are remain  un-modifiable. Introduced a mirrored property, but it's a hack
2. IE doesn't like certain actions from occuring when the video element is not initialised (e.g. readyState still 0), so there is one place where currentTime is being set before this, so we moved it into a loadedmetadata handler (not 100% if this causes issues)
3. Similar to above, calling setMediaKeys when readyState is 0 causes an InvalidStateError - have moved it into a loadstart event handler, but I think there may be a race condition, as playback occasionaly fails
4. Not tested this with ClearKey
5. Not tested this with pre-supplied init data (i.e. we're reacting to the native msKeyNeeded event rather than fake encrypted event)
  • Loading branch information
jonoblinkbox authored and jonoward committed Jun 17, 2015
1 parent 96b369e commit 79a80f0
Show file tree
Hide file tree
Showing 5 changed files with 456 additions and 2 deletions.
48 changes: 47 additions & 1 deletion lib/media/eme_manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -545,7 +545,7 @@ shaka.media.EmeManager.prototype.onSessionMessage_ = function(event) {
shaka.log.info('onSessionMessage_', event);
shaka.asserts.assert(this.drmScheme_);
this.requestLicense_(
event.target,
event.target || event.customTarget,
/** @type {!shaka.player.DrmSchemeInfo} */ (this.drmScheme_),
event.message);
};
Expand Down Expand Up @@ -590,6 +590,11 @@ shaka.media.EmeManager.prototype.requestLicense_ = function(
var info = new shaka.player.DrmSchemeInfo.LicenseRequestInfo(
licenseRequestBody);

// Apply common pre-processors
if (drmScheme.keySystem === 'com.microsoft.playready') {
this.playReadyLicensePreProcessor_(info);
}

// Pre-process the license request.
if (drmScheme.licensePreProcessor) {
drmScheme.licensePreProcessor(info);
Expand Down Expand Up @@ -631,6 +636,47 @@ shaka.media.EmeManager.prototype.requestLicense_ = function(
);
};

/**
* Standard pre-processor for PlayReady license requests.
*
* @param {!shaka.player.DrmSchemeInfo.LicenseRequestInfo} info The license request info.
*
* @private
*/
shaka.media.EmeManager.prototype.playReadyLicensePreProcessor_ = function(info){
/*
The playready license body is actually an XML string, so need to convert
info.body (which is a Uint8Array, holding UTF-16 text data) to a string
XML typically has this structure (as an example):
<PlayReadyKeyMessage type="LicenseAcquisition">
<LicenseAcquisition Version="1">
<Challenge encoding="base64encoded">{Base64EncodedBinaryChallengeData}</Challenge>
<HttpHeaders>
<HttpHeader><name>Content-Type</name><value>text/xml; charset=utf-8</value></HttpHeader>
<HttpHeader><name>SOAPAction</name><value>"http://schemas.microsoft.com/DRM/2007/03/protocols/AcquireLicense"</value></HttpHeader>
</HttpHeaders>
</LicenseAcquisition>
</PlayReadyKeyMessage>"
Only challenge data is sent to the server, http errors are required to be added to the XHR object in order for the
request to be processed correctly (e.g. may need to add a SOAPAction header as in the above example)
*/

var licenseBodyXml = String.fromCharCode.apply(null, new Uint16Array(info.body.buffer));
var licenseBodyXmlDom = new DOMParser().parseFromString(licenseBodyXml, "application/xml");

console.log(licenseBodyXml);
var headerNames = licenseBodyXmlDom.getElementsByTagName("name");
var headerValues = licenseBodyXmlDom.getElementsByTagName("value");

for (var i = 0; i < headerNames.length; i++) {
info.headers[headerNames[i].childNodes[0].nodeValue] = headerValues[i].childNodes[0].nodeValue;
}

info.body = atob(licenseBodyXmlDom.getElementsByTagName("Challenge")[0].childNodes[0].nodeValue);
}


/**
* Returns the DRM Scheme information.
Expand Down
6 changes: 5 additions & 1 deletion lib/player/stream_video_source.js
Original file line number Diff line number Diff line change
Expand Up @@ -1110,7 +1110,11 @@ shaka.player.StreamVideoSource.prototype.startStreams_ = function(
// Set the video's current time before starting the streams so that the
// streams begin buffering at the stream start time.
shaka.log.info('Starting each stream from', streamStartTime);
this.video.currentTime = streamStartTime;
function setInitialCurrentTime() {
this.currentTime = streamStartTime;
this.removeEventListener("loadedmetadata", setInitialCurrentTime);
}
this.video.addEventListener("loadedmetadata", setInitialCurrentTime);

// Start the streams.
for (var type in this.streamsByType_) {
Expand Down
4 changes: 4 additions & 0 deletions lib/polyfill/mediakeys.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ goog.provide('shaka.polyfill.MediaKeys');

goog.require('shaka.log');
goog.require('shaka.polyfill.PatchedMediaKeys.nop');
goog.require('shaka.polyfill.PatchedMediaKeys.v20140218');
goog.require('shaka.polyfill.PatchedMediaKeys.v01b');


Expand Down Expand Up @@ -51,6 +52,9 @@ shaka.polyfill.MediaKeys.install = function() {
} else if (HTMLMediaElement.prototype.webkitGenerateKeyRequest) {
shaka.log.info('Using prefixed EME v0.1b.');
shaka.polyfill.PatchedMediaKeys.v01b.install();
} else if (window.MSMediaKeys) {
shaka.log.info('Using EME v20140218');
shaka.polyfill.PatchedMediaKeys.v20140218.install();
} else {
shaka.log.info('EME not available.');
shaka.polyfill.PatchedMediaKeys.nop.install();
Expand Down
Loading

0 comments on commit 79a80f0

Please sign in to comment.