mirror of
https://github.com/shaka-project/shaka-player.git
synced 2026-06-14 15:56:38 +03:00
feat(Cast): Support getCurrentAd while casting (#9306)
This commit is contained in:
committed by
GitHub
parent
a5c2c4f6ea
commit
02a2bb54d0
@@ -3,7 +3,6 @@
|
||||
+../../ui/ad_info.js
|
||||
+../../ui/ad_statistics_button.js
|
||||
+../../ui/audio_language_selection.js
|
||||
+../../ui/basic_ad.js
|
||||
+../../ui/content_title.js
|
||||
+../../ui/externs/ui.js
|
||||
+../../ui/externs/watermark.js
|
||||
|
||||
+52
-4
@@ -8,6 +8,7 @@ goog.provide('shaka.cast.CastProxy');
|
||||
|
||||
goog.require('goog.asserts');
|
||||
goog.require('shaka.Player');
|
||||
goog.require('shaka.ads.AbstractAd');
|
||||
goog.require('shaka.cast.CastSender');
|
||||
goog.require('shaka.cast.CastUtils');
|
||||
goog.require('shaka.device.DeviceFactory');
|
||||
@@ -71,6 +72,9 @@ shaka.cast.CastProxy = class extends shaka.util.FakeEventTarget {
|
||||
/** @private {Object} */
|
||||
this.adManagerProxy_ = null;
|
||||
|
||||
/** @private {Object} */
|
||||
this.currentAdProxy_ = null;
|
||||
|
||||
/** @private {shaka.util.FakeEventTarget} */
|
||||
this.videoEventTarget_ = null;
|
||||
|
||||
@@ -156,6 +160,7 @@ shaka.cast.CastProxy = class extends shaka.util.FakeEventTarget {
|
||||
this.videoProxy_ = null;
|
||||
this.playerProxy_ = null;
|
||||
this.adManagerProxy_ = null;
|
||||
this.currentAdProxy_ = null;
|
||||
|
||||
// FakeEventTarget implements IReleasable
|
||||
super.release();
|
||||
@@ -494,8 +499,8 @@ shaka.cast.CastProxy = class extends shaka.util.FakeEventTarget {
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates over all of the methods of the player, including inherited methods
|
||||
* from FakeEventTarget.
|
||||
* Iterates over all of the methods of the ad manager, including inherited
|
||||
* methods from FakeEventTarget.
|
||||
* @param {function(string, function())} operation
|
||||
* @private
|
||||
*/
|
||||
@@ -505,6 +510,16 @@ shaka.cast.CastProxy = class extends shaka.util.FakeEventTarget {
|
||||
this.iterateOverClassMethods_(operation, adManager);
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates over all of the methods of the current ad.
|
||||
* @param {function(string, function())} operation
|
||||
* @private
|
||||
*/
|
||||
iterateOverCurrentAdMethods_(operation) {
|
||||
const ad = /** @type {!Object} */ (new shaka.cast.BasicAd());
|
||||
this.iterateOverClassMethods_(operation, ad);
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates over all of the methods of an Object, including inherited methods
|
||||
* from FakeEventTarget.
|
||||
@@ -948,11 +963,10 @@ shaka.cast.CastProxy = class extends shaka.util.FakeEventTarget {
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @param {boolean} dontRecordCalls
|
||||
* @return {?}
|
||||
* @private
|
||||
*/
|
||||
adManagerProxyGet_(name, dontRecordCalls = false) {
|
||||
adManagerProxyGet_(name) {
|
||||
// If name is a shortened compiled name, get the original version
|
||||
// from our map.
|
||||
if (this.adManagerCompiledToExternNames_.has(name)) {
|
||||
@@ -981,6 +995,10 @@ shaka.cast.CastProxy = class extends shaka.util.FakeEventTarget {
|
||||
return value.bind(this.localAdManager_);
|
||||
}
|
||||
|
||||
if (name == 'getCurrentAd') {
|
||||
return this.currentAdProxy_;
|
||||
}
|
||||
|
||||
return this.sender_.get('adManager', name);
|
||||
}
|
||||
|
||||
@@ -997,6 +1015,20 @@ shaka.cast.CastProxy = class extends shaka.util.FakeEventTarget {
|
||||
this.adManagerEventTarget_.dispatchEvent(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @return {?}
|
||||
* @private
|
||||
*/
|
||||
currentAdProxyGet_(name) {
|
||||
// This function should not be called if we are not casting.
|
||||
if (!this.sender_.isCasting()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.sender_.get('currentAd', name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} targetName
|
||||
* @param {!shaka.util.FakeEvent} event
|
||||
@@ -1018,6 +1050,20 @@ shaka.cast.CastProxy = class extends shaka.util.FakeEventTarget {
|
||||
}
|
||||
this.playerEventTarget_.dispatchEvent(event);
|
||||
} else if (targetName == 'adManager') {
|
||||
if (event.type == 'ad-started') {
|
||||
this.currentAdProxy_ = {};
|
||||
this.iterateOverCurrentAdMethods_((name, method) => {
|
||||
goog.asserts.assert(this.currentAdProxy_,
|
||||
'Must have current ad proxy!');
|
||||
Object.defineProperty(this.currentAdProxy_, name, {
|
||||
configurable: false,
|
||||
enumerable: true,
|
||||
get: () => this.currentAdProxyGet_(name),
|
||||
});
|
||||
});
|
||||
} else if (event.type == 'ad-stopped') {
|
||||
this.currentAdProxy_ = null;
|
||||
}
|
||||
this.adManagerEventTarget_.dispatchEvent(event);
|
||||
}
|
||||
}
|
||||
@@ -1046,3 +1092,5 @@ shaka.cast.CastProxy = class extends shaka.util.FakeEventTarget {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
shaka.cast.BasicAd = class extends shaka.ads.AbstractAd {};
|
||||
|
||||
@@ -52,6 +52,9 @@ shaka.cast.CastReceiver = class extends shaka.util.FakeEventTarget {
|
||||
/** @private {shaka.extern.IAdManager} */
|
||||
this.adManager_ = player.getAdManager();
|
||||
|
||||
/** @private {?shaka.extern.IAd} */
|
||||
this.currentAd_ = null;
|
||||
|
||||
/** @private {shaka.util.EventManager} */
|
||||
this.eventManager_ = new shaka.util.EventManager();
|
||||
|
||||
@@ -60,6 +63,7 @@ shaka.cast.CastReceiver = class extends shaka.util.FakeEventTarget {
|
||||
'video': video,
|
||||
'player': player,
|
||||
'adManager': this.adManager_,
|
||||
'currentAd': this.currentAd_,
|
||||
};
|
||||
|
||||
/** @private {?function(Object)} */
|
||||
@@ -299,9 +303,16 @@ shaka.cast.CastReceiver = class extends shaka.util.FakeEventTarget {
|
||||
|
||||
if (this.adManager_) {
|
||||
for (const name of shaka.cast.CastUtils.AdManagerEvents) {
|
||||
this.eventManager_.listen(
|
||||
this.adManager_, name,
|
||||
(event) => this.proxyEvent_('adManager', event));
|
||||
this.eventManager_.listen(this.adManager_, name, (event) => {
|
||||
if (event.type == 'ad-started') {
|
||||
this.currentAd_ = this.adManager_.getCurrentAd();
|
||||
// Reset update message frequency values after new ad.
|
||||
this.updateNumber_ = 0;
|
||||
} else if (event.type == 'ad-stopped') {
|
||||
this.currentAd_ = null;
|
||||
}
|
||||
this.proxyEvent_('adManager', event);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -521,6 +532,15 @@ shaka.cast.CastReceiver = class extends shaka.util.FakeEventTarget {
|
||||
}
|
||||
});
|
||||
|
||||
if (this.currentAd_) {
|
||||
shaka.cast.CastUtils.CurrentAdGetterMethods.forEach((frequency, name) => {
|
||||
if (this.updateNumber_ % frequency == 0) {
|
||||
update['currentAd'][name] =
|
||||
/** @type {Object} */ (this.currentAd_)[name]();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Volume attributes are tied to the system volume.
|
||||
const manager = cast.receiver.CastReceiverManager.getInstance();
|
||||
const systemVolume = manager.getSystemVolume();
|
||||
@@ -695,7 +715,9 @@ shaka.cast.CastReceiver = class extends shaka.util.FakeEventTarget {
|
||||
}
|
||||
}
|
||||
|
||||
this.targets_[targetName][property] = value;
|
||||
if (this.targets_[targetName]) {
|
||||
this.targets_[targetName][property] = value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'call': {
|
||||
@@ -703,8 +725,10 @@ shaka.cast.CastReceiver = class extends shaka.util.FakeEventTarget {
|
||||
const methodName = message['methodName'];
|
||||
const args = message['args'];
|
||||
const target = this.targets_[targetName];
|
||||
// eslint-disable-next-line prefer-spread
|
||||
target[methodName].apply(target, args);
|
||||
if (target) {
|
||||
// eslint-disable-next-line prefer-spread
|
||||
target[methodName].apply(target, args);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'asyncCall': {
|
||||
|
||||
@@ -383,6 +383,13 @@ shaka.cast.CastSender = class {
|
||||
if (CastUtils.LargeAdManagerGetterMethods.has(property)) {
|
||||
return () => this.propertyGetter_(targetName, property);
|
||||
}
|
||||
} else if (targetName == 'currentAd') {
|
||||
if (CastUtils.CurrentAdVoidMethods.includes(property)) {
|
||||
return (...args) => this.remoteCall_(targetName, property, ...args);
|
||||
}
|
||||
if (CastUtils.CurrentAdGetterMethods.has(property)) {
|
||||
return () => this.propertyGetter_(targetName, property);
|
||||
}
|
||||
}
|
||||
|
||||
return this.propertyGetter_(targetName, property);
|
||||
|
||||
@@ -529,6 +529,57 @@ shaka.cast.CastUtils.AdManagerEvents = [
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* Current ad getter methods that are proxied while casting.
|
||||
* The key is the method, the value is the frequency of updates.
|
||||
* Frequency 1 translates to every update; frequency 2 to every 2 updates, etc.
|
||||
* @const {!Map<string, number>}
|
||||
*/
|
||||
shaka.cast.CastUtils.CurrentAdGetterMethods = new Map()
|
||||
.set('needsSkipUI', 20)
|
||||
.set('isClientRendering', 20)
|
||||
.set('hasCustomClick', 20)
|
||||
.set('isUsingAnotherMediaElement', 20)
|
||||
.set('getDuration', 1)
|
||||
.set('getMinSuggestedDuration', 1)
|
||||
.set('getRemainingTime', 1)
|
||||
.set('getTimeUntilSkippable', 1)
|
||||
.set('isPaused', 1)
|
||||
.set('isSkippable', 1)
|
||||
.set('canSkipNow', 1)
|
||||
.set('getVolume', 1)
|
||||
.set('isMuted', 1)
|
||||
.set('isLinear', 20)
|
||||
.set('getSequenceLength', 20)
|
||||
.set('getPositionInSequence', 20)
|
||||
.set('getTitle', 20)
|
||||
.set('getDescription', 20)
|
||||
.set('getVastMediaBitrate', 20)
|
||||
.set('getVastMediaHeight', 20)
|
||||
.set('getVastMediaWidth', 20)
|
||||
.set('getVastAdId', 20)
|
||||
.set('getAdId', 20)
|
||||
.set('getCreativeAdId', 20)
|
||||
.set('getAdvertiserName', 20)
|
||||
.set('getMediaUrl', 20)
|
||||
.set('getTimeOffset', 20)
|
||||
.set('getPodIndex', 20);
|
||||
|
||||
|
||||
/**
|
||||
* Current ad methods with no return value that are proxied while casting.
|
||||
* @const {!Array<string>}
|
||||
*/
|
||||
shaka.cast.CastUtils.CurrentAdVoidMethods = [
|
||||
'skip',
|
||||
'play',
|
||||
'pause',
|
||||
'setVolume',
|
||||
'setMuted',
|
||||
'resize',
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* video: Object,
|
||||
|
||||
@@ -113,6 +113,34 @@ describe('CastUtils', () => {
|
||||
.toEqual([]);
|
||||
});
|
||||
|
||||
it('includes every Ad member', () => {
|
||||
const ignoredMembers = [
|
||||
'constructor', // JavaScript added field
|
||||
'release', // Handled by AdManager
|
||||
];
|
||||
|
||||
const castMembers = CastUtils.CurrentAdVoidMethods
|
||||
.concat(Array.from(CastUtils.CurrentAdGetterMethods.keys()));
|
||||
|
||||
const allAdMembers =
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
Object.getOwnPropertyNames(shaka.ads.AbstractAd.prototype);
|
||||
expect(
|
||||
ignoredMembers.filter((member) => !allAdMembers.includes(member)))
|
||||
.toEqual([]);
|
||||
const adMembers = allAdMembers.filter((name) => {
|
||||
// Private members end with _.
|
||||
return !ignoredMembers.includes(name) && !name.endsWith('_');
|
||||
});
|
||||
|
||||
// To make debugging easier, don't check that they are equal; instead check
|
||||
// that neither has any extra entries.
|
||||
expect(castMembers.filter((name) => !adMembers.includes(name)))
|
||||
.toEqual([]);
|
||||
expect(adMembers.filter((name) => !castMembers.includes(name)))
|
||||
.toEqual([]);
|
||||
});
|
||||
|
||||
describe('serialize/deserialize', () => {
|
||||
it('transfers infinite values and NaN', () => {
|
||||
const orig = {
|
||||
|
||||
@@ -14,6 +14,9 @@ shaka.test.FakeAdManager = class extends shaka.util.FakeEventTarget {
|
||||
|
||||
/** @private {shaka.ads.AdsStats} */
|
||||
this.stats_ = new shaka.ads.AdsStats();
|
||||
|
||||
/** @private {?shaka.extern.IAd} */
|
||||
this.currentAd_ = null;
|
||||
}
|
||||
|
||||
/** @override */
|
||||
@@ -102,12 +105,15 @@ shaka.test.FakeAdManager = class extends shaka.util.FakeEventTarget {
|
||||
getInterstitialPlayer() {}
|
||||
|
||||
/** @override */
|
||||
getCurrentAd() {}
|
||||
getCurrentAd() {
|
||||
return this.currentAd_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {!shaka.test.FakeAd} ad
|
||||
*/
|
||||
startAd(ad) {
|
||||
this.currentAd_ = ad;
|
||||
const event = new shaka.util.FakeEvent(shaka.ads.Utils.AD_STARTED,
|
||||
(new Map()).set('ad', ad));
|
||||
|
||||
@@ -116,6 +122,7 @@ shaka.test.FakeAdManager = class extends shaka.util.FakeEventTarget {
|
||||
|
||||
/** @public */
|
||||
finishAd() {
|
||||
this.currentAd_ = null;
|
||||
const event = new shaka.util.FakeEvent(shaka.ads.Utils.AD_STOPPED);
|
||||
this.dispatchEvent(event);
|
||||
}
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
/*! @license
|
||||
* Shaka Player
|
||||
* Copyright 2016 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
|
||||
goog.provide('shaka.ui.BasicAd');
|
||||
|
||||
goog.require('shaka.ads.AbstractAd');
|
||||
|
||||
shaka.ui.BasicAd = class extends shaka.ads.AbstractAd {
|
||||
/**
|
||||
* @param {HTMLMediaElement} video
|
||||
* @param {?number} startTime
|
||||
* @param {?number} endTime
|
||||
*/
|
||||
constructor(video, startTime, endTime) {
|
||||
super(video);
|
||||
|
||||
/** @private {?number} */
|
||||
this.startTime_ = startTime;
|
||||
|
||||
/** @private {?number} */
|
||||
this.endTime_ = endTime;
|
||||
|
||||
/** @private {boolean} */
|
||||
this.isLinear_ = this.startTime_ != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
getDuration() {
|
||||
if (this.endTime_ == null || this.startTime_ == null) {
|
||||
return -1;
|
||||
}
|
||||
return this.endTime_ - this.startTime_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
getRemainingTime() {
|
||||
if (this.endTime_ == null) {
|
||||
return -1;
|
||||
}
|
||||
return this.endTime_ - this.video.currentTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
isLinear() {
|
||||
return this.isLinear_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
getTimeOffset() {
|
||||
if (this.startTime_ == null) {
|
||||
return 0;
|
||||
}
|
||||
return this.startTime_;
|
||||
}
|
||||
};
|
||||
Vendored
+3
-15
@@ -15,7 +15,6 @@ goog.require('shaka.device.DeviceFactory');
|
||||
goog.require('shaka.device.IDevice');
|
||||
goog.require('shaka.log');
|
||||
goog.require('shaka.ui.AdInfo');
|
||||
goog.require('shaka.ui.BasicAd');
|
||||
goog.require('shaka.ui.BigPlayButton');
|
||||
goog.require('shaka.ui.ContextMenu');
|
||||
goog.require('shaka.ui.HiddenFastForwardButton');
|
||||
@@ -178,7 +177,7 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget {
|
||||
this.queueManager_.setCustomPlayer(this.player_);
|
||||
|
||||
/** @private {?shaka.extern.IAd} */
|
||||
this.ad_ = null;
|
||||
this.ad_ = this.adManager_.getCurrentAd();
|
||||
|
||||
/** @private {!Array<!shaka.extern.AdCuePoint>} */
|
||||
this.adCuePoints_ = [];
|
||||
@@ -1523,19 +1522,8 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget {
|
||||
});
|
||||
|
||||
this.eventManager_.listen(
|
||||
this.adManager_, shaka.ads.Utils.AD_STARTED, (e) => {
|
||||
this.ad_ = (/** @type {!Object} */ (e))['ad'];
|
||||
if (!this.ad_) {
|
||||
const currentTime = this.video_.currentTime;
|
||||
const cuePoint = this.adCuePoints_.find((c) => {
|
||||
return currentTime >= c.start &&
|
||||
currentTime <= (c.end || Infinity);
|
||||
});
|
||||
const start = cuePoint ? cuePoint.start : null;
|
||||
const end = cuePoint ? cuePoint.end : null;
|
||||
// Note: We assume the ad uses the base video.
|
||||
this.ad_ = new shaka.ui.BasicAd(this.video_, start, end);
|
||||
}
|
||||
this.adManager_, shaka.ads.Utils.AD_STARTED, () => {
|
||||
this.ad_ = this.adManager_.getCurrentAd();
|
||||
this.showAdUI();
|
||||
this.onBufferingStateChange_();
|
||||
});
|
||||
|
||||
+3
-15
@@ -8,7 +8,6 @@
|
||||
goog.provide('shaka.ui.Element');
|
||||
|
||||
goog.require('shaka.ads.Utils');
|
||||
goog.require('shaka.ui.BasicAd');
|
||||
goog.require('shaka.util.EventManager');
|
||||
goog.requireType('shaka.Player');
|
||||
goog.requireType('shaka.ui.Controls');
|
||||
@@ -72,22 +71,11 @@ shaka.ui.Element = class {
|
||||
* @protected {?shaka.extern.IAd}
|
||||
* @exportInterface
|
||||
*/
|
||||
this.ad = controls.getAd();
|
||||
this.ad = this.adManager.getCurrentAd(); ;
|
||||
|
||||
const AD_STARTED = shaka.ads.Utils.AD_STARTED;
|
||||
this.eventManager.listen(this.adManager, AD_STARTED, (e) => {
|
||||
this.ad = (/** @type {!Object} */ (e))['ad'];
|
||||
if (!this.ad) {
|
||||
const currentTime = this.video.currentTime;
|
||||
const cuePoint = this.controls.getAdCuePoints().find((c) => {
|
||||
return currentTime >= c.start &&
|
||||
currentTime <= (c.end || Infinity);
|
||||
});
|
||||
const start = cuePoint ? cuePoint.start : null;
|
||||
const end = cuePoint ? cuePoint.end : null;
|
||||
// Note: We assume the ad uses the base video.
|
||||
this.ad = new shaka.ui.BasicAd(this.video, start, end);
|
||||
}
|
||||
this.eventManager.listen(this.adManager, AD_STARTED, () => {
|
||||
this.ad = this.adManager.getCurrentAd();
|
||||
});
|
||||
|
||||
const AD_STOPPED = shaka.ads.Utils.AD_STOPPED;
|
||||
|
||||
Reference in New Issue
Block a user