Ad rules

IMA HTML5 SDK supports fully automated ad playlists. This feature inserts ad breaks into the content as specified in Google Ad Manager when trafficking your ads. It also greatly simplifies the video player code necessary to support ad breaks, including pre-rolls, mid-rolls and post-rolls.

  • When creating the AdsManager, a contentPlayback object is passed in via the getAdsManager call. This object must have a currentTime property that returns the current playhead position of the video. If you're using an HTML5 video element to display your content, you can just pass that element to the SDK. This object is used to track the progress of the content playback so ad breaks are automatically inserted at the times specified in Ad Manager. You also need to let the SDK know that you want it to handle content state on your behalf.
    var adsRenderingSettings = new google.ima.AdsRenderingSettings();
    adsRenderingSettings.restoreCustomPlaybackStateOnAdBreakComplete = true;
    adsManager = adsManagerLoadedEvent.getAdsManager(
        videoContent, adsRenderingSettings); // See API reference for
        
        contentPlayback.
  • To ensure post-rolls are played, you need to let the SDK know when your content is finished. This is a bit tricky, because in some cases the SDK uses your video player to play ads, so you need to make sure you're only letting the SDK know when your content is finished, and not when an ad is finished. You can do that using the code below:
    var videoContent = document.getElementById('contentElement');
    var contentEndedListener = function() {adsLoader.contentComplete();};
    
    videoContent.addEventListener('ended', contentEndedListener);
    
    function onContentPauseRequested() {
      contentElement.removeEventListener('ended', contentEndedListener);
      ...
    }
    
    function onContentResumeRequested() {
      contentElement.addEventListener('ended', contentEndedListener);
      ...
    }
  • The CONTENT_PAUSE_REQUESTED and CONTENT_RESUME_REQUESTED events are used to pause and resume the content when ad breaks are played.
  • If your video player supports drag-to-seek, and the current time property of the video player updates while the user is dragging, the SDK can't differentiate between content progressing normally and a user seeking through the content. You must use a custom contentPlayback object as your parameter to getAdsManager. For an example of this use case, see The Trouble with Seeking.

Note: When the content has finished playing or the user has stopped playback, be sure to call AdsLoader.contentComplete in order to signal to the SDK that the content is done. The SDK then plays the post-roll ad break, if one has been scheduled. The ALL_ADS_COMPLETED event is raised when ALL ad breaks have been played. In addition, note that content tracking begins when init() is called and you should always call init() before playing content.

Disabling automatic playback of ad breaks

In some circumstances you may want to prevent the SDK from playing ad breaks until you're ready for them. In this scenario, you can disable automatic playback of ad breaks in favor of letting the SDK know when you're ready for an ad break to play. With this configuration, once the SDK has loaded an ad break, it fires an AD_BREAK_READY event. When your player is ready for the ad break to start, you can call adsManager.start():

function requestAds() {}
  ...
  adsLoader.getSettings().setAutoPlayAdBreaks(false);
  ...
}

function onAdsManagerLoaded() {
  ...
  // For non-auto ad breaks, listen for ad break ready
  adsManager.addEventListener(
      google.ima.AdEvent.Type.AD_BREAK_READY,
      adBreakReadyHandler);
  ...
}

function adBreakReadyHandler() {
  // Once we're ready to play ads. To skip this ad break, simply return
  // from this handler without calling adsManager.start().
  adsManager.start();
}

Try it out

You can see a working implementation below.

The trouble with seeking

If you use ad rules, you may run into a problem with click-and-drag seeking. Specifically, after a user clicks and drags to seek through video past multiple midroll pods, they may see 2 or more of those pods play back to back before content resumes. This is caused by the video playhead time updating while the user is seeking; if the SDK happens to poll for the current time while the user seeks past an ad, it may think that ad should be played. When the content resumes, it plays that ad, and then the most recent ad since the seek. For a visual representation of this problem, see the diagram below:

The simple way to solve this is save the current time when the user starts seeking, and report that time when the SDK asks for it until the user resumes normal playback. For a visual representation of this solution, see the diagram below:

With this solution, you properly skip the 0:10 mid-roll and only play the 0:20 mid-roll. This is done using a custom playhead tracker, as shown below. The code below contains modifications (shown in bold) of ads.js in the advanced HTML5 sample available on our download page.

var Ads = function(application, videoPlayer) {
  ...
  this.currentTime = 0;
  setInterval(this.updateCurrentTime, 1000);
};

Ads.prototype.updateCurrentTime = function() {
  if (!this.videoPlayer_.contentPlayer.seeking) {
    this.currentTime = this.videoPlayer_.contentPlayer.currentTime;
  }
};

Ads.prototype.onAdsManagerLoaded_ = function(adsManagerLoadedEvent) {
  this.application_.log('Ads loaded.');
  this.adsManager_ = adsManagerLoadedEvent.getAdsManager(this);
  this.processAdsManager_(this.adsManager_);
};

Known issues with mobile Safari

This method should work on every plaform except mobile Safari. On mobile Safari, the seeking property of the video tag is not properly implemented (it always returns false). To get around that, you need to do your own check to see if the user is seeking through the video. The sample code for this method follows. Again, the bolded lines below are modifications to existing code.

var Ads = function(application, videoPlayer) {
  ...
  this.currentTime = 0;
  setInterval(this.updateCurrentTime, 1000);
  this.seeking = false;
  this.seekCheckInterval = 1000;
  // You may need to adjust this value, depending on your platform
  this.seekThreshold = 100;
  this.previousTime = 0;
  setInterval(
      Application.bind(this, this.checkForSeeking),
      this.seekCheckInterval);
};

Ads.prototype.updateCurrentTime = function() {
  if (!this.seeking) {
    this.currentTime = this.videoPlayer_.contentPlayer.currentTime;
  }
};

Ads.prototype.checkForSeeking = function() {
  var currentTime = this.videoPlayer_.contentPlayer.currentTime;
  // How much time has passed since you last ran this method, in milliseconds
  var diff = (currentTime - this.previousTime) * 1000;
  // If that difference is greater than the time since you last ran this method,
  // plus the threshold, the user was seeking
  if (Math.abs(diff)  > this.interval + this.threshold) {
    this.seeking = true;
  } else {
    this.seeking = false;
  }
  // Grab the current video time again to make up for time spent in this method
  previousTime = this.videoPlayer_.contentPlayer.currentTime;
};

Ads.prototype.onAdsManagerLoaded_ = function(adsManagerLoadedEvent) {
  this.application_.log('Ads loaded.');
  this.adsManager_ = adsManagerLoadedEvent.getAdsManager(this);
  this.processAdsManager_(this.adsManager_);
};

With these changes, the SDK is now using the currentTime property of your Ads object to determine when to play ad breaks, not the currentTime property of the content video player.