Advertising¶
PRESTOplay for web includes a built-in ads API plus ready-to-use plugins for popular ad providers, so you can insert client-side and server-side ads without building ad tracking from scratch.
PRESTOplay includes integrations for Google Interactive Media Ads (IMA), Yospace, and FreeWheel AdManager, plus support for HLS interstitials.
Ads API¶
Ads manager¶
Call getAdsManager() on a Player
instance to access IAdsManager. Call this method
after the open() promise resolves. From that point,
you can control the currently playing ad.
Ad events¶
When ads are active, the player emits ad-related events. Attach ad event listeners to the player instance:
player.on(clpp.events.ADS_TIMELINE_CHANGED, event => {
console.log('==> ad-timeline-changed', event.adsTimeline.getCuePoints());
});
player.on(clpp.events.AD_LOADED, event => {
console.log('==> ad-loaded', event.ad);
});
Useful ad events include:
clpp.events.ADS_TIMELINE_CHANGEDclpp.events.AD_LOADEDclpp.events.AD_BREAK_STARTEDclpp.events.AD_STARTEDclpp.events.AD_BUFFERINGclpp.events.AD_PROGRESSclpp.events.AD_FIRST_QUARTILEclpp.events.AD_MIDPOINTclpp.events.AD_THIRD_QUARTILEclpp.events.AD_PAUSEDclpp.events.AD_RESUMEDclpp.events.AD_SKIPPEDclpp.events.AD_CLICKEDclpp.events.AD_IMPRESSIONclpp.events.AD_COMPLETEDclpp.events.AD_STOPPEDclpp.events.AD_BREAK_STOPPED
See Events for general event-handling patterns.
Google IMA plugin¶
The SDK includes a Google Interactive Media Ads (IMA) plugin for client-side ads.
To get started, add the IMA library and player styles, and import the IMA plugin:
import "@castlabs/prestoplay/cl.ima";
<link rel="stylesheet" href="clpp.styles.css">
<script src="https://imasdk.googleapis.com/js/sdkloader/ima3.js"></script>
Pass a Video Multiple Ad Playlist (VMAP) ad tag to the player:
player.open({
// ...
ima: {
adTagUrl: '//pubads.g.doubleclick.net/...'
}
});
Pass a Video Ad Serving Template (VAST) ad tag to the player:
player.open({
// ...
ima: {
adTagUrl: '//pubads.g.doubleclick.net/...',
isVast: true
}
});
See ImaConfiguration for all available configuration options.
The Google IMA sample plays
pre-roll, mid-roll, and post-roll ads from a VMAP ad tag and logs ad events,
including the ad details available through IAd, on the page.
IMA SDK on smart TVs¶
For smart TV-specific limitations, including skippable ads, see Smart TV.
IMA SDK on iOS¶
The IMA plugin is supported on iOS in experimental mode, for the same reasons as on smart TVs.
Custom UI controls give a better user experience than built-in video controls on iOS.
See ImaConfiguration and its disableCustomPlayback option to
disable ads on iOS devices if you run into issues.
Yospace plugin¶
To get started with Yospace, download the Yospace ad management SDK from the Yospace developer site. The currently supported version is 3.9.0. Other similar versions may also work, but compatibility is not guaranteed.
Import the Yospace SDK:
<script src="./lib/yospace/admanagement-sdk-3.9.0.min.js"></script>
Import the Yospace plugin:
import { clpp } from "@castlabs/prestoplay";
import "@castlabs/prestoplay/cl.yospace";
Enable the Yospace plugin when calling open() with a Yospace Central
Session Manager (CSM) uniform resource identifier (URI). This automatically
starts a Yospace session. source.type must be specified:
await player.open({
source: {
// Yospace CSM URI
url: 'https://csm...',
type: clpp.Type.HLS,
},
yospace: {
enabled: true,
streamType: clpp.yospace.StreamType.VOD
}
});
Set up listeners for Yospace-related events:
player.on(clpp.events.YOSPACE_SESSION_STARTED, (e) => {
// e.detail contains the YospaceAdManagement.Session object
});
player.on(clpp.events.YOSPACE_SESSION_ENDED, (e) => {
// e.detail contains the YospaceAdManagement.Session object
});
await player.open({
source: '<manifest_url>',
});
Once the video starts playing, call getSession() on the plugin instance to
get the YospaceAdManagement.Session:
const plugin = player.getPlugin(clpp.yospace.YospacePlugin.Id);
const session = plugin.getSession();
FreeWheel plugin (beta)¶
To configure the FreeWheel plugin:
Add the FreeWheel AdManager SDK and player styles, and import the FreeWheel plugin:
import "@castlabs/prestoplay/cl.freewheel";
<link rel="stylesheet" href="clpp.styles.css">
<script src="//adm.stg.fwmrm.net/libs/adm/6.37.0/AdManager.js"></script>
Add the FreeWheel customer data to the player configuration:
const playerConfig = {
// ...
freewheel: {
networkId: 42015,
serverUrl: '//demo.v.fwmrm.net/ad/g/1',
profileId: '42015:js_allinone_profile',
videoAssetId: 'js_allinone_demo_video',
pods: [
{ id: 'Preroll_1', position: 0, type: clpp.ads.PodType.PREROLL },
{ id: 'Midroll_1', position: 3, type: clpp.ads.PodType.MIDROLL },
{ id: 'Midroll_2', position: 30, type: clpp.ads.PodType.MIDROLL },
{ id: 'Postroll_1', position: 60, type: clpp.ads.PodType.POSTROLL }
]
}
};
See FreeWheelConfiguration for all available configuration
options.
The whole code can look like this:
<link rel="stylesheet" href="clpp.styles.css">
<script src="//adm.stg.fwmrm.net/libs/adm/6.37.0/AdManager.js"></script>
<video id="video" controls></video>
<button id="playButton">Play</button>
<script type="module">
import { clpp } from "@castlabs/prestoplay";
import "@castlabs/prestoplay/cl.mse";
import "@castlabs/prestoplay/cl.freewheel";
const player = new clpp.Player('video', ...);
player.open({
source: '//demo.cf.castlabs.com/media/prestomp4/presto.mp4',
freewheel: {
networkId: 42015,
serverUrl: '//demo.v.fwmrm.net/ad/g/1',
profileId: '42015:js_allinone_profile',
videoAssetId: 'js_allinone_demo_video',
pods: [
{ id: 'Preroll_1', position: 0, type: clpp.ads.PodType.PREROLL },
{ id: 'Midroll_1', position: 3, type: clpp.ads.PodType.MIDROLL },
{ id: 'Midroll_2', position: 30, type: clpp.ads.PodType.MIDROLL },
{ id: 'Postroll_1', position: 60, type: clpp.ads.PodType.POSTROLL }
]
}
});
// Listen to ad events the same way as in the IMA example.
document.getElementById('playButton').addEventListener('click', () => {
player.play();
});
</script>
Broadpeak SSAI ads¶
The Broadpeak plugin also supports server-side ad insertion (SSAI) tracking. See Broadpeak SSAI ads on the Analytics page.
HLS interstitials¶
Support for HLS interstitials is limited to modern browsers. To enable this
feature on Safari, set preferNativeHlsOnSafari to false:
const player = new clpp.Player('video', {
streaming: {
preferNativeHlsOnSafari: false,
}
});
You can disable this feature entirely:
const player = new clpp.Player('video', {
ads: {
hls: {
enable: false,
},
},
});
See HlsAdsConfiguration for other configuration options.
Playback session ID¶
To give decisioning servers useful context, you can enable playback session identification:
const player = new clpp.Player('video', {
mse: {
sendPlaybackSessionIdHeader: true,
}
});
With this in place, the player sends the X-Playback-Session-Id HTTP header
with every HTTP request related to primary playback, and the
_HLS_primary_id query parameter containing the same value with every
interstitial request.
Note
For this to work, your primary content server must allow the HTTP header
X-Playback-Session-Id in Access-Control-Allow-Headers.
Rendering UI¶
To render UI related to ads, listen to ad events and use the event payload
(IAd) to get information about the currently playing ad.
player.on(clpp.events.AD_STARTED, event => {
const position = event.ad.getPositionInSequence();
const count = event.ad.getSequenceLength();
adLabel = `Ad ${position} / ${count}`;
});
player.on(clpp.events.AD_BREAK_STOPPED, () => {
adLabel = null;
});
To render a play/pause button, listen to play and pause events as well as ad
paused, resumed, and started. In all these cases the return value of
isPaused() may change.
function renderPlayButton() {
if (player.isPaused()) {
playButton.textContent = 'Play';
} else {
playButton.textContent = 'Pause';
}
}
player.on('play', () => {
renderPlayButton();
});
player.on('pause', () => {
renderPlayButton();
});
player.on(clpp.events.AD_STARTED, (e) => {
renderPlayButton();
});
player.on(clpp.events.AD_PAUSED, () => {
renderPlayButton();
});
player.on(clpp.events.AD_RESUMED, () => {
renderPlayButton();
});
Seek UI during ads¶
Some ad formats do not allow seeking. Use canSeek() on the current ad
(IAd#canSeek) to determine whether the seek bar
and any other seek or playback rate controls should be enabled.
function renderSeekBar(ad) {
const seekEnabled = ad ? ad.canSeek() : true;
seekBar.disabled = !seekEnabled;
playbackRateSelect.disabled = !seekEnabled;
// Hide or disable any other seek or playback rate controls accordingly.
}
player.on(clpp.events.AD_STARTED, event => {
// Check and possibly disable seeking when the ad starts.
renderSeekBar(event.ad);
});
player.on(clpp.events.AD_BREAK_STOPPED, () => {
// Re-enable seek when the ad break ends.
renderSeekBar(null);
});
Skippable ads¶
Use getSkipTimeOffset() to determine
whether an ad is skippable. It returns the number of seconds of playback
before the ad can be skipped, or -1 if the ad is not skippable.
To skip the current ad, call skip() on the
ads manager.
Here’s an example of rendering a skip button in the UI:
let currentAd = null;
player.on(clpp.events.AD_STARTED, event => {
currentAd = event.ad;
renderSkipButton();
});
player.on(clpp.events.AD_PROGRESS, () => {
renderSkipButton();
});
player.on(clpp.events.AD_BREAK_STOPPED, () => {
currentAd = null;
skipButton.style.display = 'none';
});
function renderSkipButton() {
if (!currentAd) return;
const skipOffset = currentAd.getSkipTimeOffset();
if (skipOffset === -1) {
// Ad is not skippable.
skipButton.style.display = 'none';
return;
}
const remaining = skipOffset - currentAd.getPosition();
if (remaining <= 0) {
skipButton.textContent = 'Skip Ad';
skipButton.disabled = false;
} else {
skipButton.textContent = `Skip in ${Math.ceil(remaining)}s`;
skipButton.disabled = true;
}
skipButton.style.display = 'block';
}
skipButton.addEventListener('click', () => {
player.getAdsManager().skip();
});