Advertising with Google IMA DAI Plugin

This topic covers the use of the IMA DAI plugin and how it can be implemented using Studio and through custom coding.

Overview

IMA DAI SDK stitches your video content and ads into a single stream, independent of a web page or app, and removes the ad request and ad response process from the SDK. This reduces the likelihood of client-side errors and produces a seamless TV-like experience without latency or buffering between content and ads.

With IMA DAI SDK, you can target individual ads for live linear and video on demand programming, obtain multi-screen reach with broad device support, and take advantage of programmatic monetization across all devices with Ad Exchange for Video.

Regardless of the original format of your content, once it becomes digital, DAI can stitch custom targeted video ads into the stream, based on the individual user viewing the content.

It minimizes buffering and latency, and it ensures ads are rendered in the appropriate format, so that viewers have the broadcast-quality experience they expect.

Requirements

  • Have an Ad Manager 360 Advanced account.

Features

  • Provides a seamless, broadcast-like viewing experience.
  • Streams are combined on the Ad Manager servers, regardless of which network an ad is hosted on.

Implement using Players module - Plugins section

Add the DAI plugin

  1. Open the PLAYERS module and either create a new player or locate the player to which you wish to add the plugin.
  2. Click the player's name to open the player's properties.
  3. Click Plugins in the left navigation menu.
  4. From the Add a Plugin dropdown, select Custom Plugin.

    Custom Plugin
  5. For the Plugin Name enter imaDai.
  6. For the JavaScript URL, enter:
    https://players.brightcove.net/videojs-ima-dai/1/videojs-ima-dai.min.js
  7. For the CSS enter:
    https://players.brightcove.net/videojs-ima-dai/1/videojs-ima-dai.css
  8. Click Save.
  9. To publish the player, click Publish & Embed > Publish Changes.
  10. To close the open dialog, click Close.

Implement using code

To Initialize the IMA DAI Plugin, follow these steps:

  1. Get the IMA DAI Plugin class:

    const ImaDaiPlugin = videojs.getPlugin('imaDai');
  2. Initialize the bc player:

    
             const videoId = 'ima-dai-player';
              const player = bc(videoId);
  3. Initialize the IMA DAI Plugin:

    const imaDai = player.imaDai();
  4. Listen to the IMA_DAI_SDK_LOADED event:

    imaDai.one(ImaDaiPlugin.EVENTS.IMA_DAI_SDK_LOADED, ({ imaDaiSdk }) => {
              // The IMA DAI SDK is now ready, and you can send a stream request.
            });
  5. Create a StreamRequest instance: streamRequest should be one of the following classes: LiveStreamRequest, VODStreamRequest, PodStreamRequest.

    const streamRequest = new VODStreamRequest();
  6. Provide a fallback stream: A fallback is required for any IMA DAI stream request. If the IMA DAI SDK fails to get a stream, the player will use this fallback stream.

    
              const fallback = {
                type: 'your-fallback-type',
                src: 'your-fallback-src'
              };
  7. Set the player source:

    
              player.src({
                type: fallback.type,
                src: fallback.src,
                imaDai: { streamRequest }
              });
  8. Final Code

    
    const ImaDaiPlugin = videojs.getPlugin('imaDai');
    const videoId = 'ima-dai-player';
    const player = bc(videoId);
    const imaDai = player.imaDai();
    
    imaDai.one(ImaDaiPlugin.EVENTS.IMA_DAI_SDK_LOADED, ({ imaDaiSdk }) => {
      const streamRequest = new VODStreamRequest();
      const fallback = { type: 'your-fallback-type', src: 'your-fallback-src' };
    
      player.src({
        type: fallback.type,
        src: fallback.src,
        imaDai: { streamRequest }
      });
    });
    

    Stream Request Examples:

    1. VOD Stream Request:

      
                // ...
      imaDai.one(ImaDaiPlugin.EVENTS.IMA_DAI_SDK_LOADED, ({ imaDaiSdk }) => {
        const { VODStreamRequest } = imaDaiSdk;
      
        const streamRequest = new VODStreamRequest();
      
        streamRequest.videoId = 'tears-of-steel';
        streamRequest.contentSourceId = '2528370';
      
        const fallback = {
          type: 'application/x-mpegURL',
          src: 'https://bitmovin-a.akamaihd.net/content/sintel/hls/playlist.m3u8'
        };
      
        player.src({
          type: fallback.type,
          src: fallback.src,
          imaDai: { streamRequest }
        });
      });
      // ...
                
    2. Live Stream Request:

      
                // ...
      imaDai.one(ImaDaiPlugin.EVENTS.IMA_DAI_SDK_LOADED, ({ imaDaiSdk }) => {
        const { LiveStreamRequest } = imaDaiSdk;
      
        const streamRequest = new LiveStreamRequest();
      
        streamRequest.assetKey = 'c-rArva4ShKVIAkNfy6HUQ';
      
        const fallback = {
          type: 'application/x-mpegURL',
          src: 'https://bitmovin-a.akamaihd.net/content/sintel/hls/playlist.m3u8'
        };
      
        player.src({
          type: fallback.type,
          src: fallback.src,
          imaDai: { streamRequest }
        });
      });
      // ...
    3. Pod Serving Stream Request:

      
                // ...
      imaDai.one(ImaDaiPlugin.EVENTS.IMA_DAI_SDK_LOADED, ({ imaDaiSdk }) => {
        const { PodStreamRequest } = imaDaiSdk;
      
        const streamRequest = new PodStreamRequest();
      
        streamRequest.networkCode = '51636543';
        streamRequest.customAssetKey = 'google-sample';
        streamRequest.apiKey = '';
      
        // Pod stream request requires podSourceResolver
        const podStreamUrl = 'https://encodersim.sandbox.google.com/masterPlaylist/9c654d63-5373-4673-8c8d-6d92b66b9d46/master.m3u8?gen-seg-redirect=true&network=51636543&event=google-sample&pids=devrel4628000,devrel896000,devrel3528000,devrel1428000,devrel2628000,devrel1928000&seg-host=dai.google.com&stream_id=[[STREAMID]]';
        const podSourceResolver = (streamId) => {
        const src = podStreamUrl.replace('[[STREAMID]]', streamId);
        const type = 'application/x-mpegURL';
      
          return { src, type };
        };
      
        const fallback = {
          type: 'application/x-mpegURL',
          src: 'https://bitmovin-a.akamaihd.net/content/sintel/hls/playlist.m3u8'
        };
      
        player.src({
          type: fallback.type,
          src: fallback.src,
          imaDai: { streamRequest, podSourceResolver }
        });
      });
      // ...

Open Measurement

The IMA SDK for HTML5 includes the Open Measurement (OM) SDK, an industry standard developed by the Interactive Advertising Bureau (IAB) to enable third-party viewability and verification measurement. When using the IMA SDK for HTML5, the included OM SDK automatically parses the tag within VAST ad tags and sends viewability data to the specified measurement vendors via the OMID API. You can optionally set access mode rules for each request to control what content the verification script can access. Learn more

Access modes The Open Measurement SDK supports running verification scripts in different access modes, which control how much the verification script can access. The four access modes are:

  • FULL: The verification script has direct access to the creative and the publisher page.
  • CREATIVE: The verification script and creative are sandboxed from the publisher page. However, the script has direct access to the creative.
  • DOMAIN: The verification script is sandboxed and cannot access the creative or publisher page. However, the script is loaded in such a way that it can directly confirm what publisher domain it is on.
  • LIMITED: The verification script is sandboxed and cannot access the creative or publisher page and cannot directly confirm what publisher domain it is on.

Here is an example of how you can set access mode rules for a request for different verification script providers:


      // ...
imaDai.one(ImaDaiPlugin.EVENTS.IMA_DAI_SDK_LOADED, ({ imaDaiSdk }) => {
  const { LiveStreamRequest } = imaDaiSdk;

  const streamRequest = new LiveStreamRequest();

  streamRequest.assetKey = 'c-rArva4ShKVIAkNfy6HUQ';
  streamRequest.omidAccessModeRules = {};
  // set full access mode for goole:
  streamRequest.omidAccessModeRules[google.ima.OmidVerificationVendor.GOOGLE] = google.ima.OmidAccessMode.FULL;
  // set domain acess mode for other verfication script provider: 
  streamRequest.omidAccessModeRules[google.ima.OmidVerificationVendor.OTHER] = google.ima.OmidAccessMode.DOMAIN;

  const fallback = {
    type: 'application/x-mpegURL',
    src: 'https://bitmovin-a.akamaihd.net/content/sintel/hls/playlist.m3u8'
  };

  player.src({
    type: fallback.type,
    src: fallback.src,
    imaDai: { streamRequest }
  });
});
// ...
      

Privacy

CCPA

To help publishers toward compliance with the California Consumer Privacy Act (CCPA), the Google Interactive Media Ads SDK allows publishers to use two different parameters to indicate whether Google should enable restricted data processing (RDP). The SDK provides publishers with the ability to set RDP at an ad request level utilizing the following parameters:

RDP signal

To notify Google that RDP should be enabled using Google's signal, append rdp=1 to your ad tag parameters, as shown in the following example:


      // ...
imaDai.one(ImaDaiPlugin.EVENTS.IMA_DAI_SDK_LOADED, ({ imaDaiSdk }) => {
  const { LiveStreamRequest } = imaDaiSdk;

  const streamRequest = new LiveStreamRequest();

  streamRequest.assetKey = 'c-rArva4ShKVIAkNfy6HUQ';
  // RDP signal:
  streamRequest.adTagParameters = {"rdp": 1};

  const fallback = {
    type: 'application/x-mpegURL',
    src: 'https://bitmovin-a.akamaihd.net/content/sintel/hls/playlist.m3u8'
  };

  player.src({
    type: fallback.type,
    src: fallback.src,
    imaDai: { streamRequest }
  });
});
// ...
      

IAB signal

To notify Google that RDP should be enabled using IAB's signal, use the ad tag parameter us_privacy. The snippet below demonstrates how to create an ad request with the IAB parameter "1YNN":


        // ...
imaDai.one(ImaDaiPlugin.EVENTS.IMA_DAI_SDK_LOADED, ({ imaDaiSdk }) => {
  const { LiveStreamRequest } = imaDaiSdk;

  const streamRequest = new LiveStreamRequest();

  streamRequest.assetKey = 'c-rArva4ShKVIAkNfy6HUQ';
  // IAB signal:
  streamRequest.adTagParameters = {"us_privacy": "1YNN"};

  const fallback = {
    type: 'application/x-mpegURL',
    src: 'https://bitmovin-a.akamaihd.net/content/sintel/hls/playlist.m3u8'
  };

  player.src({
    type: fallback.type,
    src: fallback.src,
    imaDai: { streamRequest }
  });
});
// ...
        

GDPR

Under Google's updated EU User Consent Policy, you must make certain disclosures to your users in the European Economic Area (EEA) and obtain their consent for the use of cookies or other local storage where legally required and for the collection, sharing, and use of personal data for ads personalization. This policy reflects the requirements of the EU ePrivacy Directive and the General Data Protection Regulation (GDPR). Learn more.

Force non-personalized ads

Append npa=1 to your ad tag to specify that only non-personalized ad content.


          // ...
          imaDai.one(ImaDaiPlugin.EVENTS.IMA_DAI_SDK_LOADED, ({ imaDaiSdk }) => {
            const { LiveStreamRequest } = imaDaiSdk;
          
            const streamRequest = new LiveStreamRequest();
          
            streamRequest.assetKey = 'c-rArva4ShKVIAkNfy6HUQ';
            // Force non-personalized ads:
            streamRequest.adTagParameters = {"npa": 1};
          
            const fallback = {
              type: 'application/x-mpegURL',
              src: 'https://bitmovin-a.akamaihd.net/content/sintel/hls/playlist.m3u8'
            };
          
            player.src({
              type: fallback.type,
              src: fallback.src,
              imaDai: { streamRequest }
            });
          });
          // ...
          

Tagging users as under the age of consent

This parameter disables personalized advertising, including remarketing, for that specific ad request. It also disables requests to third-party ad vendors, such as ad measurement pixels and third-party ad servers. To include this tag on all ad requests made from your implementation, append tfua=1 to your ad tag.


            // ...
imaDai.one(ImaDaiPlugin.EVENTS.IMA_DAI_SDK_LOADED, ({ imaDaiSdk }) => {
  const { LiveStreamRequest } = imaDaiSdk;

  const streamRequest = new LiveStreamRequest();

  streamRequest.assetKey = 'c-rArva4ShKVIAkNfy6HUQ';
  // Tagging users as under the age of consent:
  streamRequest.adTagParameters = {"tfua": 1};


  const fallback = {
    type: 'application/x-mpegURL',
    src: 'https://bitmovin-a.akamaihd.net/content/sintel/hls/playlist.m3u8'
  };

  player.src({
    type: fallback.type,
    src: fallback.src,
    imaDai: { streamRequest }
  });
});
// ...
            

Main Concepts

Stream Time vs. Relative Time

When we use server-side ad insertion (SSAI), ads and content are stitched together into a single stream.

Since there's only one stream, we need to create a way to understand time:

  • Stream Time (or Absolute Time): This is the time in the whole stream.
  • Relative Time: This depends on what's playing right now (ads or content).
    1. If we're playing an ad, the relative time will be the time within that specific ad.
    2. If we're playing content, the relative time will be the time within the content.

Example:

In this 70-second stream, ads take up to 30 seconds, and content takes up to 40 seconds.

  • Absolute Time Duration = 70 seconds
  • Relative Time Duration (Content) = 40 seconds
  • Relative Time Duration (each Ad) = each individual ad's length (10s, 5s, ...)

Snapback

Snapback is the ability to take the user back to the start of the ad break they seeked past and then return them to their seek location after that ad break is over. Learn more.

Options

Option Description Default Value
debug If true, it enables debug messages and logs extra information. false
sdkUrl The URL of your self-hosted IMA DAI SDK. //imasdk.googleapis.com/js/sdkloader/ima3_dai.js
hideOverlays If true, it hides overlays while ads are playing. false

Static Members

Member Description Type Usage example
DEFAULTS Object with possible plugin options and their default values. Record<string, any> console.log(ImaDaiPlugin.DEFAULTS);
VERSION Current plugin's version. string console.log(ImaDaiPlugin.VERSION);
EVENTS Object with possible plugin events. Record<string, string> console.log(ImaDaiPlugin.EVENTS);
SOURCE_TYPES Object with possible plugin source types. Record<string, string> console.log(ImaDaiPlugin.SOURCE_TYPES);

Instance getters

Getter Description Type Usage Example
options Getter for the current options of the IMA DAI plugin instance. Options console.log(imaDai.options);
isImaDaiStream Check if the IMA DAI stream is playing. Returns true if it is, false otherwise. boolean console.log(imaDai.isImaDaiStream);
contentDuration Getter for the content duration (without ads). number console.log(imaDai.contentDuration);
relativeDuration Get the current ad duration if in ad mode or content duration without ads otherwise. number console.log(imaDai.relativeDuration);
canSeekNow Check if it is possible to perform a seek now. boolean console.log(imaDai.canSeekNow);
relativeCurrentTime Get the current time for the current ad if in ad mode or current content time (without ads) otherwise. number console.log(imaDai.relativeCurrentTime);
totalAdsDuration Get the total duration of all known ads (applicable for VOD only). number console.log(imaDai.totalAdsDuration);

Instance methods

Method Description Type Usage Example
updateOptions Update the plugin's options. (receivedOptions: Record<string, any>) => void imaDai.updateOptions({});
streamTimeForContentTime Convert a given content time to stream time with ads. (contentTime: number) => number console.log(imaDai.streamTimeForContentTime(contentTime));
contentTimeForStreamTime Convert a given stream time to content time without ads. (streamTime: number) => number console.log(imaDai.contentTimeForStreamTime(streamTime));

Events

Event Description Value Usage Example
IMA_DAI_SDK_LOADED The event is triggered when the IMA DAI SDK has finished loading. 'imaDai:imaDaiSdkLoaded'
imaDai.on(ImaDaiPlugin.EVENTS.IMA_DAI_SDK_LOADED, ({ imaDaiSdk }) => { ... });