Source: lib/ads/client_side_ad.js

  1. /*! @license
  2. * Shaka Player
  3. * Copyright 2016 Google LLC
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. goog.provide('shaka.ads.ClientSideAd');
  7. goog.require('shaka.util.EventManager');
  8. /**
  9. * @implements {shaka.extern.IAd}
  10. * @export
  11. */
  12. shaka.ads.ClientSideAd = class {
  13. /**
  14. * @param {!google.ima.Ad} imaAd
  15. * @param {!google.ima.AdsManager} imaAdManager
  16. * @param {HTMLMediaElement} video
  17. */
  18. constructor(imaAd, imaAdManager, video) {
  19. /** @private {google.ima.Ad} */
  20. this.ad_ = imaAd;
  21. /** @private {google.ima.AdsManager} */
  22. this.manager_ = imaAdManager;
  23. /** @private {HTMLMediaElement} */
  24. this.video_ = video;
  25. /** @private {boolean} */
  26. this.isPaused_ = false;
  27. /** @private {number} */
  28. this.volume_ = this.manager_.getVolume();
  29. /** @private {shaka.util.EventManager} */
  30. this.eventManager_ = new shaka.util.EventManager();
  31. this.eventManager_.listen(this.manager_,
  32. google.ima.AdEvent.Type.PAUSED, () => {
  33. this.isPaused_ = true;
  34. });
  35. this.eventManager_.listen(this.manager_,
  36. google.ima.AdEvent.Type.RESUMED, () => {
  37. this.isPaused_ = false;
  38. });
  39. }
  40. /**
  41. * @override
  42. * @export
  43. */
  44. needsSkipUI() {
  45. return false;
  46. }
  47. /**
  48. * @override
  49. * @export
  50. */
  51. isClientRendering() {
  52. return true;
  53. }
  54. /**
  55. * @override
  56. * @export
  57. */
  58. hasCustomClick() {
  59. return true;
  60. }
  61. /**
  62. * @override
  63. * @export
  64. */
  65. isUsingAnotherMediaElement() {
  66. return true;
  67. }
  68. /**
  69. * @override
  70. * @export
  71. */
  72. getDuration() {
  73. return this.ad_.getDuration();
  74. }
  75. /**
  76. * @override
  77. * @export
  78. */
  79. getMinSuggestedDuration() {
  80. return this.ad_.getMinSuggestedDuration();
  81. }
  82. /**
  83. * @override
  84. * @export
  85. */
  86. getRemainingTime() {
  87. return this.manager_.getRemainingTime();
  88. }
  89. /**
  90. * @override
  91. * @export
  92. */
  93. isPaused() {
  94. return this.isPaused_;
  95. }
  96. /**
  97. * @override
  98. * @export
  99. */
  100. isSkippable() {
  101. // IMA returns -1 for non-skippable ads. Any positive number is a genuine
  102. // skip offset, meaning the ad is skippable.
  103. return this.ad_.getSkipTimeOffset() >= 0;
  104. }
  105. /**
  106. * @override
  107. * @export
  108. */
  109. getTimeUntilSkippable() {
  110. const skipOffset = this.ad_.getSkipTimeOffset();
  111. const canSkipIn = this.getRemainingTime() - skipOffset;
  112. return Math.max(canSkipIn, 0);
  113. }
  114. /**
  115. * @override
  116. * @export
  117. */
  118. canSkipNow() {
  119. return this.manager_.getAdSkippableState();
  120. }
  121. /**
  122. * @override
  123. * @export
  124. */
  125. skip() {
  126. return this.manager_.skip();
  127. }
  128. /**
  129. * @param {boolean} paused
  130. */
  131. setPaused(paused) {
  132. this.isPaused_ = paused;
  133. }
  134. /**
  135. * @override
  136. * @export
  137. */
  138. pause() {
  139. return this.manager_.pause();
  140. }
  141. /**
  142. * @override
  143. * @export
  144. */
  145. play() {
  146. return this.manager_.resume();
  147. }
  148. /**
  149. * @override
  150. * @export
  151. */
  152. getVolume() {
  153. return this.manager_.getVolume();
  154. }
  155. /**
  156. * @override
  157. * @export
  158. */
  159. setVolume(volume) {
  160. this.video_.volume = volume;
  161. return this.manager_.setVolume(volume);
  162. }
  163. /**
  164. * @override
  165. * @export
  166. */
  167. isMuted() {
  168. return this.manager_.getVolume() == 0;
  169. }
  170. /**
  171. * @override
  172. * @export
  173. */
  174. isLinear() {
  175. return this.ad_.isLinear();
  176. }
  177. /**
  178. * @override
  179. * @export
  180. */
  181. resize(width, height) {
  182. this.manager_.resize(width, height);
  183. }
  184. /**
  185. * @override
  186. * @export
  187. */
  188. setMuted(muted) {
  189. this.video_.muted = muted;
  190. // Emulate the "mute" functionality, where current, pre-mute
  191. // volume is saved and can be restored on unmute.
  192. if (muted) {
  193. this.volume_ = this.getVolume();
  194. this.manager_.setVolume(0);
  195. } else {
  196. this.manager_.setVolume(this.volume_);
  197. }
  198. }
  199. /**
  200. * It's required for a muted ad to start when autoplaying.
  201. *
  202. * @param {number} videoVolume
  203. */
  204. setInitialMuted(videoVolume) {
  205. this.volume_ = videoVolume;
  206. this.manager_.setVolume(0);
  207. }
  208. /**
  209. * @override
  210. * @export
  211. */
  212. getSequenceLength() {
  213. const podInfo = this.ad_.getAdPodInfo();
  214. if (podInfo == null) {
  215. // No pod, just one ad.
  216. return 1;
  217. }
  218. return podInfo.getTotalAds();
  219. }
  220. /**
  221. * @override
  222. * @export
  223. */
  224. getPositionInSequence() {
  225. const podInfo = this.ad_.getAdPodInfo();
  226. if (podInfo == null) {
  227. // No pod, just one ad.
  228. return 1;
  229. }
  230. return podInfo.getAdPosition();
  231. }
  232. /**
  233. * @override
  234. * @export
  235. */
  236. getTitle() {
  237. return this.ad_.getTitle();
  238. }
  239. /**
  240. * @override
  241. * @export
  242. */
  243. getDescription() {
  244. return this.ad_.getDescription();
  245. }
  246. /**
  247. * @override
  248. * @export
  249. */
  250. getVastMediaBitrate() {
  251. return this.ad_.getVastMediaBitrate();
  252. }
  253. /**
  254. * @override
  255. * @export
  256. */
  257. getVastMediaHeight() {
  258. return this.ad_.getVastMediaHeight();
  259. }
  260. /**
  261. * @override
  262. * @export
  263. */
  264. getVastMediaWidth() {
  265. return this.ad_.getVastMediaWidth();
  266. }
  267. /**
  268. * @override
  269. * @export
  270. */
  271. getAdId() {
  272. return this.ad_.getAdId();
  273. }
  274. /**
  275. * @override
  276. * @export
  277. */
  278. getCreativeAdId() {
  279. return this.ad_.getCreativeAdId();
  280. }
  281. /**
  282. * @override
  283. * @export
  284. */
  285. getAdvertiserName() {
  286. return this.ad_.getAdvertiserName();
  287. }
  288. /**
  289. * @override
  290. * @export
  291. */
  292. getMediaUrl() {
  293. return this.ad_.getMediaUrl();
  294. }
  295. /**
  296. * @override
  297. * @export
  298. */
  299. getTimeOffset() {
  300. const podInfo = this.ad_.getAdPodInfo();
  301. if (podInfo == null) {
  302. // Defaults to 0 if this ad is not part of a pod, or the pod is not part
  303. // of an ad playlist.
  304. return 0;
  305. }
  306. return podInfo.getTimeOffset();
  307. }
  308. /**
  309. * @override
  310. * @export
  311. */
  312. getPodIndex() {
  313. const podInfo = this.ad_.getAdPodInfo();
  314. if (podInfo == null) {
  315. // Defaults to 0 if this ad is not part of a pod, or the pod is not part
  316. // of an ad playlist.
  317. return 0;
  318. }
  319. return podInfo.getPodIndex();
  320. }
  321. /**
  322. * @override
  323. * @export
  324. */
  325. release() {
  326. this.ad_ = null;
  327. this.manager_ = null;
  328. }
  329. };