开始使用

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

本指南演示了如何将 IMA SDK 集成到一个简单的视频播放器应用中。如需查看或遵循已完成的示例集成,请从 GitHub 下载基本示例

IMA DAI 概览

实现 IMA DAI 涉及两个主要 SDK 组件,本指南对此进行了演示:

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

前提条件

在开始之前,您需要执行以下操作:

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

播放视频

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

将视频播放器转换为 IMA 动态广告插播播放器

创建 Sdk.xml

连同名为 Sdk.xmlMainScene.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 SDK 依赖于 Roku 广告框架。如需加载框架,请将以下代码添加到 manifestSdk.xml

清单
bs_libs_required=roku_ads_lib,googleima3
Sdk.xml
Library "Roku_Ads.brs"
Library "IMA3.brs"

加载 IMA SDK

若要加载 IMA 动态广告插播流,第一步是加载并初始化 IMA SDK。以下命令可加载 IMA 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
  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
End Sub

创建和执行流请求

现在您已经有了流播放器,可以创建和执行流请求了。 此示例包含直播和 VOD 视频流的数据。目前使用的是 VOD 视频流。 如需使用直播而不是 VOD,请将 selectedStreamm.testVodStream 更改为 m.testLiveStream

为了能够支持 AdUI(如“广告选择”图标),您还必须在请求中传递对包含内容视频的节点的引用。

MainScene.xml
function init()
  m.video = m.top.findNode("myVideo")
  m.video.notificationinterval = 1
  m.testLiveStream = {
    title: "Live Stream",
    assetKey: "sN_IYUG8STe1ZzhIIE_ksA",
    apiKey: "",
    type: "live"
  }
  m.testVodStream = {
    title: "VOD stream"
    contentSourceId: "2528370",
    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 = {}
  if m.top.streamData.type = "live"
    request = sdk.CreateLiveStreamRequest(m.top.streamData.assetKey, m.top.streamData.apiKey)
  else if m.top.streamData.type = "vod"
    request = sdk.CreateVodStreamRequest(m.top.streamData.contentSourceId, m.top.streamData.videoId, m.top.streamData.apiKey)
  else
    request = sdk.CreateStreamRequest()
  end if

  request.player = m.player
  request.adUiNode = m.top.video
  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

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

请求视频流后,只需执行以下操作即可:添加事件监听器,以跟踪广告进度、启动视频流以及将 Roku 消息转发到 SDK。请务必将所有消息转发到 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