Tutorial on integrating Fairplay on Safari
Enable DRM
DRM encryption needs to be enabled via processing settings in Video CMS. Navigate to Processing Settings in Video CMS here.
Once enabled, you can process a new video using this profile and that video will be DRM-encrypted.
Heads Up!
You won't be able to play Fairplay protected videos on Apple devices if you have not added Fairplay Credentials to Gumlet. Please follow this guide to request and add Fairplay Credentials to Gumlet.
Playing DRM Content
Once a video is DRM encrypted, it can't be played only via playback URL. DRM playback also needs Key Server URL and Certificate Server URL along with the playback URL to play the content. The key server URL will inform the player of the place from where the decryption keys can be acquired.
For security reasons, the Key Server URL needs an authentication token to be passed so it knows the request for the key request is legitimate.
Obtain the Key Server URL and Secret Key
Please go to the DRM Credentials page on the Gumlet dashboard to get:
- Key Server URL - It should look like https://fairplay.gumlet.com/licence/5f2bdde3e93619b8859d8831
- Certificate Server URL - It should look like https://fairplay.gumlet.com/certificate/5f2bdde3e93619b8859d8831
- Secret Key
Once you get them, the following sample code can be used to generate auth token.
Create Auth Token
const crypto = require('crypto');
// Secret Key provided by Gumlet
let proxySecret = "abcdefghijklmn";
// this is content id for asset. for gumlet processed videos, it's same as asset_id
let content_id = "1234567890abcdef";
// Licence Server URL provided by Gumlet
let proxyUrl = `https://fairplay.gumlet.com/licence/5f2bdde3e93619b8859d8831/${content_id}`;
proxySecret = Buffer.from(proxySecret, 'base64');
// expiration time in seconds
let tokenlifetime = 30;
let queryparams = {
expires: Math.round(Date.now() + tokenlifetime*1000)
};
let stringForTokenGeneration = `${proxyUrl.slice(35)}?` + (new URLSearchParams(queryparams)).toString();
let signature = crypto.createHmac('sha1', proxySecret).update(stringForTokenGeneration).digest('hex');
console.log(`Auth Token: ${signature}`);
console.log(`Signed Licence Server URL: ${proxyUrl}?${(new URLSearchParams(queryparams)).toString()}&token=${signature}`);
Key Server URL with secure token should look like this: https://fairplay.gumlet.com/licence/5f2bdde3e93619b8859d8831/627b533efd2da43909008562?expires=1652258263811&token=9290ba08e7743655a450b9543bdca737ea973b61
Fairplay Integration with Safari
Safari browsers on macOS and iOS natively support playback of HLS streams encrypted with Fairplay. This guide gives you a sample code to get started with Fairplay integration on Safari.
Once you add Fairplay credentials on the DRM Credentials page, you will be given these 2 URLs.
// ADAPT: You need to replace this with actual URL
window.certificate_url = "https://fairplay.gumlet.com/certificate/:org_id";
// ADAPT: You need to replace with actual URL
window.licence_url = "https://fairplay.gumlet.com/licence/:org_id";
// ADAPT: You need to replace with actual URL
window.playback_url = "https://video.gumlet.io/your_url.m3u8";
Here org_id
will be replaced with your Gumlet organization id. The first step is to write a function that can download and store certificates. The certificate will be used later.
async function loadCertificate()
{
try {
let response = await fetch(window.certificate_url);
window.certificate = await response.arrayBuffer();
} catch(e) {
window.console.error(`Could not load certificate at ${serverCertificatePath}`);
}
}
Now we will write a function to call this function to load the certificate and start video playback.
async function startVideo()
{
await loadCertificate();
let video = document.querySelector('video');
video.addEventListener('encrypted', onEncrypted);
video.src = window.playback_url;
}
You can see there is a function onEncrypted
that we will call when 'encrypted'
event is fired. That function will fetch credentials and pass them to a secure session so Safari can decode the encrypted content.
async function onEncrypted(event) {
try {
let initDataType = event.initDataType;
if (initDataType !== 'skd') {
window.console.error(`Received unexpected initialization data type "${initDataType}"`);
return;
}
let video = event.target;
if (!video.mediaKeys) {
let access = await navigator.requestMediaKeySystemAccess("com.apple.fps", [{
initDataTypes: [initDataType],
videoCapabilities: [{ contentType: 'application/vnd.apple.mpegurl', robustness: '' }],
distinctiveIdentifier: 'not-allowed',
persistentState: 'not-allowed',
sessionTypes: ['temporary'],
}]);
let keys = await access.createMediaKeys();
// Heads Up! The certificate we fetched earlier is used here.
await keys.setServerCertificate(window.certificate);
await video.setMediaKeys(keys);
}
let initData = event.initData;
let session = video.mediaKeys.createSession();
session.generateRequest(initDataType, initData);
let message = await new Promise(resolve => {
session.addEventListener('message', resolve, { once: true });
});
// licence_url we set earlier is used here.
let response = await getResponse(message, window.licence_url);
await session.update(response);
return session;
} catch(e) {
window.console.error(`Could not start encrypted playback due to exception "${e}"`)
}
}
Here is our last function that we used. It's getResponse
function which sends SPC
message to Fairplay server and returned CKC
is set in given encrypted session.
async function getResponse(event, licence_server_url) {
// need to convert the message to Base64 string
let spc_string = btoa(String.fromCharCode.apply(null, new Uint8Array(event.message)));
let licenseResponse = await fetch(licence_server_url, {
method: 'POST',
headers: new Headers({'Content-type': 'application/json'}),
body: JSON.stringify({
"spc" : spc_string
}),
});
let responseObject = await licenseResponse.json();
return Uint8Array.from(atob(responseObject.ckc), c => c.charCodeAt(0));
}
Now, everything is ready and we can just put these functions in <head>
section or in a separate JS file. All we need to do is to put <video>
tag and call startVideo
function.
<script>
document.addEventListener('DOMContentLoaded', startVideo);
</script>
<video controls preload="metadata" width=960></video>
Done!
All steps are now complete and Fairplay video can be played on Safari browsers on iOS and MacOS. Full HTML code is available.