Skip to content
On this page

Rx-Player

1. Introduction

This section will provide information on integrating SigmaMultiDRM system into Rx-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 Rx-Player

3.1. Add script tag to load 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>

Note: The implementation will be presented in the following sections

3.2 Implementation utils feature

javascript
// With HTML5 SmartTV, you don't need call function initApp
function initApp() {
  if (window)
    window.sigmaPacker = new SigmaPacker();
    window.sigmaPacker.onload = () => {
    console.log('DataPacker: Loaded');
  };
  window.sigmaPacker.init();
}
function initPlayer() {
  const videoElement = document.getElementById("video");
  player = new RxPlayer({ videoElement });

  player.addEventListener("error", (err) => {
    console.log("the content stopped with the following error", err);
  });

  player.addEventListener("playerStateChange", (state) => {
    if (state === "LOADED") {
      console.log("The content is loaded");

      // toggle between play and pause when the user clicks on the video
      videoElement.onclick = function () {
        if (player.getPlayerState() === "PLAYING") {
          player.pause();
        } else {
          player.play();
        }
      };
    }
  });
}

function base64ToArrayBuffer(base64) {
  var binaryString = atob(base64);
  var bytes = new Uint8Array(binaryString.length);
  for (var i = 0; i < binaryString.length; i++) {
    bytes[i] = binaryString.charCodeAt(i);
  }
  return bytes.buffer;
}

function formatPlayreadyChallenge(challenge) {
  let u8Challenge;
  if (!(challenge instanceof Uint8Array)) {
    if (challenge instanceof ArrayBuffer) {
      u8Challenge = new Uint8Array(challenge);
    } else {
      u8Challenge = new Uint8Array(challenge.buffer);
    }
  } else {
    u8Challenge = challenge;
  }
  const str = leUtf16ToStr(u8Challenge);
  const match = /<Challenge encoding="base64encoded">(.*)<\/Challenge>/.exec(str);
  const xml = match ?
    atob(match[1]) : utf8ToStr(u8Challenge);
  return xml;
}
function leUtf16ToStr(bytes) {
  let str = "";
  for (let i = 0; i < bytes.length; i += 2) {
    str += String.fromCharCode((bytes[i + 1] << 8) + bytes[i]);
  }
  return str;
}

3.3 Implementation get license Api

javascript
const licenseWidevineServer = LICENSE_WIDEVINE_URL;
const licensePlayReadyServer = LICENSE_PLAYREADY_URL;

function getLicense(rawChallenge, isPlayready = false) {
  const challenge = isPlayready ? formatPlayreadyChallenge(rawChallenge) : rawChallenge;
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open('POST', isPlayready ? licensePlayReadyServer : widevineLicenseURI, true);
    // With HTML5 SmartTV, you don't need get information from Sigma Packer
    const packInfo = window.sigmaPacker.getDataPacker(challenge) || {};

    xhr.setRequestHeader(
      'custom-data',
      btoa(
        JSON.stringify({
          userId: 'USER_ID',
          sessionId: 'SESSION_ID',
          merchantId: 'MERCHANT_ID',
          appId: 'APP_ID',
          // With HTML5 SmartTV, you don't need add reqId and deviceInfo to custom-data header
          reqId: packInfo.requestId,
          deviceInfo: packInfo.deviceInfo,
        })
      )
    );
    xhr.onerror = (err) => {
      reject(err);
    };
    xhr.onload = (evt) => {
      if (xhr.status >= 200 && xhr.status < 300) {
        const response = evt.target.response;
        const { license } = response;
        const licenseBuffer = base64ToArrayBuffer(license);

        // With HTML5 SmartTV, you don't need update clientInfo
        let clientInfo = response.clientInfo;
        if (clientInfo) {
          window.sigmaPacker.update(JSON.stringify(clientInfo));
        } else {
          if (xhr.readyState === xhr.HEADERS_RECEIVED) {
            clientInfo = xhr.getResponseHeader('client-info');
            if (clientInfo) {
              window.sigmaPacker.update(atob(clientInfo));
            }
          }
        }

        resolve(licenseBuffer);
      } else {
        const error = new Error(
          "getLicense's request finished with a " + `${xhr.status} HTTP error`
        );
        reject(error);
      }
    };
    if (isPlayready) {
      xhr.setRequestHeader("content-type", "text/xml; charset=utf-8");
    }
    xhr.responseType = 'json';
    xhr.send(challenge);
  });
}

In where:

PropsTypeDescription
licenseWidevineServerStringSee heading 1. Introduction
licensePlayReadyServerStringSee heading 1. Introduction
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 Implementation load video

javascript
function loadVideo(manifestUri) {
  player.loadVideo({
    url: manifestUri,
    transport: 'dash',
    autoPlay: true,
    keySystems: [
      {
        type: 'widevine',
        getLicense: function(challenge) {
          return getLicense(challenge)
        },
        getLicenseConfig: {
          retry: 3,
          timeout: -1,
        },
      },
      {
        type: 'playready',
        getLicense:  function(challenge) {
          return getLicense(challenge, true)
        },
        getLicenseConfig: {
          retry: 3,
          timeout: -1,
        },
      },
    ],
  });
}

3.4.2. Call all function together

javascript
// Note: Always call initApp before initPlayer();
initApp();
initPlayer();
loadVideo(manifestUri)

In where:

PropsTypeDescription
manifestUriStringThe URL of the content you want to see

4. Demo

Sample Demo