开始使用 IMA DAI SDK

IMA SDK 可让您轻松地将多媒体广告集成到您的网站和应用中。IMA SDK 可从任何 符合 VAST 标准的广告服务器请求广告,并管理您应用中的广告播放。使用 IMA DAI SDK 时,应用会针对广告和内容视频(VOD 或直播内容)发出视频流请求。然后,SDK 会返回组合的视频流,因此您无需在应用中管理广告与内容视频之间的切换。

选择您感兴趣的 DAI 解决方案

全方位服务 DAI

本指南演示了如何将 IMA DAI SDK 集成到简单的视频播放器应用中。如果您想要查看已完成的示例集成或了解已完成的示例集成,请从 GitHub 下载基本示例

IMA DAI 概览

实现 IMA DAI 涉及两个主要 SDK 组件,如本指南中所示:

  • StreamRequest:用于定义流请求的对象。视频流请求可以针对视频点播或直播。请求会指定内容 ID、API 密钥或身份验证令牌和其他参数。
  • StreamManager:用于处理动态广告插播数据流以及与 DAI 后端的互动的对象。视频流管理器还处理跟踪 ping,并将视频流和广告事件转发给发布商。

前提条件

  • 请仔细阅读我们的兼容性页面,确保您的预期用例受支持。
  • 下载我们的 Roku 示例播放器代码
  • 将示例播放器代码部署到 Roku 设备,以验证开发设置是否正常运行。

播放视频

提供的示例视频播放器可直接播放内容视频。将示例播放器部署到 Roku 播放器,以确保正确设置您的开发环境。

将视频播放器转换为 IMA DAI 流媒体播放器

请按照以下步骤实现流式播放器。

创建 Sdk.xml

向您的项目添加一个名为 Sdk.xml 的新文件(与 MainScene.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>

在本指南中,您需要修改这两个文件。

加载 Roku 广告框架

IMA DAI SDK 依赖于 Roku 广告框架。如需加载框架,请将以下代码添加到 manifestSdk.xml

清单

bs_libs_required=roku_ads_lib,googleima3

Sdk.xml

Library "Roku_Ads.brs"
Library "IMA3.brs"

加载 IMA DAI SDK

加载 IMA 动态广告插播视频流的第一步是加载并初始化 IMA DAI SDK。以下代码会加载 IMA DAI SDK 脚本。

Sdk.xml

<interface>
  <field id="sdkLoaded" type="Boolean" />
  <field id="errors" type="stringarray" />
</interface>
...
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

现在,在 MainScene.xml 中启动此任务,并移除加载内容流的调用。

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

创建 IMA 流媒体播放器

接下来,您需要使用现有的 roVideoScreen 来创建 IMA 流媒体播放器。该流播放器实现了三种回调方法:loadUrladBreakStartedadBreakEnded。此外,在加载视频流时,请停用特技播放功能。这样可以防止用户在前贴片广告开始播放的那一刻(广告插播时间点已启动事件触发之前)跳过。

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
End Sub

创建和执行数据流请求

现在,您已经有了流式播放器,您可以创建和执行流请求。此示例包含一场直播和一次 VOD 直播的数据。它使用 VOD 视频流,但您也可以通过将 selectedStreamm.testVodStream 更改为 m.testLiveStream 来改用直播功能。

为了能够支持 AdUI,例如 adChoices 图标,您还必须在请求中传递对包含您的内容视频的节点的引用。

MainScene.xml

function init()
  m.video = m.top.findNode("myVideo")
  m.video.notificationinterval = 1
  m.testLiveStream = {
    title: "Livestream",
    assetKey: "sN_IYUG8STe1ZzhIIE_ksA",
    apiKey: "",
    type: "live"
  }
  m.testVodStream = {
    title: "VOD stream"
    contentSourceId: "2548831",
    videoId: "tears-of-steel",
    apiKey: "",
    type: "vod"
  }
  loadImaSdk()
end function

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.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
  sdk.initSdk()
  setupVideoPlayer()

  request = {}
  streamData = m.top.streamData
  if streamData.type = "live"
    request = sdk.CreateLiveStreamRequest(streamData.assetKey, streamData.apiKey)
  else if streamData.type = "vod"
    request = sdk.CreateVodStreamRequest(streamData.contentSourceId, streamData.videoId, streamData.apiKey)
  else
    request = sdk.CreateStreamRequest()
  end if

  request.player = m.player
  request.videoObject = m.top.video
  ' Required to support UI elements for 'Why This Ad?' and skippability
  request.adUiNode = m.top.video

  requestResult = sdk.requestStream(request)
  If requestResult &lt;&gt; 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"] &lt;&gt; 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

添加事件监听器并启动数据流

请求视频流后,只需进行几项操作:添加事件监听器以跟踪广告进度、启动视频流,以及将 Roku 消息转发到 SDK。

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", "urlLoadRequested")
  m.sdkTask.video = m.video
  m.sdkTask.control = "RUN"
end function

Sub urlLoadRequested(message as Object)
  print "Url Load Requested ";message
  data = message.getData()

  playStream(data.manifest)
End Sub

Sub playStream(url as Object)
  vidContent = createObject("RoSGNode", "ContentNode")
  vidContent.url = url
  vidContent.title = m.videoTitle
  vidContent.streamformat = "hls"
  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

添加对可跳过式广告的支持(可选)

为了支持可跳过的广告,您需要向 IMA DAI SDK 的 player 对象添加 seek 方法,以便以编程方式将视频定位到指定的位置(以浮点秒为单位)。

如需支持可跳过的广告,您还必须确保在请求中设置 adUiNode

Sdk.xml

  m.player.loadUrl = Function(urlData)
    m.top.video.enableTrickPlay = false
    m.top.urlData = urlData
  End Function

  m.player.seek = Function(timeSeconds as Float)
     print "---- SDK requested seek to ----" ; timeSeconds
     m.top.video.seekMode = "accurate"
     m.top.video.seek = timeSeconds
  End Function

  m.player.adBreakStarted = Function(adBreakInfo as Object)
    print "---- Ad Break Started ---- "
    m.top.adPlaying = True
    m.top.video.enableTrickPlay = false
  End Function