Skip to content

Commit

Permalink
feat(Ads): Add control AdsRenderingSettings (#5536)
Browse files Browse the repository at this point in the history
Closes #5011
  • Loading branch information
avelad committed Aug 25, 2023
1 parent 62156ba commit d37143e
Show file tree
Hide file tree
Showing 8 changed files with 144 additions and 10 deletions.
3 changes: 2 additions & 1 deletion demo/main.js
Expand Up @@ -1375,7 +1375,8 @@ shakaDemo.Main = class {
// If that happens, just proceed to load.
goog.asserts.assert(this.video_ != null, 'this.video should exist!');
adManager.initClientSide(
this.controls_.getClientSideAdContainer(), this.video_);
this.controls_.getClientSideAdContainer(), this.video_,
/** adsRenderingSettings= **/ null);
const adRequest = new google.ima.AdsRequest();
adRequest.adTagUrl = asset.adTagUri;
adManager.requestClientSideAds(adRequest);
Expand Down
14 changes: 14 additions & 0 deletions docs/tutorials/ad_monetization.md
Expand Up @@ -81,6 +81,20 @@ See: [google.ima.AdsRequest][] for details on the request object.

[google.ima.AdsRequest]: https://developers.google.com/interactive-media-ads/docs/sdks/html5/v3/reference/js/ima.AdsRequest

Control the rendering of ads:

```js
const adsRenderingSettings = new google.ima.AdsRenderingSettings();
adsRenderingSettings.restoreCustomPlaybackStateOnAdBreakComplete = true;
adManager.initClientSide(container, video, adsRenderingSettings);
// Updates the ads rendering settings.
adManager.updateClientSideAdsRenderingSettings(adsRenderingSettings);
```

See: [google.ima.AdsRenderingSettings][] for details on the request object.

[google.ima.AdsRenderingSettings]: https://developers.google.com/interactive-media-ads/docs/sdks/html5/client-side/reference/js/google.ima.AdsRenderingSettings

#### Streaming with Server Side Ads Insertion

To integrate Server Side ads into a presentation, you need to have a Google Ad
Expand Down
76 changes: 75 additions & 1 deletion externs/ima.js
Expand Up @@ -108,19 +108,93 @@ google.ima.AdsManager = class {

/** @override */
dispatchEvent() {}

/**
* @param {!google.ima.AdsRenderingSettings} adsRenderingSettings
*/
updateAdsRenderingSettings(adsRenderingSettings) {}
};


/** @const */
google.ima.AdsManagerLoadedEvent = class extends Event {
/**
* @param {!HTMLElement} video
* @param {!google.ima.AdsRenderingSettings=} adsRenderingSettings
* @return {!google.ima.AdsManager}
*/
getAdsManager(video) {}
getAdsManager(video, adsRenderingSettings) {}
};


/**
* @typedef {{
* autoAlign: (boolean),
* bitrate: (number),
* enablePreloading: (boolean),
* loadVideoTimeout: (number),
* mimeTypes: (?Array.<string>),
* playAdsAfterTime: (number),
* restoreCustomPlaybackStateOnAdBreakComplete: (boolean),
* uiElements: (?Array.<string>),
* useStyledLinearAds: (boolean),
* useStyledNonLinearAds: (boolean),
* }}
*
* @description Defines parameters that control the rendering of ads.
* @property {boolean} autoAlign
* Set to false if you wish to have fine grained control over
* the positioning of all non-linear ads.
* If this value is true, the ad is positioned in the bottom center.
* If this value is false, the ad is positioned in the top left corner.
* The default value is true.
* @property {number} bitrate
* Maximum recommended bitrate. The value is in kbit/s.
* The SDK will pick media with bitrate below the specified max,
* or the closest bitrate if there is no media with lower bitrate found.
* Default value, -1, means the SDK selects the maximum bitrate.
* @property {boolean} enablePreloading
* Enables preloading of video assets.
* For more info see [our guide to preloading media]{@link https://developers.google.com/interactive-media-ads/docs/sdks/html5/preload}.
* @property {number} loadVideoTimeout
* Timeout (in milliseconds) when loading a video ad media file.
* If loading takes longer than this timeout, the ad playback is canceled
* and the next ad in the pod plays, if available.
* Use -1 for the default of 8 seconds.
* @property {?Array.<string>} mimeTypes
* Only supported for linear video mime types.
* If specified, the SDK will include media that matches
* the MIME type(s) specified in the list and exclude media.
* that does not match the specified MIME type(s).
* The format is a list of strings,
* for example, [ 'video/mp4', 'video/webm', ... ] If not specified,
* the SDK will pick the media based on player capabilities.
* @property {number} playAdsAfterTime
* For VMAP and ad rules playlists, only play ad breaks scheduled
* after this time (in seconds).
* This setting is strictly after - for example,
* setting playAdsAfterTime to 15 will cause IMA to ignore
* an ad break scheduled to play at 15s.
* @property {boolean} restoreCustomPlaybackStateOnAdBreakComplete
* Specifies whether or not the SDK should restore the custom playback
* state after an ad break completes. This is setting is used primarily
* when the publisher passes in its content player to use for
* custom ad playback.
* @property {?Array.<string>} uiElements
* Specifies whether the UI elements that should be displayed.
* The elements in this array are ignored for AdSense/AdX ads.
* @property {boolean} useStyledLinearAds
* Render linear ads with full UI styling.
* This setting does not apply to AdSense/AdX ads
* or ads played in a mobile context that already
* use full UI styling by default.
* @property {boolean} useStyledNonLinearAds
* Render non-linear ads with a close and recall button.
* @exportDoc
*/
google.ima.AdsRenderingSettings;


/** @const */
google.ima.AdDisplayContainer = class {
/**
Expand Down
8 changes: 7 additions & 1 deletion externs/shaka/ads.js
Expand Up @@ -80,14 +80,20 @@ shaka.extern.IAdManager = class extends EventTarget {
/**
* @param {!HTMLElement} adContainer
* @param {!HTMLMediaElement} video
* @param {?google.ima.AdsRenderingSettings} adsRenderingSettings
*/
initClientSide(adContainer, video) {}
initClientSide(adContainer, video, adsRenderingSettings) {}

/**
* @param {!google.ima.AdsRequest} imaRequest
*/
requestClientSideAds(imaRequest) {}

/**
* @param {!google.ima.AdsRenderingSettings} adsRenderingSettings
*/
updateClientSideAdsRenderingSettings(adsRenderingSettings) {}

/**
* @param {!HTMLElement} adContainer
* @param {!HTMLMediaElement} video
Expand Down
20 changes: 18 additions & 2 deletions lib/ads/ad_manager.js
Expand Up @@ -419,7 +419,7 @@ shaka.ads.AdManager = class extends shaka.util.FakeEventTarget {
* @override
* @export
*/
initClientSide(adContainer, video) {
initClientSide(adContainer, video, adsRenderingSettings) {
// Check that Client Side IMA SDK has been included
// NOTE: (window['google'] && google.ima) check for any
// IMA SDK, including SDK for Server Side ads.
Expand All @@ -438,7 +438,7 @@ shaka.ads.AdManager = class extends shaka.util.FakeEventTarget {
}

this.csAdManager_ = new shaka.ads.ClientSideAdManager(
adContainer, video, this.locale_,
adContainer, video, this.locale_, adsRenderingSettings,
(e) => {
const event = /** @type {!shaka.util.FakeEvent} */ (e);
if (event && event.type) {
Expand Down Expand Up @@ -519,6 +519,22 @@ shaka.ads.AdManager = class extends shaka.util.FakeEventTarget {
}


/**
* @override
* @export
*/
updateClientSideAdsRenderingSettings(adsRenderingSettings) {
if (!this.csAdManager_) {
throw new shaka.util.Error(
shaka.util.Error.Severity.RECOVERABLE,
shaka.util.Error.Category.ADS,
shaka.util.Error.Code.CS_AD_MANAGER_NOT_INITIALIZED);
}

this.csAdManager_.updateAdsRenderingSettings(adsRenderingSettings);
}


/**
* @override
* @export
Expand Down
21 changes: 19 additions & 2 deletions lib/ads/client_side_ad_manager.js
Expand Up @@ -24,9 +24,10 @@ shaka.ads.ClientSideAdManager = class {
* @param {HTMLElement} adContainer
* @param {HTMLMediaElement} video
* @param {string} locale
* @param {?google.ima.AdsRenderingSettings} adsRenderingSettings
* @param {function(!shaka.util.FakeEvent)} onEvent
*/
constructor(adContainer, video, locale, onEvent) {
constructor(adContainer, video, locale, adsRenderingSettings, onEvent) {
/** @private {HTMLElement} */
this.adContainer_ = adContainer;

Expand Down Expand Up @@ -72,6 +73,10 @@ shaka.ads.ClientSideAdManager = class {
/** @private {google.ima.AdsManager} */
this.imaAdsManager_ = null;

/** @private {!google.ima.AdsRenderingSettings} */
this.adsRenderingSettings_ =
adsRenderingSettings || new google.ima.AdsRenderingSettings();

this.eventManager_.listenOnce(this.adsLoader_,
google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED, (e) => {
this.onAdsManagerLoaded_(
Expand Down Expand Up @@ -111,6 +116,17 @@ shaka.ads.ClientSideAdManager = class {
this.adsLoader_.requestAds(imaRequest);
}

/**
* @param {!google.ima.AdsRenderingSettings} adsRenderingSettings
*/
updateAdsRenderingSettings(adsRenderingSettings) {
this.adsRenderingSettings_ = adsRenderingSettings;
if (this.imaAdsManager_) {
this.imaAdsManager_.updateAdsRenderingSettings(
this.adsRenderingSettings_);
}
}

/**
* Stop all currently playing ads.
*/
Expand Down Expand Up @@ -172,7 +188,8 @@ shaka.ads.ClientSideAdManager = class {
this.onEvent_(new shaka.util.FakeEvent(shaka.ads.AdManager.ADS_LOADED,
(new Map()).set('loadTime', loadTime)));

this.imaAdsManager_ = e.getAdsManager(this.video_);
this.imaAdsManager_ = e.getAdsManager(this.video_,
this.adsRenderingSettings_);

this.onEvent_(new shaka.util.FakeEvent(
shaka.ads.AdManager.IMA_AD_MANAGER_LOADED,
Expand Down
7 changes: 5 additions & 2 deletions test/ads/ad_manager_unit.js
Expand Up @@ -13,6 +13,8 @@ describe('Ad manager', () => {
let adManager;
/** @type {!HTMLElement} */
let adContainer;
/** @type {google.ima.AdsRenderingSettings} */
let adsRenderingSettings;

beforeEach(() => {
window['google'] = null;
Expand All @@ -35,7 +37,7 @@ describe('Ad manager', () => {
shaka.util.Error.Code.CS_IMA_SDK_MISSING);

expect(() => adManager.initClientSide(
adContainer, mockVideo)).toThrow(error);
adContainer, mockVideo, adsRenderingSettings)).toThrow(error);
});

it('doesn\'t request ads until CS is initialized', () => {
Expand Down Expand Up @@ -138,7 +140,7 @@ describe('Ad manager', () => {
});

// Set up the ad manager.
adManager.initClientSide(adContainer, mockVideo);
adManager.initClientSide(adContainer, mockVideo, adsRenderingSettings);
goog.asserts.assert(loadEvent != null, 'loadEvent exists');
mockAdsLoaderInstance.dispatchEvent(/** @type {!Event} */ (loadEvent));
expect(loaded).toBe(true);
Expand Down Expand Up @@ -199,6 +201,7 @@ describe('Ad manager', () => {
window['google'].ima = {};
window['google'].ima.AdsLoader = {};
window['google'].ima.dai = {};
window['google'].ima.AdsRenderingSettings = class {};
window['google'].ima.AdsRequest = class {};
window['google'].ima.dai.api = {};
window['google'].ima.dai.api.StreamRequest = class {};
Expand Down
5 changes: 4 additions & 1 deletion test/test/util/fake_ad_manager.js
Expand Up @@ -26,7 +26,7 @@ shaka.test.FakeAdManager = class extends shaka.util.FakeEventTarget {
configure(config) {}

/** @override */
initClientSide(adContainer, video) {}
initClientSide(adContainer, video, adsRenderingSettings) {}

/** @override */
onAssetUnload() {}
Expand All @@ -36,6 +36,9 @@ shaka.test.FakeAdManager = class extends shaka.util.FakeEventTarget {
return Promise.resolve('fake:url');
}

/** @override */
updateClientSideAdsRenderingSettings(adsRenderingSettings) {}

/** @override */
initServerSide(adContainer, video) {}

Expand Down

0 comments on commit d37143e

Please sign in to comment.