ShakaPlayer
1. Introduction
This section will provide information on integrating SigmaMultiDRM system into shaka player:
::: Information
- Widevine:
- Playready:
- Fairplay:
- Staging:
- Production:
- Certificate URI: https://cert.sigmadrm.com/app/fairplay/{MERCHANT_ID}/{APP_ID}
- License URL: https://license.sigmadrm.com/license/verify/fairplay
In which, MERCHANT_ID and APP_ID will be obtained from Dashboard.
:::
2. Require
::: Prerequisite
- Html 5 Browsers:
Html 5 browsers | Widevine | PlayReady | FairPlay | License encryption support |
---|---|---|---|---|
Chrome (Window, MacOS, Android, ChromeOS, Linux) | Yes | No | No | Yes |
Firefox (Window, MacOS, Linux) | Yes | No | No | Yes |
Microsoft Edge (Window, MacOS, Android) | Yes | Yes | No | No |
Safari (Safari 8+ on MacOS, Safari on iOS 11.2+) | No | No | Yes | No |
iOS Browser (Chrome, Cốc Cốc, Microsoft Edge, Firefox, Opera) | No | No | Yes | No |
Opera (Window, MacOS) | Yes | No | No | Yes |
Internet Explorer (Window 8.1+) | No | Yes | Yes | No |
- Smart TVs:
Smart TVs | Widevine | PlayReady | FairPlay | License encryption support |
---|---|---|---|---|
SamSung Tizen (2016-2017, 2018+ Models) | Yes | Yes | No | No |
SamSung Tizen&Orsay (2010-2015 Models) | No | Yes | No | No |
LG (WebOS 3.0+) | Yes | Yes | No | No |
LG (WebOS 1.2 & Netcast) | No | Yes | No | No |
Smart TV Alliance (LG, Philips, Toshiba, Panasonic) | Yes | Yes | No | No |
Android TV | Yes | Yes | No | No |
:::
3. Integrate license into Shaka Player
3.1. License encryption feature with Sigma Packer SDK
To use the license encryption feature, you need to install the Sigma Packer SDK into your application
Installing the SDK
Source: sigma_packer.js
Add script:
html<script src="sigma_packer.js"></script> <script src="shaka-player.compiled.js"></script>
Note: The implementation will be presented in the following sections
3.2 Init application
- Default
var player;
document.addEventListener('DOMContentLoaded', function () {
shaka.polyfill.installAll();
var video = document.getElementById('VIDEO_ID'); // media element
player = new this.shaka.Player(video);
});
- License encryption feature: You need run shaka polyfill when SigmaPacker loaded
var player;
document.addEventListener('DOMContentLoaded', function () {
var video = document.getElementById('VIDEO_ID'); // media element
player = new this.shaka.Player(video);
window.sigmaPacker = new SigmaPacker();
window.sigmaPacker.onload = () => {
console.log('SigmaPacker loaded');
shaka.polyfill.installAll();
};
window.sigmaPacker.init();
});
Note: If you are using ReactJS
, place this code snippet in the src/App.js
file immediately after importing the utilities used to initialize the SigmaPacker.
// src/App.js
import ... from "...";
import ... from "...";
window.sigmaPacker = new SigmaPacker();
window.sigmaPacker.onload = () => {
console.log('SigmaPacker loaded');
shaka.polyfill.installAll();
};
window.sigmaPacker.init();
// do something
3.3 Configure the player to use this license server
const manifestUri = MANIFEST_URI;
const licenseWidevineServer = LICENSE_WIDEVINE_URL;
const licensePlayReadyServer = LICENSE_PLAYREADY_URL;
const licenseFairPlayServer = LICENSE_FAIRPLAY_URL;
player.configure({
drm: {
servers: {
'com.widevine.alpha': licenseWidevineServer,
'com.microsoft.playready': licensePlayReadyServer,
'com.apple.fps.1_0': licenseFairPlayServer,
},
},
});
// Try to load a manifest.
try {
await player.load(manifestUri);
// The video should now be playing!
} catch (e) {
console.error(e);
}
Props | Type | Description |
---|---|---|
licenseWidevineServer | String | See heading 1. Introduction |
licensePlayReadyServer | String | See heading 1. Introduction |
licenseFairPlayServer | String | See heading 1. Introduction |
manifestUri | String | The URL of the content you want to see |
3.4 Config Request Header
Props | Type | Description |
---|---|---|
CERTIFICATE_URI | String | See heading 1. Introduction |
MERCHANT_ID | String | Id of merchant's user |
APP_ID | String | Id of application |
USER_ID | String | UserId of merchant's client |
SESSION_ID | String | SessionId of merchant's client |
3.4.1 Config Request Header for FairPlay
var licenseURL; // Variable to store request license server URL
// First need to configure the certificate
const serverCertificateURI = CERTIFICATE_URI; // xem mục 1
const response = await fetch(serverCertificateURI);
if (!response.ok) {
console.error('Could not get certificate!');
return;
}
const certificate = await response.arrayBuffer();
player.configure({
drm: {
advanced: {
'com.apple.fps.1_0': {
serverCertificate: new Uint8Array(certificate),
},
},
},
});
// Edit content information (assetId) and save licenseURL information
player.configure('drm.initDataTransform', (initData, type, drmInfo) => {
if (type != 'skd') return initData;
const skdURL = shaka.util.StringUtils.fromBytesAutoDetect(initData);
const contentId = new URL(skdURL).searchParams.get('assetId');
const cert = player.drmInfo().serverCertificate;
licenseURL = skdURL.replace('skd://', 'https://');
return shaka.util.FairPlayUtils.initDataTransform(initData, contentId, cert);
});
// Customize license request
player.getNetworkingEngine().registerRequestFilter(function (type, request) {
if (type == shaka.net.NetworkingEngine.RequestType.LICENSE) {
request.uris = [licenseURL];
request.method = 'POST';
request.headers['Content-Type'] = 'application/json';
request.headers['custom-data'] = btoa(
JSON.stringify({
userId: 'USER_ID',
sessionId: 'SESSION_ID',
merchantId: 'MERCHANT_ID',
appId: 'APP_ID',
})
);
const originalPayload = new Uint8Array(request.body);
const base64Payload =
shaka.util.Uint8ArrayUtils.toStandardBase64(originalPayload);
request.body = JSON.stringify({
spc: base64Payload,
assetId: new URL(licenseURL).searchParams.get('assetId'),
});
}
});
3.4.2. Config Request Header for Widevine or PlayReady
player.getNetworkingEngine().registerRequestFilter(function (type, request) {
// Only add headers to license requests:
if (type == shaka.net.NetworkingEngine.RequestType.LICENSE) {
// This is the specific header name and value the server wants:
request.headers['Content-Type'] = 'application/octet-stream';
request.headers['custom-data'] = btoa(
JSON.stringify({
userId: 'USER_ID',
sessionId: 'SESSION_ID',
merchantId: 'MERCHANT_ID',
appId: 'APP_ID',
})
);
}
});
If you use license encryption feature, you need to add some fields to the header:
player.getNetworkingEngine().registerRequestFilter(function (type, request) {
const StringUtils = shaka.util.StringUtils;
const Uint8ArrayUtils = shaka.util.Uint8ArrayUtils;
if (type == shaka.net.NetworkingEngine.RequestType.LICENSE) {
// This is the specific header name and value the server wants:
const packInfo = window.sigmaPacker.getDataPacker(request.body) || {};
request.headers['Content-Type'] = 'application/octet-stream';
request.headers['custom-data'] = btoa(
JSON.stringify({
userId: 'USER_ID',
sessionId: 'SESSION_ID',
merchantId: 'MERCHANT_ID',
appId: 'APP_ID',
reqId: packInfo.requestId,
deviceInfo: packInfo.deviceInfo,
})
);
}
});
Props | Type | Description |
---|---|---|
MERCHANT_ID | String | Id of merchant's user |
APP_ID | String | Id of application |
USER_ID | String | UserId of merchant's client |
SESSION_ID | String | SessionId of merchant's client |
3.5 Config Response Header
Configurations for Widevine, PlayReady and FairPlayer are the same
player.getNetworkingEngine().registerResponseFilter(function (type, response) {
var StringUtils = shaka.util.StringUtils;
var Uint8ArrayUtils = shaka.util.Uint8ArrayUtils;
// Only manipulate license responses:
if (type == shaka.net.NetworkingEngine.RequestType.LICENSE) {
// This is the wrapped license, which is a JSON string.
try {
var wrappedString = StringUtils.fromUTF8(response.data);
// Parse the JSON string into an object.
var wrapped = JSON.parse(wrappedString);
// This is a base64-encoded version of the raw license.
var 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.data = Uint8ArrayUtils.fromBase64(rawLicenseBase64);
// Read additional fields from the server.
// The server we are using in this tutorial does not send anything useful.
// In practice, you could send any license metadata the client might need.
// Here we log what the server sent to the JavaScript console for
// inspection.
console.log(wrapped);
} catch (err) {}
}
});
If you use license encryption feature, you need a few more handling;
player.getNetworkingEngine().registerResponseFilter(function (type, response) {
// Alias some utilities provided by the library.
const StringUtils = shaka.util.StringUtils;
const Uint8ArrayUtils = shaka.util.Uint8ArrayUtils;
// Only manipulate license responses:
if (type == shaka.net.NetworkingEngine.RequestType.LICENSE) {
// This is the wrapped license, which is a JSON string.
try {
const wrappedString = StringUtils.fromUTF8(response.data);
// Parse the JSON string into an object.
const wrapped = JSON.parse(wrappedString);
if (response.headers['client-info']) {
console.log(response.headers['client-info']);
window.sigmaPacker.update(atob(response.headers['client-info']));
} else if (wrapped.clientInfo) {
console.log(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.data = Uint8ArrayUtils.fromBase64(rawLicenseBase64);
// Read additional fields from the server.
// The server we are using in this tutorial does not send anything useful.
// In practice, you could send any license metadata the client might need.
// Here we log what the server sent to the JavaScript console for
// inspection.
console.log(wrapped);
} catch (err) {}
}
});