shaka-player 4.9.13 → 4.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/README.md +4 -0
  2. package/dist/controls.css +2 -2
  3. package/dist/controls.css.map +1 -1
  4. package/dist/deps.js +24 -19
  5. package/dist/locales.js +46 -35
  6. package/dist/shaka-player.compiled.d.ts +291 -12
  7. package/dist/shaka-player.compiled.debug.d.ts +291 -12
  8. package/dist/shaka-player.compiled.debug.externs.js +443 -6
  9. package/dist/shaka-player.compiled.debug.js +651 -556
  10. package/dist/shaka-player.compiled.debug.map +4 -4
  11. package/dist/shaka-player.compiled.externs.js +443 -6
  12. package/dist/shaka-player.compiled.js +1325 -1270
  13. package/dist/shaka-player.compiled.map +4 -4
  14. package/dist/shaka-player.ui.d.ts +306 -13
  15. package/dist/shaka-player.ui.debug.d.ts +306 -13
  16. package/dist/shaka-player.ui.debug.externs.js +477 -18
  17. package/dist/shaka-player.ui.debug.js +799 -681
  18. package/dist/shaka-player.ui.debug.map +4 -4
  19. package/dist/shaka-player.ui.externs.js +477 -18
  20. package/dist/shaka-player.ui.js +1600 -1533
  21. package/dist/shaka-player.ui.map +4 -4
  22. package/externs/shaka/ads.js +21 -1
  23. package/externs/shaka/manifest.js +20 -1
  24. package/externs/shaka/manifest_parser.js +18 -1
  25. package/externs/shaka/player.js +164 -50
  26. package/lib/ads/ad_manager.js +225 -10
  27. package/lib/ads/ad_utils.js +323 -0
  28. package/lib/ads/ads_stats.js +24 -0
  29. package/lib/ads/client_side_ad_manager.js +42 -34
  30. package/lib/ads/interstitial_ad.js +292 -0
  31. package/lib/ads/interstitial_ad_manager.js +772 -0
  32. package/lib/ads/media_tailor_ad_manager.js +19 -22
  33. package/lib/ads/server_side_ad_manager.js +12 -15
  34. package/lib/cast/cast_utils.js +1 -0
  35. package/lib/cea/cea_utils.js +12 -0
  36. package/lib/cea/mp4_cea_parser.js +17 -0
  37. package/lib/dash/dash_parser.js +81 -28
  38. package/lib/dash/segment_base.js +3 -0
  39. package/lib/hls/hls_parser.js +429 -121
  40. package/lib/hls/manifest_text_parser.js +1 -0
  41. package/lib/media/adaptation_set_criteria.js +31 -1
  42. package/lib/media/drm_engine.js +16 -2
  43. package/lib/media/manifest_filterer.js +7 -5
  44. package/lib/media/preload_manager.js +8 -10
  45. package/lib/media/presentation_timeline.js +37 -5
  46. package/lib/media/quality_observer.js +68 -2
  47. package/lib/media/segment_reference.js +1 -1
  48. package/lib/media/segment_utils.js +33 -1
  49. package/lib/media/streaming_engine.js +120 -43
  50. package/lib/mss/mss_parser.js +13 -2
  51. package/lib/net/networking_engine.js +1 -0
  52. package/lib/offline/manifest_converter.js +3 -0
  53. package/lib/offline/offline_manifest_parser.js +5 -0
  54. package/lib/offline/storage.js +2 -0
  55. package/lib/player.js +498 -165
  56. package/lib/util/fake_event.js +1 -0
  57. package/lib/util/manifest_parser_utils.js +9 -2
  58. package/lib/util/mp4_box_parsers.js +32 -0
  59. package/lib/util/player_configuration.js +21 -9
  60. package/lib/util/stats.js +43 -0
  61. package/package.json +1 -1
  62. package/shaka-player.uncompiled.js +2 -0
  63. package/ui/ad_counter.js +3 -3
  64. package/ui/ad_position.js +3 -3
  65. package/ui/ad_statistics_button.js +237 -0
  66. package/ui/audio_language_selection.js +8 -4
  67. package/ui/controls.js +6 -3
  68. package/ui/element.js +3 -3
  69. package/ui/enums.js +1 -0
  70. package/ui/externs/ui.js +3 -0
  71. package/ui/language_utils.js +48 -3
  72. package/ui/less/containers.less +32 -0
  73. package/ui/less/thumbnails.less +13 -0
  74. package/ui/locales/en.json +2 -0
  75. package/ui/locales/es-419.json +2 -0
  76. package/ui/locales/es.json +3 -0
  77. package/ui/locales/fr.json +1 -0
  78. package/ui/locales/pl.json +1 -0
  79. package/ui/locales/pt-BR.json +1 -0
  80. package/ui/locales/pt-PT.json +1 -0
  81. package/ui/locales/source.json +8 -0
  82. package/ui/mute_button.js +4 -4
  83. package/ui/overflow_menu.js +3 -3
  84. package/ui/play_button.js +5 -7
  85. package/ui/presentation_time.js +3 -3
  86. package/ui/save_video_frame_button.js +196 -0
  87. package/ui/seek_bar.js +75 -20
  88. package/ui/skip_ad_button.js +4 -4
  89. package/ui/statistics_button.js +14 -2
  90. package/ui/ui.js +15 -0
  91. package/ui/volume_bar.js +4 -4
@@ -0,0 +1,772 @@
1
+ /*! @license
2
+ * Shaka Player
3
+ * Copyright 2016 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+
8
+ goog.provide('shaka.ads.InterstitialAdManager');
9
+
10
+ goog.require('goog.asserts');
11
+ goog.require('shaka.Player');
12
+ goog.require('shaka.ads.InterstitialAd');
13
+ goog.require('shaka.ads.Utils');
14
+ goog.require('shaka.log');
15
+ goog.require('shaka.media.PreloadManager');
16
+ goog.require('shaka.net.NetworkingEngine');
17
+ goog.require('shaka.util.Dom');
18
+ goog.require('shaka.util.EventManager');
19
+ goog.require('shaka.util.FakeEvent');
20
+ goog.require('shaka.util.IReleasable');
21
+ goog.require('shaka.util.Platform');
22
+ goog.require('shaka.util.PublicPromise');
23
+ goog.require('shaka.util.StringUtils');
24
+ goog.require('shaka.util.Timer');
25
+
26
+
27
+ /**
28
+ * A class responsible for Interstitial ad interactions.
29
+ *
30
+ * @implements {shaka.util.IReleasable}
31
+ */
32
+ shaka.ads.InterstitialAdManager = class {
33
+ /**
34
+ * @param {HTMLElement} adContainer
35
+ * @param {shaka.Player} basePlayer
36
+ * @param {HTMLMediaElement} baseVideo
37
+ * @param {function(!shaka.util.FakeEvent)} onEvent
38
+ */
39
+ constructor(adContainer, basePlayer, baseVideo, onEvent) {
40
+ /** @private {HTMLElement} */
41
+ this.adContainer_ = adContainer;
42
+
43
+ /** @private {shaka.Player} */
44
+ this.basePlayer_ = basePlayer;
45
+
46
+ /** @private {HTMLMediaElement} */
47
+ this.baseVideo_ = baseVideo;
48
+
49
+ /** @private {?HTMLMediaElement} */
50
+ this.adVideo_ = null;
51
+
52
+ /** @private {boolean} */
53
+ this.usingBaseVideo_ = true;
54
+
55
+ /** @private {HTMLMediaElement} */
56
+ this.video_ = this.baseVideo_;
57
+
58
+ /** @private {function(!shaka.util.FakeEvent)} */
59
+ this.onEvent_ = onEvent;
60
+
61
+ /** @private {!Set.<string>} */
62
+ this.interstitialIds_ = new Set();
63
+
64
+ /** @private {!Set.<shaka.ads.InterstitialAdManager.Interstitial>} */
65
+ this.interstitials_ = new Set();
66
+
67
+ /**
68
+ * @private {!Map.<shaka.ads.InterstitialAdManager.Interstitial,
69
+ * Promise<?shaka.media.PreloadManager>>}
70
+ */
71
+ this.preloadManagerInterstitials_ = new Map();
72
+
73
+ /** @private {shaka.Player} */
74
+ this.player_ = new shaka.Player();
75
+
76
+ this.updatePlayerConfig_();
77
+
78
+ /** @private {shaka.util.EventManager} */
79
+ this.eventManager_ = new shaka.util.EventManager();
80
+
81
+ /** @private {shaka.util.EventManager} */
82
+ this.adEventManager_ = new shaka.util.EventManager();
83
+
84
+ /** @private {boolean} */
85
+ this.playingAd_ = false;
86
+
87
+ /** @private {?number} */
88
+ this.lastTime_ = null;
89
+
90
+ this.eventManager_.listen(this.baseVideo_, 'timeupdate', () => {
91
+ if (this.playingAd_) {
92
+ return;
93
+ }
94
+ const needPreRoll = this.lastTime_ == null;
95
+ this.lastTime_ = this.baseVideo_.currentTime;
96
+ const currentInterstitial = this.getCurrentInterstitial_(needPreRoll);
97
+ if (currentInterstitial) {
98
+ this.setupAd_(currentInterstitial, /* sequenceLength= */ 1,
99
+ /* adPosition= */ 1, /* initialTime= */ Date.now());
100
+ }
101
+ });
102
+
103
+ /** @private {shaka.util.Timer} */
104
+ this.pollTimer_ = new shaka.util.Timer(async () => {
105
+ if (this.interstitials_.size && !this.playingAd_ &&
106
+ this.lastTime_ != null) {
107
+ let cuepointsChanged = false;
108
+ const interstitials = Array.from(this.interstitials_);
109
+ const seekRange = this.basePlayer_.seekRange();
110
+ for (const interstitial of interstitials) {
111
+ if ((seekRange.start - interstitial.startTime) >= 1) {
112
+ if (this.preloadManagerInterstitials_.has(interstitial)) {
113
+ const preloadManager =
114
+ // eslint-disable-next-line no-await-in-loop
115
+ await this.preloadManagerInterstitials_.get(interstitial);
116
+ if (preloadManager) {
117
+ preloadManager.destroy();
118
+ }
119
+ this.preloadManagerInterstitials_.delete(interstitial);
120
+ }
121
+ const interstitialId = JSON.stringify(interstitial);
122
+ if (this.interstitialIds_.has(interstitialId)) {
123
+ this.interstitialIds_.delete(interstitialId);
124
+ }
125
+ this.interstitials_.delete(interstitial);
126
+ cuepointsChanged = true;
127
+ } else {
128
+ const difference = interstitial.startTime - this.lastTime_;
129
+ if (difference > 0 && difference <= 10) {
130
+ if (!this.preloadManagerInterstitials_.has(interstitial)) {
131
+ this.preloadManagerInterstitials_.set(
132
+ interstitial, this.player_.preload(interstitial.uri));
133
+ }
134
+ }
135
+ }
136
+ }
137
+ if (cuepointsChanged) {
138
+ this.cuepointsChanged_();
139
+ }
140
+ }
141
+ }).tickEvery(/* seconds= */ 1);
142
+ }
143
+
144
+ /**
145
+ * Called by the AdManager to provide an updated configuration any time it
146
+ * changes.
147
+ *
148
+ * @param {shaka.extern.AdsConfiguration} config
149
+ */
150
+ configure(config) {
151
+ if (!this.adContainer_ ||
152
+ this.usingBaseVideo_ != config.supportsMultipleMediaElements) {
153
+ return;
154
+ }
155
+ this.usingBaseVideo_ = !config.supportsMultipleMediaElements;
156
+ if (this.usingBaseVideo_) {
157
+ this.video_ = this.baseVideo_;
158
+ if (this.adVideo_) {
159
+ if (this.adVideo_.parentElement) {
160
+ this.adContainer_.removeChild(this.adVideo_);
161
+ }
162
+ this.adVideo_ = null;
163
+ }
164
+ } else {
165
+ if (!this.adVideo_) {
166
+ this.adVideo_ = this.createMediaElement_();
167
+ }
168
+ this.video_ = this.adVideo_;
169
+ }
170
+ }
171
+
172
+
173
+ /**
174
+ * Resets the Interstitial manager and removes any continuous polling.
175
+ */
176
+ stop() {
177
+ if (this.adEventManager_) {
178
+ this.adEventManager_.removeAll();
179
+ }
180
+ this.interstitialIds_.clear();
181
+ this.interstitials_.clear();
182
+ this.player_.destroyAllPreloads();
183
+ this.preloadManagerInterstitials_.clear();
184
+ this.player_.detach();
185
+ this.playingAd_ = false;
186
+ this.lastTime_ = null;
187
+ }
188
+
189
+ /** @override */
190
+ release() {
191
+ this.stop();
192
+ if (this.eventManager_) {
193
+ this.eventManager_.release();
194
+ }
195
+ if (this.adEventManager_) {
196
+ this.adEventManager_.release();
197
+ }
198
+ if (this.adContainer_) {
199
+ shaka.util.Dom.removeAllChildren(this.adContainer_);
200
+ }
201
+ this.player_.destroy();
202
+ }
203
+
204
+
205
+ /**
206
+ * @param {shaka.extern.Interstitial} interstitial
207
+ */
208
+ async addMetadata(interstitial) {
209
+ if (this.basePlayer_.getLoadMode() == shaka.Player.LoadMode.SRC_EQUALS &&
210
+ this.usingBaseVideo_) {
211
+ shaka.log.alwaysWarn(
212
+ 'Unsupported interstitial when using single media element',
213
+ interstitial);
214
+ return;
215
+ }
216
+ this.updatePlayerConfig_();
217
+ let cuepointsChanged = false;
218
+ const interstitialsAd = await this.getInterstitialsInfo_(interstitial);
219
+ if (interstitialsAd.length) {
220
+ for (const interstitialAd of interstitialsAd) {
221
+ const interstitialId = JSON.stringify(interstitialAd);
222
+ if (this.interstitialIds_.has(interstitialId)) {
223
+ continue;
224
+ }
225
+ cuepointsChanged = true;
226
+ this.interstitialIds_.add(interstitialId);
227
+ this.interstitials_.add(interstitialAd);
228
+ let shouldPreload = false;
229
+ if (interstitial.pre && this.lastTime_ == null) {
230
+ shouldPreload = true;
231
+ } else if (interstitialAd.startTime == 0 && !interstitialAd.canJump) {
232
+ shouldPreload = true;
233
+ } else if (this.lastTime_ != null) {
234
+ const difference = interstitial.startTime - this.lastTime_;
235
+ if (difference > 0 && difference <= 10) {
236
+ shouldPreload = true;
237
+ }
238
+ }
239
+ if (shouldPreload) {
240
+ if (!this.preloadManagerInterstitials_.has(interstitialAd)) {
241
+ this.preloadManagerInterstitials_.set(
242
+ interstitialAd, this.player_.preload(interstitialAd.uri));
243
+ }
244
+ }
245
+ }
246
+ } else {
247
+ shaka.log.alwaysWarn('Unsupported interstitial', interstitial);
248
+ }
249
+ if (cuepointsChanged) {
250
+ this.cuepointsChanged_();
251
+ }
252
+ }
253
+
254
+ /**
255
+ * @return {!HTMLMediaElement}
256
+ * @private
257
+ */
258
+ createMediaElement_() {
259
+ const video = /** @type {!HTMLMediaElement} */(
260
+ document.createElement(this.baseVideo_.tagName));
261
+ video.autoplay = true;
262
+ video.style.position = 'absolute';
263
+ video.style.top = '0';
264
+ video.style.left = '0';
265
+ video.style.width = '100%';
266
+ video.style.height = '100%';
267
+ video.style.backgroundColor = 'rgb(0, 0, 0)';
268
+ video.setAttribute('playsinline', '');
269
+ return video;
270
+ }
271
+
272
+
273
+ /**
274
+ * @param {boolean} needPreRoll
275
+ * @param {number=} numberToSkip
276
+ * @return {?shaka.ads.InterstitialAdManager.Interstitial}
277
+ * @private
278
+ */
279
+ getCurrentInterstitial_(needPreRoll, numberToSkip = 0) {
280
+ let skipped = 0;
281
+ let currentInterstitial = null;
282
+ if (this.interstitials_.size && this.lastTime_ != null) {
283
+ const isEnded = this.baseVideo_.ended;
284
+ const interstitials = Array.from(this.interstitials_).sort((a, b) => {
285
+ return b.startTime - a.startTime;
286
+ });
287
+ const roundDecimals = (number) => {
288
+ return Math.round(number * 1000) / 1000;
289
+ };
290
+ for (const interstitial of interstitials) {
291
+ let isValid = false;
292
+ if (needPreRoll) {
293
+ isValid = interstitial.pre;
294
+ } else if (isEnded) {
295
+ isValid = interstitial.post;
296
+ } else if (!interstitial.pre && !interstitial.post) {
297
+ const difference =
298
+ this.lastTime_ - roundDecimals(interstitial.startTime);
299
+ isValid = difference > 0 &&
300
+ (difference <= 1 || !interstitial.canJump);
301
+ }
302
+ if (isValid) {
303
+ if (skipped == numberToSkip) {
304
+ currentInterstitial = interstitial;
305
+ break;
306
+ }
307
+ skipped++;
308
+ }
309
+ }
310
+ }
311
+ return currentInterstitial;
312
+ }
313
+
314
+
315
+ /**
316
+ * @param {shaka.ads.InterstitialAdManager.Interstitial} interstitial
317
+ * @param {number} sequenceLength
318
+ * @param {number} adPosition
319
+ * @param {number} initialTime the clock time the ad started at
320
+ * @param {number=} oncePlayed
321
+ * @private
322
+ */
323
+ async setupAd_(interstitial, sequenceLength, adPosition, initialTime,
324
+ oncePlayed = 0) {
325
+ goog.asserts.assert(this.video_, 'Must have video');
326
+
327
+ const startTime = Date.now();
328
+
329
+ if (!this.video_.parentElement && this.adContainer_) {
330
+ this.adContainer_.appendChild(this.video_);
331
+ }
332
+
333
+ if (adPosition == 1 && sequenceLength == 1) {
334
+ sequenceLength = Array.from(this.interstitials_).filter((i) => {
335
+ if (interstitial.pre) {
336
+ return i.pre == interstitial.pre;
337
+ } else if (interstitial.post) {
338
+ return i.post == interstitial.post;
339
+ }
340
+ return Math.abs(i.startTime - interstitial.startTime) < 0.001;
341
+ }).length;
342
+ }
343
+
344
+ if (interstitial.once) {
345
+ oncePlayed++;
346
+ this.interstitials_.delete(interstitial);
347
+ this.cuepointsChanged_();
348
+ }
349
+ this.playingAd_ = true;
350
+
351
+ if (this.usingBaseVideo_ && adPosition == 1) {
352
+ this.onEvent_(new shaka.util.FakeEvent(
353
+ shaka.ads.Utils.AD_CONTENT_PAUSE_REQUESTED,
354
+ (new Map()).set('saveLivePosition', true)));
355
+ const detachBasePlayerPromise = new shaka.util.PublicPromise();
356
+ const checkState = async (e) => {
357
+ if (e['state'] == 'detach') {
358
+ if (shaka.util.Platform.isSmartTV()) {
359
+ await new Promise(
360
+ (resolve) => new shaka.util.Timer(resolve).tickAfter(0.1));
361
+ }
362
+ detachBasePlayerPromise.resolve();
363
+ this.adEventManager_.unlisten(
364
+ this.basePlayer_, 'onstatechange', checkState);
365
+ }
366
+ };
367
+ this.adEventManager_.listen(
368
+ this.basePlayer_, 'onstatechange', checkState);
369
+ await detachBasePlayerPromise;
370
+ }
371
+ if (!this.usingBaseVideo_) {
372
+ this.baseVideo_.pause();
373
+ if (interstitial.resumeOffset != null &&
374
+ interstitial.resumeOffset != 0) {
375
+ this.baseVideo_.currentTime += interstitial.resumeOffset;
376
+ }
377
+ }
378
+
379
+ let unloadingInterstitial = false;
380
+ /** @type {?shaka.util.Timer} */
381
+ let playoutLimitTimer = null;
382
+
383
+ const updateBaseVideoTime = () => {
384
+ if (!this.usingBaseVideo_) {
385
+ if (interstitial.resumeOffset == null) {
386
+ if (interstitial.timelineRange && interstitial.endTime) {
387
+ if (this.baseVideo_.currentTime != interstitial.endTime) {
388
+ this.baseVideo_.currentTime = interstitial.endTime;
389
+ }
390
+ } else {
391
+ const now = Date.now();
392
+ this.baseVideo_.currentTime += (now - initialTime) / 1000;
393
+ initialTime = now;
394
+ }
395
+ }
396
+ }
397
+ };
398
+
399
+ const basicTask = async () => {
400
+ updateBaseVideoTime();
401
+ if (playoutLimitTimer) {
402
+ playoutLimitTimer.stop();
403
+ }
404
+ goog.asserts.assert(typeof(oncePlayed) == 'number',
405
+ 'Should be an number!');
406
+ // Optimization to avoid returning to main content when there is another
407
+ // interstitial below.
408
+ const nextCurrentInterstitial = this.getCurrentInterstitial_(
409
+ interstitial.pre, adPosition - oncePlayed);
410
+ if (nextCurrentInterstitial) {
411
+ this.onEvent_(
412
+ new shaka.util.FakeEvent(shaka.ads.Utils.AD_STOPPED));
413
+ this.adEventManager_.removeAll();
414
+ this.setupAd_(nextCurrentInterstitial, sequenceLength,
415
+ ++adPosition, initialTime, oncePlayed);
416
+ } else {
417
+ if (this.usingBaseVideo_) {
418
+ await this.player_.detach();
419
+ } else {
420
+ await this.player_.unload();
421
+ }
422
+ if (this.usingBaseVideo_) {
423
+ let offset = interstitial.resumeOffset;
424
+ if (offset == null) {
425
+ if (interstitial.timelineRange && interstitial.endTime) {
426
+ offset = interstitial.endTime - (this.lastTime_ || 0);
427
+ } else {
428
+ offset = (Date.now() - initialTime) / 1000;
429
+ }
430
+ }
431
+ this.onEvent_(new shaka.util.FakeEvent(
432
+ shaka.ads.Utils.AD_CONTENT_RESUME_REQUESTED,
433
+ (new Map()).set('offset', offset)));
434
+ }
435
+ this.onEvent_(
436
+ new shaka.util.FakeEvent(shaka.ads.Utils.AD_STOPPED));
437
+ this.adEventManager_.removeAll();
438
+ this.playingAd_ = false;
439
+ if (!this.usingBaseVideo_) {
440
+ updateBaseVideoTime();
441
+ if (!this.baseVideo_.ended) {
442
+ this.baseVideo_.play();
443
+ }
444
+ }
445
+ }
446
+ };
447
+ const error = async (e) => {
448
+ if (unloadingInterstitial) {
449
+ return;
450
+ }
451
+ unloadingInterstitial = true;
452
+ this.onEvent_(new shaka.util.FakeEvent(shaka.ads.Utils.AD_ERROR,
453
+ (new Map()).set('originalEvent', e)));
454
+ await basicTask();
455
+ };
456
+ const complete = async () => {
457
+ if (unloadingInterstitial) {
458
+ return;
459
+ }
460
+ unloadingInterstitial = true;
461
+ await basicTask();
462
+ this.onEvent_(
463
+ new shaka.util.FakeEvent(shaka.ads.Utils.AD_COMPLETE));
464
+ };
465
+ const onSkip = async () => {
466
+ if (unloadingInterstitial) {
467
+ return;
468
+ }
469
+ unloadingInterstitial = true;
470
+ this.onEvent_(new shaka.util.FakeEvent(shaka.ads.Utils.AD_SKIPPED));
471
+ await basicTask();
472
+ };
473
+
474
+ const ad = new shaka.ads.InterstitialAd(this.video_,
475
+ interstitial.isSkippable, onSkip, sequenceLength, adPosition);
476
+ if (!this.usingBaseVideo_) {
477
+ ad.setMuted(this.baseVideo_.muted);
478
+ ad.setVolume(this.baseVideo_.volume);
479
+ }
480
+
481
+ this.onEvent_(
482
+ new shaka.util.FakeEvent(shaka.ads.Utils.AD_STARTED,
483
+ (new Map()).set('ad', ad)));
484
+
485
+ if (ad.canSkipNow()) {
486
+ this.onEvent_(new shaka.util.FakeEvent(
487
+ shaka.ads.Utils.AD_SKIP_STATE_CHANGED));
488
+ }
489
+ this.adEventManager_.listenOnce(this.player_, 'error', error);
490
+ this.adEventManager_.listenOnce(this.player_, 'firstquartile', () => {
491
+ updateBaseVideoTime();
492
+ this.onEvent_(
493
+ new shaka.util.FakeEvent(shaka.ads.Utils.AD_FIRST_QUARTILE));
494
+ });
495
+ this.adEventManager_.listenOnce(this.player_, 'midpoint', () => {
496
+ updateBaseVideoTime();
497
+ this.onEvent_(
498
+ new shaka.util.FakeEvent(shaka.ads.Utils.AD_MIDPOINT));
499
+ });
500
+ this.adEventManager_.listenOnce(this.player_, 'thirdquartile', () => {
501
+ updateBaseVideoTime();
502
+ this.onEvent_(
503
+ new shaka.util.FakeEvent(shaka.ads.Utils.AD_THIRD_QUARTILE));
504
+ });
505
+ this.adEventManager_.listenOnce(this.player_, 'complete', complete);
506
+ this.adEventManager_.listen(this.video_, 'play', () => {
507
+ this.onEvent_(
508
+ new shaka.util.FakeEvent(shaka.ads.Utils.AD_RESUMED));
509
+ });
510
+ this.adEventManager_.listen(this.video_, 'pause', () => {
511
+ this.onEvent_(
512
+ new shaka.util.FakeEvent(shaka.ads.Utils.AD_PAUSED));
513
+ });
514
+ this.adEventManager_.listen(this.video_, 'volumechange', () => {
515
+ if (this.video_.muted) {
516
+ this.onEvent_(
517
+ new shaka.util.FakeEvent(shaka.ads.Utils.AD_MUTED));
518
+ } else {
519
+ this.onEvent_(
520
+ new shaka.util.FakeEvent(shaka.ads.Utils.AD_VOLUME_CHANGED));
521
+ }
522
+ });
523
+ try {
524
+ if (interstitial.playoutLimit) {
525
+ playoutLimitTimer = new shaka.util.Timer(() => {
526
+ ad.skip();
527
+ }).tickAfter(interstitial.playoutLimit);
528
+ }
529
+ this.updatePlayerConfig_();
530
+ await this.player_.attach(this.video_);
531
+ if (this.preloadManagerInterstitials_.has(interstitial)) {
532
+ const preloadManager =
533
+ await this.preloadManagerInterstitials_.get(interstitial);
534
+ this.preloadManagerInterstitials_.delete(interstitial);
535
+ if (preloadManager) {
536
+ await this.player_.load(preloadManager);
537
+ } else {
538
+ await this.player_.load(interstitial.uri);
539
+ }
540
+ } else {
541
+ await this.player_.load(interstitial.uri);
542
+ }
543
+ const loadTime = (Date.now() - startTime) / 1000;
544
+ this.onEvent_(new shaka.util.FakeEvent(shaka.ads.Utils.ADS_LOADED,
545
+ (new Map()).set('loadTime', loadTime)));
546
+ if (this.usingBaseVideo_) {
547
+ this.baseVideo_.play();
548
+ }
549
+ } catch (e) {
550
+ error(e);
551
+ }
552
+ }
553
+
554
+
555
+ /**
556
+ * @param {shaka.extern.Interstitial} interstitial
557
+ * @return {!Promise.<!Array.<shaka.ads.InterstitialAdManager.Interstitial>>}
558
+ * @private
559
+ */
560
+ async getInterstitialsInfo_(interstitial) {
561
+ const interstitialsAd = [];
562
+ if (!interstitial) {
563
+ return interstitialsAd;
564
+ }
565
+ const assetUri = interstitial.values.find((v) => v.key == 'X-ASSET-URI');
566
+ const assetList = interstitial.values.find((v) => v.key == 'X-ASSET-LIST');
567
+ if (!assetUri && !assetList) {
568
+ return interstitialsAd;
569
+ }
570
+ const restrict = interstitial.values.find((v) => v.key == 'X-RESTRICT');
571
+ let isSkippable = true;
572
+ let canJump = true;
573
+ if (restrict && restrict.data) {
574
+ const data = /** @type {string} */(restrict.data);
575
+ isSkippable = !data.includes('SKIP');
576
+ canJump = !data.includes('JUMP');
577
+ }
578
+ let resumeOffset = null;
579
+ const resume = interstitial.values.find((v) => v.key == 'X-RESUME-OFFSET');
580
+ if (resume) {
581
+ const resumeOffsetString = /** @type {string} */(resume.data);
582
+ resumeOffset = parseFloat(resumeOffsetString);
583
+ if (isNaN(resumeOffset)) {
584
+ resumeOffset = null;
585
+ }
586
+ }
587
+ let playoutLimit = null;
588
+ const playout = interstitial.values.find((v) => v.key == 'X-PLAYOUT-LIMIT');
589
+ if (playout) {
590
+ const playoutLimitString = /** @type {string} */(playout.data);
591
+ playoutLimit = parseFloat(playoutLimitString);
592
+ if (isNaN(playoutLimit)) {
593
+ playoutLimit = null;
594
+ }
595
+ }
596
+ let once = false;
597
+ let pre = false;
598
+ let post = false;
599
+ const cue = interstitial.values.find((v) => v.key == 'CUE');
600
+ if (cue) {
601
+ const data = /** @type {string} */(cue.data);
602
+ once = data.includes('ONCE');
603
+ pre = data.includes('PRE');
604
+ post = data.includes('POST');
605
+ }
606
+ let timelineRange = true;
607
+ const timelineOccupies =
608
+ interstitial.values.find((v) => v.key == 'X-TIMELINE-OCCUPIES');
609
+ if (timelineOccupies) {
610
+ const data = /** @type {string} */(timelineOccupies.data);
611
+ timelineRange = data.includes('RANGE');
612
+ } else if (!resume && this.basePlayer_.isLive()) {
613
+ timelineRange = true;
614
+ }
615
+ if (assetUri) {
616
+ const uri = /** @type {string} */(assetUri.data);
617
+ if (!uri) {
618
+ return interstitialsAd;
619
+ }
620
+ interstitialsAd.push({
621
+ startTime: interstitial.startTime,
622
+ endTime: interstitial.endTime,
623
+ uri,
624
+ isSkippable,
625
+ canJump,
626
+ resumeOffset,
627
+ playoutLimit,
628
+ once,
629
+ pre,
630
+ post,
631
+ timelineRange,
632
+ });
633
+ } else if (assetList) {
634
+ const uri = /** @type {string} */(assetList.data);
635
+ if (!uri) {
636
+ return interstitialsAd;
637
+ }
638
+ const type = shaka.net.NetworkingEngine.RequestType.ADS;
639
+ const request = shaka.net.NetworkingEngine.makeRequest(
640
+ [uri],
641
+ shaka.net.NetworkingEngine.defaultRetryParameters());
642
+ const op = this.basePlayer_.getNetworkingEngine().request(type, request);
643
+ try {
644
+ const response = await op.promise;
645
+ const data = shaka.util.StringUtils.fromUTF8(response.data);
646
+ const dataAsJson =
647
+ /** @type {!shaka.ads.InterstitialAdManager.AssetsList} */ (
648
+ JSON.parse(data));
649
+ for (const asset of dataAsJson.ASSETS) {
650
+ if (asset.URI) {
651
+ interstitialsAd.push({
652
+ startTime: interstitial.startTime,
653
+ endTime: interstitial.endTime,
654
+ uri: asset.URI,
655
+ isSkippable,
656
+ canJump,
657
+ resumeOffset,
658
+ playoutLimit,
659
+ once,
660
+ pre,
661
+ post,
662
+ timelineRange,
663
+ });
664
+ }
665
+ }
666
+ } catch (e) {
667
+ // Ignore errors
668
+ }
669
+ }
670
+ return interstitialsAd;
671
+ }
672
+
673
+
674
+ /**
675
+ * @private
676
+ */
677
+ cuepointsChanged_() {
678
+ /** @type {!Array.<!shaka.extern.AdCuePoint>} */
679
+ const cuePoints = [];
680
+ for (const interstitial of this.interstitials_) {
681
+ /** @type {shaka.extern.AdCuePoint} */
682
+ const shakaCuePoint = {
683
+ start: interstitial.startTime,
684
+ end: null,
685
+ };
686
+ if (interstitial.pre) {
687
+ shakaCuePoint.start = 0;
688
+ shakaCuePoint.end = null;
689
+ } else if (interstitial.post) {
690
+ shakaCuePoint.start = -1;
691
+ shakaCuePoint.end = null;
692
+ } else if (interstitial.timelineRange) {
693
+ shakaCuePoint.end = interstitial.endTime;
694
+ }
695
+ const isValid = !cuePoints.find((c) => {
696
+ return shakaCuePoint.start == c.start && shakaCuePoint.end == c.end;
697
+ });
698
+ if (isValid) {
699
+ cuePoints.push(shakaCuePoint);
700
+ }
701
+ }
702
+
703
+ this.onEvent_(new shaka.util.FakeEvent(
704
+ shaka.ads.Utils.CUEPOINTS_CHANGED,
705
+ (new Map()).set('cuepoints', cuePoints)));
706
+ }
707
+
708
+
709
+ /**
710
+ * @private
711
+ */
712
+ updatePlayerConfig_() {
713
+ goog.asserts.assert(this.player_, 'Must have player');
714
+ goog.asserts.assert(this.basePlayer_, 'Must have base player');
715
+ this.player_.configure(this.basePlayer_.getNonDefaultConfiguration());
716
+ const netEngine = this.player_.getNetworkingEngine();
717
+ goog.asserts.assert(netEngine, 'Need networking engine');
718
+ netEngine.clearAllRequestFilters();
719
+ netEngine.clearAllResponseFilters();
720
+ this.basePlayer_.getNetworkingEngine().copyFiltersInto(netEngine);
721
+ }
722
+ };
723
+
724
+
725
+ /**
726
+ * @typedef {{
727
+ * ASSETS: !Array.<shaka.ads.InterstitialAdManager.Asset>
728
+ * }}
729
+ *
730
+ * @property {!Array.<shaka.ads.InterstitialAdManager.Asset>} ASSETS
731
+ */
732
+ shaka.ads.InterstitialAdManager.AssetsList;
733
+
734
+
735
+ /**
736
+ * @typedef {{
737
+ * URI: string
738
+ * }}
739
+ *
740
+ * @property {string} URI
741
+ */
742
+ shaka.ads.InterstitialAdManager.Asset;
743
+
744
+
745
+ /**
746
+ * @typedef {{
747
+ * startTime: number,
748
+ * endTime: ?number,
749
+ * uri: string,
750
+ * isSkippable: boolean,
751
+ * canJump: boolean,
752
+ * resumeOffset: ?number,
753
+ * playoutLimit: ?number,
754
+ * once: boolean,
755
+ * pre: boolean,
756
+ * post: boolean,
757
+ * timelineRange: boolean
758
+ * }}
759
+ *
760
+ * @property {number} startTime
761
+ * @property {?number} endTime
762
+ * @property {string} uri
763
+ * @property {boolean} isSkippable
764
+ * @property {boolean} canJump
765
+ * @property {?number} resumeOffset
766
+ * @property {?number} playoutLimit
767
+ * @property {boolean} once
768
+ * @property {boolean} pre
769
+ * @property {boolean} post
770
+ * @property {boolean} timelineRange
771
+ */
772
+ shaka.ads.InterstitialAdManager.Interstitial;