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. getDuration() {
  59. return this.ad_.getDuration();
  60. }
  61. /**
  62. * @override
  63. * @export
  64. */
  65. getMinSuggestedDuration() {
  66. return this.ad_.getMinSuggestedDuration();
  67. }
  68. /**
  69. * @override
  70. * @export
  71. */
  72. getRemainingTime() {
  73. return this.manager_.getRemainingTime();
  74. }
  75. /**
  76. * @override
  77. * @export
  78. */
  79. isPaused() {
  80. return this.isPaused_;
  81. }
  82. /**
  83. * @override
  84. * @export
  85. */
  86. isSkippable() {
  87. // IMA returns -1 for non-skippable ads. Any positive number is a genuine
  88. // skip offset, meaning the ad is skippable.
  89. return this.ad_.getSkipTimeOffset() >= 0;
  90. }
  91. /**
  92. * @override
  93. * @export
  94. */
  95. getTimeUntilSkippable() {
  96. const skipOffset = this.ad_.getSkipTimeOffset();
  97. const canSkipIn = this.getRemainingTime() - skipOffset;
  98. return Math.max(canSkipIn, 0);
  99. }
  100. /**
  101. * @override
  102. * @export
  103. */
  104. canSkipNow() {
  105. return this.manager_.getAdSkippableState();
  106. }
  107. /**
  108. * @override
  109. * @export
  110. */
  111. skip() {
  112. return this.manager_.skip();
  113. }
  114. /**
  115. * @param {boolean} paused
  116. */
  117. setPaused(paused) {
  118. this.isPaused_ = paused;
  119. }
  120. /**
  121. * @override
  122. * @export
  123. */
  124. pause() {
  125. return this.manager_.pause();
  126. }
  127. /**
  128. * @override
  129. * @export
  130. */
  131. play() {
  132. return this.manager_.resume();
  133. }
  134. /**
  135. * @override
  136. * @export
  137. */
  138. getVolume() {
  139. return this.manager_.getVolume();
  140. }
  141. /**
  142. * @override
  143. * @export
  144. */
  145. setVolume(volume) {
  146. this.video_.volume = volume;
  147. return this.manager_.setVolume(volume);
  148. }
  149. /**
  150. * @override
  151. * @export
  152. */
  153. isMuted() {
  154. return this.manager_.getVolume() == 0;
  155. }
  156. /**
  157. * @override
  158. * @export
  159. */
  160. isLinear() {
  161. return this.ad_.isLinear();
  162. }
  163. /**
  164. * @override
  165. * @export
  166. */
  167. resize(width, height) {
  168. let isInFullscreen = false;
  169. const video = /** @type {HTMLVideoElement} */(this.video_);
  170. if (document.fullscreenEnabled) {
  171. isInFullscreen = !!document.fullscreenElement;
  172. } else if (video.webkitSupportsFullscreen) {
  173. isInFullscreen = video.webkitDisplayingFullscreen;
  174. }
  175. const viewMode = isInFullscreen ?
  176. google.ima.ViewMode.FULLSCREEN : google.ima.ViewMode.NORMAL;
  177. this.manager_.resize(width, height, viewMode);
  178. }
  179. /**
  180. * @override
  181. * @export
  182. */
  183. setMuted(muted) {
  184. this.video_.muted = muted;
  185. // Emulate the "mute" functionality, where current, pre-mute
  186. // volume is saved and can be restored on unmute.
  187. if (muted) {
  188. this.volume_ = this.getVolume();
  189. this.manager_.setVolume(0);
  190. } else {
  191. this.manager_.setVolume(this.volume_);
  192. }
  193. }
  194. /**
  195. * It's required for a muted ad to start when autoplaying.
  196. *
  197. * @param {number} videoVolume
  198. */
  199. setInitialMuted(videoVolume) {
  200. this.volume_ = videoVolume;
  201. this.manager_.setVolume(0);
  202. }
  203. /**
  204. * @override
  205. * @export
  206. */
  207. getSequenceLength() {
  208. const podInfo = this.ad_.getAdPodInfo();
  209. if (podInfo == null) {
  210. // No pod, just one ad.
  211. return 1;
  212. }
  213. return podInfo.getTotalAds();
  214. }
  215. /**
  216. * @override
  217. * @export
  218. */
  219. getPositionInSequence() {
  220. const podInfo = this.ad_.getAdPodInfo();
  221. if (podInfo == null) {
  222. // No pod, just one ad.
  223. return 1;
  224. }
  225. return podInfo.getAdPosition();
  226. }
  227. /**
  228. * @override
  229. * @export
  230. */
  231. getTitle() {
  232. return this.ad_.getTitle();
  233. }
  234. /**
  235. * @override
  236. * @export
  237. */
  238. getDescription() {
  239. return this.ad_.getDescription();
  240. }
  241. /**
  242. * @override
  243. * @export
  244. */
  245. getVastMediaBitrate() {
  246. return this.ad_.getVastMediaBitrate();
  247. }
  248. /**
  249. * @override
  250. * @export
  251. */
  252. getVastMediaHeight() {
  253. return this.ad_.getVastMediaHeight();
  254. }
  255. /**
  256. * @override
  257. * @export
  258. */
  259. getVastMediaWidth() {
  260. return this.ad_.getVastMediaWidth();
  261. }
  262. /**
  263. * @override
  264. * @export
  265. */
  266. getAdId() {
  267. return this.ad_.getAdId();
  268. }
  269. /**
  270. * @override
  271. * @export
  272. */
  273. getCreativeAdId() {
  274. return this.ad_.getCreativeAdId();
  275. }
  276. /**
  277. * @override
  278. * @export
  279. */
  280. getAdvertiserName() {
  281. return this.ad_.getAdvertiserName();
  282. }
  283. /**
  284. * @override
  285. * @export
  286. */
  287. getMediaUrl() {
  288. return this.ad_.getMediaUrl();
  289. }
  290. /**
  291. * @override
  292. * @export
  293. */
  294. getTimeOffset() {
  295. const podInfo = this.ad_.getAdPodInfo();
  296. if (podInfo == null) {
  297. // Defaults to 0 if this ad is not part of a pod, or the pod is not part
  298. // of an ad playlist.
  299. return 0;
  300. }
  301. return podInfo.getTimeOffset();
  302. }
  303. /**
  304. * @override
  305. * @export
  306. */
  307. getPodIndex() {
  308. const podInfo = this.ad_.getAdPodInfo();
  309. if (podInfo == null) {
  310. // Defaults to 0 if this ad is not part of a pod, or the pod is not part
  311. // of an ad playlist.
  312. return 0;
  313. }
  314. return podInfo.getPodIndex();
  315. }
  316. /**
  317. * @override
  318. * @export
  319. */
  320. release() {
  321. this.ad_ = null;
  322. this.manager_ = null;
  323. }
  324. };