本指南說明如何將 IMA DAI SDK 整合至影片播放器應用程式。如要查看或按照完整的整合範例操作,請從 GitHub 下載基本範例。
IMA DAI 總覽
導入 IMA DAI 涉及兩個主要 SDK 元件,如本指南所示:
StreamRequest:定義串流要求的物件。串流請求可以是隨選影片或直播。直播請求會指定素材資源金鑰,VOD 請求則會指定 CMS ID 和影片 ID。這兩種要求類型都可以選擇加入 API 金鑰 (用於存取指定串流),以及 Google Ad Manager 聯播網代碼 (用於讓 IMA SDK 按照 Google Ad Manager 設定處理廣告 ID)。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,請按照下列步驟操作:
使用
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。載入串流時停用特技播放,防止使用者在串流開始時,廣告插播時間點開始事件觸發前,略過片頭廣告。
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()子常式,用於建立及要求串流。如要支援廣告 UI (例如 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選取 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