Skip to content

Digital Rights Management (DRM)

dash.js offers support for playback of DRM protected content. In this context, multiple adjustments can be made.

DRM Examples

Multiple samples implementing the functionalities described in this documentation can be found in the DRM section.

Widevine

Google Widevine is supported on Chromium based browsers (Chrome, Edge, Opera), Firefox, Android and many smart TV platforms. dash.js registers the key system under the system string com.widevine.alpha (urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed). Widevine protected DASH content is typically encrypted using the cenc scheme, cbcs is supported on newer CDM versions.

The initialization data is taken from the cenc:pssh element in the MPD or from the encrypted event thrown by the EME. If the license server URL is signaled in the MPD (for instance via dashif:Laurl), playback works without any additional configuration. Otherwise, provide the license server URL via the protection data:

js
const protData = {
    "com.widevine.alpha": {
        "serverURL": "https://drm-widevine-licensing.axtest.net/AcquireLicense"
    }
};
player.setProtectionData(protData);

A Widevine service certificate can be provided directly or downloaded from a certificate server URL, see Server certificates. To unlock hardware backed playback (L1) specific robustness levels are required, see Robustness levels.

An example is available in the Widevine sample.

PlayReady

Microsoft PlayReady is supported on Windows (Edge), Xbox and a large number of smart TVs and set-top boxes. dash.js registers the key system under the system string com.microsoft.playready (urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95). Depending on the platform, additional system strings such as com.microsoft.playready.recommendation and com.microsoft.playready.hardware are available. The recommendation key system is required for cbcs encrypted content and can be prioritized via the system string priority. PlayReady protected DASH content is commonly encrypted using the cenc scheme.

The initialization data is taken from the PlayReady header in the mspr:pro/cenc:pssh elements of the MPD or from the encrypted event thrown by the EME. If the license server URL is signaled in the MPD (for instance via dashif:Laurl or the PlayReady header), playback works without any additional configuration. Otherwise, provide the license server URL via the protection data:

js
const protData = {
    "com.microsoft.playready": {
        "serverURL": "https://drm-playready-licensing.axtest.net/AcquireLicense"
    }
};
player.setProtectionData(protData);

PlayReady allows passing custom data to the CDM as part of the license acquisition. dash.js supports this via the cdmData attribute of the protection data, which is wrapped into a PlayReadyCDMData object and handed to the key session. A server certificate can be provided as well, see Server certificates.

An example is available in the PlayReady sample.

FairPlay

Apple FairPlay Streaming is supported on Apple platforms (Safari on macOS, iOS and iPadOS). dash.js registers the key system under the system string com.apple.fps (urn:uuid:94ce86fb-07ff-4f43-adb8-93d2fa968ca2). FairPlay protected DASH content is typically encrypted using the cbcs scheme.

In contrast to Widevine and PlayReady, FairPlay does not use PSSH data from the manifest — the initialization data is provided by the platform via the encrypted event (sinf init data type). If the license server URL and certificate are signaled in the MPD (for instance via dashif:laurl), playback works without any additional configuration:

js
const player = dashjs.MediaPlayer().create();
player.initialize(video, url, true);

Otherwise, provide the license server URL — and, if required by your DRM provider, the FairPlay server certificate — via the protection data:

js
const protData = {
    "com.apple.fps": {
        "serverURL": "https://fairplay-license.example.com/license",
        "serverCertificate": "<base64 encoded certificate>"
    }
};
player.setProtectionData(protData);

In contrast to Widevine and PlayReady, FairPlay usually requires a server certificate before a license request can be made. If it is not provided via the API, dash.js attempts to download it from the certificate URLs signaled in the MPD, see Server certificates.

An example is available in the FairPlay sample.

Server certificates

Some DRM systems require or recommend a server certificate before license requests can be made — mandatory for FairPlay, optional for Widevine (service certificate) and PlayReady. dash.js supports providing the certificate for any key system in three ways:

  1. Directly via the API using the serverCertificate attribute as a Base64 encoded string. The certificate is applied immediately and no certificate request is performed:
js
const protData = {
    "com.widevine.alpha": {
        "serverURL": "https://license.example.com/AcquireLicense",
        "serverCertificate": "<base64 encoded certificate>"
    }
};
player.setProtectionData(protData);
  1. Via certificate server URLs in the API using the certUrls attribute. dash.js downloads the certificate from the provided URLs:
js
const protData = {
    "com.widevine.alpha": {
        "serverURL": "https://license.example.com/AcquireLicense",
        "certUrls": [
            { "url": "https://certificates.example.com/widevine.der", "certType": "widevine" }
        ]
    }
};
player.setProtectionData(protData);
  1. Via certificate server URLs in the MPD signaled in a Certurl element (for instance dashif:Certurl) under the ContentProtection descriptor:
xml
<ContentProtection schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed" value="Widevine">
    <dashif:Certurl>https://certificates.example.com/widevine.der</dashif:Certurl>
</ContentProtection>

URLs provided via the API take priority over URLs signaled in the MPD. The deduplicated candidates are requested sequentially, the first successfully downloaded certificate is applied and cached for the key system. The number of retries per URL can be configured via streaming.retryAttempts. Certificate requests and responses can be modified via filters, see the certificate wrapping sample and the external certificate URL sample.

License server settings

In order to specify the license server for a DRM system use the serverURL attribute:

js
const protData = {
    "com.widevine.alpha": {
        "serverURL": "https://drm-widevine-licensing.axtest.net/AcquireLicense"
    },
    "com.microsoft.playready": {
        "serverURL": "https://drm-playready-licensing.axtest.net/AcquireLicense"
    }
};
player.setProtectionData(protData);

Key system priority

In some cases the underlying platform supports multiple DRM systems, for instance Widevine and Playready. To prioritize a specific system in the player's selection process use the priority attribute. A lower value means a higher priority. In the example below, dash.js checks for the support of com.widevine.alpha prior to com.microsoft.playready.

js
const protData = {
    "com.widevine.alpha": {
        "serverURL": "someurl",
        "priority": 1
    },
    "com.microsoft.playready": {
        "serverURL": "someurl",
        "priority": 2
    }
}
player.setProtectionData(protData)

Key System String - Priority

In some cases, multiple key system strings map to the same uuid/schemeIdUri of a DRM system. As an example, multiple platforms support the call to requestMediaKeySystemAccess for the Playready DRM system using the system strings com.microsoft.playready and com.microsoft.playready.recommendation. A detailed explanation is given here and here.

dash.js allows the application to define a system string priority for each key system as part of the protection data:

js
var protData = {
    'com.widevine.alpha': {
        'serverURL': 'https://drm-widevine-licensing.axtest.net/AcquireLicense',
        'systemStringPriority': [
            'com.widevine.something',
            'com.widevine.alpha'
        ]
    },
    'com.microsoft.playready': {
        'serverURL': 'https://drm-playready-licensing.axtest.net/AcquireLicense',
        'systemStringPriority': [
            'com.microsoft.playready.something',
            'com.microsoft.playready.recommendation',
            'com.microsoft.playready.hardware',
            'com.microsoft.playready'
        ]
    }
};

DRM specific headers

License servers might require custom headers in order to provide a valid license. dash.js allows the addition of custom headers using the httpRequestHeaders attribute:

js
const protData = {
    "com.microsoft.playready": {
        "serverURL": "https://drm-playready-licensing.axtest.net/AcquireLicense",
        "httpRequestHeaders": {
            "custom-header": "data"
        }
    }
};
player.setProtectionData(protData)

Robustness levels (Hard- & Software DRM)

Some DRM systems like Widevine require specific robustness levels to enable L1-L3 DRM playback. The robustness level can be set as part of the protection data in the following way:

js
const protData = {
    "com.widevine.alpha": {
        "serverURL": "https://drm-widevine-licensing.axtest.net/AcquireLicense",
        "audioRobustness": "SW_SECURE_CRYPTO",
        "videoRobustness": "HW_SECURE_ALL"
    }
}

License server url via MPD

DRM systems generally use the concept of license requests as the mechanism for obtaining content keys and associated usage constraints. For DRM systems that use this concept, one or more dashif:Laurl elements may be present under the ContentProtection descriptor, with the value of the element being the URL to send license requests to. An example looks the following:

xml

<ContentProtection
        schemeIdUri="urn:uuid:d0ee2730-09b5-459f-8452-200e52b37567"
        value="FirstDRM 2.0">
    <cenc:pssh>
        YmFzZTY0IGVuY29kZWQgY29udGVudHMgb2YgkXBzc2iSIGJveCB3aXRoIHRoaXMgU3lzdGVtSUQ=
    </cenc:pssh>
    <dashif:Authzurl>https://example.com/tenants/5341/authorize</dashif:Authzurl>
    <dashif:Laurl>https://example.com/AcquireLicense</dashif:Laurl>
</ContentProtection>

Note: dash.js prioritizes the license server urls in the following order:

  1. URL provided via the the API
  2. URL provided via the MPD
  3. URL provided via pssh

Ignoring init data from the PSSH

By default, dash.js listens to needkey and encrypted events thrown by the EME. In case the init data has changed a new key session is created and a license request is triggered. In order to ignore DRM init data coming from initialization and media segments the settings object needs to be adjusted:

js
player.updateSettings({
    streaming: {
        protection: {
            ignoreEmeEncryptedEvent: true
        }
    }
})

Modifying the license payload

dash.js allows the modification of the license request payload and the license response body.

License request modification

In order to modify the license request, filter functions can be added and removed dynamically.

Note: The filter functions are reset when calling player.destroy().

js
const player = dashjs.MediaPlayer().create();
const callback = (payload) => {
    return new Promise((resolve, reject) => {
        resolve(payload)
    })
}
player.initialize(video, url, false);
player.registerLicenseRequestFilter(callback)
js
player.unregisterLicenseRequestFilter(callback)

The registered functions are called within the ProtectionController class before the license request is send to the license server

js
let licenseRequest = new LicenseRequest(url, reqMethod, responseType, reqHeaders, withCredentials, messageType, sessionId, reqPayload);
applyFilters(licenseRequestFilters, licenseRequest).then(() => {
    doLicenseRequest(licenseRequest, LICENSE_SERVER_REQUEST_RETRIES, timeout, onLoad, onAbort, onError);
});

License response modification

In order to modify the license response, filter functions can be added and removed dynamically:

js
const player = dashjs.MediaPlayer().create();
const callback = (payload) => {
    return new Promise((resolve, reject) => {
        resolve(payload)
    })
}
player.initialize(video, url, false);
player.registerLicenseResponseFilter(callback)
js
player.unregisterLicenseResponseFilter(callback)

Keeping the MediaKeySession

The ProtectionController and the created MediaKeys and MediaKeySessions can be preserved during the MediaPlayer lifetime. As a consequence, only the first playback attempt for a DRM protected stream will result in a license request. For any subsequent playback attempt of the same content the existing MediaKeySession is reused and no additional license requests are performed.

To enable MediaKeySession reusage keepProtectionMediaKeys needs to be enabled.

js
player.updateSettings({
    streaming: {
        protection: {
            keepProtectionMediaKeys: true
        }
    }
})

Key status changes

After a successful license request or even during playback, the key status of a MediaKeySession can change. With version 5 dash.js handles such key status changes and switches to a different track or a different Representation if required.

If the application needs to know about such key status updates it can register for the KEY_STATUSES_MAP_UPDATED event. This event is triggered once the internal key status map of dash.js was updated.

js
player.on(dashjs.protectionEvents.KEY_STATUSES_MAP_UPDATED, eventHandler, null);

Different versions of the EME

The EME is the API that enables playback of protected content in the browser. It provides the necessary function calls to discover and interact with the underlying DRM system. Like any other API, EME changed over time and the current version is a lot different compared to the one in 2013. While desktop and mobile browsers are frequently updated, some embedded devices and set-top boxes are still running on outdated or even customized versions of the EME. For that reason dash.js detects the EME version on the client and triggers the right API functions [1].

By default, dash.js ships with support for three different versions of EME:

  • ProtectionModel_01b.js: initial implementation of the EME, implemented by Google Chrome prior to version 36. This EME version is not-promised based and uses outdated or prefixed events like “needkey” or “webkitneedkey”.
  • ProtectionModel_3Feb2014.js: implementation of EME APIs as of the 3 Feb 2014 state of the specification. Implemented by Internet Explorer 11 (Windows 8.1).
  • ProtectionModel_21Jan2015.js: most recent EME implementation. Latest changes in the EME specification are added to this model and It supports the promised-based EME function calls.

The detection of the appropriate EME version is done automatically in Protection.js:

js
if ((!videoElement || videoElement.onencrypted !== undefined) &&
    (!videoElement || videoElement.mediaKeys !== undefined)) {
    logger.info('EME detected on this user agent! (ProtectionModel_21Jan2015)');
    return ProtectionModel_21Jan2015(context).create();
} else if (getAPI(videoElement, APIS_ProtectionModel_3Feb2014)) {
    logger.info('EME detected on this user agent! (ProtectionModel_3Feb2014)');
    return ProtectionModel_3Feb2014(context).create();
} else if (getAPI(videoElement, APIS_ProtectionModel_01b)) {
    logger.info('EME detected on this user agent! (ProtectionModel_01b)');
    return ProtectionModel_01b(context).create();
} else {
    logger.warn('No supported version of EME detected on this user agent! - Attempts to play encrypted content will fail!');
    return null;
}

References

[1] dash.js: License acquisition for multiple EME versions