Hi All,
I am trying to setup up dash.js to play DRM content (both Widevine & PlayReady). Went through the github samples found https://github.com/Dash-Industry-Forum/dash.js/blob/master/samples/protection/getLicense.html This shows what config to pass and how to use the protectionController to initialize.
Can we get a full working example from manifest URL to the video actually playing. This will actually help to understand how exactly to use dash.js to play DRM content.
BTW i found something on the web https://www.axinom.com/setting-up-a-simple-dash-js-player-for-multi-drm-playback/. But i guess thats for the old version of dash.js.
Below is the understanding i have for the DRM flow in general, which is not perfect and needs feedback-
There is a newer version of the guide you linked above at https://github.com/Axinom/drm-quick-start - perhaps this is of some help.
DRM flow is documented, with diagram fairly similar to yours, at https://www.w3.org/TR/encrypted-media/#introduction.
Right took a look at url provided by @sandersaares, and it includes a very good example of how to play DRM protected streams: https://github.com/Axinom/drm-quick-start/blob/master/Website/Index.html.
@subhranshudas, as you will see, playing DRM protected streams in dash.js is quite simple. You just need to call setProtectionData to set keysystems information (ex: license server url) before attaching the source. You neither need to deal with ProtectionController nor need to know DRM flow.
I will add an entry in dash.js FAQ to clarify this point.
@sandersaares @jeoliva thank you for the references. I went through the https://github.com/Axinom/drm-quick-start and when i tried going through the https://github.com/Axinom/drm-quick-start#sample-scenario-2-creating-your-own-license-tokens
i was unable to create an evaluation account https://github.com/Axinom/drm-quick-start/issues/14
Though i am waiting on being able to set up the evaluation account and move forward. But what i understood from https://github.com/Axinom/drm-quick-start you need to get token first to send as part of the http request header when dash.js is making the call to acquire license.
With this understanding i am trying to connect the dots (with the dash.js drm example) as to how do we then get all the params for
var config = {
// From manifest
KID: '6e5a1d26-2757-47d7-8046-eaa5d1d34b5a',
audioPssh: 'AAAANHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABQIARIQblodJidXR9eARuql0dNLWg==',
videoPssh: 'AAAANHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABQIARIQblodJidXR9eARuql0dNLWg==',
// From protData['com.widevine.alpha']
serverURL: 'https://drm-widevine-licensing.axtest.net/AcquireLicense',
httpRequestHeaders: {
"X-AxDRM-Message": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ2ZXJzaW9uIjoxLCJjb21fa2V5X2lkIjoiNjllNTQwODgtZTllMC00NTMwLThjMWEtMWViNmRjZDBkMTRlIiwibWVzc2FnZSI6eyJ0eXBlIjoiZW50aXRsZW1lbnRfbWVzc2FnZSIsImtleXMiOlt7ImlkIjoiNmU1YTFkMjYtMjc1Ny00N2Q3LTgwNDYtZWFhNWQxZDM0YjVhIn1dfX0.yF7PflOPv9qHnu3ZWJNZ12jgkqTabmwXbDWk_47tLNE"
},
audioRobustness: "SW_SECURE_CRYPTO",
videoRobustness: "SW_SECURE_DECODE"
}
i mean params like KID, audioPssh, videoPssh, audioRobustness, videoRobustness to pass on to protData.
Attaching the a PDF file which has detailed queries.
Thanks a lot for the support!
Any inputs guys?
Also what if some specific request body is to be requested by the DRM service is there a provision to provide for that in dash.js?
My DRM service is expecting a XML in the request body. Is there any option to pass a request body in the config of protectionController.setProtectionData()
Regarding your previous question, you don't have to provide pssh data as part of the configuration. That is done in the Protection sample but just because its goal is showing how to use Protection module separately and without any media associated.
In your case, you are setting dash.js player to play a DRM protected stream so configuration is much more simpler (extracted from the Axinom sample):
player.setProtectionData({
"com.widevine.alpha": {
"serverURL": "https://drm-widevine-licensing.axtest.net/AcquireLicense",
"httpRequestHeaders": {
"X-AxDRM-Message": licenseToken
}
},
"com.microsoft.playready": {
"serverURL": "https://drm-playready-licensing.axtest.net/AcquireLicense",
"httpRequestHeaders": {
"X-AxDRM-Message": licenseToken
}
}
});
@epiclabsDASH if i understand what you have mentioned in https://github.com/Dash-Industry-Forum/dash.js/issues/2478#issuecomment-374890813 to play encrypted content for both DASH or MSS, we need to (just to be extra clear)-
1) Initialize the media player
2) Use player.setProtectionData() to pass an object which will contain information for both Widevine & PlayReady.
In that information we will have -
(_Mandatory_) serverURL: 'this is the DRM service endpoint'
(_Mandatory_???) httpRequestHeaders : {
/*
some http request headers now
(a) are these HTTP request headers mandatory
(b) what will happen if we do not pass them at all. I believe the http headers are determined by the
service consuming them
*/
}
So typically if i pass a DRM service URL and the httpRequestHeaders we should be good?
Also what i am interested in knowing is ultimately what HTTP request dash.js makes to the DRM service ultimately. The request shape, because i am interested to understand what is the contract honored by both parties. Because if the DRM service i am dealing with expects a different request body is there some customization i have to do in dash.js source to make the request body match the contract.
Now my requirement is the DRM PlayReady service which i am consuming that expects an XML (will share the format shortly) in the request body. So is there any provision to do it. I mean what is the flexibility already available with dash.js setProtectionData() interface if the DRM service has some custom things going on.
@epiclabsDASH @bbert few observations and edits i have for now with observing the Axinom example's NETWORK traces vs the Request Shape i am supposed to create.
So does dash.js has any interface where this data for extra tags can be fitted into.
@epiclabsDASH @bbert i think i might pass the extra tags' key/value(s) info in the player.setProtectionData() and try to pick it up in the KeySystemPlayReady::getLicenseRequestFromMessage and do a customized xhr.send() based on some conditions. This is what i feel would be the cleanest approach. (_speaking only for the XML in request body in the MSS case for now_)
But the problem will be every time dash.js updates, if i wanna use the source code, i will have to add that change.
So looking for a cleaner way to have/expect dash.js to expose some interface to add these extra tags.
@epiclabsDASH was thinking of another approach in terms of interceptors, does dash.js has that kind of support yet? I can write one, but i want to keep it localized to protection module without changing the source code of protection more from a middleware point of view. Yet to discover a dash.js interface which does this.
Had found about
``` /**
* Get Playready Custom data
*/
function getCDMData() {
let customData,
cdmData,
cdmDataBytes,
i;
checkConfig();
if (protData && protData.cdmData) {
// Convert custom data into multibyte string
customData = [];
for (i = 0; i < protData.cdmData.length; ++i) {
customData.push(protData.cdmData.charCodeAt(i));
customData.push(0);
}
customData = String.fromCharCode.apply(null, customData);
// Encode in Base 64 the custom data string
customData = BASE64.encode(customData);
// Initialize CDM data with Base 64 encoded custom data
// (see https://msdn.microsoft.com/en-us/library/dn457361.aspx)
cdmData = PRCDMData.replace('%CUSTOMDATA%', customData);
// Convert CDM data into multibyte characters
cdmDataBytes = [];
for (i = 0; i < cdmData.length; ++i) {
cdmDataBytes.push(cdmData.charCodeAt(i));
cdmDataBytes.push(0);
}
return new Uint8Array(cdmDataBytes).buffer;
}
return null;
}```
Is this a hook to provide customData? for PlayReady?
But i see this never gets called even if i pass cdmData in the setProtection()..
Any way to hook this up ?
@bbcrddave @bbert @nicosang @sandersaares any inputs as in why the above code is never called in place and what happened to providing customData to the challenge w.r.t.
https://github.com/Dash-Industry-Forum/dash.js/issues/1944
https://github.com/Dash-Industry-Forum/dash.js/issues/876
Need a way to plugin in CustomData to the challenge. I have tried String Manipulations, XML Node creations in getLicenseRequestFromMessage but i repeatedly get 500 with the below error code

Also can we never modify the generated challenge (after it has been BASE64 decoded) to insert CustomData ?? to add our custom tags?
BTW i am currently trying on IE Edge, Windows 10
and tried passing something like-
player.setProtectionData({
'com.microsoft.playready': {
cdmData: '<CustomData>.....stuff in here...</CustomData>'
}};
As per https://docs.microsoft.com/en-us/previous-versions/windows/apps/dn457361%28v%3dieb.10%29 passing cdmData should take care of creating the
// MS-prefixed (IE11, Windows 8.1)
{
// Video Element
setMediaKeys: 'msSetMediaKeys',
// MediaKeys
MediaKeys: 'MSMediaKeys',
// MediaKeySession
release: 'close',
// Events
needkey: 'msneedkey',
error: 'mskeyerror',
message: 'mskeymessage',
ready: 'mskeyadded',
close: 'mskeyclose'
}
seems like the 3 params createSession() is called only if its IE11, Windows 8.1 so what about IE Edge, Windows 10 & IE 11, Windows 10
Custom data insertion is already supported in dash.js.
But as far as I know custom data are supported only by MS-prefixed PlayReady CDM, that can be used through ProtectionModel_3Feb2014.
MS-prefixed PlayReady CDM is available on IE11 and Edge, but on Edge since both EME versions (3Feb2014 and 21Jan2015) are available, the ProtectionModel_21Jan2015 is used by default.
However, at application level you can force dash.js selecting ProtectionModel_3Feb2014 on Edge :
if (HTMLVideoElement.prototype.msSetMediaKeys) {
navigator.requestMediaKeySystemAccess = undefined;
HTMLVideoElement.prototype.setMediaKeys = undefined;
}
Then, when using MS-prefixed PlayReady CDM and ProtectionModel_3Feb2014, you can set custom data by setting the 'cdmData' field in the input protectionData:
protData['com.microsoft.playready'] = {
....
cdmData: '<CDM_specific_data>',
}
Hi @bbert Thanks :)
I tried returning the _3Feb2014 model directly for now......
It works for IE Edge, Windows 10...so far....testing on other platforms.
Will update about details in some time.
@bbert
Following is what i found---
After using the below hack in the client layer, as suggested, for IE Edge on Windows 10 ( _for_ IE 11 on Windows 10 it picks up the ProtectionModel_3Feb2014 without any hack),
if (HTMLVideoElement.prototype.msSetMediaKeys) {
navigator.requestMediaKeySystemAccess = undefined;
HTMLVideoElement.prototype.setMediaKeys = undefined;
}
the below scenarios happen (detailed in comments)-
function getProtectionModel(config) {
let log = config.log;
let eventBus = config.eventBus;
let errHandler = config.errHandler;
let videoElement = config.videoModel ? config.videoModel.getElement() : null;
/*
* the 1st if condition is evaluated to true even after hack,
* so the control goes inside the 1st if and `ProtectionModel_21Jan2015`
* is picked and return. Hence the control never goes to the `ProtectionModel_3Feb2014`
* block even though getAPI(videoElement, APIS_ProtectionModel_3Feb2014) is actually true
*/
if ((!videoElement || videoElement.onencrypted !== undefined) &&
(!videoElement || videoElement.mediaKeys !== undefined)) {
log('EME detected on this user agent! (ProtectionModel_21Jan2015)');
return ProtectionModel_21Jan2015(context).create({log: log, eventBus: eventBus, events: config.events});
} else if (getAPI(videoElement, APIS_ProtectionModel_3Feb2014)) {
log('EME detected on this user agent! (ProtectionModel_3Feb2014)');
return ProtectionModel_3Feb2014(context).create({log: log, eventBus: eventBus, events: config.events, api: getAPI(videoElement, APIS_ProtectionModel_3Feb2014)});
} else if (getAPI(videoElement, APIS_ProtectionModel_01b)) {
log('EME detected on this user agent! (ProtectionModel_01b)');
return ProtectionModel_01b(context).create({log: log, eventBus: eventBus, errHandler: errHandler, events: config.events, api: getAPI(videoElement, APIS_ProtectionModel_01b)});
} else {
log('No supported version of EME detected on this user agent! - Attempts to play encrypted content will fail!');
return null;
}
}
So to test the playback i tried 2 things --
ProtectionModel_3Feb2014, after which i got the playback. if (getAPI(videoElement, APIS_ProtectionModel_3Feb2014)) {
log('EME detected on this user agent! (ProtectionModel_3Feb2014)');
return ProtectionModel_3Feb2014(context).create({log: log, eventBus: eventBus, events: config.events, api: getAPI(videoElement, APIS_ProtectionModel_3Feb2014)});
} else if ((!videoElement || videoElement.onencrypted !== undefined) &&
(!videoElement || videoElement.mediaKeys !== undefined)) {
log('EME detected on this user agent! (ProtectionModel_21Jan2015)');
return ProtectionModel_21Jan2015(context).create({log: log, eventBus: eventBus, events: config.events});
} else if (getAPI(videoElement, APIS_ProtectionModel_01b)) {
log('EME detected on this user agent! (ProtectionModel_01b)');
return ProtectionModel_01b(context).create({log: log, eventBus: eventBus, errHandler: errHandler, events: config.events, api: getAPI(videoElement, APIS_ProtectionModel_01b)});
} else {
log('No supported version of EME detected on this user agent! - Attempts to play encrypted content will fail!');
return null;
}
which also works perfectly fine.
SO my question is should dash.js be making this change to accommodate this scenario [Playready protected MSS on IE11, Windows 10 with facility to insert CustomData]
Also
Q1) if we change the order of IF blocks, will there be any impact ? i haven't noticed any.
Q2) is the current order of the IF blocks as per design?
Hi,
I tested protected smooth streaming form microsoft playready player demo
It works perfectly on IE 11 and Edge (windows 10)
But when I try to play it on a web browser control in a winform application written in c#, I have audio but no video in the player. and there is no error on the console log
Anyone has an idea about this issue ?
Personally, I did not know that we could exploit MSE and EME in a winform application.
So I have no idea about this issue.
BTW, this issue seems to be different from the one treated in this thread.
Please open another issue.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
This issue has been automatically closed because no further activity occurred. If you think this issue is still relevant please reopen it. Thank you for your contributions.
Most helpful comment
Custom data insertion is already supported in dash.js.
But as far as I know custom data are supported only by MS-prefixed PlayReady CDM, that can be used through ProtectionModel_3Feb2014.
MS-prefixed PlayReady CDM is available on IE11 and Edge, but on Edge since both EME versions (3Feb2014 and 21Jan2015) are available, the ProtectionModel_21Jan2015 is used by default.
However, at application level you can force dash.js selecting ProtectionModel_3Feb2014 on Edge :
Then, when using MS-prefixed PlayReady CDM and ProtectionModel_3Feb2014, you can set custom data by setting the 'cdmData' field in the input protectionData: