Jetzt starten

Mit dem PAL SDK (Programmatic Access Library) für Roku können Publisher, die die Genehmigung für direkte VAST-Aufrufe (Direct VAST Call, DVC) haben, DVC-basierte Roku-Anwendungen monetarisieren. Mit dem PAL SDK kannst du von Google Nonces anfordern, also verschlüsselte Strings, damit du DVC-Anfragen signieren kannst. Jede neue Streamanfrage muss mit einer neu generierten Nonce versehen sein. Sie können denselben Nonce jedoch für mehrere Anzeigenanfragen innerhalb desselben Streams wiederverwenden.

In diesem Leitfaden wird anhand eines Beispiels erläutert, wie du das PAL SDK in eine Roku-Anwendung einbinden, eine Nonce anfordern und Anzeigenimpressionen erfassen kannst.

Vorbereitung

Bevor Sie mit dieser Anleitung beginnen, müssen Sie Folgendes tun:

  • Eine Roku-Entwicklungsumgebung. Weitere Informationen finden Sie im Einrichtungsleitfaden für die Roku-Entwicklungsumgebung.
  • Einen Projektordner mit der folgenden Struktur:

    ./
      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
    

Projekt einrichten

Bevor du das PAL SDK einbindest, musst du deine Projektdateien konfigurieren.

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

Beispiel-Videoplayer erstellen

Die SampleVideoPlayer-Komponente umschließt einfach eine Videokomponente, um Tastendrücke der Fernbedienung zu erfassen. Überschreiben Sie onKeyEvent so, dass alle weiteren Tastendrücke (nach oben, unten, links, rechts, Klick usw.), die ausgeführt werden, nachdem der Fokus der Fernbedienung auf den Video-/Anzeigenplayer übertragen wurde, erfasst und an PAL gesendet werden.

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>

Testoberfläche erstellen

Implementiert eine Szene mit Schaltflächen für folgende Aktionen:

  • Nonce anfordern
  • Senden Sie einen Anzeigenklick.
  • Sende ein Ereignis „Wiedergabe gestartet“.
  • Sende ein Ereignis, dass die Wiedergabe beendet wurde.
  • Verschieben Sie den Fokus auf die Videoschaltfläche.

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>

SDK-Schnittstellenkomponente erstellen

Für die Kommunikation zwischen der Hauptszene und dem PAL SDK ist eine Komponente mit asynchronem Code erforderlich. Das ist erforderlich, da das PAL SDK externe Netzwerkanfragen stellt, die nicht im Hauptthread einer Roku-Anwendung erfolgen können. Um Daten an diese Komponente zu senden, benötigen Sie eine Schnittstelle, die festlegt, welche Daten die Komponente sendet und empfängt.

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>

IMA SDK importieren

Wenn du die PAL-Bibliothek verwenden möchtest, musst du das IMA SDK für Roku in deinem App-Manifest anfordern und in die PALInterface-Komponente importieren.

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>

Benutzeroberflächenkomponente über Szene auslösen

Fügen Sie als Nächstes den BrightScript-Code hinzu, der auf Nutzerinteraktionen achtet und Änderungen an der Benutzeroberflächenkomponente auslöst:

  • Wenn Sie eine Ausgabe von der Benutzeroberflächenkomponente erhalten möchten, implementieren Sie Feldbeobachter für die zugehörigen Benutzeroberflächenfelder und binden Sie sie an Rückruffunktionen in der Hauptkomponente an. Dies geschieht bei der Erstregistrierung der Komponente.

  • Um Nutzerinteraktionen an die Benutzeroberflächenkomponente zu senden, implementieren Sie Feldbeobachter auf den Schaltflächen, die Sie zuvor erstellt haben, um Änderungen in den zugehörigen Benutzeroberflächenfeldern auszulösen.

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>

Methoden zum Übertragen des Fokus hinzufügen

Erfassen Sie als Nächstes die Tastendrücke der Nutzer, um den Fokus auf Ihr Videoelement zu legen und wieder darauf zu verlagern.

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>

PAL SDK initialisieren und einen NonceLoader erstellen

Jetzt können Sie mit dem Erstellen der Kernlogik der PAL SDK-Implementierung beginnen. Initialisieren Sie zuerst das SDK in einem separaten Thread.

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>

Nonce-Anfragen verarbeiten

Nachdem die nonceLoader erstellt wurde, müssen Sie Anfragen für Nonces bearbeiten, indem Sie dem Feld requestNonce einen Beobachter zuweisen. Wenn du diesen Beobachter erst nach der Initialisierung von nonceLoader anfügst, kannst du dafür sorgen, dass Anfragen für Nonces im SDK-Thread verarbeitet werden und dass eine Anfrage für einen Nonce nur gesendet werden kann, wenn ein gültiger nonceLoader vorhanden ist.

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>

Der Standardwert für NonceRequest.storageAllowed ist true. Dieser Wert kann jedoch geändert werden, nachdem Sie die entsprechende Einwilligung eingeholt haben. Die Methode getConsentToStorage() ist ein Platzhalter für Ihre eigene Methode zum Einholen der Nutzereinwilligung, entweder durch Einbindung in eine CMP oder auf Grundlage anderer Methoden zur Verwaltung der Einwilligung zur Speicherung.

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

Signale zum Lebenszyklus von Spielern beachten

Damit deine PAL-Integration Signale richtig senden kann, musst du einen Loop einrichten, der auf die Lebenszyklussignale des Players achtet.

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>

Listener für sendPlaybackStart, sendPlaybackEnd, sendAdClick und sendAdTouch registrieren

Rufen Sie als Nächstes sendPlaybackStart bei „Videoplayer starten“ auf. Bei dieser Methode werden asynchrone Aufrufe an Google-Server gestartet, um das Signal zu erfassen, das für die Überwachung und Erkennung von IVTs erforderlich ist. Rufen Sie sendPlaybackEnd an, wenn die Wiedergabe endet. Rufen Sie sendAdClick auf, wenn auf eine Anzeige geklickt wurde. Rufen Sie dann sendAdTouch für Touch- oder Klickereignisse auf, die nicht zu einem Klick führen.

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>

Nonce an die Anzeigenanfragen anhängen

Wenn Sie den von der PAL-Bibliothek erhaltenen Nonce in einer Produktionsanwendung verwenden möchten, starten Sie Ihre Anzeigenanfragen erst, nachdem der Nonce generiert wurde. Fügen Sie dann mit dem Parameter u_paln die Nonce an das Anzeigen-Tag an.

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
...

Fertig! Du hast jetzt eine Roku-App, die eine PAL-Nonce anfordern und Wiedergabesitzungsereignisse mit dem PAL SDK registrieren kann.

Optional: Google Ad Manager-Signale über Ad-Server von Drittanbietern senden

Konfigurieren Sie die Anfrage des Ad-Servers des Drittanbieters für Ad Manager.

Konfigurieren Sie den Ad-Server des Drittanbieters so, dass die Nonce in die Anfrage des Servers an Ad Manager aufgenommen wird. Hier ein Beispiel für ein Anzeigen-Tag, das im Ad-Server eines Drittanbieters konfiguriert wurde:

'https://pubads.serverside.net/gampad/ads?givn=%%custom_key_for_google_nonce%%&...'

Weitere Informationen finden Sie im Leitfaden zur serverseitigen Implementierung von Google Ad Manager.

In Ad Manager wird nach givn= gesucht, um den Nonce-Wert zu ermitteln. Der Ad-Server des Drittanbieters muss ein eigenes Makro wie %%custom_key_for_google_nonce%% unterstützen und es durch den im vorherigen Schritt angegebenen Abfrageparameter „nonce“ ersetzen. Weitere Informationen dazu finden Sie in der Dokumentation des Ad-Servers des Drittanbieters.

Fertig! Der Nonce-Parameter sollte jetzt vom PAL SDK über Ihre Intermediärserver an Google Ad Manager übertragen werden. So lässt sich die Monetarisierung über Google Ad Manager optimieren.