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