Skip to main content

Concurrent Stream Limiting (CSL)

Overview​

Concurrent stream limiting (CSL) allows one to control how many streams can be watched simultaneously from a specific end-user account. This can be useful to reduce account-sharing or to put in place subscription plans with different concurrent viewing limits.

The feature can be enabled and configured via the Entitlement Message.

Additions to player-side logic may be needed, depending on the base capabilities of the player and the DRM used.

warning

The Entitlement Message (EM) document is not yet updated with the CSL settings. Please refer to the examples on this page.

Concept​

CSL is based on the following aspects:

  1. Issuing short-durations licenses that need to be extended regularly. These are often called "heart-beat requests" or license renewals.
  2. The renewals provide an indication whether a particular stream on a particular client is still actively playing, making it possible to count the total number of active sessions or unique clients per a end-user account.
  3. Once the configured CSL limits are exceeded, renewals will be denied and playback will be forced to stop due to the duration of the existing licenses running out. Since the license expiry on the client is enforced by the DRM component, it is tamper-resistant.
  4. On the server side, that lack of further indicates that a playback session has been stopped and a slot will be automatically freed - avoiding the need for explicit termination signals and potential "hanging" sessions.

Setting up CSL on the server side​

Minimal configuration​

Minimally, to enable the CSL feature, you need how many concurrent playbacks are allowed, via the concurrency_limit setting. The allowed values are 1-6.

Additionally, since the CSL limits are enforced per end-user, you need to specify the ID of the user that is currently requesting a license, via the user_id settings. Any non-empy and non-whitespace ID is allowed.

{
"license_server": {
"access_control": {
"concurrency_limit": 2
}
},
"session": {
"user_id": "user_account_01000"
}
}

In the above case, rest of the settings (see below) will receive their default values.

note

When CSL is enabled several other EM settings internally receive new defaults and a restricted to different values, to support the CSL workflow. Among these are:

  • "allow_persistence" is always "false"
  • "real_time_expiration" is always "true"

Choosing a Concurrency Mode​

Axinom DRM supports choosing among several behaviours that dictate how the concurrency limits are applied:

  1. BLOCK_NEW_DEVICE - In this mode, only the number of active unique clients (devices) is tracked. Once all the playback slots are full, a request from a new client will be blocked. This is the default mode.
info

In case of Desktop browsers on the same machine, different browsers (e.g. Chrome, Edge, etc), different variants of the same browser (e.g. Chrome Stable and Chrome Beta), or some different versions of the same browser (Chrome vOld and Chrome vNew) are considered different clients.

{
"license_server": {
"access_control": {
"concurrency_limit": 2
}
},
"session": {
"user_id": "user_account_01000"
}
}
  1. BLOCK_NEW_SESSION - This is similar to "BLOCK_NEW_DEVICE", except multiple playback sessions (or playbacks of distinct content, depending on how sessions are distinguished) on the exact same client will individually count towards the limit. For example, playing two movies in two browser tabs on the same browser will take up two slots, while in the "BLOCK_NEW_DEVICE" mode, one could have unlimited playback sessions per client.

    In this mode, it is also required to set the Session ID of the current request. In practice, the Session ID value may be set to the Content ID of stream being played. The Content ID should be readily available to your component that handles Entitlement Message creation.
info

DRMs generally also provide support for built-in session identifiers, which can remove the need for providing custom IDs. However, there are some limitations. For example, PlayReady does not provide such support, so using a concurrency mode other than the "BLOCK_NEW_DEVICE" requires a custom session ID. Additionally, in some situations, when playing multi-key content, Widevine players may create multiple client-side sessions per stream, causing a single stream to potentially take up multiple slots. For these reasons, it is recommended to provide a Content ID as the Session ID in the Entitlement Message as a universal solution. Contact Axinom in case of questions.

info

In case a logical Content ID is used as the Session ID, then all playbacks of the same content on the same client will count as one. This is expected to be acceptable and is considered an infrequent scenario.

{
"license_server": {
"access_control": {
"concurrency_limit": 2,
"concurrency_mode": "BLOCK_NEW_SESSION"
}
},
"session": {
"id": "content_01000",
"user_id": "user_account_01000"
}
}

Alternative: using native session IDs​

In this case the custom session ID does not need to be provided as the native session ID has precedence. Except, if you still wish to enable CSL for PlayReady in this mode - PlayReady does not support built-in native session IDs.

With PlayReady CSL disabled:​
{
"license_server": {
"access_control": {
"concurrency_limit": 2,
"concurrency_mode": "BLOCK_NEW_SESSION",
"widevine": {
"use_native_session_ids": true
},
"fairplay": {
"use_native_sessions_ids": true
},
"playready": {
"concurrency_mode:": "NONE"
}
}
}
},
"session": {
"user_id": "user_account_01000"
}
}
With PlayReady using custom session IDs:​
{
"license_server": {
"access_control": {
"concurrency_limit": 2,
"concurrency_mode": "BLOCK_NEW_SESSION",
"widevine": {
"use_native_session_ids": true
},
"fairplay": {
"use_native_sessions_ids": true
}
}
}
},
"session": {
"id": "content_01000",
"user_id": "user_account_01000"
}
}
  1. BLOCK_OLDEST_SESSION - This is similar to "BLOCK_NEW_SESSION", except all new playbacks are allowed and instead the oldest playbacks are blocked from renewing their licenses, once the amount of concurrent playbacks exceeds the concurrency limit. In this mode simultaneous playbacks may temporarily exceed the limit. This scenario benefits new viewers who can always start watching a stream, at the cost of previous viewers being interrupted.
    \
{
"license_server": {
"access_control": {
"concurrency_limit": 2,
"concurrency_mode": "BLOCK_OLDEST_SESSION"
}
},
"session": {
"id": "content_01000",
"user_id": "user_account_01000"
}
}

Choosing renewal interval and grace period​

Finally, it is possible to configure the interval at which the concurrency checks (license renewals) take place, and set a grace period to allow playback in the period where a renewal is triggered until a succesful response is received.

The below settings apply to all DRMs for which CSL is enabled.

{
"license": {
"concurrency_interval": 300,
"concurrency_grace_period": 60
}
}
  • Concurrency interval - specifies the interval, in seconds, at which license renewals are triggered. Valid range of values is: 60 - 600. Default is 300.

    The first renewal takes place this amount of time after the start of the playback. Subsequent renewals are counted from the time of last successful renewal (response for previous renewal was received).

    Setting a shorter time reduces the time it takes to react to changes, but increases the amount of license requests (potentially subject for billing). For example, if the interval is long, it will take a bit longer time for a slot to be freed once end-user stops a playback; or, in the "block_oldest_session" scenario, an old session can continue playback for longer before a check is made to detect that the limits are exceeded.

  • Concurrency grace period - specifies the time, in seconds, for how long the playback can still proceed after triggering a license renewal and before receiving a successful license extension. A valid range of values is: 1 - 120. Default is 60.

    This period should cover any network latency and possible renewal retry attempts in case of network or service failures.

Setting up CSL on the client side​

The steps needed to support CSL on the playback application side vary by the DRM. Widevine DRM notably provides the most built-in client side support. FairPlay and PlayReady clients need extra development, depending on the existing feature set of your player.

General​

For good end-user experience, it is recommended for the client to actively handle "CSL limits exceeded" from the license service, instead of letting the license run out, which may produce more obscure errors. For this:

  1. Add a response handler to your application that listens for non-200 OK responses from the license service.
  2. If you receive such a response, check the error code. The error code is the value of the "X-AxDrm-ErrorMessage" header. Note: not all errors produce a code.
  3. If the error code is "1", it means the license renewal was denied because concurrency limits are exceeded.
  4. In the above case, it is recommended the playback application reacts to it by automatically stopping the playback session and showing a relevant informational message to the end-user.

Widevine DRM​

In case of Desktop browsers, make sure the playback application enables persisting DRM information in the browser, but setting the EME "persistentState" configuration option to "required". This is needed to obtain unique client IDs on Desktop browsers. If this is not done, CSL-related license requests may either fail or the client ID the server sees will not be unique.

FairPlay DRM​

Triggering license renewals​

Unlike Widevine, FairPlay license renewals need to be triggered by the playback application. This means that the concurrency interval and the concurrency grace period that were configured on the server side, need to be also shared with and handled by the player application:

  1. Add a timer to the player code that tracks the time from the start of the playback (getting and starting the use of the initial license) or the last successful renewal.
  2. When this timer runs out, trigger a renewal as per your Player provider documentation or Apple's FairPlay client programming documentation found as part of the FairPlay SDK.
  3. Once a renewal license is received, reset the timer.
Error handling​
  1. (optional) If no renewal response arrives for in an appropriate time for any reason, you may consider triggering another renewal as a retry.
  2. (optional) If the total seconds since start of playback or last renewal are about to exceed the available license duration, which is concurrency_interval + concurrency_grace_period, consider stopping playback with a clear error message, instead of letting the license expire.

Blocking playback on macOS Safari​

Unlike other FairPlay platforms, FairPlay on macOS Safari currently doesn't support real-time license expiration. This means that it's not possible to securely block playback on the DRM level when license renewals are denied due to concurrency limits being exceeded. Due to this it is important to implement the blocking in the player application:

  1. Stop playback when error code "1" is received from the server.
  2. Stop playback when license duration (concurrency_interval + concurrency_grace_period) is about to run out.

On iOS Safari, real-time license expiration works. Consider sending a feature request about this to Apple, to increase the priority of adding this feature to macOS as well.

PlayReady DRM​

Similar to FairPlay, PlayReady license extensions need to be triggered manually by player application code. Additionally, PlayReady has no built-in "license renewing" concept. This means that licenses need to be extended using low-level EME methods where a new DRM session on the client needs to be created each time the license associated with a previous session is about to run out. Conceptually, the steps to take are the same as for FairPlay. Consult your player documentation or the EME documentation on how to achieve this.

note

Unlike FairPlay on macOS, PlayReady enforces real-time expiration on all platforms.

Coming soon - Axinom CSL demo player​

We are working on a demo player to showcase some of the manual implementation steps that are required to support CSL from the client side.

Appendix A - Example Entitlement Messages​

Example 1 - "BLOCK_NEW_DEVICE"​

{
"version": 1,
"com_key_id": "69e54088-e9e0-4530-8c1a-1eb6dcd0d14e",
"message": {
"type": "entitlement_message",
"version": 2,
"license": {
"concurrency_interval": 300,
"concurrency_grace_period": 60
},
"license_server": {
"access_control": {
"concurrency_limit": 2
}
},
"session": {
"user_id": "user_account_01000"
},
"content_keys_source": {
"inline": [
{
"id": "302f80dd-411e-4886-bca5-bb1f8018a024"
}
]
}
}
}