
VUDRM DOCUMENTATION¶
The VUDRM platform provides a variety of services that allow you to protect your OTT video content; It has device agnostic support for Microsoft PlayReady, Google Widevine, and FairPlay Streaming and is supported by all major players.
If you have any questions or need assistance in using this platform, please contact info@vualto.com.
Contents:¶
USER GUIDE¶
Our VUDRM Admin page gives you the tools you will need to test our DRM services with your content. If you need assistance or clarification in relation to anything in this guide, please contact support@vualto.com.
Login¶
To access the VUDRM Admin site, please go to the Admin Login page - login using the credentials on your onboarding documentation.

Dashboard¶
Once you have successfully logged in, you will be redirected to the Dashboard
page.

The dashboard will show you an overview of all DRM licenses served with your content.
By clicking on the Range
dropdown field, you can set custom time frames to get an overview for that time period.

Configuration¶
Client Info¶
The Client Info
section contains the details of how your account is configured, such as which DRM providers are enabled and which URLs have been set to retrieve each service’s DRM licenses.

If you require amendments to your account, please contact support@vualto.com.
Client Info - Edit default token policies¶
At the bottom of the Client Info
page, you can view each DRM service’s default token policy. This can be edited to give more control over how a service will act by default.

Clicking Edit Token Policy
will load the JSON editor.

Please ensure that the JSON has been entered correctly, otherwise you will be informed of an error and will not be able to save the amended policy.
When the amends are complete, click Close
and then Save
.
Each DRM service has limitations to how their policy can be altered. Please read the links below for more details:
VUDRM Token¶
In this section, you can create, validate, decrypt, encode, and decode VUDRM Tokens.

There are a variety of templates to choose from which will automatically populate the Policy
text box, please ensure that you replace all default values (such as {"polend":"DD-MM-YYYY HH:MM:SS"}
to {"polend":"09-05-2021 12:30:00"}
). When the default values have been changed, you can then generate a token with those parameters.
The buttons on the page do the following:
Generate
- This will generate a token to include the policy you have enteredValidate Policy
- This will ensure the policy is validValidate Token
- Validate the token createdDecrypt Token
- Decrypts the tokenEncode Token
- URL Encodes the tokenDecode Token
- URL Decodes the token
Once a token has been generated, a new button will appear - Use Token To Test Your Stream
.

Clicking Use Token To Test Your Stream
will load the Test Your Stream
player with the generated token in the VUDRM Token
text box.
For an in depth guide of how our tokens work - please refer to the VUDRM Token documentation.
VUDRM Encryption Keys¶
Within this section, you can call the CPIX Key Provider
to fetch VUDRM Encryption Keys in CPIX XML document format as well as the CPIX keys as JSON which is generated at the same time, both of which are available to copy.

To create an Encryption key, do the following:
- Select which Key Provider API you wish to use by using the
Key Provider API
- Select which DRM Providers by changing the relevant providers button to
YES
- Enter a relevant name in the
Content ID
text field - Click
Generate
- Click
Copy
to copy the newly created key
We also have our Legacy JSON Key provider
(available on the Key Provider API
dropdown) to use if your product requires it. However, we recommend you use the CPIX Key Provider
whenever possible.
For more information - please refer to our Encryption Key Provision documentation.
Test Your Stream¶
Integrated into the VUDRM Admin site is our internal demo player which can be used for testing your streams.

A VUDRM Token with an open policy is loaded into the VUDRM Token
field used by the player. You can copy and paste a token you created in the VUDRM Token
section of the Info
page. This can be used to test content with your variation of the token. Alternatively, clicking the Use Token To Test Your Stream
button within the VUDRM Token
page will automatically enter that token into the Token field used by the player.
The fields are as follows:
VUDRM Token
- Token used by the player for the streamDASH Stream URL
- DASH stream to testHLS Stream URL
- HLS stream to test

If you select the Use Custom License Server URLs?
button, you will be presented with three fields available to edit:
Widevine License Server URL
Playready License Server URL
Fairplay License Server URL
If these custom fields have values, they will override the default License Server URLs set within the client configuration.
Clicking Load Player
will then load the content with the conditions of the filled in fields.

Health¶
The Health
page gives an overview of the general health for each DRM or DRM related service we provide. If one of these services are down, it will be down throughout all regions.

By default, the page is set to auto-refresh every 60 seconds, but you can toggle this to No
if not required.
The world map contains region specific services. A regions health colour will change in the following circumstances:
GREEN
- All services are operationalAMBER
- 1 or more services are not operationalRED
- All services are not operational
DEVELOPER DOCUMENTATION¶
The purpose of this documentation is to explain the usage and functionality of the various VUDRM services. If you have any questions or need assistance in using this platform, please contact info@vualto.com.
PACKAGING GUIDES¶
AWS Media Services¶
In order to use our SPEKE API you will need to set up an API gateway that adds the header API-KEY
with your API key and with your client name in place of client-name
in “https://speke.vudrm.tech/client-name/speke”. For more information on how to setup the API Gateway see the API Gateway section.
Media Packager¶
- Log into the AWS console
- Either type “Media Packager” into the search box and then select “MediaPackager” or open the “Services” drop down in the top left of the screen and select “MediaPackager” from under the “Media Services” header.
- To create a channel press the “Create” button in the top right corner, enter an ID for the channel and press create.
- Press the “Add/edit endpoints” button
- Press the “Add” button to create a new endpoint
- You can also add a description of the endpoint if needed
- Use DASH-ISO if you want to use Playready or Widevine DRM
- Use Apple HLS if you want to use Fairplay DRM
- If you wish to use Widevine DRM use “edef8ba9-79d6-4ace-a3c8-27dcd51d21ed” as the System ID (only if output group is DASH ISO)
- If you wish to use Playready DRM use “9a04f079-9840-4286-ab92-e65be0885f95” as the System ID (only if output group is DASH ISO)
- If you wish to use Fairplay DRM use “94ce86fb-07ff-4f43-adb8-93d2fa968ca2” as the System ID (only if output group is APPLE HLS)
- Open “https://admin.vudrm.tech/” in a new Google Chrome tab and log in, if you do not have a log in please contact support@vualto.com
- Select “Configuration” from the left hand navigation menu and then select the “Test Your Stream” tab
- Paste the endpoints URL into the “DASH Stream URL” box
- Press “Load Player”
Media Convert¶
- Log into the AWS console
- Either type “S3” into the search box and then select “S3” or open the “Services” drop down in the top left of the screen and select “S3” from under the “Storage” header.
- Press the “Create folder” button
- Enter a name for the folder (Remember this as you will need it later)
- Press the “Save” button
- Log into the AWS console in a new tab
- Either type “Media Convert” into the search box and then select “MediaConvert” or open the “Services” drop down in the top left of the screen and select “MediaConvert” from under the “Media Services” header.
- If you cannot see the “Create job” button ensure you can see the “Recent jobs” list by clicking the icon with 3 lines in the top left of the screen and then selecting “jobs”
- If you are using Widevine or Playready select “DASH ISO”
- If you are using Fairplay select “Apple HLS”
- Select your bucket from the “S3 bucket” drop down
- Select the folder you created earlier from the “Location” drop down (Should be something like “YOUR-BUCKET/YOUR-FOLDER-NAME/”)
- Confirm selection by pressing “Choose” button
- Press the toggle next to “DRM Encryption” to add the DRM information.
- Add a “Resource ID”
- Set the “System ID” to “edef8ba9-79d6-4ace-a3c8-27dcd51d21ed”
- Set the “URL” to that of your API gateway
- Press the toggle next to “DRM Encryption” to add the DRM information.
- Add a “Resource ID”
- Set the “System ID” to “9a04f079-9840-4286-ab92-e65be0885f95”
- Set the “URL” to that of your API gateway
- Press the toggle next to “DRM Encryption” to add the DRM information.
- Set “Encryption-method” to “SAMPLE-AES”
- Set “Key provider type” to “SPEKE”
- Set “Initialization vector in manifest” to “Exclude”
- Add a “Resource ID”
- Set the “System ID” to “94ce86fb-07ff-4f43-adb8-93d2fa968ca2”
- Set the “URL” to that of your API gateway
- Set the “Constant initilization vector” to “00000000000000000000000000000000”
- Press the “Add output” button.
- Set the “Name modifier” for “Output 1” to “video”
- Set the “Name modifier” for “Output 2” to “audio”
- Select “Output 1” from the “Output groups” section on the left of the screen
- Select “Video” if not already selected and set the “Bitrate (bits/s)” to “5000000”
- Select “Audio 1” and click the “Remove audio” button
- Select “Output 2” from the “Output groups” section on the left of the screen
- Select “Video” if not already selected and click the “Remove video” button
- Press the “Add output” button.
- Set the “Name modifier” for “Output 1” to “video”
- Set the “Name modifier” for “Output 2” to “audio”
- Select “Output 1” from the “Output groups” section on the left of the screen
- Select “Video” if not already selected and set the “Bitrate (bits/s)” to “5000000”
- Select “Audio 1” and click the “Remove audio” button
- Select “Output 2” from the “Output groups” section on the left of the screen
- Select “Video” if not already selected and click the “Remove video” button
- Set the “Name modifier” for “Output 1” to “video-audio”
- Select “Output 1” from the “Output groups” section on the left of the screen
- Select “Video” if not already selected and set the “Bitrate (bits/s)” to “5000000”
- Click “Settings” under “Job settings”
- Select an appropriate IAM from the dropdown under “IAM role”
- On the “Job summary” page you can press the “Refresh” button to check if your job is complete. (You will know it has finished once there is a finish time)
- If you selected “DASH ISO” as the output group, the file should be called “manifest.mpd”
- If you selected “Apple HLS” as the output group, the file should be called “manifest.m3u8”
- Open “https://admin.vudrm.tech/” in a new Google Chrome tab and log in, if you do not have a log in please contact support@vualto.com
- Select “Configuration” from the left hand navigation menu and then select the “Test Your Stream” tab
- Return to your S3 tab and copy the link from the bottom of the page and paste it into “DASH Stream URL” box, it should look something like “https://s3-eu-west-1.amazonaws.com/YOUR-BUCKET/YOUR-FOLDER-NAME/manifest.mpd”
- Press “Load Player”
API Gateway¶
After navigating to API Gateway Service in AWS and clicking Create API
, simply click Import
for a REST API.
Then ensure the protocol is set to REST
and Import from Swagger or Open API 3
is selected.
You can then paste the JSON below and click Import
.
{
"swagger": "2.0",
"info": {
"title": "speke proxy"
},
"basePath": "/speke",
"schemes": [
"https"
],
"paths": {
"/": {
"post": {
"produces": [
"application/json"
],
"parameters": [
{
"name": "API-KEY",
"in": "header",
"required": false,
"type": "string"
}
],
"responses": {
"200": {
"description": "200 response"
}
}
}
}
}
}
Once imported you will need to set up the post method by clicking Set up now
.
- Set
Integration Type
to beHTTP
- Check
Use HTTP Proxy integration
- Set the
Endpoint URL
to behttps://speke.vudrm.tech/<your-client-name>/speke
With these options set click Create
.
Then select Integration Request
and under the HTTP Headers
section add a header with the name API-KEY
and Mapped from
set to '<your-api-key>'
ensuring to include the single quotes surrounding your API key.
Now in the Actions
dropdown select Deploy API
.
From the Deployment stage
dropdown select [New Stage]
, and enter an appropriate name for the stage in the box below.
You will then be sent to the stage editor for the stage you have just created and deployed to. On this page there will be your Invoke URL
. This is the URL that can be used by other AWS services to make requests to our SPEKE API.
NGINX VOD Module¶
Our CPIX Key Provider API has an endpoint that is dedicated to providing the key information needed to integrate with Kaltura’s NGINX VOD Module.
You can request encryption keys in the format required by the NGINX VOD Module by performing the following command.
curl --location --request GET 'https://cpix.vudrm.tech/v1/keys/<client-name>/nginx/<content-id>' \
--header 'api-key: <api-key>'
This will return the following response:
[
{
"pssh": [
{
"data": "<base64 encoded PlayReady PSSH data>",
"uuid": "9a04f079-9840-4286-ab92-e65be0885f95"
},
{
"data": "<base64 encoded Widevine PSSH data>",
"uuid": "edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"
}
],
"key": "<base64 encoded encryption key>",
"key_id": "<base64 encoded key id>",
"iv": "<base64 encoded iv>"
}
]
Below you can find a full example of an NGINX config file that will integrate the VOD module with VUDRM. This example is a modified version of the example given by The New York Times in their repo containing the Docker files necessary to run the VOD module as a Docker container.
This modifications to the example config are as follows: You will need the addition of this to activate just in time DRM on all endpoints.
vod_drm_enabled on;
vod_drm_request_uri "/<client-name>/nginx/<content-id>";
vod_drm_upstream_location /drm;
The addition of this proxy pass inorder to add your api key to the request made to our CPIX Key Provider API.
location /drm {
internal;
proxy_pass "https://cpix.vudrm.tech/v1/keys";
proxy_set_header API-KEY <api-key>;
}
This will give you support for DASH with Widevine and PlayReady. To support HLS with Fairplay you will need to add the following to your hls endpoint.
vod_hls_encryption_method sample-aes;
vod_hls_encryption_key_uri "skd://fairplay-license.vudrm.tech/v2/license/<content-id>";
vod_hls_encryption_key_format "com.apple.streamingkeydelivery";
vod_hls_encryption_key_format_versions "1";
NGINX Config - Full Example¶
worker_processes auto;
events {
use epoll;
}
http {
log_format main '$remote_addr $remote_user [$time_local] "$request" '
'$status "$http_referer" "$http_user_agent"';
access_log /dev/stdout main;
error_log stderr debug;
default_type application/octet-stream;
include /usr/local/nginx/conf/mime.types;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
vod_mode local;
vod_metadata_cache metadata_cache 16m;
vod_response_cache response_cache 512m;
vod_last_modified_types *;
vod_segment_duration 9000;
vod_align_segments_to_key_frames on;
vod_dash_fragment_file_name_prefix "segment";
vod_hls_segment_file_name_prefix "segment";
vod_drm_enabled on;
vod_drm_request_uri "/vualto-demo/nginx/test-nginx";
vod_drm_upstream_location /drm;
vod_manifest_segment_durations_mode accurate;
open_file_cache max=1000 inactive=5m;
open_file_cache_valid 2m;
open_file_cache_min_uses 1;
open_file_cache_errors on;
aio on;
server {
listen 80;
server_name localhost;
root /opt/static;
location ~ ^/videos/.+$ {
autoindex on;
}
location /hls/ {
vod hls;
vod_hls_encryption_method sample-aes;
vod_hls_encryption_key_uri "skd://fairplay-license.vudrm.tech/v2/license/test-nginx";
vod_hls_encryption_key_format "com.apple.streamingkeydelivery";
vod_hls_encryption_key_format_versions "1";
alias /opt/static/videos/;
add_header Access-Control-Allow-Headers '*';
add_header Access-Control-Allow-Origin '*';
add_header Access-Control-Allow-Methods 'GET, HEAD, OPTIONS';
}
location /thumb/ {
vod thumb;
alias /opt/static/videos/;
add_header Access-Control-Allow-Headers '*';
add_header Access-Control-Allow-Origin '*';
add_header Access-Control-Allow-Methods 'GET, HEAD, OPTIONS';
}
location /dash/ {
vod dash;
alias /opt/static/videos/;
add_header Access-Control-Allow-Headers '*';
add_header Access-Control-Allow-Origin '*';
add_header Access-Control-Allow-Methods 'GET, HEAD, OPTIONS';
}
location /drm {
internal;
proxy_pass "https://cpix.vudrm.tech/v1/keys";
proxy_set_header API-KEY <api-key>;
}
}
}
Shaka Packager¶
Setup using Docker¶
Setup Shaka Packager using the commands below:
# Pull and run shaka packager
docker pull google/shaka-packager
docker run -v /<file_path_to_content>/:/media -it --rm google/shaka-packager
# Test that setup is correct by getting stream info
packager input=/media/<name_of_content>.mp4 --dump_stream_info
If successful, the expected output of the stream dump should look like this:
File "/media/test.mp4":
Found 1 stream(s).
Stream [0] type: Video
codec_string: avc1.640028
time_scale: 12288
duration: 17078784 (1389.9 seconds)
is_encrypted: false
codec: H264
width: 1920
height: 810
pixel_aspect_ratio: 1:1
trick_play_factor: 0
nalu_length_size: 4
Packaging completed successfully.
Get CPIX Documents¶
CPIX Documents are required to give Shaka Packager the necessary information in order to encrypt your media.
You can retrieve these documents from the VUDRM Admin site.
For more information, please view our documentation on CPIX Document Requests and our CPIX Key Provider API.
Below is an example of a CPIX Document with all DRM Providers
selected.
<?xml version="1.0" encoding="UTF-8"?>
<cpix:CPIX xmlns:pskc="urn:ietf:params:xml:ns:keyprov:pskc" xmlns:xsi="urn:ietf:params:xml:ns:keyprov:pskc" xmlns:cpix="urn:dashif:org:cpix" xsi:schemaLocation="urn:dashif:org:cpix cpix.xsd">
<cpix:ContentKeyList>
<cpix:ContentKey kid="11111111-1111-1111-1111-111111111111" explicitIV="MjlkMzNlYzVjYWIyZmRmZg==" commonEncryptionScheme="cenc">
<cpix:Data>
<pskc:Secret>
<pskc:PlainValue>3wFFjPspsydY/t2uSPeE6w==</pskc:PlainValue>
</pskc:Secret>
</cpix:Data>
</cpix:ContentKey>
</cpix:ContentKeyList>
<cpix:DRMSystemList>
<cpix:DRMSystem kid="11111111-1111-1111-1111-111111111111" systemId="9a04f079-9840-4286-ab92-e65be0885f95">
<cpix:PSSH><PlayReady_PSSH></cpix:PSSH>
</cpix:DRMSystem>
<cpix:DRMSystem kid="22222222-2222-2222-22222-22222222222" systemId="edef8ba9-79d6-4ace-a3c8-27dcd51d21ed">
<cpix:PSSH><Widevine_PSSH></cpix:PSSH>
</cpix:DRMSystem>
<cpix:DRMSystem kid="33333333-3333-3333-33333-33333333333" systemId="94ce86fb-07ff-4f43-adb8-93d2fa968ca2">
<cpix:URIExtXKey><FairPlay_URIExtXKey</cpix:URIExtXKey>
<cpix:HLSSignalingData playlist="master"><master_playlist_value></cpix:HLSSignalingData>
<cpix:HLSSignalingData playlist="media"><media_playlist_value></cpix:HLSSignalingData>
</cpix:DRMSystem>
</cpix:DRMSystemList>
<cpix:ContentKeyUsageRuleList></cpix:ContentKeyUsageRuleList>
</cpix:CPIX>
Package your content¶
Now you have retrieved the relevant documentation, you can package content with Shaka Packager.
The values from the CPIX documents will need to be copied and pasted to the corresponding section in the examples below.
For more information, please refer to the documentation from Google on Shaka Packager.
Below is an example of how we can send a request to Shaka Packager to encrypt and package our content with Widevine and PlayReady DRM.
Please note, the Widevine_PSSH
from the CPIX Document is in Base64 format. Shaka Packager requires this to be in Hex format which can be achieved by running the following command echo '<Widevine_PSSH>' | od -A n -t x1 | sed 's/ *//g'
:
packager \
in=/media/<name-of-content>.mp4,stream=audio,output=/media/<name-of-output-content>.mp4,drm_label=AUDIO \
in=/media/<name-of-content>.mp4,stream=video,output=/media/<name-of-output-content>.mp4,drm_label=VIDEO \
--enable_raw_key_encryption \
--keys label=AUDIO:key_id=<key_id_hex_value>:key=<content_key_hex_value>,label=VIDEO:key_id=<key_id_hex_value>:key=<content_key_hex_value> \
--pssh <Widevine_PSSH> \
--protection_systems Widevine,PlayReady \
--mpd_output /media/<name-of-manifest>.mpd
Below is an example of how we can send a request to Shaka Packager to encrypt and package our content with FairPlay DRM.
packager \
in=media/<name-of-content>.mp4,stream=audio,output=media/<name-of-output-content>.mp4,drm_label=AUDIO \
in=media/<name-of-content>.mp4,stream=video,output=media/<name-of-output-content>.mp4,drm_label=VIDEO \
--protection_scheme cbcs \
--enable_raw_key_encryption \
--keys label=AUDIO:key_id=<key_id_hex_value>:key=<content_key_hex_value>,label=VIDEO:key_id=<key_id_hex_value>:key=<content_key_hex_value> \
--protection_systems FairPlay \
--iv <iv_hex_value> \
--hls_master_playlist_output media/<name-of-manifest>.m3u8 \
--hls_key_uri <fairplay_laurl_value>
Testing packaged content¶
Upload your packaged files and manifest to AWS S3 or an equivalent hosting service. You can now test your newly packaged content with our player on the VUDRM Admin site.
For more information, please view our documentation on how to Test Your Stream.
You can confirm a relevant license request has been made by checking the network tab of the browsers dev tools.
Unified Streaming¶
The recommended approach to integrate with USP will depend on your exact use case. If you will be requesting CPIX documents regularly, e.g. for use with live content, it is recommended to use our CPIX Edge Reverse Proxy to retrieve CPIX documents. If you will be making less frequent calls for CPIX documents, e.g. for one time packaging, you can use either our CPIX Edge Reverse Proxy or request CPIX documents from our CPIX Key Provider API directly.
If you would prefer to not use a CPIX document at all you can also use the JSON formatted response.
mp4split¶
A CPIX document can be used in the below mp4split commands to encrypt content. For a full list CPIX mp4split options see Unified Streamings full documentation.
You can encrypt content with a CPIX document stored locally by doing the following commands.
curl -XGET -H "API-KEY: <api-key>" \
"https://cpix.vudrm.tech/v1/cpix/<client-name>/<content-id>" \
> drm.cpix
mp4split --license-key=$USP_LICENSE_KEY -o $ismName \
--cpix=drm.cpix \
video.ismv audio.isma
Or you can reference our CPIX Edge Reverse Proxy directly in the mp4split command as follows.
mp4split --license-key=$USP_LICENSE_KEY -o $ismName \
--cpix="http://local-cpix-proxy/v1/cpix/<client-name>/<content-id>" \
video.ismv audio.isma
Keys requested in JSON format can be used in the below mp4split to encrypt content. For more information about using mp4split you can vist Unified Streamings full documentation.
mp4split --license-key=$USP_LICENSE_KEY -o $ismName \
--iss.key=$key_id_hex:$content_key_hex \
--iss.license_server_url="https://playready-license.vudrm.tech/rightsmanager.asmx" \
--widevine.key=$key_id_hex:$content_key_hex \
--widevine.license_server_url="https://widevine-license.vudrm.tech/proxy" \
--widevine.drm_specific_data=$widevine_drm_specific_data \
--hls.client_manifest_version=4 \
--hls.key=:$content_key_hex \
--hls.key_iv=$iv_hex \
--hls.license_server_url=$fairplay_laurl \
--hls.playout=sample_aes_streamingkeydelivery \
video.ismv audio.isma
Wowza¶
The encryption keys provided by the Key Provider APIs are compatible with Wowza.
The following steps detail how to configure Wowza to support VUDRM:
Configure Wowza Stream Settings¶
- Stop Wowza appending the session ID to the license server URL:
- Change the
EXT-X-VERSION
for HLS streaming to5
by setting thecupertinoExtXVersion
value:
Manually Set VUDRM Settings¶
This method of integrating VUDRM with Wowza uses key files:
NB: Widevine, PlayReady, and Fairplay key values can be set in the same file.
An example key file is:
mpegdashstreaming-cenc-key-id: MT8jItzhV+ay+3QEtoyIxQ==
mpegdashstreaming-cenc-content-key: EcZ9tM1adkoleC1hwFlpQw==
mpegdashstreaming-cenc-algorithm: AESCTR
mpegdashstreaming-cenc-keyserver-widevine: true
mpegdashstreaming-cenc-keyserver-widevine-system-id: edef8ba9-79d6-4ace-a3c8-27dcd51d21ed
mpegdashstreaming-cenc-keyserver-widevine-pssh-data: Igp3b3d6YS1kZW1vSOPclZsG
mpegdashstreaming-cenc-keyserver-playready: true
mpegdashstreaming-cenc-keyserver-playready-system-id: 9a04f079-9840-4286-ab92-e65be0885f95
mpegdashstreaming-cenc-keyserver-playready-license-url: https://playready-license.vudrm.tech/rightsmanager.asmx
mpegdashstreaming-cenc-keyserver-playready-checksum: Mv5YB5EhHKw=
cupertinostreaming-aes128-method: SAMPLE-AES
cupertinostreaming-aes128-url: skd://fairplay-license.vudrm.tech/license/wowza-demo
cupertinostreaming-aes128-key: 24F79075974B8E1BC8AF576925B8458F
cupertinostreaming-aes128-iv: A140A11A450DBC04E67F39B850C13D41
cupertinostreaming-aes128-iv-include-in-chunklist: false
cupertinostreaming-aes128-key-format: com.apple.streamingkeydelivery
cupertinostreaming-aes128-key-format-version: 1
The values are:
mpegdashstreaming-cenc-key-id
: Thekey_id_big
value from the CENC response.mpegdashstreaming-cenc-content-key
: Thecontent_key
value from the CENC response.mpegdashstreaming-cenc-algorithm
: Must be set toAESCTR
.mpegdashstreaming-cenc-keyserver-widevine
: Must be set totrue
.mpegdashstreaming-cenc-keyserver-widevine-system-id
: Must be set toedef8ba9-79d6-4ace-a3c8-27dcd51d21ed
mpegdashstreaming-cenc-keyserver-widevine-pssh-data
: Thewidevine_drm_specific_data
value from the CENC Response.mpegdashstreaming-cenc-keyserver-playready
: Must be set totrue
.mpegdashstreaming-cenc-keyserver-playready-system-id
: Must be set to9a04f079-9840-4286-ab92-e65be0885f95
mpegdashstreaming-cenc-keyserver-playready-license-url
: Theplayready_laurl
value from the CENC Response.mpegdashstreaming-cenc-keyserver-playready-checksum
: Thechecksum
value from the CENC Responsecupertinostreaming-aes128-method
: Must be set toSAMPLE-AES
.cupertinostreaming-aes128-url
: Thelaurl
value from the Fairplay Responsecupertinostreaming-aes128-key
: Thekey_hex
value from the Fairplay Responsecupertinostreaming-aes128-iv
: Theiv_hex
value from the Fairplay Responsecupertinostreaming-aes128-iv-include-in-chunklist
: Must be set tofalse
.cupertinostreaming-aes128-key-format
: Must be set tocom.apple.streamingkeydelivery
.cupertinostreaming-aes128-key-format-version
: Must be set to1
.
VUALTO Wowza DRM API¶
The VUALTO Wowza DRM API is a small Docker web API application which runs on the Wowza server, allowing the provisioning of keyfiles through a simple REST interface. The VUALTO Wowza DRM API has been tested with Wowza Streaming Engine version 4.7.7.
In this example, the API is exposed on port 9000 and the API key is set to some GUID:
docker run -d --restart always -p 9000:80 \
-e WOWZADRMAPI_APIKEY=93640045-31f1-45c0-aa0d-b5682d7c9ac8 \
-e WOWZADRMAPI_KEYFILESPATH=/mnt/keyfiles \
-e WOWZADRMAPI_GRANDCENTRALURL=https://config.vudrm.tech/v1/clients \
-e WOWZADRMAPI_KEYPROVIDERURL=https://keyprovider.vudrm.tech \
-v [wowza-install-dir]/keys:/mnt/keyfiles \
--name wowza-drm-api vualto/wowza-drm-api:1.0.0
<streamid>
must match the name of the Stream to protect. This will be the name of the .key file created on the server.
Details about what name should be used for the key file can be found here: https://www.wowza.com/docs/how-to-secure-mpeg-dash-streaming-using-common-encryption-cenc#dash_cenc
All requests to the API must be authorised with a x-api-key
header, whose value must match that set by the WOWZADRMAPI_APIKEY
environment variable. A missing or mismatched x-api-key
header will yield a 401 Unauthorized
status.
GET /api/keyfile/<streamid>
Returns the contents of the keyfile for that Wowza stream if it exists, otherwise a 404.
POST /api/keyfile/<streamid>
Create or overwrite the keyfile for the Wowza stream.
Keys can be supplied either automatically using the Vualto DRM platform or manually.
Example body for manual keys:
{
"cenc": {
"keyid": "RNpS70UjCY5oVHd+4yJoHQ==",
"contentkey": "lnA03mLuxnL3toiRMxV4Zw=="
},
"playready": {
"laurl": "https://playready-license.vudrm.tech/rightsmanager.asmx",
"checksum": "pcTs6CEp98A="
},
"widevine": {
"psshdata": "IiRkN2EyOTk1Ni0yOTgwLTQzMzgtYjYyNy04N2MxZjA3OWUwOTFI49yVmwY="
},
"fairplay": {
"key": "CBF76BB43B9A54254A5FC20074A3A53B",
"iv": "1A3F2AA76D84742DD123E3E50E7BC681",
"laurl": "skd://fairplay-license.vudrm.tech/license/d7a29956-2980-4338-b627-87c1f079e091"
}
}
Example response of the created keyfile:
mpegdashstreaming-cenc-key-id: RNpS70UjCY5oVHd+4yJoHQ==
mpegdashstreaming-cenc-content-key: lnA03mLuxnL3toiRMxV4Zw==
mpegdashstreaming-cenc-algorithm: AESCTR
mpegdashstreaming-cenc-keyserver-playready: true
mpegdashstreaming-cenc-keyserver-playready-system-id: 9a04f079-9840-4286-ab92-e65be0885f95
mpegdashstreaming-cenc-keyserver-playready-license-url: https://playready-license.vudrm.tech/rightsmanager.asmx
mpegdashstreaming-cenc-keyserver-playready-checksum: pcTs6CEp98A=
mpegdashstreaming-cenc-keyserver-widevine: true
mpegdashstreaming-cenc-keyserver-widevine-system-id: edef8ba9-79d6-4ace-a3c8-27dcd51d21ed
mpegdashstreaming-cenc-keyserver-widevine-pssh-data: IiRkN2EyOTk1Ni0yOTgwLTQzMzgtYjYyNy04N2MxZjA3OWUwOTFI49yVmwY=
cupertinostreaming-aes128-method: SAMPLE-AES
cupertinostreaming-aes128-url: skd://fairplay-license.vudrm.tech/license/d7a29956-2980-4338-b627-87c1f079e091
cupertinostreaming-aes128-key: CBF76BB43B9A54254A5FC20074A3A53B
cupertinostreaming-aes128-iv: 1A3F2AA76D84742DD123E3E50E7BC681
cupertinostreaming-aes128-iv-include-in-chunklist: true
cupertinostreaming-aes128-key-format: com.apple.streamingkeydelivery
cupertinostreaming-aes128-key-format-version: 1
cenc
is required with either playready
or widevine
.
Example body using the VUALTO DRM key provider:
{
"contentId": "mycontentid",
"client": "my-client-name",
"clientApiKey": "8f30c7d2-a9b9-474e-907b-97a190abd6c4",
"drmJson": "{\"drm_provider\": \"VUALTO\"}"
}
Example response:
mpegdashstreaming-cenc-key-id: jQGIareSAu0jZ5MYUQBQlw==
mpegdashstreaming-cenc-content-key: kMvY/VcE9FnWe61SKUnKYw==
mpegdashstreaming-cenc-algorithm: AESCTR
mpegdashstreaming-cenc-keyserver-playready: true
mpegdashstreaming-cenc-keyserver-playready-system-id: 9a04f079-9840-4286-ab92-e65be0885f95
mpegdashstreaming-cenc-keyserver-playready-license-url: https://playready-license.vudrm.tech/rightsmanager.asmx
mpegdashstreaming-cenc-keyserver-playready-checksum: qYKKs3wVDDk=
mpegdashstreaming-cenc-keyserver-widevine: true
mpegdashstreaming-cenc-keyserver-widevine-system-id: edef8ba9-79d6-4ace-a3c8-27dcd51d21ed
mpegdashstreaming-cenc-keyserver-widevine-pssh-data: IgtteWNvbnRlbnRpZEjj3JWbBg==
cupertinostreaming-aes128-method: SAMPLE-AES
cupertinostreaming-aes128-url: skd://fairplay-license.vudrm.tech/license/mycontentid
cupertinostreaming-aes128-key: 74FF8E038FB0F65AA5D01C52596B99A5
cupertinostreaming-aes128-iv: 59E932087732AA68DF8BCAE63230479E
cupertinostreaming-aes128-iv-include-in-chunklist: false
cupertinostreaming-aes128-key-format: com.apple.streamingkeydelivery
cupertinostreaming-aes128-key-format-version: 1
DELETE /api/keyfile/<streamid>
Remove the keyfile for the specified Wowza stream, returning it’s contents before deletion.
Bento4¶
Installing Bento4¶
You can download the binaries for your preferred OS from Bento4.
Add the filepath for the binaries to the Bento4 bin
folder as a value to your Path
environemnt variables on your computer.
Get CPIX Documents¶
CPIX Documents are required to give Bento4 the necessary information in order to encrypt your media.
You can retrieve these documents from the VUDRM Admin site.
For more information, please view our documentation on CPIX Document Requests and our CPIX Key Provider API.
Below is an example of a CPIX Document with all DRM Providers
selected.
<?xml version="1.0" encoding="UTF-8"?>
<cpix:CPIX xmlns:pskc="urn:ietf:params:xml:ns:keyprov:pskc" xmlns:xsi="urn:ietf:params:xml:ns:keyprov:pskc" xmlns:cpix="urn:dashif:org:cpix" xsi:schemaLocation="urn:dashif:org:cpix cpix.xsd">
<cpix:ContentKeyList>
<cpix:ContentKey kid="11111111-1111-1111-1111-111111111111" explicitIV="MjlkMzNlYzVjYWIyZmRmZg==" commonEncryptionScheme="cenc">
<cpix:Data>
<pskc:Secret>
<pskc:PlainValue>3wFFjPspsydY/t2uSPeE6w==</pskc:PlainValue>
</pskc:Secret>
</cpix:Data>
</cpix:ContentKey>
</cpix:ContentKeyList>
<cpix:DRMSystemList>
<cpix:DRMSystem kid="11111111-1111-1111-1111-111111111111" systemId="9a04f079-9840-4286-ab92-e65be0885f95">
<cpix:PSSH><PlayReady_PSSH></cpix:PSSH>
</cpix:DRMSystem>
<cpix:DRMSystem kid="22222222-2222-2222-22222-22222222222" systemId="edef8ba9-79d6-4ace-a3c8-27dcd51d21ed">
<cpix:PSSH><Widevine_PSSH></cpix:PSSH>
</cpix:DRMSystem>
<cpix:DRMSystem kid="33333333-3333-3333-33333-33333333333" systemId="94ce86fb-07ff-4f43-adb8-93d2fa968ca2">
<cpix:URIExtXKey><FairPlay_URIExtXKey</cpix:URIExtXKey>
<cpix:HLSSignalingData playlist="master"><master_playlist_value></cpix:HLSSignalingData>
<cpix:HLSSignalingData playlist="media"><media_playlist_value></cpix:HLSSignalingData>
</cpix:DRMSystem>
</cpix:DRMSystemList>
<cpix:ContentKeyUsageRuleList></cpix:ContentKeyUsageRuleList>
</cpix:CPIX>
Package your content¶
Now you have retrieved the relevant documentation, you can package content with Bento4.
The values from the CPIX documents will need to be copied and pasted to the corresponding section in the examples below.
For more information, please refer to the documentation from Bento4.
Below is an example of how we can send a request to Bento4 to encrypt our content with Widevine and PlayReady DRM and package it:
mp4dash \
--encryption-key=<key_id_hex_value>:<content_key_hex_value> \
--playready \
--widevine-header=#<Widevine_PSSH> \
--mpd-name=<name_of_manifest>.mpd \
<name_of_video_file>.mp4 \
<name_of_audio_file>.mp4
Below is an example of how we can send a request to Bento4 to encrypt our content with FairPlay DRM and package it:
mp4dash \
--hls \
--encryption-key=<key_id_hex>:<content_key_hex_value>:<iv_hex_value> \
--encryption-cenc-scheme=cbcs \
--fairplay-key-uri=<fairplay_laurl_value> \
--hls-master-playlist-name=<name_of_manifest>.m3u8 \
<name_of_video_file>.mp4 \
<name_of_audio_file>.mp4
Testing packaged content¶
Upload your packaged files and manifest to AWS S3 or an equivalent hosting service. You can now test your newly packaged content with our player on the VUDRM Admin site.
For more information, please view our documentation on how to Test Your Stream.
You can confirm a relevant license request has been made by checking the network tab of the browsers dev tools.
PLAYER INTEGRATIONS¶
Bitmovin¶
Bitmovin is an industry leading HTML5 video player.
The vuplay-bitmovin repository demonstrates at a lower level how to integrate VUDRM with Bitmovin.
Basic setup¶
var container = document.getElementById("my-player");
var vudrmToken = "<your-vudrm-token>";
var playerConfig = {
key: "<your-bitmovin-player-key>"
};
var source = {
title: "Getting Started with the Bitmovin Player",
description:
"Now you are ready to embed the Bitmovin Player into your own website :)",
dash: "<your-mpeg-dash-stream-url>",
hls: "<your-hls-stream-url>",
poster: "https://bitmovin-a.akamaihd.net/content/MI201109210084_1/poster.jpg"
};
The source object above has a drm
property, within this you can add the appropriate DRM configuration.
Widevine example¶
source.drm = {
widevine: {
LA_URL: "https://widevine-license.vudrm.tech/proxy",
prepareMessage: function(keyMessage) {
return JSON.stringify({
token: vudrmToken,
drm_info: Array.apply(null, new Uint8Array(keyMessage.message)),
kid: "<your-content-key-id>"
});
}
}
};
PlayReady example¶
source.drm = {
playready: {
LA_URL: "https://playready-license.vudrm.tech/rightsmanager.asmx?token=" + encodeURIComponent(vudrmToken);
}
}
FairPlay example¶
source.drm = {
fairplay: {
certificateURL: "<your-fairplay-cert>",
LA_URL: "<fairplay-license-server-url>",
certificateHeaders: {
"x-vudrm-token": [vudrmToken]
},
headers: {
"Content-Type": "application/json"
},
prepareMessage: function(keyMessageEvent, keySession) {
return JSON.stringify({
token: vudrmToken,
contentId: keySession.contentId,
payload: keyMessageEvent.messageBase64Encoded
});
},
prepareContentId: function(rawContentId) {
var tmp = rawContentId.split("/");
return tmp[tmp.length - 1];
},
prepareCertificate: function(cert) {
return new Uint8Array(cert);
},
prepareLicense: function(license) {
return new Uint8Array(license);
},
licenseResponseType: "arraybuffer"
}
};
dash.js¶
dash.js is an initiative of the DASH Industry Forum to establish a production quality framework for building video and audio players that play back MPEG-DASH content using client-side JavaScript libraries leveraging the Media Source Extensions API set as defined by the W3C.
The vuplay-dashjs repository demonstrates at a lower level how to integrate VUDRM with dash.js.
Basic setup¶
var streamUrl = "<your-stream-url>";
var vudrmToken = "<your-vudrm-token>";
var player = dashjs.MediaPlayer().create();
Widevine example¶
var overrideKeySystemWidevine = function() {
return {
getInitData: function(cpData, kid) {
this.kid = kid;
if ("pssh" in cpData) {
return BASE64.decodeArray(cpData.pssh.__text).buffer;
}
return null;
},
getLicenseRequestFromMessage: function(message) {
var body = {
token: vudrmToken,
drm_info: Array.apply(null, new Uint8Array(message)),
kid: this.kid
};
body = JSON.stringify(body);
return body;
}
};
};
var overrideProtectionKeyController = function() {
var parent = this.parent;
return {
getSupportedKeySystemsFromContentProtection: function(cps) {
var cp, ks, ksIdx, cpIdx;
var supportedKS = [];
var keySystems = parent.getKeySystems();
var cpsWithKeyId = cps.find(function(element) {
return element.KID !== null;
});
if (cps) {
for (ksIdx = 0; ksIdx < keySystems.length; ++ksIdx) {
ks = keySystems[ksIdx];
for (cpIdx = 0; cpIdx < cps.length; ++cpIdx) {
cp = cps[cpIdx];
if (cp.schemeIdUri.toLowerCase() === ks.schemeIdURI) {
var initData = ks.getInitData(cp, cpsWithKeyId.KID);
if (initData) {
supportedKS.push({
ks: keySystems[ksIdx],
initData: initData
});
}
}
}
}
}
return supportedKS;
}
};
};
var widevineLaUrl = "https://widevine-license.vudrm.tech/proxy";
player.setProtectionData({
"com.widevine.alpha": {
serverURL: widevineLaUrl,
httpRequestHeaders: {}
}
});
player.attachSource(streamUrl);
PlayReady example¶
var playReadyLaUrl =
"https://playready-license.vudrm.tech/rightsmanager.asmx?token=" +
encodeURIComponent(vudrmToken);
player.setProtectionData({
"com.microsoft.playready": {
serverURL: playReadyLaUrl,
httpRequestHeaders: {}
}
});
player.attachSource(streamUrl);
No FairPlay support¶
dash.js does not currently support Fairplay.
JWPlayer¶
JWPlayer is an industry leading HTML5 video player.
The vuplay-jwplayer repository demonstrates at a lower level how to integrate VUDRM with JWPlayer.
Example of JWplayer Integration¶
This file can be found in the repo mentioned above here: https://github.com/Vualto/vuplay-jwplayer/blob/master/vuplay-jwplayer.js
(function() {
// Set the mpeg-dash stream URL.
var dashStreamURL = "<dash-stream-url>";
// Set the hls stream URL.
var hlsStreamURL = "<hls-stream-url>";
// Set the URL to retrieve the fairplay certificate from.
var fairplayCertURL = "<fairplay-cert-url>";
// Please login to https://admin.vudrm.tech to generate a VUDRM token.
var vudrmToken = "<your-vudrm-token>";
// setup jwplayer, passing the stream URLs and DRM configurations.
jwplayer("vuplay-container").setup({
"playlist": [{
"sources": [{
"file": dashStreamURL,
"drm": {
"widevine": {
"url": "https://widevine-license.vudrm.tech/proxy",
"headers": [{
"name": "X-VUDRM-TOKEN",
"value": vudrmToken
}]
},
"playready": {
"url": "https://playready-license.vudrm.tech/rightsmanager.asmx",
"headers": [{
"name": "X-VUDRM-TOKEN",
"value": vudrmToken
}]
}
}
},
{
"file": hlsStreamURL,
"drm": {
"fairplay": {
"certificateUrl": fairplayCertURL,
"processSpcUrl": function (initData) {
return "https://" + initData.split("skd://").pop();
},
"licenseRequestHeaders": [
{
"name": "Content-type",
"value": "arraybuffer"
},
{
"name": "X-VUDRM-TOKEN",
"value": vudrmToken
}
]
}
}
}]
}]
});
})();
Shaka¶
Shaka Player is a JavaScript library for adaptive video streaming. It plays DASH content without browser plugins using MediaSource Extensions and Encrypted Media Extensions.
The vuplay-shaka repository demonstrates at a lower level how to integrate VUDRM with Google’s Shaka player.
Google’s Shaka player can be used to playback MPEG-dash streams with Widevine and PlayReady encryption, or HLS with Fairplay.
Example of Shaka Integration¶
This file can be found in the repo mentioned above here: https://github.com/Vualto/vuplay-shaka/blob/master/vuplay.js
(function () {
// Set your mpeg dash stream url
var mpegdashStreamUrl = "<mpeg-dash-stream-url>";
// Set your HLS stream url
var hlsStreamUrl = "<hls-stream-url>";
// Set your fairplay certificate url
var fairplayCertificateUrl = "<fairplay-certificate-url>";
// Will get overridden with the one from the manifest but we have to set something otherwise shaka will complain!
var fairplayLicenseServerUrl = "https://fairplay-license.vudrm.tech/license";
// Please login to https://admin.vudrm.tech to generate a vudrm token.
var vudrmToken = "<vudrm-token>";
// A bit of hacky way to detect Safari but will do for demo purposes...
var isSafari = (navigator.userAgent.indexOf("Safari") != -1 && navigator.userAgent.indexOf("Chrome") == -1);
// Fetch the fairplay certificate used to generate the fairplay license request
function getFairplayCertificate() {
var certRequest = new XMLHttpRequest();
certRequest.responseType = "arraybuffer";
certRequest.open("GET", fairplayCertificateUrl, true);
certRequest.onload = function (event) {
if (event.target.status == 200) {
loadPlayer(new Uint8Array(event.target.response));
} else {
var error = new Error("HTTP status: " + event.target.status + " when getting Fairplay Certificate.");
onError(error);
}
};
certRequest.send();
}
// Returns a shaka player config for use with mpeg-dash streams
function getNonSafariPlayerConfig() {
return {
drm: {
servers: {
"com.widevine.alpha": "https://widevine-license.vudrm.tech/proxy",
"com.microsoft.playready": "https://playready-license.vudrm.tech/rightsmanager.asmx"
}
}
}
}
// returns a shaka player config for use with HLS streams
function getSafariPlayerConfig(fairplayCertificate) {
return {
drm: {
servers: {
"com.apple.fps.1_0": fairplayLicenseServerUrl
},
advanced: {
"com.apple.fps.1_0": {
serverCertificate: fairplayCertificate
}
},
initDataTransform: function (initData, initDataType) {
if (initDataType == "skd") {
// Set the Fairplay license server URL with the one from the HLS manifest
fairplayLicenseServerUrl = shaka.util.StringUtils.fromBytesAutoDetect(initData);
// Create the initData for Fairplay
var contentId = fairplayLicenseServerUrl.split("/").pop();
var certificate = window.shakaPlayerInstance.drmInfo().serverCertificate;
return shaka.util.FairPlayUtils.initDataTransform(initData, contentId, certificate);
} else {
return initData;
}
}
}
}
}
function loadPlayer(fairplayCertificate) {
// setup the shaka player and attach an error event listener
var video = document.getElementById("video");
window.shakaPlayerInstance = new shaka.Player(video);
window.shakaPlayerInstance.addEventListener("error", onErrorEvent);
// configure the DRM license servers
var playerConfig = isSafari ? getSafariPlayerConfig(fairplayCertificate) : getNonSafariPlayerConfig();
window.shakaPlayerInstance.configure(playerConfig);
// Something special is needed for the widevine license request.
window.shakaPlayerInstance
.getNetworkingEngine()
.registerRequestFilter(function (type, request) {
// ignore requests that are not license requests.
if (type != shaka.net.NetworkingEngine.RequestType.LICENSE)
return;
// set the VUDRM token as a header on the license request
request.headers["X-VUDRM-TOKEN"] = vudrmToken;
// custom fairplay license request body required
if (window.shakaPlayerInstance.drmInfo().keySystem == "com.apple.fps.1_0") {
request.headers["Content-Type"] = "ArrayBuffer"
request.uris = [fairplayLicenseServerUrl.replace("skd", "https")];
}
});
// load the mpeg-dash or HLS stream into the shaka player
window.shakaPlayerInstance
.load(isSafari ? hlsStreamUrl : mpegdashStreamUrl)
.then(function () {
console.log("The stream has now been loaded!");
})
.catch(onError);
}
// Set polyfills required by shaka
shaka.polyfill.installAll();
// Check browser is supported and load the player.
if (shaka.Player.isBrowserSupported()) {
if (isSafari) {
// Get the fairplay certificate, once the cert is retrieved then the player will be loaded.
getFairplayCertificate();
} else {
loadPlayer();
}
} else {
console.error("This browser does not have the minimum set of APIs needed for shaka!");
}
})();
function onErrorEvent(event) {
// Extract the shaka.util.Error object from the event.
onError(event.detail);
}
function onError(error) {
console.error("Error code", error.code, "object", error);
}
THEOplayer¶
THEOplayer is an industry leading HTML5 video player.
The vuplay-theoplayer repository demonstrates at a lower level how to integrate VUDRM with THEOplayer.
Basic setup¶
<div
id="vuplay-container"
class="video-js theoplayer-skin theo-seekbar-above-controls"
></div>
var streamUrl = "<your-stream-url>";
var vudrmToken = "<your-vudrm-token>";
var containerElement = document.getElementById("vuplay-container");
var player = new THEOplayer.Player(containerElement, {
libraryLocation: "<your-theoplayerjs-scripts-path>",
ui: {
fluid: true
}
});
Widevine example¶
player.source = {
sources: [
{
src: streamUrl,
type: "application/dash+xml",
contentProtection: {
integration: "vudrm",
token: vudrmToken,
widevine: {
licenseAcquisitionURL: "https://widevine-license.vudrm.tech/proxy"
}
}
}
]
};
PlayReady example¶
player.source = {
sources: [
{
src: streamUrl,
type: "application/dash+xml",
contentProtection: {
integration: "vudrm",
token: vudrmToken,
playready: {
licenseAcquisitionURL:
"https://playready-license.vudrm.tech/rightsmanager.asmx"
}
}
}
]
};
FairPlay example¶
player.source = {
sources: [
{
src: streamUrl,
type: "application/x-mpegurl",
contentProtection: {
integration: "vudrm",
token: vudrmToken
}
}
]
};
Video JS¶
Video JS is an industry leading HTML5 video player.
The vuplay-videojs repository demonstrates at a lower level how to integrate VUDRM with Video JS.
Example of Video JS Integration¶
You will need to load the following libraries:
<script src="https://cdnjs.cloudflare.com/ajax/libs/video.js/7.10.2/video.min.js"></script>
<script src="https://unpkg.com/@videojs/http-streaming@2.3.0/dist/videojs-http-streaming.js"></script>
<script src="https://unpkg.com/videojs-contrib-eme@3.7.0/dist/videojs-contrib-eme.js"></script>
The following code snippet can be found in our vuplay videojs repo.
const streamURL = "<your-stream-url>";
const token = "<your-vudrm-token>";
// playready
const playReadyLicenseServerURL =
"https://playready-license.vudrm.tech/rightsmanager.asmx?token=" + encodeURIComponent(token);
// widevine
const widevineLicenseServerURL =
"https://widevine-license.vudrm.tech/proxy?token=" + encodeURIComponent(token);
// fairplay
const fairplayCertificateUri =
"https://fairplay-license.vudrm.tech/certificate/<your-client-name>";
var fairplayLicenseUri;
var Utils = {
uint16ArrayToString: function(array) {
var uint16Array = new Uint16Array(array.buffer);
return String.fromCharCode.apply(String, uint16Array);
}
};
(function () {
var player = videojs("my-video", {
autoplay: true,
});
// eme extension must be initialised before source is set
player.eme();
player.src({
src: streamURL,
type: (streamURL.endsWith('.mpd') ? "application/dash+xml" : "application/x-mpegURL"),
keySystems: {
"com.apple.fps.1_0": {
certificateUri: fairplayCertificateUri,
getContentId: function (emeOptions, initData, eme) {
fairplayLicenseUri = "https://" + Utils.uint16ArrayToString(initData).split("skd://").pop();
var contentId = fairplayLicenseUri.split("/").pop();
return contentId;
},
getLicense: function (emeOptions, contentId, keyMessage, callback) {
videojs.xhr({
url: fairplayLicenseUri,
method: 'POST',
responseType: 'arraybuffer',
body: keyMessage,
headers: {
'Content-type': 'application/octet-stream',
'x-vudrm-token': token
}
}, (err, response, responseBody) => {
if (err) {
callback(err)
return
}
callback(null, responseBody)
})
},
},
"com.microsoft.playready": playReadyLicenseServerURL,
"com.widevine.alpha": widevineLicenseServerURL,
},
});
})();
MOBILE SDKS¶
Android Widevine SDK¶
VUDRMWidevine is an Android Archive (AAR) which can be used during the media rendering pipeline to provide a DRM plugin to ExoPlayer 2.12.2 which will work with Vualto DRM workflow. VUDRMWidevine has been developed to specifically manage the session DRM, allowing complete asset and player management.
Current release: v0.3.5 (277)
- Requirements
- Android Studio Integration
- Example Usage
- Error handling
- Devices tested on:
- Known Issues
- Troubleshooting
- Release Notes
Requirements¶
- Minimum SDK version is 19 (Android 4.4)
- Android Studio 4.0.1
Android Studio Integration¶
VUDRMWidevine is distributed from our maven repository, access to which is made possible by adding the following to the repositories closure of your apps top level build.gradle file:
allprojects { repositories { jcenter() maven { url "https://maven.drm.technology/artifactory/vudrm-widevine" credentials { username = "mavenUsername" password = "mavenPassword" } } } }
Map the username and password to your credentials for authentication to the maven repository
Under your module’s build.gradle dependencies add:
implementation 'com.vualto.vudrm:widevine:0.3.5'
Access to our KID plugin is enabled similarly using the same maven repository configuration, but with the url:
https://maven.drm.technology/artifactory/kid-plugin
and dependency:
implementation 'com.vualto.vudrm:kidplugin:0.3.5'
Example Usage¶
Instances of VUDRMWidevine are associated with specific assets. Offline assets can be tracked using Exoplayers OfflineLicenseHelper, DownloadService, and DownloadTracker classes. Two types of session are available, determined by the current session’s VUDRM token policy, and the asset configuration.
For further information about VUDRM please contact us, or refer to our documentation.
Exoplayer 12 introduces more ‘under the hood’ management of DRM assets, which simplifies the flow for online and offline streaming sessions. For an example of implementation we recommend referring to our multiple asset VUDRM Widevine demo application, which is directly adapted from Exoplayers demo application. To manage the required modification of license calls we have added a simple VudrmHelper
class to the demo application.
Asset configurations should contain at least a stream name
,uri
,drm_scheme
(”widevine”), and a drm_license_url
. Tokens may be presented using either Exoplayers license header drm_key_request_properties
, or a URL encoded token may instead be added to the license server URI, eg: "drm_license_uri": "https://widevine-license.staging.vudrm.tech/proxy?token=vualto-token-value"
.
To implement VUDRM Widevine, instantiate an AssetConfiguration using the fluent interface for both online and offline sessions. Minimally you need to provide the contents KID, DRM token, and license server URI.
assetConfiguration = new AssetConfiguration.Builder() .tokenWith(drmToken) .KIDWith(KID) .build();
Once you’ve built the object you can construct a plugin by instantiating a WidevineCallback object with the asset configuration.
MediaDrmCallback callback = new WidevineCallback (assetConfiguration);
Then you can pass it to ExoPlayer as the component required when creating the DefaultDrmSessionManager object.
return new DefaultDrmSessionManager.Builder() .setUuidAndExoMediaDrmProvider(C.WIDEVINE_UUID, FrameworkMediaDrm.DEFAULT_PROVIDER) .setMultiSession(false) .build(mediaDrmCallback);
For offline sessions, Exoplayers DownloadTracker will create a
WidevineOfflineLicenseFetchTask
to acquire and download the license. After this the download will commence. When the download is complete, Exoplayer will manage the asset for playback.
References: http://google.github.io/ExoPlayer/doc/reference/
Error handling¶
DRM related errors will be bubbled up to ExoPlayer’s event listener system, this should contain the license server’s HTTP response code and a transaction ID in the exception if applicable.
Devices tested on:¶
Internally
- Xiaomi Redmi 5 Plus (8.1.0)
- Nexus 10 (5.1.1)
- Samsung S5 (6.0.1)
- Samsung Tab 10 A5 (6.0.1)
- HP 8 Tablet (4.4.2)
- Huawei Honor 9 (8.0.0)
Known Issues¶
- Exoplayer 12 for AndroidX introduced a requirement that the PSSH box be present in the main manifest. If the PSSH box is not present in the main manifest then Exoplayers
DefaultDrmSessionManager
will throw this errorMissingSchemeDataException: Media does not support uuid: edef8ba9-79d6-4ace-a3c8-27dcd51d21ed
. - 32-bit devices displayed issues where a license expiry time (secs) is too large to be handled, it therefore returns 0 seconds remaining and considers the license expired. To work around this, always set the license expiry time in your VUDRM token policy. For further information about VUDRM please contact us, or refer to our documentation.
If you believe you have found any other issue, please contact us at support@vualto.com
Troubleshooting¶
- Most issues are content related. You can use our demo application to test your own content by updating the
media.exolist.json
with your configurations. - Errors may also arise because the stream or asset configuration is not correct.
- Tokens - You can easily eliminate token issues by beginning with an empty policy in the VUDRM token. Please ensure your token is formatted correctly and validates. For the avoidance of doubt, where tokens use dates, the dates should always be in the future. For further information about tokens please refer to our documentation.
If you are not able to play your content after checking it in our demo application please contact support@vualto.com with the demo application logs and the stream configuration used.
Release Notes¶
v0.3.5 (build 277) on 07/08/2019
- Update license server DNS
- Replaces instances of assetName and assetID with contentID to be consistent across DRM platform
v0.3.4 (build 272) on 22/05/2019
- Widevine Offline Implementation – Download asset and license, and play downloaded asset offline with saved license.
v0.3.3 (build 243) on 15/05/2019
- Build automation updates
v0.3.2 (build 233) on 17/04/2019
- Update dependencies
- Resolve deprecations
- Update to Android Studio 3.3.2 and SDK 28
v0.3.1 (build 216) on 21/02/2019
- Add Widevine provision callback
v0.3.0 (build 204) on 12/01/2018
- Add Widevine provision callback
- Bug fixes and improvements
v0.2.0 on 12/04/2017
- New major exoplanet release
- Bug fixes and improvements
v0.1.0 on 10/03/2017
- Initial release
iOS / tvOS FairPlay SDK¶
VUDRMFairPlaySDK is available for iOS and tvOS. This documentation describes the steps to integrate and use VUDRMFairPlaySDK on these platforms, and how to configure our demo applications.
Current release: iOS v1.0 (37) tvOS v1.0 (38)
- Overview
- Requirements
- Xcode Integration
- Information about Application Transport Security (ATS)
- Preparation
- Example framework usage
- Limitations
- Known Issues
- Troubleshooting
- Release Notes
Overview¶
VUDRMFairPlaySDK enables Apple’s AVPlayer from AVFoundation to securely request licenses from Vualto’s VUDRM cloud based DRM platform.
The deployment scenario will allow Online Playback / Download and Offline Playback using an associated rental or persist token.
Make use of Apple’s AVPlayer and AVPlayerViewController to present users with the platform default skins, with DRM content, or create your own video player based on AVPlayer.
This SDK has a number of key benefits:
- Complete control over the entire AVPlayer lifecycle
- Numerous optimisations to enable faster playback
- Bitcode support
The SDK is fully supported in both Objective-C and Swift applications.
Multiple asset demo applications written in Swift, and based on Apple’s FairPlay SDK example applications, are available on request. Please contact support@vualto.com to request access.
Requirements¶
- Minimum deployment target of iOS 10.0 and tvOS 9.0 or higher
- Xcode 12.4
- Swift 5.3
- Cocoapods
Xcode Integration¶
VUDRMFairPlaySDK is distributed using Cocoapods. Projects set up with Cocoapods use an Xcode workspace, so it is important that projects with Cocoapods are always opened using the workspace file instead of the project file. Our simple demo applications demonstrate the required set up for Cocoapods.
If you are integrating VUDRMFairPlaySDK into your own project, please ensure that the project has been set up correctly for Cocoapods. There are numerous online tutorials demonstrating how to do this. You can then replace or merge the podfile
with the one in our demo application.
It is important that the latest version of Cocoapods is installed before the demo project or your project can be opened without error.
You can ensure that you have the latest version of Cocoapods by opening a new shell in the Terminal application and entering:
sudo gem install cocoapods
To pull vudrmFairPlaySDK.framework in to the demo project or your project with Cocoapods, ensure that you quit Xcode if it is running, then open the Terminal application and navigate to the project directory, for example:
cd /Users/username/Documents/Dev/vudrm-fairplay-demo
Then enter:
pod install
or, where the pod has previously been installed:
pod update
If you have installed our earlier SDK’s and receive this CocoaPod error:
[!] Unable to find a specification for 'vudrmFairPlaySDK'
you may also need to enter the following:
pod install --repo-update
The project should now be ready to open and run in Xcode.
Please see below for more information on using the demo application.
If you are unable to use Cocoapods in your project, please contact support@vualto.com to discuss other options.
Information about application transport security (ATS)¶
iOS/tvOS 9 introduces Application Transport Security (ATS) which restricts the use of insecure HTTP.
In order to permit playback of content over insecure HTTP exemptions need to be added into your application’s Info.plist
. For example to disable ATS for any connection you would add the following into your application’s Info.plist
:
<dict>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key> <true/>
</dict>
</dict>
More information about ATS can be found in Apple’s TechNote.
Preparation¶
Instances of VUDRMFairPlaySDK require some configuration which would normally be populated with an API. Our demo projects, which are based on Apple’s FairPlay SDK example applications, are configured using the projects Streams.plist
file.
To provide complete configuration for each Stream
object you will require:
- An
skd://
URI entry in thecontent_key_id_list
. The correct URI can be obtained by retrieving the manifest and parsing out theURI
from theEXT-X-SESSION-KEY
orEXT-X-KEY
, this can be done in code (see our demo application for an example) or manually with curl in the Terminal app, e.g. enter:curl https://eample.cloudfront.net/example-demo/examplecontent/examplecontent.ism/.m3u8
. - The
playlist_url
to correctly prepared content. - A
name
for the content. This will be the readable display name and is not used by the SDK. This may differ from thecontent_id
. - A
content_id
for the content, being the same content ID that the content was prepared with. This value will always be the last path component of the streamcontent_key_id_list
orskd://
URI entry. Our demo application shows how to retrieve and parse thecontent_key_id_list
, however, thecontent_id
always needs to be provided for use with offline assets. - An
is_protected
boolean value which indicates that theStream
object is protected with DRM. This setting would not be needed in scenarios where all content is protected, as all assets can be set as protected by default in the source code. - A
vudrm-token
which must be correctly generated for the type of instance you wish to create. Please see the Tokens and Licensing section for further information. - An optional
renew_interval
value which represents lease renewal interval in seconds. This value is only used where token policy usesduration_lease
.
For added convenience, VUDRMFairPlaySDK also bubbles up notifications of errors and progress during the licensing request. Errors can be intercepted as follows:
@objc func handleVudrmDidError(_ notification: Notification)
{
if let data = notification.userInfo as? [String: String]
{
for (function, error) in data
{
print("vudrmFairPlaySDK - \(function) reported error \(error)!")
}
}
}
And, progress can be monitored using the following:
@objc func handleVudrmProgressUpdate(_ notification: Notification)
{
if let data = notification.userInfo as? [String: String]
{
for (function, message) in data
{
print("vudrmFairPlaySDK - \(function) reported progress \(message).")
}
}
}
Example framework usage¶
We strongly recommend referring to our example iOS / tvOS multiple asset demo application.
Using either of Apples recommended implementations, being AssetResourceLoaderDelegate (AVAssetResourceLoader API) or ContentKeyDelegate (Contentkey API), VUDRMFairPlaySDK performs the two required licensing network tasks, being acquisition of an application certificate, followed by a FairPlay license either for online or offline use.
An application certificate is always required to initialise a request for a FairPlay license.
To invoke an instance of vudrmFairPlaySDK and call for an application certificate using either AssetResourceLoaderDelegate or ContentKeyDelegate, use:
self.drm = vudrmFairPlaySDK()
let applicationCertificate = try drm!.requestApplicationCertificate(token: self.vuToken, contentID: contentID)
Using the application certificate, a call for a license may then be prepared on device using the same call but passing different streamingContentKeyRequestData options for online and offline requests. The resulting data is then passed back to the vudrmFairPlaySDK to request a license from the KSM for either online or offline use. The implementation of the next steps differ for each method of handling FairPlay requests, they are as follows:
let spcData = try resourceLoadingRequest.streamingContentKeyRequestData(forApp: applicationCertificate!,contentIdentifier: assetIDData, options: nil)
let ckcData = try self.drm!.requestContentKeyFromKeySecurityModule(spcData: spcData, token: vuToken, assetID: self.contentID, licenseURL: licenseUrl)
if ckcData != nil {
resourceLoadingRequest.dataRequest?.respond(with: ckcData!)
} else {
print("Failed to get CKC for the request object of the resource.")
return
}
You should always set the contentType before calling finishLoading() on the resourceLoadingRequest to make sure you have a contentType that matches the key response.
resourceLoadingRequest.contentInformationRequest?.contentType = AVStreamingKeyDeliveryContentKeyType
resourceLoadingRequest.finishLoading()
let spcData = try resourceLoadingRequest.streamingContentKeyRequestData(forApp: applicationCertificate!, contentIdentifier: assetIDData, options: [AVAssetResourceLoadingRequestStreamingContentKeyRequestRequiresPersistentKey: true])
let ckcData = try self.drm!.requestContentKeyFromKeySecurityModule(spcData: spcData, token: self.vuToken, assetID: self.contentID, licenseURL: self.licenseUrl, renewal: self.renewalInterval)
let persistentKey = try resourceLoadingRequest.persistentContentKey(fromKeyVendorResponse: ckcData!, options: nil)
You can now write the persistent content key to disk:
try writePersistableContentKey(contentKey: persistentKey, withContentKeyIdentifier: contentID)
and then provide the content key response to make protected content available for processing before calling finishLoading() on the resourceLoadingRequest.
resourceLoadingRequest.dataRequest?.respond(with: persistentKey)
resourceLoadingRequest.finishLoading()
The call for an offline license should be made upon download of the asset.
let completionHandler = { [weak self] (spcData: Data?, error: Error?) in
guard let strongSelf = self else { return }
if let error = error {
keyRequest.processContentKeyResponseError(error)
return
}
guard let spcData = spcData else { return }
do {
guard let ckcData = try strongSelf.drm!.requestContentKeyFromKeySecurityModule(spcData: spcData, token: vuToken, assetID: self!.contentID, licenseURL: licenseUrl, renewal: self.renewalInterval) else { return }
let keyResponse = AVContentKeyResponse(fairPlayStreamingKeyResponseData: ckcData)
keyRequest.processContentKeyResponse(keyResponse)
} catch {
keyRequest.processContentKeyResponseError(error)
}
}
keyRequest.makeStreamingContentKeyRequestData(forApp: applicationCertificate!, contentIdentifier: assetIDData, options: [AVContentKeyRequestProtocolVersionsKey: [1]], completionHandler: completionHandler)
let ckcData = try self!.drm!.requestContentKeyFromKeySecurityModule(spcData: spcData, token: self!.vuToken, assetID: self!.contentID, licenseURL: self!.licenseUrl, renewal: self.renewalInterval)
let persistentKey = try keyRequest.persistableContentKey(fromKeyVendorResponse: ckcData!, options: nil)
You can now write the persistent content key to disk:
try strongSelf.writePersistableContentKey(contentKey: persistentKey, withContentKeyIdentifier: self!.contentID)
Finally, provide the content key response to make protected content available for processing.
let keyResponse = AVContentKeyResponse(fairPlayStreamingKeyResponseData: persistentKey)
keyRequest.processContentKeyResponse(keyResponse)
Requests for playback or download will result in a license request for the content from the license server, based on the type of token presented for each VUDRMFairPlaySDK request. The tokens may be FairPlay Rental, FairPlay Lease, or FairPlay Persist.
- FairPlay Rental token license requests will be provided a streaming content key, which may be used for online streaming only.
- FairPlay Lease token license requests will be provided a streaming content key, which may be used for online streaming only, and may be renewed periodically.
- FairPlay Persist token license requests will be provided a persistent content key, which may be written to device and subsequently used for both online streaming, and offline playback of downloaded content.
In iOS, when a download of an asset has been completed with a persist token policy, further playback requests will use the downloaded asset and associated license (or content key), even when online.
Example VUDRM token type templates available are:
- Fairplay Rental
{"type": "r","duration_rental": 3600}
- Fairplay Rental tokens may only be used to stream online content.
- Fairplay Lease
{"type": "l","duration_lease": 3600}
- Fairplay Lease tokens may only be used to stream online content. The token duration should always be greater than
360
seconds and should also exceed the streamsrenew_interval
period. The streamsrenew_interval
should always exceed300
seconds. We recommend a difference of at least60
seconds between the token duration andrenew_interval
to allow adequate time for the license to be retrieved and processed by the OS. In all other circumstances therenew_interval
period should be set to0
or not included in the stream configuration. Lease renewals will fail if therenew_interval
is set to any value below300
seconds, except for0
. This is to prevent license server overload and unexpected overheads. Please refer to our demo application for an example, and do not hesitate to contact us to discuss the use of Fairplay Lease.
- Fairplay Lease tokens may only be used to stream online content. The token duration should always be greater than
- Fairplay Persist
{"type": "p","duration_persist": 3600}
- Fairplay Persist tokens may be used to stream online content and play offline (downloaded) content.
For further information about VUDRM please contact us, or refer to our documentation.
The content ID should be unique to each asset, as it is used along with the playlist_url
to create a path to, and identify, offline assets and their associated content keys.
The tvOS platform does not support downloading or offline playback.
There are significant limitations using AirPlay to stream any content to an Apple TV that has been downloaded to the users device. Please see the Limitations section for further information.
We have developed demo applications based on Apples Fairplay SDK examples, using AssetResourceLoaderDelegate
for iOS 10 and above, and the newer ContentKeyDelegate
for iOS 11.2 and above.
The demo applications target both iOS and tvOS platforms using shared source code. Each target platform references its own version of the framework. The lowest version of iOS supported in the demo applications is iOS 11.2, however it is possible to add the required support for iOS 10 and above.
With the VUDRMFairPlaySDK framework installed, both the example demo applications read entries in the Streams.plist
file to configure the content and DRM. You can edit the Streams.plist
file values, and/or add your own valid values, which should include a content_id
, playlist_url
, renew_interval
, and vudrm_token
(see Preparation above).
- The
content_key_id_list
entry in theStreams.plist
is retrieved in the demo by parsing the manifest using thegetContentKeyIDList
function in theAssetListTableViewController
. - The
name
value is the display name, which may differ from thecontent_id
. - The
is_protected
boolean value indicates whether the content is protected with DRM or not. This value can be overridden in source if all content is protected. - The optional
renew_interval
value represents the lease renewal interval in seconds. This value is only used where token policy usesduration_lease
and in all other casesrenew_interval
should either be set to0
or not used in the configuration. Whererenew_interval
is used, it must always be a value above300
seconds to prevent license server overload and/or unexpected overheads. Requests will fail for a renewed license where the value used is below300
, except for0
. There should be a difference of at least60
seconds between the token duration andrenew_interval
to allow adequate time for the license to be retrieved and processed by the OS.
In both target platforms, the AssetResourceLoaderDelegate
or ContentKeyDelegate
class handles license requests for online streaming.
In iOS only the AssetResourceLoaderDelegate+Persistable
or ContentKeyDelegate+Persistable
extension handles license requests for iOS offline playback.
The call to make a request for an online license from the framework is made by iOS when a protected asset is requested for playback in the override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)
function of the
AssetListTableViewController
. This corresponds to the specific stream, and an Asset
object (with associated AVURLAsset
is initialised for the stream by the returned table cell.
In iOS, the call used to make a request for an offline license from the framework in the AssetResourceLoaderDelegate demo is
ContentKeyManager.shared.assetResourceLoaderDelegate.requestPersistableContentKeys(forAsset: asset)
, or in the ContentKeyDelegate demo ContentKeyManager.shared.contentKeyDelegate.requestPersistableContentKeys(forAsset: asset)
and this call is in the: override func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath)
function of the
AssetListTableViewController
. This corresponds to the specific stream Download button.
In iOS, a call is made upon launch by the AppDelegate
class to the AssetPersistenceManager
class to restore any persisted content. The call AssetPersistenceManager.sharedManager.restorePersistenceManager()
asks the AssetPersistenceManager
class to check any already mapped asset against any outstanding AVAssetDownloadTask
.
Limitations¶
- The SDK does not support AirPlay of protected content after a persist content key has been persisted (content downloaded to device). Apple TV is never able to play protected offline/downloaded content with a persist content key because the key cannot be written to the playback device. Attempting to do so may result in unexpected behaviours, crashes referring to the content key type, or the content may not load. Protected content may be transmitted with AirPlay using a streaming content key whenever the Apple TV has a network connection.
- The SDK does not support playback of protected content when running in the iOS Simulator. Attempting to do so will result in an invalid
KEYFORMAT
error and the content will not load.
Known Issues¶
- If you believe you have found any issues, please contact us at support@vualto.com.
Troubleshooting¶
- Most issues are content related. You can use our demo application to test your own content by updating it with your
Stream
configurations. - Errors may also arise because the
Stream
configuration is not correct.- Content ID - Please ensure that the
content_id
corresponds to that which was used when preparing your content. Thecontent_id
should always be unique for eachStream
instance. - Tokens - You can easily eliminate token issues by beginning with a default FairPlay token policy. Please ensure your token is formatted correctly and validates. For the avoidance of doubt, where tokens use dates, the dates should always be in the future. For further information about tokens please refer to our token documentation.
- Content ID - Please ensure that the
If you are not able to play your content after checking it in our demo application please contact support@vualto.com with the demo application logs and the stream configuration used.
Legacy¶
iOS / tvOS FairPlay SDK¶
The VUDRMFairPlay SDK is available for iOS and tvOS. This documentation describes the steps to integrate and use the VUDRMFairPlay SDK on these platforms, and how to configure our demo application.
Current release: iOS v1.0 (254) tvOS v1.0 (255)
- Overview
- Requirements
- Xcode Integration
- Information about Application Transport Security (ATS)
- Preparation
- Example framework usage
- Limitations
- Known Issues
- Troubleshooting
- Release Notes
VUDRMFairPlay SDK enables Apple’s AVPlayer from AVFoundation to securely request licenses from Vualto’s VUDRM cloud based DRM platform using an extended instance of AssetResourceLoaderDelegate.
The deployment scenario will allow Online Playback / Download and Offline Playback using an associated rental or persist token.
Make use of Apple’s AVPlayer and AVPlayerViewController to present users with the platform default skins, with DRM content. Or create your own video player based on AVPlayer.
This SDK has a number of key benefits:
- Complete control over the entire AVPlayer lifecycle
- Numerous optimisations to enable faster playback
- Bitcode support
The SDK is fully supported in both Objective-C and Swift applications.
A multiple asset demo application written in Swift, and based on Apple’s FairPlay SDK example application, is available on request. Please contact support@vualto.com to request access.
- Minimum deployment target of iOS 12.0 and tvOS 12.0 or higher
- Xcode 12.2
- Swift 5.3
- Cocoapods
The VUDRMFairPlay SDK is distributed using Cocoapods. Projects set up with Cocoapods use an Xcode workspace, so it is important that projects with Cocoapods are always opened using the workspace file instead of the project file. Our simple demo application demonstrates the required set up for Cocoapods.
If you are integrating the VUDRMFairPlay SDK into your own project, please ensure that the project has been set up correctly for Cocoapods. There are numerous online tutorials demonstrating how to do this. You can then replace or merge the podfile
with the one in our demo application.
It is important that the latest version of Cocoapods is installed before the demo project or your project can be opened without error.
You can ensure that you have the latest version of Cocoapods by opening a new shell in the Terminal application and entering:
sudo gem install cocoapods
To pull vudrmFairPlay.framework in to the demo project or your project with Cocoapods, ensure that you quit Xcode if it is running, then open the Terminal application and navigate to the project directory, for example:
cd /Users/username/Documents/Dev/vudrm-fairplay-demo
Then enter:
pod install
or, where the pod has previously been installed:
pod update
The project should now be ready to open and run in Xcode.
Please see below for more information on using the demo application.
If you are unable to use Cocoapods in your project, please contact support@vualto.com to discuss other options.
iOS/tvOS 9 introduces Application Transport Security (ATS) which restricts the use of insecure HTTP. In order to permit playback of content over insecure HTTP exemptions need to be added into your application’s Info.plist. For example to disable ATS for any connection you would add the following into your application’s Info.plist:
<dict>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key> <true/>
</dict>
</dict>
More information about ATS can be found in Apple’s TechNote: https://developer.apple.com/library/prerelease/ios/technotes/App-Transport-Security-Technote/
The demo project is based on Apple’s FairPlay SDK example application. This is configured using the projects Streams.plist
file.
To provide complete configuration for each Stream
object in the Streams.plist
you will require:
- An
skd://
URI entry in thecontent_key_id_list
. The correct URI can be obtained by retrieving the manifest and parsing out theURI
from theEXT-X-SESSION-KEY
orEXT-X-KEY
, this can be done in code (see our demo application for an example) or manually with curl in the Terminal app, e.g. enter:curl https://eample.cloudfront.net/example-demo/examplecontent/examplecontent.ism/.m3u8
. - The
playlist_url
to correctly prepared content. - A
name
for the content. This will be the readable display name and is not used by the SDK. This may differ from thecontent_id
. - A
content_id
for the content, being the same content ID that the content was prpeared with. This value will always be the last path component of the streamcontent_key_id_list
orskd://
URI entry. Our demo application shows how to retrieve and parse thecontent_key_id_list
, however, thecontent_id
always needs to be provided for use with offline assets. - An
is_protected
boolean value which indicates that theStream
object is protected with DRM. This setting would not be needed in scenarios where all content is protected, as all assets can be set as protected by default in the source code. - A
vudrm-token
which must be correctly generated for the type of instance you wish to create. Example VUDRM token type templates available are:- Fairplay Rental
{"type": "r","duration_rental": 3600}
- Fairplay Rental tokens may only be used to stream online content to devices running iOS 12 or higher.
- Fairplay Persist
{"type": "p","duration_persist": 3600}
- Fairplay Persist tokens may be used to stream online content and play offline (downloaded) content to devices running iOS 12 or higher.
- Fairplay Rental
For further information about VUDRM please contact us, or refer to our documentation: https://docs.vualto.com/projects/vudrm/en/latest/VUDRM-token.html
For added convenience, VUDRMFairPlay also bubbles up notifications of errors and progress during the licensing request.
Errors can be intercepted as follows:
@objc func handleVudrmDidError(_ notification: Notification)
{
if let data = notification.userInfo as? [String: String]
{
for (function, error) in data
{
print("vudrmFairPlay - \(function) reported error \(error)!")
}
}
}
And, progress can be monitored using the following:
@objc func handleVudrmProgressUpdate(_ notification: Notification)
{
if let data = notification.userInfo as? [String: String]
{
for (function, message) in data
{
print("vudrmFairPlay - \(function) reported progress \(message).")
}
}
}
We strongly recommend referring to our iOS / tvOS example multiple asset demo application which uses a ContentKeyManager
class to initialise an instance of VUDRMFairPlay for each stream, it then attaches the instance to an AssetResourceLoaderDelegate
which is added to the AssetResourceLoaderDelegateQueue
. Used with an AssetPersistenceManager
class, the ContentKeyManager
can also correctly handle persistence restoration with the updateResourceLoaderDelegate
function.
The ContentKeyManager class should look like this:
import AVFoundation
import vudrmFairPlay
class ContentKeyManager {
/// MARK: Types.
/// The singleton for `ContentKeyManager`
static let shared: ContentKeyManager = ContentKeyManager()
// MARK: Properties.
/**
The instance of `AssetResourceLoaderDelegate` which conforms to `AVAssetResourceLoaderDelegate` and is used to respond to content key requests
from `AVAssetResourceLoader`.
*/
let assetResourceLoaderDelegate: vudrmFairPlay
/// The DispatchQueue to use for delegate callbacks.
let assetResourceLoaderDelegateQueue = DispatchQueue(label: "com.vualto.vurdrm.fairplay.AssetResourceLoaderDelegateQueue")
var drm: vudrmFairPlay?
/// MARK: Initialization.
private init() {
var token = ""
var contentID = ""
for stream in StreamListManager.shared.streams {
if stream.vudrmToken != nil {
token = stream.vudrmToken!
contentID = stream.name
let asset = AVURLAsset(url: URL(string: stream.playlistURL)!)
self.drm = vudrmFairPlay(asset: asset, contentID: contentID, token: token)!
}
}
assetResourceLoaderDelegate = self.drm!
}
func updateResourceLoaderDelegate(forAsset asset: AVURLAsset) {
asset.resourceLoader.setDelegate(self.drm, queue: assetResourceLoaderDelegateQueue)
}
}
The tvOS platform does not support downloading or offline playback.
Requests for playback or download will result in a license request for the content from the license server, based on the type of token presented for each instance of VUDRMFairPlay. The tokens may be FairPlay Rental or FairPlay Persist. FairPlay Rental token license requests will be provided a streaming content key, which may be used for online streaming only. FairPlay Persist token license requests will be provided a persistent content key, which may be used for both online streaming, and offline playback of downloaded content.
In iOS, when a download of an asset has been completed with a persist token policy, further playback requests will use the downloaded asset and associated content key, even when online.
There are significant limitations using AirPlay to stream any content to an Apple TV that has been downloaded to the users device. Please see the Limitations section for further information.
To initialise your assets DRM instances, your application should create a DRM instance for each entry in the Streams.plist
file (or comparable configuration) using a ContentKeyManager
class. How to do this is demonstrated in our demo application. The ContentKeyManager
attaches instances of VUDRMFairPlay to an AssetResourceLoaderDelegate
for each asset using the asset URL, content ID, and valid token. The content ID should be unique to each asset, as it is used along with the playlist_url
to create a path to, and identify, offline assets and their associated content keys.
When using the same implementation as our demo application, iOS requests for licences for offfine content would reference the framework instance using theContentKeyManager
by calling:ContentKeyManager.shared.assetResourceLoaderDelegate.doRequestPersistableContentKeys(contentKeyIDList: asset.stream.contentKeyIDList!, streamName: asset.stream.contentID, vudrmToken: asset.stream.vudrmToken!, avUrlAsset: avasset)
The demo application that we use targets both iOS and tvOS platforms using shared source code. Each target platform references its own version of the framework.
With the VUDRMFairPlay framework installed, the example demo application reads entries in the Streams.plist
file to configure the content and DRM. You can edit the Streams.plist
file values, and/or add your own valid values, which should include a content_id
, playlist_url
, and vudrm_token
(see Preparation above).
- The
content_key_id_list
entry in theStreams.plist
is retrieved in the demo by parsing the manifest using thegetContentKeyIDList
function in theAssetListTableViewController
. - The
name
value is the display name, which may differ from thecontent_id
. - The
is_protected
boolean value indicates whether the content is protected with DRM or not. This value can be overridden in source if all content is protected.
Stream
class objects are mapped to Asset
class objects to initialise AVURLAssets using the ContentKeyManager
class.
In iOS, a call is made upon launch by the AppDelegate
class to the AssetPersistenceManager
class to restore any persisted content. The call AssetPersistenceManager.sharedManager.restorePersistenceManager()
asks the AssetPersistenceManager
class to check any already mapped asset against any outstanding AVAssetDownloadTask
.
In both target platforms, the call to initialise content from the Streams.plist
is then performed by the ContentKeyManager
class.
In iOS, the call to request an offline license from the framework is:
ContentKeyManager.shared.assetResourceLoaderDelegate.doRequestPersistableContentKeys(contentKeyIDList: asset.stream.contentKeyIDList!, streamName: asset.stream.contentID, vudrmToken: asset.stream.vudrmToken!, avUrlAsset: avasset)
and is in the: override func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath)
function of the
AssetListTableViewController
. This corresponds to the specific stream Download button.
- The SDK does not support AirPlay of protected content after a persist content key has been persisted (content downloaded to device). Apple TV is never able to play protected offline/downloaded content with a persist content key. Attempting to do so may result in unexpected behaviours, crashes referring to the content key type, or the content may not load. Protected content may be transmitted with AirPlay before downloading content (and the content key has been persisted on the users device). Protected content may be transmitted with AirPlay using a streaming content key whenever the Apple TV has a network connection.
- The SDK does not support playback of protected content when running in the iOS Simulator. Attempting to do so will result in an invalid
KEYFORMAT
error and the content will not load.
- If you believe you have found any issues, please contact us at support@vualto.com
- Most issues are content related. You can use our demo application to test your own content by updating it with your
Stream
configurations. - Errors may also arise because the
Stream
configuration is not correct.- Content ID - Please ensure that the
content_id
corresponds to that which was used when preparing your content. Thecontent_id
should always be unique for eachStream
instance. - Tokens - You can easily eliminate token issues by beginning with a default FairPlay token policy. Please ensure your token is formatted correctly and validates. For the avoidance of doubt, where tokens use dates, the dates should always be in the future. For further information about tokens please refer to our token documentation.
- Content ID - Please ensure that the
If you are not able to play your content after checking it in our demo application please contact support@vualto.com with the demo application logs and the stream configuration used.
- Update to Xcode 12.2, Swift 5.3.1.
- Minor improvements.
- Fix issue with iOS simulators.
- Merge iOS and tvOS documentation.
- Update to Xcode 12, iOS 14, Swift 5.3.
- Added error and progress notifications.
- Improved error handling.
- SDK redeveloped to:
- Resolve issue with persistence restoration after application cache clearance.
- Enable demonstration of multiple asset DRM based on Apple’s HLS Catalog example application.
- Improve flow and handling using Apple’s latest FairPlay SDK methods.
- Added retrieval of license server URI from manifest
- Update required for XCode 11.4
- Cocoapod distribution integration
- Update required for XCode 11 / iOS 13
- Update license server DNS
- Replaces instances of assetName and assetID with contentID to be consistent across DRM platform
- Update to Swift 5
- Bug fixes and improvements
- Fix streaming content key handling
- Bug fixes and improvements
- Update Persistable Offline framework to Swift 4.2
- Persistable Offline Release
- Update framework to Swift 4.2
- Bug fixes and improvements
- Update framework to Swift 4.1
-Bug fixes and improvements
- Initial Release
SMART TV’S AND CASTING¶
LG¶
LG TV’s use the WebOS SDK to provide applications to their Smart TV platforms.
Basic setup¶
WebOS applications are HTML5, Javascript applications which incorporate a WebOS Javascript library which allows access to TV functionality.
Being a HTML5, Javascript friendly environment there are two options for you when writing an application and integrating with VUDRM.
- Use one of our partner players with existing integration (Bitmovin, JWPlayer, THEOplayer).
- Use the inbuilt
luna
services with avideo
element.
Mandatory stream considerations¶
It is not possible to use the mpd.inline_drm
setting in the playout profiles for LG TV streams, this will result in playback issues.
Recommendations¶
Below are a list of choices we recommend based on our experience using the LG WebOS platform.
Using a partner player will give your viewers and your dev team the best experience at getting your application up and running as a robust understanding of PlayReady or Widevine is required.
Links to the documentation for integrating VUDRM with our partner players can be found below:
If you choose to use the luna services please follow LG’s own documentation describing how to play DRM content.
Within the LG documentation you are shown a snippet of XML you are to provide to the luna DRM service.
function getPlayReadyMessage(manifest, token) {
return '<?xml version="1.0" encoding="utf-8"?>'
+ '<PlayReadyInitiator xmlns= "http://schemas.microsoft.com/DRM/2007/03/protocols/">'
+ '<SetCustomData>'
+ '<CustomData>' + token + '</CustomData>'
+ '</SetCustomData>'
+ '</PlayReadyInitiator>';
});
}
let XMLMessages = getPlayReadyMessage(manifestString, "VUDRM TOKEN");
Tizen¶
Samsung TV’s use the Tizen SDK extension for providing applications to their Smart TV platforms.
Basic setup¶
Samsung provide extensive documentation showing how to get a development environment up and running, including this quick start guide.
DRM support¶
PlayReady DRM is currently the only DRM supported by Samsung.
Player Partners support¶
All of our player partners provide mechanisms to make use of VUDRM, and as Tizen only supports PlayReady, the documentation for each can be followed to implement.
Native support¶
Tizen has an inbuilt player within the SDK which can be used to provide encrypted playback utilising PlayReady DRM.
For further information on Samsungs PlayReady support please visit the Tizen PlayReady documentation.
Implementing a PlayReady solution can be done with the following demo repo.
// create listeners for your player
const listeners = {};
const source = "<YOUR DASH STREAM>";
const drmParam = {
DeleteLicenseAfterUse: true,
LicenseServer: "https://playready-license.vudrm.tech/rightsmanager.asmx",
CustomData: "<YOUR_VUDRM_TOKEN>"
};
webapis.avplay.open(source);
webapis.avplay.setDisplayRect(0, 0, 1920, 1080);
webapis.avplay.setListener(listeners);
webapis.avplay.setDrm("PLAYREADY", "SetProperties", JSON.stringify(drmParam));
webapis.avplay.prepareAsync(function () {
// do something
});
Tizen legacy¶
Samsung TV’s use the Tizen SDK extension for providing applications to their Smart TV platforms.
Basic setup¶
Samsung provide extensive documentation showing how to get a development environment up and running, including this quick start guide.
DRM support¶
PlayReady DRM is currently the only DRM supported by Samsung.
Player Partners support¶
All of our player partners provide mechanisms to make use of VUDRM, and as Tizen only supports PlayReady, the documentation for each can be followed to implement.
Native support¶
Tizen has an inbuilt player within the SDK which can be used to provide encrypted playback utilising PlayReady DRM.
For further information on Samsungs PlayReady support please visit the Tizen PlayReady documentation.
Implementing a PlayReady solution can be done with the following code sample.
For Samsungs full documentation on PlayReady support please visit the Tizen PlayRead documentation.
avplayObj.show();
avplayObj.open(
url,
{
drm : {
type : "Playready",
company : 'Microsoft Corporation',
deviceID : '1'
}
}
);
avplayObj.play(
self.playSuccess,
function (error) {
console.error(error.message);
},
0
);
let customData = "<YOUR VUDRM TOKEN>"
avplayObj.setPlayerProperty(3, customData, customData.length);
let laUrl ="https://playready-license.vudrm.tech/rightsmanager.asmx";
avplayObj.setPlayerProperty(4, laUrl, laUrl.length);
SefPluginPlayReady = document.getElementById('PluginSefPlayReady');
SefPluginPlayReady.Open("PlayReadyDrm", "1.000", "PlayReadyDrm");
SefPluginPlayReady.Execute("ProcessInitiatorsFromUrl", laUrl);
SefPluginPlayReady.Close();
Chromecast¶
Chromecast is a casting device developed by Google, to enable streaming from web apps upon large screen display devices.
Google have extensive documentation about their SDK. If you have never developed a Chromecast application before we would strongly advise you start here.
Basic setup¶
In order to stream DRM protected content on a Chromecast using Googles’ web SDK you will need to create a Custom Receiver app. The app will utilise two parts a Sender
and a Receiver
.
Typically the Sender
would reside either as part of the web page or your chosen player.
The Receiver
is an application that you host and register with Google.
Below is an example of basic Sender
& Receiver
side code using the Google SDK.
Sender¶
Setup of the load function within your sender
within your site or player.
load(url, token, laUrl) {
if (player.isConnected) { return };
let mediaInfo = new chrome.cast.media.MediaInfo(url);
mediaInfo.metadata = new chrome.cast.media.GenericMediaMetadata();
mediaInfo.metadata.metadataType = chrome.cast.media.MetadataType.GENERIC;
mediaInfo.metadata.title = 'VUDRM Demo';
mediaInfo.customData = { laUrl, token };
let request = new chrome.cast.media.LoadRequest(mediaInfo);
request.autoplay = true;
request.currentTime = 0;
cast.framework.CastContext.getInstance().getCurrentSession().loadMedia(request).catch((error) => {
console.error(error);
});
}
Receiver¶
Setup of the host within your receiver
application.
let videoElement = document.getElementById("your-video-element");
if (event.data.media && event.data.media.contentId){
let url = event.data.media.contentId;
let initStart = event.data.media.currentTime || 0;
let autoplay = !!event.data.autoplay;
let customData = event.data.media.customData || {};
let host = new cast.player.api.Host({mediaElement:videoElement, url});
let protocol = cast.player.api.CreateDashStreamingProtocol(host);
let player = new cast.player.api.Player(host);
player.load(protocol, initStart);
host.onError = (errorCode) => {
console.error('Fatal Error ' + errorCode);
if (player) {
player.unload();
player = null;
}
}
host.protectionSystem = cast.player.api.ContentProtection.WIDEVINE;
host.licenseUrl = 'https://widevine-license.vudrm.tech/proxy';
host.processManifest = (manifest) => {
let parser = new DOMParser();
let xmlDoc = parser.parseFromString(manifest, 'text/xml');
let el = xmlDoc.querySelector('ContentProtection');
let kid = el ? el.getAttribute('cenc:default_KID') : '';
return manifest;
};
host.updateLicenseRequestInfo = (requestInfo) => {
// customData is the message object that was sent to the receiver.
let body = JSON.stringify({
token: customData.token,
drm_info: Array.apply(null, requestInfo.content),
kid: kid
});
let buffer = [];
for (let i=0; i<body.length; i++) {
buffer.push(body.charCodeAt(i));
}
requestInfo.headers['Content-Type'] = 'application/json';
requestInfo.content = new Uint8Array(buffer);
}
protocol = cast.player.api.CreateDashStreamingProtocol(host);
Roku¶
The Roku OS was purpose-built for streaming and runs across all Roku devices, including streaming players and Roku TVs. On the Roku platform the applications that stream your media are called channels.
Basic setup¶
Follow the getting started instructions laid out in the Roku documentation to set up your environment. Roku uses it’s own scripting language called BrightScript to set up channels/apps.
We recommend using DASH with PlayReady on Roku devices.
The documentation for which can be found in the content protection.
customData = { "<VUDRM-TOKEN>" };
contentNode = createObject("roSGNode", "contentNode")
contentNode.streamFormat = "dash"
contentNode.url = "<MPEG-DASH-URL>"
contentNode.encodingType = "PlayReadyLicenseAcquisitionAndChallenge"
contentNode.encodingKey = "PlayReadyLicenseServerUrl" + "%%%" + customData
Testing your stream¶
You can also test your streams outside of the Roku app using the Stream testing tool.
- Add your device
IP
andModel
to the device manager. - Ensure that
Mode
is set toVideo
. - Ensure that
Format
is set to eitherAuto
orDASH
. - Within the DRM section choose
encoding type
:PlayReady: LA And Challenge
. - Put your newly generated VUDRM-token in to the custom data input box.
- In
License server Url(optional)
puthttps://playready-license.vudrm.tech/rightsmanager.asmx
. - Click
Play
.
ENCRYPTION KEY PROVISION¶
The VUDRM key provision service exposes the generation of DRM encryption. It provides two secure REST APIs allowing you to retrieve encryption keys for all DRM types in order to encrypt your content.
CPIX Key Provider API¶
We recommend using our CPIX API to fetch VUDRM encryption keys in CPIX XML document format. Compatible with Unified Streaming (version 1.9.3 and above).
This API can also be used to return the keys from the CPIX document in JSON format; for more information on this please see the JSON keys section.
All requests made to this API will require your API key set as the header API-KEY
. If you do not know or have not been given your API key please contact support@vualto.com.
Replace<api-key>
,<client-name>
, and<content-id>
with appropiate values.
<content-id>
may only contain alphanumeric characters, underscores, and hyphens.
Available DRM systems [fairplay
,playready
,widevine
].
Endpoints¶
Get a CPIX 2.1 document with all DRM systems <client-name>
is entitled to use.
drm
: comma separated list of DRM systems to be included in the response.
curl -XGET -H "API-KEY: <api-key>" \
"https://cpix.vudrm.tech/v1/cpix/<client-name>/<content-id>"
<?xml version="1.0" encoding="UTF-8"?>
<cpix:CPIX xmlns:pskc="urn:ietf:params:xml:ns:keyprov:pskc" xmlns:xsi="urn:ietf:params:xml:ns:keyprov:pskc" xmlns:cpix="urn:dashif:org:cpix" xsi:schemaLocation="urn:dashif:org:cpix cpix.xsd">
<cpix:ContentKeyList>
<cpix:ContentKey kid="11111111-1111-1111-1111-111111111111" explicitIV="YmFzZTY0ZW5jb2RlZAo=">
<cpix:Data>
<pskc:Secret>
<pskc:PlainValue>YmFzZTY0ZW5jb2RlZAo=</pskc:PlainValue>
</pskc:Secret>
</cpix:Data>
</cpix:ContentKey>
</cpix:ContentKeyList>
<cpix:DRMSystemList>
<cpix:DRMSystem kid="11111111-1111-1111-1111-111111111111" systemId="9a04f079-9840-4286-ab92-e65be0885f95">
<cpix:PSSH>YmFzZTY0ZW5jb2RlZAo=</cpix:PSSH>
</cpix:DRMSystem>
<cpix:DRMSystem kid="11111111-1111-1111-1111-111111111111" systemId="edef8ba9-79d6-4ace-a3c8-27dcd51d21ed">
<cpix:PSSH>YmFzZTY0ZW5jb2RlZAo=</cpix:PSSH>
</cpix:DRMSystem>
<cpix:DRMSystem kid="11111111-1111-1111-1111-111111111111" systemId="94ce86fb-07ff-4f43-adb8-93d2fa968ca2">
<cpix:URIExtXKey>YmFzZTY0ZW5jb2RlZAo=</cpix:URIExtXKey>
<cpix:HLSSignalingData>YmFzZTY0ZW5jb2RlZAo=</cpix:HLSSignalingData>
</cpix:DRMSystem>
</cpix:DRMSystemList>
</cpix:CPIX>
Get a CPIX 2.1 document with only a single content key.
drm
: comma separated list of DRM systems to be included in the response.
curl -XGET -H "API-KEY: <api-key>" \
"https://cpix.vudrm.tech/v1/cpix/<client-name>/<content-id>/decrypt"
<?xml version="1.0" encoding="UTF-8"?>
<cpix:CPIX xmlns:pskc="urn:ietf:params:xml:ns:keyprov:pskc" xmlns:xsi="urn:ietf:params:xml:ns:keyprov:pskc" xmlns:cpix="urn:dashif:org:cpix" xsi:schemaLocation="urn:dashif:org:cpix cpix.xsd">
<cpix:ContentKeyList>
<cpix:ContentKey kid="11111111-1111-1111-1111-111111111111" explicitIV="YmFzZTY0ZW5jb2RlZAo=">
<cpix:Data>
<pskc:Secret>
<pskc:PlainValue>YmFzZTY0ZW5jb2RlZAo=</pskc:PlainValue>
</pskc:Secret>
</cpix:Data>
</cpix:ContentKey>
</cpix:ContentKeyList>
</cpix:CPIX>
Get a CPIX 2.1 document that uses separate keys for video and audio tracks.
drm
: comma separated list of DRM systems to be included in the response.
curl -XGET -H "API-KEY: <api-key>" \
"https://cpix.vudrm.tech/v1/cpix/<client-name>/<content-id>/multikey"
<?xml version="1.0" encoding="UTF-8"?>
<cpix:CPIX xmlns:pskc="urn:ietf:params:xml:ns:keyprov:pskc" xmlns:xsi="urn:ietf:params:xml:ns:keyprov:pskc" xmlns:cpix="urn:dashif:org:cpix" xsi:schemaLocation="urn:dashif:org:cpix cpix.xsd">
<cpix:ContentKeyList>
<cpix:ContentKey kid="11111111-1111-1111-1111-111111111111" explicitIV="YmFzZTY0ZW5jb2RlZAo=">
<cpix:Data>
<pskc:Secret>
<pskc:PlainValue>YmFzZTY0ZW5jb2RlZAo=</pskc:PlainValue>
</pskc:Secret>
</cpix:Data>
</cpix:ContentKey>
<cpix:ContentKey kid="22222222-2222-2222-22222-22222222222" explicitIV="YmFzZTY0ZW5jb2RlZAo=">
<cpix:Data>
<pskc:Secret>
<pskc:PlainValue>YmFzZTY0ZW5jb2RlZAo=</pskc:PlainValue>
</pskc:Secret>
</cpix:Data>
</cpix:ContentKey>
</cpix:ContentKeyList>
<cpix:DRMSystemList>
<cpix:DRMSystem kid="11111111-1111-1111-1111-111111111111" systemId="9a04f079-9840-4286-ab92-e65be0885f95">
<cpix:PSSH>YmFzZTY0ZW5jb2RlZAo=</cpix:PSSH>
</cpix:DRMSystem>
<cpix:DRMSystem kid="11111111-1111-1111-1111-111111111111" systemId="edef8ba9-79d6-4ace-a3c8-27dcd51d21ed">
<cpix:PSSH>YmFzZTY0ZW5jb2RlZAo=</cpix:PSSH>
</cpix:DRMSystem>
<cpix:DRMSystem kid="11111111-1111-1111-1111-111111111111" systemId="94ce86fb-07ff-4f43-adb8-93d2fa968ca2">
<cpix:URIExtXKey>YmFzZTY0ZW5jb2RlZAo=</cpix:URIExtXKey>
<cpix:HLSSignalingData playlist="master">YmFzZTY0ZW5jb2RlZAo=</cpix:HLSSignalingData>
<cpix:HLSSignalingData playlist="media">YmFzZTY0ZW5jb2RlZAo=</cpix:HLSSignalingData>
</cpix:DRMSystem>
<cpix:DRMSystem kid="22222222-2222-2222-22222-22222222222" systemId="9a04f079-9840-4286-ab92-e65be0885f95">
<cpix:PSSH>YmFzZTY0ZW5jb2RlZAo=</cpix:PSSH>
</cpix:DRMSystem>
<cpix:DRMSystem kid="22222222-2222-2222-22222-22222222222" systemId="edef8ba9-79d6-4ace-a3c8-27dcd51d21ed">
<cpix:PSSH>YmFzZTY0ZW5jb2RlZAo=</cpix:PSSH>
</cpix:DRMSystem>
<cpix:DRMSystem kid="22222222-2222-2222-22222-22222222222" systemId="94ce86fb-07ff-4f43-adb8-93d2fa968ca2">
<cpix:URIExtXKey>YmFzZTY0ZW5jb2RlZAo=</cpix:URIExtXKey>
<cpix:HLSSignalingData playlist="master">YmFzZTY0ZW5jb2RlZAo=</cpix:HLSSignalingData>
<cpix:HLSSignalingData playlist="media">YmFzZTY0ZW5jb2RlZAo=</cpix:HLSSignalingData>
</cpix:DRMSystem>
</cpix:DRMSystemList>
<cpix:ContentKeyUsageRuleList>
<cpix:ContentKeyUsageRule kid="11111111-1111-1111-1111-111111111111">
<cpix:VideoFilter></cpix:VideoFilter>
</cpix:ContentKeyUsageRule>
<cpix:ContentKeyUsageRule kid="22222222-2222-2222-22222-22222222222">
<cpix:AudioFilter></cpix:AudioFilter>
</cpix:ContentKeyUsageRule>
</cpix:ContentKeyUsageRuleList>
</cpix:CPIX>
Get a CPIX 2.1 document that uses only 1 content key for video; audio tracks are left in the clear.
drm
: comma separated list of DRM systems to be included in the response.
curl -XGET -H "API-KEY: <api-key>" \
"https://cpix.vudrm.tech/v1/cpix/<client-name>/<content-id>/multikey/audioclear"
<?xml version="1.0" encoding="UTF-8"?>
<cpix:CPIX xmlns:pskc="urn:ietf:params:xml:ns:keyprov:pskc" xmlns:xsi="urn:ietf:params:xml:ns:keyprov:pskc" xmlns:cpix="urn:dashif:org:cpix" xsi:schemaLocation="urn:dashif:org:cpix cpix.xsd">
<cpix:ContentKeyList>
<cpix:ContentKey kid="11111111-1111-1111-1111-111111111111" explicitIV="YmFzZTY0ZW5jb2RlZAo=">
<cpix:Data>
<pskc:Secret>
<pskc:PlainValue>YmFzZTY0ZW5jb2RlZAo=</pskc:PlainValue>
</pskc:Secret>
</cpix:Data>
</cpix:ContentKey>
<cpix:ContentKey kid="22222222-2222-2222-22222-22222222222">
</cpix:ContentKey>
</cpix:ContentKeyList>
<cpix:DRMSystemList>
<cpix:DRMSystem kid="11111111-1111-1111-1111-111111111111" systemId="9a04f079-9840-4286-ab92-e65be0885f95">
<cpix:PSSH>YmFzZTY0ZW5jb2RlZAo=</cpix:PSSH>
</cpix:DRMSystem>
<cpix:DRMSystem kid="11111111-1111-1111-1111-111111111111" systemId="edef8ba9-79d6-4ace-a3c8-27dcd51d21ed">
<cpix:PSSH>YmFzZTY0ZW5jb2RlZAo=</cpix:PSSH>
</cpix:DRMSystem>
<cpix:DRMSystem kid="11111111-1111-1111-1111-111111111111" systemId="94ce86fb-07ff-4f43-adb8-93d2fa968ca2">
<cpix:URIExtXKey>YmFzZTY0ZW5jb2RlZAo=</cpix:URIExtXKey>
<cpix:HLSSignalingData playlist="master">YmFzZTY0ZW5jb2RlZAo=</cpix:HLSSignalingData>
<cpix:HLSSignalingData playlist="media">YmFzZTY0ZW5jb2RlZAo=</cpix:HLSSignalingData>
</cpix:DRMSystem>
<cpix:DRMSystem kid="22222222-2222-2222-22222-22222222222" systemId="9a04f079-9840-4286-ab92-e65be0885f95">
<cpix:PSSH>YmFzZTY0ZW5jb2RlZAo=</cpix:PSSH>
</cpix:DRMSystem>
<cpix:DRMSystem kid="22222222-2222-2222-22222-22222222222" systemId="edef8ba9-79d6-4ace-a3c8-27dcd51d21ed">
<cpix:PSSH>YmFzZTY0ZW5jb2RlZAo=</cpix:PSSH>
</cpix:DRMSystem>
<cpix:DRMSystem kid="22222222-2222-2222-22222-22222222222" systemId="94ce86fb-07ff-4f43-adb8-93d2fa968ca2">
<cpix:URIExtXKey>YmFzZTY0ZW5jb2RlZAo=</cpix:URIExtXKey>
<cpix:HLSSignalingData playlist="master">YmFzZTY0ZW5jb2RlZAo=</cpix:HLSSignalingData>
<cpix:HLSSignalingData playlist="media">YmFzZTY0ZW5jb2RlZAo=</cpix:HLSSignalingData>
</cpix:DRMSystem>
</cpix:DRMSystemList>
<cpix:ContentKeyUsageRuleList>
<cpix:ContentKeyUsageRule kid="11111111-1111-1111-1111-111111111111">
<cpix:VideoFilter></cpix:VideoFilter>
</cpix:ContentKeyUsageRule>
<cpix:ContentKeyUsageRule kid="22222222-2222-2222-22222-22222222222">
<cpix:AudioFilter></cpix:AudioFilter>
</cpix:ContentKeyUsageRule>
</cpix:ContentKeyUsageRuleList>
</cpix:CPIX>
Get a CPIX 2.1 document that uses different content keys per bitrate and one for audio. If provided with a single bitrate the document will use one key for all bitrates up to the given bitrate, and one key for the given bitrate up. If provided with two bitrates the document will use one key for all bitrates up to the lowest given bitrate, one key for all bitrates between the lowest and highest bitrates, and one key for the highest given bitrate up.
bitrates
: a single bitrate, or two seperated by a comma.
drm
: comma separated list of DRM systems to be included in the response.
curl -XGET -H "API-KEY: <api-key>" \
"https://cpix.vudrm.tech/v1/cpix/<client-name>/<content-id>/multikey?bitrates=8"
<?xml version="1.0" encoding="UTF-8"?>
<cpix:CPIX xmlns:pskc="urn:ietf:params:xml:ns:keyprov:pskc" xmlns:xsi="urn:ietf:params:xml:ns:keyprov:pskc" xmlns:cpix="urn:dashif:org:cpix" xmlns:speke="urn:aws:amazon:com:speke" xsi:schemaLocation="urn:dashif:org:cpix cpix.xsd">
<cpix:ContentKeyList>
<cpix:ContentKey kid="11111111-1111-1111-1111-111111111111" explicitIV="YmFzZTY0ZW5jb2RlZAo=">
<cpix:Data>
<pskc:Secret>
<pskc:PlainValue>YmFzZTY0ZW5jb2RlZAo=</pskc:PlainValue>
</pskc:Secret>
</cpix:Data>
</cpix:ContentKey>
<cpix:ContentKey kid="22222222-2222-2222-22222-22222222222" explicitIV="YmFzZTY0ZW5jb2RlZAo=">
<cpix:Data>
<pskc:Secret>
<pskc:PlainValue>YmFzZTY0ZW5jb2RlZAo=</pskc:PlainValue>
</pskc:Secret>
</cpix:Data>
</cpix:ContentKey>
<cpix:ContentKey kid="33333333-3333-3333-33333-33333333333" explicitIV="YmFzZTY0ZW5jb2RlZAo=">
<cpix:Data>
<pskc:Secret>
<pskc:PlainValue>YmFzZTY0ZW5jb2RlZAo=</pskc:PlainValue>
</pskc:Secret>
</cpix:Data>
</cpix:ContentKey>
</cpix:ContentKeyList>
<cpix:DRMSystemList>
<cpix:DRMSystem kid="11111111-1111-1111-1111-111111111111" systemId="9a04f079-9840-4286-ab92-e65be0885f95">
<cpix:PSSH>YmFzZTY0ZW5jb2RlZAo=</cpix:PSSH>
</cpix:DRMSystem>
<cpix:DRMSystem kid="11111111-1111-1111-1111-111111111111" systemId="edef8ba9-79d6-4ace-a3c8-27dcd51d21ed">
<cpix:PSSH>YmFzZTY0ZW5jb2RlZAo=</cpix:PSSH>
</cpix:DRMSystem>
<cpix:DRMSystem kid="11111111-1111-1111-1111-111111111111" systemId="94ce86fb-07ff-4f43-adb8-93d2fa968ca2">
<cpix:URIExtXKey>YmFzZTY0ZW5jb2RlZAo=</cpix:URIExtXKey>
<cpix:HLSSignalingData playlist="master">YmFzZTY0ZW5jb2RlZAo=</cpix:HLSSignalingData>
<cpix:HLSSignalingData playlist="media">YmFzZTY0ZW5jb2RlZAo=</cpix:HLSSignalingData>
</cpix:DRMSystem>
<cpix:DRMSystem kid="22222222-2222-2222-22222-22222222222" systemId="9a04f079-9840-4286-ab92-e65be0885f95">
<cpix:PSSH>YmFzZTY0ZW5jb2RlZAo=</cpix:PSSH>
</cpix:DRMSystem>
<cpix:DRMSystem kid="22222222-2222-2222-22222-22222222222" systemId="edef8ba9-79d6-4ace-a3c8-27dcd51d21ed">
<cpix:PSSH>YmFzZTY0ZW5jb2RlZAo=</cpix:PSSH>
</cpix:DRMSystem>
<cpix:DRMSystem kid="22222222-2222-2222-22222-22222222222" systemId="94ce86fb-07ff-4f43-adb8-93d2fa968ca2">
<cpix:URIExtXKey>YmFzZTY0ZW5jb2RlZAo=</cpix:URIExtXKey>
<cpix:HLSSignalingData playlist="master">YmFzZTY0ZW5jb2RlZAo=</cpix:HLSSignalingData>
<cpix:HLSSignalingData playlist="media">YmFzZTY0ZW5jb2RlZAo=</cpix:HLSSignalingData>
</cpix:DRMSystem>
<cpix:DRMSystem kid="33333333-3333-3333-33333-33333333333" systemId="9a04f079-9840-4286-ab92-e65be0885f95">
<cpix:PSSH>YmFzZTY0ZW5jb2RlZAo=</cpix:PSSH>
</cpix:DRMSystem>
<cpix:DRMSystem kid="33333333-3333-3333-33333-33333333333" systemId="edef8ba9-79d6-4ace-a3c8-27dcd51d21ed">
<cpix:PSSH>YmFzZTY0ZW5jb2RlZAo=</cpix:PSSH>
</cpix:DRMSystem>
<cpix:DRMSystem kid="33333333-3333-3333-33333-33333333333" systemId="94ce86fb-07ff-4f43-adb8-93d2fa968ca2">
<cpix:URIExtXKey>YmFzZTY0ZW5jb2RlZAo=</cpix:URIExtXKey>
<cpix:HLSSignalingData playlist="master">YmFzZTY0ZW5jb2RlZAo=</cpix:HLSSignalingData>
<cpix:HLSSignalingData playlist="media">YmFzZTY0ZW5jb2RlZAo=</cpix:HLSSignalingData>
</cpix:DRMSystem>
</cpix:DRMSystemList>
<cpix:ContentKeyPeriodList></cpix:ContentKeyPeriodList>
<cpix:ContentKeyUsageRuleList>
<cpix:ContentKeyUsageRule kid="11111111-1111-1111-1111-111111111111">
<cpix:BitrateFilter maxBitrate="8"></cpix:BitrateFilter>
</cpix:ContentKeyUsageRule>
<cpix:ContentKeyUsageRule kid="22222222-2222-2222-22222-22222222222">
<cpix:BitrateFilter minBitrate="8"></cpix:BitrateFilter>
</cpix:ContentKeyUsageRule>
<cpix:ContentKeyUsageRule kid="33333333-3333-3333-33333-33333333333">
<cpix:AudioFilter></cpix:AudioFilter>
</cpix:ContentKeyUsageRule>
</cpix:ContentKeyUsageRuleList>
</cpix:CPIX>
Get a CPIX 2.1 document that uses different content keys per video qualty and one for audio. If provided with multiple video qualities the response will contain a encryption key for each video quality and one encrypt key for everything else.
videoTracks
: a list of video qualities seperated by a comma, can only containSD
,HD
, orUHD
.
drm
: comma separated list of DRM systems to be included in the response.
curl -XGET -H "API-KEY: <api-key>" \
"https://cpix.vudrm.tech/v1/cpix/<client-name>/<content-id>/multikey?videoTracks=SD,HD"
<?xml version="1.0" encoding="UTF-8"?>
<cpix:CPIX xmlns:pskc="urn:ietf:params:xml:ns:keyprov:pskc" xmlns:xsi="urn:ietf:params:xml:ns:keyprov:pskc" xmlns:cpix="urn:dashif:org:cpix" xmlns:speke="urn:aws:amazon:com:speke" xsi:schemaLocation="urn:dashif:org:cpix cpix.xsd">
<cpix:ContentKeyList>
<cpix:ContentKey kid="11111111-1111-1111-1111-111111111111" explicitIV="YmFzZTY0ZW5jb2RlZAo=">
<cpix:Data>
<pskc:Secret>
<pskc:PlainValue>YmFzZTY0ZW5jb2RlZAo=</pskc:PlainValue>
</pskc:Secret>
</cpix:Data>
</cpix:ContentKey>
<cpix:ContentKey kid="22222222-2222-2222-22222-22222222222" explicitIV="YmFzZTY0ZW5jb2RlZAo=">
<cpix:Data>
<pskc:Secret>
<pskc:PlainValue>YmFzZTY0ZW5jb2RlZAo=</pskc:PlainValue>
</pskc:Secret>
</cpix:Data>
</cpix:ContentKey>
<cpix:ContentKey kid="33333333-3333-3333-33333-33333333333" explicitIV="YmFzZTY0ZW5jb2RlZAo=">
<cpix:Data>
<pskc:Secret>
<pskc:PlainValue>YmFzZTY0ZW5jb2RlZAo=</pskc:PlainValue>
</pskc:Secret>
</cpix:Data>
</cpix:ContentKey>
<cpix:ContentKey kid="44444444-4444-4444-44444-44444444444" explicitIV="YmFzZTY0ZW5jb2RlZAo=">
<cpix:Data>
<pskc:Secret>
<pskc:PlainValue>YmFzZTY0ZW5jb2RlZAo=</pskc:PlainValue>
</pskc:Secret>
</cpix:Data>
</cpix:ContentKey>
</cpix:ContentKeyList>
<cpix:DRMSystemList>
<cpix:DRMSystem kid="11111111-1111-1111-1111-111111111111" systemId="9a04f079-9840-4286-ab92-e65be0885f95">
<cpix:PSSH>YmFzZTY0ZW5jb2RlZAo=</cpix:PSSH>
</cpix:DRMSystem>
<cpix:DRMSystem kid="11111111-1111-1111-1111-111111111111" systemId="edef8ba9-79d6-4ace-a3c8-27dcd51d21ed">
<cpix:PSSH>YmFzZTY0ZW5jb2RlZAo=</cpix:PSSH>
</cpix:DRMSystem>
<cpix:DRMSystem kid="11111111-1111-1111-1111-111111111111" systemId="94ce86fb-07ff-4f43-adb8-93d2fa968ca2">
<cpix:URIExtXKey>YmFzZTY0ZW5jb2RlZAo=</cpix:URIExtXKey>
<cpix:HLSSignalingData playlist="master">YmFzZTY0ZW5jb2RlZAo=</cpix:HLSSignalingData>
<cpix:HLSSignalingData playlist="media">YmFzZTY0ZW5jb2RlZAo=</cpix:HLSSignalingData>
</cpix:DRMSystem>
<cpix:DRMSystem kid="22222222-2222-2222-22222-22222222222" systemId="9a04f079-9840-4286-ab92-e65be0885f95">
<cpix:PSSH>YmFzZTY0ZW5jb2RlZAo=</cpix:PSSH>
</cpix:DRMSystem>
<cpix:DRMSystem kid="22222222-2222-2222-22222-22222222222" systemId="edef8ba9-79d6-4ace-a3c8-27dcd51d21ed">
<cpix:PSSH>YmFzZTY0ZW5jb2RlZAo=</cpix:PSSH>
</cpix:DRMSystem>
<cpix:DRMSystem kid="22222222-2222-2222-22222-22222222222" systemId="94ce86fb-07ff-4f43-adb8-93d2fa968ca2">
<cpix:URIExtXKey>YmFzZTY0ZW5jb2RlZAo=</cpix:URIExtXKey>
<cpix:HLSSignalingData playlist="master">YmFzZTY0ZW5jb2RlZAo=</cpix:HLSSignalingData>
<cpix:HLSSignalingData playlist="media">YmFzZTY0ZW5jb2RlZAo=</cpix:HLSSignalingData>
</cpix:DRMSystem>
<cpix:DRMSystem kid="33333333-3333-3333-33333-33333333333" systemId="9a04f079-9840-4286-ab92-e65be0885f95">
<cpix:PSSH>YmFzZTY0ZW5jb2RlZAo=</cpix:PSSH>
</cpix:DRMSystem>
<cpix:DRMSystem kid="33333333-3333-3333-33333-33333333333" systemId="edef8ba9-79d6-4ace-a3c8-27dcd51d21ed">
<cpix:PSSH>YmFzZTY0ZW5jb2RlZAo=</cpix:PSSH>
</cpix:DRMSystem>
<cpix:DRMSystem kid="33333333-3333-3333-33333-33333333333" systemId="94ce86fb-07ff-4f43-adb8-93d2fa968ca2">
<cpix:URIExtXKey>YmFzZTY0ZW5jb2RlZAo=</cpix:URIExtXKey>
<cpix:HLSSignalingData playlist="master">YmFzZTY0ZW5jb2RlZAo=</cpix:HLSSignalingData>
<cpix:HLSSignalingData playlist="media">YmFzZTY0ZW5jb2RlZAo=</cpix:HLSSignalingData>
</cpix:DRMSystem>
<cpix:DRMSystem kid="44444444-4444-4444-44444-44444444444" systemId="9a04f079-9840-4286-ab92-e65be0885f95">
<cpix:PSSH>YmFzZTY0ZW5jb2RlZAo=</cpix:PSSH>
</cpix:DRMSystem>
<cpix:DRMSystem kid="44444444-4444-4444-44444-44444444444" systemId="edef8ba9-79d6-4ace-a3c8-27dcd51d21ed">
<cpix:PSSH>YmFzZTY0ZW5jb2RlZAo=</cpix:PSSH>
</cpix:DRMSystem>
<cpix:DRMSystem kid="44444444-4444-4444-44444-44444444444" systemId="94ce86fb-07ff-4f43-adb8-93d2fa968ca2">
<cpix:URIExtXKey>YmFzZTY0ZW5jb2RlZAo=</cpix:URIExtXKey>
<cpix:HLSSignalingData playlist="master">YmFzZTY0ZW5jb2RlZAo=</cpix:HLSSignalingData>
<cpix:HLSSignalingData playlist="media">YmFzZTY0ZW5jb2RlZAo=</cpix:HLSSignalingData>
</cpix:DRMSystem>
</cpix:DRMSystemList>
<cpix:ContentKeyPeriodList></cpix:ContentKeyPeriodList>
<cpix:ContentKeyUsageRuleList>
<cpix:ContentKeyUsageRule kid="11111111-1111-1111-1111-111111111111">
<cpix:BitrateFilter maxPixels="409920"></cpix:BitrateFilter>
</cpix:ContentKeyUsageRule>
<cpix:ContentKeyUsageRule kid="22222222-2222-2222-22222-22222222222">
<cpix:BitrateFilter minPixels="409921" maxPixels="2073600"></cpix:BitrateFilter>
</cpix:ContentKeyUsageRule>
<cpix:ContentKeyUsageRule kid="33333333-3333-3333-33333-33333333333">
<cpix:VideoFilter minPixels="2073601"></cpix:VideoFilter>
</cpix:ContentKeyUsageRule>
<cpix:ContentKeyUsageRule kid="44444444-4444-4444-44444-44444444444">
<cpix:AudioFilter></cpix:AudioFilter>
</cpix:ContentKeyUsageRule>
</cpix:ContentKeyUsageRuleList>
</cpix:CPIX>
Get a CPIX 2.1 document with Fairplay keys at every given interval between two given times; key rotation is only supported by HLS with Fairplay or AES.
Key rotation works with USP versions 1.10.12
and 1.10.18
. If you wish to use version 1.9.5
the explicitIV
needs to be removed from each content key before the CPIX document is used. This can be done with the following command if the CPIX document has been stored in a file name keys.cpix
.
sed -i -E 's/ explicitIV=.*"//' keys.cpix
start
: start time inyyyy-MM-ddThh-mm-ssZ
format. E.g. 1970-01-01T00:00:00Zend
: end time inyyyy-MM-ddThh-mm-ssZ
format. E.g. 1970-01-01T01:00:00Zinterval
: interval each key should last in seconds.
drm
: comma separated list of DRM systems to be included in the response. Can only befairplay
oraes
curl -XGET -H "API-KEY: <api-key>" \
"https://cpix-beta.eu-central-1.vudrm.tech/v1/cpix/<client-name>/<content-id>/rotate?start=<start-time>&end=<end-time>&interval=<interval>"
<?xml version="1.0" encoding="UTF-8"?>
<cpix:CPIX xmlns:pskc="urn:ietf:params:xml:ns:keyprov:pskc" xmlns:xsi="urn:ietf:params:xml:ns:keyprov:pskc" xmlns:cpix="urn:dashif:org:cpix" xmlns:speke="urn:aws:amazon:com:speke" xsi:schemaLocation="urn:dashif:org:cpix cpix.xsd">
<cpix:ContentKeyList>
<cpix:ContentKey kid="11111111-1111-1111-1111-111111111111" explicitIV="YmFzZTY0ZW5jb2RlZAo=">
<cpix:Data>
<pskc:Secret>
<pskc:PlainValue>YmFzZTY0ZW5jb2RlZAo=</pskc:PlainValue>
</pskc:Secret>
</cpix:Data>
</cpix:ContentKey>
<cpix:ContentKey kid="22222222-2222-2222-22222-22222222222" explicitIV="YjIxZmRlNzI5MDUyMDEzYw==">
<cpix:Data>
<pskc:Secret>
<pskc:PlainValue>YmFzZTY0ZW5jb2RlZAo=</pskc:PlainValue>
</pskc:Secret>
</cpix:Data>
</cpix:ContentKey>
<cpix:ContentKey kid="33333333-3333-3333-3333-333333333333" explicitIV="YmFzZTY0ZW5jb2RlZAo=">
<cpix:Data>
<pskc:Secret>
<pskc:PlainValue>YmFzZTY0ZW5jb2RlZAo=</pskc:PlainValue>
</pskc:Secret>
</cpix:Data>
</cpix:ContentKey>
</cpix:ContentKeyList>
<cpix:DRMSystemList>
<cpix:DRMSystem kid="11111111-1111-1111-1111-111111111111" systemId="94ce86fb-07ff-4f43-adb8-93d2fa968ca2">
<cpix:URIExtXKey>YmFzZTY0ZW5jb2RlZAo=</cpix:URIExtXKey>
<cpix:HLSSignalingData>YmFzZTY0ZW5jb2RlZAo=</cpix:HLSSignalingData>
</cpix:DRMSystem>
<cpix:DRMSystem kid="22222222-2222-2222-22222-22222222222" systemId="94ce86fb-07ff-4f43-adb8-93d2fa968ca2">
<cpix:URIExtXKey>YmFzZTY0ZW5jb2RlZAo=</cpix:URIExtXKey>
<cpix:HLSSignalingData>YmFzZTY0ZW5jb2RlZAo=</cpix:HLSSignalingData>
</cpix:DRMSystem>
<cpix:DRMSystem kid="33333333-3333-3333-3333-333333333333" systemId="94ce86fb-07ff-4f43-adb8-93d2fa968ca2">
<cpix:URIExtXKey>YmFzZTY0ZW5jb2RlZAo=</cpix:URIExtXKey>
<cpix:HLSSignalingData>YmFzZTY0ZW5jb2RlZAo=</cpix:HLSSignalingData>
</cpix:DRMSystem>
</cpix:DRMSystemList>
<cpix:ContentKeyPeriodList>
<cpix:ContentKeyPeriod id="period_0" start="1970-01-01T00:00:00Z" end="1970-01-01T00:30:00Z"></cpix:ContentKeyPeriod>
<cpix:ContentKeyPeriod id="period_1" start="1970-01-01T00:30:00Z" end="1970-01-01T01:00:00Z"></cpix:ContentKeyPeriod>
<cpix:ContentKeyPeriod id="period_2" start="1970-01-01T01:00:00Z" end="1970-01-01T01:30:00Z"></cpix:ContentKeyPeriod>
</cpix:ContentKeyPeriodList>
<cpix:ContentKeyUsageRuleList>
<cpix:ContentKeyUsageRule kid="11111111-1111-1111-1111-111111111111">
<cpix:KeyPeriodFilter periodId="period_0"></cpix:KeyPeriodFilter>
</cpix:ContentKeyUsageRule>
<cpix:ContentKeyUsageRule kid="22222222-2222-2222-22222-22222222222">
<cpix:KeyPeriodFilter periodId="period_1"></cpix:KeyPeriodFilter>
</cpix:ContentKeyUsageRule>
<cpix:ContentKeyUsageRule kid="33333333-3333-3333-3333-333333333333">
<cpix:KeyPeriodFilter periodId="period_2"></cpix:KeyPeriodFilter>
</cpix:ContentKeyUsageRule>
</cpix:ContentKeyUsageRuleList>
</cpix:CPIX>
Get encryption keys and license URLs for a given <content-id>
as JSON.
drm
: comma separated list of DRM systems to be included in the response.
curl -XGET -H "API-KEY: <api-key>" \
"https://cpix.vudrm.tech/v1/keys/<client-name>/<content-id>"
{
"key_id_hex": "76616c7565686578666f726d61746564",
"content_key_hex": "76616c7565686578666f726d61746564",
"iv_hex": "76616c7565686578666f726d61746564",
"playready_pssh_data": "YmFzZTY0LXBsYXlyZWFkeS1oZWFkZXItb2JqZWN0Cg==",
"playready_system_id": "9a04f079-9840-4286-ab92-e65be0885f95",
"widevine_drm_specific_data": "YmFzZTY0LXdpZGV2aW5lLXBzc2gtZGF0YQo=",
"widevine_system_id": "edef8ba9-79d6-4ace-a3c8-27dcd51d21ed",
"fairplay_system_id": "94ce86fb-07ff-4f43-adb8-93d2fa968ca2",
"fairplay_laurl": "skd://fairplay-license.vudrm.tech/license/<content-id>",
}
The values are:
key_id_hex
: Unique ID for the encryption. Base16 in Little Endian. Used by PlayReady and Widevine.content_key_hex
: 128bit encryption key in base16 in Little Endian. Used by Fairplay, PlayReady, and Widevine.iv_hex
: The encryption key. Used by Fairplay.playready_pssh_data
: Base 64 encoded pssh box containing the PlayReady header.playready_system_id
: The DASH protection system-specific identifier for PlayReady.widevine_drm_specific_data
: Base 64 encoded pssh box containing the content id.widevine_system_id
: The DASH protection system-specific identifier for Widevine.fairplay_system_id
: The DASH protection system-specific identifier for Fairplay.fairplay_laurl
: The Fairplay license url.
SPEKE Key Provider API¶
These are the docs for the speke key provider, which has a speke endpoint for use with AWS. To learn how to use our SPEKE API with AWS’ Media Services please read our guide.
To retrieve drm information formatted to the AWS SPEKE standard send a POST request, formatting below, to https://speke.vudrm.tech//client-name/speke, where “client-name” is the name of the client.
Examples¶
Content-Type: application/xml
API-KEY: <api-key>
<?xml version="1.0" encoding="UTF-8"?>
<cpix:CPIX id="someContentId" xmlns:cpix="urn:dashif:org:cpix" xmlns:pskc="urn:ietf:params:xml:ns:keyprov:pskc" xmlns:speke="urn:aws:amazon:com:speke">
<cpix:ContentKeyList>
<cpix:ContentKey kid="" explicitIV=""/>
</cpix:ContentKeyList>
<cpix:DRMSystemList>
<!-- playready -->
<cpix:DRMSystem kid="" systemId="9a04f079-9840-4286-ab92-e65be0885f95">
</cpix:DRMSystem>
<!-- widevine -->
<cpix:DRMSystem kid="" systemId="edef8ba9-79d6-4ace-a3c8-27dcd51d21ed">
</cpix:DRMSystem>
</cpix:DRMSystemList>
</cpix:CPIX>
<?xml version="1.0" encoding="UTF-8"?>
<cpix:CPIX id="someContentId" xmlns:cpix="urn:dashif:org:cpix" xmlns:pskc="urn:ietf:params:xml:ns:keyprov:pskc" xmlns:speke="urn:aws:amazon:com:speke">
<cpix:ContentKeyList>
<cpix:ContentKey kid="" explicitIV=""/>
</cpix:ContentKeyList>
<cpix:DRMSystemList>
<!-- fairplay -->
<cpix:DRMSystem kid="" systemId="94ce86fb-07ff-4f43-adb8-93d2fa968ca2">
</cpix:DRMSystem>
</cpix:DRMSystemList>
</cpix:CPIX>
Content-Type: application/xml
<?xml version="1.0" encoding="UTF-8"?>
<cpix:CPIX id="someContentId" xmlns:cpix="urn:dashif:org:cpix" xmlns:pskc="urn:ietf:params:xml:ns:keyprov:pskc" xmlns:speke="urn:aws:amazon:com:speke">
<cpix:ContentKeyList>
<cpix:ContentKey explicitIV="" kid="someKid">
<cpix:Data>
<pskc:Secret>
<pskc:PlainValue>playreadyContentKey</pskc:PlainValue>
</pskc:Secret>
</cpix:Data>
</cpix:ContentKey>
</cpix:ContentKeyList>
<cpix:DRMSystemList>
<cpix:DRMSystem kid="someKid" systemId="9a04f079-9840-4286-ab92-e65be0885f95">
<speke:ProtectionHeader>playreadyProtectionHeader</speke:ProtectionHeader>
<cpix:PSSH>playreadyPSSHBox</cpix:PSSH>
</cpix:DRMSystem>
<cpix:DRMSystem kid="someKid" systemId="edef8ba9-79d6-4ace-a3c8-27dcd51d21ed">
<PSSH>widevinePSSHBox</PSSH>
</cpix:DRMSystem>
</cpix:DRMSystemList>
</cpix:CPIX>
<?xml version="1.0" encoding="UTF-8"?>
<cpix:CPIX id="someContentId" xmlns:cpix="urn:dashif:org:cpix" xmlns:pskc="urn:ietf:params:xml:ns:keyprov:pskc" xmlns:speke="urn:aws:amazon:com:speke">
<cpix:ContentKeyList>
<cpix:ContentKey explicitIV="fairplayIvHex-base64-encoded" kid="someKid">
<cpix:Data>
<pskc:Secret>
<pskc:PlainValue>fairplayKeyHex-base64-encoded</pskc:PlainValue>
</pskc:Secret>
</cpix:Data>
</cpix:ContentKey>
</cpix:ContentKeyList>
<cpix:DRMSystemList>
<cpix:DRMSystem kid="someKid" systemId="94ce86fb-07ff-4f43-adb8-93d2fa968ca2">
<URIExtXKey>fairplayLaurl-base64-encoded</URIExtXKey>
<KeyFormat>fairplayKeyFormat-base64-encoded</KeyFormat>
<KeyFormatVersions>fairplayKeyFormatVersion-base64-encoded</KeyFormatVersions>
</cpix:DRMSystem>
</cpix:DRMSystemList>
</cpix:CPIX>
Legacy¶
Legacy JSON Key provider API¶
The Legacy JSON Key Provider API will provide all the information required in order to encrypt various types of content. In order to be as flexible as possible it will return encryption keys in Base16 and Base64 as some systems require different values.
The following sections describe the requests and responses in order to retrieve the keys. The most common scenario is to request CENC and Fairplay encryption keys.
A GET
should be made to the following URL:
https://keyprovider.vudrm.tech/<DRM_TYPE>/<CLIENT_NAME>/<CONTENT_ID>
The breakdown of this URL is:
DRM_TYPE
: The type of encryption keys being requested. Possible values arecenc
,fairplay
,widevine
,playready
, andaes
. See Using the encryption keys for more information.CLIENT_NAME
: The account name. Plese contact support@vualto.com if you do not have an account name.CONTENT_ID
: A unique content identifier. This value will always generate the same encryption keys. May only contain alphanumeric characters, underscores, and hyphens.
In order to provide the keys securely, an API key is required in the header of the request. Please contact support@vualto.com if you do not have an API key.
Below is an example curl request for cenc
encryption keys using the client vualto
and with the CONTENT_ID
of test
.
curl -X "GET" "https://keyprovider.vudrm.tech/cenc/vualto/test" -H "API_KEY: <API_KEY>"
In order to provide the keys securely, the DRM encryption keys are encrypted in the response.
The format of the response will be:
{
"key":"r8tXpf8wSSHKVnW1fz+MgZY3BTnTO+/IGFglt9oUwtKWj8eyguSLd0bzOhcn4DMF4JWOAbhbKopJE/cZWPqIfl3RCIwl46UP57/m7870+z3NWxh/JSvrfWBq4SGmkykfDLKjyLBqb5F6dDlUB+flcfZMcQh6FGk5GNGEniXliB/kyCrVDiKdwAw3hft8rT4/itFQDMRKkhJf1Fh67AZ1LyzOI3CCY/oO4w/XF/XMAJ1z92tAKULI+cPMFaXT3I77N3iaQoYN78mJkKgAr71Tlf91+ASB8jqOCHD6nNgm6nGxZlsTVK5wxdhT4zbMJq4xb7daSy7xMWdH/1eXuIh0ZhWicKJqbWHKIeifZWFras/0QzqN27rupHF3UYnYQ40V3ESE2PUIe8Cs8471QRHlruYlwtgBBmGme2ERZWFjrRFEeUt8SobQkCIZrzDEKaQ2bJMyOy1yMt8oklpFaW9pBg3yHZEK1WZn5u8ynV+ZWLI6soCNpMgkPYe2UtnFOhFRZfT8WG09FGJiouzyL3fCZAguxP8u9jkzQTv15UhwO/StdpTKCn1yxr3KoyxGAk3L|166c43d4023880a99691fd9679e6ad785305f205"
}
The value of key
has two components separated by a pipe (ASCII code 124):
- Encrypted blob containing the DRM encryption keys.
- Hash used in a checksum.
This section contains examples of how to decrypt the response from the Legacy JSON Key Provider API.
You will need the CLIENT_NAME
, SHARED_SECRET
and the KEY_PROVIDER_RESPONSE
value from the request to the Legacy JSON Key Provider API.
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace Vudrm.EncryptionExamples
{
class Program
{
private const string Client = "CLIENT_NAME";
private const string SharedSecret = "SHARED_SECRET";
private const string KeyProviderResponse = "KEY_PROVIDER_RESPONSE";
static void Main(string[] args)
{
Console.WriteLine("Decrypting keyprovider API response...");
var splitKeyProviderResponse = KeyProviderResponse.Split('|');
var computedHash = ComputeHash(SharedSecret, Client, splitKeyProviderResponse[0]);
if (computedHash == splitKeyProviderResponse[1])
{
Console.WriteLine("key providers response hash passed validation");
var decryptedKeyProviderResponse = Decrypt(splitKeyProviderResponse[0], SharedSecret);
Console.WriteLine("Decrypted key provider response: " + decryptedKeyProviderResponse);
}
else
{
Console.WriteLine("Key provider response hash did not validate");
}
Console.ReadLine();
}
private string Decrypt(string policy, string sharedSecret)
{
try
{
var encrypted = Convert.FromBase64String(policy);
var key = Encoding.ASCII.GetBytes(sharedSecret.Substring(0, 16));
var rj = new RijndaelManaged
{
Mode = CipherMode.ECB,
BlockSize = 128,
Key = key
};
var decryptor = rj.CreateDecryptor(key, null);
var ms = new MemoryStream(encrypted);
var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read);
var plain = new byte[encrypted.Length];
var decryptcount = cs.Read(plain, 0, plain.Length);
ms.Close();
cs.Close();
return (Encoding.UTF8.GetString(plain, 0, decryptcount));
}
catch
{
return policy;
}
}
private string ComputeHash(string sharedSecret, string client, string encryptedMessage)
{
var preComputed = sharedSecret + client + encryptedMessage;
var algorithm = new SHA1Managed();
var bytesOf = Encoding.UTF8.GetBytes(preComputed);
var hashOf = algorithm.ComputeHash(bytesOf);
var result = new StringBuilder();
foreach (var b in hashOf)
{
result.Append(b.ToString("x2").ToLower());
}
return result.ToString();
}
}
}
require 'base64'
require 'crypt/rijndael'
require 'digest'
require 'openssl'
client = "CLIENT_NAME"
shared_secret = "SHARED_SECRET"
encrypted_keys = "KEY_PROVIDER_RESPONSE"
message, message_hash = encrypted_keys.split('|')
pre_computed = client + message
computed_hash = Digest::SHA1.new.hexdigest(shared_secret + pre_computed)
if computed_hash == message_hash
decoded_message = Base64.strict_decode64(message)
decipher = OpenSSL::Cipher::Cipher.new('AES-128-ECB').decrypt
decipher.key = shared_secret[0..15]
unencrypted_keys = decipher.update(decoded_message) + decipher.final
else
raise "Key provider response hash did not validate"
end
package main
import (
"crypto/aes"
"crypto/sha1"
"encoding/base64"
"errors"
"fmt"
"strings"
)
var (
Client = "CLIENT"
SharedSecret = "SHARED_SECRET"
KeyProviderResponse = "KEY_PROVIDER_RESPONSE"
PKCS5 = &pkcs5{}
)
type pkcs5 struct{}
func main() {
s := strings.Split(KeyProviderResponse, "|")
encryptedMessage, hash := s[0], s[1]
generatedHash := generateKeyProviderHash(SharedSecret, Client, encryptedMessage)
if generatedHash == hash {
decryptedKeys := decrypt(encryptedMessage, SharedSecret)
fmt.Printf(decryptedKeys)
} else {
fmt.Printf("hash failed validation")
}
}
func generateKeyProviderHash(sharedSecret string, client string, encryptedMessage string) string {
v := fmt.Sprintf("%s%s%s", sharedSecret, client, encryptedMessage)
hasher := sha1.New()
hasher.Write([]byte(v))
return fmt.Sprintf("%x", hasher.Sum(nil))
}
func decrypt(encryptedData string, sharedSecret string) string {
source, _ := base64.StdEncoding.DecodeString(encryptedData)
key := []byte(sharedSecret)[0:16]
cipher, _ := aes.NewCipher([]byte(key))
buffer := make([]byte, len(source))
size := 16
for bs, be := 0, size; bs < len(source); bs, be = bs+size, be+size {
cipher.Decrypt(buffer[bs:be], source[bs:be])
}
plainBytes, _ := PKCS5.unPadding(buffer, 16)
return string(plainBytes)
}
func (p *pkcs5) unPadding(src []byte, blockSize int) ([]byte, error) {
srcLen := len(src)
paddingLen := int(src[srcLen-1])
if paddingLen >= srcLen || paddingLen > blockSize {
return nil, errors.New("padding size error")
}
return src[:srcLen-paddingLen], nil
}
import base64
import binascii
import json
import hashlib
from Crypto.Cipher import AES
client = "CLIENT"
shared_secret = "SHARED_SECRET"
key_provider_response = "KEY_PROVIDER_RESPONSE"
print("Decrypting Key Provider Response...")
split_key_provider_response = key_provider_response.split("|")
pre_computed = (shared_secret + client + split_key_provider_response[0]).encode("utf-8")
computed_hash = hashlib.sha1(pre_computed).hexdigest()
if computed_hash == split_key_provider_response[1]:
key = shared_secret[0:16]
decoded_text = base64.b64decode(split_key_provider_response[0])
aes = AES.new(key.encode("utf8"), AES.MODE_ECB)
padded_text = aes.decrypt(decoded_text)
unpadded_text = padded_text[:-padded_text[-1]]
decrypted_keys = json.loads(unpadded_text)
print("Decrypted keys: " + json.dumps(decrypted_keys))
else:
print("Hash validation failed for key provider response!")
Using the encryption keys¶
The next section explains the various encryption keys provided by the Legacy JSON Key Provider API. Each set has a different use case. For use with USP products CENC and Fairplay encryption keys are recommended. See the Unified Streaming Integration section below for more details on how to use mp4split
with the Legacy JSON Key Provider API.
This section uses the Legacy JSON Key Provider API but the JSON keys retrieved from the CPIX key provider can be used in the same way.
CENC is the Common Encryption Scheme and it standardises encryption keys between different DRM systems. This allows a single set of encryption keys to be use to encrypt a single file using different DRM systems. VUDRM supports Widevine and PlayReady when generating CENC encryption keys.
Make the following request to the Legacy JSON Key Provider API in order to retrieve cenc
Keys.
curl -X GET https://keyprovider.vudrm.tech/cenc/<CLIENT>/<CONTENT_ID> -H 'API_KEY: <API_KEY>'
The keys returned in this response can be used for PlayReady and Widevine scenarios. They allow a single piece of content to be used across multiple devices and browsers.
An example decrypted response would be:
{
"key_id_big": "qMTumUz5drgbusWY+fi5LA==",
"key_id_hex": "A8C4EE994CF976B81BBAC598F9F8B92C",
"content_key": "ETweRxyDIuDqAadsWdx0HQ==",
"content_key_hex": "113C1E471C8322E0EA01A76C59DC741D",
"playready_key_iv": "dd80b66b2fe44be4",
"playready_laurl": "https://playready-license.vudrm.tech/rightsmanager.asmx",
"playready_checksum": "j+7cluk2J58=",
"widevine_drm_specific_data": "Ig1zb21lY29udGVudGlkSOPclZsG",
"widevine_laurl": "https://widevine-license.vudrm.tech/proxy"
}
The values are:
key_id_big
: Unique ID for the encryption. Base64 in Big Endian.key_id_hex
: Unique ID for the encryption. Base16 in Little Endian. This one should be used withmp4split
.content_key
: 128bit encryption key in base64 in Big Endian.content_key_hex
: 128bit encryption key in base16 in Little Endian. This one should be used withmp4split
.playready_key_iv
: Additional random value to strengthen the PlayReady encryption.playready_laurl
: The PlayReady license server URL.playready_checksum
: A PlayReady specific security value required by some older PlayReady solutions and Wowza.widevine_drm_specific_data
: The Widevine PSSH box.widevine_laurl
: The Widevine license server URL.
Fairplay is Apple’s DRM system and is commonly used in conjunction with CENC encryption to provide support to the widest amount of devices possible.
Make the following request to retrieve fairplay
keys from the Legacy JSON Key Provider API:
curl -X GET https://keyprovider.vudrm.tech/fairplay/<CLIENT>/<CONTENT_ID> -H 'API_KEY: <API_KEY>'
An example decrypted response would be:
{
"key_hex": "457A8E3300DE6D549A95037F1C7ADEB1",
"iv_hex": "EB86EAFBD487391383E8FFF957561B0C",
"laurl": "skd://fairplay-license.vudrm.tech/license/somecontentid"
}
The values are:
key_hex
: Unique ID for the encryption.iv_hex
: The encryption key.laurl
: The Fairplay license server URL. At the point of the request to the license server theskd
protocol should be replaced withhttps
.
HLS AES-128 is the Advanced Encryption Standard using a 128 bit key, Cipher Block Chaining (CBC) and PKCS7 padding.
Make the following request to retrieve HLS AES encryption keys from the Legacy JSON Key Provider API:
curl -X GET https://keyprovider.vudrm.tech/aes/<CLIENT>/<CONTENT_ID> -H 'API_KEY: <API_KEY>'
An example decrypted response would be:
{
"key_hex":"dd682004123622a99c2a2afcdad6217c",
"key_url":"http://keyprovider.vudrm.tech:9293/aes/getkey/vualto/somecontentid"
}
The values are:
key_hex
: Base16 AES Content keykey_url
: URL to the AES Key Server
PlayReady is Microsoft’s DRM system. Only use these keys directly to target PlayReady enabled devices. The use of CENC keys is preferred.
Make the following request to retrieve playready
keys from the Legacy JSON Key Provider API:
curl -X GET https://keyprovider.vudrm.tech/playready/<CLIENT>/<CONTENT_ID> -H 'API_KEY: <API_KEY>'
An example decrypted response would be:
{
"key_id":"kX9efHkfIS++X+m8kZPDOw==",
"key_id_guid":"7c5e7f91-1f79-2f21-be5f-e9bc9193c33b",
"key_id_uuid":"917f5e7c791f-2f21-be5fe9bc9193c33b",
"key_id_hex":"917F5E7C791F212FBE5FE9BC9193C33B",
"content_key":"eI5JjujIo1Ek7lqO+3gg0A==",
"content_key_hex":"788E498EE8C8A35124EE5A8EFB7820D0",
"laurl":"http://vualto.playready-license.vudrm.tech/rightsmanager.asmx",
"service_id":"gwICI8yfIUGf4R/5qOWuqg==",
"service_id_guid":"23020283-9fcc-4121-9fe11ff9a8e5aeaa",
"key_iv":"07261ee6ee074f1d",
"checksum":"wf0goCGB2O4="
}
The values are:
key_id
: Unique ID for the encryption. Base64 in Big Endian.key_id_guid
: Unique ID for the encryption. GUID in Big Endian.key_id_uuid
: Unique ID for the encryption. UUID in Little Endian.key_id_hex
: Unique ID for the encryption. Base16 in Little Endean. This one should be used withmp4split
.content_key
: 128bit encryption key in base64.content_key_hex
: 128bit encryption key in base16. This one should be used withmp4split
.laurl
: The PlayReady license server URL.service_id
: Unique VUDRM service ID for Vualto in Base64.service_id_guid
: Unique VUDRM service ID for Vualto as a GUID.key_iv
: Additional random value to strengthen the PlayReady encryption.checksum
: Extra security value required by some older PlayReady solutions.
Widevine is Google’s DRM system. Use these keys to target Widevine enabled devices directly. The use of CENC keys is preferred.
Make the following request to retrieve widevine
keys from the Legacy JSON Key Provider API:
curl -X GET https://keyprovider.vudrm.tech/widevine/<CLIENT>/<CONTENT_ID> -H 'API_KEY: <API_KEY>'
An example decrypted response would be:
{
"key_id":"NxB8uJFHVSuaO5BjiMzuEQ==",
"key_id_hex":"37107CB89147552B9A3B906388CCEE11",
"content_key":"Unyx1s3fMFUedA688fCNxw==",
"content_key_hex":"527CB1D6CDDF30551E740EBCF1F08DC7",
"laurl":"https://widevine-license.vudrm.tech/proxy",
"drm_specific_data":"CAESEDcQfLiRR1UrmjuQY4jM7hEaBnZ1YWx0byIFdGVzdDEqAkhEMgA="
}
The values are:
key_id
: Unique ID for the encryption. Base64 in Little Endian.key_id_hex
: Unique ID for the encryption. Base16 in Little Endian. This one should be used withmp4split
.content_key
: 128bit encryption key in base64.content_key_hex
: 128bit encryption key in base16. This one should be used withmp4split
.laurl
: The Widevine license server URL.drm_specific_data
: The Widevine PSSH box.
Unified Streaming Integration¶
The encryption keys provided by the Legacy JSON Key Provider APIs are compatible with Unified Streaming Platform’s mp4split product.
The recommended approach is to call the CPIX Key Provider API to retrieve CENC and Fairplay Keys. Once the encryption keys have been retrieved they can be used with mp4split to generate an ism:
mp4split --license-key=$LICENSE_KEY -o $ISM \
--iss.key=${key_id_hex}:${content_key_hex} \
--iss.key_iv=${playready_key_iv} \
--iss.license_server_url=${playready_laurl} \
--widevine.key=${key_id_hex}:${content_key_hex} \
--widevine.license_server_url=${widevine_laurl} \
--widevine.drm_specific_data=${widevine_drm_specific_data} \
--hls.client_manifest_version=4 \
--hls.key=:${key_hex} \
--hls.key_iv=${iv_hex} \
--hls.license_server_url=${laurl} \
--hls.playout=sample_aes_streamingkeydelivery
The HLS values come from the Fairplay encryption keys, all other values are from the CENC keys.
VUDRM TOKEN¶
The VUDRM token has two purposes: authentication and the delivery of the DRM policy to the license server. It also represents a signed authorisation on the client’s behalf for Vualto to issue a DRM license to the holder of the token, issuing a VUDRM token to a player will grant that player access to the DRM-protected content.
Due to the first purpose VUDRM tokens have a limited lifetime and are designed to be single use. Please contact support@vualto.com if you do not know what the VUDRM token’s TTL is for your account.
The second purpose allows the user’s playback rights for individual pieces of content to be set dynamically. A single user may be granted different rights on a single piece of content depending on business requirements.
VUDRM tokens should be generated using the VUDRM token API. The request to the VUDRM token API should be made from a server side application and the VUDRM token should then be delivered to the client side for use by a player in a license request.
Warning
VUDRM tokens must not be generated on the client side.
VUDRM token structure¶
test-client|2018-26-11T11:01:04Z|cO9H1B2VKw0WyyNTOfoHEw==|05aff2dd06f4c52291b7a032c815ce8f599cf409
The VUDRM token is comprised of four components each separated by the pipe character (ASCII code 124):
- The client name.
- The time the token was generated in an ISO8601 format (yyyy-MM-ddThh:mm:ssZ).
- The encrypted DRM policy.
- A signed hash.
Danger
VUDRM tokens are designed to be single use and have a one to one relationship with the license request. Each license request must use a new VUDRM token. VUDRM tokens will expire, the TTL is set at an account level and in production will be set to around 30 seconds. The time a token will expire is calculated by the time the token is generated plus the TTL; The time a token was generated is displayed in the token. This is a separate time to the DRM policy times set in the encrypted DRM policy.
DRM policy¶
The DRM policy is included in the VUDRM token as the encrypted third component. When using multiple DRM providers the parameters that are applicable to ALL
will work across all DRM technologies. Parameters specific to one DRM provider can be used but will be ignored if not relevant. For example, a VUDRM token can be created that pertains to both Widevine and PlayReady. Any PlayReady specific parameters will only be applied when a PlayReady licence is required and will be ignored for Widevine.
Key | Type | Format | Applicable DRM | Description |
---|---|---|---|---|
content_id |
string | Any | ALL | The content identifier. This is used for tracking content in the stats and is not required. |
polbegin |
datetime | ISO 8601 or DD-MM-YYYY HH:mm:ss | ALL | The non ISO 8601 format is no longer recommended, please use the ISO 8601 format. Policy Begin. The start date for the license. Default value depends on the DRM provider. If using the ISO 8601 format the timezone required can be specified in the value given, e.g. 2021-03-19T14:30:00+01:00 , otherwise the default timezone for this value is Europe/London. This value comes from this database, of which we support a subset; if you wish to use a different timezone please contact support@vualto.com. |
polend |
datetime | ISO 8601 or DD-MM-YYYY HH:mm:ss | ALL | The non ISO 8601 format is no longer recommended, please use the ISO 8601 format. Policy End. The end date for the license. Default value depends on the DRM provider. If using the ISO 8601 format the timezone required can be specified in the value given, e.g. 2021-03-19T14:30:00+01:00 , otherwise the default timezone for this value is Europe/London. This value comes from this database, of which we support a subset; if you wish to use a different timezone please contact support@vualto.com. |
liccache |
string | yes OR no |
ALL | Licence Cache. Should the license be cached. Implementation depends on the DRM provider. |
firstplayback |
integer | Seconds | PlayReady, Widevine | Once playback is initiated the user will have this window to complete playback. Once the window completes the license will expire. You can not use firstplayback when liccache is set to no . |
securitylevel |
integer | 150 OR 2000 OR 3000 |
PlayReady | https://docs.microsoft.com/en-us/playready/overview/security-level |
type |
string | r OR l OR p |
Fairplay | rental, lease, or persist. See Fairplay DRM policy section for details. |
duration_rental |
string | Seconds | Fairplay | Overrides polend for Fairplay rental licenses. |
duration_lease |
string | Seconds | Fairplay | Overrides polend for Fairplay lease licenses. |
duration_persist |
string | Seconds | Fairplay | Overrides polend for Fairplay persist licenses. |
match_content_id |
bool | true or false |
ALL | Boolean for if the content id in the token should be compared with the content id used in the license request; the content id in the license request will be the same as the content id used to generate the keys that encrypted a given piece of content. |
geo_whitelist |
string array | Array of 3 letter country codes (ISO 3166-1 alpha-3) | ALL | A list of 3 letter country codes, ISO 3166-1 alpha-3, for all countries that are allowed access. |
block_vpn_and_tor |
bool | true or false |
ALL | Boolean for if ips coming from known vpn or tor networks should be blocked. |
session |
JSON | See below | ALL | A JSON object containing information about a DRM session that can be used to allow/deny a user's license requests. |
This table is not an exhaustive list, for example it does not include advanced PlayReady settings. If you require the use of more advanced settings please contact support@vualto.com
The polbegin
and polend
settings use the timezone set at the account level. Please contact support@vualto.com to confirm the timezone for your account.
Default values¶
Key | Fairplay | PlayReady | Widevine |
---|---|---|---|
content_id |
Not set | Not set | Not set |
polbegin |
Now* | 01-01-0001 00:00:00 |
Now* |
polend |
Now* | 12-31-9999 23:59:59 |
Now* |
liccache |
no |
yes |
no |
firstplayback |
N/A | 10,675,199 days | Not set |
securitylevel |
N/A | 2000 |
N/A |
type |
l |
N/A | N/A |
duration_rental |
0 |
N/A | N/A |
duration_lease |
0 |
N/A | N/A |
duration_persist |
0 |
N/A | N/A |
match_content_id |
false |
false |
false |
geo_whitelist |
Not set | Not set | Not set |
session |
Not set | Not set | Not set |
*When these values have not been set in the policy, as long as no other values would cause playback to stop, content will play indefinitely.
There are also limitations depending on environments that are not explained in the table. Please refer to the following sections for more detail:
Match content id¶
If content_id
has been set and match_content_id
has been set to true
, when a license request is made the content id in the license request and the content id in the VUDRM token will be compared. If they are the same a license will be served as normal; if they are not the same the license request will be denied. If match_content_id
has been set to false
or content_id
has not been specified then this comparision will not occur.
GEO whitelisting¶
If geo_whitelist
has been added to the DRM policy, when a license is requested the GEO location of the client’s ip will be checked. If the country is in the whitelist the license request will be successful. If the country is not in the whitelist the request will be denied. E.g. if the DRM policy is { "geo_whitelist":[ "gbr", "deu" ] }
only users from the UK and Germany will be able to make successful license requests.
DRM Session in policy¶
It is possible to set in the policy used by VUDRM token information about the current DRM session. With this information we will make a request to make to an API of your choosing with an ID and any needed headers. You can set the request to be either a GET request or a POST request. In the case of a GET request we would make a GET request to the URL you specify with a query string parameter called sessionId
set to ID the passed in the policy. E.g. a GET request to https://some-url.com/valid?sessionId=abc123
. In the case of a POST request we would make a POST request to the URL you specify with the body of the request being the sessionId
in JSON. E.g. a POST request to https://some-url.com/valid
with the body being {"sessionId":"abc123"}
. The result of the request we make will dictate whether or not a license is returned. If the result is a 200
we will serve a valid license back. If the result is not a 200
we will not return a valid license.
There are four parts to the session information:
Key | Type | Format | Example |
---|---|---|---|
id |
string | Any | abc123 |
url |
string | URL | https://some-url.com/valid |
method |
string | GET OR POST |
GET |
headers |
key-value pairs | { "key", "value", ... } |
{ "someKey", "someValue" } |
The structure of the session information in a policy should be as follows:
{
... other policy values ...
"session": {
"id": "...",
"url": "...",
"method": "...",
"headers": { "headerKey": "headerValue", ... }
}
}
{
"polbegin": "02-04-2019 12:00:00",
"polend": "02-04-2019 17:00:00",
"session": {
"id": "someID",
"url": "https://www.some-url.com/valid",
"method": "GET",
"headers": { "myHeader": "someValue", "myOtherHeader": "someOtherValue" }
}
}
Default DRM policy¶
It is possible to specify a default DRM policy that will be used when we receive a VUDRM token. For example if you wish for all licenses requested to default to caching the license, the default DRM policy would be { "liccache":"yes" }
, meaning a VUDRM token with the policy { "polend":"DD-MM-YYYY HH:mm:ss" }
would be the same as { "liccache":"yes", "polend":"DD-MM-YYYY HH:mm:ss" }
. The values in the default policy can be overridden by simply putting them in the policy you pass in the VUDRM token. For example if the default DRM policy was { "liccache":"yes" }
but you wanted a license to not be cached, the policy in the VUDRM token would be { "liccache":"no" }
. Any of the values listed above can be be set in the default DRM policy.
If you wish to utilise this feature please contact support@vualto.com.
PlayReady DRM policy¶
PlayReady has an extensive list of policy options described here. The majority of these options are supported via the VUDRM token’s policy. Please contact support@vualto.com for more details.
Fairplay DRM policy¶
Fairplay has three types of license; rental
, lease
, and persist
.
Fairplay licenses can only be persisted past the user session by an offline enabled iOS application. When using Safari a session will be preserved until the browser tab is closed.
Fairplay does not allow playback over non-HDCP connections.
You can specify a rental
license by setting the type
key to r
in the DRM policy.
You can set the expiry of the license using the duration_rental
key or the polend
key. If both duration_rental
and polend
are set in a policy then duration_rental
will be used.
When using the type
of rental
playback will continue after license expiry until the encryption keys change.
You can specify a lease
license by setting the type
key to l
in the DRM policy.
You can set the expiry of the license using the duration_lease
key or the polend
key. If both duration_lease
and polend
are set in a policy then duration_lease
will be used.
Using the type
of lease
will cause playback to stop at the license expiry. Normally a player will make a new license request at this point, please ensure the VUDRM token is updated before further license requests are made. Using an expired VUDRM token will cause the license request to fail.
A persist
license is used for offline playback.
Setting liccache
to yes
or the type
to p
will generate a persisted license. Setting liccache
to yes
will override any other type
settings.
Setting type
to p
will override setting liccache
to no
.
You can set the expiry of the license using the duration_persist
key or the polend
key. If both duration_persist
and polend
are set in a policy then duration_persist
will be used.
Using the type
of persist
will cause playback to stop at the license expiry and the license will be removed from the device.
Widevine DRM policy¶
Widevine licenses can only be persisted past the user session by an Android application. When using Chrome a session will be preserved until the browser tab is closed.
DRM policy examples¶
The following sections display some example polices. These polices apply to all DRM providers.
This policy is designed for a rental business model. The license generated from this policy will expire at the time set by the polend
value and will allow immediate playback.
{
"content_id":"filename",
"polend":"DD-MM-YYYY HH:mm:ss",
"type":"r"
}
This policy is designed for a subscription business model. The license generated from this policy will not allow playback until polbegin
is reached and will expire when polend
is reached.
{
"content_id":"filename",
"polbegin":"DD-MM-YYYY HH:mm:ss",
"polend":"DD-MM-YYYY HH:mm:ss",
"type":"l"
}
This policy is designed for an offline playback scenario. The license generated from this policy will allow playback immediately and the license will expire when the polend
value is reached. The license will be cached until the polend
value is reached.
{
"content_id":"filename",
"polend":"DD-MM-YYYY HH:mm:ss",
"liccache":"yes"
}
VUDRM token API¶
VUDRM tokens can be generated by making a request to the VUDRM token api: https://token.vudrm.tech/generate
The request to the VUDRM token API needs to be a POST and requires an API_KEY
header with your account API key as the value. Please contact support@vualto.com if you do not have this.
The body of the POST request comprises of the account name and the DRM policy.
For example:
{
"client": "YOUR_NAME",
"policy": {
"content_id": "filename",
"polend": "26-11-2018 16:48:59"
}
}
An example request to the VUDRM token API in curl is:
curl -X POST \
https://token.vudrm.tech/generate \
-H 'API_KEY: <your-api-key>' \
-d '{"client": "<client>","policy": {"content_id":"<content-id>","polend":"<pol-end>","liccache":"no"}}'
GEO LOCATION API¶
This API can be used to retrieve geographical location information about IPv4 or IPv6 addresses.
This service requires an API Key and client name. Please contact support@vualto.com if you do not have this information.
1. Make a Request¶
A GET
request should be made to the following URL:
https://geo-location.vudrm.tech/ip_lookup/<client>/<ip_address>
Replace <client>
with your client name and <ip_address>
with the IPv4 or IPv6 address.
The X_AUTH_KEY
header should be set and the value should be the Geo Location API Key.
This is not the same as your VUDRM token API key.
2. The Response¶
The response will be in JSON and will look like this:
{
"ip": "188.39.163.11",
"geo": {
"timezone_name": "europe/london",
"country": "gbr",
"country_code": 826,
"country_confidence_percentage": 99,
"two_letter_country_code": "uk",
"continent_code": 5,
"region": "ply",
"region_code": 25486,
"region_confidence_percentage": null,
"area_codes": null,
"metro_code": 826046,
"postal_code": "pl1 1ab",
"postal_confidence_percentage": 30,
"city": "plymouth",
"city_code": 12140,
"city_confidence_percentage": 60,
"longitude": 0,
"latitude": 50,
"gmt_offset": "+0",
"in_dst": false,
"connection_speed": "xdsl"
},
"proxy": {
"identification": null,
"type": null
}
}
The key ip
details the requested IP address. The following sections explain the other sections in the returned JSON.
geo
¶
Key | Type | Description |
---|---|---|
timezone_name |
string | |
country |
string | |
country_code |
integer | The ISO-3166-1 numeric country code |
country_confidence_percentage |
integer | null |
two_letter_country_code |
string | |
continent_code |
string | |
region |
string | |
region_code |
integer | Includes AOL identification for the associated country, reserved ranges and RFC 1918 reserved ranges. Follows the ISO 3166-2 standardization. |
region_confidence_percentage |
integer | The confidence percentage that the region is correct |
area_codes |
string | |
metro_code |
integer | Metro codes included Designated Market Areas (DMA’s) in the U.S., ITV regions in the U.K., Department Codes in France, German Nielsen TV Markets, South Korean Si/Gun/Gu, Chinese Diji Shi Cities, Russian Federal Districts, Norwegian municipalities, urban areas in New Zealand, and the Greater Capital City Statistical Area and Significant Urban Area in Australia. |
postal_code |
string | |
postal_confidence_percentage |
integer | The confidence percentage that the postal code is correct |
city |
string | |
city_code |
integer | |
city_confidence_percentage |
integer | |
longitude |
integer | |
latitude |
integer | |
gmt_offset |
string | Current GMT/UTC offset - format: +hhmm |
in_dst |
boolean | Are they currently observing DST? |
connection_speed |
string | Can have the following values: dialup , DSL , cable , broadband (non-specific high-speed connection), t1 , t3 , oc3 , oc12 , mobile , wireless and satellite . |
NB:
- Confidence values range from 0 to 100, with 0 representing least confidence in data sources and 100 representing total confidence in data sources.
- All values are nullable.
proxy
¶
Possible values for proxy.identification
:
Value | Description |
---|---|
tor_exit |
The gateway nodes where encrypted/anonymous Tor traffic hits the Internet. |
tor_relay |
Receives traffic on the Tor network and passes it along. Also referred to as "routers". |
cloud |
Enables ubiquitous network access to a shared pool of configurable computing resources. |
vpn |
Virtual private network that encrypts and routes all traffic through the VPN server, including programs and applications. |
dns |
A proxy used by overriding the client’s DNS value for an endpoint host to that of the proxy instead of the actual DNS value. |
web_browser |
This value will indicate connectivity that is taking place through mobile device webbrowser software that proxies the user through a centralized location. A couple of examples of this type of connectivity are Opera mobile browsers and UCBrowser. |
cloud_security |
A host accessing the Internet via a web security and data protection cloud provider. Example providers with this type of service are Zscaler, Scansafe, and Onavo. |
null |
The IP has not been identified as a proxy. |
Possible values for proxy.type
:
Value | Description |
---|---|
anonymous |
Actual IP address of client is not available. Includes services that change location to beat DRM, TOR points, temporary proxies and other masking services. |
transparent |
Actual IP address of client is available via HTTP headers, though value is not necessarily reliable (i.e., it can be spoofed). |
hosting |
Address belongs to a hosting facility or cloud provider and is likely to be a proxy as end users are not typically located in a hosting facility. |
corporate |
Generally considered harmless, but location can be a concern. Can identify if multiple users are proxied through a central location or locations, and thus share a single networkapparent IP address |
public |
Multiple users proxied from a location allowing public Internet access. |
edu |
Proxied users from an educational institution. |
aol |
AOL proxy |
blackberry |
This value will identify an IP address owned by RIM (Research In Motion) who is responsible for Blackberry mobile devices. All Blackberry users go through a centralized proxy location and thus cannot be accurately geo-targeted. |
null |
Not determined to be a proxy. |
Example Requests¶
curl -X GET \
https://geo-location.vudrm.tech/ip_lookup/vualto-demo/188.39.163.11 \
-H 'X_AUTH_KEY: <geo-location-api-key>'
package main
import (
"fmt"
"net/http"
"io/ioutil"
)
func main() {
url := "https://geo-location.vudrm.tech/ip_lookup/vualto-demo/188.39.163.11"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("X_AUTH_KEY", "<geo-location-api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := ioutil.ReadAll(res.Body)
fmt.Println(res)
fmt.Println(string(body))
}
require 'uri'
require 'net/http'
url = URI("https://geo-location.vudrm.tech/ip_lookup/vualto-demo/188.39.163.11")
http = Net::HTTP.new(url.host, url.port)
request = Net::HTTP::Get.new(url)
request["X_AUTH_KEY"] = '<geo-location-api-key>'
response = http.request(request)
puts response.read_body
var client = new RestClient("https://geo-location.vudrm.tech/ip_lookup/vualto-demo/188.39.163.11");
var request = new RestRequest(Method.GET);
request.AddHeader("X_AUTH_KEY", "<geo-location-api-key>");
IRestResponse response = client.Execute(request);
import requests
url = "https://geo-location.vudrm.tech/ip_lookup/vualto-demo/188.39.163.11"
headers = {
'X_AUTH_KEY': "<geo-location-api-key>",
}
response = requests.request("GET", url, headers=headers)
print(response.text)
<?php
$request = new HttpRequest();
$request->setUrl('https://geo-location.vudrm.tech/ip_lookup/vualto-demo/188.39.163.11');
$request->setMethod(HTTP_METH_GET);
$request->setHeaders(array(
'X_AUTH_KEY' => '<geo-location-api-key>'
));
try {
$response = $request->send();
echo $response->getBody();
} catch (HttpException $ex) {
echo $ex;
}
VUDRM SUPPORT MATRIX¶
See bottom of page for caveats.
Browsers | PlayReady | Widevine | Fairplay |
---|---|---|---|
Chrome | ✘ |
✔ |
✘ |
Edge Windows 10^ | ✔ |
✔ |
✘ |
Firefox | ✘ |
✔ |
✘ |
Internet Explorer 11 Windows 8.1^ | ✔ |
✘ |
✘ |
Safari | ✘ |
✘ |
✔ |
Opera | ✘ |
✔ |
✘ |
Mobile | PlayReady | Widevine | Fairplay |
---|---|---|---|
Android 4.4^ VUDRMWidevine SDK: offline playback available | ✘ |
✔ |
✘ |
Chrome mobile (Android) | ✘ |
✔ |
✘ |
iOS VUDRMfairplay SDK: 10^ offline available, 9^ online only | ✘ |
✘ |
✔ |
Safari mobile (iOS) iOS 11.2^ | ✘ |
✘ |
✔ |
Windows phone | ✔ |
✘ |
✘ |
Consoles | PlayReady | Widevine | Fairplay |
---|---|---|---|
PlayStation | ✔ |
✘ |
✘ |
Xbox | ✔ |
✘ |
✘ |
Smart TVs | PlayReady | Widevine | Fairplay |
---|---|---|---|
Amazon Fire | ✔ |
✔ |
✘ |
Android TV VUDRMWidevine SDK | ✘ |
✔ |
✘ |
Apple TV VUDRMfairplay SDK | ✘ |
✘ |
✔ |
Google Chromecast | ✘ |
✔ |
✘ |
Google TV | ✘ |
✔ |
✘ |
Roku | ✔ |
✔ |
✘ |
Samsung Tizen 2017 models^ | ✔ |
✘ |
✘ |
tvOS VUDRMfairplay SDK | ✘ |
✘ |
✔ |
Smart TV Alliance LG, PANASONIC, PHILIPS, TOSHIBA | ✔ |
✔ |
✘ |
- Widevine mandates all browser CDM implementations to stay current with Chrome stable releases to ensure that the latest updates are applied. Older versions of Chrome, Firefox, and Opera may not be able to use DRM. See Widevine’s deprecation schedule.
- VUDRM uses Widevine Modular by default (Widevine Classic is not supported).
- Some Smart TV Alliance models may not support Widevine.
- In Linux based systems, Widevine may not be supported on some versions of Chrome and Firefox.
RELEASE NOTES¶
- December 2021
- November 2021
- October 2021
- September 2021
- August 2021
- July 2021
- June 2021
- May 2021
- April 2021
- March 2021
- February 2021
- January 2021
- December 2020
- November 2020
- September 2020
- July 2020
- May 2020
December 2021¶
- Internal improvements to our Config API. Added functionality to integrate with JW Player systems.
- Internal improvements to our CPIX API.
November 2021¶
- Improvements to VUDRM Admin. Video.js and Dash.js added as an option for the public player.
- Improvements to our PlayReady license server. Added support for Token v2.
- Bug fixes on our FairPlay license server. Fixed an iOS issue when downloading content.
- Internal improvements to our Stats API.
- Internal improvements to our CPIX API.
- Internal improvements to our KP API.
October 2021¶
- Improvements to our FairPlay, Widevine, and KP API systems to be compatible with Token v2.
- Improvements to VUDRM Admin. HLS JS can now be enabled in the public player.
- Internal improvements to our Stats API.
- Internal improvements to our KP API.
September 2021¶
- Bug fixes on our CPIX API. Now returns the correct encryption schemes.
- Fixed issues with our PlayReady CircleCI tests.
August 2021¶
- Internal improvements to the Widevine license server. Added license renewal support.
- Internal improvements for our CPIX logging.
July 2021¶
- Updates to O/S on most services.
- Multikey support for all DRM providers added to CPIX API. Our documentation has been updated to reflect this.
- VUDRM Admin user guide has now been added to our public documentation.
- Various improvements to VUDRM Admin, including a new public demo page.
- Internal improvements for our Stats API logging.
- Beta release for VUDRM Token v2.
June 2021¶
- New VUDRM cluster based in Milan has been released and is serving traffic for the duration of the Euro 2020 Championship.
- Various improvements to VUDRM Admin, including the ability for the client to edit default VUDRM Token policy values.
- Various internal improvements to KP-API, Legacy KeyProvider API, Token API, FairPlay license server, GeoLocation API, Widevine license server and CPIX API. Updated builds and services used.
May 2021¶
- Various improvements to VUDRM Admin, including the integration of the JW Player client for testing purposes and Health page amends.
April 2021¶
- Improvements for our Widevine logging. Logs that show the request start and completion now appear in Kibana.
- Bug fixes in the VUDRM Admin site, including a client cloning issue displaying incorrect errors and the user sometimes being redirected to the Login page incorrectly.
March 2021¶
- Improvements to the AES license server. Now responds with a 4xx status code when the call to KP API fails.
- Improvements have been made to our Documentation. All documentation now follows a unified template throughout.
- Bug fix in the VUDRM Admin site where the Configuration page would sometimes break the service for certain clients.
- Improvements to the Widevine license server. Now forwards the Referer to the Stats API.
- Internal improvements to Kibana. Referer and License URL are displayed in Kibana.
February 2021¶
- Improvements to our cluster infrastructure. Pods will automatically recycle when they are 14 days old.
- Internal improvements to the KP API and Widevine license server. Settings for each can ammended via a VUDRM Token.
- Internal improvements to the Config API. Failure to connect to the Database will now respond with a 5xx status code.
January 2021¶
- Internal improvements to the ElasticSearch monitoring.
- Improvements to the Widevine license server - service has been re-written in Go.
- Various improvements to VUDRM Admin, including the ability to create a video for a client and JSON logging.
December 2020¶
- Elasticsearch migration to improve reliablity.
- Various improvements to VUDRM Admin, including a new status page, a HTTPS redirect, and speed improvements to the dashboard.
November 2020¶
- FairPlay license server now accepts a more standard request.
- Improved internal logging.
- All license requests can now accept a VUDRM token in the request body, header, or as a query string parameter.
- VPN’s and TOR networks can now be blocked by setting
block_vpn_and_tor
in a VUDRM token’s policy. - Widevine license server now returns
x-request-id
instead ofvualto-transaction-id
bringing it inline with web standards.
September 2020¶
- Improvements to license server responses to aid in issue detection.
- New VUDRM cluster based in Paris has been released and is serving traffic.
- Improvement to internal apis to reduce length of license requests.
match_content_id
in token policy now support content encrypted with multiple keys from our CPIX API.
July 2020¶
- New VUDRM cluster based in Oregon has been released and is serving traffic.
- Improved internal logging.
- Improved stats recorded.
- NGINX VOD Module support added to CPIX API.
- Harmonic support added to CPIX API.
- GEO based restrictions in can now be set in VUDRM token policy.
May 2020¶
- New VUDRM cluster based in Bahrain has been released and is serving traffic.
- New endpoint for VUDRM CPIX API has been release to work with USP version 1.9.5.
- Infrastructure update to ensure latest security patches e.t.c are running on all VUDRM clusters.
SUPPORT¶
How to contact us¶
For all non-critical support requests please email support@vualto.com. If you are a registered user you can contact support via the help centre.
For all critical issues please contact support via one of the numbers below.
- +44 (0)800 0314391
- +44 (0)1752 916051
- (800) 857 1808 (toll-free US number)
When contacting our support about a DRM issue please include as much relevant info as possible, this should include but not be limited to the x-request-id for failing requests, the VUDRM token used, the VUDRM service, the DRM type/s, relevant content URL/s, response codes of the failed request, type of device/s the issue is occurring on, and browser type. If you can not get any of this information please state in you initial message that you are unable to provide this otherwise our first response will be asking for this information.
X-Request-ID¶
All requests made to and from VUDRM services will have a header called x-request-id
. This header allows us to track a request through our backend; if you are getting unexpected errors from our services giving us the value of this header will allow us to efficiently track any errors that occurred while processing your request.
Vualto-Transaction-ID¶
On some requests you may find a header called vualto-transaction-id
; this header was originally used for the same purpose as x-request-id
header. In order to be more in line with industry standards, we are currently in the process of removing the vualto-transaction-id
and replacing it with x-request-id
. Please ensure that you use x-request-id
contact support when contacting support and in any internal logging you perform.
Fairplay onboarding support¶
In order to configure Fairplay for use with VUDRM we need the following:
- Private key: This is normally named ‘privatekey.pem’ and is encrypted with a password.
- Password for private key, so that the private key can be decrypted.
- An ‘Application Secret key’ aka the ASk. This is normally named ‘Ask.txt’.
- FairPlay certificate. This is normally named ‘fairplay.cer’.
You can request these from Apple by visiting their FairPlay streaming page and clicking on “Request FPS Deployment Package”.