إعداد حزمة تطوير البرامج لإعلانات الوسائط التفاعلية لعرض الإعلانات أثناء التشغيل

تسهّل حِزم تطوير البرامج (SDK) لإعلانات الوسائط التفاعلية دمج إعلانات الوسائط المتعددة في مواقعك الإلكترونية وتطبيقاتك. يمكن لحِزم تطوير البرامج لإعلانات الوسائط التفاعلية طلب الإعلانات من أي خادم إعلانات متوافق مع VAST وإدارة تشغيل الإعلانات في تطبيقاتك. باستخدام حِزم تطوير البرامج لإدراج الإعلانات الديناميكي في "إعلانات الوسائط التفاعلية"، تقدّم التطبيقات طلب بث لإعلان وفيديو محتوى، سواء كان فيديو عند الطلب أو محتوى مباشرًا. بعد ذلك، تعرض حزمة SDK بث فيديو مدمجًا، ما يغنيك عن إدارة التبديل بين الفيديو الإعلاني وفيديو المحتوى داخل تطبيقك.

اختيار حلّ "الإعلانات الديناميكية أثناء عرض الفيديو" الذي يهمّك

يوضّح هذا الدليل كيفية تشغيل بث مباشر أو بث عند الطلب باستخدام ميزة "عرض الإعلانات ضمن حزمة" في "الإعلانات الديناميكية أثناء عرض الفيديو" من IMA على Roku. لعرض نموذج تكامل مكتمل أو اتّباعه، نزِّل مثال عرض الحزمة.

نظرة عامة حول عرض "حزمة إعلانات" باستخدام ميزة "إدراج الإعلان الديناميكي" في "إعلانات الوسائط التفاعلية"

يتضمّن تنفيذ ميزة "عرض الإعلانات المتسلسلة" باستخدام ميزة "الإعلانات الديناميكية أثناء عرض الفيديو" من "إعلانات الوسائط التفاعلية" مكوّنَين رئيسيَّين من حزمة SDK، كما هو موضّح في هذا الدليل:

  • StreamRequest.createPodLiveStreamRequest() / StreamRequest.createPodVodStreamRequest(): تنشئ هذه السمة عنصرًا يحدّد طلب بث إلى خوادم الإعلانات من Google. تحدّد هذه الطلبات رمز شبكة، ويتطلّب Pod Live ima.StreamRequest أيضًا مفتاح أصول مخصّص ومفتاح واجهة برمجة تطبيقات اختياري.
  • StreamManager: هو عنصر يعالج التواصل بين بث الفيديو و"حزمة تطوير البرامج (SDK) للإعلانات الديناميكية أثناء عرض الفيديو" من "إعلانات الوسائط التفاعلية"، مثل إرسال طلبات ping للتتبّع وإعادة توجيه أحداث البث إلى الناشر.

بالإضافة إلى ذلك، عليك إرسال طلب إلى خادم معالجة ملف البيان لاسترداد ملف بيان البث الذي سيعرضه تطبيقك. قد تختلف العملية المحدّدة من شريك تكنولوجي للفيديو (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) في جميع أنحاء هذا الدليل.

تحميل إطار عمل حزمة تطوير البرامج (SDK) الخاصة بإعلانات البث المباشر من "إعلانات الوسائط التفاعلية"

لتحميل إطار العمل، أضِف ما يلي إلى manifest و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>

إعداد حزمة تطوير البرامج لإدراج إعلان ديناميكي لإعلانات الوسائط التفاعلية

تتمثّل الخطوة الأولى في تحميل بث "إدراج الإعلان الديناميكي" من "إعلانات الوسائط التفاعلية" في تحميل حزمة تطوير البرامج (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.

عرض إعلانات ضمن البث المباشر

بالنسبة إلى أحداث البث المباشر، ينفّذ مشغّل البث هذا ثلاث طرق لمعالجة الردود: 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>

عرض الإعلانات داخل حزمة في بث الفيديو عند الطلب

بالنسبة إلى بث الفيديوهات عند الطلب، ينفّذ مشغّل البث هذا أربع طرق لمعالجة الردود: streamInitialized وloadUrl وadBreakStarted وadBreakEnded. في streamInitialized، احرص على الاتصال بـ 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>

إنشاء طلب بث مباشر أو بث عند الطلب لعرض إعلانات ضمن "حزمة إعلانات" وتنفيذه

بعد الحصول على مشغّل بث، يمكنك إنشاء طلب بث وتنفيذه. يحتوي هذا المثال على بيانات لبث Pod Serving مخزَّنة في m.testPodServingStream.

عرض إعلانات ضمن البث المباشر

في العنصر m.testPodServingStream، خزِّن المَعلمات التي يحتاجها &quot;مدير إعلانات Google&quot; لتحديد البث المعنيّ، مثل رمز الشبكة ومفتاح مادة العرض المخصّص. يجب أيضًا تخزين عنوان URL لملف البيان المستخدَم للوصول إلى خادم معالجة ملف البيان. في هذه الحالة، يجب أن يتضمّن عنوان URL لملف البيان معرّف Google Stream بعد عرض طلب البث.

لكي تتمكّن من إتاحة واجهة مستخدم الإعلان، مثل رموز AdChoices، عليك أيضًا تمرير مرجع إلى العقدة التي تحتوي على فيديو المحتوى كجزء من طلبك.

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

عرض الإعلانات داخل حزمة في بث الفيديو عند الطلب

في العنصر m.testPodServingStream، عليك تخزين رمز الشبكة المستخدَم في طلب البث، كي يتمكّن "مدير إعلانات Google" من تقديم معرّف بث. عليك أيضًا تخزين عنوان URL لملف البيان المستخدَم للوصول إلى ملف البيان الخاص بالمستخدم على خادم معالجة ملف البيان.

لكي تتمكّن من إتاحة واجهة مستخدم الإعلان، مثل رموز adChoices، عليك أيضًا تمرير مرجع إلى العقدة التي تحتوي على فيديو المحتوى كجزء من طلبك.

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

إضافة أدوات معالجة الأحداث وبدء البث

عرض إعلانات ضمن البث المباشر

بعد طلب بثّك، لم يتبقَّ سوى بضع خطوات عليك اتّخاذها، وهي: إضافة أدوات معالجة الأحداث لتتبُّع تقدّم الإعلان وإعادة توجيه رسائل 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

عرض الإعلانات داخل حزمة في بث الفيديو عند الطلب

بعد طلب بثّك، لم يتبقَّ سوى بضع خطوات عليك اتّخاذها، وهي: إضافة أدوات معالجة الأحداث لتتبُّع تقدّم الإعلان وإعادة توجيه رسائل 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