Programmatic Access Library (PAL) SDK untuk Roku memungkinkan penayang dengan persetujuan panggilan VAST langsung (DVC) untuk memonetisasi aplikasi Roku berbasis DVC. PAL SDK memungkinkan Anda meminta nonce, yang merupakan string terenkripsi, dari Google, sehingga Anda dapat menandatangani permintaan DVC. Setiap permintaan streaming baru harus disertai dengan nonce yang baru dibuat. Namun, Anda dapat menggunakan kembali nonce yang sama untuk beberapa permintaan iklan dalam streaming yang sama.
Panduan ini menjelaskan contoh cara menggabungkan PAL SDK ke dalam aplikasi Roku, meminta nonce, dan mendaftarkan tayangan iklan.
Prasyarat
Sebelum memulai panduan ini, Anda perlu melakukan hal berikut:
- Lingkungan pengembangan Roku — lihat panduan penyiapan lingkungan Developer Roku untuk mengetahui informasi selengkapnya.
Folder project dengan struktur berikut:
./ 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
Menyiapkan project
Sebelum mengintegrasikan PAL SDK, Anda perlu mengonfigurasi file project.
manifes
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
source/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
Membuat contoh pemutar video
Komponen SampleVideoPlayer
hanya menggabungkan komponen video untuk merekam
pencet remote control. Ganti onKeyEvent
sehingga setelah fokus remote
ditransfer ke pemutar video/iklan, penekanan tombol lebih lanjut (atas, bawah, kiri,
kanan, klik, dll.), akan diambil dan di-bubble ke PAL.
components/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>
Membuat antarmuka pengujian
Menerapkan tampilan dengan tombol untuk melakukan hal berikut:
- Minta nonce.
- Kirim klik iklan.
- Kirim peristiwa pemutaran dimulai.
- Kirim peristiwa pemutaran berakhir.
- Transfer fokus ke tombol video.
components/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>
Membuat komponen antarmuka SDK
Untuk berkomunikasi antara tampilan utama dan PAL SDK, Anda memerlukan komponen yang berisi kode asinkron. Hal ini diperlukan karena PAL SDK membuat permintaan jaringan eksternal, yang tidak dapat terjadi di thread utama dalam aplikasi Roku. Untuk mengirim data ke komponen ini, Anda memerlukan antarmuka yang menentukan data yang dikirim dan diterima komponen.
components/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>
Mengimpor IMA SDK
Untuk menggunakan library PAL, Anda harus mewajibkan IMA SDK untuk Roku dalam manifes
aplikasi dan mengimpornya ke komponen PALInterface
.
manifes
... splash_color=#000000 splash_min_time=1000 ui_resolutions=hd bs_libs_required=googleima3
components/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>
Memicu komponen antarmuka dari tampilan
Selanjutnya, tambahkan kode BrightScript yang memproses interaksi pengguna dan memicu perubahan pada komponen antarmuka:
Untuk menerima output dari komponen antarmuka, terapkan observer kolom pada kolom antarmuka yang terkait dengan output tersebut, dan lampirkan ke fungsi callback di komponen utama. Lakukan hal ini saat komponen pertama kali didaftarkan.
Untuk mengirim interaksi pengguna ke komponen antarmuka, terapkan pengamat kolom pada tombol yang Anda buat sebelumnya untuk memicu perubahan di kolom antarmuka yang terkait dengan perintah tersebut.
components/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>
Menambahkan metode untuk mentransfer fokus
Selanjutnya, rekam penekanan tombol pengguna untuk mentransfer fokus ke dan dari elemen video Anda.
components/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>
Melakukan inisialisasi PAL SDK dan membuat nonceLoader
Sekarang Anda dapat mulai mem-build logika inti implementasi PAL SDK. Pertama, lakukan inisialisasi SDK dari thread terpisah.
components/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>
Memproses permintaan nonce
Setelah nonceLoader
dibuat, Anda perlu menangani permintaan nonce dengan
melampirkan observer ke kolom requestNonce
. Dengan melampirkan observer ini hanya
setelah nonceLoader
diinisialisasi, Anda dapat memastikan bahwa permintaan nonce
ditangani di thread SDK, dan bahwa permintaan nonce hanya dapat dilakukan jika ada
nonceLoader
yang valid.
components/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>
Mengumpulkan informasi izin penyimpanan
Nilai default untuk
NonceRequest.storageAllowed
adalah true
, tetapi nilai ini dapat diubah setelah Anda mengumpulkan izin yang sesuai. Metode
getConsentToStorage()
adalah placeholder untuk metode Anda sendiri dalam mendapatkan
izin pengguna, baik dengan berintegrasi dengan CMP, atau berdasarkan metode lain untuk
menangani izin penyimpanan.
components/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
Memproses sinyal siklus proses pemutar
Agar integrasi PAL dapat mengirim sinyal dengan benar, Anda perlu menyiapkan loop untuk memproses sinyal siklus proses pemutar.
components/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>
Mendaftarkan pemroses untuk sendPlaybackStart
, sendPlaybackEnd
, sendAdClick
, dan sendAdTouch
Selanjutnya, panggil sendPlaybackStart
di "video player start". Metode ini memulai
panggilan asinkron ke server Google untuk mengumpulkan sinyal yang diperlukan untuk pemantauan
dan deteksi IVT. Panggil sendPlaybackEnd
saat pemutaran berakhir.
Panggil sendAdClick
sebagai respons terhadap klik-tayang iklan. Kemudian, panggil sendAdTouch
untuk
peristiwa sentuh atau klik pengguna non-klik-tayang.
components/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>
Melampirkan nonce ke permintaan iklan
Untuk menggunakan nonce yang Anda terima dari library PAL dalam aplikasi produksi,
mulai permintaan iklan hanya setelah nonce dibuat. Kemudian, tambahkan
nonce ke tag iklan menggunakan parameter u_paln
.
components/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 ...
Selesai. Sekarang Anda memiliki aplikasi Roku yang dapat meminta nonce PAL dan mendaftarkan peristiwa sesi pemutaran dengan PAL SDK.
(Opsional) Mengirim sinyal Google Ad Manager melalui server iklan pihak ketiga
Konfigurasikan permintaan server iklan pihak ketiga untuk Ad Manager.
Konfigurasikan server iklan pihak ketiga Anda untuk menyertakan nonce dalam permintaan server ke Ad Manager. Berikut adalah contoh tag iklan yang dikonfigurasi di dalam server iklan pihak ketiga:
'https://pubads.serverside.net/gampad/ads?givn=%%custom_key_for_google_nonce%%&...'
Untuk mengetahui detail selengkapnya, lihat Panduan penerapan sisi server Google Ad Manager.
Ad Manager mencari givn=
untuk mengidentifikasi nilai nonce. Server iklan pihak ketiga
harus mendukung beberapa makronya sendiri, seperti
%%custom_key_for_google_nonce%%
, dan menggantinya dengan parameter kueri nonce
yang Anda berikan di langkah sebelumnya. Informasi selengkapnya tentang cara melakukannya
akan tersedia di dokumentasi server iklan pihak ketiga.
Selesai. Sekarang Anda harus memiliki parameter nonce yang disebarkan dari PAL SDK, melalui server perantara, lalu ke Google Ad Manager. Hal ini memungkinkan monetisasi yang lebih baik melalui Google Ad Manager.