UNPKG

shaka-player

Version:
896 lines (744 loc) 21.3 kB
/*! @license * Shaka Player * Copyright 2016 Google LLC * SPDX-License-Identifier: Apache-2.0 */ goog.provide('shaka.ads.AdManager'); goog.provide('shaka.ads.CuePoint'); goog.require('shaka.Player'); goog.require('shaka.ads.AdsStats'); goog.require('shaka.ads.ClientSideAdManager'); goog.require('shaka.ads.ServerSideAdManager'); goog.require('shaka.log'); goog.require('shaka.util.Error'); goog.require('shaka.util.FakeEvent'); goog.require('shaka.util.FakeEventTarget'); /** * @event shaka.ads.AdManager.ADS_LOADED * @description Fired when an ad has started playing. * @property {string} type * 'ads-loaded' * @property {number} loadTime * The time it takes to load ads. * @exportDoc */ /** * @event shaka.ads.AdManager.AdStartedEvent * @description Fired when an ad has started playing. * @property {string} type * 'ad-started' * @property {!shaka.extern.IAd} ad * The ad that has started playing. * @property {Object} sdkAdObject * The ad object in the SDK format, if there is one. * @property {Object} originalEvent * The native SDK event, if available. * @exportDoc */ /** * @event shaka.ads.AdManager.AdCompleteEvent * @description Fired when an ad has played through. * @property {string} type * 'ad-complete' * @property {Object} originalEvent * The native SDK event, if available. * @exportDoc */ /** * @event shaka.ads.AdManager.AdSkippedEvent * @description Fired when an ad has been skipped. * @property {string} type * 'ad-skipped' * @property {Object} originalEvent * The native SDK event, if available. * @exportDoc */ /** * @event shaka.ads.AdManager.AdFirstQuartileEvent * @description Fired when an ad has played through the first 1/4. * @property {string} type * 'ad-first-quartile' * @property {Object} originalEvent * The native SDK event, if available. * @exportDoc */ /** * @event shaka.ads.AdManager.AdMidpointEvent * @description Fired when an ad has played through its midpoint. * @property {string} type * 'ad-midpoint' * @property {Object} originalEvent * The native SDK event, if available. * @exportDoc */ /** * @event shaka.ads.AdManager.AdThirdQuartileEvent * @description Fired when an ad has played through the third quartile. * @property {string} type * 'ad-third-quartile' * @property {Object} originalEvent * The native SDK event, if available. * @exportDoc */ /** * @event shaka.ads.AdManager.AdStoppedEvent * @description Fired when an ad has stopped playing, was skipped, * or was unable to proceed due to an error. * @property {string} type * 'ad-stopped' * @property {Object} originalEvent * The native SDK event, if available. * @exportDoc */ /** * @event shaka.ads.AdManager.AdVolumeChangedEvent * @description Fired when an ad's volume changed. * @property {string} type * 'ad-volume-changed' * @property {Object} originalEvent * The native SDK event, if available. * @exportDoc */ /** * @event shaka.ads.AdManager.AdMutedEvent * @description Fired when an ad was muted. * @property {string} type * 'ad-muted' * @property {Object} originalEvent * The native SDK event, if available. * @exportDoc */ /** * @event shaka.ads.AdManager.AdPausedEvent * @description Fired when an ad was paused. * @property {string} type * 'ad-paused' * @property {Object} originalEvent * The native SDK event, if available. * @exportDoc */ /** * @event shaka.ads.AdManager.AdResumedEvent * @description Fired when an ad was resumed after a pause. * @property {string} type * 'ad-resumed' * @property {Object} originalEvent * The native SDK event, if available. * @exportDoc */ /** * @event shaka.ads.AdManager.AdSkipStateChangedEvent * @description Fired when an ad's skip state changes (for example, when * it becomes possible to skip the ad). * @property {string} type * 'ad-skip-state-changed' * @property {Object} originalEvent * The native SDK event, if available. * @exportDoc */ /** * @event shaka.ads.AdManager.AdResumedEvent * @description Fired when the ad cue points change, signalling ad breaks * change. * @property {string} type * 'ad-cue-points-changed' * @property {Object} originalEvent * The native SDK event, if available. * @exportDoc */ /** * @event shaka.ads.AdManager.AdProgressEvent * @description Fired when there is an update to the current ad's progress. * @property {string} type * 'ad-progress' * @property {Object} originalEvent * The native SDK event, if available. * @exportDoc */ /** * @event shaka.ads.AdManager.AdBufferingEvent * @description Fired when the ad has stalled playback to buffer. * @property {string} type * 'ad-buffering' * @property {Object} originalEvent * The native SDK event, if available. * @exportDoc */ /** * @event shaka.ads.AdManager.AdImpressionEvent * @description Fired when the impression URL has been pinged. * @property {string} type * 'ad-impression' * @property {Object} originalEvent * The native SDK event, if available. * @exportDoc */ /** * @event shaka.ads.AdManager.AdClickEvent * @description Fired when the ad was clicked. * @property {string} type * 'ad-clicked' * @property {Object} originalEvent * The native SDK event, if available. * @exportDoc */ /** * @event shaka.ads.AdManager.AdDurationChangedEvent * @description Fired when the ad's duration changes. * @property {string} type * 'ad-duration-changed' * @property {Object} originalEvent * The native SDK event, if available. * @exportDoc */ /** * @event shaka.ads.AdManager.AdClosedEvent * @description Fired when the ad was closed by the user. * @property {string} type * 'ad-closed' * @property {Object} originalEvent * The native SDK event, if available. * @exportDoc */ /** * @event shaka.ads.AdManager.AdLoadedEvent * @description Fired when the ad data becomes available. * @property {string} type * 'ad-loaded' * @property {Object} originalEvent * The native SDK event, if available. * @exportDoc */ /** * @event shaka.ads.AdManager.AllAdsCompletedEvent * @description Fired when the ads manager is done playing all the ads. * @property {string} type * 'all-ads-completed' * @property {Object} originalEvent * The native SDK event, if available. * @exportDoc */ /** * @event shaka.ads.AdManager.AdLinearChangedEvent * @description Fired when the displayed ad changes from * linear to nonlinear, or vice versa. * @property {string} type * 'ad-linear-changed' * @property {Object} originalEvent * The native SDK event, if available. * @exportDoc */ /** * @event shaka.ads.AdManager.AdMetadataEvent * @description Fired when the ad's metadata becomes available. * @property {string} type * 'ad-metadata' * @property {Object} originalEvent * The native SDK event, if available. * @exportDoc */ /** * @event shaka.ads.AdManager.AdBreakReadyEvent * @description Fired when the client-side SDK is ready to play a * VPAID ad or an ad rule. * @property {string} type * 'ad-break-ready' * @property {Object} originalEvent * The native SDK event, if available. * @exportDoc */ /** * @event shaka.ads.AdManager.AdRecoverableErrorEvent * @description Fired when the a non-fatal error was encountered. * The presentation will continue with the same or next ad playback * depending on the error situation. * @property {string} type * 'ad-recoverable-error' * @property {Object} originalEvent * The native SDK event, if available. * @exportDoc */ /** * @event shaka.ads.AdManager.AdInteractionEvent * @description Fired when an ad triggers the interaction callback. * @property {string} type * 'ad-interaction' * @property {Object} originalEvent * The native SDK event, if available. * @exportDoc */ /** * @event shaka.ads.AdManager.ImaAdManagerLoadedEvent * @description Fired when the native IMA ad manager becomes available. * @property {string} type * 'ima-ad-manager-loaded' * @exportDoc */ /** * @event shaka.ads.AdManager.AdClickedEvent * @description Fired when the ad was clicked. * @property {string} type * 'ad-clicked' * @exportDoc */ /** * A class responsible for ad-related interactions. * @implements {shaka.extern.IAdManager} * @export */ shaka.ads.AdManager = class extends shaka.util.FakeEventTarget { constructor() { super(); /** @private {shaka.ads.ClientSideAdManager} */ this.csAdManager_ = null; /** @private {shaka.ads.ServerSideAdManager} */ this.ssAdManager_ = null; /** @private {shaka.ads.AdsStats} */ this.stats_ = new shaka.ads.AdsStats(); /** @private {string} locale */ this.locale_ = navigator.language; } /** * @override * @export */ setLocale(locale) { this.locale_ = locale; } /** * @override * @export */ initClientSide(adContainer, video) { // 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. // The 3rd check insures we have the right SDK: // {google.ima.AdsLoader} is an object that's part of CS IMA SDK // but not SS SDK. if (!window['google'] || !google.ima || !google.ima.AdsLoader) { throw new shaka.util.Error( shaka.util.Error.Severity.CRITICAL, shaka.util.Error.Category.ADS, shaka.util.Error.Code.CS_IMA_SDK_MISSING); } this.csAdManager_ = new shaka.ads.ClientSideAdManager( adContainer, video, this.locale_, (e) => { const event = /** @type {!shaka.util.FakeEvent} */ (e); if (event && event.type) { switch (event.type) { case shaka.ads.AdManager.ADS_LOADED: { const loadTime = (/** @type {!Object} */ (e))['loadTime']; this.stats_.addLoadTime(loadTime); break; } case shaka.ads.AdManager.AD_STARTED: this.stats_.incrementStarted(); break; case shaka.ads.AdManager.AD_COMPLETE: this.stats_.incrementPlayedCompletely(); break; case shaka.ads.AdManager.AD_SKIPPED: this.stats_.incrementSkipped(); break; } } this.dispatchEvent(event); }); } /** * @override * @export */ onAssetUnload() { if (this.csAdManager_) { this.csAdManager_.stop(); } this.dispatchEvent( new shaka.util.FakeEvent(shaka.ads.AdManager.AD_STOPPED)); // TODO: // For SS DAI streams, if a different asset gets unloaded as // part of the process // of loading a DAI asset, stream manager state gets reset and we // don't get any ad events. // We need to figure out if it makes sense to stop the SS // manager on unload, and, if it does, find // a way to do it safely. // if (this.ssAdManager_) { // this.ssAdManager_.stop(); // } this.stats_ = new shaka.ads.AdsStats(); } /** * @override * @export */ requestClientSideAds(imaRequest) { 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_.requestAds(imaRequest); } /** * @override * @export */ initServerSide(adContainer, video) { // 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. // The 3rd check insures we have the right SDK: // {google.ima.dai} is an object that's part of DAI IMA SDK // but not SS SDK. if (!window['google'] || !google.ima || !google.ima.dai) { throw new shaka.util.Error( shaka.util.Error.Severity.CRITICAL, shaka.util.Error.Category.ADS, shaka.util.Error.Code.SS_IMA_SDK_MISSING); } this.ssAdManager_ = new shaka.ads.ServerSideAdManager( adContainer, video, this.locale_, (e) => { const event = /** @type {!shaka.util.FakeEvent} */ (e); if (event && event.type) { switch (event.type) { case shaka.ads.AdManager.ADS_LOADED: { const loadTime = (/** @type {!Object} */ (e))['loadTime']; this.stats_.addLoadTime(loadTime); break; } case shaka.ads.AdManager.AD_STARTED: this.stats_.incrementStarted(); break; case shaka.ads.AdManager.AD_COMPLETE: this.stats_.incrementPlayedCompletely(); break; case shaka.ads.AdManager.AD_SKIPPED: this.stats_.incrementSkipped(); break; } } this.dispatchEvent(event); }); } /** * @param {!google.ima.dai.api.StreamRequest} imaRequest * @param {string=} backupUrl * @return {!Promise.<string>} * @override * @export */ requestServerSideStream(imaRequest, backupUrl = '') { if (!this.ssAdManager_) { throw new shaka.util.Error( shaka.util.Error.Severity.RECOVERABLE, shaka.util.Error.Category.ADS, shaka.util.Error.Code.SS_AD_MANAGER_NOT_INITIALIZED); } if (!imaRequest.adTagParameters) { imaRequest.adTagParameters = {}; } const adTagParams = imaRequest.adTagParameters; if (adTagParams['mpt'] || adTagParams['mpv']) { shaka.log.alwaysWarn('You have attempted to set "mpt" and/or "mpv" ' + 'parameters of the ad tag. Please note that those parameters are ' + 'used for Shaka adoption tracking and will be overriden.'); } // Set player and version parameters for tracking imaRequest.adTagParameters['mpt'] = 'Shaka Player'; imaRequest.adTagParameters['mpv'] = shaka.Player.version; return this.ssAdManager_.streamRequest(imaRequest, backupUrl); } /** * @override * @export */ replaceServerSideAdTagParameters(adTagParameters) { if (!this.ssAdManager_) { throw new shaka.util.Error( shaka.util.Error.Severity.RECOVERABLE, shaka.util.Error.Category.ADS, shaka.util.Error.Code.SS_AD_MANAGER_NOT_INITIALIZED); } if (adTagParameters['mpt'] || adTagParameters['mpv']) { shaka.log.alwaysWarn('You have attempted to set "mpt" and/or "mpv" ' + 'parameters of the ad tag. Please note that those parameters are ' + 'used for Shaka adoption tracking and will be overriden.'); } adTagParameters['mpt'] = 'Shaka Player'; adTagParameters['mpv'] = shaka.Player.version; this.ssAdManager_.replaceAdTagParameters(adTagParameters); } /** * @return {shaka.extern.AdsStats} * @override * @export */ getStats() { return this.stats_.getBlob(); } /** * @override * @export */ onDashTimedMetadata(region) { if (this.ssAdManager_ && region.schemeIdUri == 'urn:google:dai:2018') { const type = region.schemeIdUri; const data = region.eventElement ? region.eventElement.getAttribute('messageData') : null; const timestamp = region.startTime; this.ssAdManager_.onTimedMetadata(type, data, timestamp); } } /** * @override * @export */ onHlsTimedMetadata(metadata, timestamp) { if (this.ssAdManager_) { this.ssAdManager_.onTimedMetadata('ID3', metadata['data'], timestamp); } else { shaka.log.warning('ID3 metadata processing was called without ' + 'initializing server side ad logic. Ad-related metadata will ' + 'not take effect'); } } /** * @override * @export */ onCueMetadataChange(value) { if (this.ssAdManager_) { this.ssAdManager_.onCueMetadataChange(value); } else { shaka.log.warning('ID3 metadata processing was called without ' + 'initializing server side ad logic. Ad-related metadata will ' + 'not take effect'); } } }; shaka.ads.CuePoint = class { /** * @param {number} start * @param {?number=} end */ constructor(start, end = null) { /** @public {number} */ this.start = start; /** @public {?number} */ this.end = end; } }; /** * The event name for when a sequence of ads has been loaded. * * @const {string} * @export */ shaka.ads.AdManager.ADS_LOADED = 'ads-loaded'; /** * The event name for when an ad has started playing. * * @const {string} * @export */ shaka.ads.AdManager.AD_STARTED = 'ad-started'; /** * The event name for when an ad playhead crosses first quartile. * * @const {string} * @export */ shaka.ads.AdManager.AD_FIRST_QUARTILE = 'ad-first-quartile'; /** * The event name for when an ad playhead crosses midpoint. * * @const {string} * @export */ shaka.ads.AdManager.AD_MIDPOINT = 'ad-midpoint'; /** * The event name for when an ad playhead crosses third quartile. * * @const {string} * @export */ shaka.ads.AdManager.AD_THIRD_QUARTILE = 'ad-third-quartile'; /** * The event name for when an ad has completed playing. * * @const {string} * @export */ shaka.ads.AdManager.AD_COMPLETE = 'ad-complete'; /** * The event name for when an ad has finished playing * (played all the way through, was skipped, or was unable to proceed * due to an error). * * @const {string} * @export */ shaka.ads.AdManager.AD_STOPPED = 'ad-stopped'; /** * The event name for when an ad is skipped by the user.. * * @const {string} * @export */ shaka.ads.AdManager.AD_SKIPPED = 'ad-skipped'; /** * The event name for when the ad volume has changed. * * @const {string} * @export */ shaka.ads.AdManager.AD_VOLUME_CHANGED = 'ad-volume-changed'; /** * The event name for when the ad was muted. * * @const {string} * @export */ shaka.ads.AdManager.AD_MUTED = 'ad-muted'; /** * The event name for when the ad was paused. * * @const {string} * @export */ shaka.ads.AdManager.AD_PAUSED = 'ad-paused'; /** * The event name for when the ad was resumed after a pause. * * @const {string} * @export */ shaka.ads.AdManager.AD_RESUMED = 'ad-resumed'; /** * The event name for when the ad's skip status changes * (usually it becomes skippable when it wasn't before). * * @const {string} * @export */ shaka.ads.AdManager.AD_SKIP_STATE_CHANGED = 'ad-skip-state-changed'; /** * The event name for when the ad's cue points (start/end markers) * have changed. * * @const {string} * @export */ shaka.ads.AdManager.CUEPOINTS_CHANGED = 'ad-cue-points-changed'; /** * The event name for when the native IMA ad manager object has * loaded and become available. * * @const {string} * @export */ shaka.ads.AdManager.IMA_AD_MANAGER_LOADED = 'ima-ad-manager-loaded'; /** * The event name for when the ad was clicked. * * @const {string} * @export */ shaka.ads.AdManager.AD_CLICKED = 'ad-clicked'; /** * The event name for when there is an update to the current ad's progress. * * @const {string} * @export */ shaka.ads.AdManager.AD_PROGRESS = 'ad-progress'; /** * The event name for when the ad is buffering. * * @const {string} * @export */ shaka.ads.AdManager.AD_BUFFERING = 'ad-buffering'; /** * The event name for when the ad's URL was hit. * * @const {string} * @export */ shaka.ads.AdManager.AD_IMPRESSION = 'ad-impression'; /** * The event name for when the ad's duration changed. * * @const {string} * @export */ shaka.ads.AdManager.AD_DURATION_CHANGED = 'ad-duration-changed'; /** * The event name for when the ad was closed by the user. * * @const {string} * @export */ shaka.ads.AdManager.AD_CLOSED = 'ad-closed'; /** * The event name for when the ad data becomes available. * * @const {string} * @export */ shaka.ads.AdManager.AD_LOADED = 'ad-loaded'; /** * The event name for when all the ads were completed. * * @const {string} * @export */ shaka.ads.AdManager.ALL_ADS_COMPLETED = 'all-ads-completed'; /** * The event name for when the ad changes from or to linear. * * @const {string} * @export */ shaka.ads.AdManager.AD_LINEAR_CHANGED = 'ad-linear-changed'; /** * The event name for when the ad's metadata becomes available. * * @const {string} * @export */ shaka.ads.AdManager.AD_METADATA = 'ad-metadata'; /** * The event name for when the ad display encountered a recoverable * error. * * @const {string} * @export */ shaka.ads.AdManager.AD_RECOVERABLE_ERROR = 'ad-recoverable-error'; /** * The event name for when the client side SDK signalled its readiness * to play a VPAID ad or an ad rule. * * @const {string} * @export */ shaka.ads.AdManager.AD_BREAK_READY = 'ad-break-ready'; /** * The event name for when the interaction callback for the ad was * triggered. * * @const {string} * @export */ shaka.ads.AdManager.AD_INTERACTION = 'ad-interaction'; /** * Set this is a default ad manager for the player. * Apps can also set their own ad manager, if they'd like. */ shaka.Player.setAdManagerFactory(() => new shaka.ads.AdManager());