Başlarken

Roku için Programatik Erişim Kitaplığı (PAL) SDK'sı, doğrudan VAST çağrısı (DVC) onayı olan yayıncıların DVC tabanlı Roku uygulamalarından para kazanmasını sağlar. PAL SDK'sı, DVC isteklerini imzalayabilmeniz için Google'dan şifrelenmiş dize olan tek seferlik kimlikler isteyebilirsiniz. Her yeni akış isteği, yeni oluşturulmuş bir tek seferlik sayı ile birlikte gönderilmelidir. Ancak aynı tek seferlik rastgele sayıyı aynı akıştaki birden fazla reklam isteği için yeniden kullanabilirsiniz.

Bu kılavuzda, PAL SDK'sının bir Roku uygulamasına nasıl dahil edileceği, tek seferlik rastgele sayı isteğinde bulunulacağı ve reklam gösterimlerinin nasıl kaydedileceği açıklanmaktadır.

Ön koşullar

Bu rehbere başlamadan önce şunları yapmanız gerekir:

  • Roku geliştirme ortamı: Daha fazla bilgi için Roku Geliştirici ortamı kurulum kılavuzuna bakın.
  • Aşağıdaki yapıya sahip bir proje klasörü:

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

Projenizi oluşturma

PAL SDK'sını entegre etmeden önce proje dosyalarınızı yapılandırmanız gerekir.

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

Örnek video oynatıcı oluşturma

SampleVideoPlayer bileşeni, uzaktan kumanda tuşlarına basma işlemlerini yakalamak için bir video bileşenini sarar. Uzaktan kumandanın odağı video/reklam oynatıcıya aktarıldıktan sonra diğer tuş basışlarının (yukarı, aşağı, sol, sağ, tıklama vb.) yakalanıp PAL'e aktarılması için onKeyEvent değerini geçersiz kılın.

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>

Test arayüzü oluşturma

Aşağıdakileri yapmak için düğmelerin bulunduğu bir sahne uygular:

  • Tek seferlik rastgele sayı isteyin.
  • Bir reklam tıklaması gönderin.
  • Oynatma işleminin başlatıldığı bir etkinlik gönderin.
  • Oynatma sona eren bir etkinlik gönderir.
  • Odağı video düğmesine aktarın.

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 arayüz bileşeni oluşturma

Ana sahne ile PAL SDK'sı arasında iletişim kurmak için asenkron kod içeren bir bileşene ihtiyacınız vardır. PAL SDK'sı harici ağ istekleri gönderir. Bu istekler, Roku uygulamasındaki ana iş parçacığında yapılamaz. Bu bileşene veri göndermek için bileşenin hangi verileri gönderip aldığını tanımlayan bir arayüze ihtiyacınız vardır.

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'sını içe aktarma

PAL kitaplığını kullanmak için uygulama manifestinizde Roku için IMA SDK'yı zorunlu kılmanız ve PALInterface bileşenine içe aktarmanız gerekir.

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>

Arayüz bileşenini sahneden tetikleme

Ardından, kullanıcı etkileşimlerini dinleyen ve arayüz bileşeninde değişiklikleri tetikleyen BrightScript kodunu ekleyin:

  • Arayüz bileşeninden çıkış almak için bu çıkışlarla ilişkili arayüz alanlarında alan gözlemcileri uygulayın ve bunları ana bileşendeki geri çağırma işlevlerine ekleyin. Bunu bileşen ilk kaydedildiğinde yapın.

  • Kullanıcı etkileşimlerini arayüz bileşenine göndermek için, daha önce oluşturduğunuz düğmelere alan gözlemcileri uygulayarak bu komutlarla ilişkili arayüz alanlarında değişiklikleri tetikleyin.

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>

Odağı aktarmak için yöntemler ekleme

Ardından, odak noktasını video öğenize ve video öğenizden aktarmak için kullanıcının tuş basmalarını yakalayı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>

PAL SDK'yı başlatın ve bir nonceLoader oluşturun

Artık PAL SDK uygulamasını temel mantığını oluşturmaya başlayabilirsiniz. Öncelikle SDK'yı ayrı bir mesaj dizisinden başlatın.

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>

Tek seferlik rastgele olmayan istekleri işleme

nonceLoader oluşturulduktan sonra requestNonce alanına bir gözlemci ekleyerek tek seferlik rastgele istekleri işlemeniz gerekir. Bu gözlemciyi yalnızca nonceLoader başlatıldıktan sonra ekleyerek tek seferlik isteklerin SDK iş parçacığında işlenmesini ve tek seferlik isteklerin yalnızca geçerli bir nonceLoader varsa gönderilebilmesini sağlayabilirsiniz.

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>

NonceRequest.storageAllowed için varsayılan değer true'tır ancak uygun izni aldıktan sonra bu değer değiştirilebilir. getConsentToStorage() yöntemi, bir CMP ile entegrasyon yaparak veya depolama alanı iznini işlemek için diğer yöntemlere göre kullanıcı izni alma yönteminiz için bir yer tutucudur.

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

Oyuncu yaşam döngüsü sinyallerini dinleme

PAL entegrasyonunuzun sinyalleri düzgün şekilde göndermesini sağlamak için oynatıcınızın yaşam döngüsü sinyallerini dinleyecek bir döngü oluşturmanız gerekir.

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>

sendPlaybackStart, sendPlaybackEnd, sendAdClick ve sendAdTouch için dinleyicileri kaydedin

Ardından, "video oynatıcıyı başlat" için sendPlaybackStart işlevini çağırın. Bu yöntem, IVT izleme ve algılama için gereken sinyali toplamak amacıyla Google sunucularına asynkron çağrılar başlatır. Oynatma sona erdiğinde sendPlaybackEnd numaralı telefonu arayın. Reklam tıklamasına yanıt olarak sendAdClick'ü arayın. Ardından, tıklama olmayan kullanıcı dokunma veya tıklama etkinlikleri için sendAdTouch işlevini çağırın.

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>

Tek seferlik kimliği reklam isteklerine ekleme

PAL kitaplığından aldığınız tek seferlik rastgele sayısını bir üretim uygulamasında kullanmak için reklam isteklerinizi yalnızca tek seferlik rastgele sayı oluşturulduktan sonra başlatın. Ardından, u_paln parametresini kullanarak tek seferlik rastgele sayıyı reklam etiketine ekleyin.

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

İşte bu kadar. Artık PAL tek seferlik rastgele sayı isteyebilen ve PAL SDK ile oynatma oturumu etkinliklerini kaydedebilen bir Roku uygulamanız var.

(İsteğe bağlı) Üçüncü taraf reklam sunucuları üzerinden Google Ad Manager sinyalleri gönderme

Üçüncü taraf reklam sunucusunun Ad Manager isteğini yapılandırın.

Üçüncü taraf reklam sunucunuzu, sunucunun Ad Manager'a gönderdiği isteğe tek seferlik kimliği dahil edecek şekilde yapılandırın. Aşağıda, üçüncü taraf reklam sunucusunun içinde yapılandırılmış bir reklam etiketi örneği verilmiştir:

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

Daha fazla bilgi için Google Ad Manager sunucu tarafı uygulama kılavuzuna bakın.

Ad Manager, tek seferlik değer belirlemek için givn= değerini arar. Üçüncü taraf reklam sunucusunun %%custom_key_for_google_nonce%% gibi kendi makrolarını desteklemesi ve bunu bir önceki adımda sağladığınız tek seferlik sorgu parametresiyle değiştirmesi gerekir. Bunu nasıl yapacağınızla ilgili daha fazla bilgiyi üçüncü taraf reklam sunucusunun belgelerinde bulabilirsiniz.

İşte bu kadar. Artık tek seferlik rastgele sayı parametresinin PAL SDK'sından, aracı sunucularınız aracılığıyla Google Ad Manager'a yayıldığını görebilirsiniz. Bu sayede Google Ad Manager üzerinden daha iyi para kazanabilirsiniz.