Framework Version: 3.0.3
Last Updated: 09/25/2023
Supports: SceneGraph
Engineer and co-author: Elias Kahwaji
The channel integration sample included in this document demonstrates a simple integration of the BrightLine Roku Direct SDK within a SceneGraph Roku application that does not rely on a RAF implementation.
In your channel's code, you'll load BrightLineDirect using component library. We'll provide you with your BrightLine SDK pkg file location url in our kick-off meeting. The following is a sample load:
sub BrightLine_LoadAPI(pkgUrl as String)
print "Channel _____BrightLineDirect:BrightLine_LoadAPI"
m.brightlineLoaded = false
' Cleaning previous version of the lib
BrightLine_Cleanup()
' Load the SDK using component library
m.adlib = CreateObject("roSGNode", "ComponentLibrary")
m.adlib.ObserveField("loadStatus", "BrightLine_LoadStatus")
m.adlib.SetField("uri", pkgUrl)
end sub
sub BrightLine_Cleanup()
' Invalidate previous instance of BrightLineDirect
m.top.RemoveChild(m.BrightLineDirect)
m.BrightLineDirect = invalid
end sub
sub BrightLine_LoadStatus()
print " _____onAdLibLoadStatusChanged: " m.adlib.loadStatus
print "m.adlib: " m.adlib
' Here we check the load status of the component library
if (m.adlib.loadStatus = "ready")
BrightLine_RunEnvironmentTask() ' just for demo purpose printing environment info
m.brightlineLoaded = true
else if m.adlib.loadStatus = "failed" then
m.brightlineLoaded = false
end if
end sub
To initialize library you should set the configID, which we'll be provided to you in our kick-off meeting, "1018" value can be used for development purposes. Second part is to provide library with video node and it's width/height using action interface. You can create new instance of the library each time you need it or you can use lib as singleton object, initialize it once and then just setting ad object each time you want to show the ad.
sub CreateBrightLineDirect(videoNode as Object)
' Createing the BrightLine SDK
BrightLine_CreateBLDirect()
' Here are three examples of "setting an action object"
' Set a pointer to the stream player, player must be set before setting the ad
' Player should be actual video node that will do playback
m.BrightLineDirect.action = {video: videoNode}
' Set the width of the UI
m.BrightLineDirect.action = {width: 1920}
' Set the height of the UI
m.BrightLineDirect.action = {height: 1080}
' Here we set the trackers object
m.BrightLineDirect.trackers = GetTrackers()
' We can make BrightLineDirect visible now.
' We'll use m.BrightLineDirect.state callback to set ad and focus.
m.BrightLineDirect.visible = true
end sub
sub BrightLine_CreateBLDirect()
' Making sure previous SDK instance is removed and cleaned by garbage collector
BrightLine_Cleanup()
' Creating the BrightLine SDK
m.BrightLineDirect = CreateObject("roSGNode", "BrightLineDirect:BL_init")
' m.BrightLineDirect.showDebug = true
m.top.AppendChild(m.BrightLineDirect)
' Set config ID to BLDirect to initialize it (it will be provided, for testing 1018 can be used)
' This is critical part, channel must set config id before setting ad
m.BrightLineDirect.configID = "1018"
' We observe the "state" field of the library. This will direct us to make it visible and focused, or not.
m.BrightLineDirect.ObserveField("state", "BrightLine_OnStateChange")
' This is the event listener. This observer is optional and channel can ommit it in most cases.
' It tells you when BrightLineDirect expanding and collapsing the microsite.
m.BrightLineDirect.ObserveField("event", "BrightLine_OnEvent")
end sub
Channel should observe state field to know when the library needs focus, visibility or signals the ad exited state. Different templates have different states flow:
Template | States Flow |
Overlay/StoreLocator | ready -> starting -> showing -> overlay_did_open -> overlay_did_close -> ready |
Microsite | ready -> starting -> showing -> overlay_did_open -> overlay_did_close_to_open_microsite -> microsite_did_open -> microsite_did_close -> ready |
AdSelector | ready -> starting -> showing -> overlay_did_open -> overlay_did_close -> (microsite_did_open -> microsite_did_close) -> ad_selector_completed -> ready |
Below is calback example with best practices of handling different states:
sub BrightLine_OnStateChange(msg as Object)
state = msg.getData()
print "~~~~~ onBrightLineDirectStateChange", state
if state = "initialized" then
print "BrightLine INIT"
' Lib is initialized, here we can provide our ad object.
' Ad will be shown according to startAt field of the ad object, based on video position
m.BrightLineDirect.ad = m.currentAd
else if state = "showing" then
print "BrightLine SHOWING"
' BrightLineDirect needs focus when it's in it's "showing" state
m.BrightLineDirect.SetFocus(true)
else if state = "ready" then
print "BrightLine READY"
' Ready is set when lib is initialized and not showing ad.
' Send focus back to whatever you want.
' In this case we're using the video player.
m.componentController.currentView.SetFocus(true)
else if state = "ad_selector_completed" then
' To not show stream-stitched ad after ad selector we can skip to needed position in this callback
m.componentController.currentView.seek = m.adStartTime + m.adDuration
else if state = "exited" then
' BrightLine has received an "exit" signal
' this is the equivalent of pressing the "back" key in a stream.
print "BrightLine EXITED"
' this will close player screen
m.componentController.currentView.exited = true
m.BrightLineDirect.visible = false
end if
end sub
Event field is optional to observe and channel should fully rely on state field. Below is callback with possible events currently fired.
sub BrightLine_OnEvent(msg as Object)
' Callback for events coming from the BrightLineDirect library.
blEvent = msg.GetData()
print "Channel _____onBrightLineAPI_event", blEvent
if blEvent.type = "UI"
' This type of events are fired when library UI was changed
' Currently it fires only when microsite is opened/closed
if blEvent.value = "expanded"
' microsite was opened to full screen
else if blEvent.value = "collapsed"
' microsite was collapsed and closed
end if
else if blEvent.type = "DL"
' This event is fired only by Conversion Template. It indicates when user presses on template
' blEvent.value will contain deeplink string for this conversion screen
end if
end sub
The method outlined below will prepare one ad for the library. The ad object which channel is setting to lib should have the same format as below. Library will be observing the video player, will start ad showing based on ad.startAt and will hide overlay at ad.startAt+ad.duration.
function GetAdObject(adUrl as String) as Object
return {
adId: 0315281 ' Populate from your ad
contentId: 1426392 ' Populate from your ad
adName: "single ad" ' Populate from your ad
provider: "BrightLine" ' This must be "BrightLine" for the library to work.
adURL: adUrl ' This is where the ad JSON url is passed into the single-ad structure
startAt: m.adStartTime ' This is when in the stream's progress the ad should appear.
duration: m.adDuration ' This is how long the ad should play.
tracking: [ ' This array is the created from the ad server response.
{
event: "AdStart"
time: 12
url: "http://www.foo.com/track"
triggered: false
},
{
event: "AdComplete"
time: 42
url: "http://www.foo.com/track"
triggered: false
}
]
}
end function
sub init()
m.componentController = m.top.findNode("componentController")
ShowAdSelectionScreen()
end sub
sub ShowAdSelectionScreen()
screen = CreateObject("roSGNode", "AdSelectionScreen")
screen.ObserveFieldScoped("selectedSDK", "OnSDKChanged")
screen.ObserveFieldScoped("selectedAd", "OnAdSelected")
m.componentController.callFunc("show", { view: screen })
end sub
sub OnSDKChanged(event as Object)
sdk = event.GetData()
if sdk <> invalid and sdk.pkg <> invalid
BrightLine_LoadAPI("pkg:/components/app.zip")
end if
end sub
sub OnAdSelected(event as Object)
m.selectedAd = event.GetData().Clone(false)
SetAdConstants()
' This is the setup for an ad using BrightLineDirect:
if m.brightlineLoaded = false then
print "*************************** (BrightLineDirect is NOT LOADED) ***************************"
else if m.brightlineLoaded = true then
' Here I'm getting the ad JSON url. This can be any approach you choose, but it'll be in the companion
' node, and have a "Brightline_RSG" subvert
newAd = event.GetData()
print"newAd: "newAd.url
if lcase(newAd.urlType) = "vast"
vastParser = CreateObject("roSGNode", "VastParserTask")
vastParser.vastUrl = newAd.url
vastParser.ObserveFieldScoped("vastData", "OnVastData")
vastParser.control = "run"
else ' standard json ad url
StartBrightLinePlayback(newAd.url)
end if
end if
end sub
sub SetAdConstants()
m.adStartTime = 12
m.adDuration = 30
if m.selectedAd <> invalid
if m.selectedAd.ad_start <> invalid then m.adStartTime = m.selectedAd.ad_start
if m.selectedAd.ad_duration <> invalid then m.adDuration = m.selectedAd.ad_duration
end if
end sub
sub OnVastData(event as Object)
vastData = event.GetData()
if vastData <> invalid
m.selectedAd.Append({
apiFramework: vastData.apiFramework
})
StartBrightLinePlayback(vastData.adJsonUrl.trim())
end if
end sub
sub StartBrightLinePlayback(adUrl as Object)
adTitle = m.selectedAd.title
videoURL = "http://cdn-media.brightline.tv/videos/ads/server_side_stitching_test/showmewhatyougot02/showmewhatyougot02.m3u8"
if m.selectedAd.mediafile <> invalid then videoURL = m.selectedAd.mediafile
videoNode = ShowPlayerScreen(videoURL)
' Creating and saving ad, ad should be set after BLDirect is initialized (state callback).
m.currentAd = GetAdObject(adUrl)
' Creating instance of the library and setting needed interfaces
CreateBrightLineDirect(videoNode)
' starting from v3.0.2 we can set ad object here and lib will handle it properly
' m.BrightLineDirect.ad = GetAdObject(adUrl)
end sub
sub CreateBrightLineDirect(videoNode as Object)
' Createing the BrightLine SDK
BrightLine_CreateBLDirect()
' Here are three examples of "setting an action object"
' Set a pointer to the stream player, player must be set before setting the ad
' Player should be actual video node that will do playback
m.BrightLineDirect.action = {video: videoNode}
' Set the width of the UI
m.BrightLineDirect.action = {width: 1920}
' Set the height of the UI
m.BrightLineDirect.action = {height: 1080}
' Here we set the trackers object
m.BrightLineDirect.trackers = GetTrackers()
' We can make BrightLineDirect visible now.
' We'll use m.BrightLineDirect.state callback to set ad and focus.
m.BrightLineDirect.visible = true
end sub
function ShowPlayerScreen(videoURL as String) as Object
playerScreen = CreateObject("roSGNode", "PlayerScreen")
videoPlayer = playerScreen.FindNode("videoPlayer")
' creating and setting video node content
videoContent = createObject("RoSGNode", "ContentNode")
videoContent.url = videoURL
videoPlayer.content = videoContent
' showing screen and starting playback
m.componentController.callFunc("show", { view: playerScreen })
videoPlayer.control = "play"
return videoPlayer
end function
function GetAdObject(adUrl as String) as Object
return {
adId: 0315281 ' Populate from your ad
contentId: 1426392 ' Populate from your ad
adName: "single ad" ' Populate from your ad
provider: "BrightLine" ' This must be "BrightLine" for the library to work.
adURL: adUrl ' This is where the ad JSON url is passed into the single-ad structure
startAt: m.adStartTime ' This is when in the stream's progress the ad should appear.
duration: m.adDuration ' This is how long the ad should play.
tracking: [ ' This array is the created from the ad server response.
{
event: "AdStart"
time: 12
url: "http://www.foo.com/track"
triggered: false
},
{
event: "AdComplete"
time: 42
url: "http://www.foo.com/track"
triggered: false
}
]
}
end function
function GetTrackers() as Object
' This is your tracking array for the spot quartiles.
return [
{
type:"Impression"
url:"http://events.brightline.tv/track?data=%7B%22type%22%3A%22impression%22%2C%22valid%22%3Atrue%2C%22ad_id%22%3A2344293%7D"
time:"12"
triggered: false
},
{
type:"FirstQuartile"
url:"http://events.brightline.tv/track?data=%7B%22type%22%3A%22duration%22%2C%22duration_type%22%3A%22impression%22%2C%22percent_complete%22%3A25%2C%22ad_id%22%3A2344293%7D"
time:"18"
triggered: false
},
{
type:"Midpoint"
url:"http://events.brightline.tv/track?data=%7B%22type%22%3A%22duration%22%2C%22duration_type%22%3A%22impression%22%2C%22percent_complete%22%3A50%2C%22ad_id%22%3A2344293%7D"
time:"27"
triggered: false
},
{
type:"ThirdQuartile"
url:"http://events.brightline.tv/track?data=%7B%22type%22%3A%22duration%22%2C%22duration_type%22%3A%22impression%22%2C%22percent_complete%22%3A75%2C%22ad_id%22%3A2344293%7D"
time:"33"
triggered: false
},
{
type:"Complete"
url:"http://events.brightline.tv/track?data=%7B%22type%22%3A%22duration%22%2C%22duration_type%22%3A%22impression%22%2C%22percent_complete%22%3A100%2C%22ad_id%22%3A2344293%7D"
time:"42"
triggered: false
},
]
end function
sub BrightLine_LoadAPI(pkgUrl as String)
print "Channel _____BrightLineDirect:BrightLine_LoadAPI"
m.brightlineLoaded = false
' Cleaning previous version of the lib
BrightLine_Cleanup()
' Load the SDK using component library
m.adlib = CreateObject("roSGNode", "ComponentLibrary")
m.adlib.ObserveField("loadStatus", "BrightLine_LoadStatus")
m.adlib.SetField("uri", pkgUrl)
end sub
sub BrightLine_Cleanup()
' Invalidate previous instance of BrightLineDirect
m.top.RemoveChild(m.BrightLineDirect)
m.BrightLineDirect = invalid
end sub
sub BrightLine_LoadStatus()
print " _____onAdLibLoadStatusChanged: " m.adlib.loadStatus
print "m.adlib: " m.adlib
' Here we check the load status of the component library
if (m.adlib.loadStatus = "ready")
BrightLine_RunEnvironmentTask() ' just for demo purpose printing environment info
m.brightlineLoaded = true
else if m.adlib.loadStatus = "failed" then
m.brightlineLoaded = false
end if
end sub
sub BrightLine_RunEnvironmentTask()
m.environmentInfoTask = createObject("roSGNode", "EnvironmentInfoTask")
#if debug
m.environmentInfoTask.showDebug = true
#end if
m.environmentInfoTask.control = "run"
end sub
sub BrightLine_CreateBLDirect()
' Making sure previous SDK instance is removed and cleaned by garbage collector
BrightLine_Cleanup()
' Creating the BrightLine SDK
m.BrightLineDirect = CreateObject("roSGNode", "BrightLineDirect:BL_init")
' m.BrightLineDirect.showDebug = true
m.top.AppendChild(m.BrightLineDirect)
' Set config ID to BLDirect to initialize it (it will be provided, for testing 1018 can be used)
' This is critical part, channel must set config id before setting ad
m.BrightLineDirect.configID = "1018"
' We observe the "state" field of the library. This will direct us to make it visible and focused, or not.
m.BrightLineDirect.ObserveField("state", "BrightLine_OnStateChange")
' This is the event listener. This observer is optional and channel can ommit it in most cases.
' It tells you when BrightLineDirect expanding and collapsing the microsite.
m.BrightLineDirect.ObserveField("event", "BrightLine_OnEvent")
end sub
sub BrightLine_OnStateChange(msg as Object)
state = msg.getData()
print "~~~~~ onBrightLineDirectStateChange", state
if state = "initialized" then
print "BrightLine INIT"
' Lib is initialized, here we can provide our ad object.
' Ad will be shown according to startAt field of the ad object, based on video position
m.BrightLineDirect.ad = m.currentAd
else if state = "showing" then
print "BrightLine SHOWING"
' BrightLineDirect needs focus when it's in it's "showing" state
m.BrightLineDirect.SetFocus(true)
else if state = "ready" then
print "BrightLine READY"
' Ready is set when lib is initialized and not showing ad.
' Send focus back to whatever you want.
' In this case we're using the video player.
m.componentController.currentView.SetFocus(true)
else if state = "ad_selector_completed" then
' To not show stream-stitched ad after ad selector we can skip to needed position in this callback
m.componentController.currentView.seek = m.adStartTime + m.adDuration
else if state = "exited" then
' BrightLine has received an "exit" signal
' this is the equivalent of pressing the "back" key in a stream.
print "BrightLine EXITED"
' this will close player screen
m.componentController.currentView.exited = true
m.BrightLineDirect.visible = false
end if
end sub
sub BrightLine_OnEvent(msg as Object)
' Callback for events coming from the BrightLineDirect library.
blEvent = msg.GetData()
print "Channel _____onBrightLineAPI_event", blEvent
if blEvent.type = "UI"
' This type of events are fired when library UI was changed
' Currently it fires only when microsite is opened/closed
if blEvent.value = "expanded"
' microsite was opened to full screen
else if blEvent.value = "collapsed"
' microsite was collapsed and closed
end if
else if blEvent.type = "DL"
' This event is fired only by Conversion Template. It indicates when user presses on template
' blEvent.value will contain deeplink string for this conversion screen
end if
end sub