Widevine License Server
Widevine License Server allows you to obtain playback licenses that let your viewers play restricted (encrypted) content protected by Google Widevine DRM.
Below you will find API documentation needed to connect to the Widevine License Server service.
Service Endpoint URL
Certificate URL
https://cloud-api.redge.media/api-services/ls/wv/certificate
License URL
https://cloud-api.redge.media/api-services/ls/wv
NOTE
Widevine CDM (client component from the Widevine DRM ecosystem) usually needs 2 requests to the DRM Proxy (and thus to the License Server) - 1st for the certificate and 2nd for the actual license. Remember that these 2 requests should use different endpoint URLs as listed above. To differentiate these you can check the data length. For a request with data length shorter than 50 bytes the certificate URL should be used. Otherwise use the licence URL. See the curl_init
call in DRM Proxy Example
Authentication
Authentication is required for each request to the License Server. In order to authenticate a request, an HTTP Header Auth-Code
should be provided with the value of Authentication Code obtained from Redge Media Cloud panel.
See Authentication for details.
API description
All parameters to the Widevine License Server should be given as HTTP headers.
IMPORTANT
All requests to Widevine License Server API endpoint URL should be done using HTTP POST method. GET is not supported.
HTTP request headers
Content-type
- Type:
String
- Required: Yes
- Value: always
application/octet-stream
- Example:
Content-type: application/octet-stream
Content-length
- Type:
Integer
- Required: Yes
- Value: length of HTTP body in bytes (as defined in HTTP protocol specification)
- Example:
Content-length: 892
Auth-Code
- Type:
String
- Required: Yes
- Value: Authentication Code - see Authentication
- Example:
Auth-Code: ef9f846b-b446-414f-8cb6-51faca43d4f2
Asset-Default-Key
- Type:
base64 String
- Required: Yes
- Value: Base64 encoded AES-128 content key (128-bit width). This is dafault content key value for every KID found in the License Request's PSSH.
- Example:
Asset-Default-Key: c192e5e92f08c33115f393bae5ae7c79
key
- DEPRECATED
- Type:
base64 String
- Required: Yes
- Value: Base64 encoded AES-128 content key (128-bit width). This is default content key value for every KID found in the License Request's PSSH.
IMPORTANT: This header is now deprecated. UseAsset-Default-Key
instead.
Output-HDCP
- Type:
Integer
- Required: No
- Default:
0
- Value: Required output HDCP Level (for every KID entry in the license).
value name 0
HDCP_NONE 1
HDCP_V1 2
HDCP_V2 3
HDCP_V2_1 4
HDCP_V2_2 - Example:
Output-HDCP: 1
Output-Secure-Path
- Type:
Boolean
- Required: No
- Default:
false
- Value:
true
|false
- Description: Enables output Secure Path requirement (for every KID entry in the license)
- Example:
Output-Secure-Path: true
Policy-License-Duration
- Type:
Integer
- Required: No
- Default:
0
- Value: The license window (license validity period duration) in seconds.
0
indicates unlimited window. - Example:
Policy-License-Duration: 3600
Policy-Playback-Duration
- Type:
Integer
- Required: No
- Default:
0
- Value: The viewing window (allowed playback duration) in seconds from the start of playback (not to exceed the license duration).
0
indicates unlimited window. - Example:
Policy-Playback-Duration: 3600
Policy-Persist
- Type:
Boolean
- Required: No
- Default:
false
- Value:
true
for persistent license,false
for non-persistent license - Description: Determine license type to be issued.
- Example:
Policy-Persist: true
Full example
Example of the complete minimal set of required HTTP headers:
Content-type: application/octet-stream
Content-length: 892
Auth-Code: ef9f846b-b446-414f-8cb6-51faca43d4f2
Asset-Default-Key: c192e5e92f08c33115f393bae5ae7c79
HTTP Body
HTTP body should contain only the license Request acquired from client message - without any changes.
Response
HTTP Code | Description |
---|---|
200 OK | Generated License Response or Error Response (to be forwarded to client)
|
400 Bad Request | Incomplete/broken request. Possible problems:
|
401 Unauthorized | Missing/incorrect Auth-Code header (response could not follow CORS) |
5xx | Unspecified server errors |
DRM Proxy Example
Below you can find a very simple DRM Proxy implementation in PHP.
WARNING
In this example the key is passed as a query string parameter to the DRM Proxy. In your implementation you should always keep the key in a safe database and pass publicly only a video identifier instead of the key itself.
View the code
<?php
class WVLSProxy {
const ERROR_REQUEST = 999;
const ERROR_AUTHORIZATION = 998;
const CURL_CONNECT_TIMEOUT = 5;
const CURL_TIMEOUT = 5;
private $lsurl;
private $lscerturl;
private $authCode;
public function __construct($url, $certUrl, $authCode) {
$this->lsurl = $url;
$this->lscerturl = $certUrl;
$this->authCode = $authCode;
}
public function getLicense($challenge, $key) {
$challengeLength = strlen($challenge);
$headers = array();
$headers[] = 'Content-Length: ' . $challengeLength;
$headers[] = 'Content-Type: application/octet-stream';
$headers[] = 'Policy-Persist: false';
$headers[] = 'key: ' . base64_encode(hex2bin($key));
$headers[] = 'Auth-Code: ' . $this->authCode;
$curl = curl_init($challengeLength > 50 ? $this->lsurl : $this->lscerturl);
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, self::CURL_CONNECT_TIMEOUT);
curl_setopt($curl, CURLOPT_TIMEOUT, self::CURL_TIMEOUT);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($curl, CURLOPT_HEADER, true);
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
curl_setopt($curl, CURLOPT_POSTFIELDS, $challenge);
$result = curl_exec($curl);
$info = curl_getinfo($curl);
curl_close($curl);
if ($info['http_code'] == 401) {
return self::ERROR_AUTHORIZATION;
} else if ($info['http_code'] != 200) {
return self::ERROR_REQUEST;
}
return substr($result, $info["header_size"], $info["size_download"]);
}
}
// ***********************
define('LS_WV_URL', 'https://cloud-api.redge.media/api-services/ls/wv');
define('LS_WV_CERT_URL', 'https://cloud-api.redge.media/api-services/ls/wv/certificate');
$authCode = urldecode($_GET['auth_code']);
$challenge = file_get_contents('php://input');
// implement here:
// - your BO authorization
// - checking your business rules (e.g. paid access)
// - getting cipher data (key, id, etc.) from your
//
$ls = new WVLSProxy(LS_WV_URL, LS_WV_CERT_URL, $authCode);
$response = $ls->getLicense($challenge, $_GET['key']);
header('Access-Control-Allow-Origin: *');
if(is_int($response) && $response == WVLSProxy::ERROR_AUTHORIZATION) {
header("HTTP/1.0 401 Unauthorized");
header("Content-Type: text/plain");
} else if (is_int($response) && $response == WVLSProxy::ERROR_REQUEST) {
header("HTTP/1.0 400 Bad Request");
header("Content-Type: text/plain");
} else {
header("Content-Type: application/octet-stream");
header("Content-Length: " . strlen($response));
echo $response;
}