Safari
1. Introduction
This section will provide information on integrating the SigmaMultiDRM system into the safari browser using Fairplay DRM:
Informaton
- Fairplay:
- Staging:
- Production:
In which, MERCHANT_ID and APP_ID will be obtained from Dashboard.
2. Require
Prerequisite
- Html 5 Browsers:
- Safari (Safari 8+ on MacOS, Safari on iOS 11.2+)
- Application Info:
- Certificate issued from apple.
3. Integrate Fairplay into Web application
3.1 Download the certificate provided by apple
You must download the certificate provided by apple and save it for future use. We recommend that certificate retention should be valid for one session.
Example of certificate download from Sigma MultiDRM system
javascript
const serverCertificatePath =
'https://cert.sigmadrm.com/app/fairplay/{MECHANT_ID}/{APP_ID}';
function loadCertificate() {
var request = new XMLHttpRequest();
request.responseType = 'arraybuffer';
request.addEventListener('load', onCertificateLoaded, false);
request.addEventListener('error', onCertificateError, false);
request.open('GET', serverCertificatePath, true);
request.send();
}
function onCertificateLoaded(event) {
var request = event.target;
certificate = new Uint8Array(request.response);
console.log('Certificate: ', certificate);
/*
Start video at here. Here, we call function startVideo()
*/
startVideo();
}
function onCertificateError(event) {
window.console.error('Failed to retrieve the server certificate.');
/*
Retry get certificate here
*/
}
3.2 Running a content using Fairplay DRM
This part will have to configure the information for the video card like the example below:
javascript
function startVideo() {
var video = document.getElementsByTagName('video')[0];
video.addEventListener('webkitneedkey', onneedkey, false);
video.addEventListener('error', onerror, false);
// ADAPT: there must be logic here to fetch/build the appropriate m3u8 URL
video.src = '{M3U8_URL}';
}
function onerror(event) {
window.console.error('A video playback error occurred', event);
}
function onneedkey(event) {
console.log('onneedkey: ', event);
var video = event.target;
var initData = event.initData;
var contentId = extractContentId(initData);
initData = concatInitDataIdAndCertificate(initData, contentId, certificate);
if (!video.webkitKeys) {
selectKeySystem();
video.webkitSetMediaKeys(new WebKitMediaKeys(keySystem));
}
if (!video.webkitKeys) throw 'Could not create MediaKeys';
console.log('Init Data: ', btoa(initData));
var keySession = video.webkitKeys.createSession('video/mp4', initData);
if (!keySession) throw 'Could not create key session';
keySession.contentId = contentId;
waitForEvent('webkitkeymessage', licenseRequestReady, keySession);
waitForEvent('webkitkeyadded', onkeyadded, keySession);
waitForEvent('webkitkeyerror', onkeyerror, keySession);
}
function extractContentId(initData) {
parserInitData(initData);
var contentId = licenseUrl.searchParams.get('assetId');
return contentId;
}
function parserInitData(initData) {
var url = arrayToString(initData).substring(1);
licenseUrl = new URL(url);
licenseUrl.protocol = 'https';
}
function concatInitDataIdAndCertificate(initData, id, cert) {
if (typeof id == 'string') id = stringToArray(id);
var offset = 0;
var buffer = new ArrayBuffer(
initData.byteLength + 4 + id.byteLength + 4 + cert.byteLength,
);
var dataView = new DataView(buffer);
var initDataArray = new Uint8Array(buffer, offset, initData.byteLength);
initDataArray.set(initData);
offset += initData.byteLength;
dataView.setUint32(offset, id.byteLength, true);
offset += 4;
var idArray = new Uint16Array(buffer, offset, id.length);
idArray.set(id);
offset += idArray.byteLength;
dataView.setUint32(offset, cert.byteLength, true);
offset += 4;
var certArray = new Uint8Array(buffer, offset, cert.byteLength);
certArray.set(cert);
return new Uint8Array(buffer, 0, buffer.byteLength);
}
function selectKeySystem() {
if (WebKitMediaKeys.isTypeSupported('com.apple.fps.1_0', 'video/mp4')) {
keySystem = 'com.apple.fps.1_0';
} else {
throw 'Key System not supported';
}
}
function waitForEvent(name, action, target) {
target.addEventListener(
name,
function () {
console.log('Wait For Event: ', name, arguments);
action(arguments[0]);
},
false,
);
}
function licenseRequestFailed(event) {
window.console.error('The license request failed.');
}
function onkeyerror(event) {
window.console.error('A decryption key error was encountered', event);
}
function onkeyadded(event) {
window.console.log('Decryption key was added to session.');
}
3.3 Configure license information
This section will be responsible for configuring the information to get the license and the license parser to push into the video player.
javascript
function licenseRequestReady(event) {
var session = event.target;
var message = event.message;
var request = new XMLHttpRequest();
var sessionId = event.sessionId;
request.responseType = 'text';
request.session = session;
request.addEventListener('load', licenseRequestLoaded, false);
request.addEventListener('error', licenseRequestFailed, false);
// var params = 'spc=' + base64EncodeUint8Array(message) + '&assetId=' + encodeURIComponent(session.contentId);
var params = JSON.stringify({
spc: base64EncodeUint8Array(message),
assetId: encodeURIComponent(session.contentId),
});
console.log(message);
request.open('POST', licenseUrl.toString(), true);
request.setRequestHeader('Content-type', 'application/json');
request.setRequestHeader(
'custom-data',
btoa(
JSON.stringify({
userId: 'USER_ID',
sessionId: 'SESSION_ID',
merchantId: 'MERCHANT_ID',
appId: 'APP_ID',
}),
),
);
request.send(params);
}
function licenseRequestLoaded(event) {
var request = event.target;
var session = request.session;
// response can be of the form: '\n<ckc>base64encoded</ckc>\n'
// so trim the excess:
var wrappedString = request.responseText.trim();
var wrapped = JSON.parse(wrappedString);
var keyText = wrapped.license;
console.log('request license: ', request);
if (keyText.substr(0, 5) === '<ckc>' && keyText.substr(-6) === '</ckc>')
keyText = keyText.slice(5, -6);
key = base64DecodeUint8Array(keyText);
session.update(key);
}
Information
Information to be provided
Props | Type | Description |
---|---|---|
MERCHANT_ID | String | Customer ID |
APP_ID | String | App ID |
USER_ID | String | UserId is allocated from the application side |
SESSION_ID | String | The user's session is allocated by the application |