במדריך הזה מוסבר איך להפעיל שידור חי או שידור וידאו על פי דרישה (VOD) עם הצגת מודעות ברצף (DAI) באמצעות IMA DAI SDK ל-Roku. כדי לראות או לעקוב אחרי שילוב לדוגמה שהושלם, הורידו את הדוגמה להצגת מודעות Pod.
סקירה כללית על הצגת מודעות ברצף ב-IMA DAI
כדי להטמיע את Pod Serving באמצעות IMA DAI, צריך להשתמש בשני רכיבים עיקריים של ה-SDK, שמוצגים במדריך הזה:
StreamRequest.createPodLiveStreamRequest()/StreamRequest.createPodVodStreamRequest(): יוצר אובייקט שמגדיר בקשת סטרימינג לשרתי הפרסום של Google. בבקשות האלה מצוין קוד רשת, וב-Pod Liveima.StreamRequestנדרש גם מפתח נכס בהתאמה אישית, ואופציונלית מפתח API.-
StreamManager: אובייקט שמטפל בתקשורת בין שידור הווידאו לבין IMA DAI SDK, למשל הפעלת פינגים למעקב והעברת אירועי שידור לבעל האפליקציה.
בנוסף, צריך לשלוח בקשה לשרת לשינוי מניפסט כדי לאחזר את המניפסט של השידור ולהציג אותו באפליקציה. התהליך המדויק עשוי להשתנות בין שותפי טכנולוגיית הווידאו (VTP).
דרישות מוקדמות
- כדאי לקרוא את דף התאימות שלנו כדי לוודא שתרחיש השימוש הרצוי נתמך.
- מורידים את קוד הנגן לדוגמה של 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>
במהלך המדריך הזה תצטרכו לערוך את שני הקבצים האלה (MainScene.xml ו-Sdk.xml).
טעינת ה-framework של IMA DAI SDK
כדי לטעון את המסגרת, מוסיפים את הקוד הבא ל-manifest ול-Sdk.xml:
manifest
bs_libs_required=googleima3
Sdk.xml
<?xml version = "1.0" encoding = "utf-8" ?>
<component name = "imasdk" extends = "Task">
<interface>
</interface>
<script type = "text/brightscript">
<![CDATA[
Library "IMA3.brs"
]]>
</script>
</component>
אתחול IMA DAI SDK
השלב הראשון בטעינת הסטרימינג של IMA Dynamic Ad Insertion הוא לטעון ולהפעיל את IMA DAI SDK. הקוד הבא מאתחל את הסקריפט של IMA DAI SDK.
Sdk.xml
<?xml version="1.0" encoding="utf-8" ?>
<component name="IMASDKTask" extends="Task">
<interface>
<field id="IMASDKInitialized" type="Boolean" />
<field id="errors" type="stringarray" />
</interface>
<script type = "text/brightscript">
<![CDATA[
Library "IMA3.brs"
sub init()
m.top.functionName = "runThread"
end sub
sub runThread()
if not m.top.IMASDKInitialized
initializeIMASDK()
end if
end sub
sub initializeIMASDK()
if m.sdk = invalid
m.sdk = New_IMASDK()
end if
m.top.IMASDKInitialized = true
end sub
]]>
</script>
</component>
עכשיו צריך להתחיל את המשימה הזו ב-MainScene.xml ולהסיר את הקריאה לטעינת ה-content stream.
MainScene.xml
<?xml version="1.0" encoding="utf-8" ?>
<component extends="Scene" initialFocus="myVideo" name="MainScene">
<script type="text/brightscript">
<![CDATA[
function init()
m.video = m.top.findNode("myVideo")
m.video.notificationinterval = 1
runIMASDKTask()
end function
function runIMASDKTask()
m.IMASDKTask = createObject("roSGNode", "IMASDKTask")
m.IMASDKTask.observeField("IMASDKInitialized", "handleIMASDKInitialized")
m.IMASDKTask.observeField("errors", "handleIMASDKErrors")
m.IMASDKTask.control = "RUN"
end function
sub handleIMASDKInitialized()
' Follow your manifest manipulator (VTP) documentation to register a user
' streaming session if needed.
end sub
sub handleIMASDKErrors(message as object)
print "------ IMA DAI SDK failed ------"
if message <> invalid and message.getData() <> invalid
print "IMA DAI SDK Error ";message.getData()
end if
end sub
]]>
</script>
<children>
<Video height="720" id="myVideo" visible="false" width="1280"/>
</children>
</component>
יצירת נגן סטרימינג של IMA
בשלב הבא, צריך להשתמש ב-roVideoScreen הקיים כדי ליצור נגן סטרימינג של IMA.
הצגת מודעות ברצף בשידור חי
בשידורים חיים, נגן השידור הזה מטמיע שלוש שיטות קריאה חוזרת:
streamInitialized, adBreakStarted ו-adBreakEnded.
צריך גם להשבית את האפשרות להרצה מהירה כשהשידור נטען. כך המשתמשים לא יכולים לדלג על מודעה לפני הסרטון ברגע שהיא מתחילה, לפני שהאירוע "התחלת הפסקה למודעה" מופעל.
Sdk.xml
<?xml version="1.0" encoding="utf-8" ?>
<component name="IMASDKTask" extends="Task">
<interface>
<field id="IMASDKInitialized" type="Boolean" />
<field id="errors" type="stringarray" />
<field id="urlData" type="assocarray" />
<field id="adPlaying" type="Boolean" />
<field id="videoNode" type="Node" />
</interface>
<script type="text/brightscript">
...
sub runThread()
if not m.top.IMASDKInitialized
initializeIMASDK()
end if
setupPlayerCallbacks()
end sub
...
sub initializeIMASDK()
if m.ima = invalid
ima = New_IMASDK()
ima.initSdk()
m.ima = ima
end if
m.top.IMASDKInitialized = true
end sub
sub setupPlayerCallbacks()
m.player = m.ima.createPlayer()
m.player.top = m.top
m.player.streamInitialized = function(urlData)
m.top.videoNode.enableTrickPlay = false
m.top.urlData = urlData
end function
m.player.adBreakStarted = function(adBreakInfo)
print "------ Ad break started ------"
m.top.adPlaying = true
m.top.videoNode.enableTrickPlay = false
end function
m.player.adBreakEnded = function(adBreakInfo)
print "------ Ad break ended ------"
m.top.adPlaying = false
m.top.videoNode.enableTrickPlay = true
end function
end sub
</script>
...
</component>
הצגת מודעות ב-Pod בשידור VOD
בשידורי VOD, נגן השידורים הזה מטמיע ארבע שיטות קריאה חוזרת:
streamInitialized, loadUrl, adBreakStarted ו-adBreakEnded. בקריאה החוזרת (callback) של streamInitialized, חשוב להפעיל את StreamManager.loadThirdPartyStream(). אם לא תעשו את זה, ערכת ה-SDK לא תפעיל את הפונקציה loadUrl.
בשלב הזה, אתם גם מבקשים כתובת URL של שידור מהשותף הטכנולוגי שלכם בתחום הווידאו (VTP) עם מזהה שידור שהתקבל ב-loadAdPodStream(). לאחר מכן, קוראים ל-StreamManager.loadThirdPartyStream() עם המניפסט של רצף המודעות וכל הכתוביות שהוחזרו על ידי ה-VTP.
כדאי גם להשבית את האפשרות להריץ קדימה או אחורה כשהשידור נטען. כך המשתמשים לא יכולים לדלג על מודעה לפני הסרטון ברגע שהיא מתחילה, לפני שהאירוע "התחלת הפסקה למודעה" מופעל.
Sdk.xml
<?xml version="1.0" encoding="utf-8" ?>
<component name="IMASDKTask" extends="Task">
<interface>
<field id="IMASDKInitialized" type="Boolean" />
<field id="errors" type="stringarray" />
<field id="adStitchedStreamInfo" type="assocarray" />
<field id="adPlaying" type="Boolean" />
<field id="videoNode" type="Node" />
<field id="streamParameters" type="assocarray" />
</interface>
<script type="text/brightscript">
...
sub runThread()
if not m.top.IMASDKInitialized
initializeIMASDK()
end if
setupPlayerCallbacks()
end sub
...
sub initializeIMASDK()
if m.ima = invalid
ima = New_IMASDK()
ima.initSdk()
m.ima = ima
end if
m.top.IMASDKInitialized = true
end sub
sub loadThirdPartyStream(adStitchedManifest as string, subtitleConfig as dynamic)
m.streamManager.loadThirdPartyStream(adStitchedManifest, subtitleConfig)
end sub
sub setupPlayerCallbacks()
m.player = m.ima.createPlayer()
m.player.top = m.top
m.player.streamInitialized = function(urlData)
adStitchedManifest = m.top.streamParameters.VTPManifest.replace("[[STREAMID]]", urlData.streamId)
loadThirdPartyStream(adStitchedManifest, m.top.streamParameters.subtitleConfig)
end function
m.player.loadUrl = function(streamInfo)
m.top.adStitchedStreamInfo = streamInfo
end function
m.player.adBreakStarted = function(adBreakInfo)
print "------ Ad break started ------"
m.top.adPlaying = true
m.top.videoNode.enableTrickPlay = false
end function
m.player.adBreakEnded = function(adBreakInfo)
print "------ Ad break ended ------"
m.top.adPlaying = false
m.top.videoNode.enableTrickPlay = true
end function
end sub
</script>
...
</component>
יצירה והפעלה של בקשה להצגת מודעות ב-Pod בשידור חי או ב-VOD
אחרי שיוצרים נגן סטרימינג, אפשר ליצור ולהפעיל בקשת סטרימינג.
בדוגמה הזו יש נתונים של שידור Pod Serving שמאוחסנים ב-m.testPodServingStream.
הצגת מודעות ברצף בשידור חי
באובייקט m.testPodServingStream, מאחסנים את הפרמטרים שנדרשים ל-Google Ad Manager כדי לזהות את השידור הרלוונטי, כמו קוד רשת ומפתח נכס בהתאמה אישית. צריך גם לשמור את כתובת ה-URL של המניפסט שמשמשת לגישה לשרת לשינוי המניפסט. במקרה כזה, צריך להוסיף לכתובת ה-URL של המניפסט את מזהה השידור של Google אחרי שהבקשה לשידור מוחזרת.
כדי לתמוך בממשק המשתמש של המודעות, כמו סמלי AdChoices, צריך גם להעביר הפניה לצומת שמכיל את סרטון התוכן כחלק מהבקשה.
MainScene.xml
function init()
m.video = m.top.findNode("myVideo")
m.video.notificationinterval = 1
m.testPodServingStream = {
title: "Test live stream for DAI Pod Serving",
assetKey: "test-live-stream",
networkCode: "your-network-code",
manifest: "https://.../master.m3u8?stream_id=[[STREAMID]]",
apiKey: ""
}
runIMASDKTask()
end function
function runIMASDKTask()
m.IMASDKTask = createObject("roSGNode", "IMASDKTask")
m.IMASDKTask.streamParameters = m.testPodservingStream
m.IMASDKTask.videoNode = m.video
m.IMASDKTask.observeField("IMASDKInitialized", "handleIMASDKInitialized")
m.IMASDKTask.observeField("errors", "handleIMASDKErrors")
m.IMASDKTask.control = "RUN"
end function
Sdk.xml
<interface>
<field id="IMASDKInitialized" type="Boolean" />
<field id="errors" type="stringarray" />
<field id="urlData" type="assocarray" />
<field id="adPlaying" type="Boolean" />
<field id="videoNode" type="Node" />
<field id="streamParameters" type="assocarray" />
</interface>
...
sub runThread()
if not m.top.IMASDKInitialized
initializeIMASDK()
end if
setupPlayerCallbacks()
loadAdPodStream()
end sub
sub loadAdPodStream()
request = m.ima.CreatePodLiveStreamRequest(m.top.streamParameters.assetKey, m.top.streamParameters.networkCode, m.top.streamParameters.apiKey)
' Set the player object so that the request can trigger the player's
' callbacks at stream initialization or playback events.
request.player = m.player
' Set the video node for the IMA DAI SDK to create ad UI as its child nodes.
request.adUiNode = m.top.video
requestResult = m.ima.requestStream(request)
if requestResult <> invalid
print "Error requesting stream ";requestResult
return
end if
m.streamManager = invalid
while m.streamManager = invalid
sleep(50)
m.streamManager = m.ima.getStreamManager()
end while
if m.streamManager = invalid
errors = CreateObject("roArray", 1, True)
invalidStreamManagerError = "Invalid stream manager"
print invalidStreamManagerError
errors.push(invalidStreamManagerError)
m.top.errors = errors
return
end if
if m.streamManager["type"] <> invalid and m.streamManager["type"] = "error"
errors = CreateObject("roArray", 1, True)
print "Stream request returns an error. " ; m.streamManager["info"]
errors.push(m.streamManager["info"])
m.top.errors = errors
return
end if
setupStreamManager()
m.streamManager.start()
end sub
הצגת מודעות ב-Pod בשידור VOD
באובייקט m.testPodServingStream, מאחסנים את קוד הרשת שמשמש בבקשה לשידור, כדי ש-Google Ad Manager יוכל לספק מזהה שידור. בנוסף, צריך לאחסן בשרת לשינוי המניפסט את כתובת ה-URL של המניפסט שמשמשת לגישה למניפסט הספציפי למשתמש.
כדי לתמוך בממשק משתמש של מודעות, כמו סמלי AdChoices, צריך גם להעביר הפניה לצומת שמכיל את סרטון התוכן כחלק מהבקשה.
MainScene.xml
sub init()
m.video = m.top.findNode("myVideo")
m.video.notificationinterval = 1
m.testPodServingStream = {
title: "Pod Serving VOD Stream",
networkCode: "your-network-code",
VTPManifest: "https://.../manifest.m3u8?gam-stream-id=[[STREAMID]]",
subtitleConfig: []
}
runIMASDKTask()
end sub
sub runIMASDKTask()
m.IMASDKTask = createObject("roSGNode", "IMASDKTask")
m.IMASDKTask.streamParameters = m.testPodservingStream
m.IMASDKTask.videoNode = m.video
m.IMASDKTask.observeField("IMASDKInitialized", "handleIMASDKInitialized")
m.IMASDKTask.observeField("errors", "handleIMASDKErrors")
m.IMASDKTask.control = "RUN"
end sub
Sdk.xml
sub runThread()
if not m.top.IMASDKInitialized
initializeIMASDK()
end if
setupPlayerCallbacks()
loadAdPodStream()
end sub
sub loadAdPodStream()
request = m.ima.CreatePodVodStreamRequest(m.top.streamParameters.networkCode)
' Set the player object so that the request can trigger the player
' callbacks at stream initialization or playback events.
request.player = m.player
' Set the video node for the IMA DAI SDK to create ad UI as its child nodes.
request.adUiNode = m.top.video
requestResult = m.ima.requestStream(request)
if requestResult <> invalid
print "Error requesting stream ";requestResult
return
end if
m.streamManager = invalid
while m.streamManager = invalid
sleep(50)
m.streamManager = m.ima.getStreamManager()
end while
if m.streamManager = invalid
errors = CreateObject("roArray", 1, True)
invalidStreamManagerError = "Invalid stream manager"
print invalidStreamManagerError
errors.push(invalidStreamManagerError)
m.top.errors = errors
return
end if
if m.streamManager["type"] <> invalid and m.streamManager["type"] = "error"
errors = CreateObject("roArray", 1, True)
print "Stream request returns an error. " ; m.streamManager["info"]
errors.push(m.streamManager["info"])
m.top.errors = errors
return
end if
setupStreamManager()
m.streamManager.start()
end sub
הוספת רכיבי event listener והתחלת השידור
הצגת מודעות ברצף בשידור חי
אחרי ששולחים בקשה לשידור, נשאר רק לבצע כמה פעולות: להוסיף רכיבי event listener כדי לעקוב אחרי התקדמות המודעה ולהעביר הודעות של Roku ל-SDK. חשוב להעביר את כל ההודעות ל-SDK כדי להבטיח שהמודעות יופעלו בצורה תקינה. אם לא תעשו את זה, הצפיות במודעות לא ידווחו בצורה נכונה.
בשלב הזה, מוסיפים גם פונקציה שתחליף את המאקרו [[STREAMID]] במזהה הסטרימינג ותעביר את כתובת ה-URL המלאה של בקשת המניפסט לנגן הווידאו.
בהטמעה הזו, מזהה הסטרימינג מתקבל בשלב הזה, אבל בהתאם לשילוב של VTP, יכול להיות שהוא יהיה זמין לפני השלב הזה.
MainScene.xml
function runIMASDKTask()
m.IMASDKTask = createObject("roSGNode", "IMASDKTask")
m.IMASDKTask.streamParameters = m.testPodservingStream
m.IMASDKTask.videoNode = m.video
m.IMASDKTask.observeField("IMASDKInitialized", "handleIMASDKInitialized")
m.IMASDKTask.observeField("errors", "handleIMASDKErrors")
m.sdkTask.observeField("adStitchedStreamInfo", "loadAdStitchedStream")
m.sdkTask.control = "RUN"
end function
sub loadAdStitchedStream(message as object)
print "Ad pod stream information ";message
adPodStreamInfo = message.getData()
manifest = m.testPodservingStream.manifest.Replace("[[STREAMID]]", adPodStreamInfo.streamId)
playStream(manifest, adPodStreamInfo.format)
end sub
sub playStream(url as string, format as string)
vidContent = createObject("RoSGNode", "ContentNode")
vidContent.url = url
vidContent.title = m.testPodservingStream.title
vidContent.streamformat = format
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.IMASDKInitialized
initializeIMASDK()
end if
setupPlayerCallbacks()
loadAdPodStream()
if m.streamManager <> invalid
runLoop()
end if
end sub
sub runLoop()
m.top.videoNode.timedMetaDataSelectionKeys = ["*"]
' IMPORTANT: Failure to listen to the position and timedmetadata fields
' could result in ad impressions not being reported.
m.port = CreateObject("roMessagePort")
m.top.videoNode.observeField("position", m.port)
m.top.videoNode.observeField("timedMetaData", m.port)
m.top.videoNode.observeField("timedMetaData2", m.port)
m.top.videoNode.observeField("state", m.port)
while True
msg = wait(1000, m.port)
if m.top.videoNode = invalid
print "exiting"
exit while
end if
m.streamManager.onMessage(msg)
currentTime = m.top.videoNode.position
if currentTime > 3 And not m.top.adPlaying
m.top.videoNode.enableTrickPlay = true
end if
end while
end sub
sub setupStreamManager()
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 sub
sub startCallback(ad as object)
print "Callback from SDK -- Start called - "
end sub
sub firstQuartileCallback(ad as object)
print "Callback from SDK -- First quartile called - "
end sub
sub midpointCallback(ad as object)
print "Callback from SDK -- Midpoint called - "
end sub
sub thirdQuartileCallback(ad as object)
print "Callback from SDK -- Third quartile called - "
end sub
sub completeCallback(ad as object)
print "Callback from SDK -- Complete called - "
end sub
function errorCallback(error as object)
print "Callback from SDK -- Error called - " ; error
m.errorState = True
end function
הצגת מודעות ב-Pod בשידור VOD
אחרי ששולחים בקשה לשידור, נשאר רק לעשות כמה דברים: להוסיף רכיבי event listener כדי לעקוב אחרי התקדמות המודעה ולהעביר הודעות של Roku ל-SDK. חשוב להעביר את כל ההודעות ל-SDK כדי להבטיח שהמודעות יופעלו בצורה תקינה. אם לא תעשו את זה, הצפיות במודעות לא ידווחו בצורה תקינה.
MainScene.xml
sub runIMASDKTask()
m.IMASDKTask = createObject("roSGNode", "IMASDKTask")
m.IMASDKTask.streamParameters = m.testPodservingStream
m.IMASDKTask.videoNode = m.video
m.IMASDKTask.observeField("IMASDKInitialized", "handleIMASDKInitialized")
m.IMASDKTask.observeField("errors", "handleIMASDKErrors")
m.sdkTask.observeField("adStitchedStreamInfo", "loadAdStitchedStream")
m.sdkTask.control = "RUN"
end sub
sub loadAdStitchedStream(message as object)
print "Ad pod stream information ";message
adPodStreamInfo = message.getData()
end sub
sub playStream(url as string, format as string, subtitleConfig as object)
vidContent = createObject("RoSGNode", "ContentNode")
vidContent.title = m.testPodservingStream.title
vidContent.url = url
vidContent.subtitleConfig = subtitleConfig
vidContent.streamformat = format
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.IMASDKInitialized
initializeIMASDK()
end if
setupPlayerCallbacks()
loadAdPodStream()
if m.streamManager <> invalid
runLoop()
end if
end sub
sub runLoop()
m.top.videoNode.timedMetaDataSelectionKeys = ["*"]
' IMPORTANT: Failure to listen to the position and timedmetadata fields
' could result in ad impressions not being reported.
m.port = CreateObject("roMessagePort")
m.top.videoNode.observeField("position", m.port)
m.top.videoNode.observeField("timedMetaData", m.port)
m.top.videoNode.observeField("timedMetaData2", m.port)
m.top.videoNode.observeField("state", m.port)
while True
msg = wait(1000, m.port)
if m.top.videoNode = invalid
exit while
end if
m.streamManager.onMessage(msg)
currentTime = m.top.videoNode.position
if currentTime > 3 and not m.top.adPlaying
m.top.videoNode.enableTrickPlay = true
end if
end while
end sub
sub setupStreamManager()
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 sub
sub startCallback(ad as object)
print "Callback from SDK -- Start called - "
end sub
sub firstQuartileCallback(ad as object)
print "Callback from SDK -- First quartile called - "
end sub
sub midpointCallback(ad as object)
print "Callback from SDK -- Midpoint called - "
end sub
sub thirdQuartileCallback(ad as object)
print "Callback from SDK -- Third quartile called - "
end sub
sub completeCallback(ad as object)
print "Callback from SDK -- Complete called - "
end sub
sub errorCallback(error as object)
print "Callback from SDK -- Error called - " ; error
m.errorState = True
end sub