SDK библиотеки программного доступа (PAL) для Roku позволяет издателям, получившим разрешение на прямой вызов VAST (DVC), монетизировать приложения Roku на основе DVC. PAL SDK позволяет вам запрашивать одноразовые номера, которые представляют собой зашифрованные строки, у Google, чтобы вы могли подписывать запросы DVC. Каждый новый запрос потока должен сопровождаться вновь сгенерированным одноразовым номером. Однако вы можете повторно использовать один и тот же одноразовый номер для нескольких запросов объявлений в одном потоке.
В этом руководстве объясняется пример того, как включить PAL SDK в приложение Roku, запросить одноразовый номер и зарегистрировать показы рекламы.
Предварительные условия
Прежде чем приступить к работе с этим руководством, вам необходимо выполнить следующие действия:
- Среда разработки Roku — дополнительную информацию см. в руководстве по настройке среды разработчика Roku .
Папка проекта со следующей структурой:
./ components/ MainScene.xml PALInterface.xml SampleVideoPlayer.xml images/ icon_focus_hd.png icon_focus_sd.png icon_side_hd.png icon_side_sd.png splash_fhd.png splash_hd.png splash_sd.png source/ main.brs manifest
Настройте свой проект
Прежде чем интегрировать PAL SDK, вам необходимо настроить файлы проекта.
манифестировать
title=PAL for Roku Sample
subtitle=As seen in the PAL for Roku Get Started Guide
major_version=1
minor_version=0
build_version=00001
mm_icon_focus_hd=pkg:/images/icon_focus_hd.png
mm_icon_side_hd=pkg:/images/icon_side_hd.png
mm_icon_focus_sd=pkg:/images/icon_focus_sd.png
mm_icon_side_sd=pkg:/images/icon_side_sd.png
splash_screen_sd=pkg:/images/splash_sd.jpg
splash_screen_hd=pkg:/images/splash_hd.jpg
splash_screen_fhd=pkg:/images/splash_fhd.jpg
splash_color=#000000
splash_min_time=1000
ui_resolutions=hd
источник/main.brs
sub Main()
showChannelSGScreen()
end sub
sub showChannelSGScreen()
screen = CreateObject("roSGScreen")
m.port = CreateObject("roMessagePort")
screen.setMessagePort(m.port)
m.scene = screen.CreateScene("MainScene")
screen.show()
while(true)
msg = wait(0, m.port)
msgType = type(msg)
if msgType = "roSGScreenEvent"
if msg.isScreenClosed() then return
end if
end while
end sub
Создайте образец видеоплеера
Компонент SampleVideoPlayer
просто оборачивает видеокомпонент для захвата нажатий с пульта дистанционного управления. Переопределите onKeyEvent
, чтобы после перемещения фокуса пульта на проигрыватель видео/рекламы любые дальнейшие нажатия клавиш (вверх, вниз, влево, вправо, щелчок и т. д.) фиксировались и переносились в PAL.
компоненты/SampleVideoPlayer.xml
<?xml version="1.0" encoding="utf-8" ?>
<component name="SampleVideoPlayer" extends="Video">
<interface>
<field id="pressedKey" type="String" />
</interface>
<script type="text/brightscript">
<![CDATA[
Function onKeyEvent(key as String, press as Boolean) as Boolean
If press
m.top.pressedKey = key
End If
return True
End Function
]]>
</script>
<children>
<Label text="VIDEO" color="0xFFFFFFFF" font="font:MediumBoldSystemFont" horizAlign="center" vertAlign="center" width="720" height="480" />
</children>
</component>
Создайте тестовый интерфейс
Реализует сцену с кнопками, позволяющими выполнять следующие действия:
- Запросите одноразовый номер.
- Отправьте клик по объявлению.
- Отправьте событие начала воспроизведения.
- Отправьте событие завершения воспроизведения.
- Перенесите фокус на кнопку видео.
компоненты/MainScene.xml
<?xml version="1.0" encoding="utf-8" ?>
<component name="MainScene" extends="Scene" initialFocus="requestNonceButton">
<children>
<ButtonGroup>
<button text="Request Nonce" id="requestNonceButton" />
<button text="Send Ad Click" id="sendAdClickButton" />
<button text="Send Playback Start" id="sendPlaybackStartButton" />
<button text="Send Playback End" id="sendPlaybackEndButton" />
<button text="Transfer Focus to Video" id="transferFocusToVideoButton" />
</ButtonGroup>
<SampleVideoPlayer id="YourVideoPlayer" width="720" height="480" focusable="true" />
</children>
</component>
Создайте компонент интерфейса SDK.
Для связи между основной сценой и PAL SDK вам понадобится компонент, содержащий асинхронный код. Это необходимо, поскольку PAL SDK выполняет внешние сетевые запросы, которые не могут выполняться в основном потоке приложения Roku. Чтобы отправить данные этому компоненту, вам нужен интерфейс, который определяет, какие данные отправляет и получает компонент.
компоненты/PALInterface.xml
<?xml version="1.0" encoding="utf-8" ?>
<component name="PALInterface" extends="Task">
<interface>
<!--Commands-->
<field id="requestNonce" type="Boolean" />
<field id="sendAdClick" type="Boolean" />
<field id="sendAdTouchKey" type="String" />
<field id="sendPlaybackStart" type="Boolean" />
<field id="sendPlaybackEnd" type="Boolean" />
<field id="endThread" type="Boolean" />
<!--Responses-->
<field id="errors" type="stringarray" />
<field id="nonce" type="String" />
</interface>
</component>
Импортируйте IMA SDK
Чтобы использовать библиотеку PAL, вам необходимо включить IMA SDK для Roku в манифест вашего приложения и импортировать его в компонент PALInterface
.
манифестировать
... splash_color=#000000 splash_min_time=1000 ui_resolutions=hd bs_libs_required=googleima3
компоненты/PALInterface.xml
<?xml version = "1.0" encoding = "utf-8" ?> <component name="PALInterface" extends="Task"> <interface> <!-- commands --> <field id="requestNonce" type="Boolean" /> <field id="sendAdClick" type="Boolean" /> <field id="sendAdTouchKey" type="String" /> <field id="sendPlaybackStart" type="Boolean" /> <field id="sendPlaybackEnd" type="Boolean" /> <field id="endThread" type="Boolean" /> <!-- responses --> <field id="errors" type="stringarray" /> <field id="nonce" type="String" /> </interface> <script type = "text/brightscript"> <![CDATA[ Library "IMA3.brs" ]]> </script> </component>
Запустить компонент интерфейса из сцены
Затем добавьте код BrightScript, который прослушивает действия пользователя и запускает изменения в компоненте интерфейса:
Чтобы получать выходные данные от компонента интерфейса, реализуйте наблюдатели полей в полях интерфейса, связанных с этими выходными данными, и прикрепите их к функциям обратного вызова в основном компоненте. Сделайте это при первой регистрации компонента.
Чтобы отправлять действия пользователя в компонент интерфейса, реализуйте наблюдателей полей на кнопках, которые вы создали ранее, чтобы инициировать изменения в полях интерфейса, связанных с этими командами.
компоненты/MainScene.xml
<?xml version="1.0" encoding="utf-8" ?> <component name="MainScene" extends="Scene" initialFocus="requestNonceButton"> <children> <ButtonGroup> <button text="Request Nonce" id="requestNonceButton" /> <button text="Send Ad Click" id="sendAdClickButton" /> <button text="Send Ad Touch" id="sendAdTouchButton" /> <button text="Send Playback Start" id="sendPlaybackStartButton" /> <button text="Send Playback End" id="sendPlaybackEndButton" /> </ButtonGroup> <Video id="YourVideoPlayer" width="720" height="480" focusable="true" /> </children> <script type="text/brightscript"> <![CDATA[ Function init() requestNonceButton = m.top.findNode("requestNonceButton") requestNonceButton.observeField("buttonSelected", "requestNonce") sendAdClickButton = m.top.findNode("sendAdClickButton") sendAdClickButton.observeField("buttonSelected", "sendAdClick") sendPlaybackStart = m.top.findNode("sendPlaybackStartButton") sendPlaybackStart.observeField("buttonSelected", "sendPlaybackStart") sendPlaybackEnd = m.top.findNode("sendPlaybackEndButton") sendPlaybackEnd.observeField("buttonSelected", "sendPlaybackEnd") loadImaSdk() End Function ' Initialize SDK Interface component and attach callbacks to field observers. Function loadImaSdk() as Void m.sdkTask = createObject("roSGNode", "PALInterface") m.sdkTask.observeField("errors", "onSdkLoadedError") m.sdkTask.observeField("nonce", "onNonceLoaded") print "Running load IMA task." m.sdkTask.control = "RUN" End Function Sub onSdkLoadedError(message as Object) print "----- errors in the sdk loading process --- ";message.getData() End Sub ' Callback triggered when Nonce is loaded. Sub onNonceLoaded(message as Object) nonce = m.sdkTask.nonce print "onNonceLoaded ";nonce End Sub Function requestNonceButtonPressed() As Void print "Request Nonce" ' Inform the SDK interface component to request a nonce. m.sdkTask.requestNonce = True End Function ' Action triggered on player start, either from user action or autoplay. Function sendPlaybackStart() As Void m.sdkTask.sendPlaybackStart = True End Function ' Action triggered on player end, either when content ends or the user exits ' playback of this content. Function sendPlaybackEnd() As Void m.sdkTask.sendPlaybackEnd = True End Function ]]> </script> </component>
Добавьте методы для передачи фокуса
Затем запишите нажатия пользовательских клавиш, чтобы перенести фокус на видеоэлемент и обратно.
компоненты/MainScene.xml
... <script type="text/brightscript"> <![CDATA[ Function init() ... m.sendPlaybackStart = m.top.findNode("sendPlaybackStartButton") m.sendPlaybackStart.observeField("buttonSelected", "sendPlaybackStart") m.sendPlaybackEnd = m.top.findNode("sendPlaybackEndButton") m.sendPlaybackEnd.observeField("buttonSelected", "sendPlaybackEnd") m.transferFocusToVideoButton = m.top.findNode("transferFocusToVideoButton") m.transferFocusToVideoButton.observeField("buttonSelected", "transferFocusToVideo") ' Your video player set up to handle key press events. m.video = m.top.findNode("YourVideoPlayer") m.video.observeField("pressedKey", "onVideoKeyPress") loadImaSdk() End Function ... ' Action triggered on player end, either when content ends or the user exits ' playback of this content. Function sendPlaybackEnd() As Void m.sdkTask.sendPlaybackEnd = True End Function Function transferFocusToVideo() As Void m.video.setFocus(true) End Function Function onVideoKeyPress() As Void key = m.video.pressedKey If key = "" Return End If m.sdkTask.sendAdTouchKey = key ' If back or up is pressed, transfer focus back up to the buttons. If key = "back" or key = "up" m.transferFocusToVideoButton.setFocus(true) End If ' Reset so that we get the next key press, even if it's a repeat of the last ' key. m.video.pressedKey = "" End Function ]]> </script> </component>
Инициализируйте PAL SDK и создайте nonceLoader.
Теперь вы можете приступить к созданию базовой логики реализации PAL SDK. Сначала инициализируйте SDK из отдельного потока.
компоненты/PALInterface.xml
... <script type = "text/brightscript"> <![CDATA[ Library "IMA3.brs" Sub init() ' It is not possible to access roUrlTransfer on the main thread. Setting ' functionName to a function and then setting control to "RUN" causes that 'function to run on a separate thread. m.top.functionName = "runPalThread" ' Loads the SDK on the current thread if it is not yet loaded. ' This blocks execution of other functions on this thread until the SDK is loaded. If m.sdk = Invalid m.sdk = new_imaSdk() End If m.nonceLoader = m.sdk.CreateNonceLoader() End Sub ' Starts the player event loop. This loop only terminates when "endThread" is sent. Function runPalThread() as Void ' Used for the player life cycle loop. m.top.endThread = False port = CreateObject("roMessagePort") End Function ]]> </script> </component>
Обработка запросов nonce
После создания nonceLoader
вам необходимо обрабатывать запросы nonce, присоединяя наблюдателя к полю requestNonce
. Прикрепив этот наблюдатель только после инициализации nonceLoader
, вы можете гарантировать, что запросы nonce обрабатываются в потоке SDK и что запрос nonce может быть выполнен только при наличии допустимого nonceLoader
.
компоненты/PALInterface.xml
... ' Starts the player event loop. This loop only terminates when "endThread" is sent. Function runPalThread() as Void ' Used for the player life cycle loop. m.top.endThread = False port = CreateObject("roMessagePort") ' Now that the nonceLoader exists, begin listening for nonce requests. m.top.observeField("requestNonce", m.port) End Function ' Requests a nonce from the PAL SDK. Function requestNonce() as Void nonceRequest = m.sdk.CreateNonceRequest() m.nonceManager = m.nonceLoader.loadNonceManager(nonceRequest) m.top.nonce = nonceManager.getNonce() End Function ]]> </script> </component>
Сбор информации о согласии на хранение
Значением по умолчанию для NonceRequest.storageAllowed
является true
, но это значение можно изменить после получения соответствующего согласия. Метод getConsentToStorage()
— это заполнитель для вашего собственного метода получения согласия пользователя либо путем интеграции с CMP, либо на основе других методов обработки согласия на хранение.
компоненты/PALInterface.xml
... <script type = "text/brightscript"> <![CDATA[ Library "IMA3.brs" Sub init() ' It is not possible to access roUrlTransfer on the main thread. Setting ' functionName to a function and then setting control to "RUN" causes that 'function to run on a separate thread. m.top.functionName = "runPalThread" ' Loads the SDK on the current thread if it is not yet loaded. ' This blocks execution of other functions on this thread until the SDK is loaded. If m.sdk = Invalid m.sdk = new_imaSdk() End If m.isConsentToStorage = getConsentToStorage() m.nonceLoader = m.sdk.CreateNonceLoader() End Sub ... ' Requests a nonce from the PAL SDK. Function requestNonce() as Void nonceRequest = m.sdk.CreateNonceRequest() ' Include changes to storage consent here. nonceRequest.storageAllowed = m.isConsentToStorage m.nonceManager = m.nonceLoader.loadNonceManager(nonceRequest) m.top.nonce = nonceManager.getNonce() End Function
Слушайте сигналы жизненного цикла игрока
Чтобы ваша интеграция с PAL могла правильно отправлять сигналы, вам необходимо настроить цикл для прослушивания сигналов жизненного цикла вашего плеера.
компоненты/PALInterface.xml
... ' Now that the nonceLoader exists, begin listening for nonce requests. m.top.observeField("requestNonce", m.port) m.top.observeField("sendAdClick", m.port) m.top.observeField("sendAdTouchKey", m.port) m.top.observeField("sendPlaybackStart", m.port) m.top.observeField("sendPlaybackEnd", m.port) ' Setting endThread to true causes the while loop to exit. m.top.observeField("endThread", m.port) While Not m.top.endThread message = m.port.waitMessage(1000) If message = Invalid pollManager() Else If message.getField() = "requestNonce" And m.top.requestNonce = True requestNonce() m.top.requestNonce = False Else If message.getField() = "sendAdClick" And m.top.sendAdClick = True sendAdClick() m.top.sendAdClick = False Else If message.getField() = "sendAdTouchKey" And m.top.sendAdTouchKey <> "" sendAdTouch(m.top.sendAdTouchKey) m.top.sendAdTouchKey = "" Else If message.getField() = "sendPlaybackStart" And m.top.sendPlaybackStart = True sendPlaybackStart() m.top.sendPlaybackStart = False Else If message.getField() = "sendPlaybackEnd" And m.top.sendPlaybackEnd = True sendPlaybackEnd() m.top.sendPlaybackEnd = False End If End While End Function Function pollManager() as Void If m.nonceManager <> Invalid m.nonceManager.poll() End If End Function ' Requests a nonce from the PAL SDK. Function requestNonce() as Void nonceRequest = m.sdk.CreateNonceRequest() m.nonceManager = m.nonceLoader.loadNonceManager(nonceRequest) m.top.nonce = nonceManager.getNonce() End Function ]]> </script> </component>
Зарегистрируйте прослушиватели для sendPlaybackStart
, sendPlaybackEnd
, sendAdClick
и sendAdTouch
Затем вызовите sendPlaybackStart
при запуске видеоплеера. Этот метод запускает асинхронные вызовы серверов Google для сбора сигнала, необходимого для мониторинга и обнаружения IVT. Вызовите sendPlaybackEnd
, когда воспроизведение закончится. Вызов sendAdClick
в ответ на переход по объявлению. Затем вызовите sendAdTouch
для событий касания или щелчка пользователя, не связанных с кликом.
компоненты/PALInterface.xml
... ' Requests a nonce from the IMA SDK. Function requestNonce() as Void nonceRequest = m.sdk.CreateNonceRequest() m.nonceManager = m.nonceLoader.loadNonceManager(nonceRequest) m.top.nonce = nonceManager.getNonce() End Function ' Registers an ad click using the IMA SDK. Function sendAdClick() as Void If m.nonceManager <> Invalid m.nonceManager.sendAdClick() End If End Function ' Registers an ad touch event using the IMA SDK. Function sendAdTouch(touch as String) as Void If m.nonceManager <> Invalid m.nonceManager.sendAdTouch(touch) End If End Function ' Registers the start of playback using the IMA SDK. Function sendPlaybackStart() as Void If m.nonceManager <> Invalid m.nonceManager.sendPlaybackStart() End If End Function ' Registers the end of playback using the IMA SDK. Function sendPlaybackEnd() as Void If m.nonceManager <> Invalid m.nonceManager.sendPlaybackEnd() End If End Function ]]> </script> </component>
Прикрепите nonce к запросам объявлений
Чтобы использовать одноразовый номер, полученный из библиотеки PAL, в рабочем приложении, инициируйте запросы объявлений только после того, как этот одноразовый номер будет сгенерирован. Затем добавьте nonce к тегу объявления, используя параметр u_paln
.
компоненты/MainScene.xml
... ' Callback triggered when Nonce is loaded. Sub onNonceLoaded(message as Object) nonce = m.sdkTask.nonce print "onNonceLoaded ";nonce makeAdRequest(nonce) End Sub Sub makeAdRequest(nonce) ' Sample ad tag URL used in this sample. Your apps method of getting this ' URL will likely be different. adTag = "https://pubads.g.doubleclick.net/gampad/ads?iu=/124319096/external/single_ad_samples" preparedTag = adTag + "&u_paln=" + nonce ' Implement custom ad request logic here. Print "ad tag with nonce ";preparedTag End Sub ...
Вот и все! Теперь у вас есть приложение Roku, которое может запрашивать одноразовый номер PAL и регистрировать события сеанса воспроизведения с помощью PAL SDK.
(Необязательно) Отправляйте сигналы Google Ad Manager через сторонние рекламные серверы.
Настройте запрос стороннего рекламного сервера к Менеджеру рекламы.
Настройте свой сторонний рекламный сервер так, чтобы он включал nonce в запрос сервера к Менеджеру рекламы. Вот пример рекламного тега, настроенного на стороннем рекламном сервере:
'https://pubads.serverside.net/gampad/ads?givn=%%custom_key_for_google_nonce%%&...'
Дополнительную информацию см. в руководстве по внедрению на стороне сервера Google Ad Manager .
Менеджер рекламы ищет givn=
чтобы определить значение nonce. Сторонний сервер объявлений должен поддерживать какой-либо собственный макрос, например %%custom_key_for_google_nonce%%
, и заменять его параметром запроса nonce, который вы указали на предыдущем шаге. Дополнительную информацию о том, как это сделать, можно найти в документации стороннего рекламного сервера.
Вот и все! Теперь параметр nonce должен быть передан из PAL SDK через промежуточные серверы, а затем в Google Ad Manager. Это обеспечивает лучшую монетизацию через Google Ad Manager.