设置全方位服务 DAI

请选择平台: HTML5 Android iOS tvOS Cast Roku

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

IMA DAI 概览

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

  • StreamRequest:定义流请求的对象。视频流请求可以针对视频点播或直播。直播请求会指定素材资源键,而 VOD 请求会指定 CMS ID 和视频 ID。这两种请求类型都可以选择包含访问指定视频流所需的 API 密钥,以及供 IMA SDK 按照 Google Ad Manager 设置中的指定方式处理广告标识符的 Google Ad Manager 广告资源网代码。
  • 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 Advertising Framework

IMA DAI SDK 依赖于 Roku Advertising Framework。如需加载该框架,请将以下代码添加到 manifest 和 Sdk.xml 中:

bs_libs_required=roku_ads_lib,googleima3
Library "Roku_Ads.brs"
Library "IMA3.brs"

加载 IMA DAI SDK

如需加载 IMA DAI SDK,请执行以下操作:

  1. 使用 New_IMASDK() 调用初始化 IMA SDK:

    sub loadSdk()
      If m.sdk = invalid
        m.sdk = New_IMASDK()
      End If
      m.top.sdkLoaded = true
    End Sub
    
  2. 通过创建 sdkLoaded 布尔值字段来跟踪 IMA 是否已加载:

    <field id="sdkLoaded" type="Boolean" />
    
  3. 从主 runThread() 子例程调用 loadSdk() 子例程:

    if not m.top.sdkLoaded
      loadSdk()
    End If
    
  4. 在 MainScene.xml 中创建 loadImaSdk() 函数,以创建并运行 sdkTask 对象:

    function loadImaSdk() as void
      m.sdkTask = createObject("roSGNode", "imasdk")
      m.sdkTask.observeField("sdkLoaded", "onSdkLoaded")
      m.sdkTask.observeField("errors", "onSdkLoadedError")
    
      ' Change to m.testLiveStream to demo live instead of VOD.
      selectedStream = m.testVodStream
      m.videoTitle = selectedStream.title
      m.sdkTask.streamData = selectedStream
    
      m.sdkTask.observeField("urlData", "urlLoadRequested")
      m.sdkTask.video = m.video
      ' Setting control to run starts the task thread.
      m.sdkTask.control = "RUN"
    end function
    
  5. 从 init() 函数调用 loadImaSdk() 函数。

  6. 创建 onSdkLoaded() 和 onSdkLoadedError() 监听器子例程,以响应 SDK 加载事件:

    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 视频流播放器

如需创建 IMA 视频流播放器,请执行以下操作:

  1. 创建一个执行以下操作的 setupVideoPlayer() 子例程:

    1. 使用 createPlayer() 方法创建直播播放器。

    2. 让该直播播放器实现三种回调方法:loadUrl、adBreakStarted 和 adBreakEnded。

    3. 在加载直播时停用特技播放,以防止用户在直播开始时、广告插播时间点开始事件触发之前跳过前贴片广告。

    sub setupVideoPlayer()
      sdk = m.sdk
      m.player = sdk.createPlayer()
      m.player.top = m.top
      m.player.loadUrl = Function(urlData)
        ' This line prevents users from scanning during buffering
        ' or during the first second of the ad before we have a callback from roku.
        ' If there are no prerolls disabling trickplay isn't needed.
        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 Double)
        print "---- SDK requested seek to ----" ; timeSeconds
        m.top.video.seekMode = "accurate"
        m.top.video.seek = timeSeconds
      End Function
    End Sub
    

    添加 seek 回调方法以支持可跳过的广告。如需了解详情,请参阅添加对可跳过广告的支持。

  2. 添加 setupVideoPlayer() 子例程中使用的 urlData、adPlaying 和 video 字段:

    <field id="urlData" type="assocarray" />
    <field id="adPlaying" type="Boolean" />
    <field id="video" type="Node" />
    

创建并执行流式传输请求

如需请求 DAI 视频流,请执行以下操作:

  1. 创建一个 loadStream() 子例程,用于创建和请求数据流。如需支持广告界面(例如 AdChoices 图标),您还必须在请求中传递对包含内容视频的节点的引用:

    Sub loadStream()
      sdk = m.sdk
      sdk.initSdk()
      setupVideoPlayer()
    
      request = {}
      streamData = m.top.streamData
      if streamData.type = "live"
        request = sdk.CreateLiveStreamRequest(streamData.assetKey, streamData.apiKey, streamData.networkCode)
      else if streamData.type = "vod"
        request = sdk.CreateVodStreamRequest(streamData.contentSourceId, streamData.videoId, streamData.apiKey, streamData.networkCode)
      else
        request = sdk.CreateStreamRequest()
      end if
    
      request.player = m.player
      request.adUiNode = 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
    
  2. 添加 loadStream() 子例程中使用的 streamData 和 streamManagerReady 字段:

    <field id="streamManagerReady" type="Boolean" />
    <field id="streamData" type="assocarray" />
    
  3. 如果直播管理器不可用,请从 runThread() 子例程中调用 loadStream() 子例程:

    if not m.top.streamManagerReady
      loadStream()
    End If
    
  4. 选择 VOD 或直播。以下示例包含直播和 VOD 视频流的视频流参数:

    m.testLiveStream = {
      title: "Live Stream",
      assetKey: "c-rArva4ShKVIAkNfy6HUQ",
      networkCode: "21775744923",
      apiKey: "",
      type: "live"
    }
    m.testVodStream = {
      title: "VOD stream"
      contentSourceId: "2548831",
      videoId: "tears-of-steel",
      networkCode: "21775744923",
      apiKey: "",
      type: "vod"
    }
    

    默认情况下,本指南使用 VOD 视频流。您可以通过将 selectedStream 变量从 m.testVodStream 对象更改为 m.testLiveStream 对象来改用直播。

启动数据流

创建 urlLoadRequested() 子例程以监听流数据并调用 playStream() 子例程:

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

  playStream(data.manifest, data.format)
End Sub

创建 playStream() 以开始播放直播:

Sub playStream(url as String, format as String)
  vidContent = createObject("RoSGNode", "ContentNode")
  vidContent.url = url
  vidContent.title = m.videoTitle
  vidContent.streamformat = format
  m.video.content = vidContent
  m.video.setFocus(true)
  m.video.visible = true
  m.video.control = "play"
  m.video.EnableCookies()
End Sub

监听直播元数据

创建 runLoop() 子例程,其中包含一个在直播播放期间运行的 while 循环,并使用 StreamManager.onMessage() 将直播元数据发送到 IMA:

Sub runLoop()
  ' Forward all timed metadata events.
  m.top.video.timedMetaDataSelectionKeys = ["*"]

  ' Cycle through all the fields and just listen to them all.
  m.port = CreateObject("roMessagePort")
  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
    ' Only enable trickplay after a few seconds, in case we start with an ad,
    ' to prevent users from skipping through that ad.
    If currentTime > 3 And not m.top.adPlaying
       m.top.video.enableTrickPlay = true
    End If
  end while
End Sub

监听广告事件

现在,您已将直播元数据传递给 IMA,IMA 可以在广告插播时间点发出广告事件。根据需要创建广告事件监听器,以响应广告事件:

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
  ' errors are critical and should terminate the stream.
  m.errorState = True
End Function

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

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

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

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