תחילת העבודה עם IMA DAI SDK

ערכות IMA SDK מאפשרות לכם לשלב בקלות מודעות מולטימדיה באתרים ובאפליקציות שלכם. ערכות IMA SDK יכולות לבקש מודעות מכל שרת מודעות שתואם ל-VAST ולנהל את הפעלת המודעות באפליקציות שלכם. בעזרת ערכות IMA DAI SDK, אפליקציות בקשה לשידור וידאו של מודעה ותוכן – VOD או תוכן בשידור חי. לאחר מכן ה-SDK מחזיר סטרימינג משולב של מודעות וידאו, כך שלא יהיה צורך לנהל את המעבר בין מודעות וידאו בתוכן בתוך האפליקציה.

איך בוחרים את פתרון DAI שמעניין אתכם

המדריך הזה מדגים איך מפעילים Pod של DAI להצגת מודעות בשידור חי או ב-VOD, באמצעות IMA DAI SDK ל-Roku.

סקירה כללית בנושא הצגת מודעות Pod של IMA DAI

כדי להטמיע הצגת Pod באמצעות IMA DAI יש שני רכיבים עיקריים של SDK: שמודגמים במדריך זה:

  • StreamRequest.createPodLiveStreamRequest() / StreamRequest.createPodVodStreamRequest(): יצירת אובייקט שמגדיר בקשה של מקור נתונים לפרסום של Google שרתים. הבקשות האלה מציינות קוד רשת ו-Pod Live. הפרמטר ima.StreamRequest מחייב גם מפתח נכס מותאם אישית, ואופציונלי API key.
  • StreamManager אובייקט שמטפל בתקשורת בין שידור הווידאו לבין IMA DAI SDK, למשל הפעלת פינגים למעקב והעברת אירועי סטרימינג אל מו"ל.

בנוסף, צריך לשלוח בקשה למניפולציה של המניפסט כדי לאחזר את השידור. מניפסט להצגה באפליקציה. התהליך המדויק עשוי להשתנות בהתאם לטכנולוגיית הווידאו שותף (VTP) ל-VTP.

דרישות מוקדמות

  • מומלץ לקרוא את התאימות שלנו כדי לוודא יש תמיכה בתרחיש לדוגמה המיועד.
  • מורידים את נגן לדוגמה של Roku .
  • פורסים את קוד הנגן לדוגמה במכשיר Roku כדי לוודא הגדרת הפיתוח עובדת.

הפעלת הסרטון

את הסרטון לדוגמה נגן מפעיל סרטון תוכן מחוץ לקופסה. פורסים את הנגן לדוגמה מכשיר Roku כדי לוודא שסביבת הפיתוח מוגדרת כראוי.

הפיכת נגן הווידאו שלך לנגן סטרימינג של IMA DAI

כדי להטמיע נגן סטרימינג, מבצעים את השלבים הבאים.

יצירת Sdk.xml

מוסיפים לפרויקט קובץ חדש בשם MainScene.xml בשם Sdk.xml, וגם מוסיפים את התבנית הבאה:

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>

צריך לערוך את שני הקבצים האלה (MainScene.xml וגם Sdk.xml) לאורך כל הקובץ במדריך הזה.

טעינת המסגרת של IMA DAI SDK

כדי לטעון את ה-framework, צריך להוסיף את הפרטים הבאים ל-manifest ול-Sdk.xml:

מניפסט

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>

אתחול IMA DAI SDK

השלב הראשון לטעינת הזרם 'הטמעת מודעות דינמיות של IMA' הוא טעינה להפעיל את IMA DAI SDK. הקוד הבא מפעיל את הסקריפט IMA DAI SDK.

Sdk.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>

עכשיו צריך להתחיל את המשימה הזו בMainScene.xml ולהסיר את הקריאה לטעינת התוכן .

MainScene.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>

יצירת נגן זרם IMA

בשלב הבא, עליך להשתמש במכשיר roVideoScreen הקיים כדי ליצור שידור IMA נגן.

הצגת Pod של שידורים חיים

בשידורים חיים, הנגן הזה מיישם שלוש שיטות לקריאה חוזרת (callback): streamInitialized, adBreakStarted וגם adBreakEnded.

להשבית את הפעלת הטריקים גם כשהשידור נטען. הפעולה הזו מונעת ממשתמשים דילוג על מודעה לפני סרטון ברגע שהיא מתחילה, לפני שההפסקה למודעות התחילה האירוע מופעל.

Sdk.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>

הצגת Pod של שידורים ב-VOD

בשידורי VOD, נגן הסטרימינג הזה מיישם ארבע שיטות קריאה חוזרת: streamInitialized, loadUrl, adBreakStarted וגם adBreakEnded. ב streamInitialized קריאות חוזרות (callback), חשוב להתקשר StreamManager.loadThirdPartyStream(). אם לא תעשו זאת, ייעשה שימוש ב-SDK לא מפעילה את הפונקציה loadUrl.

בשלב הזה, נבקש ממך גם את כתובת ה-URL של השידור מהשותף הטכנולוגי של הווידאו (VTP) עם מזהה מקור נתונים שהתקבל ב-loadAdPodStream(). לאחר מכן, התקשרו StreamManager.loadThirdPartyStream() במניפסט של רצף המודעות וב כתוביות שהוחזרו על ידי ה-VTP.

להשבית את הפעלת הטריקים גם כשהשידור נטען. הפעולה הזו מונעת ממשתמשים דילוג על מודעה לפני סרטון ברגע שהיא מתחילה, לפני שההפסקה למודעות התחילה האירוע מופעל.

Sdk.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>

יצירה והפעלה של בקשה לשידור חי של רצף מודעות או VOD

אחרי שיש לכם נגן סטרימינג, אתם יכולים ליצור ולהפעיל בקשה לשידור. בדוגמה הזו יש נתונים לגבי מקור נתונים להצגת Pod ששמור ב- m.testPodServingStream

הצגת Pod של שידורים חיים

באובייקט m.testPodServingStream, מאחסנים את הפרמטרים ש-Google Ads המנהל צריך לזהות את מקור הנתונים הרלוונטי, כמו קוד רשת מפתח נכס בהתאמה אישית. צריך לאחסן גם את כתובת ה-URL של המניפסט שמשמשת לגישה למניפסט שרת שינוי ועיבוד. במקרה זה, כתובת ה-URL של המניפסט צריכה לכלול את הפרטים הבאים: מזהה מקור הנתונים מתווסף אחרי החזרת הבקשה לשידור.

כדי שתהיה לכם אפשרות לתמוך ב-AdUI, כמו הסמלים של בחירת המודעות, עליכם להעביר גם הפניה לצומת שמכיל את סרטון התוכן שלכם כחלק מהבקשה.

MainScene.xml

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

<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

הצגת Pod של שידורים ב-VOD

באובייקט m.testPodServingStream, מאחסנים את קוד הרשת שבו נעשה שימוש הבקשה של מקור הנתונים, כדי שמערכת Google Ad Manager תוכל לספק מזהה של מקור נתונים. גם לאחסן כתובת ה-URL של המניפסט שמשמשת לגישה למניפסט הספציפי למשתמש במניפסט שרתמניפולציה.

כדי לתמוך ב-AdUI, למשל עם הסמלים של בחירת המודעות, עליכם להעביר גם הפניה אל צומת שמכיל את סרטון התוכן שלך כחלק מהבקשה.

MainScene.xml

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

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

הוספת פונקציות event listener והפעלת השידור

הצגת Pod של שידורים חיים

לאחר שליחת הבקשה לשידור, נשארו רק כמה דברים לעשות: להוסיף פונקציות event listener לעקוב אחר התקדמות המודעות ולהעביר הודעות Roku אל ה-SDK. חשוב להעביר את כל ההודעות ל-SDK כדי לוודא שהמודעה נכונה להפעלה. אם לא תעשו זאת, תקבלו דיווח בלתי הולם על צפיות במודעות.

בשלב הזה מוסיפים גם פונקציה שתחליף את פקודת המאקרו [[STREAMID]] מזהה הסטרימינג, ומעבירים לנגן הווידאו את כתובת ה-URL המלאה של בקשת המניפסט. ההטמעה הזו מקבלת את מזהה מקור הנתונים בשלב הזה, אבל בהתאם ל-VTP יכול להיות שהוא יהיה זמין לפני השלב הזה.

MainScene.xml

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

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

הצגת Pod של שידורים ב-VOD

לאחר שליחת הבקשה לשידור, נשארו רק כמה דברים לעשות: להוסיף פונקציות event listener לעקוב אחר התקדמות המודעות ולהעביר הודעות Roku אל ה-SDK. הוא חשוב להעביר את כל ההודעות אל ה-SDK כדי להבטיח שהמודעה נכונה להפעלה. אם לא תעשו זאת, תקבלו דיווח שגוי על צפיות במודעות.

MainScene.xml

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

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