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>