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