Skip to content

Quality Entitlements and Quality-rated Stream Access

As stream quality keeps improving, end users are often keen to get the highest definition content available. It can be an interesting opportunity for a tenant to monetise high quality content.

The Vimond platform can help you to achieve this goal.

Firstly, you need to define an ordered list of qualities. This list has to be defined with the tenant and put into the configuration by the Vimond support team. Secondly, we recommend that you define a base quality. All the users will have access to this base quality by default.

Now let us take an example:

  • Ordered list of qualities (ranging from lowest to highest): sd, hd, 4k
  • Base quality: hd

Each user will have access to the streams available from the list, up to and including the base quality, which in this case this would include streams tagged sd, hd or non-tagged.

Users that have special entitlements can also access the streams above the base quality, in this case the 4k streams.

In order to give an end user special entitlements, we can add a special claim in the end user’s JWT access token. Each of these entitlements objects can contain a “quality” field.

{
"...": "...",
"https://vimond/entitlements": [
{
"svod": "*",
"quality": "4k",
"streamcount": "5"
}
]
}

In the example above, we allow the end user to watch 4k streams. If the quality field is not set, we will fallback on the base quality.

🚧 Legacy entitlements

The recommended approach is to use the entitlement structure in the jwt as shown above. However, if you are using the legacy entitlements the quality field is represented by its own jwt claim https://vimond/quality.

In this case we will define an asset with three streams, one tagged with sd, one with hd and one with 4k.

Here we add three external stream URLs to an asset:

curl -X PUT \
https://vimond-rest-api.ha.dev.vops.io/api/web/asset/141414 \
-H 'Authorization: Bearer {admin_bearer_token}' \
-H 'Content-Type: application/json;v=3'
-d '{
"id": 141414,
"categoryId": 97131,
"playbackStrategy": "EXTERNAL",
"externalPlaybacks": [
{
"playoutUri": "https://tenant.akamaihd.net/i/free/nogeoblock/path/to/video/sd/master.m3u8",
"format": "HLS",
"cdn": "AKAMAI",
"tokenizer": "AKAMAI_EDGE",
"cdnProfile": "default",
"urlProfile": "sd"
},
{
"playoutUri": "https://tenant.akamaihd.net/i/free/nogeoblock/path/to/video/hd/master.m3u8",
"format": "HLS",
"cdn": "AKAMAI",
"tokenizer": "AKAMAI_EDGE",
"cdnProfile": "default",
"urlProfile": "hd"
},
{
"playoutUri": "https://tenant.akamaihd.net/i/free/nogeoblock/path/to/video/4k/master.m3u8",
"format": "HLS",
"cdn": "AKAMAI",
"tokenizer": "AKAMAI_EDGE",
"cdnProfile": "default",
"urlProfile": "4k"
}
]
}'

Now, if we make a request with a user that has no quality field in his entitlement claim ( quality), the stream selected will be the best one available from the list, up to and including the base quality.

curl -X POST \
https://{tenant}.play-service.{domain}\
/api/v2/asset/141414/play?fields=alternativeStreams \
-H 'Authorization: Bearer {jwt_access_token}' \
-d '{
"device": {
"label": "DEVICE_LABEL_MY_IPHONE_FOR_EXAMPLE",
"udid": "DEVICE_LABEL_MY_IPHONE_FOR_EXAMPLE"
}
}
'

The response returns the hd streams as recommended, but notice that only the sd and hd streams are available.

{
"data": [
{
"assetId": "141414",
"assetTitle": "Test asset with external stream urls",
"categoryId": "97131",
"startTime": 0,
"endTime": 0,
"duration": 0,
"transmissionTime": "2018-07-30T11:17:13Z",
"isDrmProtected": false,
"isLive": false,
"recommendedStream": {
"id": "3",
"bitrate": 0,
"mediaFormat": "hls",
"mimeType": "application/x-mpegurl",
"provider": "AKAMAI",
"urlProfile": "hd",
"manifest": {
"headers": [],
"uri": "https://tenant.akamaihd.net/i/free/nogeoblock/path/to/video/hd/master.m3u8?hdnea=st=1533065048~exp=1533065336~acl=%2fi%2ffree%2fnogeoblock%2fpath%2fto%2fvideo%2fhd%2f%2a~hmac=0420f52f1cddff04de5e4c0cb0667bfdbe6baa75eebe88eeb0159afe122ac8b9"
}
},
"alternativeStreams": [
{
"id": "1",
"bitrate": 0,
"mediaFormat": "hls",
"mimeType": "application/x-mpegurl",
"provider": "AKAMAI",
"urlProfile": "sd",
"manifest": {
"headers": [],
"uri": "https://tenant.akamaihd.net/i/free/nogeoblock/path/to/video/sd/master.m3u8?hdnea=st=1533065048~exp=1533065336~acl=%2fi%2ffree%2fnogeoblock%2fpath%2fto%2fvideo%2fsd%2f%2a~hmac=ee4271080e9ccdae510e22f368d99ac3304e258ba2ac1de09e41cf12b5b212ad"
}
}
],
"playerEventRequest": {
"body": {
"eventName": "player_log_event",
"originator": "${originator}",
"originatorId": "fb85ba9a-72bd-47a8-8071-68dddee1347d",
"subProfileId": "00000000-0000-0000-0000-000000000000",
"versions": [
"1.0"
],
"tenant": "vimond",
"logData": "To complete",
"client": {
"buildName": "${client_build_name}",
"buildVersion": "${client_build_version}",
"deviceId": "DEVICE_LABEL_MY_IPHONE_FOR_EXAMPLE",
"drm": "${client_drm}",
"envPlatform": "${client_env_platform}",
"envVersion": "${client_env_version}",
"pageUrl": "${client_page_url}",
"platform": "web",
"playerEvent": "${player_event}",
"playerState": "${player_state}",
"streamUrl": "${client_stream_url}",
"userAgent": "PostmanRuntime/7.2.0",
"videoFormat": "${video_mime_type}",
"videoProtocol": "${video_protocol}",
"viewingSession": "${viewing_session}"
},
"progress": {
"assetId": "141414",
"categoryId": "97131",
"title": "Test asset with external stream urls",
"playbackType": "vod",
"eventNumber": 1,
"vod": {
"duration": 0,
"position": "${vod_stream_position}"
}
},
"timestamp": "${client_date}",
"streamRules": {
"subscription": {
"name": "Subscription rule",
"streamLimit": 2
}
}
},
"eventInterval": 10,
"uri": "https://vimond.player-session-service.lof.dev.k8s.vops.io/api/v1/external/player-events"
}
}
],
"links": {
"self": "/api/v2/asset/141414/play"
}
}

Now if we add the following field to the entitlement of the end user token "quality": "4k", and run the same request we can see that the result differs. The recommended stream is now the 4k one and the user can have access to all of the below 4k quality too.

{
"data": [
{
"assetId": "141414",
"assetTitle": "Test asset with external stream urls",
"categoryId": "97131",
"startTime": 0,
"endTime": 0,
"duration": 0,
"transmissionTime": "2018-07-30T11:17:13Z",
"isDrmProtected": false,
"isLive": false,
"recommendedStream": {
"id": "2",
"bitrate": 0,
"mediaFormat": "hls",
"mimeType": "application/x-mpegurl",
"provider": "AKAMAI",
"urlProfile": "4k",
"manifest": {
"headers": [],
"uri": "https://tenant.akamaihd.net/i/free/nogeoblock/path/to/video/4k/master.m3u8?hdnea=st=1533065483~exp=1533065771~acl=%2fi%2ffree%2fnogeoblock%2fpath%2fto%2fvideo%2f4k%2f%2a~hmac=7be8506d66c48023a4a767b8300005dce566e77790a8ee91821b94775b1a1d7e"
}
},
"alternativeStreams": [
{
"id": "1",
"bitrate": 0,
"mediaFormat": "hls",
"mimeType": "application/x-mpegurl",
"provider": "AKAMAI",
"urlProfile": "sd",
"manifest": {
"headers": [],
"uri": "https://tenant.akamaihd.net/i/free/nogeoblock/path/to/video/sd/master.m3u8?hdnea=st=1533065483~exp=1533065771~acl=%2fi%2ffree%2fnogeoblock%2fpath%2fto%2fvideo%2fsd%2f%2a~hmac=0e411e148aceea3137e5612dff06336deb0c9b10b2cbbdcf3e11e8fd550d3d45"
}
},
{
"id": "3",
"bitrate": 0,
"mediaFormat": "hls",
"mimeType": "application/x-mpegurl",
"provider": "AKAMAI",
"urlProfile": "hd",
"manifest": {
"headers": [],
"uri": "https://tenant.akamaihd.net/i/free/nogeoblock/path/to/video/hd/master.m3u8?hdnea=st=1533065483~exp=1533065771~acl=%2fi%2ffree%2fnogeoblock%2fpath%2fto%2fvideo%2fhd%2f%2a~hmac=d256aa65fb78bf4afbfc3554967d3914dd7e964c5a5132259ca00e5852836a20"
}
}
],
"playerEventRequest": {
"body": {
"eventName": "player_log_event",
"originator": "${originator}",
"originatorId": "fb85ba9a-72bd-47a8-8071-68dddee1347d",
"subProfileId": "00000000-0000-0000-0000-000000000000",
"versions": [
"1.0"
],
"tenant": "vimond",
"logData": "To complete",
"client": {
"buildName": "${client_build_name}",
"buildVersion": "${client_build_version}",
"deviceId": "DEVICE_LABEL_MY_IPHONE_FOR_EXAMPLE",
"drm": "${client_drm}",
"envPlatform": "${client_env_platform}",
"envVersion": "${client_env_version}",
"pageUrl": "${client_page_url}",
"platform": "web",
"playerEvent": "${player_event}",
"playerState": "${player_state}",
"streamUrl": "${client_stream_url}",
"userAgent": "PostmanRuntime/7.2.0",
"videoFormat": "${video_mime_type}",
"videoProtocol": "${video_protocol}",
"viewingSession": "${viewing_session}"
},
"progress": {
"assetId": "141414",
"categoryId": "97131",
"title": "Test asset with external stream urls",
"playbackType": "vod",
"eventNumber": 1,
"vod": {
"duration": 0,
"position": "${vod_stream_position}"
}
},
"timestamp": "${client_date}",
"streamRules": {
"subscription": {
"name": "Subscription rule",
"streamLimit": 2
}
}
},
"eventInterval": 10,
"uri": "https://vimond.player-session-service.lof.dev.k8s.vops.io/api/v1/external/player-events"
}
}
],
"links": {
"self": "/api/v2/asset/141414/play"
}
}

Even though an end user may be entitled to watch a high quality stream, forcing them to take the highest quality is not recommended. It could potentially give them a bad experience if their bandwidth does not support the highest quality, or it could simply be useless if they are watching a stream on their smartphone.

It is possible to overwrite the highest definition wished in the play request by taking advantage of the profile query parameter.

curl -X POST \
'https://{tenant}.play-service.{domain}/api/v2/asset/141414/play?profile=hd' \
-H 'Authorization: Bearer {jwt_access_token}' \
-d '{
"device": {
"label": "DEVICE_LABEL_MY_IPHONE_FOR_EXAMPLE",
"udid": "DEVICE_LABEL_MY_IPHONE_FOR_EXAMPLE"
}
}
'

In the response, the recommended stream is now hd, although this user is entitled to watch 4k streams.

{
"data": [
{
"assetId": "141414",
"assetTitle": "Test asset with external stream urls",
"categoryId": "97131",
"startTime": 0,
"endTime": 0,
"duration": 0,
"transmissionTime": "2018-07-30T11:17:13Z",
"isDrmProtected": false,
"isLive": false,
"recommendedStream": {
"id": "3",
"bitrate": 0,
"mediaFormat": "hls",
"mimeType": "application/x-mpegurl",
"provider": "AKAMAI",
"urlProfile": "hd",
"manifest": {
"headers": [],
"uri": "https://tenant.akamaihd.net/i/free/nogeoblock/path/to/video/hd/master.m3u8?hdnea=st=1533065804~exp=1533066092~acl=%2fi%2ffree%2fnogeoblock%2fpath%2fto%2fvideo%2fhd%2f%2a~hmac=092355d6712ead8a80b04880b1db0dc814840e6cb9576d8583551208d485dc12"
}
},
"playerEventRequest": {
"body": {
"eventName": "player_log_event",
"originator": "${originator}",
"originatorId": "fb85ba9a-72bd-47a8-8071-68dddee1347d",
"subProfileId": "00000000-0000-0000-0000-000000000000",
"versions": [
"1.0"
],
"tenant": "vimond",
"logData": "To complete",
"client": {
"buildName": "${client_build_name}",
"buildVersion": "${client_build_version}",
"deviceId": "DEVICE_LABEL_MY_IPHONE_FOR_EXAMPLE",
"drm": "${client_drm}",
"envPlatform": "${client_env_platform}",
"envVersion": "${client_env_version}",
"pageUrl": "${client_page_url}",
"platform": "web",
"playerEvent": "${player_event}",
"playerState": "${player_state}",
"streamUrl": "${client_stream_url}",
"userAgent": "PostmanRuntime/7.2.0",
"videoFormat": "${video_mime_type}",
"videoProtocol": "${video_protocol}",
"viewingSession": "${viewing_session}"
},
"progress": {
"assetId": "141414",
"categoryId": "97131",
"title": "Test asset with external stream urls",
"playbackType": "vod",
"eventNumber": 1,
"vod": {
"duration": 0,
"position": "${vod_stream_position}"
}
},
"timestamp": "${client_date}",
"streamRules": {
"subscription": {
"name": "Subscription rule",
"streamLimit": 2
}
}
},
"eventInterval": 10,
"uri": "https://vimond.player-session-service.lof.dev.k8s.vops.io/api/v2/external/player-events"
}
}
],
"links": {
"self": "/api/v2/asset/141414/play"
}
}