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