Integration DRM - xbmc/inputstream.adaptive GitHub Wiki

To play DRM-protected content is required, in addition to the standard properties described on Integration page, configure the DRM(s) to be used.

Let's start by saying that a manifest (e.g. DASH, HLS, ...) can support one or more DRM's, but a device (operative system) may support only some DRM's, moreover this add-on has some limitations based on the operative system used, see also matrix scheme of supported DRM's on the wiki main page.

Until Kodi v21 the choice of the DRM will have to be handled "manually" by the add-on or playlist that starts the playback, by taking into account the variables mentioned earlier (matrix scheme, etc), and so by configuring it by using "old properties" method, or the inputstream.adaptive.drm_legacy.

Starting with Kodi v22 the choice of DRM will be handled automatically by ISA thanks also to the new inputstream.adaptive.drm property, and then you will have to provide DRM configurations only if requested.

Note

The Widevine CDM library for operating systems other than Android must be installed separately because it is not included in this add-on. To handle the library installation automatically we suggest to use InputStream Helper add-on.

How to configure DRM's

The way in which configuration is required has changed slightly from older versions of Kodi, as follows:

  • Until to Kodi v21: it is always mandatory to use one of the following methods to set which DRM to use.
  • From Kodi v22 and above: Only if the DRM have mandatory requirements, you need to set one of the following methods to configure the DRM, otherwise, you do not need to configure the DRM because the video will still play.

Mandatory DRM requirements

The following DRMs require some mandatory configurations:

  • Widevine / Wiseplay DRM: its mandatory set the license server url.
  • PlayReady DRM: if the manifest does not embed the license server url, then its mandatory set the license server url.
  • ClearKey DRM: if the manifest does not embed the license server url, then its mandatory set the license server url or the keyids.

Methods to configure the DRM

🟠old properties - Deprecated advanced method [from Kodi v18 to v22] [click to expand]

Available from Kodi 18 to Kodi 22

This is the old advanced method that makes use of multiple properties. Has been deprecated on Kodi v22.

Recommendations: We suggest to use this old method with older versions of Kodi until to v21.
Starting with Kodi v22 we suggest the use of the new "drm" property interface. If instead you don't need of an advanced method, you can use directly the "drm_legacy" property starting from Kodi v21.

Open "Integration DRM (old)" page to learn how to use the old properties.

The support of this old method will be removed in future versions of Kodi.


🟠inputstream.adaptive.drm_legacy - Simple method to configure a single DRM [Kodi v21+] [click to expand]

Available from v.21.5.0 / Kodi 21

The simple way to configure a DRM can be used for cases where the provider does not require advanced configurations such as wrappers, certificate, etc... it can configure a single DRM, that will have the higher priority (over others DRM's, if any on the manifest).

DRM sessions, Kodi v22 breaking-change: unlike the old properties and drm_legacy on Kodi v21 that forced the DRM with a single session, drm_legacy on Kodi v22 will no longer force a single DRM session, this is to prevent old standing playback problems. If you want force again the single session you have to make use of inputstream.adaptive.drm property where force_single_session parameter allow you to change this behavior.

The expected value is a string, which can be extended with 2 more optional pipes | as required:
[DRM KeySystem] | [License server URL or KeyId's] | [License server headers]

Template fields:

  • [DRM Key System]
    The Key System of the DRM to configure, the supported Key System strings are shown in the table above.

  • [License server URL or KeyId's]
    Supported string values:

    • URL: it can also override the one embedded in the manifest, if any
    • KID/KeyId's pairs: Only ClearKey DRM, with following format: kid1:key1,kid2:key2,... where the values are in HEX format.
    • URI "data" scheme: Only ClearKey DRM, example data:application/json;base64,CK_LICENSE_AS_BASE64
  • [License server headers]
    Allow you to add custom HTTP headers for the license HTTP request. With some providers it may be necessary to avoid a server rejection of the HTTP request.
    We suggest to URL encode the headers values. The headers string must follow the scheme: param1=value1&param2=value2

Example for Widevine:

# Flat string example
listitem.setProperty('inputstream.adaptive.drm_legacy', 'com.widevine.alpha|https://license.server.com/licenserequest|User-Agent=Mozilla%2F5.0+%28Windows+NT+10.0%3B+Win64%3B+x64%29+AppleWebKit%2F537.36+%28KHTML%2C+like+Gecko%29+Chrome%2F106.0.0.0+Safari%2F537.36')

# Constructed string example (recommended)
license_headers = {
    'Content-Type': 'application/octet-stream',
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/109.0',
}
from urllib.parse import urlencode
drm_config = {  # Keeping items in order
    'DRM KeySystem': 'com.widevine.alpha'
    'License server url': 'https://license.server.com/licenserequest',
    'License headers': urlencode(license_headers)
}
listitem.setProperty('inputstream.adaptive.drm_legacy', '|'.join(drm_config.values()))

Example for PlayReady:

listitem.setProperty('inputstream.adaptive.drm_legacy', 'com.microsoft.playready|https://www.licenseserver.com/AcquireLicense/')

Example for ClearKey:

# KeyId's provided by manifest:
listitem.setProperty('inputstream.adaptive.drm_legacy', 'org.w3.clearkey')
# KeyId's provided by property:
listitem.setProperty('inputstream.adaptive.drm_legacy', 'org.w3.clearkey|000102030405060708090a0b0c0d0e0f:00112233445566778899aabbccddeeff')
# License URL provided by property: (supported from v21.5.3)
listitem.setProperty('inputstream.adaptive.drm_legacy', 'org.w3.clearkey|https://www.licenseserver.com/AcquireLicense/')
# License URI "data" scheme provided by property: (supported from v22.2.0)
listitem.setProperty('inputstream.adaptive.drm_legacy', 'org.w3.clearkey|data:application/json;base64,CK_LICENSE_AS_BASE64')


🟠inputstream.adaptive.drm - Advanced method to configure one or more DRM's [Kodi v22+] [click to expand]

Available from v.22.1.5 / Kodi 22

The advanced method allows you to configure one or multiple DRM's at same time with more complex parameters. Setting multiple DRMs makes sense when a manifest incorporates support for multiple DRMs that will allow ISA to select the most appropriate DRM based for example on the operating system in use and other parameters. In case you want to alter the automatic DRM selection, you can make use of the β€œpriority” parameter.

The expected value is a json converted as string. The base scheme is structured by a dictionary, where the key is a Key System string, and the value is a dictionary with most of the time optionals parameters, as in the following example:

# Brief example for python, full examples on relative chapter
drm_configs = {
    # This is Widevine DRM Key System
    "com.widevine.alpha": {
        "license": {
            "server_url": "https://theserverurl.com"
        }
    }
}
list_item.setProperty('inputstream.adaptive.drm', json.dumps(drm_configs))

You can specify a DRM Key System string (example "com.widevine.alpha", see the complete KS list at the end of the page), or "none" value. The "none" value is reserved for special cases such as HLS encryption (e.g. AES-128) which can be used in order to configure these specific use cases (see chapter examples).

DRM sessions note: unlike the old properties that forced the DRM with a single session, drm property will not force a single DRM session by default, this is to prevent old standing playback problems. If you want change this behavior you can make use of the force_single_session parameter.

Configuration parameters

To configure a DRM you can use following parameters, each parameter is optional unless otherwise specified, or required by your service provider.

πŸ”΅ priority - The DRM priority over others DRM's


Allows this DRM to be given a higher priority than the others (if multiple). This can be useful when a manifest support more DRM's and the operating system in use supports all of them. So for example if a specific DRM provides higher resolutions, you can prioritize that DRM over the others.

The value can start from 1 to "n" where lower number have the higher priority. Invalid values are: 0 or a same value used on multiple DRM's.

DATA TYPE: int
EXAMPLE: "priority": 1

πŸ”΅ license - To configure the license request


The configuration for the license request is strictly dependent on the streaming services requirements so we cannot provide guidelines for every use case, so we just provide generalities.

DATA TYPE: dict

The license dictionary support these parameters:

🟒 server_certificate - To set a license server certificate


Specifies a server certificate to be used to encrypt messages to the license server. Must be encoded as Base64.

DATA TYPE: str
EXAMPLE: "server_certificate": "base64 data"

🟒 server_url - To set a license server URL


Specifies the license server URL where make the license HTTP request.

Supported string values:

  • URL: it can also override the one embedded in the manifest, if any
  • URI "data" scheme: Only ClearKey DRM, example data:application/json;base64,CK_LICENSE_AS_BASE64

For Widevine DRM: In the URL is possible optionally inject the DRM challenge (key request) by using following placeholders:

  • {CHA-B64U} To inject to the URL the DRM Challenge as base64, URL encoded
  • {CHA-MD5} To inject to the URL the DRM Challenge hashed as MD5

DATA TYPE: str
EXAMPLE: "server_url": "http://the-server-url.com/challenge/{CHA-B64U}"

🟒 use_http_get_request - Force HTTP GET for the license request


Widevine, Playready, Wiseplay only.

By enabling this parameter will force an HTTP GET request to request the license (by default the request is done with an HTTP POST request).

DATA TYPE: bool
EXAMPLE: "use_http_get_request": True

🟒 req_headers - To set the HTTP headers for the license request


Allows to add the HTTP headers to the license HTTP request, values of headers must be URL encoded (see also chapter "examples").

DATA TYPE: str
EXAMPLE: "req_headers": "param1=valueUrlEncoded1&param2=valueUrlEncoded2"

🟒 req_params - To set parameters to the license URL


Allows to append to the license URL, some URL parameters.

DATA TYPE: str
EXAMPLE: "req_params": "/one/two/three-path"

🟒 req_data - To customize the data for the license request


Allows to provide custom data for the license request. The data must be base 64 encoded.

It is usually used when a license requet/response makes use of wrappers, so the data structure is customized with other types of formats by the service provider, such as JSON, BASE64, etc... (see wrapper/unwrapper parameters).

You can construct the format of the required data structure as a string and use the following placeholders to inject specific data into the constructed string:

  • {CHA-RAW} Challenge (key request) data as is, raw bytes
  • {CHA-B64} Challenge (key request) data base64 encoded
  • {CHA-B64U} Challenge (key request) data base64 and URL encoded
  • {CHA-DEC} Challenge (key request) data decimal converted (each char converted as integer concatenated by comma)
  • {SID-RAW} Session ID as is, raw text
  • {SID-B64} Session ID base64 encoded
  • {SID-B64U} Session ID base64 and URL encoded
  • {KID-UUID} KeyID as UUID format
  • {KID-HEX} KeyID as HEX format
  • {PSSH-B64} Initialization PSSH data base64 encoded
  • {PSSH-B64U} Initialization PSSH data base64 and URL encoded

DATA TYPE: str
EXAMPLE: Example of provider customized license request data as JSON with injected challenge data and session ID

lic_req_data = json.dumps({"movie_id": "123456", "challenge_base64": "{CHA-B64}", "SID": "{SID-B64}"}).encode("utf-8")

drm_configs = {
    "...": {
        "license": {
            "req_data": base64.b64encode(lic_req_data).decode("utf-8"),
            ...
        }
    }
}
🟒 wrapper / unwrapper / unwrapper_params - Widevine only. To wrap/unwrap the license data


Widevine only.

Some services use custom data for license server request/response, where the data are wrapped in other types of formats, such as Base64, JSON, XML, etc... but Widevine by default works only with raw binary data, so its needed to prepare the license data wrapped in a suitable way accepted by the server to make the license request, and after extract the raw data from the wrapped data of the server license response.

There are two ways to manage a custom license:

  1. Use built-in wrappers/unwrappers
  2. Via proxy server. An add-on must implement a proxy server to translate the license request response (as explained on How-to-provide-custom-manifest-and-license)

The built-in method is the simplest, but may be not suitable if the custom license is too much complex.

wrapper parameter flags:

  • base64 To encode as base 64
  • urlenc To encode as URL
  • none To keep data as it is raw (explicit way, as if the parameter was not set)

Multiple flags are accepted and must be splitted by , char, the sequence order declares the way in which to wrap the data.

unwrapper parameter flags:

  • auto Try auto-detect the wrapping types (single-use only)
  • base64 To decode as base 64
  • json To parse JSON data, required unwrapper_params
  • xml To parse XML data, required unwrapper_params
  • none To keep data as it is raw (explicit way, as if the parameter was not set)

Multiple flags are accepted and must be splitted by , char, the sequence order declares the way in which to unwrap the data.

unwrapper_params parameters:

  • "path_data": Can be used to specify an absolute JSON or XML path to get the license data.
  • "path_data_traverse": bool, If True the search with path_data will be performed by traversing all the dict's to find a specified key instead of an absolute path.
  • "path_hdcp_res": Can be used to specify the absolute JSON path to get the HDCP resolution limit. Read HDCP resolution limit page for details.
  • "path_hdcp_res_traverse": bool, If True the search with path_hdcp_res will be performed by traversing all the dict's to find a specified key instead of an absolute path.
  • "path_hdcp_ver": Can be used to specify the absolute JSON path to get the HDCP resolution limit. Read HDCP resolution limit page for details.
  • "path_hdcp_ver_traverse": bool, If True the search with path_hdcp_ver will be performed by traversing all the dict's to find a specified key instead of an absolute path.

We cannot provide examples for every use case, here we provide an example that can also be adapted for other use cases as they are similar.

Example to configure built-in wrappers/unwrappers for JSON/BASE64 wrapped license:

  1. The service require that the license request is encoded as BASE64, the encoded data must be in JSON format. The Json is a dictionary challenge_base64 that contains the challenge (widevine key request) raw data encoded as base 64.
  2. The license server give a response BASE64 encoded, where contains a JSON with nested dictionaries. The first dictionary is licenseresponse that contains a nested data dictionary, having the license raw data encoded as base 64.
lic_req_data = json.dumps({"challenge_base64": "{CHA-B64}"}).encode("utf-8") # Prepared Json string, {CHA-B64} used to jniect the challenge as base64

drm_configs = {
    "com.widevine.alpha": {
        "license": {
            "server_url": "https://theserverurl.com",
            "req_data": base64.b64encode(lic_req_data).decode("utf-8"), # Encoded as base64, because its a requirement of the "req_data" parameter.
            "wrapper": "base64", # Encode "req_data" data as base 64, before make the request
            "unwrapper": "base64,json,base64", # From the response -> (1) decode as base 64 -> (2) parse JSON path -> (3) decode base 64 the value
            "unwrapper_params": {"path_data": "licenseresponse/data"}
        }
    }
}

# Expected license response data that will be unwrapped by the configuration:
response_data = "eyJsaWNlbnNlcmVzcG9uc2UiOiB7ImRhdGEiOiAiZEdocGN5QnBjeUJ5WVhjZ1pHRjBZUT09In19Cg=="

To check if you are sending/receive the data correctly, you can enable the ISA expert setting to save the license data and so inspect the saved files. In short, the ".request" file should reflect the format required by your service provider, while ".response" file should show that it contains only the license data (raw) bytes.

🟒 keyids - ClearKey DRM only. Map of KID/Key pairs


Allows to specify a map of unencrypted KID/Key pairs to decrypt stream CENC content, can be useful for diagnosing problems and testing integrations.

Both KID/Key values must be in hex format.

DATA TYPE: dict
EXAMPLE: "keyids": { "KID_1": "KEY_1", "KID_2": "KEY_2" }


πŸ”΅ init_data - To set custom initialization data


Allow to provide a initialization data (PSSH box) to initialize the CDM sessions, in case the manifest provides one, this will replace it.

Accepted values are: a standard PSSH box or else a Widevine PSSH, encoded as base64.

For the Widevine PSSH case, two placeholders can be optionally used to inject data in to the custom data:

  • {KID} to inject the KID as bytes
  • {UUID} to inject the KID as UUID string

The placeholders if used must be encoded as base64 together with the raw data, and not separately. Technically speaking the Widevine PSSH data will be added to content_id field of WidevinePsshData structure. The WidevinePsshData generated will be enclosed into a standard PSSH box.

DATA TYPE: str
EXAMPLE: "init_data": "base64 data"

πŸ”΅ pre_init_data - Widevine only, for custom licensed manifests


Widevine DRM only.

NOTE: To use this feature, its mandatory set to Widevine DRM configuration priority parameter to 1.

Pre-initialize the DRM is required for services that make use of licensed manifests (custom manifests). Compared to the standard manifests (e.g. DASH) this encloses also the license data. To request these custom manifests usually you need to provide the DRM session id and the challenge (widevine key request). To obtain these two data, this parameter allows you to pre-initialize the DRM by opening a DRM session. To open a DRM session you need to provide a PSSH and KID, both values must be base 64 encoded and splitted by a pipe char |.

After that, you need to implement a proxy in your add-on to intercept the ISAdaptive HTTP manifest/license requests, as in the example How to provide custom manifest and license, will allow your addon to manage and convert the manifest/license data. When you will get the HTTP manifest response, you will also need to transfer the license data into the ISA license HTTP request.

With the HTTP manifest request, two custom HTTP headers will be provided:

  • challengeB64 Provide the challenge (key request) encoded as base 64, and URL encoded.
  • sessionId Provide the DRM session id.

WARNING: Do not enable pre_init_data parameter without a proxy server in your add-on, otherwise the license data will not be managed.
WARNING: This feature is currently intended for non-Android systems. Can works also on Android system, but to keep in mind that it is not possible to maintain the same DRM session, this mismatch will result in the license data may not working.

DATA TYPE: str
EXAMPLE: "pre_init_data": "PSSH encoded base 64|KID encoded base 64"

πŸ”΅ persistent_storage - Enables the CDM persistent state


Set to `True` to enable the CDM persistent state. This allow to store locally the session data or other type of state. To be enabled only if the streaming service requires it.

DATA TYPE: bool
EXAMPLE: "persistent_storage": True

πŸ”΅ secure_decoder - To enable/disable secure decoder


Set to `True` or `False` to "force" enable or disable the secure decoder, every time this is set will override the user settings set on the ISA add-on settings window.

NOTE: If you dont want override the ISA add-on user settings, remember to remove secure_decoder parameter and so add it only when needed.

Disable the secure decoder could be helpful for some android devices where playback may result in a black screen, usually due to faulty Widevine L1 certifications.

DATA TYPE: bool
EXAMPLE: "secure_decoder": True

πŸ”΅ force_single_session - To enforce the usage of a single DRM session


Depending on the DRM-backend capabilities it may be needed to execute one or more requests towards the backend in order to get all the keys needed for the playback, this is the case of multi-key playback. One of the multi-key setup is with audio and video tracks being encrypted with different keys. In this case when the player dont know what KIDs are supported by the license it creates two DRM sessions by default, one for audio and one for video. To disable this behaviour (when possible) you can enforce the usage of a single DRM session if DRM-backend is certain to return all the keys in one request. Disabling it can save time by avoiding multiple license requests, but it may lead to playback problems such as corrupted/pixellated videos.

DATA TYPE: bool
EXAMPLE: "force_single_session": True

πŸ”΅ optional_key_req_params - To set specific initialization CDM parameters


This parameter is closely related to the type of CDM used, allows you to configure CDM initialization parameters usually used for the key request.

PlayReady DRM: custom_data Allow to set data to the PRCustomData CDM parameter to make the key request.

DATA TYPE: dict
EXAMPLE: "optional_key_req_params": {"custom_data": "the data"}



🟠inputstream.adaptive.drm - Examples [click to expand]

DASH + WIDEVINE: Play encrypted video stream from add-on

listitem = xbmcgui.ListItem(path='https://www.videoservice.com/manifest.mpd', offscreen=True)

# These two lines are needed to prevent the HTTP HEAD request from Kodi core, used to determine the mimetype
listitem.setMimeType('application/dash+xml')
listitem.setContentLookup(False)

listitem.setProperty('inputstream', 'inputstream.adaptive')

license_headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/109.0',
    'Content-Type': 'application/octet-stream'
}

# Example configuration with drm_legacy property
listitem.setProperty('inputstream.adaptive.drm_legacy', 'com.widevine.alpha|https://www.licenseserver.com/acquirelicense|' + urlencode(license_headers))

# Example configuration with drm property
drm_cfg = {"com.widevine.alpha":
    {"license":
        {"server_url": "https://www.licenseserver.com/acquirelicense",
         "req_headers": urlencode(license_headers)}
    }
}
listitem.setProperty('inputstream.adaptive.drm', json.dumps(drm_cfg))

xbmcplugin.setResolvedUrl(pluginhandle, True, listitem=listitem)

DASH + WIDEVINE: Play encrypted video stream from playlist file STRM / M3U8

#KODIPROP:inputstream=inputstream.adaptive
# Example configuration with drm_legacy property
#KODIPROP:inputstream.adaptive.drm_legacy=com.widevine.alpha|https://www.licenseserver.com/acquirelicense|User-Agent=Mozilla%2F5.0+%28Windows+NT+10.0%3B+Win64%3B+x64%29+AppleWebKit%2F537.36+%28KHTML%2C+like+Gecko%29+Chrome%2F106.0.0.0+Safari%2F537.36
# Example configuration with drm property
#KODIPROP:inputstream.adaptive.drm={"com.widevine.alpha":{"license": {"server_url": "https://www.licenseserver.com/acquirelicense","req_headers": "User-Agent=Mozilla%2F5.0+%28Windows+NT+10.0%3B+Win64%3B+x64%29+AppleWebKit%2F537.36+%28KHTML%2C+like+Gecko%29+Chrome%2F106.0.0.0+Safari%2F537.36"}}}
#KODIPROP:mimetype=application/dash+xml
https://www.videoservice.com/manifest.mpd

SMOOTHSTREAMING + PLAYREADY: Play encrypted video stream from add-on

listitem = xbmcgui.ListItem(path='https://www.videoservice.com/tearsofsteel_4k.ism/manifest', offscreen=True)

# These two lines are needed to prevent the HTTP HEAD request from Kodi core, used to determine the mimetype
listitem.setMimeType('application/vnd.ms-sstr+xml')
listitem.setContentLookup(False)

listitem.setProperty('inputstream', 'inputstream.adaptive')
# Example configuration with drm_legacy property
listitem.setProperty('inputstream.adaptive.drm_legacy', 'com.microsoft.playready')
# Example configuration with drm property
listitem.setProperty('inputstream.adaptive.drm', '{"com.microsoft.playready": {}}')

xbmcplugin.setResolvedUrl(pluginhandle, True, listitem=listitem)

HLS + AES-128: Play encrypted video stream from add-on

listitem = xbmcgui.ListItem(path='https://www.videoservice.com/master_manifest.m3u8', offscreen=True)

# These two lines are needed to prevent the HTTP HEAD request from Kodi core, used to determine the mimetype
listitem.setMimeType('application/vnd.apple.mpegurl')
listitem.setContentLookup(False)

listitem.setProperty('inputstream', 'inputstream.adaptive')
# Usually there is no need to set inputstream.adaptive.drm or inputstream.adaptive.drm_legacy
# but, if you need to add custom headers or parameters to the HLS HTTP key request's
# you can use the following configuration (the Key System is, none):
encrypt_cfg = {"none":{"license": {"req_headers": "User-Agent=Mozilla%2F5.0+%28Windows+NT+10.0%3B+Win64%3B+x64%29+AppleWebKit%2F537.36+%28KHTML%2C+like+Gecko%29+Chrome%2F106.0.0.0+Safari%2F537.36", "req_params": "/example/param"}}}
listitem.setProperty('inputstream.adaptive.drm', json.dumps(encrypt_cfg))

xbmcplugin.setResolvedUrl(pluginhandle, True, listitem=listitem)

DASH + CLEARKEY: Play encrypted video stream from add-on by providing clear key's or license url

listitem = xbmcgui.ListItem(path='https://www.videoservice.com/manifest.mpd', offscreen=True)

# These two lines are needed to prevent the HTTP HEAD request from Kodi core, used to determine the mimetype
listitem.setMimeType('application/dash+xml')
listitem.setContentLookup(False)

listitem.setProperty('inputstream', 'inputstream.adaptive')
# To provide clear keys, use:
drm_cfg = {"org.w3.clearkey":
    {"license":
        {"keyids": { "KID_1": "KEY_1", "KID_2": "KEY_2" }}
    }
}
# To set a license server url, use:
drm_cfg = {"org.w3.clearkey":
    {"license":
        {"server_url": "https://www.licenseserver.com/AcquireLicense/"}
    }
}
# If the manifest embed the license server url, you can leave empty:
drm_cfg = {"org.w3.clearkey": {} }

listitem.setProperty('inputstream.adaptive.drm', json.dumps(drm_cfg))

xbmcplugin.setResolvedUrl(pluginhandle, True, listitem=listitem)

DRM Key System's

List of supported DRM Key Systems, required to set a DRM configuration.

DRM Name Key System
Widevine com.widevine.alpha
PlayReady (android only) com.microsoft.playready
Wiseplay (android only) com.huawei.wiseplay
ClearKey (from v.21.5.0 / Kodi 21) org.w3.clearkey
Special case, for encryptions without DRM (Kodi 22+) none

The special case none can be used to set some parameters to other encryption cases for example HLS AES-128.

Other

🟀 License URL too long - How to solve PVR binary API bug (Only for Kodi v20-v21) [click to expand]

On all Kodi versions until v21, there is a know Kodi PVR API interface bug, that truncate ISAdaptive properties values to max 1024 chars, then if the server license URL is too long will trucate the data set on the InputStream properties (e.g. inputstream.adaptive.license_key), and the video playback will fails.

The PVR API bug has been fixed on Kodi v22.

To workaround this bug, has been added these two properties (that can be used only on Kodi 20/21) to split the URL on two parts of 1024 chars:

  • inputstream.adaptive.license_url [deprecated on Kodi 22]
  • inputstream.adaptive.license_url_append [deprecated on Kodi 22]

How to do:

  1. Leave empty the field used to set the license URL (e.g. on inputstream.adaptive.license_key the [license server URL] template field)
  2. Take care to split the license server url in two parts of 1024 chars, then use license_url to specify the first 1024 chars, and license_url_append to add the last part of remaining url chars.

NOTE: On Kodi v20, has been introduced from ISA v20.3.15

🟀 How to configure a PlayReady manifest to use Widevine [click to expand]

Some video services can use manifests (often SmoothStreaming) protected with PlayReady DRM and provide also a Widevine server license URL. Then it is possible try to force use Widevine DRM to play the contents protected with PlayReady DRM. This is useful because currently the use of PlayReady is limited on android only, and some android devices may not support PlayReady.

NOTE: Works only when the manifest provides a single DRM encryption scheme and there is set only one DRM configuration (when using the inputstream.adaptive.drm property).

To force Widevine you need just to specify "com.widevine.alpha" (instead of "com.microsoft.playready") and set the license server URL, as follow:

listitem = xbmcgui.ListItem(path='https://www.videoservice.com/tearsofsteel_4k.ism/manifest', offscreen=True)

# These two lines are needed to prevent the HTTP HEAD request from Kodi core, used to determine the mimetype
listitem.setMimeType('application/vnd.ms-sstr+xml')
listitem.setContentLookup(False)

listitem.setProperty('inputstream', 'inputstream.adaptive')
listitem.setProperty('inputstream.adaptive.manifest_type', 'ism') # Deprecated on Kodi 21, removed on Kodi 22
if Kodi < v21:
  listitem.setProperty('inputstream.adaptive.license_type', 'com.widevine.alpha')
  listitem.setProperty('inputstream.adaptive.license_key', 'https://widevinelicenseserverurl')
if Kodi >= v21:
  listitem.setProperty('inputstream.adaptive.drm_legacy', 'com.widevine.alpha|https://widevinelicenseserverurl')

xbmcplugin.setResolvedUrl(pluginhandle, True, listitem=listitem)

If the video service require custom data to the Widevine PSSH:

  • On Kodi <= v21: Set your custom data by using inputstream.adaptive.license_data property.
  • On Kodi >= v22: Set your custom data by using init_data parameter from inputstream.adaptive.drm property.
# Example for Kodi >= v22
drm_config = {
  "com.widevine.alpha": {
      "license": {
          "server_url": "https://theserverurl.com"
      },
      "init_data": base64.b64encode(b'CUSTOMDATA').decode('utf-8')
  }
}
listitem.setProperty('inputstream.adaptive.drm', json.dumps(drm_config))
xbmcplugin.setResolvedUrl(pluginhandle, True, listitem=listitem)

Read properties/parameters guide for more details.

🟀 ClearKey DRM remarks and limitations [click to expand]

  • This DRM was implemented later time in Kodi v21 and can be configured from Kodi v21 by using the inputstream.adaptive.drm_legacy property, and from Kodi v22 also by using the inputstream.adaptive.drm property. This choice was made because the deprecated old DRM properties will be removed in near future. So we want to encourage the use of the new methods to limit future changes needed.
  • It can be used to test streams protected with other types of DRM's, by providing the appropriate kid / key's pairs.
  • CBCS encryption is supported from Kodi v22.
  • The format for the license request and response meets the W3C standard https://www.w3.org/TR/encrypted-media/#clear-key-request-format
  • Streams using DRM Key Rotation feature cant be supported.
  • Compatibility with Microsoft SmoothStreaming manifest (to override DRM) has been implemented from Kodi v22.
⚠️ **GitHub.com Fallback** ⚠️