IMA SDKs make it easy to integrate multimedia ads into your websites and apps. IMA SDKs can request ads from any VAST-compliant ad server and manage ad playback in your apps. With IMA DAI SDKs, apps make a stream request for ad and content video—either VOD or live content. The SDK then returns a combined video stream, so that you don't have to manage switching between ad and content video within your app.
This guide demonstrates how to play a DAI Pod Serving stream, using the IMA DAI SDK for Roku.
IMA DAI Pod Serving overview
Implementing pod serving using the IMA DAI involves two main SDK components, which are demonstrated in this guide:
- PodStreamRequest: An object that defines a stream request to Google's advertising servers. Requests specify a Network Code, Custom Asset Key, and an optional API key, and other optional parameters.
- StreamManager: An object that handles communication between the video stream and the IMA DAI SDK, such as firing tracking pings and forwarding stream events to the publisher.
In addition, you will need to make a request to your manifest manipulation server to retrieve the stream manifest for your app to display. The exact process may vary from server to server.
Prerequisites
Before you begin, you need to:
- Read through our compatibility page to make sure your intended use case is supported.
- Download our Roku sample player code.
- Deploy the above sample player code to a Roku device to verify that your development set up is working.
Play your video
The sample video player provided plays a content video out of the box. Deploy the sample player to your Roku device to ensure your development environment is set up properly.
Turn your video player into an IMA Dynamic Ad Insertion stream player
Create Sdk.xml
Add a new file to your project alongside MainScene.xml
called Sdk.xml
,
and add the following boilerplate:
Sdk.xml
<?xml version = "1.0" encoding = "utf-8" ?> <component name = "imasdk" extends = "Task"> <interface> </interface> <script type = "text/brightscript"> <![CDATA[ ' Your code goes here. ]]> </script> </component>
You need to edit both of these files (MainScene.xml
and Sdk.xml
)
throughout this guide. A heading above each code snippet indicates in which file you need
to add that snippet.
Load the IMA SDK Framework
To load the framework, add the following to manifest and Sdk.xml:
Manifest
bs_libs_required=googleima3
Sdk.xml
<?xml version = "1.0" encoding = "utf-8" ?> <component name = "imasdk" extends = "Task"> <interface> </interface> <script type = "text/brightscript"> <![CDATA[ Library "IMA3.brs" ]]> </script> </component>
Initialize the IMA SDK
The first step to loading your IMA Dynamic Ad Insertion stream is to load and initialize the IMA SDK. The following initializes the IMA SDK script.
Sdk.xml
<?xml version = "1.0" encoding = "utf-8" ?> <component name = "imasdk" extends = "Task"> <interface> <field id="sdkLoaded" type="Boolean" /> <field id="errors" type="stringarray" /> </interface> <script type = "text/brightscript"> <![CDATA[ Library "IMA3.brs" sub init() m.top.functionName = "runThread" End Sub sub runThread() if not m.top.sdkLoaded loadSdk() End If End Sub sub loadSdk() If m.sdk = invalid m.sdk = New_IMASDK() End If m.top.sdkLoaded = true End Sub ]]> </script> </component>
Now start this task in MainScene.xml and remove the call to load the content stream.
MainScene.xml
function init() m.video = m.top.findNode("myVideo") m.video.notificationinterval = 1 loadImaSdk() end function function loadImaSdk() as void m.sdkTask = createObject("roSGNode", "imasdk") m.sdkTask.observeField("sdkLoaded", "onSdkLoaded") m.sdkTask.observeField("errors", "onSdkLoadedError") m.sdkTask.control = "RUN" end function Sub onSdkLoaded(message as Object) print "----- onSdkLoaded --- control " ; message End Sub Sub onSdkLoadedError(message as Object) print "----- errors in the sdk loading process --- " ; message End Sub
Create an IMA stream player
Next, you need to use your existing roVideoScreen
to create an IMA stream player.
This stream player implements three callback methods: loadUrl
,
adBreakStarted
, and adBreakEnded
. Also disable trick play when the
stream is loaded. This prevents users from skipping a pre-roll in the instant that it starts,
before the ad break started event is fired.
Sdk.xml
<interface> <field id="sdkLoaded" type="Boolean" /> <field id="errors" type="stringarray" /> <field id="urlData" type="assocarray" /> <field id="adPlaying" type="Boolean" /> <field id="video" type="Node" /> </interface> ... sub setupVideoPlayer() sdk = m.sdk m.player = sdk.createPlayer() m.player.top = m.top m.player.loadUrl = Function(urlData) m.top.video.enableTrickPlay = false m.top.urlData = urlData End Function m.player.adBreakStarted = Function(adBreakInfo as Object) print "---- Ad Break Started ---- " m.top.adPlaying = True m.top.video.enableTrickPlay = false End Function m.player.adBreakEnded = Function(adBreakInfo as Object) print "---- Ad Break Ended ---- " m.top.adPlaying = False m.top.video.enableTrickPlay = true End Function m.player.seek = Function(timeSeconds as Double) print "---- SDK requested seek to ----" ; timeSeconds m.top.video.seekMode = "accurate" m.top.video.seek = timeSeconds End Function End Sub
Create and execute a pod serving stream request
Now that you have a stream player, you can create and execute a stream request. This example has
data for a pod serving stream stored in m.testPodServingStream
.
In the m.testPodServingStream
object, you'll store the parameters that Google Ad
Manager needs to identify the stream in question, such as network code and custom asset key. Also
store the manifest url used to access your video stitching server. In this case the manifest URL
will need to have the Google Stream ID added, after the stream request is returned.
To be able to support AdUI, such as adChoices icons, you must also pass a reference to the node containing your content video as part of your request.
MainScene.xml
function init() m.video = m.top.findNode("myVideo") m.video.notificationinterval = 1 m.testPodServingStream = { title: "Pod Serving Stream", networkCode: "51636543", customAssetKey: "google-sample", manifestUrl: "https://encodersim.sandbox.google.com/masterPlaylist/9c654d63-5373-4673-8c8d-6d92b66b9d46/master.m3u8?gen-seg-redirect=true&network=51636543&event=google-sample&pids=devrel4628000,devrel896000,devrel3528000,devrel1428000,devrel2628000,devrel1928000&seg-host=dai.google.com&stream_id=[[STREAMID]]", apiKey: "", } loadImaSdk() end function function loadImaSdk() as void m.sdkTask = createObject("roSGNode", "imasdk") m.sdkTask.observeField("sdkLoaded", "onSdkLoaded") m.sdkTask.observeField("errors", "onSdkLoadedError") selectedStream = m.testPodServingStream m.videoTitle = selectedStream.title m.sdkTask.streamData = selectedStream m.sdkTask.video = m.video m.sdkTask.control = "RUN" end function
Sdk.xml
<interface> <field id="sdkLoaded" type="Boolean" /> <field id="errors" type="stringarray" /> <field id="urlData" type="assocarray" /> <field id="adPlaying" type="Boolean" /> <field id="video" type="Node" /> <field id="streamData" type="assocarray" /> <field id="streamManagerReady" type="Boolean" /> </interface> ... sub runThread() if not m.top.sdkLoaded loadSdk() End If if not m.top.streamManagerReady loadStream() End If End Sub Sub loadStream() sdk = m.sdk stream = m.top.streamData sdk.initSdk() setupVideoPlayer() ' This pod serving request for a live stream will return a stream id along with important information for the IMA DAI SDK. request = sdk.streamRequest.CreatePodLiveStreamRequest(stream.customAssetKey, stream.networkCode, stream.apiKey) request.player = m.player request.adUiNode = m.top.video ' Required to support UI elements for 'Why This Ad?' and skippability request.videoObject = m.top.video requestResult = sdk.requestStream(request) If requestResult <> Invalid print "Error requesting stream " ; requestResult Else m.streamManager = Invalid While m.streamManager = Invalid sleep(50) m.streamManager = sdk.getStreamManager() End While If m.streamManager = Invalid or m.streamManager["type"] <> Invalid or m.streamManager["type"] = "error" errors = CreateObject("roArray", 1, True) print "error " ; m.streamManager["info"] errors.push(m.streamManager["info"]) m.top.errors = errors Else m.top.streamManagerReady = True addCallbacks() m.streamManager.start() End If End If End Sub
Add event listeners and start the stream
After requesting your stream, there are only a few things left to do: add event listeners to track ad progress and forward Roku messages to the SDK. It is important that you forward all messages to the SDK to ensure correct ad playback. Failure to do so will cause ad views to be improperly reported.
In this step, you also add a function to replace the [[STREAMID]] macro with the stream id and pass the completed manifest request url to the video player.
MainScene.xml
function loadImaSdk() as void m.sdkTask = createObject("roSGNode", "imasdk") m.sdkTask.observeField("sdkLoaded", "onSdkLoaded") m.sdkTask.observeField("errors", "onSdkLoadedError") selectedStream = m.testVodStream m.videoTitle = selectedStream.title m.sdkTask.streamData = selectedStream m.sdkTask.observeField("urlData", "loadAdPodStream") m.sdkTask.video = m.video m.sdkTask.control = "RUN" end function Sub loadAdPodStream(message as Object) print "Url Load Requested " ; message data = message.getData() streamId = data.streamId manifest = m.sdkTask.streamData.manifestUrl.Replace("[[STREAMID]]", streamId) playStream(manifest, data.format) End Sub Sub playStream(url as Object, format as String) vidContent = createObject("RoSGNode", "ContentNode") vidContent.url = url vidContent.title = m.videoTitle vidContent.streamformat = format m.video.content = vidContent m.video.setFocus(true) m.video.visible = true m.video.control = "play" m.video.EnableCookies() End Sub
Sdk.xml
sub runThread() if not m.top.sdkLoaded loadSdk() End If if not m.top.streamManagerReady loadStream() End If If m.top.streamManagerReady runLoop() End If End Sub Sub runLoop() m.top.video.timedMetaDataSelectionKeys = ["*"] m.port = CreateObject("roMessagePort") ' Listen to all fields. ' IMPORTANT: Failure to listen to the position and timedmetadata fields ' could result in ad impressions not being reported. fields = m.top.video.getFields() for each field in fields m.top.video.observeField(field, m.port) end for while True msg = wait(1000, m.port) if m.top.video = invalid print "exiting" exit while end if m.streamManager.onMessage(msg) currentTime = m.top.video.position If currentTime > 3 And not m.top.adPlaying m.top.video.enableTrickPlay = true End If end while End Sub Function addCallbacks() as Void m.streamManager.addEventListener(m.sdk.AdEvent.ERROR, errorCallback) m.streamManager.addEventListener(m.sdk.AdEvent.START, startCallback) m.streamManager.addEventListener(m.sdk.AdEvent.FIRST_QUARTILE, firstQuartileCallback) m.streamManager.addEventListener(m.sdk.AdEvent.MIDPOINT, midpointCallback) m.streamManager.addEventListener(m.sdk.AdEvent.THIRD_QUARTILE, thirdQuartileCallback) m.streamManager.addEventListener(m.sdk.AdEvent.COMPLETE, completeCallback) End Function Function startCallback(ad as Object) as Void print "Callback from SDK -- Start called - " End Function Function firstQuartileCallback(ad as Object) as Void print "Callback from SDK -- First quartile called - " End Function Function midpointCallback(ad as Object) as Void print "Callback from SDK -- Midpoint called - " End Function Function thirdQuartileCallback(ad as Object) as Void print "Callback from SDK -- Third quartile called - " End Function Function completeCallback(ad as Object) as Void print "Callback from SDK -- Complete called - " End Function Function errorCallback(error as Object) as Void print "Callback from SDK -- Error called - " ; error m.errorState = True End Function