Skip to content

Commit

Permalink
feat(FEC-10541): add support on working with bidding, Prebid and IMA (#…
Browse files Browse the repository at this point in the history
…412)

Add prebid config to Kaltura player config to allow play prebid ads via IMA plugin and play prebid ad via playAdNow API.
prebid will inject ad tag URL to ad layout config and delay the loading of playback, Prebid can get ad only once.
  • Loading branch information
Yuvalke authored Mar 15, 2021
1 parent 5add08a commit 0f21b24
Show file tree
Hide file tree
Showing 9 changed files with 623 additions and 27 deletions.
117 changes: 101 additions & 16 deletions docs/advertisement-layout-management.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Advertisement layout management

With ad layout config you can create your own ad break timeline using your vast tags.
With ad layout config you can create your own ad break timeline using your vast tags.
Ad break can be set as pre, mid and post rolls and each ad break can contain a single vast tag or multiple tags, either as a pod, but also as a [Waterfall](#waterfalling).

> **Important:** [IMA plugin](https://github.com/kaltura/playkit-js-ima) must be active to enable this feature.
Expand All @@ -15,7 +15,9 @@ Ad break can be set as pre, mid and post rolls and each ad break can contain a s
- [Play Ads After Time](#play-ads-after-time)
- [Events](#events)
- [Play Ad Now](#play-ad-now)
- [Play Ad Now with Prebid](#play-ad-now-with-prebid)
- [Seekbar Cue Points](#seekbar-cue-points)
- [Prebid](#prebid)

### Single Ad

Expand Down Expand Up @@ -56,7 +58,7 @@ const config = {
const kalturaPlayer = KalturaPlayer.setup(config);
```

> In this sample, the player will try to request and play 4 ads.
> In this sample, the player will try to request and play 4 ads.
> Note: `position: 0` means pre-roll. `position: -1` means post-roll.
### Ad Pod
Expand Down Expand Up @@ -103,11 +105,11 @@ const kalturaPlayer = KalturaPlayer.setup(config);
> Waterfalling ("daisy chain") is a process used by publishers selling ads in order to get a better fill-rate.
> This process can be implemented on the ad server itself, but it can also be implemented on the client - which is what we did.
> The process is rather simple: if the first ad server doesn't have any ads to serve, a call to a second server is made etc.
> Meaning, if the first priority server did not "purchase" the impression, it would cascade down to the next server and to the next, until someone bought it.
> The same process is applied also if the ad server returned an error (or the client failed to communicate with it), and then it would cascade to the next server and to the next...
> Meaning, if the first priority server did not "purchase" the impression, it would cascade down to the next server and to the next, until someone bought it.
> The same process is applied also if the ad server returned an error (or the client failed to communicate with it), and then it would cascade to the next server and to the next...
An application may want to set a fallback vast url, so in case the primary ad request had failed, the ad won't be skipped.
This mechanism called _Waterfalling_, is configurable easily using the ad layout config.
An application may want to set a fallback vast url, so in case the primary ad request had failed, the ad won't be skipped.
This mechanism called _Waterfalling_, is configurable easily using the ad layout config.
Here's a sample of a mid-roll with waterfalling:

```js
Expand All @@ -131,19 +133,19 @@ const kalturaPlayer = KalturaPlayer.setup(config);
```

> Important: A fallback url (e.g. MID_ROLL_VAST_URL_2) will be only used if the previous request (MID_ROLL_VAST_URL_1) is failed.
> Hence, In this sample, only one ad will be played.
> Hence, In this sample, only one ad will be played.
> Note: There is no limit to the fallback url list.
### Ad Break Options

Each ad break in the `adBreaks` list gets the following options:

`position: number` - The position, in seconds, to show the ad break.
`percentage?: number` - Alternative parameter to `position`. The position, in percentage of the media length, to show the ad break (optional).
`every?: number` - Alternative parameter to `position`. Play ad break every X seconds (optional).
`position: number` - The position, in seconds, to show the ad break.
`percentage?: number` - Alternative parameter to `position`. The position, in percentage of the media length, to show the ad break (optional).
`every?: number` - Alternative parameter to `position`. Play ad break every X seconds (optional).
`ads: Array<Object>` - An array of ads to play ([Ad pod](#ad-pod)).

> Note. `position`, `percentage` and `every` are several options to configure the ad break position.
> Note. `position`, `percentage` and `every` are several options to configure the ad break position.
> Only one should be provided. If none will be provided, the ad break will be ignored.
> If more than one will be provided, only one configuration will be considered, by the following priority:
>
Expand All @@ -153,9 +155,10 @@ Each ad break in the `adBreaks` list gets the following options:

Each ad in the `ads` list gets the following options:

`url:Array<string>` - List of urls, each one specifies the ad tag url that is requested from the ad server.
`response:Array<string>` - List of XMLs, each one specifies a VAST 2.0 document to be used as the ads response instead of making a request via an ad tag url.
`url:Array<string>` - List of urls, each one specifies the ad tag url that is requested from the ad server.
`response:Array<string>` - List of XMLs, each one specifies a VAST 2.0 document to be used as the ads response instead of making a request via an ad tag url.
`bumper:boolean` - Specifies whether this is a bumper. `false` by default.
`prebid:KPAdPrebidConfig` - Specifies whether this is a prebid ad and add the relevant config for prebid request.

All the options above work together, that means the application may use and mix them in the same `advertising` object.
Here's a sample:
Expand Down Expand Up @@ -207,7 +210,7 @@ const kalturaPlayer = KalturaPlayer.setup(config);

### Play Ads After Time

An application may want to configure the player to play ads only from a specific time.
An application may want to configure the player to play ads only from a specific time.
This can be achieved by `playAdsAfterTime` parameter. For example:

```js
Expand Down Expand Up @@ -273,14 +276,14 @@ In this sample, the player will play the pre-roll, and only after that will star

In addition to the current [ad events](./ads.md#ad-events) (`adbreakstart, adbreakend, adloaded` etc.) we have added 2 new events:

`adwaterfalling` - Fired when an ad request failed and the player is trying to request the fallback.
`adwaterfalling` - Fired when an ad request failed and the player is trying to request the fallback.
`adwaterfallingfailed` - Fired when the all fallback requests failed.

> Note: `aderror` will not be fired with `adwaterfalling` but with `adwaterfallingfailed`.
### Play Ad Now

All the above features are supported not only by `advertising` config, but also by `playAdNow` api which gets an ad pod as a parameter.
All the above features are supported not only by `advertising` config, but also by `playAdNow` api which gets an ad pod as a parameter.
The app may call it whenever it wants to play an ad pod.

```js
Expand All @@ -303,6 +306,45 @@ kalturaPlayer.ads.playAdNow(
]);
```

### Play Ad Now with Prebid

All the above features are supported not only by `advertising` config, but also by `playAdNow` api which gets an ad pod as a parameter.
The app may call it whenever it wants to play an ad pod with prebid.

```js
const config = {
...
plugins: {
ima: {}
},
advertising: {
prebid:{
libUrl: 'https://acdn.adnxs.com/prebid/not-for-prod/3/prebid.js',
}
}
}
const kalturaPlayer = KalturaPlayer.setup(config);
kalturaPlayer.play();
... // playback
kalturaPlayer.ads.playAdNow(
[
{
url: [MID_ROLL_1_VAST_URL_1, MID_ROLL_1_VAST_URL_2, ...],
prebid: {
adUnit: {
//configuration for bidders
},
params: {
//params for dfp in prebid
},
options: {
//configuration for prebid cache url and etc.
}
}
}
]);
```

### Seekbar Cue Points

To display cue points on the seekbar to indicates the ad break positions use `showAdBreakCuePoint` option, as following:
Expand Down Expand Up @@ -355,4 +397,47 @@ const config = {
}
```

### Prebid

For prebid configuration there are two places to config:

1. top level config - set the general configuration for prebid - libUrl/timeout, also configure adUnit/params/options could be relevant for all prebid ads like same cache url for or same mediaTypes setting.
2. ad level config - adUnit/params/options for ad configuration, timeout could be set different value per an ad.

Prebid can use as pre-roll/mid-roll/post-roll return list of ads and use as waterfall
Here is sample for midroll position 60 and url provided will be the fallback for prebid configuration.

```js
const config = {
...
plugins: {
ima: {}
},
advertising: {
prebid:{
libUrl: 'https://acdn.adnxs.com/prebid/not-for-prod/3/prebid.js',
}
adBreaks: [{
position: 60,
ads: [{
url: [MID_ROLL_VAST_URL],
prebid: {
adUnit: {
//configuration for bidders
},
params: {
//params for dfp in prebid
},
options: {
//configuration for prebid cache url and etc.
}
}
}]
}
]
}
...
}
```

All style options are listed [here](https://github.com/kaltura/playkit-js-timeline/blob/main/docs/types.md#cuepointoptionsobject).
6 changes: 5 additions & 1 deletion flow-typed/types/advertising.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
* @property {Array<string>} url - List of urls, each one specifies the ad tag url that is requested from the ad server. The player will request the first url, if failed, it will request the second url and so on (aka waterfalling).
* @property {Array<string>} response - List of XMLs, each one specifies a VAST 2.0 document to be used as the ads response instead of making a request via an ad tag url. The player will use the first XML, if failed, it will use the second and so on (aka waterfalling).
* @property {boolean} bumper - Specifies whether this is a bumper.
* @property {KPAdPrebidConfig} prebid - Specifies whether this is a prebid ad and add the relevant config for prebid request.
*/
type _KPAdObject = {
url?: Array<string>,
response?: Array<string>,
bumper?: boolean
bumper?: boolean,
prebid?: KPAdPrebidConfig
};
declare type KPAdObject = _KPAdObject;

Expand All @@ -36,12 +38,14 @@ declare type KPAdBreakObject = _KPAdBreakObject;

/**
* @typedef {Object} KPAdvertisingConfigObject
* @property {KPPrebidConfig} prebid - The prebid config.
* @property {Array<KPAdBreakObject>} adBreaks - The ad breaks scheme.
* @property {number} [playAdsAfterTime] - Only play ad breaks scheduled after this time (in seconds). This setting is strictly after - e.g. setting playAdsAfterTime to 15 will cause the player to ignore an ad break scheduled to play at 15s.
* @property {boolean} [showAdBreakCuePoint] - Whether to show the ad breaks cue points.
* @property {Object} [adBreakCuePointStyle] - Style options for the ad breaks cue points - See the options {@link https://github.com/kaltura/playkit-js-timeline/blob/main/docs/types.md#cuepointoptionsobject|Here}.
*/
type _KPAdvertisingConfigObject = {
prebid?: KPPrebidConfig,
adBreaks: Array<KPAdBreakObject>,
playAdsAfterTime?: number,
showAdBreakCuePoint?: boolean,
Expand Down
12 changes: 12 additions & 0 deletions flow-typed/types/prebid-config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// @flow

declare type KPAdPrebidConfig = {
adUnit: Object,
params?: Object,
options?: Object,
timeout: number
};

declare type KPPrebidConfig = KPAdPrebidConfig & {
libUrl: string
};
88 changes: 88 additions & 0 deletions samples/ovp/prebid.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
<title>Title</title>
<link rel="stylesheet" type="text/css" href="./style.css"/>
<script src="./kaltura-ovp-player.js" type="text/javascript"></script>
<script src="http://location-for-ima/playkit-ima.js" type="text/javascript"></script>
</head>
<body>
<div id="player-placeholder"></div>
<script>
var config = {
logLevel:'DEBUG',
targetId: 'player-placeholder',
provider: {
partnerId: 1091,
env: {
cdnUrl: "https://qa-apache-php7.dev.kaltura.com/",
serviceUrl: "https://qa-apache-php7.dev.kaltura.com/api_v3"
},
},
advertising: {
prebid: {
libUrl: 'https://acdn.adnxs.com/prebid/not-for-prod/3/prebid.js'
},
adBreaks: [
{
position: 0,
ads: [{
url: ['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='],
prebid:{
adUnit: {
code: 'video1',
mediaTypes: {
video: {
playerSize: [640, 480],
context: 'instream',
mimes: ['video/mp4'],
protocols: [1, 2, 3, 4, 5, 6, 7, 8],
playbackmethod: [2]
}
},
bids: [{
bidder: 'appnexus',
params: {
placementId: 13232361, // Add your own placement id here
video: {
skippable: true,
playback_method: ['auto_play_sound_off']
}
}
}
]
},
options: {
cache: {
url: 'https://prebid.adnxs.com/pbc/v1/cache'
},
debug: true,
enableSendAllBids: true,
s2sConfig: {
endpoint: 'http://prebid.adnxs.com/pbs/v1/openrtb2/auction',
enabled: true,
accountId: 'c9d412ee-3cc6-4b66-9326-9f49d528f13e',
bidders: ['appnexus']
}
}
}
}]
}
]
},
plugins: {
ima: {}
}
};

try {
var kalturaPlayer = KalturaPlayer.setup(config);
kalturaPlayer.loadMedia({entryId: '0_wifqaipd'});
} catch (e) {
console.error(e.message)
}
</script>
</body>
</html>
53 changes: 53 additions & 0 deletions src/common/ads/ad-layout-middleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// @flow
import {BaseMiddleware} from '@playkit-js/playkit-js';
import {AdsController} from '../controllers';

/**
* Middleware implementation for ima plugin.
* @class AdLayoutMiddleware
* @param {Ima} context - The ima plugin context.
* @private
*/
class AdLayoutMiddleware extends BaseMiddleware {
/**
* The id of the ima middleware.
* @type {string}
* @public
* @memberof AdLayoutMiddleware
*/
id: string = 'AdLayoutMiddleware';
/**
* The plugin context.
* @member
* @private
* @memberof AdLayoutMiddleware
*/
_context: AdsController;

constructor(context: AdsController) {
super();
this._context = context;
}

/**
* Load middleware handler.
* @param {Function} next - The load play handler in the middleware chain.
* @returns {void}
* @memberof AdLayoutMiddleware
*/
load(next: Function): void {
this._context.prerollReady.then(() => this.callNext(next));
}

/**
* Play middleware handler.
* @param {Function} next - The next play handler in the middleware chain.
* @returns {void}
* @memberof AdLayoutMiddleware
*/
play(next: Function): void {
this._context.prerollReady.then(() => this.callNext(next));
}
}

export {AdLayoutMiddleware};
Loading

0 comments on commit 0f21b24

Please sign in to comment.