Playing protected content with Sigma DRM
1. Overview
Nearly every license provider requires a few special pieces of information to be sent to the DRM license server or responds with a proprietary format. Instead of integrating a few license providers into the core of our player, we decided to provide necessary configuration options via the player configuration.
2. License encryption feature
We provide license encryption for Widevine DRM. To use the license encryption feature, you need to install the Sigma Packer SDK into your application:
-
Install SigmaPacker SDK
-
Source: sigma_packer.js
-
Add script:
<script src="sigma_packer.js"></script>
-
-
Initialize the SigmaPacker instance
window.sigmaPacker = new SigmaPacker();
window.sigmaPacker.onload = () => {
console.log('SigmaPacker loaded');
};
window.sigmaPacker.init();
3. Widevine
const source = {
dash: DASH_MANIFEST_URL,
drm: {
widevine: {
LA_URL: WIDEVINE_LICENSE_SERVER_URL,
},
},
};
// The configuration object using init bitmovin player instance
const config = {
network: {
preprocessHttpRequest: function (type, request) {
const HttpRequestType = bitmovin.player.HttpRequestType;
if (type === HttpRequestType.DRM_LICENSE_WIDEVINE) {
const customData = {
merchantId: MERCHANT_ID,
appId: APP_ID,
userId: USER_ID,
sessionId: SESSION_ID,
};
// FIXME: If you use the license encryption feature, you need to uncomment the code below
/**
const packInfo = window.sigmaPacker.getDataPacker(request.body) || {};
customData.reqId = packInfo.requestId;
customData.deviceInfo = packInfo.deviceInfo;
*/
request.headers['Content-Type'] = 'application/octet-stream';
request.headers['custom-data'] = btoa(JSON.stringify(customData));
}
return Promise.resolve(request);
},
preprocessHttpResponse: function (type, response) {
const HttpRequestType = bitmovin.player.HttpRequestType;
if (type === HttpRequestType.DRM_LICENSE_WIDEVINE) {
// This is the wrapped license, which is a JSON string.
try {
const wrappedString = new TextDecoder().decode(response.body);
// Parse the JSON string into an object.
const wrapped = JSON.parse(wrappedString);
// FIXME: If you use the license encryption feature, you need to uncomment the code below
/**
if (response.headers['client-info']) {
window.sigmaPacker.update(atob(response.headers['client-info']));
} else if (wrapped.clientInfo) {
window.sigmaPacker.update(JSON.stringify(wrapped.clientInfo));
}
*/
// This is a base64-encoded version of the raw license.
const rawLicenseBase64 = wrapped.license;
// Decode that base64 string into a Uint8Array and replace the response
// data. The raw license will be fed to the Widevine CDM.
response.body = Uint8Array.from(atob(rawLicenseBase64), (c) =>
c.charCodeAt(0)
);
} catch (error) {}
}
},
},
};
4. PlayReady
const source = {
dash: DASH_MANIFEST_URL,
drm: {
playready: {
LA_URL: PLAYREADY_LICENSE_SERVER_URL,
},
},
};
// The configuration object using init Bitmovin player instance
const config = {
network: {
preprocessHttpRequest: function (type, request) {
const HttpRequestType = bitmovin.player.HttpRequestType;
if (type === HttpRequestType.DRM_LICENSE_PLAYREADY) {
const customData = {
merchantId: MERCHANT_ID,
appId: APP_ID,
userId: USER_ID,
sessionId: SESSION_ID,
};
request.headers['Content-Type'] = 'application/octet-stream';
request.headers['custom-data'] = btoa(JSON.stringify(customData));
}
return Promise.resolve(request);
},
preprocessHttpResponse: function (type, response) {
const HttpRequestType = bitmovin.player.HttpRequestType;
if (type === HttpRequestType.DRM_LICENSE_PLAYREADY) {
// This is the wrapped license, which is a JSON string.
try {
const wrappedString = new TextDecoder().decode(response.body);
// Parse the JSON string into an object.
const wrapped = JSON.parse(wrappedString);
// This is a base64-encoded version of the raw license.
const rawLicenseBase64 = wrapped.license;
// Decode that base64 string into a Uint8Array and replace the response data.
response.body = Uint8Array.from(atob(rawLicenseBase64), (c) =>
c.charCodeAt(0)
);
} catch (error) {}
}
},
},
};
5. FairPlay
let licenseURL = ''; // The variable to store license request url
// SourceConfig object to be passed to Bitmovin Player instance
const source = {
hls: HLS_MANIFEST_URL,
drm: {
fairplay: {
LA_URL: FAIRPLAY_LICENSE_SERVER_URL,
certificateURL: FAIRPLAY_CERTIFICATE_URL,
prepareLicense: (license) => {
return new Uint8Array(license);
},
prepareMessage: (event, session) => {
return JSON.stringify({
spc: event.messageBase64Encoded,
assetId: session.contentId,
});
},
prepareContentId: (contentId) => {
const pattern = 'skd://';
const idx = contentId.indexOf(pattern);
if (idx > -1) {
licenseURL = contentId.substring(idx + pattern.length);
licenseURL = 'https://' + licenseURL;
return new URL(licenseURL).searchParams.get('assetId');
}
return '';
},
},
},
};
// The configuration object is used to initialize the Bitmovin Player instance
const config = {
network: {
preprocessHttpRequest: function (type, request) {
const HttpRequestType = bitmovin.player.HttpRequestType;
if (type === HttpRequestType.DRM_LICENSE_FAIRPLAY) {
const customData = {
merchantId: MERCHANT_ID,
appId: APP_ID,
userId: USER_ID,
sessionId: SESSION_ID,
};
request.url = licenseURL;
request.headers['Content-Type'] = 'application/json';
request.headers['custom-data'] = btoa(JSON.stringify(customData));
}
return Promise.resolve(request);
},
preprocessHttpResponse: function (type, response) {
const HttpRequestType = bitmovin.player.HttpRequestType;
if (type === HttpRequestType.DRM_LICENSE_FAIRPLAY) {
// This is the wrapped license, which is a JSON string.
try {
const wrapped = JSON.parse(response.body);
// Parse the JSON string into an object.
// This is a base64-encoded version of the raw license.
const rawLicenseBase64 = wrapped.license;
// Decode that base64 string into a Uint8Array and replace the response
// data. The raw license will be fed to the FairPlay.
response.body = Uint8Array.from(atob(rawLicenseBase64), (c) =>
c.charCodeAt(0)
);
} catch (error) {}
}
},
},
};
6. Complete example for Widevine, PlayReady and FairPlay
// FIXME: If you use the license encryption feature, you need to uncomment the code below
/**
(function initApp() {
window.sigmaPacker = new SigmaPacker();
window.sigmaPacker.onload = () => {
console.log('SigmaPacker loaded');
};
window.sigmaPacker.init();
})();
*/
let licenseURL = ''; // The variable to store license request url
// SourceConfig object to be passed to Bitmovin Player instance
const source = {
dash: DASH_MANIFEST_URL,
hls: HLS_MANIFEST_URL,
drm: {
widevine: {
LA_URL: WIDEVINE_LICENSE_SERVER_URL,
},
playready: {
LA_URL: PLAYREADY_LICENSE_SERVER_URL,
},
fairplay: {
LA_URL: FAIRPLAY_LICENSE_SERVER_URL,
certificateURL: FAIRPLAY_CERTIFICATE_URL,
prepareLicense: (license) => {
return new Uint8Array(license);
},
prepareMessage: (event, session) => {
return JSON.stringify({
spc: event.messageBase64Encoded,
assetId: session.contentId,
});
},
prepareContentId: (contentId) => {
const pattern = 'skd://';
const idx = contentId.indexOf(pattern);
if (idx > -1) {
licenseURL = contentId.substring(idx + pattern.length);
licenseURL = 'https://' + licenseURL;
return new URL(licenseURL).searchParams.get('assetId');
}
return '';
},
},
},
};
// The configuration object is used to initialize the Bitmovin Player instance
const config = {
network: {
preprocessHttpRequest: function (type, request) {
const HttpRequestType = bitmovin.player.HttpRequestType;
if (type === HttpRequestType.DRM_LICENSE_WIDEVINE) {
const customData = {
merchantId: MERCHANT_ID,
appId: APP_ID,
userId: USER_ID,
sessionId: SESSION_ID,
};
// FIXME: If you use the license encryption feature, you need to uncomment the code below
/**
const packInfo = window.sigmaPacker.getDataPacker(request.body) || {};
customData.reqId = packInfo.requestId;
customData.deviceInfo = packInfo.deviceInfo;
*/
request.headers['Content-Type'] = 'application/octet-stream';
request.headers['custom-data'] = btoa(JSON.stringify(customData));
}
if (type === HttpRequestType.DRM_LICENSE_PLAYREADY) {
const customData = {
merchantId: MERCHANT_ID,
appId: APP_ID,
userId: USER_ID,
sessionId: SESSION_ID,
};
request.headers['Content-Type'] = 'application/octet-stream';
request.headers['custom-data'] = btoa(JSON.stringify(customData));
}
if (type === HttpRequestType.DRM_LICENSE_FAIRPLAY) {
const customData = {
merchantId: MERCHANT_ID,
appId: APP_ID,
userId: USER_ID,
sessionId: SESSION_ID,
};
request.url = licenseURL;
request.headers['Content-Type'] = 'application/json';
request.headers['custom-data'] = btoa(JSON.stringify(customData));
}
},
preprocessHttpResponse: function (type, response) {
const HttpRequestType = bitmovin.player.HttpRequestType;
if (type === HttpRequestType.DRM_LICENSE_WIDEVINE) {
// This is the wrapped license, which is a JSON string.
try {
const wrappedString = new TextDecoder().decode(response.body);
// Parse the JSON string into an object.
const wrapped = JSON.parse(wrappedString);
// FIXME: If you use the license encryption feature, you need to uncomment the code below
/**
if (response.headers['client-info']) {
window.sigmaPacker.update(atob(response.headers['client-info']));
} else if (wrapped.clientInfo) {
window.sigmaPacker.update(JSON.stringify(wrapped.clientInfo));
}
*/
// This is a base64-encoded version of the raw license.
const rawLicenseBase64 = wrapped.license;
// Decode that base64 string into a Uint8Array and replace the response
// data. The raw license will be fed to the Widevine CDM.
response.body = Uint8Array.from(atob(rawLicenseBase64), (c) =>
c.charCodeAt(0)
);
} catch (error) {}
}
if (type === HttpRequestType.DRM_LICENSE_PLAYREADY) {
// This is the wrapped license, which is a JSON string.
try {
const wrappedString = new TextDecoder().decode(response.body);
// Parse the JSON string into an object.
const wrapped = JSON.parse(wrappedString);
// This is a base64-encoded version of the raw license.
const rawLicenseBase64 = wrapped.license;
// Decode that base64 string into a Uint8Array and replace the response data.
// The raw license will be fed to the PlayReady.
response.body = Uint8Array.from(atob(rawLicenseBase64), (c) =>
c.charCodeAt(0)
);
} catch (error) {}
}
if (type === HttpRequestType.DRM_LICENSE_FAIRPLAY) {
// This is the wrapped license, which is a JSON string.
try {
const wrapped = JSON.parse(response.body);
// Parse the JSON string into an object.
// This is a base64-encoded version of the raw license.
const rawLicenseBase64 = wrapped.license;
// Decode that base64 string into a Uint8Array and replace the response data.
// The raw license will be fed to the FairPlay.
response.body = Uint8Array.from(atob(rawLicenseBase64), (c) =>
c.charCodeAt(0)
);
} catch (error) {}
}
},
},
};
const player = new bitmovin.player.Player(
document.getElementById('VIDEO_CONTAINER_ELEMENT'),
config
);
player.load(source);
Please replace the following placeholders in the code:
Props | Type | Description |
---|---|---|
DASH_MANIFEST_URL | String | The URL to the DASH manifest (MPD) file. |
HLS_MANIFEST_URL | String | The URL to the HLS Multivariant Playlist (M3U8) file. |
WIDEVINE_LICENSE_SERVER_URL | String | The URL to SigmaMultiDRM's Widevine license server. |
PLAYREADY_LICENSE_SERVER_URL | String | The URL to SigmaMultiDRM's PlayReady license server. |
FAIRPLAY_LICENSE_SERVER_URL | String | The URL to SigmaMultiDRM's FairPlay license server. |
FAIRPLAY_CERTIFICATE_URL | String | The URL to retrieve the FairPlay certificate. |
MERCHANT_ID | String | The ID of SigmaMultiDRM's merchant |
APP_ID | String | The ID of merchant's application |
USER_ID | String | The ID merchant's user |
SESSION_ID | String | The session of merchant's user |
Updated 8 months ago