Fairplay Integration

Tutorial on integrating Fairplay on Safari

Enable DRM

DRM encryption needs to be enabled via profile settings first. Please go to the video profiles section on the Gumlet dashboard and either edit the existing profile or create a new profile with the DRM option enabled as shown in the screenshot.

Enable DRM in the profile section of Gumlet

Enable DRM in the profile section of Gumlet

Once this is 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:

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_idwill 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.