Skip to content
On this page

ShakaPlayer ​

1. Introduction ​

This section will provide information on integrating SigmaMultiDRM system into shaka player:

::: Information

In which, MERCHANT_ID and APP_ID will be obtained from Dashboard.

get_customer_info :::

2. Require ​

::: Prerequisite

  • Html 5 Browsers:
Html 5 browsersWidevinePlayReadyFairPlayLicense encryption support
Chrome (Window, MacOS, Android, ChromeOS, Linux)YesNoNoYes
Firefox (Window, MacOS, Linux)YesNoNoYes
Microsoft Edge (Window, MacOS, Android)YesYesNoNo
Safari (Safari 8+ on MacOS, Safari on iOS 11.2+)NoNoYesNo
iOS Browser (Chrome, Cốc Cốc, Microsoft Edge, Firefox, Opera)NoNoYesNo
Opera (Window, MacOS)YesNoNoYes
Internet Explorer (Window 8.1+)NoYesYesNo
  • Smart TVs:
Smart TVsWidevinePlayReadyFairPlayLicense encryption support
SamSung Tizen (2016-2017, 2018+ Models)YesYesNoNo
SamSung Tizen&Orsay (2010-2015 Models)NoYesNoNo
LG (WebOS 3.0+)YesYesNoNo
LG (WebOS 1.2 & Netcast)NoYesNoNo
Smart TV Alliance (LG, Philips, Toshiba, Panasonic)YesYesNoNo
Android TVYesYesNoNo

:::

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

javascript
// 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 ​

javascript
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);
}
PropsTypeDescription
licenseWidevineServerStringSee heading 1. Introduction
licensePlayReadyServerStringSee heading 1. Introduction
licenseFairPlayServerStringSee heading 1. Introduction
manifestUriStringThe URL of the content you want to see

3.4 Config Request Header ​

PropsTypeDescription
CERTIFICATE_URIStringSee heading 1. Introduction
MERCHANT_IDStringId of merchant's user
APP_IDStringId of application
USER_IDStringUserId of merchant's client
SESSION_IDStringSessionId of merchant's client

3.4.1 Config Request Header for FairPlay ​

javascript
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 ​

javascript
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:

javascript
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,
      })
    );
  }
});
PropsTypeDescription
MERCHANT_IDStringId of merchant's user
APP_IDStringId of application
USER_IDStringUserId of merchant's client
SESSION_IDStringSessionId of merchant's client

3.5 Config Response Header ​

Configurations for Widevine, PlayReady and FairPlayer are the same

javascript
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;

javascript
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) {}
  }
});

4. Demo ​

Sample source code