Le SDK PAL (bibliothèque d'accès programmatique) pour Roku permet aux éditeurs disposant d'une approbation de l'appel VAST direct (DVC) de monétiser les applications Roku basées sur le DVC. Le SDK PAL vous permet de demander à Google des nonces, qui sont des chaînes chiffrées, afin de pouvoir signer les requêtes DVC. Chaque nouvelle requête de flux doit être accompagnée d'un nouveau nonce. Toutefois, vous pouvez réutiliser le même nonce pour plusieurs requêtes d'annonces dans le même flux.
Ce guide explique comment intégrer le SDK PAL dans une application Roku, demander un nonce et enregistrer des impressions d'annonces.
Prérequis
Avant de commencer ce guide, vous devez :
- Un environnement de développement Roku : consultez le guide de configuration de l'environnement de développement Roku pour en savoir plus.
Un dossier de projet dont la structure est la suivante :
./ 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
Configurer votre projet
Avant d'intégrer le SDK PAL, vous devez configurer les fichiers de votre projet.
fichier manifeste
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
Créer un exemple de lecteur vidéo
Le composant SampleVideoPlayer
encapsule simplement un composant vidéo pour capturer les pressions sur la télécommande. Ignorez onKeyEvent
de sorte qu'une fois le focus de la télécommande transféré vers la vidéo ou le lecteur d'annonces, toute pression supplémentaire sur les touches (haut, bas, gauche, droite, clic, etc.) est capturée et affichée dans la 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>
Créer une interface de test
Implémente une scène avec des boutons pour effectuer les actions suivantes :
- Demandez un nonce.
- Envoyer un clic sur une annonce
- Envoyez un événement de début de lecture.
- Envoyer un événement de lecture
- Déplacez le focus sur le bouton de la vidéo.
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>
Créer un composant d'interface de SDK
Pour communiquer entre la scène principale et le SDK PAL, vous avez besoin d'un composant contenant du code asynchrone. Cela est nécessaire, car le SDK PAL effectue des requêtes réseau externes, ce qui ne peut pas se produire sur le thread principal d'une application Roku. Pour envoyer des données à ce composant, vous avez besoin d'une interface qui définit les données que le composant envoie et reçoit.
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>
Importer le SDK IMA
Pour utiliser la bibliothèque PAL, vous devez exiger le SDK IMA pour Roku dans le fichier manifeste de votre application et l'importer dans le composant PALInterface
.
fichier manifeste
... 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>
Déclencher le composant d'interface à partir d'une scène
Ajoutez ensuite le code BrightScript qui écoute les interactions utilisateur et déclenche des modifications dans le composant de l'interface:
Pour recevoir des résultats du composant d'interface, implémentez des observateurs de champ sur les champs d'interface associés à ces sorties, puis associez-les aux fonctions de rappel du composant principal. Effectuez cette opération lorsque le composant est enregistré pour la première fois.
Pour envoyer des interactions utilisateur au composant d'interface, implémentez des observateurs de champ sur les boutons que vous avez créés précédemment afin de déclencher des modifications dans les champs d'interface associés à ces commandes.
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>
Ajouter des méthodes pour transférer la sélection
Ensuite, enregistrez les pressions sur les touches de l'utilisateur pour transférer la sélection vers et depuis votre élément vidéo.
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>
Initialiser le SDK PAL et créer un nonceLoader
Vous pouvez maintenant commencer à développer la logique de base de l'implémentation du SDK PAL. Commencez par initialiser le SDK à partir d'un thread distinct.
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>
Traiter les requêtes nonce
Une fois le nonceLoader
créé, vous devez gérer les requêtes de nonce en associant un observateur au champ requestNonce
. En associant cet observateur uniquement après l'initialisation de nonceLoader
, vous pouvez vous assurer que les requêtes nonce sont traitées dans le thread du SDK et qu'une requête nonce ne peut être effectuée que s'il existe un nonceLoader
valide.
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>
Recueillir des informations sur l'autorisation du stockage
La valeur par défaut de NonceRequest.storageAllowed
est true
, mais vous pouvez la modifier après avoir obtenu le consentement approprié. La méthode getConsentToStorage()
est un espace réservé pour votre propre méthode d'obtention du consentement de l'utilisateur, soit en l'intégrant à une PGC, soit en fonction d'autres méthodes pour gérer le consentement au stockage.
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
Écouter les signaux du cycle de vie des joueurs
Pour permettre à votre intégration PAL d'envoyer correctement des signaux, vous devez configurer une boucle pour écouter les signaux de cycle de vie de votre lecteur.
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>
Enregistrer des écouteurs pour sendPlaybackStart
, sendPlaybackEnd
, sendAdClick
et sendAdTouch
Appelez ensuite sendPlaybackStart
sur "démarrage du lecteur vidéo". Cette méthode lance des appels asynchrones aux serveurs Google afin de collecter le signal nécessaire à la surveillance et à la détection de l'IVT. Appelez sendPlaybackEnd
à la fin de la lecture.
Appelez sendAdClick
en réponse au clic sur l'annonce. Appelez ensuite sendAdTouch
pour les événements tactiles ou de clic utilisateur sans clic.
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>
Associer le nonce aux demandes d'annonces
Pour utiliser le nonce que vous recevez de la bibliothèque PAL dans une application de production, démarrez vos demandes d'annonces uniquement après que le nonce a été généré. Ajoutez ensuite le nonce à la balise d'annonce à l'aide du paramètre 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 ...
Et voilà ! Vous disposez désormais d'une application Roku pouvant demander un nonce PAL et enregistrer des événements de session de lecture avec le SDK PAL.
(Facultatif) Envoyer des signaux Google Ad Manager via des ad servers tiers
Configurez la demande de l'ad server tiers pour Ad Manager.
Configurez votre ad server tiers pour inclure le nonce dans la requête du serveur adressée à Ad Manager. Voici un exemple de tag d'emplacement publicitaire configuré au sein de l'ad server tiers:
'https://pubads.serverside.net/gampad/ads?givn=%%custom_key_for_google_nonce%%&...'
Pour en savoir plus, consultez le guide d'implémentation côté serveur de Google Ad Manager.
Ad Manager recherche givn=
pour identifier la valeur nonce. Le serveur publicitaire tiers doit prendre en charge une macro qui lui est propre, telle que %%custom_key_for_google_nonce%%
, et la remplacer par le paramètre de requête nonce que vous avez fourni à l'étape précédente. Pour en savoir plus, consultez la documentation de l'ad server tiers.
Et voilà ! Le paramètre nonce doit maintenant être propagé à partir du SDK PAL, via vos serveurs intermédiaires, puis vers Google Ad Manager. Cela permet une meilleure monétisation via Google Ad Manager.