借助 IMA SDK,您可以轻松地将多媒体广告集成到网站和应用中。IMA SDK 可以从任何 符合 VAST 标准的广告服务器请求广告,并在您的应用中管理广告播放。借助 IMA DAI SDK,应用可以针对广告和内容视频(视频点播或直播内容)发出视频流请求。然后,SDK 会返回一个组合视频流,这样您就不必在应用中管理广告和内容视频之间的切换。
选择您感兴趣的 DAI 解决方案
全方位服务 DAI
本指南演示了如何将 IMA DAI SDK 集成到简单的视频播放器应用中。如果您想查看或跟随完成的示例集成,请从 GitHub 下载基本示例。
IMA DAI 概览
实现 IMA DAI 涉及两个主要 SDK 组件,如本指南所示:
StreamRequest
: 用于定义流式请求的对象。视频流请求可以是视频点播请求,也可以是直播请求。直播请求会指定素材资源键,而 VOD 请求会指定 CMS ID 和视频 ID。这两种请求类型都可以选择性地包含访问指定视频流所需的 API 密钥,以及 Google Ad Manager 网络代码,以便 IMA SDK 按照 Google Ad Manager 设置中的指定方式处理广告标识符。StreamManager
:用于处理动态广告插播流以及与 DAI 后端互动的对象。流管理器还会处理跟踪 ping,并将流和广告事件转发给发布商。
前提条件
- 请仔细阅读我们的兼容性页面,确保您的预期使用情形受支持。
- 下载我们的 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>
在本指南中,您需要编辑这两个文件。
加载 Roku 广告框架
IMA DAI SDK 依赖于 Roku 广告框架。如需加载框架,请将以下内容添加到 manifest
和 Sdk.xml
:
bs_libs_required=roku_ads_lib,googleima3
Library "Roku_Ads.brs"
Library "IMA3.brs"
加载 IMA DAI SDK
如需加载 IMA DAI SDK,请执行以下操作:
通过
New_IMASDK()
调用初始化 IMA SDK:sub loadSdk() If m.sdk = invalid m.sdk = New_IMASDK() End If m.top.sdkLoaded = true End Sub
通过创建
sdkLoaded
布尔值字段来跟踪 IMA 是否已加载:<field id="sdkLoaded" type="Boolean" />
从主
runThread()
子例程调用loadSdk()
子例程:if not m.top.sdkLoaded loadSdk() End If
在
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
从
init()
函数调用loadImaSdk()
函数。创建
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 流播放器,请执行以下操作:
创建一个执行以下操作的
setupVideoPlayer()
子例程:使用
createPlayer()
方法创建流播放器。让该流播放器实现三个回调方法:
loadUrl
、adBreakStarted
和adBreakEnded
。在加载视频流时停用快进/快退功能,以防止用户在视频流开始播放时(在触发 ad break started 事件之前)跳过前贴片广告。
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
回调方法以支持可跳过的广告。如需了解详情,请参阅添加对可跳过广告的支持。添加
setupVideoPlayer()
子例程中使用的urlData
、adPlaying
和video
字段:<field id="urlData" type="assocarray" /> <field id="adPlaying" type="Boolean" /> <field id="video" type="Node" />
创建并执行流式请求
如需请求 DAI 直播,请执行以下操作:
创建一个
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
添加
loadStream()
子例程中使用的streamData
和streamManagerReady
字段:<field id="streamManagerReady" type="Boolean" /> <field id="streamData" type="assocarray" />
如果流管理器不可用,请从
runThread()
子例程调用loadStream()
子例程:if not m.top.streamManagerReady loadStream() End If
选择视频点播或直播。以下示例包含直播和视频点播流的流参数:
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