Skip to content
Product Documentation

Advertising in the Video Center Player

The video player supports pre-roll ads via DFP. You can tailor ad logic to each video through a JavaScript function. The system makes ad requests once per video right before the video begins playing.

By default, the video player looks for a value located at window.PoWaSettings.advertising.adTag. The value can be either a string or a function. If the value is a string and it begins with window, the player then looks at that location and restarts the same process (for example, is window.other.location.adTag a string or a function?). If the ultimate value is a string, the system assumes that string is an ad tag URL. If the ultimate value is a function, the player provides an object ({powa, videoData}) containing the powa object (for example, the player) and the JSON object of the current video (videoData). The returned value must be an ad tag URL (string), a falsey value (also written as falsy, as both are accepted) (for example, an empty string, null, and so on), or a Promise that resolves to either of those values or rejects. If the value returned by adTag is falsey, then the system doesn’t make an ad request for the current video. If the system returns a string, it passes the string to the Google IMA plugin to make the actual ad request.

The player also sets ad timeouts, which you can customize. You can choose from three standard timeouts: vastLoad, videoLoad, and adStart. The vastLoad timeout runs for each ad wrapper and defaults to 5 seconds. For example, if an ad has three wrappers, the maximum allowed load time is 15 seconds. The videoLoad timeout runs on the fetch for the ad video and defaults to 8 seconds. The adStart timeout overrides the other timeouts and runs at the start of the ad request and defaults to 5 seconds. You can customize ad timeouts by providing the timeout in seconds for any or all of the timeouts. If you require customization per player (for example, more than one player on a page), you can set each timeout as a function that receives the powa object that pertains to each player and then returns the desired ad timeout in seconds.

You can configure several other options, if needed. You can configure the location with the locale setting (for example, es, fr, and so on). You can set the maximum number of ad wrappers with the maxAdWrappers setting (default: 4). By default, the player supports VPAID ads, but you can disable this setting with the vpaidEnabled property.

Defining the ad tag function at window.PoWaSettings.advertising.adTag works for development, but you must provide the adTag (string or function) to the Video Center Player team for them to include in the organization’s <org>.js configuration file.

Examples

Simple Static String

window.PoWaSettings = window.PoWaSettings || {};
window.PoWaSettings.advertising = window.PoWaSettings.advertising || {}
window.PoWaSettings.advertising.adTag = '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=';

Simple String Function

window.PoWaSettings = window.PoWaSettings || {};
window.PoWaSettings.advertising = window.PoWaSettings.advertising || {}
window.PoWaSettings.advertising.adTag = () => '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=';

Function with Logic

window.PoWaSettings = window.PoWaSettings || {};
window.PoWaSettings.advertising = window.PoWaSettings.advertising || {};
window.PoWaSettings.advertising.adTag = () => {
let everyOtherVideo = 0;
return ({powa, videoData}) => {
const videoAdZone = videoData.additional_properties.advertising.videoAdZone;
const commercialAdNode = videoData.additional_properties.advertising.commercialAdNode;
const adTag = `https://pubads.g.doubleclick.net/gampad/ads?sz=640x480&iu=/124319096/${ commercialAdNode }${ videoAdZone }&ciu_szs=300x250&impl=s&gdfp_req=1&env=vp&output=vast&unviewed_position_start=1&cust_params=deployment%3Ddevsite%26sample_ct%3Dlinear&correlator=`;
return everyOtherVideo++ % 2 === 0 ? adTag : null;
}
}();

Asynchronous Function (For Example, Programmatic, Header Bidding, and so on)

window.PoWaSettings.advertising.adTag = () => {
return new Promise((resolve, reject) => {
const adTagURL = '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=';
setTimeout(() => {
resolve(adTagURL);
}, 100);
});
};

Changing Location

window.PoWaSettings.advertising.adTag = 'window.other.location.adTag';
window.other.location.adTag = '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=';

Ad Timeouts

window.PoWaSettings.advertising.timeouts = {
standard: {
vastLoad: 5, // default
videoLoad: 8, // default
adStart: 5, // default
}
};

Custom Ad Timeouts

window.PoWaSettings.advertising.timeouts = {
standard: {
// give the first player on a page short ad timeouts
vastLoad: powa => powa.getDataset().powaIndex === 0 ? 3 : 5,
videoLoad: powa => powa.getDataset().powaIndex === 0 ? 3 : 8,
adStart: powa => powa.getDataset().powaIndex === 0 ? 5 : 25,
}
};

Other Settings

window.PoWaSettings.advertising = {
vpaidEnabled: true, // default
maxWrappers: 4, // default
locale: '', // default, '' defaults to 'en', e.g. 'es', 'fr', etc.
};

Inserting Ads Server-Side

Instead of running an ad before a video plays, you can stitch into streams server-side. This technology is referred to with many names: server-side ad insertion (SSAI), dynamic ad insertion (DAI), and ad stitching. The benefit to ad stitching is that this process inserts ads into a live stream similar to a commercial break on broadcast television. Ad stitching customizes ads to the individual watching the stream as the ad parameters that populate the ad tag request can be set client-side. If you are interested in using server-side ad insertion, you must add the MediaTailor plugin to your player configuration.

We leverage AWS MediaTailor for ad stitching with client-side reporting of ad events. You can insert ads into live streams as long as that live stream contains cue points to indicate a window when ads can be inserted. You can also insert ads into video-on-demand (VOD) streams as pre-roll. VOD does not require cue points. If the initial request to MediaTailor fails, the player resolves to running client-side ads.

The client-side ad insertion process starts very early in the player lifecycle. The process starts by checking if ad insertion is enabled for the video in question. If ad insertion is enabled, the system then obtains a unique video steam for the current end user.

The first step is to obtain the ad parameters that the system sends to MediaTailor on the initial request. We leverage the same window.PoWaSettings.advertising.adTag function detailed previously to obtain these parameters. The adTag function returns an ad tag request URL string (for example, "https://adserver.com/Ads?Key1=Val1&Key2=Val2"). We take the query string parameters off of the URL (for example, "key1=val1&key2=val2") and convert them to a JSON object (for example, {"key1″: "val1", "key2": "val2"}). The adTag function is provided a Boolean adInsertion property on the object that is passed in to it (for example, adTag({adInsertion, powa, videoData})) to facilitate the customization of the ad tag request URL for ad insertion.

With our ad parameters and video stream in hand, we make our request to MediaTailor. On the server, MediaTailor takes the ad parameters that we send and parameters that are available server-side (like client IP) and constructs a new ad tag request URL. The template the system uses to create this server-side ad request URL is the Ad Decision Server URL field in Video Center. This new server-side ad tag request URL is what the system uses to request all of the ads that are inserted into the stream.

MediaTailor responds with a unique video stream URL and a separate tracking URL from which we obtain information about the ads that have been inserted into this stream. After the user starts the stream and is receiving video, we make a call to the tracking URL to obtain information about any available ads. VOD streams require just one call to the tracking URL for any potential pre-roll ads. Live streams require additional calls as the stream progresses. With hls.js, we have visibility to the cue points that were added to the stream and we send additional requests to the tracking URL when we encounter one. When we run HLS natively (like mobile Safari), we don’t have visibility to the stream’s cue points, so we send additional requests to the tracking URL on an interval.

When we obtain ad information from the tracking URL, it details when the ad starts, the ad’s duration, and any tracking events that we are responsible for. The player then waits for any stream events (like ad start, 25% complete, and so on) or user events (like click through, mute, and pause) and sends the related tracking beacons.

We support two request timeouts for our MediaTailor implementation: a two-second timeout for the initial session request and a five-second timeout for the tracking requests. You can customize these timeout requests with window.PoWaSettings.advertising.mediaTailor.session and window.PoWaSettings.advertising.mediaTailor.tracking.

We support VPAID ads through MediaTailor. MediaTailor is able to stitch in some VPAID ads, in which case we treat the ad the same as any other stitched ad. When a VPAID ad is stitched in, we flag the adMeta object with stitchedVPAID: true. If MediaTailor is unable to stitch the ad into the stream, we run VPAID ad client-side. A VPAID is denoted by the VPAID property on the ad’s adMeta object.

Example

window.PoWaSettings = window.PoWaSettings || {};
window.PoWaSettings.advertising = window.PoWaSettings.advertising || {};
window.PoWaSettings.advertising.adTag = ({adInsertion, powa, videoData}) => {
if (adInsertion) {
return 'https://example.com/ad?key1=val1&key2=val2&ad_insertion=true';
}
else {
return 'https://example.com/ad?key1=val1&key2=val2&ad_insertion=false';
}
};
window.PoWaSettings.advertising.timeouts = window.PoWaSettings.advertising.timeouts || {};
window.PoWaSettings.advertising.timeouts.mediaTailor = {
session: 3 * 1000, // three seconds
tracking: 10 * 1000, // ten seconds
};
window.addEventListener('powaReady' event => {
new MediaTailor({
powa: event.detail.powa,
// how long to store ad data locally
// e.g. for DVR functionality during a live stream
adMemoryDuration: 60 * 60, // in seconds, default: one hour
// how often to request ad data when using native HLS
// our live streams are usually 6-second chunks
// and we usually buffer three chunks
// 6 * 3 = 18
trackingInterval: 18, // in seconds, default: 18 seconds
});
})

Ad Bar

If enabled, the ad bar appears during ads and offers countdowns and controls. You can enable the ad bar with the default configuration by setting window.PoWaSettings.advertising.adBar = true or with a configuration through window.PoWaSettings.advertising.adBar = {...}.

By default, the ad bar provides controls to play or pause, mute or unmute, and skip the ad. The ad bar also displays an ad countdown for the duration of the ad. The ad bar defaults to a 15-second skip offset to enable the skip control. The ad bar also displays a skip countdown to count down until the time the skip option is available. A two-second threshold is factored in to the skip calculation so that ads that are slightly longer than the skip offset do not trigger the skip control.

Example

window.PoWaSettings = window.PoWaSettings || {};
PoWaSettings.advertising = PoWaSettings.advertising || {};
PoWaSettings.advertising.adBar = true;

Customization

You can customize the ad bar by defining style and template properties on the PoWaSettings.advertising.adBar object. This object also accepts Boolean values to enable or disable play or pause controls, mute or unmute controls, the ad countdown, and the skip control. This object is also where you can adjust the length of your skipOffset and skipThreshold. You can customize the language that the ad bar displays with the adSkipText, adCountdownTemplate, and adSkipCountdownTemplate properties.

The checkSizing function is run on every PoWa.EVENTS.TIME update to the ad bar and is employed to hide the ad countdown and skip countdown elements if those elements would otherwise overlap the controls on the ad bar. In other words, this function checks to see if the player is too small to display all of the elements of the ad bar and conditionally hides one or both countdowns to ensure that the more important elements are visible.

The ad bar resizes the ad so that the ad bar does not overlay the ad. You can disable this function, and the default style of the ad bar has a level of transparency such that the ad is not completely obscured. Some advertisers take exception to the ad being obscured at all, so the default is to resize. To disable the setting, set window.PoWaSettings.advertising.adBar.shrinkAd = false. Otherwise, shrinkAd should be a function that returns an object with values needed to resize the ad. The shrinkAdCSS function accepts the object created by shrinkAd and returns a JavaScript CSS object to be applied to the ad to resize it.

// defaults
PoWaSettings.advertising.adBar = {
// enable play/pause control
playControl: true,
// enable mute/unmute control
muteControl: true,
// enable ad countdown
adCountdown: true,
// enable skip control
skipControl: true,
// allow user to skip after N seconds
skipOffset: 15,
// fudge factor to prevent showing 'skip' for ads slightly longer than skipOffset
skipThreshold: 2,
// text displayed on 'skip control'
adSkipText: 'Skip',
// receives {duration, elapsed, remaining, skipOffset, skipThreshold}
adCountdownTemplate: timing => `This ad will end in ${ Math.ceil(timing.remaining) } seconds`,
// receives {duration, elapsed, remaining, skipOffset, skipThreshold}
adSkipCountdownTemplate: timing => `You can skip this ad in ${ Math.ceil(timing.skipOffset - (timing.duration - timing.remaining)) } seconds`,
// ensure countdowns aren't too long, else, hide them
// receives object of all ad bar elements wrapped in PoWaPack objects
checkSizing: adBarElements => {
const $controls = adBarElements.$controls;
const $countdown = adBarElements.$countdown;
const $skipContainer = adBarElements.$skipContainer;
const $skipCountdown = adBarElements.$skipCountdown;
const adControlsRight = $controls.position().left + $controls.width();
const adCountdownRight = $countdown.position().left + $countdown.width();
const adSkipContainerLeft = $skipContainer.position().left;
if (adCountdownRight > adSkipContainerLeft) {
$countdown.addClass(classes.hidden);
}
if (adControlsRight > adSkipContainerLeft) {
$skipCountdown.addClass(classes.hidden);
}
},
// receives adBar = {height, width}, player = {fullscreen, height, width}
shrinkAd: (adBar, player) => {
const sides = (adBar.height * (player.width/player.height)) / 2;
return {
top: adBar.height,
bottom: 0,
left: sides,
right: sides,
height: player.height - adBar.height,
width: player.width - (2 * sides),
};
},
// receives {top, bottom, left, right, height, width} (from shrinkAd)
shrinkAdCSS: shrinkBy => {
return {
top: `${ shrinkBy.top }px`,
left: `${ shrinkBy.left }px`,
width: `${ shrinkBy.width }px`,
height: `${ shrinkBy.height }px`
};
},
style: config => `
.powa-sell-bar {
position: absolute;
top: 0px;
left: 0px;
width: 100%;
height: 22px;
display: flex;
align-items: center;
justify-content: flex-start;
pointer-events: auto;
font-family: Helvetica;
color: rgb(240, 248, 255);
background-color: rgba(0, 0, 0, 0.7);
z-index: 2;
}
.powa-sell-bar-controls {
display: flex;
align-items: center;
justify-content: flex-start;
margin: 0;
padding: 0;
}
.powa-sell-bar-control {
display: flex;
align-items: center;
justify-content: center;
width: 20px;
}
.powa-sell-bar-control:active {
color: rgb(153, 50, 204);
}
.powa-sell-bar-countdown {
font-size: 14px;
font-weight: 400;
line-height: normal;
margin: 0 3px 0 3px;
padding: 0;
}
.powa-sell-bar-skip-container {
position: absolute;
right: 0px;
display: flex;
justify-content: flex-end;
padding: 0 3px 0 3px;
}
.powa-sell-bar-skip-countdown {
font-size: 14px;
font-weight: 400;
line-height: normal;
margin: 0;
padding: 0;
}
.powa-sell-bar-skip {
font-size: 14px;
font-weight: 400;
cursor: pointer;
line-height: normal;
margin: 0;
padding: 0;
}
.powa-sell-bar-skip:hover {
text-decoration: underline;
}
.powa-sell-bar-skip:active {
color: rgb(153, 50, 204);
}
.powa-sell-bar-hidden {
display: none;
}
`,
template: config => `
<div class="powa-sell-bar">
<span class="powa-sell-bar-controls">
<span class="powa-sell-bar-control powa-sell-bar-play-pause powa-click-play-pause-click">
<span class="powa-sell-bar-play powa-sell-bar-hidden" aria-label="Play"><i class="fa fa-play" aria-hidden="true"></i></span>
<span class="powa-sell-bar-pause" aria-label="Pause"><i class="fa fa-pause" aria-hidden="true"></i></span>
</span>
<span class="powa-sell-bar-control powa-sell-bar-mute-unmute powa-click-mute-unmute-click">
<span class="powa-sell-bar-mute" aria-label="Mute"><i class="fa fa-volume-up" aria-hidden="true"></i></span>
<span class="powa-sell-bar-unmute powa-sell-bar-hidden" aria-label="Unmute"><i class="fa fa-volume-off" aria-hidden="true"></i></span>
</span>
</span>
<span class="powa-sell-bar-countdown powa-sell-bar-hidden"></span>
<span class="powa-sell-bar-skip-container">
<span class="powa-sell-bar-skip-countdown powa-sell-bar-hidden"></span>
<span class="powa-sell-bar-skip powa-click-skip-click powa-sell-bar-hidden">Skip</span>
</span>
</div>
`,
};

Ad bar in Spanish without the play or pause control.

window.PoWaSettings.advertising.adBar = {
playControl: false,
adSkipText: 'Omitir anuncio',
adCountdownTemplate: timing => {
return `Anuncio: ${ Math.ceil(timing.remaining) } segundos`;
},
adSkipCountdownTemplate: timing => {
return `Podra saltar el anuncio en ${ Math.ceil(timing.skipOffset - (timing.duration - timing.remaining)) } segundos`;
},
};