Skip to content

VPAID Examples ​

A simple example that plays a TrueX ad through Google IMA (HTML5 SDK) with VPAID enabled, using the VPAID tag.

Live ads

The example requests a real ad when you select "Play ad". Choosing to interact, and staying with the ad, behaves like a real TrueX ad.

Live Example ​

Select "Play ad", choose "Yes" on the choice card, interact with the ad for about 30 seconds, and then select "Return to content". The log shows the events that Google IMA passes to your app.

What the Example Shows ​

  • The choice card and the interactive ad are rendered by the VPAID unit, inside the ad container that IMA provides.
  • Google IMA does not pass custom events such as AdFreePod to your app. It passes the unit's AdInteraction events as an INTERACTION event, and the event's id is the Id - see When Custom Events Are Blocked.
  • The event you need is TrueXClosedAfterCredit, shown in bold in the log. It fires when the user selects "Return to content", which appears after about 30 seconds and one interaction. That is the point to skip the rest of the ad pod.
  • CONTENT_RESUME_REQUESTED arrives when the whole ad break ends. That is the point to resume the content.
VPAID eventGoogle IMA event
AdLoadedLOADED
AdStarted, AdImpressionSTARTED, IMPRESSION
AdInteraction (with an Id)INTERACTION (getAdData().id)
AdStoppedCOMPLETE (the ad ends), then CONTENT_RESUME_REQUESTED (the ad break ends)
AdSkippedSKIPPED
AdErrorAD_ERROR

Setup ​

Enable VPAID in the IMA settings, and request the tag. IMA runs the VPAID unit for you:

js
google.ima.settings.setVpaidMode(
  google.ima.ImaSdkSettings.VpaidMode.ENABLED
);

const displayContainer =
  new google.ima.AdDisplayContainer(adContainer, videoElement);
displayContainer.initialize();

const adsLoader = new google.ima.AdsLoader(displayContainer);
const request = new google.ima.AdsRequest();
request.adTagUrl = 'https://get.truex.com/{placement_hash}/vast';
adsLoader.requestAds(request);

Skipping the Ad Break ​

Skip the ad break when the user returns to the content after earning the credit, and resume the content when the ad break ends:

js
adsManager.addEventListener(
  google.ima.AdEvent.Type.INTERACTION,
  (event) => {
    if (event.getAdData().id === 'TrueXClosedAfterCredit') {
      // Skip the rest of the ad pod
      adsManager.discardAdBreak();
    }
  }
);

adsManager.addEventListener(
  google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED,
  () => {
    // The ad break is over
    videoElement.play();
  }
);

discardAdBreak

adsManager.discardAdBreak() discards only the current ad break, and keeps the later ones. It does nothing unless the ad request returned a VMAP or playlist response, such as an ad pod. With a single ad, as in this example, the content resumes when the ad ends.

Full Listing ​

The page that runs in the live example above:

html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>VPAID demo player</title>
<style>
  html, body { margin: 0; height: 100%; background: #000; }
  #stage { position: relative; width: 100%; height: 100%; }
  #content, #adContainer {
    position: absolute; top: 0; left: 0;
    width: 100%; height: 100%;
  }
</style>
</head>
<body>
<div id="stage">
  <video id="content" playsinline controls></video>
  <div id="adContainer"></div>
</div>
<script
  src="https://imasdk.googleapis.com/js/sdkloader/ima3.js"
></script>
<script>
  const CONTENT_URL =
  'https://storage.googleapis.com/gvabox/media/samples/stock.mp4';

  const video = document.getElementById('content');
  video.src = CONTENT_URL;
  const container = document.getElementById('adContainer');
  let displayContainer;
  let adsLoader;
  let adsManager;

  // Sends an event to the docs page, which shows it in the log
  function sendMessageToParentFrame(name, data) {
    parent.postMessage(
      { source: 'vpaid-demo', name: name, data: data },
      location.origin
    );
  }

  // Hide the ad container, so that it does not cover the video
  function resumeContent() {
    container.style.display = 'none';
    video.play();
  }

  function destroyAds() {
    if (adsManager) {
      adsManager.destroy();
      adsManager = undefined;
    }
    if (adsLoader) {
      adsLoader.destroy();
      adsLoader = undefined;
    }
    if (displayContainer) {
      displayContainer.destroy();
      displayContainer = undefined;
    }
  }

  function onAdEvent(name, adEvent) {
    const adData = adEvent.getAdData();
    const isInteraction = name === 'INTERACTION' && adData;
    const id = isInteraction ? adData.id : undefined;
    sendMessageToParentFrame(name, id ? { id: id } : undefined);

    // The user was credited and selected "Return to content":
    // skip the rest of the ad break
    if (id === 'TrueXClosedAfterCredit') {
      sendMessageToParentFrame('app', {
        message: 'credited: discard the ad break'
      });
      adsManager.discardAdBreak();
    }

    // CONTENT_RESUME_REQUESTED is the end of the whole ad break
    if (name === 'CONTENT_RESUME_REQUESTED') {
      sendMessageToParentFrame('app', {
        message: 'break done: resume content'
      });
      resumeContent();
    }
  }

  function onAdsManagerLoaded(event) {
    adsManager = event.getAdsManager(video);

    const types = Object.keys(google.ima.AdEvent.Type);
    types.forEach(function (name) {
      if (name === 'AD_PROGRESS' || name === 'DURATION_CHANGE') {
        return;
      }
      adsManager.addEventListener(
        google.ima.AdEvent.Type[name],
        function (adEvent) {
          onAdEvent(name, adEvent);
        }
      );
    });

    adsManager.addEventListener(
      google.ima.AdErrorEvent.Type.AD_ERROR,
      onAdError
    );
    adsManager.init(
      video.clientWidth,
      video.clientHeight,
      google.ima.ViewMode.NORMAL
    );
    adsManager.start();
  }

  function onAdError(errorEvent) {
    const message = errorEvent.getError().getMessage();
    sendMessageToParentFrame('AD_ERROR', { message: message });
    destroyAds();
    resumeContent();
  }

  function playAd(tag) {
    destroyAds();
    video.pause();
    video.currentTime = 0;
    container.style.display = 'block';

    // Let IMA run the VPAID unit
    google.ima.settings.setVpaidMode(
      google.ima.ImaSdkSettings.VpaidMode.ENABLED
    );

    displayContainer =
      new google.ima.AdDisplayContainer(container, video);
    displayContainer.initialize();

    adsLoader = new google.ima.AdsLoader(displayContainer);
    adsLoader.addEventListener(
      google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED,
      onAdsManagerLoaded
    );
    adsLoader.addEventListener(
      google.ima.AdErrorEvent.Type.AD_ERROR,
      onAdError
    );

    const request = new google.ima.AdsRequest();
    request.adTagUrl = tag;
    request.linearAdSlotWidth = video.clientWidth;
    request.linearAdSlotHeight = video.clientHeight;
    sendMessageToParentFrame('app', {
      message: 'ad request: ' + tag
    });
    adsLoader.requestAds(request);
  }

  function stop() {
    destroyAds();
    video.pause();
    video.currentTime = 0;
    container.style.display = 'none';
  }

  // The docs page sends { type: 'play', tag } or { type: 'stop' }
  window.addEventListener('message', function (event) {
    if (event.origin !== location.origin || !event.data) {
      return;
    }
    if (event.data.type === 'play') {
      playAd(event.data.tag);
    } else if (event.data.type === 'stop') {
      stop();
    }
  });

  sendMessageToParentFrame('ready');
</script>
</body>
</html>

Integration documentation for Infillion Ads