Get Started with Tizen (Samsung)

The Programmatic Access Library (PAL) SDK for connected-TVs (CTV) enables publishers with direct VAST call (DVC) approval to monetize DVC-based CTV applications. Full support is limited to Tizen (Samsung) TVs from 2016 and later. The PAL SDK lets you request nonces, which are encrypted strings, from Google, so that you can sign DVC requests. Each new stream request must be accompanied by a newly generated nonce. However, you can reuse the same nonce for multiple ad requests within the same stream. To see a sample app that uses PAL to generate a nonce, download the CTV (Tizen) example from GitHub.

This guide explains an example of how to incorporate the PAL SDK into a Samsung (Tizen) application, request a nonce, and register ad impressions.

Prerequisites

Before starting this guide, you need to create a basic Tizen app. You can use the Tizen IDE to set up the app. See this video on getting started with Tizen for more information.

Generate a nonce

A "nonce" is a single encrypted string generated by PAL using the NonceLoader. The PAL SDK requires each new stream request to be accompanied by a newly generated nonce. However, nonces may be reused for multiple ad requests within the same stream.

Create an HTML file for your app. Include the webapis.js dependency in your HTML file. IMA requires this dependency to function. For more information, see the Samsung WebApi documentation.

Include a dependency for the CTV PAL SDK. This script tag should be loaded following the script tag for webapis.js.

pal.html

<!DOCTYPE html>
<html>
<head>
 <meta charset="utf-8" />
 <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
 <script src="$WEBAPIS/webapis/webapis.js"></script>
 <script src="//imasdk.googleapis.com/pal/sdkloader/pal_ctv.js"></script>
 <script src="pal.js"></script>
</head>
<body>
  <header>
    <hgroup>
      <h1>Tizen app for PAL sdk</h1>
    </hgroup>
  </header>
  <div id="placeholder-video"></div>
</body>
</html>

PAL spam signals require certain Tizen privileges to properly function. Create a config.xml file with the following privilege declarations:

  • "http://tizen.org/privilege/internet"
  • "http://tizen.org/privilege/system"
  • "http://tizen.org/privilege/telephony"
  • "http://tizen.org/privilege/tv.inputdevice"
  • "http://developer.samsung.com/privilege/network.public"
  • "http://developer.samsung.com/privilege/productinfo"

See the following code snippet for an example:

config.xml

<?xml version="1.0" encoding="UTF-8"?>
<widget xmlns:tizen="http://tizen.org/ns/widgets" xmlns="http://www.w3.org/ns/widgets" id="http://yourdomain/BasicProject" version="1.0.0" viewmodes="maximized">
    <tizen:application id="PACKAGE_STRING.BasicProject" package="PACKAGE_STRING" required_version="2.3"/>
    <content src="index.html"/>
    <feature name="http://tizen.org/feature/screen.size.normal.1080.1920"/>
    <icon src="icon.png"/>
    <tizen:metadata key="http://tizen.org/metadata/app_ui_type/base_screen_resolution" value="extensive"/>
    <name>BasicProject</name>
    <tizen:privilege name="http://tizen.org/privilege/internet"/>
    <tizen:privilege name="http://tizen.org/privilege/system"/>
    <tizen:privilege name="http://tizen.org/privilege/telephony"/>
    <tizen:privilege name='http://tizen.org/privilege/tv.inputdevice'/>
    <tizen:privilege name="http://developer.samsung.com/privilege/network.public"/>
    <tizen:privilege name="http://developer.samsung.com/privilege/productinfo"/>
    <tizen:profile name="tv-samsung"/>
    <tizen:setting screen-orientation="landscape" context-menu="enable" background-support="disable" encryption="disable" install-location="auto" hwkey-event="enable"/>
    <access origin="*" subdomains="true"/>
</widget>

Then, create a JavaScript file to generate a nonce. This step includes the PAL workflow of creating a NonceLoader, creating a NonceRequest, and then requesting a nonce using NonceLoader.loadNonceManager().

pal.js

let videoElement;
let nonceLoader;
let managerPromise;
let nonceManager;
let storageConsent = true;
let playbackStarted = false;

/**
 * A placeholder for the publisher's own method of obtaining user
 * consent, either by integrating with a CMP or based on other
 * methods the publisher chooses to handle storage consent.
 * @return {boolean} Whether storage consent has been given.
 */
function getConsentToStorage() {
  return storageConsent;
}

/**
 * Initializes the PAL loader.
 */
function init() {
  const videoElement = document.getElementById('placeholder-video');
  videoElement.addEventListener('mousedown', onVideoTouch);
  videoElement.addEventListener('touchstart', onVideoTouch);
  videoElement.addEventListener('play', function() {
    if (!playbackStarted) {
      sendPlaybackStart();
      playbackStarted = true;
    }
  });
  videoElement.addEventListener('ended', sendPlaybackEnd);
  videoElement.addEventListener('error', function() {
    // Handle video error.
    sendPlaybackEnd();
  });

  generateNonce();
}

/**
 * Generates a nonce with sample arguments and logs it to the console.
 */
function generateNonce() {
  // The default value for `allowStorage` is false, but can be
  // changed once the appropriate consent has been gathered.
  const consentSettings = new goog.ctv.pal.ConsentSettings();
  consentSettings.allowStorage = getConsentToStorage();

  nonceLoader = new goog.ctv.pal.NonceLoader(consentSettings);

  const request = new goog.ctv.pal.NonceRequest();
  request.adWillAutoPlay = true;
  request.adWillPlayMuted = true;
  request.continuousPlayback = false;
  request.descriptionUrl = 'https://example.com';
  request.iconsSupported = true;
  request.playerType = 'Sample Player Type';
  request.playerVersion = '1.0';
  request.ppid = 'Sample PPID';
  request.sessionId = 'Sample SID';
  // Player support for VPAID 2.0, OMID 1.0, and SIMID 1.1
  request.supportedApiFrameworks = '2,7,9';
  request.url = 'https://developers.google.com/ad-manager/pal/ctv';
  request.videoHeight = 480;
  request.videoWidth = 640;

  managerPromise = nonceLoader.loadNonceManager(request);
  managerPromise
      .then(function(manager) {
        nonceManager = manager;
      })
      .catch((error) => {
        // Handle nonce generating error.
      });

window.addEventListener("load", function(event) {
  init();
});

Attach your nonce to the ad request

To use the generated nonce, append your ad tag with a givn parameter and the nonce value, before making your ad requests.

pal.js

  /**
   * The ad tag for your ad request, for example:
   * https://pubads.g.doubleclick.net/gampad/ads?sz=640x480&iu=/124319096/external/single_ad_samples&ciu_szs=300x250&impl=s&gdfp_req=1&env=vp&output=vast&unviewed_position_start=1&cust_params=deployment%3Ddevsite%26sample_ct%3Dlinear&correlator=
   *
   * For more sample ad tags, see https://developers.google.com/interactive-media-ads/docs/sdks/html5/client-side/tags
   */
  const DEFAULT_AD_TAG = "Your ad tag";

  ...

  managerPromise = nonceLoader.loadNonceManager(request);
  managerPromise
      .then(function(manager) {
        nonceManager = manager;
        
        // Append the nonce to the ad tag URL.
        makeAdRequest(DEFAULT_AD_TAG + "&givn=" + nonceString);
        
      })

Track playback events

Lastly, you need to implement various event handlers for your player. For testing purposes, you can attach these to button click events, but in a real implementation, these would be triggered by the appropriate player events:

pal.js

/**
 * Informs PAL that an ad click has occurred. How this function is
 * called will vary depending on your ad implementation.
 */
function sendAdClick() {
  if (nonceManager) {
    nonceManager.sendAdClick();
  }
}

/**
 * Handles the user touching on the video element, passing it to PAL.
 * @param {!TouchEvent|!MouseEvent} touchEvent
 */
function onVideoTouch(touchEvent) {
  if (nonceManager) {
    nonceManager.sendAdTouch(touchEvent);
  }
}

/** Informs PAL that playback has started. */
function sendPlaybackStart() {
  if (nonceManager) {
    nonceManager.sendPlaybackStart();
  }
}

/** Informs PAL that playback has ended. */
function sendPlaybackEnd() {
  if (nonceManager) {
    nonceManager.sendPlaybackEnd();
  }
}

In your implementation, sendPlaybackStart should be called once your video playback session begins. sendPlaybackEnd should be called once your video playback session comes to an end. sendAdClick should be called each time the viewer clicks an ad. sendAdTouch should be called on every touch interaction with the player.

(Optional) Send Google Ad Manager signals through third-party ad servers

Configure the third-party ad server's request for Ad Manager.

Configure your third-party ad server to include the nonce in the server's request to Ad Manager. Here's an example of an ad tag configured inside of the third-party ad server:

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

For more details, see the Google Ad Manager Server-side implementation guide.

Ad Manager looks for givn= to identify the nonce value. The third-party ad server needs to support some macro of its own, such as %%custom_key_for_google_nonce%%, and replace it with the nonce query parameter you provided in the previous step. More information on how to accomplish this should be available in the third-party ad server's documentation.

That's it! You should now have the nonce parameter propagated from the PAL SDK, through your intermediary servers, and then to Google Ad Manager. This enables better monetization through Google Ad Manager.