Protected Content
Playing encrypted content by requesting a license from the Widevine server
The remote player uses the Widevine Content Decryption Module to decrypt encrypted content. This tutorial shows the process of requesting a license from the Widevine server.
For a detailed explanation of the license request flow, see the Protected Content reference.
- Source code: https://github.com/synamedia-senza/tears (ES5 version)
- Demo: https://andrewzc.net/tears/
HTML
This app will focus on playing video in the remote player, so our HTML5 app will just show a movie poster. See the source code above for the poster image .
<!DOCTYPE html>
<html>
<head>
<title>Tears of Steel</title>
<script type="text/javascript" src="./dist/bundle.js"></script>
</head>
<body style="margin: 0px; overflow: hidden;">
<img src="tears.jpg" width="1920" height="1080">
</body>
</html>
Setup
Start with a simple script that loads the client library. In the load
callback, we'll call init()
and uiReady()
. No further actions are required at this stage.
We'll define a constant with a link to an encrypted stream of the film Tears of Steel .
import { init, remotePlayer, uiReady, lifecycle } from "senza-sdk";
const ENCRYPTED_TEST_VIDEO = "https://storage.googleapis.com/wvmedia/cenc/h264/tears/tears.mpd";
window.addEventListener("load", async () => {
await init();
uiReady();
});
Playing
We'll define a key handler that responds to any button on the remote. It will load the encrypted video into the remote player and tell it to play.
document.addEventListener("keydown", async (event) => {
const currentState = await lifecycle.getState();
if (currentState === "background" || currentState === "inTransitionToBackground") {
lifecycle.moveToForeground();
} else {
await remotePlayer.load(ENCRYPTED_TEST_VIDEO);
remotePlayer.play();
}
}, false);
If you build the app (see instructions in the README) and run it in the simulator or on a device, you'll see that, it will play the first few seconds and then stop. That's because the video is encrypted, and we haven't obtained a license yet.
Getting a license
We'll follow the instructions in the Remote Player documentation to obtain a license key from the Widevine server.
We'll start by adding a listener for a license request event from the remote player:
remotePlayer.addEventListener("license-request", async (event) => {
const requestBuffer = event?.detail?.licenseRequest;
const requestBufferStr = String.fromCharCode.apply(null, new Uint8Array(requestBuffer));
const decodedLicenseRequest = window.atob(requestBufferStr);
const licenseRequestBytes = Uint8Array.from(decodedLicenseRequest, (l) => l.charCodeAt(0));
const res = await getLicenseFromServer(licenseRequestBytes.buffer);
event.writeLicenseResponse(res.code, res.responseBody);
});
In this code, we perform the following steps.
- Get the license request from the event.
- Convert the data buffer to a string.
- Unwrap the base64 coded license request.
- Create a byte array from the data.
- Get the license from the Widevine server.
- Write the license response back to the event, which sends it back to the remote player.
- Internally, the remote player sends the license to the content decryption module to get the keys for decoding the video.
Here's a helper function for making the license request. It makes a POST request with the binary data buffer to the Widevine server. The function returns an object with the response body as an array buffer.
async function getLicenseFromServer(licenseRequest) {
const response = await fetch("https://proxy.uat.widevine.com/proxy", {
"method": "POST",
"body": licenseRequest,
"headers" : {
"Content-Type": "application/octet-stream"
}
});
const code = response.status;
if (code !== 200) {
const responseBody = await response.text();
console.error(responseBody);
return {code, responseBody};
}
const responseBody = await response.arrayBuffer();
return {code, responseBody};
}
Launch the app again and give it a try. Hit the OK button, and you should see the encrypted video.
If you get past the rocket launch sequence, it's working! (It's not rocket science.)
Conclusion
Here we've learned how to set up the remote player to play protected video by requesting a license from the Widevine server.
Updated 5 months ago