Entitlement and DRM
What you learn​
You're instantiating DIVA Player in your Roku app and relying on DIVA Back Office as the video streaming source.
The goal of this article is to build the simplest Roku app to stream a video from the DIVA Back Office with entitlement check and DRM.
Before starting​
- Ensure
Diva Player lib
andDIVA Back Office Adapter lib
downloaded (Setup > Step 13). - Ask your video engineers team the
<video id>
and<settings URL>
. - Ensure the settings file contains:
- The
entitlementCheck
section:Note: Consider"entitlementCheck": {
"entitlementUrl": "<yourEntitlementBaseURL>/tokenize",
"heartbeatUrl": "<yourEntitlementBaseURL>/heartbeat",
"heartbeatPollingInterval": 180000,
"heartbeatSeekInterval": 10000,
"other": "{<sessionId>}|{<platform>}",
"fairPlayCertificateUrl": "<yourFairPlayCertificateBaseURL>/fairplay.cer",
"queryParams": ["VideoSourceName", "VideoId", "VideoKind"],
"data": {
"sessionId": "{Run.SessionID}",
"platform": "Roku",
"idfa": "{Run.idfa}",
"p.adsParams.paln":"{diva_pal_nonce}"
}
}other
deprecated and replaced bydata
. - The
videoPlatformsPriority
section:"videoPlatformsPriority": {
"default": [
{
"type": "DASH"
"sourceName": "Desktop-DASH",
"drmType": "playready"
},
{
"type": "DASH",
"sourceName": "Desktop-DASH",
"drmType": "widevine"
},
{
"type": "HLS",
"sourceName": "Desktop-V4",
"drmType": "fairplay"
}
]
}
- The
Instantiation​
DRM code​
There's no code to add to the Basic instantiation code.
Entitlement code​
Enrich the Basic instantiation code with the entitlement configuration:
-
Add the
entitlementConfigurationRequest
field observer toDiva Player lib
for configuring the Entitlement:sub launchBOAdapter()
m.dpUtilsNode = CreateObject("roSGNode", "DivaPlayerSDK:DPUtilsNode")
m.boAdapterNode = CreateObject("roSGNode", "DivaBOAdapter:DivaBOAdapter")
...
' Get event from the Diva Player for configuration Entitlement Service
m.dpUtilsNode.observeField("entitlementConfigurationRequest", "onEntitlementConfigurationHandler")
...
m.dpUtilsNode.callFunc("runPlayer") 'Launching the Diva Player
end sub
sub onEntitlementConfigurationHandler()
hbInterval = m.boAdapterNode.callFunc("getEntitlementHeartBeatInterval")
m.dpUtilsNode.callFunc("setEntitlementConfiguration", {heartBeatInterval: hbInterval})
end subNote:
Diva Player lib
calls theentitlementConfigurationRequest
field observer after launch. -
Add the
entitlementRequest
field observer toDiva Player lib
. After calling the fieldentitlementRequest
observer, get data fromDiva Player lib
and send to theentitlementRequest
field in theDIVA Back Office Adapter lib
:sub launchBOAdapter()
...
' Get a request from the Diva Player on making the Entitlement request to the BE
m.dpUtilsNode.observeField("entitlementRequest", "onEntitlementHandler")
...
end sub
sub onEntitlementHandler(evt as dynamic)
data = evt.getData()
m.boAdapterNode.entitlementRequest = data
end subNote:
Diva Player lib
calls the fieldentitlementRequest
observer whenDiva Player lib
sends a request to the Entitlement Service. The request is sent in two cases:- Case 1: When launching a stream playback.
- Case 2: When calling the heartbeat.
-
Implement the
entitlementPayloadMap
callback, which receives the entitlement payload and returns the same payload — possibly modified. This callback allows the integrator to modify the entitlement payload before sending it to the entitlement service....
' Register the entitlementPayloadMap callback from BO adapter'
m.boAdapterNode.activateEntitlementPayloadMapCallback = true
m.boAdapterNode.observeField("entitlementPayloadMap", "onEntitlementPayloadMapHandler")
...
sub onEntitlementPayloadMapHandler(evt as dynamic)
entitlementPayloadMap = evt.getData()
' Integrator can make changes in m.entitlementPayloadMap then send it back to the BO Adapter through "setEntitlementPayloadMap"
' Important: in case you activate Entitlement Payload Map Callback (activateEntitlementPayloadMapCallback = true)
' you need to observe on "entitlementPayloadMap" and then get data from this observer and send data back through the "setEntitlementPayloadMap" field in BO Adapter
' Without these actions, BO Adapted not make the Entitlement call. BO Adapter will wait for the Call Back from the Main Application
m.boAdapterNode.setEntitlementPayloadMap = entitlementPayloadMap
end sub3.1 Set
activateEntitlementPayloadMapCallback
totrue
to activate the callback.3.2 If needed, change
entitlementPayloadMap
based on the following callback input parameters:requestType
: It can valueprocessUrl
orheartbeat
:processUrl
: It indicates the entitlement initialization call.heartBeat
: It indicates the entitlement check call.
payloadMap
: Created every time DIVA Player calls the entitlement service: E.g.:Note: If{
"Type": 1, // 1 = 'processUrl', 2 = 'heartbeat'
"User": "<userID>", //user identifier
"VideoId": "<videoID>",
"VideoSource": "<video source>",
"VideoKind": "replay", //possible values depend on the video production
"AssetState": "3", //2 = 'live', 3 = 'VOD'
"PlayerType": "<player type>",
"VideoSourceFormat": "DASH",
"VideoSourceName": "Desktop-DASH",
"DRMType": "widevine",
"AuthType": "Token",
"ContentKeyData": "<content key data>",
"SessionId": "<sessionID>",
"PlaybackSessionId": "<PlaybackSessionId>",
"Other": "{"<sessionId>"}|{"<platform>"}",
"Data": "{"sessionId":"<sessionID>","platform":"roku","idfa":"{Run.idfa}","offsetFromLiveEdge":"0","p.adsParams.paln":"{diva_pal_nonce}"}",
"Version": "1"
}AssetState = "1"
there's something wrong: "1" means 'scheduled', which is a not yet existing video that DIVA Player cannot manage.
Working sample code (.brs)​
sub init()
initScreen()
end sub
sub initScreen()
loadLibs()
end sub
sub loadLibs()
loadPlayer()
'Init of loading the BO Adapter lib
loadBOAdapter()
end sub
sub loadPlayer()
' Creates the ComponentLibrary (the DivaPlayerSDK in this case)
m.divaPlayerSDK = CreateObject("roSGNode", "ComponentLibrary")
m.divaPlayerSDK.id = "DivaPlayerSDK"
m.divaPlayerSDK.uri = "pkg:/resources/diva-roku-cl-sdk-5.7.0.zip"
' Adding the ComponentLibrary node to the scene will start the download of the library
m.top.appendChild(m.divaPlayerSDK)
m.divaPlayerSDK.observeField("loadStatus", "onDivaPlayerLoadStatusChanged")
end sub
sub loadBOAdapter()
' Creates the ComponentLibrary (the BO Adapter in this case)
m.divaBOAdapter = CreateObject("roSGNode", "ComponentLibrary")
m.divaBOAdapter.id = "DivaBOAdapter"
m.divaBOAdapter.uri = "pkg:/resources/diva-bo-adapter-cl-1.2.0.zip"
' Adding the ComponentLibrary node to the scene will start the download of the library
m.top.appendChild(m.divaBOAdapter)
m.divaBOAdapter.observeField("loadStatus", "onDivaPlayerLoadStatusChanged")
end sub
sub onDivaPlayerLoadStatusChanged()
if m.divaPlayerSDK.loadStatus = "ready"
if m.divaBOAdapter <> invalid and m.divaBOAdapter.loadStatus = "ready"
launchBOAdapter()
end if
end if
end sub
sub launchBOAdapter()
m.boAdapterNode = CreateObject("roSGNode", "DivaBOAdapter:DivaBOAdapter")
m.boAdapterNode.initData = {
"settingId": <video id>
"videoId": <video id>
"dictionaryId": "en_GB"
}
' Observe BO Adapter DiveLaunchParams
m.boAdapterNode.observeField("divaLaunchParams", "onBOAdapterDivaLaunchParamsHandler")
' Observe BO Adapter Dictionary
m.boAdapterNode.observeField("dictionary", "onBOAdapterDictionaryHandler")
' Observe BO Adapter VideoMetaDataNode
m.boAdapterNode.observeField("videoDataNode", "onBOAdapterVideoDataNodeHandler")
' Observe BO Adapter Entitlement Data
m.boAdapterNode.observeField("entitlementData", "onEntitlementDataHandler")
' Observe BO Adapter Settings
m.boAdapterNode.observeField("settingsNode", "onSettingsHandler")
' Observe BO Adapter when all necessary data for launching the Diva Player was loaded
' After getting this event you can launch the Diva Player
m.boAdapterNode.observeField("boAdapterReady", "onBOAdapterReady")
m.boAdapterNode.activateEntitlementPayloadMapCallback = true
m.boAdapterNode.observeField("entitlementPayloadMap", "onEntitlementPayloadMapHandler")
'Diva Player Utils Node
m.dpUtilsNode = getDivaPlayerUtilsNode()
' Observe Diva Player exit call
m.dpUtilsNode.observeField("divaPlayerExit", "onDivaPlayerExitHandler")
' Observe Diva Player actions
' Observe Diva Metadata update call
m.dpUtilsNode.observeField("metaDataUpdate", "onMetadataUpdateHandler")
' Get a request from the Diva Player on making the Entitlement request to the BE
m.dpUtilsNode.observeField("entitlementRequest", "onEntitlementHandler")
' Get event from the Diva Player for configuration Entitlement Service
m.dpUtilsNode.observeField("entitlementConfigurationRequest", "onEntitlementConfigurationHandler")
' Getting Diva Player Node
m.divaPlayer = m.dpUtilsNode.callFunc("getPlayer")
' Adding Diva Player for the visualisation
screensStack = m.getScreensStack()
screensStack.appendChild(m.divaPlayer)
m.divaPlayer.setFocus(true)
end sub
function getDivaPlayerUtilsNode() as dynamic
if NOT isValid(m.dpUtilsNode)
m.dpUtilsNode = CreateObject("roSGNode", "DivaPlayerSDK:DPUtilsNode")
end if
return m.dpUtilsNode
end function
sub onBOAdapterDivaLaunchParamsHandler(evt as dynamic)
data = evt.getData()
' Setup Diva Player launch parameters
m.dpUtilsNode.callFunc("setLaunchParams", data)
end sub
sub onBOAdapterDictionaryHandler(evt as dynamic)
data = evt.getData()
' Setup Diva Player dictionary
m.dpUtilsNode.callFunc("setDictionary", data)
end sub
sub onBOAdapterVideoDataNodeHandler(evt as dynamic)
data = evt.getData()
' Setup Diva Player Metadata
m.dpUtilsNode.callFunc("setMetaData", data)
end sub
sub onSettingsHandler(evt as dynamic)
data = evt.getData()
' Setup Diva Player Settings
m.dpUtilsNode.callFunc("setSettings", data)
end sub
sub onEntitlementDataHandler(evt as dynamic)
' Set to the Diva Player response from Entitlement Service
m.dpUtilsNode.callFunc("setEntitlementData", evt.getData())
end sub
sub onBOAdapterReady(evt as dynamic)
m.dpUtilsNode.callFunc("runPlayer")
end sub
sub onEntitlementPayloadMapHandler(evt as dynamic)
entitlementPayloadMap = evt.getData()
' Integrator can make changes in m.entitlementPayloadMap then send it back to the BO Adapter through "setEntitlementPayloadMap"
' Important: in case you activate Entitlement Payload Map Callback (activateEntitlementPayloadMapCallback = true)
' you need to observe on "entitlementPayloadMap" and then get data from this observer and send data back through the "setEntitlementPayloadMap" field in BO Adapter
' Without these actions, BO Adapted not make the Entitlement call. BO Adapter will wait for the Call Back from the Main Application
m.boAdapterNode.setEntitlementPayloadMap = entitlementPayloadMap
end sub
sub destroyBOAdapter()
if m.boAdapterNode <> invalid
m.boAdapterNode.unobserveField("error")
m.boAdapterNode.destroy = true
m.boAdapterNode = invalid
end if
end sub
sub onMetadataUpdateHandler(evt as dynamic)
data = evt.getData()
m.boAdapterNode.requestMetaDataUpdate = data
end sub
sub onEntitlementHandler(evt as dynamic)
data = evt.getData()
m.boAdapterNode.entitlementRequest = data
end sub
sub onEntitlementConfigurationHandler()
hbInterval = m.boAdapterNode.callFunc("getEntitlementHeartBeatInterval")
if hbInterval <> invalid and hbInterval > 0
m.dpUtilsNode.callFunc("setEntitlementConfiguration", {heartBeatInterval: hbInterval})
end if
end sub
sub onDivaPlayerExitHandler()
m.dpUtilsNode = invalid
'Destroy BO Adapter Node
destroyBOAdapter()
end sub