SDK Thư viện truy cập có lập trình (PAL) dành cho Roku cho phép các nhà xuất bản được phê duyệt trực tiếp lệnh gọi VAST (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 đã mã hoá) từ Google, để bạn có thể ký các 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 được 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 một ví dụ về cách kết hợp SDK PAL vào ứng dụng Roku, yêu cầu một số chỉ dùng một lần và đăng ký số 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 thực hiện những việc sau:
- Môi trường phát triển Roku — 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 các tệp dự án.
manifest
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 một trình phát video mẫu
Thành phần SampleVideoPlayer
chỉ cần gói 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 tiếp theo (lên, xuống, trái, phải, nhấp, v.v.) đều được chụp và tạo bong bóng lên đế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 một sự kiện đã kết thúc phát.
- Chuyển tiêu điểm vào 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 có 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 đưa ra các yêu cầu về mạng bên ngoài, những yêu cầ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 có SDK IMA cho Roku trong tệp kê khai ứng dụng và nhập vào thành phần PALInterface
.
manifest
... 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 giúp 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 chúng 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 này được đăng ký lần đầu.
Để gửi 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 chụp các thao tác nhấn phím của người dùng để chuyển tiêu điểm giữa các 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 một nonceLoader
Bây giờ, bạn có thể bắt đầu xây dựng logic cốt lõi của việc triển khai SDK PAL. Trước tiên, hãy khởi chạy SDK từ 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 số chỉ dùng một lần bằng cách đính kèm đối tượng tiếp nhận dữ liệu 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 chạy, bạn có thể đảm bảo rằng các yêu cầu số chỉ dùng một lần sẽ đượ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 ý của người dùng trong bộ nhớ
Giá trị mặc định của
NonceRequest.storageAllowed
là true
, nhưng bạn có thể thay đổi giá trị này sau khi thu thập sự đồng ý thích hợp. Phương thức getConsentToStorage()
là một phần giữ chỗ cho phương thức của riêng bạn để thu thập 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 trên các phương thức khác để xử lý sự đồng ý của người dùng về bộ nhớ.
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
Lắng nghe tín hiệu vòng đời của người chơi
Để cho phép việc tích hợp PAL gửi tín hiệu đúng cách, bạn cần thiết lập vòng lặp để lắng nghe các tín hiệu trong vòng đời của người chơi.
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 "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 hoạt động giám sát 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 cho nhấp chuột vào quảng cáo. Sau đó, hãy gọi sendAdTouch
cho các sự kiện nhấp hoặc chạm không phải 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 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 cách sử dụng tham 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 một số chỉ dùng một lần PAL và đăng ký các sự kiện phiên phát lại 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 đối với Ad Manager.
Định cấu hình máy chủ quảng cáo bên thứ ba để đưa số chỉ dùng một lần vào yêu cầu của máy chủ đối với 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 bên thứ ba:
'https://pubads.serverside.net/gampad/ads?givn=%%custom_key_for_google_nonce%%&...'
Để biết thêm thông tin chi tiết, 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 nên 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ẽ truyền tham số số chỉ dùng một lần từ SDK PAL, thông qua các máy chủ trung gian của bạn rồi tới 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.