SDK Thư viện quyền truy cập có lập trình (PAL) cho Roku cho phép các 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. PAL SDK cho phép bạn yêu cầu số chỉ dùng một lần (là chuỗi được mã hoá) từ Google, vì vậy mà bạn có thể ký yêu cầu DVC. Mỗi yêu cầu luồng 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 quảng cáo các yêu cầu 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:
- 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.
Một thư mục dự án có cấu trúc như 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 tệp dự án của mình.
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ỉ bao bọc một thành phần video để quay
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 một lượt nhấp vào quảng cáo.
- Gửi một 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 tạo các yêu cầu mạng 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 một giao diện xác định dữ liệu mà gửi và nhận thành phầ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 có IMA SDK cho Roku trong ứng dụng của mình
tệp kê khai rồi nhập tệp đó 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 các tương tác và điều kiện kích hoạt của người dùng 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. Thực hiện việc này khi thành phần này đượ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 quay cảnh người dùng nhấn phím để chuyển tiêu điểm đến và từ phần tử video của bạn.
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 một nonceLoader
Giờ đây, bạn có thể bắt đầu xây dựng logic cốt lõi khi triển khai SDK PAL. Trước tiên, hãy khởi chạy SDK qua một luồng riêng.
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ý yêu cầu 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 có được sự đồng ý thích hợp. Chiến lược phát hành đĩa đơn
Phương thức getConsentToStorage()
là phần giữ chỗ cho phương thức lấy dữ liệu của riêng bạn
sự đồng ý của người dùng, bằng cách tích hợp với một CMP hoặc dựa vào các phương pháp 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
khi "khởi động 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 ứng dụng chính thức,
chỉ bắt đầu yêu cầu quảng cáo của mình 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! Bạn hiệ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 trong phiên phát bằng SDK PAL.
(Không bắt buộc) Gửi tín hiệu 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 cho Ad Manager. Dưới đây là ví dụ về một 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ẽ thấy 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 cho phép kiếm tiền hiệu quả hơn thông qua Google Ad Manager.