Select platform: [HTML5](https://developers.google.com/ad-manager/dynamic-ad-insertion/sdk/html5/get-started/pod-serving.md.txt "View this page for the HTML5 platform docs.") [Android](https://developers.google.com/ad-manager/dynamic-ad-insertion/sdk/android/get-started/pod-serving.md.txt "View this page for the Android platform docs.") [iOS](https://developers.google.com/ad-manager/dynamic-ad-insertion/sdk/ios/get-started/pod-serving.md.txt "View this page for the iOS platform docs.") [tvOS](https://developers.google.com/ad-manager/dynamic-ad-insertion/sdk/tvos/get-started/pod-serving.md.txt "View this page for the tvOS platform docs.") [Cast](https://developers.google.com/ad-manager/dynamic-ad-insertion/sdk/cast/get-started/pod-serving.md.txt "View this page for the Cast platform docs.") [Roku](https://developers.google.com/ad-manager/dynamic-ad-insertion/sdk/roku/get-started/pod-serving.md.txt "View this page for the Roku platform docs.")

<br />

This guide demonstrates how to play a DAI Pod Serving live or VOD stream, using
the IMA DAI SDK for Roku. To view or follow a completed sample integration,
download the
[Pod Serving example](https://github.com/googleads/googleads-ima-roku-dai/tree/main/pod_serving_example).


**Note:** The IMA DAI SDK for Roku supports both HLS and DASH streams, with no implementation differences.

<br />


> [!IMPORTANT]
> To use IMA DAI, you must have an [Ad Manager 360 Advanced](https://support.google.com/admanager/answer/3485387) account. If you have an Ad Manager account, contact your account manager for more details. For information about signing up for Ad Manager, visit the [Ad Manager Help Center](https://support.google.com/admanager/answer/7084151).

<br />


**Note:** For information on integrating with other platforms, or on using the IMA client-side SDKs, see [Interactive Media Ads SDKs](https://developers.google.com/interactive-media-ads).

<br />

## IMA DAI Pod Serving overview

Implementing Pod Serving using the IMA DAI involves two main SDK components,
which are demonstrated in this guide:

- [`StreamRequest.createPodLiveStreamRequest()`](https://developers.google.com/ad-manager/dynamic-ad-insertion/sdk/roku/dai/reference/ima.StreamRequest#.createPodLiveStreamRequest) / [`StreamRequest.createPodVodStreamRequest()`](https://developers.google.com/ad-manager/dynamic-ad-insertion/sdk/roku/dai/reference/ima.StreamRequest#.createPodVodStreamRequest): Creates an object that defines a stream request to Google's advertising servers. These requests specify a **Network Code** , and Pod Live `ima.StreamRequest` also requires a **Custom Asset Key** , and an optional **API key**.
- [`StreamManager`](https://developers.google.com/ad-manager/dynamic-ad-insertion/sdk/roku/reference/ima.StreamManager.md.txt): 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](https://support.google.com/admanager/answer/10718356) to retrieve the stream
manifest for your app to display. The exact process can vary video technology
partner (VTP) to VTP.

> [!IMPORTANT]
> **Important:** For the purposes of this guide, assume that the manifest manipulation server expects a GET request with the stream ID passed as a query variable. Use the macro `[[STREAMID]]` as a placeholder in the request URL, and replace it with the stream ID before making the request. Your manifest manipulation server could be different.

## Prerequisites

- Read through our [compatibility page](https://developers.google.com/ad-manager/dynamic-ad-insertion/sdk/roku/compatibility.md.txt) to make sure your intended use case is supported.
- Download our [Roku sample player
  code](https://github.com/googleads/googleads-ima-roku-dai).
- 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](https://github.com/googleads/googleads-ima-roku-dai/tree/main/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
<?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 DAI SDK Framework

To load the framework, add the following to `manifest` and `Sdk.xml`:

**manifest**

    bs_libs_required=googleima3

**Sdk.xml**

```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 DAI SDK

The first step to loading your IMA Dynamic Ad Insertion stream is to load and
initialize the IMA DAI SDK. The following initializes the IMA DAI SDK script.

**Sdk.xml**

```xml
<?xml version="1.0" encoding="utf-8" ?>

  <component name="IMASDKTask" extends="Task">
  <interface>
    \<field id="IMASDKInitialized" 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.IMASDKInitialized
initializeIMASDK()
end if
end sub
sub initializeIMASDK()
if m.sdk = invalid
m.sdk = New_IMASDK()
end if
m.top.IMASDKInitialized = true
end sub
  ]]>
  </script>
  </component>
```

Now start this task in `MainScene.xml` and remove the call to load the content
stream.

**MainScene.xml**

```xml
<?xml version="1.0" encoding="utf-8" ?>

<component extends="Scene" initialFocus="myVideo" name="MainScene">
  <script type="text/brightscript">
  <![CDATA[
  function init()
    m.video = m.top.findNode("myVideo")
    m.video.notificationinterval = 1
    runIMASDKTask()
  end function

  function runIMASDKTask()
m.IMASDKTask = createObject("roSGNode", "IMASDKTask")
m.IMASDKTask.observeField("IMASDKInitialized", "handleIMASDKInitialized")
m.IMASDKTask.observeField("errors", "handleIMASDKErrors")
m.IMASDKTask.control = "RUN"
end function
sub handleIMASDKInitialized()
    ' Follow your manifest manipulator (VTP) documentation to register a user
    ' streaming session if needed.
end sub
sub handleIMASDKErrors(message as object)
print "--- IMA DAI SDK failed ---"
if message \<\> invalid and message.getData() \<\> invalid
print "IMA DAI SDK Error ";message.getData()
end if
end sub
  ]]>
  </script>
  <children>
    <Video height="720" id="myVideo" visible="false" width="1280"/>
  </children>
</component>
```

### Create an IMA stream player

Next, you need to use your existing `roVideoScreen` to create an IMA stream
player.

### Live stream Pod Serving

For live streams, this stream player implements three callback methods:
`streamInitialized`, `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**

```xml
<?xml version="1.0" encoding="utf-8" ?>

  <component name="IMASDKTask" extends="Task">
  <interface>
    <field id="IMASDKInitialized" type="Boolean" />
    <field id="errors" type="stringarray" />
    \<field id="urlData" type="assocarray" /\>
\<field id="adPlaying" type="Boolean" /\>
\<field id="videoNode" type="Node" /\>
  </interface>

  <script type="text/brightscript">
  ...
  sub runThread()
    if not m.top.IMASDKInitialized
      initializeIMASDK()
    end if

    setupPlayerCallbacks()
  end sub
  ...
  sub initializeIMASDK()
if m.ima = invalid
ima = New_IMASDK()
ima.initSdk()
m.ima = ima
end if
m.top.IMASDKInitialized = true
end sub
sub setupPlayerCallbacks()
m.player = m.ima.createPlayer()
m.player.top = m.top
m.player.streamInitialized = function(urlData)
m.top.videoNode.enableTrickPlay = false
m.top.urlData = urlData
end function
m.player.adBreakStarted = function(adBreakInfo)
print "--- Ad break started ---"
m.top.adPlaying = true
m.top.videoNode.enableTrickPlay = false
end function
m.player.adBreakEnded = function(adBreakInfo)
print "--- Ad break ended ---"
m.top.adPlaying = false
m.top.videoNode.enableTrickPlay = true
end function
end sub
  </script>
...
</component>
```

### VOD stream Pod Serving

For VOD streams, this stream player implements four callback methods:
`streamInitialized`, `loadUrl`, `adBreakStarted`, and `adBreakEnded`. In the
`streamInitialized` callback, make sure to call
`StreamManager.loadThirdPartyStream()`. Failure to do so will result in SDK
not triggering the `loadUrl` function.

In this step, you also request a stream URL from your video technology partner
(VTP) with a stream ID obtained in `loadAdPodStream()`. Then, call
`StreamManager.loadThirdPartyStream()` with the ad pod manifest and any
subtitles returned by your VTP.

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**

```xml
<?xml version="1.0" encoding="utf-8" ?>

  <component name="IMASDKTask" extends="Task">
  <interface>
    <field id="IMASDKInitialized" type="Boolean" />
    <field id="errors" type="stringarray" />
    \<field id="adStitchedStreamInfo" type="assocarray" /\>
\<field id="adPlaying" type="Boolean" /\>
\<field id="videoNode" type="Node" /\>
\<field id="streamParameters" type="assocarray" /\>
  </interface>

  <script type="text/brightscript">
  ...
  sub runThread()
    if not m.top.IMASDKInitialized
      initializeIMASDK()
    end if

    setupPlayerCallbacks()
  end sub
  ...
  sub initializeIMASDK()
if m.ima = invalid
ima = New_IMASDK()
ima.initSdk()
m.ima = ima
end if
m.top.IMASDKInitialized = true
end sub
sub loadThirdPartyStream(adStitchedManifest as string, subtitleConfig as dynamic)
m.streamManager.loadThirdPartyStream(adStitchedManifest, subtitleConfig)
end sub
sub setupPlayerCallbacks()
m.player = m.ima.createPlayer()
m.player.top = m.top
m.player.streamInitialized = function(urlData)
adStitchedManifest = m.top.streamParameters.VTPManifest.replace("\[\[STREAMID\]\]", urlData.streamId)
loadThirdPartyStream(adStitchedManifest, m.top.streamParameters.subtitleConfig)
end function
m.player.loadUrl = function(streamInfo)
m.top.adStitchedStreamInfo = streamInfo
end function
m.player.adBreakStarted = function(adBreakInfo)
print "--- Ad break started ---"
m.top.adPlaying = true
m.top.videoNode.enableTrickPlay = false
end function
m.player.adBreakEnded = function(adBreakInfo)
print "--- Ad break ended ---"
m.top.adPlaying = false
m.top.videoNode.enableTrickPlay = true
end function
end sub
  </script>
...
</component>
```

### Create and execute a live or VOD 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`.

### Live stream Pod Serving

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 manifest
manipulation 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.

> [!NOTE]
> **Note:** The stream parameters provided are for a Live pod stream. To test with a VOD pod stream you need to provide your own test stream parameters.

**MainScene.xml**

```vbscript
function init()
  m.video = m.top.findNode("myVideo")
  m.video.notificationinterval = 1
  m.testPodServingStream = {
title: "Test live stream for DAI Pod Serving",
assetKey: "test-live-stream",
networkCode: "your-network-code",
manifest: "https://.../master.m3u8?stream_id=\[\[STREAMID\]\]",
apiKey: ""
}
  runIMASDKTask()
end function

function runIMASDKTask()
  m.IMASDKTask = createObject("roSGNode", "IMASDKTask")

  m.IMASDKTask.streamParameters = m.testPodservingStream
m.IMASDKTask.videoNode = m.video

  m.IMASDKTask.observeField("IMASDKInitialized", "handleIMASDKInitialized")
  m.IMASDKTask.observeField("errors", "handleIMASDKErrors")

  m.IMASDKTask.control = "RUN"
end function
```

**Sdk.xml**

```vbscript
<interface>
  <field id="IMASDKInitialized" type="Boolean" />
  <field id="errors" type="stringarray" />
  <field id="urlData" type="assocarray" />
  <field id="adPlaying" type="Boolean" />
  <field id="videoNode" type="Node" />
  \<field id="streamParameters" type="assocarray" /\>
</interface>

...

sub runThread()
  if not m.top.IMASDKInitialized
    initializeIMASDK()
  end if

  setupPlayerCallbacks()
  loadAdPodStream()
end sub

sub loadAdPodStream()
request = m.ima.CreatePodLiveStreamRequest(m.top.streamParameters.assetKey, m.top.streamParameters.networkCode, m.top.streamParameters.apiKey)
' Set the player object so that the request can trigger the player's
' callbacks at stream initialization or playback events.
request.player = m.player
' Set the video node for the IMA DAI SDK to create ad UI as its child nodes.
request.adUiNode = m.top.video
requestResult = m.ima.requestStream(request)
if requestResult \<\> invalid
print "Error requesting stream ";requestResult
return
end if
m.streamManager = invalid
while m.streamManager = invalid
sleep(50)
m.streamManager = m.ima.getStreamManager()
end while
if m.streamManager = invalid
errors = CreateObject("roArray", 1, True)
invalidStreamManagerError = "Invalid stream manager"
print invalidStreamManagerError
errors.push(invalidStreamManagerError)
m.top.errors = errors
return
end if
if m.streamManager\["type"\] \<\> invalid and m.streamManager\["type"\] = "error"
errors = CreateObject("roArray", 1, True)
print "Stream request returns an error. " ; m.streamManager\["info"\]
errors.push(m.streamManager\["info"\])
m.top.errors = errors
return
end if
setupStreamManager()
m.streamManager.start()
end sub
```

### VOD stream Pod Serving

In the `m.testPodServingStream` object, you'll store the network code used in
the stream request, so Google Ad Manager can provide a stream ID. Also store
the manifest URL used to access the user specific manifest on your manifest
manipulation server.

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.

> [!NOTE]
> **Note:** You will need to provide your own network code and manifest URL to test your VOD stream implementation.

**MainScene.xml**

```vbscript
sub init()
  m.video = m.top.findNode("myVideo")
  m.video.notificationinterval = 1
  m.testPodServingStream = {
title: "Pod Serving VOD Stream",
networkCode: "your-network-code",
VTPManifest: "https://.../manifest.m3u8?gam-stream-id=\[\[STREAMID\]\]",
subtitleConfig: \[\]
}
  runIMASDKTask()
end sub

sub runIMASDKTask()
  m.IMASDKTask = createObject("roSGNode", "IMASDKTask")

  m.IMASDKTask.streamParameters = m.testPodservingStream
m.IMASDKTask.videoNode = m.video

  m.IMASDKTask.observeField("IMASDKInitialized", "handleIMASDKInitialized")
  m.IMASDKTask.observeField("errors", "handleIMASDKErrors")

  m.IMASDKTask.control = "RUN"
end sub
```

**Sdk.xml**

```vbscript
sub runThread()
  if not m.top.IMASDKInitialized
    initializeIMASDK()
  end if

  setupPlayerCallbacks()
  loadAdPodStream()
end sub

sub loadAdPodStream()
request = m.ima.CreatePodVodStreamRequest(m.top.streamParameters.networkCode)
' Set the player object so that the request can trigger the player
' callbacks at stream initialization or playback events.
request.player = m.player
' Set the video node for the IMA DAI SDK to create ad UI as its child nodes.
request.adUiNode = m.top.video
requestResult = m.ima.requestStream(request)
if requestResult \<\> invalid
print "Error requesting stream ";requestResult
return
end if
m.streamManager = invalid
while m.streamManager = invalid
sleep(50)
m.streamManager = m.ima.getStreamManager()
end while
if m.streamManager = invalid
errors = CreateObject("roArray", 1, True)
invalidStreamManagerError = "Invalid stream manager"
print invalidStreamManagerError
errors.push(invalidStreamManagerError)
m.top.errors = errors
return
end if
if m.streamManager\["type"\] \<\> invalid and m.streamManager\["type"\] = "error"
errors = CreateObject("roArray", 1, True)
print "Stream request returns an error. " ; m.streamManager\["info"\]
errors.push(m.streamManager\["info"\])
m.top.errors = errors
return
end if
setupStreamManager()
m.streamManager.start()
end sub
```

### Add event listeners and start the stream

### Live stream Pod Serving

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.
This implementation gets the stream ID in this step, but depending on your VTP
integration, it may be available before this step.

**MainScene.xml**

```vbscript
function runIMASDKTask()
  m.IMASDKTask = createObject("roSGNode", "IMASDKTask")

  m.IMASDKTask.streamParameters = m.testPodservingStream
  m.IMASDKTask.videoNode = m.video

  m.IMASDKTask.observeField("IMASDKInitialized", "handleIMASDKInitialized")
  m.IMASDKTask.observeField("errors", "handleIMASDKErrors")
  m.sdkTask.observeField("adStitchedStreamInfo", "loadAdStitchedStream")

  m.sdkTask.control = "RUN"
end function

sub loadAdStitchedStream(message as object)
print "Ad pod stream information ";message
adPodStreamInfo = message.getData()
manifest = m.testPodservingStream.manifest.Replace("\[\[STREAMID\]\]", adPodStreamInfo.streamId)
playStream(manifest, adPodStreamInfo.format)
end sub
sub playStream(url as string, format as string)
vidContent = createObject("RoSGNode", "ContentNode")
vidContent.url = url
vidContent.title = m.testPodservingStream.title
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**

```vbscript
sub runThread()
  if not m.top.IMASDKInitialized
    initializeIMASDK()
  end if

  setupPlayerCallbacks()
  loadAdPodStream()
  if m.streamManager \<\> invalid
runLoop()
end if
end sub

sub runLoop()
m.top.videoNode.timedMetaDataSelectionKeys = \["\*"\]
' IMPORTANT: Failure to listen to the position and timedmetadata fields
' could result in ad impressions not being reported.
m.port = CreateObject("roMessagePort")
m.top.videoNode.observeField("position", m.port)
m.top.videoNode.observeField("timedMetaData", m.port)
m.top.videoNode.observeField("timedMetaData2", m.port)
m.top.videoNode.observeField("state", m.port)
while True
msg = wait(1000, m.port)
if m.top.videoNode = invalid
print "exiting"
exit while
end if
m.streamManager.onMessage(msg)
currentTime = m.top.videoNode.position
if currentTime \> 3 And not m.top.adPlaying
m.top.videoNode.enableTrickPlay = true
end if
end while
end sub
sub setupStreamManager()
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 sub
sub startCallback(ad as object)
print "Callback from SDK -- Start called - "
end sub
sub firstQuartileCallback(ad as object)
print "Callback from SDK -- First quartile called - "
end sub
sub midpointCallback(ad as object)
print "Callback from SDK -- Midpoint called - "
end sub
sub thirdQuartileCallback(ad as object)
print "Callback from SDK -- Third quartile called - "
end sub
sub completeCallback(ad as object)
print "Callback from SDK -- Complete called - "
end sub
function errorCallback(error as object)
print "Callback from SDK -- Error called - " ; error
m.errorState = True
end function
```

### VOD stream Pod Serving

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.

**MainScene.xml**

```vbscript
sub runIMASDKTask()
  m.IMASDKTask = createObject("roSGNode", "IMASDKTask")

  m.IMASDKTask.streamParameters = m.testPodservingStream
  m.IMASDKTask.videoNode = m.video

  m.IMASDKTask.observeField("IMASDKInitialized", "handleIMASDKInitialized")
  m.IMASDKTask.observeField("errors", "handleIMASDKErrors")
  m.sdkTask.observeField("adStitchedStreamInfo", "loadAdStitchedStream")

  m.sdkTask.control = "RUN"
end sub

sub loadAdStitchedStream(message as object)
print "Ad pod stream information ";message
adPodStreamInfo = message.getData()
end sub
sub playStream(url as string, format as string, subtitleConfig as object)
vidContent = createObject("RoSGNode", "ContentNode")
vidContent.title = m.testPodservingStream.title
vidContent.url = url
vidContent.subtitleConfig = subtitleConfig
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**

```vbscript
sub runThread()
  if not m.top.IMASDKInitialized
    initializeIMASDK()
  end if

  setupPlayerCallbacks()
  loadAdPodStream()
  if m.streamManager \<\> invalid
runLoop()
end if
end sub

sub runLoop()
m.top.videoNode.timedMetaDataSelectionKeys = \["\*"\]
' IMPORTANT: Failure to listen to the position and timedmetadata fields
' could result in ad impressions not being reported.
m.port = CreateObject("roMessagePort")
m.top.videoNode.observeField("position", m.port)
m.top.videoNode.observeField("timedMetaData", m.port)
m.top.videoNode.observeField("timedMetaData2", m.port)
m.top.videoNode.observeField("state", m.port)
while True
msg = wait(1000, m.port)
if m.top.videoNode = invalid
exit while
end if
m.streamManager.onMessage(msg)
currentTime = m.top.videoNode.position
if currentTime \> 3 and not m.top.adPlaying
m.top.videoNode.enableTrickPlay = true
end if
end while
end sub
sub setupStreamManager()
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 sub
sub startCallback(ad as object)
print "Callback from SDK -- Start called - "
end sub
sub firstQuartileCallback(ad as object)
print "Callback from SDK -- First quartile called - "
end sub
sub midpointCallback(ad as object)
print "Callback from SDK -- Midpoint called - "
end sub
sub thirdQuartileCallback(ad as object)
print "Callback from SDK -- Third quartile called - "
end sub
sub completeCallback(ad as object)
print "Callback from SDK -- Complete called - "
end sub
sub errorCallback(error as object)
print "Callback from SDK -- Error called - " ; error
m.errorState = True
end sub
```