Get started with the IMA DAI SDK

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.

Select the DAI solution you're interested in

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 need to make a request to your manifest manipulation server to retrieve the stream manifest for your app to display. The exact process can vary from server to server.

Prerequisites

  • Read through our compatibility page to make sure your intended use case is supported.
  • Download our Roku sample player code.
  • Deploy the 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 DAI stream player

Follow these steps to implement a 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.

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

After 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, store the parameters that Google Ad Manager needs to identify the stream in question, such as a network code and custom asset key. Also store the manifest URL used to access your video stitching server. In this case, the manifest URL needs 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 returns a stream ID along
  ' with important information for the IMA DAI SDK.
  request = sdk.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's important that you forward all messages to the SDK to ensure correct ad playback. Failure to do so results in ad views being 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