Mosaic products documentation: Concepts, API Reference, Technical articles, How-to, Downloads and tools

HLS.js player

Sample implementation

Below you can find a sample implementation with explanation.

<!DOCTYPE html>
<html>
   <body>
      <script src="../node_modules/hls.js/dist/hls.js"></script>
      <!-- Or if you want the latest version from the main branch -->
      <!-- <script src="https://cdn.jsdelivr.net/npm/hls.js@canary"></script> -->
      <video id="video"></video>
      <script>
         var video = document.getElementById('video');

         var certificateUrl = "https://vtb.axinom.com/FPScert/fairplay.cer";
         <!-- This is the Axinom test certificate and you will need to change it to your production test certificate-->

         loadCertificate();
               if (Hls.isSupported()) {
                   var hls = new Hls({
                       debug: true,
                       enableWorker: true,
                       lowLatencyMode: true,
                       backBufferLength: 30,
                       emeEnabled: true,
                       drmSystems: {
                           'com.apple.fps': {
                               licenseUrl: "https://drm-fairplay-licensing.axprod.net/AcquireLicense",
                               serverCertificateUrl: "https://vtb.axinom.com/FPScert/fairplay.cer",
                           },
                       },

                       getContentId: function (emeOptions, initData) {
                                   return arrayToString(initData).replace(/^.*:\/\//, '');
                               },
                       licenseXhrSetup: function (xhr, url, keyContext, licenseChallenge) {
                           xhr.setRequestHeader('X-AxDRM-Message', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.ewogICJ2ZXJzaW9uIjogMSwKICAiY29tX2tleV9pZCI6ICI2OWU1NDA4OC1lOWUwLTQ1MzAtOGMxYS0xZWI2ZGNkMGQxNGUiLAogICJtZXNzYWdlIjogewogICAgInR5cGUiOiAiZW50aXRsZW1lbnRfbWVzc2FnZSIsCiAgICAidmVyc2lvbiI6IDIsCiAgICAibGljZW5zZSI6IHsKICAgICAgImFsbG93X3BlcnNpc3RlbmNlIjogdHJ1ZQogICAgfSwKICAgICJjb250ZW50X2tleXNfc291cmNlIjogewogICAgICAiaW5saW5lIjogWwogICAgICAgIHsKICAgICAgICAgICJpZCI6ICIzMDJmODBkZC00MTFlLTQ4ODYtYmNhNS1iYjFmODAxOGEwMjQiLAogICAgICAgICAgImVuY3J5cHRlZF9rZXkiOiAicm9LQWcwdDdKaTFpNDNmd3YremZ0UT09IiwKICAgICAgICAgICJ1c2FnZV9wb2xpY3kiOiAiUG9saWN5IEEiCiAgICAgICAgfQogICAgICBdCiAgICB9LAogICAgImNvbnRlbnRfa2V5X3VzYWdlX3BvbGljaWVzIjogWwogICAgICB7CiAgICAgICAgIm5hbWUiOiAiUG9saWN5IEEiLAogICAgICAgICJwbGF5cmVhZHkiOiB7CiAgICAgICAgICAibWluX2RldmljZV9zZWN1cml0eV9sZXZlbCI6IDE1MCwKICAgICAgICAgICJwbGF5X2VuYWJsZXJzIjogWwogICAgICAgICAgICAiNzg2NjI3RDgtQzJBNi00NEJFLThGODgtMDhBRTI1NUIwMUE3IgogICAgICAgICAgXQogICAgICAgIH0KICAgICAgfQogICAgXQogIH0KfQ._NfhLVY7S6k8TJDWPeMPhUawhympnrk6WAZHOVjER6M');
                       },

                       drmSystemOptions: {
                           //videoRobustness: 'SW_SECURE_DECODE',
                           //videoRobustness: "HW_SECURE_ALL",
                           videoEncryptionScheme: "cbcs",
                           //audioRobustness: "HW_SECURE_ALL",
                           audioEncryptionScheme: "cbcs",
                       },
                   });
                   hls.loadSource('https://media.axprod.net/TestVectors/Cmaf/protected_1080p_h264_cbcs/manifest.m3u8');
                   hls.attachMedia(video);
                   hls.on(Hls.Events.MEDIA_ATTACHED, function () {
                       video.muted = true;
                       video.play();
                   });
               }

         // HLS.js is not supported on platforms that do not have Media Source
         // Extensions (MSE) enabled.
         //
         // When the browser has built-in HLS support (check using `canPlayType`),
         // we can provide an HLS manifest (i.e. .m3u8 URL) directly to the video
         // element through the `src` property. This is using the built-in support
         // of the plain video element, without using HLS.js.
         else if (video.canPlayType('application/vnd.apple.mpegurl')) {
           video.src = videoSrc;
         }

         function loadVideo(){
           video.addEventListener('webkitneedkey', loadvideo, false);
         }

         function loadvideo(event) {
                       console.log("Content is protected. Preparing to create license request.");
                       console.log(event);
                       var video = event.target;
                       var initData = event.initData;
                       console.log(event.initData);
                       var contentId = extractContentId(initData);

                       console.log("Content ID is: " + contentId);

                       initData = concatInitDataIdAndCertificate(initData, contentId, certificate);

                       if (!video.webkitKeys)
                       {
                           selectKeySystem();
                           video.webkitSetMediaKeys(new WebKitMediaKeys(keySystem));
                       }

                       if (!video.webkitKeys)
                       throw "Could not create MediaKeys";

                       var keySession = video.webkitKeys.createSession("video/mp4", initData);
                       if (!keySession)
                       throw "Could not create key session";

                       keySession.contentId = contentId;
                       waitForEvent('webkitkeymessage', licenseRequestReady, keySession);
                       waitForEvent('webkitkeyadded', onkeyadded, keySession);
                       waitForEvent('webkitkeyerror', onkeyerror, keySession);
                   }
        function extractContentId(initData) {
                       // "skd://{ContentID}" -> "{ContentID}".
                       var stringarray = arrayToString(initData).replace(/^.*:\/\//, '');
                       console.log('stringarray', stringarray);
                       return stringarray;
                   }
        function arrayToString(array) {
                       var uint16array = new Uint16Array(array.buffer);
                       return String.fromCharCode.apply(null, uint16array);
                   }
        function concatInitDataIdAndCertificate(initData, id, cert) {
                       if (typeof id == "string")
                       id = stringToArray(id);
                       // layout is [initData][4 byte: idLength][idLength byte: id][4 byte:certLength][certLength byte: cert]
                       var offset = 0;
                       var buffersize = initData.byteLength + 4 + id.byteLength + 4 + cert.byteLength;
                       console.log('buffersize', buffersize);
                       var buffer = new ArrayBuffer(buffersize);
                       var dataView = new DataView(buffer);

                       var initDataArray = new Uint8Array(buffer, offset, initData.byteLength);
                       initDataArray.set(initData);
                       offset += initData.byteLength;

                       dataView.setUint32(offset, id.byteLength, true);
                       offset += 4;

                       var idArray = new Uint16Array(buffer, offset, id.length);
                       idArray.set(id);
                       offset += idArray.byteLength;

                       dataView.setUint32(offset, cert.byteLength, true);
                       offset += 4;

                       var certArray = new Uint8Array(buffer, offset, cert.byteLength);
                       certArray.set(cert);

                       return new Uint8Array(buffer, 0, buffer.byteLength);
                   }
        function loadCertificate() {
                       console.log("Requesting FPS certificate from " + certificateUrl)
                       var request = new XMLHttpRequest();
                       request.responseType = 'arraybuffer';
                       request.addEventListener('load', onCertificateLoaded, false);
                       request.addEventListener('error', onCertificateError, false);
                       request.open('GET', certificateUrl, true);
                       request.setRequestHeader('Pragma', 'Cache-Control: no-cache');
                       request.setRequestHeader("Cache-Control", "max-age=0");
                       request.send();
                   }

        function onCertificateLoaded(event) {
                       console.log("FPS certificate received.");
                       var request = event.target;
                       certificate = new Uint8Array(request.response);
                       loadVideo();
                   }

        function onCertificateError(event) {
                       window.console.error('FPS certificate request failed.');
                   }
        function stringToArray(string) {
                       var buffer = new ArrayBuffer(string.length*2);
                       var array = new Uint16Array(buffer);
                       for (var i=0, strLen=string.length; i<strLen; i++) {
                           array[i] = string.charCodeAt(i);
                       }
                       return array;
                   }
      </script>
   </body>
</html>