diff --git a/lib/ads/ad_manager.js b/lib/ads/ad_manager.js index 7f71378fed..d641756fb9 100644 --- a/lib/ads/ad_manager.js +++ b/lib/ads/ad_manager.js @@ -21,6 +21,7 @@ goog.require('shaka.util.Error'); goog.require('shaka.util.FakeEvent'); goog.require('shaka.util.FakeEventTarget'); goog.require('shaka.util.IReleasable'); +goog.require('shaka.util.TXml'); /** @@ -843,14 +844,22 @@ shaka.ads.AdManager = class extends shaka.util.FakeEventTarget { if (this.config_ && this.config_.disableDASHInterstitial) { return; } - if (region.schemeIdUri != 'urn:mpeg:dash:event:alternativeMPD:2022') { - return; - } - if (!this.interstitialAdManager_) { - this.initInterstitial(/* adContainer= */ null, basePlayer, baseVideo); - } - if (this.interstitialAdManager_) { - this.interstitialAdManager_.addRegion(region); + if (region.schemeIdUri == 'urn:mpeg:dash:event:alternativeMPD:2022') { + if (!this.interstitialAdManager_) { + this.initInterstitial(/* adContainer= */ null, basePlayer, baseVideo); + } + if (this.interstitialAdManager_) { + this.interstitialAdManager_.addRegion(region); + } + } else if (region.schemeIdUri == 'urn:mpeg:dash:event:2012' && + region.eventNode && + shaka.util.TXml.findChild(region.eventNode, 'OverlayEvent')) { + if (!this.interstitialAdManager_) { + this.initInterstitial(/* adContainer= */ null, basePlayer, baseVideo); + } + if (this.interstitialAdManager_) { + this.interstitialAdManager_.addOverlayRegion(region); + } } } diff --git a/lib/ads/interstitial_ad_manager.js b/lib/ads/interstitial_ad_manager.js index f32881651c..4dfd57421a 100644 --- a/lib/ads/interstitial_ad_manager.js +++ b/lib/ads/interstitial_ad_manager.js @@ -359,6 +359,99 @@ shaka.ads.InterstitialAdManager = class { this.addInterstitials([interstitial]); } + /** + * @param {shaka.extern.TimelineRegionInfo} region + */ + addOverlayRegion(region) { + const TXml = shaka.util.TXml; + + goog.asserts.assert(region.eventNode, 'Need a region eventNode'); + const overlayEvent = TXml.findChild(region.eventNode, 'OverlayEvent'); + const uri = overlayEvent.attributes['uri']; + const mimeType = overlayEvent.attributes['mimeType']; + const loop = overlayEvent.attributes['loop'] == 'true'; + if (!uri || !mimeType) { + shaka.log.warning('Unsupported OverlayEvent', region); + return; + } + + const viewport = TXml.findChild(overlayEvent, 'Viewport'); + const topLeft = TXml.findChild(overlayEvent, 'TopLeft'); + const size = TXml.findChild(overlayEvent, 'Size'); + if (!viewport || !topLeft || !size) { + shaka.log.warning('Unsupported OverlayEvent', region); + return; + } + const viewportX = TXml.parseAttr(viewport, 'x', TXml.parseInt); + if (viewportX == null) { + shaka.log.warning('Unsupported OverlayEvent', region); + return; + } + const viewportY = TXml.parseAttr(viewport, 'y', TXml.parseInt); + if (viewportY == null) { + shaka.log.warning('Unsupported OverlayEvent', region); + return; + } + const topLeftX = TXml.parseAttr(topLeft, 'x', TXml.parseInt); + if (topLeftX == null) { + shaka.log.warning('Unsupported OverlayEvent', region); + return; + } + const topLeftY = TXml.parseAttr(topLeft, 'y', TXml.parseInt); + if (topLeftY == null) { + shaka.log.warning('Unsupported OverlayEvent', region); + return; + } + const sizeX = TXml.parseAttr(size, 'x', TXml.parseInt); + if (sizeX == null) { + shaka.log.warning('Unsupported OverlayEvent', region); + return; + } + const sizeY = TXml.parseAttr(size, 'y', TXml.parseInt); + if (sizeY == null) { + shaka.log.warning('Unsupported OverlayEvent', region); + return; + } + + /** @type {!shaka.extern.AdInterstitialOverlay} */ + const overlay = { + viewport: { + x: viewportX, + y: viewportY, + }, + topLeft: { + x: topLeftX, + y: topLeftY, + }, + size: { + x: sizeX, + y: sizeY, + }, + }; + + /** @type {!shaka.extern.AdInterstitial} */ + const interstitial = { + id: region.id, + startTime: region.startTime, + endTime: region.endTime, + uri, + mimeType, + isSkippable: false, + skipOffset: null, + skipFor: null, + canJump: true, + resumeOffset: null, + playoutLimit: null, + once: false, + pre: false, + post: false, + timelineRange: true, + loop, + overlay, + }; + this.addInterstitials([interstitial]); + } + /** * @param {string} url * @return {!Promise} diff --git a/test/ads/interstitial_ad_manager_unit.js b/test/ads/interstitial_ad_manager_unit.js index bcb94e5c5c..4627ec717d 100644 --- a/test/ads/interstitial_ad_manager_unit.js +++ b/test/ads/interstitial_ad_manager_unit.js @@ -818,6 +818,68 @@ describe('Interstitial Ad manager', () => { expect(onEventSpy).not.toHaveBeenCalled(); }); + + it('supports overlay events', async () => { + const eventString = [ + '', + '', + '', + '', + '', + '', + '', + ].join(''); + const eventNode = TXml.parseXmlString(eventString); + goog.asserts.assert(eventNode, 'Should have a event node!'); + const region = { + startTime: 0, + endTime: 1, + id: 'OVERLAY', + schemeIdUri: 'urn:mpeg:dash:event:2012', + eventNode, + eventElement: TXml.txmlNodeToDomElement(eventNode), + value: '', + }; + await interstitialAdManager.addOverlayRegion(region); + + expect(onEventSpy).not.toHaveBeenCalled(); + + const interstitials = interstitialAdManager.getInterstitials(); + expect(interstitials.length).toBe(1); + const expectedInterstitial = { + id: 'OVERLAY', + startTime: 0, + endTime: 1, + uri: 'test.mpd', + mimeType: 'application/dash+xml', + isSkippable: false, + skipOffset: null, + skipFor: null, + canJump: true, + resumeOffset: null, + playoutLimit: null, + once: false, + pre: false, + post: false, + timelineRange: true, + loop: false, + overlay: { + viewport: { + x: 1920, + y: 1080, + }, + topLeft: { + x: 0, + y: 720, + }, + size: { + x: 480, + y: 360, + }, + }, + }; + expect(interstitials[0]).toEqual(expectedInterstitial); + }); }); describe('custom', () => {