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
, acontentPlayback
object is passed in via the getAdsManager call. This object must have acurrentTime
property that returns the current playhead position of the video. If you're using an HTML5video
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
andCONTENT_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.