SDK Thư viện quyền truy cập có lập trình (PAL) cho Roku cho phép những nhà xuất bản đã được phê duyệt lệnh gọi VAST trực tiếp (DVC) kiếm tiền từ các ứng dụng Roku dựa trên DVC. SDK PAL cho phép bạn yêu cầu số chỉ dùng một lần (là các chuỗi được mã hoá) từ Google để bạn có thể ký các yêu cầu DVC. Mỗi yêu cầu truyền phát mới phải đi kèm với một số chỉ dùng một lần mới tạo. Tuy nhiên, bạn có thể sử dụng lại cùng một số chỉ dùng một lần cho nhiều yêu cầu quảng cáo trong cùng một luồng.
Hướng dẫn này giải thích ví dụ về cách kết hợp SDK PAL vào ứng dụng Roku, yêu cầu số chỉ dùng một lần và đăng ký lượt hiển thị quảng cáo.
Điều kiện tiên quyết
Trước khi bắt đầu hướng dẫn này, bạn cần làm như sau:
- Môi trường phát triển Roku — hãy xem Hướng dẫn thiết lập môi trường dành cho nhà phát triển Roku để biết thêm thông tin.
Thư mục dự án có cấu trúc sau:
./ 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
Thiết lập dự án
Trước khi tích hợp SDK PAL, bạn cần định cấu hình các tệp dự án.
tệp kê khai
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
Tạo trình phát video mẫu
Thành phần SampleVideoPlayer
chỉ cần gói một thành phần video để ghi lại các thao tác nhấn điều khiển từ xa. Ghi đè onKeyEvent
để sau khi tiêu điểm của điều khiển từ xa được chuyển sang trình phát video/quảng cáo, mọi thao tác nhấn phím khác (lên, xuống, trái, phải, nhấp, v.v.) sẽ được ghi lại và chuyển lên 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>
Tạo giao diện kiểm thử
Triển khai một cảnh có các nút để thực hiện những việc sau:
- Yêu cầu một số chỉ dùng một lần.
- Gửi lượt nhấp vào quảng cáo.
- Gửi sự kiện bắt đầu phát.
- Gửi sự kiện phát xong.
- Chuyển tiêu điểm sang nút 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>
Tạo thành phần giao diện SDK
Để giao tiếp giữa cảnh chính và SDK PAL, bạn cần một thành phần chứa mã không đồng bộ. Điều này là cần thiết vì SDK PAL thực hiện các yêu cầu mạng bên ngoài, điều này không thể xảy ra trên luồng chính trong ứng dụng Roku. Để gửi dữ liệu đến thành phần này, bạn cần có một giao diện xác định dữ liệu mà thành phần gửi và nhận.
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>
Nhập SDK IMA
Để sử dụng thư viện PAL, bạn cần yêu cầu SDK IMA cho Roku trong tệp kê khai ứng dụng và nhập SDK đó vào thành phần PALInterface
.
tệp kê khai
... 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>
Kích hoạt thành phần giao diện từ cảnh
Tiếp theo, hãy thêm mã BrightScript để theo dõi hoạt động tương tác của người dùng và kích hoạt các thay đổi trong thành phần giao diện:
Để nhận đầu ra từ thành phần giao diện, hãy triển khai trình quan sát trường trên các trường giao diện liên kết với các đầu ra đó và đính kèm các trình quan sát đó vào các hàm gọi lại trong thành phần chính. Hãy thực hiện việc này khi thành phần được đăng ký lần đầu.
Để gửi các lượt tương tác của người dùng đến thành phần giao diện, hãy triển khai trình quan sát trường trên các nút mà bạn đã tạo trước đó để kích hoạt các thay đổi trong các trường giao diện liên kết với các lệnh đó.
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>
Thêm phương thức để chuyển tiêu điểm
Tiếp theo, hãy ghi lại các thao tác nhấn phím của người dùng để chuyển tiêu điểm đến và đi từ phần tử video.
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>
Khởi chạy SDK PAL và tạo nonceLoader
Bây giờ, bạn có thể bắt đầu xây dựng logic cốt lõi của quá trình triển khai SDK PAL. Trước tiên, hãy khởi chạy SDK từ một luồng riêng biệt.
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>
Xử lý các yêu cầu về số chỉ dùng một lần
Sau khi tạo nonceLoader
, bạn cần xử lý các yêu cầu về số chỉ dùng một lần bằng cách đính kèm một trình quan sát vào trường requestNonce
. Bằng cách chỉ đính kèm trình quan sát này sau khi nonceLoader
được khởi tạo, bạn có thể đảm bảo rằng các yêu cầu số chỉ dùng một lần được xử lý trong luồng SDK và chỉ có thể thực hiện yêu cầu số chỉ dùng một lần nếu có nonceLoader
hợp lệ.
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>
Thu thập thông tin về sự đồng ý sử dụng bộ nhớ
Giá trị mặc định cho NonceRequest.storageAllowed
là true
, nhưng bạn có thể thay đổi giá trị này sau khi thu thập được sự đồng ý thích hợp. Phương thức getConsentToStorage()
là phần giữ chỗ cho phương thức của riêng bạn để lấy sự đồng ý của người dùng, bằng cách tích hợp với CMP hoặc dựa trên các phương thức khác để xử lý sự đồng ý về việc lưu trữ.
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
Nghe các tín hiệu về vòng đời của người chơi
Để cho phép tính năng tích hợp PAL gửi tín hiệu đúng cách, bạn cần thiết lập một vòng lặp để theo dõi các tín hiệu trong vòng đời của trình phát.
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>
Đăng ký trình nghe cho sendPlaybackStart
, sendPlaybackEnd
, sendAdClick
và sendAdTouch
Tiếp theo, hãy gọi sendPlaybackStart
trên "video player start" (bắt đầu trình phát video). Phương thức này bắt đầu các lệnh gọi không đồng bộ đến máy chủ của Google để thu thập tín hiệu cần thiết cho việc theo dõi và phát hiện IVT. Gọi sendPlaybackEnd
khi quá trình phát kết thúc.
Gọi sendAdClick
để phản hồi lượt nhấp vào quảng cáo. Sau đó, hãy gọi sendAdTouch
cho các sự kiện nhấn hoặc nhấp không phải lượt nhấp của người dùng.
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>
Đính kèm số chỉ dùng một lần vào yêu cầu quảng cáo
Để sử dụng số chỉ dùng một lần mà bạn nhận được từ thư viện PAL trong một ứng dụng chính thức, chỉ bắt đầu các yêu cầu quảng cáo sau khi số chỉ dùng một lần được tạo. Sau đó, hãy thêm số chỉ dùng một lần vào thẻ quảng cáo bằng thông số 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 ...
Vậy là xong! Giờ đây, bạn có một ứng dụng Roku có thể yêu cầu số chỉ dùng một lần PAL và đăng ký các sự kiện phiên phát bằng SDK PAL.
(Không bắt buộc) Gửi tín hiệu của Google Ad Manager thông qua máy chủ quảng cáo của bên thứ ba
Định cấu hình yêu cầu của máy chủ quảng cáo bên thứ ba cho Ad Manager.
Định cấu hình máy chủ quảng cáo của bên thứ ba để đưa số chỉ dùng một lần vào yêu cầu của máy chủ đến Ad Manager. Dưới đây là ví dụ về thẻ quảng cáo được định cấu hình bên trong máy chủ quảng cáo của bên thứ ba:
'https://pubads.serverside.net/gampad/ads?givn=%%custom_key_for_google_nonce%%&...'
Để biết thêm thông tin, hãy xem Hướng dẫn triển khai phía máy chủ của Google Ad Manager.
Ad Manager tìm givn=
để xác định giá trị số chỉ dùng một lần. Máy chủ quảng cáo bên thứ ba cần hỗ trợ một số macro của riêng mình, chẳng hạn như %%custom_key_for_google_nonce%%
, và thay thế bằng tham số truy vấn số chỉ dùng một lần mà bạn đã cung cấp ở bước trước. Bạn có thể xem thêm thông tin về cách thực hiện việc này trong tài liệu của máy chủ quảng cáo bên thứ ba.
Vậy là xong! Bây giờ, bạn sẽ có thông số số chỉ dùng một lần được truyền từ SDK PAL, thông qua các máy chủ trung gian rồi đến Google Ad Manager. Điều này giúp bạn kiếm tiền hiệu quả hơn thông qua Google Ad Manager.