shaka-player 3.2.0 → 3.2.4

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 (162) hide show
  1. package/.github/workflows/build_and_test.yaml +73 -0
  2. package/.github/workflows/release.yaml +53 -0
  3. package/AUTHORS +3 -0
  4. package/CHANGELOG.md +182 -1
  5. package/CONTRIBUTORS +3 -0
  6. package/README.md +4 -4
  7. package/build/checkversion.py +1 -1
  8. package/build/generateExterns.js +1 -1
  9. package/build/misspellings.txt +1 -0
  10. package/build/shakaBuildHelpers.py +6 -5
  11. package/build/subprocessWindowsPatch.py +103 -0
  12. package/build/test.py +6 -0
  13. package/demo/common/assets.js +1 -1
  14. package/demo/index.html +1 -1
  15. package/dist/controls.css +2 -2
  16. package/dist/controls.css.map +1 -1
  17. package/dist/demo.compiled.debug.js +1 -1
  18. package/dist/demo.compiled.js +1 -1
  19. package/dist/deps.js +8 -8
  20. package/dist/locales.js +37 -37
  21. package/dist/receiver.compiled.debug.js +1 -1
  22. package/dist/receiver.compiled.js +1 -1
  23. package/dist/shaka-player.compiled.d.ts +195 -5
  24. package/dist/shaka-player.compiled.debug.d.ts +195 -5
  25. package/dist/shaka-player.compiled.debug.externs.js +226 -14
  26. package/dist/shaka-player.compiled.debug.js +424 -399
  27. package/dist/shaka-player.compiled.debug.map +4 -4
  28. package/dist/shaka-player.compiled.externs.js +226 -14
  29. package/dist/shaka-player.compiled.js +826 -812
  30. package/dist/shaka-player.compiled.map +4 -4
  31. package/dist/shaka-player.ui.d.ts +197 -16
  32. package/dist/shaka-player.ui.debug.d.ts +197 -16
  33. package/dist/shaka-player.ui.debug.externs.js +230 -27
  34. package/dist/shaka-player.ui.debug.js +466 -442
  35. package/dist/shaka-player.ui.debug.map +4 -4
  36. package/dist/shaka-player.ui.externs.js +230 -27
  37. package/dist/shaka-player.ui.js +946 -932
  38. package/dist/shaka-player.ui.map +4 -4
  39. package/docs/tutorials/fairplay.md +1 -1
  40. package/docs/tutorials/faq.md +14 -0
  41. package/docs/tutorials/ui.md +5 -5
  42. package/externs/ima.js +4 -0
  43. package/externs/shaka/ads.js +25 -0
  44. package/externs/shaka/player.js +8 -0
  45. package/externs/shaka/text.js +10 -0
  46. package/karma.conf.js +20 -15
  47. package/lib/ads/ad_manager.js +46 -26
  48. package/lib/ads/client_side_ad_manager.js +82 -88
  49. package/lib/ads/server_side_ad_manager.js +45 -13
  50. package/lib/cast/cast_proxy.js +6 -2
  51. package/lib/cast/cast_receiver.js +5 -1
  52. package/lib/cast/cast_sender.js +2 -2
  53. package/lib/cea/sei_processor.js +24 -17
  54. package/lib/dash/dash_parser.js +131 -24
  55. package/lib/dash/segment_list.js +1 -1
  56. package/lib/dash/segment_template.js +1 -1
  57. package/lib/hls/hls_parser.js +74 -17
  58. package/lib/hls/manifest_text_parser.js +1 -0
  59. package/lib/media/closed_caption_parser.js +3 -1
  60. package/lib/media/content_workarounds.js +12 -0
  61. package/lib/media/drm_engine.js +20 -1
  62. package/lib/media/gap_jumping_controller.js +8 -4
  63. package/lib/media/media_source_engine.js +1 -2
  64. package/lib/media/playhead.js +1 -1
  65. package/lib/media/region_observer.js +39 -33
  66. package/lib/media/region_timeline.js +17 -19
  67. package/lib/media/segment_index.js +2 -1
  68. package/lib/media/segment_reference.js +19 -1
  69. package/lib/media/streaming_engine.js +8 -4
  70. package/lib/net/networking_engine.js +6 -1
  71. package/lib/offline/download_manager.js +1 -2
  72. package/lib/offline/storage.js +8 -9
  73. package/lib/player.js +129 -59
  74. package/lib/polyfill/aria.js +12 -2
  75. package/lib/polyfill/encryption_scheme.js +2 -0
  76. package/lib/polyfill/fullscreen.js +2 -0
  77. package/lib/polyfill/mathround.js +2 -0
  78. package/lib/polyfill/media_capabilities.js +73 -21
  79. package/lib/polyfill/mediasource.js +8 -0
  80. package/lib/polyfill/orientation.js +3 -1
  81. package/lib/polyfill/patchedmediakeys_apple.js +6 -4
  82. package/lib/polyfill/patchedmediakeys_ms.js +6 -4
  83. package/lib/polyfill/patchedmediakeys_nop.js +2 -0
  84. package/lib/polyfill/patchedmediakeys_webkit.js +6 -4
  85. package/lib/polyfill/pip_webkit.js +2 -0
  86. package/lib/polyfill/storage_estimate.js +2 -0
  87. package/lib/polyfill/video_play_promise.js +2 -0
  88. package/lib/polyfill/videoplaybackquality.js +2 -0
  89. package/lib/polyfill/vttcue.js +2 -0
  90. package/lib/text/cue.js +6 -0
  91. package/lib/text/mp4_ttml_parser.js +3 -0
  92. package/lib/text/text_engine.js +14 -2
  93. package/lib/text/ttml_text_parser.js +135 -87
  94. package/lib/text/ui_text_displayer.js +213 -80
  95. package/lib/text/vtt_text_parser.js +4 -2
  96. package/lib/text/web_vtt_generator.js +21 -12
  97. package/lib/util/fake_event.js +53 -7
  98. package/lib/util/fake_event_target.js +21 -1
  99. package/lib/util/mp4_box_parsers.js +1 -1
  100. package/lib/util/periods.js +4 -1
  101. package/lib/util/platform.js +13 -1
  102. package/lib/util/stream_utils.js +105 -13
  103. package/lib/util/xml_utils.js +50 -12
  104. package/package.json +3 -6
  105. package/roadmap.md +24 -22
  106. package/test/cast/cast_proxy_unit.js +12 -6
  107. package/test/cast/cast_receiver_integration.js +1 -1
  108. package/test/cast/cast_utils_unit.js +1 -1
  109. package/test/cea/mp4_cea_parser_unit.js +26 -0
  110. package/test/dash/dash_parser_live_unit.js +43 -0
  111. package/test/dash/dash_parser_manifest_unit.js +222 -3
  112. package/test/hls/hls_parser_unit.js +79 -1
  113. package/test/media/closed_caption_parser_unit.js +20 -0
  114. package/test/media/drm_engine_integration.js +86 -2
  115. package/test/media/drm_engine_unit.js +57 -0
  116. package/test/media/playhead_unit.js +27 -1
  117. package/test/media/region_observer_unit.js +40 -5
  118. package/test/media/region_timeline_unit.js +40 -22
  119. package/test/media/streaming_engine_integration.js +2 -1
  120. package/test/media/streaming_engine_unit.js +145 -0
  121. package/test/player_integration.js +79 -8
  122. package/test/player_src_equals_integration.js +41 -8
  123. package/test/player_unit.js +14 -4
  124. package/test/test/assets/3675/chunk-stream0-00001.m4s +0 -0
  125. package/test/test/assets/3675/chunk-stream0-00002.m4s +0 -0
  126. package/test/test/assets/3675/chunk-stream0-00003.m4s +0 -0
  127. package/test/test/assets/3675/chunk-stream0-00004.m4s +0 -0
  128. package/test/test/assets/3675/chunk-stream0-00005.m4s +0 -0
  129. package/test/test/assets/3675/chunk-stream0-00006.m4s +0 -0
  130. package/test/test/assets/3675/chunk-stream0-00007.m4s +0 -0
  131. package/test/test/assets/3675/dash_0.mpd +30 -0
  132. package/test/test/assets/3675/dash_1.mpd +30 -0
  133. package/test/test/assets/3675/dash_2.mpd +30 -0
  134. package/test/test/assets/3675/dash_3.mpd +30 -0
  135. package/test/test/assets/3675/dash_4.mpd +30 -0
  136. package/test/test/assets/3675/dash_5.mpd +26 -0
  137. package/test/test/assets/3675/init-stream0.m4s +0 -0
  138. package/test/test/assets/chapters.vtt +2 -2
  139. package/test/test/assets/chapters2.vtt +10 -0
  140. package/test/test/assets/empty_caption_video_init.mp4 +0 -0
  141. package/test/test/assets/empty_caption_video_segment.mp4 +0 -0
  142. package/test/test/util/fake_ad_manager.js +11 -1
  143. package/test/test/util/ttml_utils.js +81 -0
  144. package/test/test/util/util.js +10 -7
  145. package/test/text/mp4_ttml_parser_unit.js +11 -18
  146. package/test/text/ttml_text_parser_unit.js +542 -220
  147. package/test/text/ui_text_displayer_unit.js +93 -20
  148. package/test/text/vtt_text_parser_unit.js +17 -1
  149. package/test/text/web_vtt_generator_unit.js +58 -3
  150. package/test/ui/localization_unit.js +15 -14
  151. package/test/ui/text_displayer_layout_unit.js +21 -8
  152. package/test/util/mp4_box_parsers_unit.js +28 -0
  153. package/test/util/stream_utils_unit.js +60 -3
  154. package/ui/cast_button.js +2 -3
  155. package/ui/controls.js +11 -6
  156. package/ui/less/buttons.less +0 -4
  157. package/ui/less/containers.less +6 -7
  158. package/ui/locales/ru.json +6 -6
  159. package/ui/localization.js +40 -87
  160. package/ui/pip_button.js +2 -3
  161. package/ui/seek_bar.js +1 -2
  162. package/ui/text_selection.js +1 -0
@@ -13,10 +13,12 @@ goog.provide('shaka.ads.ClientSideAdManager');
13
13
 
14
14
  goog.require('goog.asserts');
15
15
  goog.require('shaka.ads.ClientSideAd');
16
+ goog.require('shaka.util.IReleasable');
16
17
 
17
18
 
18
19
  /**
19
20
  * A class responsible for client-side ad interactions.
21
+ * @implements {shaka.util.IReleasable}
20
22
  */
21
23
  shaka.ads.ClientSideAdManager = class {
22
24
  /**
@@ -32,6 +34,9 @@ shaka.ads.ClientSideAdManager = class {
32
34
  /** @private {HTMLMediaElement} */
33
35
  this.video_ = video;
34
36
 
37
+ /** @private {ResizeObserver} */
38
+ this.resizeObserver_ = null;
39
+
35
40
  /** @private {number} */
36
41
  this.requestAdsStartTime_ = NaN;
37
42
 
@@ -106,6 +111,21 @@ shaka.ads.ClientSideAdManager = class {
106
111
  }
107
112
  }
108
113
 
114
+ /** @override */
115
+ release() {
116
+ this.stop();
117
+ if (this.resizeObserver_) {
118
+ this.resizeObserver_.disconnect();
119
+ }
120
+ if (this.eventManager_) {
121
+ this.eventManager_.release();
122
+ }
123
+ if (this.imaAdsManager_) {
124
+ this.imaAdsManager_.destroy();
125
+ }
126
+ this.adsLoader_.destroy();
127
+ }
128
+
109
129
  /**
110
130
  * @param {!google.ima.AdErrorEvent} e
111
131
  * @private
@@ -118,7 +138,7 @@ shaka.ads.ClientSideAdManager = class {
118
138
  // Remove ad breaks from the timeline
119
139
  this.onEvent_(
120
140
  new shaka.util.FakeEvent(shaka.ads.AdManager.CUEPOINTS_CHANGED,
121
- {'cuepoints': []}));
141
+ (new Map()).set('cuepoints', [])));
122
142
  }
123
143
 
124
144
 
@@ -131,30 +151,31 @@ shaka.ads.ClientSideAdManager = class {
131
151
 
132
152
  const now = Date.now() / 1000;
133
153
  const loadTime = now - this.requestAdsStartTime_;
134
- this.onEvent_(
135
- new shaka.util.FakeEvent(shaka.ads.AdManager.ADS_LOADED,
136
- {'loadTime': loadTime}));
154
+ this.onEvent_(new shaka.util.FakeEvent(shaka.ads.AdManager.ADS_LOADED,
155
+ (new Map()).set('loadTime', loadTime)));
137
156
 
138
157
  this.imaAdsManager_ = e.getAdsManager(this.video_);
139
158
 
140
159
  this.onEvent_(new shaka.util.FakeEvent(
141
160
  shaka.ads.AdManager.IMA_AD_MANAGER_LOADED,
142
- {
143
- 'imaAdManager': this.imaAdsManager_,
144
- }));
161
+ (new Map()).set('imaAdManager', this.imaAdsManager_)));
145
162
 
146
163
  const cuePointStarts = this.imaAdsManager_.getCuePoints();
147
164
  if (cuePointStarts.length) {
148
- /** @type {!Array.<!shaka.ads.CuePoint>} */
165
+ /** @type {!Array.<!shaka.extern.AdCuePoint>} */
149
166
  const cuePoints = [];
150
167
  for (const start of cuePointStarts) {
151
- const shakaCuePoint = new shaka.ads.CuePoint(start);
168
+ /** @type {shaka.extern.AdCuePoint} */
169
+ const shakaCuePoint = {
170
+ start: start,
171
+ end: null,
172
+ };
152
173
  cuePoints.push(shakaCuePoint);
153
174
  }
154
175
 
155
- this.onEvent_(
156
- new shaka.util.FakeEvent(shaka.ads.AdManager.CUEPOINTS_CHANGED,
157
- {'cuepoints': cuePoints}));
176
+ this.onEvent_(new shaka.util.FakeEvent(
177
+ shaka.ads.AdManager.CUEPOINTS_CHANGED,
178
+ (new Map()).set('cuepoints', cuePoints)));
158
179
  }
159
180
 
160
181
  this.addImaEventListeners_();
@@ -176,6 +197,16 @@ shaka.ads.ClientSideAdManager = class {
176
197
  this.video_.offsetHeight, viewMode);
177
198
  });
178
199
 
200
+ if ('ResizeObserver' in window) {
201
+ this.resizeObserver_ = new ResizeObserver(() => {
202
+ const viewMode = document.fullscreenElement ?
203
+ google.ima.ViewMode.FULLSCREEN : google.ima.ViewMode.NORMAL;
204
+ this.imaAdsManager_.resize(this.video_.offsetWidth,
205
+ this.video_.offsetHeight, viewMode);
206
+ });
207
+ this.resizeObserver_.observe(this.video_);
208
+ }
209
+
179
210
  // Single video and overlay ads will start at this time
180
211
  // TODO (ismena): Need a better inderstanding of what this does.
181
212
  // The docs say it's called to 'start playing the ads,' but I haven't
@@ -194,6 +225,15 @@ shaka.ads.ClientSideAdManager = class {
194
225
  * @private
195
226
  */
196
227
  addImaEventListeners_() {
228
+ /**
229
+ * @param {!Event} e
230
+ * @param {string} type
231
+ */
232
+ const convertEventAndSend = (e, type) => {
233
+ const data = (new Map()).set('originalEvent', e);
234
+ this.onEvent_(new shaka.util.FakeEvent(type, data));
235
+ };
236
+
197
237
  this.eventManager_.listen(this.imaAdsManager_,
198
238
  google.ima.AdErrorEvent.Type.AD_ERROR, (error) => {
199
239
  this.onAdError_(/** @type {!google.ima.AdErrorEvent} */ (error));
@@ -211,30 +251,22 @@ shaka.ads.ClientSideAdManager = class {
211
251
 
212
252
  this.eventManager_.listen(this.imaAdsManager_,
213
253
  google.ima.AdEvent.Type.FIRST_QUARTILE, (e) => {
214
- this.onEvent_(
215
- new shaka.util.FakeEvent(shaka.ads.AdManager.AD_FIRST_QUARTILE,
216
- {'originalEvent': e}));
254
+ convertEventAndSend(e, shaka.ads.AdManager.AD_FIRST_QUARTILE);
217
255
  });
218
256
 
219
257
  this.eventManager_.listen(this.imaAdsManager_,
220
258
  google.ima.AdEvent.Type.MIDPOINT, (e) => {
221
- this.onEvent_(
222
- new shaka.util.FakeEvent(shaka.ads.AdManager.AD_MIDPOINT,
223
- {'originalEvent': e}));
259
+ convertEventAndSend(e, shaka.ads.AdManager.AD_MIDPOINT);
224
260
  });
225
261
 
226
262
  this.eventManager_.listen(this.imaAdsManager_,
227
263
  google.ima.AdEvent.Type.THIRD_QUARTILE, (e) => {
228
- this.onEvent_(
229
- new shaka.util.FakeEvent(shaka.ads.AdManager.AD_THIRD_QUARTILE,
230
- {'originalEvent': e}));
264
+ convertEventAndSend(e, shaka.ads.AdManager.AD_THIRD_QUARTILE);
231
265
  });
232
266
 
233
267
  this.eventManager_.listen(this.imaAdsManager_,
234
268
  google.ima.AdEvent.Type.COMPLETE, (e) => {
235
- this.onEvent_(
236
- new shaka.util.FakeEvent(shaka.ads.AdManager.AD_COMPLETE,
237
- {'originalEvent': e}));
269
+ convertEventAndSend(e, shaka.ads.AdManager.AD_COMPLETE);
238
270
  });
239
271
 
240
272
  this.eventManager_.listen(this.imaAdsManager_,
@@ -249,140 +281,102 @@ shaka.ads.ClientSideAdManager = class {
249
281
 
250
282
  this.eventManager_.listen(this.imaAdsManager_,
251
283
  google.ima.AdEvent.Type.SKIPPED, (e) => {
252
- this.onEvent_(
253
- new shaka.util.FakeEvent(shaka.ads.AdManager.AD_SKIPPED,
254
- {'originalEvent': e}));
284
+ convertEventAndSend(e, shaka.ads.AdManager.AD_SKIPPED);
255
285
  });
256
286
 
257
287
  this.eventManager_.listen(this.imaAdsManager_,
258
288
  google.ima.AdEvent.Type.VOLUME_CHANGED, (e) => {
259
- this.onEvent_(
260
- new shaka.util.FakeEvent(shaka.ads.AdManager.AD_VOLUME_CHANGED,
261
- {'originalEvent': e}));
289
+ convertEventAndSend(e, shaka.ads.AdManager.AD_VOLUME_CHANGED);
262
290
  });
263
291
 
264
292
  this.eventManager_.listen(this.imaAdsManager_,
265
293
  google.ima.AdEvent.Type.VOLUME_MUTED, (e) => {
266
- this.onEvent_(
267
- new shaka.util.FakeEvent(shaka.ads.AdManager.AD_MUTED,
268
- {'originalEvent': e}));
294
+ convertEventAndSend(e, shaka.ads.AdManager.AD_MUTED);
269
295
  });
270
296
 
271
297
  this.eventManager_.listen(this.imaAdsManager_,
272
298
  google.ima.AdEvent.Type.PAUSED, (e) => {
273
299
  goog.asserts.assert(this.ad_ != null, 'Ad should not be null!');
274
300
  this.ad_.setPaused(true);
275
- this.onEvent_(
276
- new shaka.util.FakeEvent(shaka.ads.AdManager.AD_PAUSED,
277
- {'originalEvent': e}));
301
+ convertEventAndSend(e, shaka.ads.AdManager.AD_PAUSED);
278
302
  });
279
303
 
280
304
  this.eventManager_.listen(this.imaAdsManager_,
281
305
  google.ima.AdEvent.Type.RESUMED, (e) => {
282
306
  goog.asserts.assert(this.ad_ != null, 'Ad should not be null!');
283
307
  this.ad_.setPaused(false);
284
- this.onEvent_(
285
- new shaka.util.FakeEvent(shaka.ads.AdManager.AD_RESUMED,
286
- {'originalEvent': e}));
308
+ convertEventAndSend(e, shaka.ads.AdManager.AD_RESUMED);
287
309
  });
288
310
 
289
311
  this.eventManager_.listen(this.imaAdsManager_,
290
312
  google.ima.AdEvent.Type.SKIPPABLE_STATE_CHANGED, (e) => {
291
313
  goog.asserts.assert(this.ad_ != null, 'Ad should not be null!');
292
- this.onEvent_(new shaka.util.FakeEvent(
293
- shaka.ads.AdManager.AD_SKIP_STATE_CHANGED,
294
- {'originalEvent': e}));
314
+ convertEventAndSend(e, shaka.ads.AdManager.AD_SKIP_STATE_CHANGED);
295
315
  });
296
316
 
297
317
  this.eventManager_.listen(this.imaAdsManager_,
298
318
  google.ima.AdEvent.Type.CLICK, (e) => {
299
- this.onEvent_(new shaka.util.FakeEvent(
300
- shaka.ads.AdManager.AD_CLICKED,
301
- {'originalEvent': e}));
319
+ convertEventAndSend(e, shaka.ads.AdManager.AD_CLICKED);
302
320
  });
303
321
 
304
322
  this.eventManager_.listen(this.imaAdsManager_,
305
323
  google.ima.AdEvent.Type.AD_PROGRESS, (e) => {
306
- this.onEvent_(new shaka.util.FakeEvent(
307
- shaka.ads.AdManager.AD_PROGRESS,
308
- {'originalEvent': e}));
324
+ convertEventAndSend(e, shaka.ads.AdManager.AD_PROGRESS);
309
325
  });
310
326
 
311
327
  this.eventManager_.listen(this.imaAdsManager_,
312
328
  google.ima.AdEvent.Type.AD_BUFFERING, (e) => {
313
- this.onEvent_(new shaka.util.FakeEvent(
314
- shaka.ads.AdManager.AD_BUFFERING,
315
- {'originalEvent': e}));
329
+ convertEventAndSend(e, shaka.ads.AdManager.AD_BUFFERING);
316
330
  });
317
331
 
318
332
  this.eventManager_.listen(this.imaAdsManager_,
319
333
  google.ima.AdEvent.Type.IMPRESSION, (e) => {
320
- this.onEvent_(new shaka.util.FakeEvent(
321
- shaka.ads.AdManager.AD_IMPRESSION,
322
- {'originalEvent': e}));
334
+ convertEventAndSend(e, shaka.ads.AdManager.AD_IMPRESSION);
323
335
  });
324
336
 
325
337
  this.eventManager_.listen(this.imaAdsManager_,
326
338
  google.ima.AdEvent.Type.DURATION_CHANGE, (e) => {
327
- this.onEvent_(new shaka.util.FakeEvent(
328
- shaka.ads.AdManager.AD_DURATION_CHANGED,
329
- {'originalEvent': e}));
339
+ convertEventAndSend(e, shaka.ads.AdManager.AD_DURATION_CHANGED);
330
340
  });
331
341
 
332
342
  this.eventManager_.listen(this.imaAdsManager_,
333
343
  google.ima.AdEvent.Type.USER_CLOSE, (e) => {
334
- this.onEvent_(new shaka.util.FakeEvent(
335
- shaka.ads.AdManager.AD_CLOSED,
336
- {'originalEvent': e}));
344
+ convertEventAndSend(e, shaka.ads.AdManager.AD_CLOSED);
337
345
  });
338
346
 
339
347
  this.eventManager_.listen(this.imaAdsManager_,
340
348
  google.ima.AdEvent.Type.LOADED, (e) => {
341
- this.onEvent_(new shaka.util.FakeEvent(
342
- shaka.ads.AdManager.AD_LOADED,
343
- {'originalEvent': e}));
349
+ convertEventAndSend(e, shaka.ads.AdManager.AD_LOADED);
344
350
  });
345
351
 
346
352
  this.eventManager_.listen(this.imaAdsManager_,
347
353
  google.ima.AdEvent.Type.ALL_ADS_COMPLETED, (e) => {
348
- this.onEvent_(new shaka.util.FakeEvent(
349
- shaka.ads.AdManager.ALL_ADS_COMPLETED,
350
- {'originalEvent': e}));
354
+ convertEventAndSend(e, shaka.ads.AdManager.ALL_ADS_COMPLETED);
351
355
  });
352
356
 
353
357
  this.eventManager_.listen(this.imaAdsManager_,
354
358
  google.ima.AdEvent.Type.LINEAR_CHANGED, (e) => {
355
- this.onEvent_(new shaka.util.FakeEvent(
356
- shaka.ads.AdManager.AD_LINEAR_CHANGED,
357
- {'originalEvent': e}));
359
+ convertEventAndSend(e, shaka.ads.AdManager.AD_LINEAR_CHANGED);
358
360
  });
359
361
 
360
362
  this.eventManager_.listen(this.imaAdsManager_,
361
363
  google.ima.AdEvent.Type.AD_METADATA, (e) => {
362
- this.onEvent_(new shaka.util.FakeEvent(
363
- shaka.ads.AdManager.AD_METADATA,
364
- {'originalEvent': e}));
364
+ convertEventAndSend(e, shaka.ads.AdManager.AD_METADATA);
365
365
  });
366
366
 
367
367
  this.eventManager_.listen(this.imaAdsManager_,
368
368
  google.ima.AdEvent.Type.LOG, (e) => {
369
- this.onEvent_(new shaka.util.FakeEvent(
370
- shaka.ads.AdManager.AD_RECOVERABLE_ERROR,
371
- {'originalEvent': e}));
369
+ convertEventAndSend(e, shaka.ads.AdManager.AD_RECOVERABLE_ERROR);
372
370
  });
373
371
 
374
372
  this.eventManager_.listen(this.imaAdsManager_,
375
373
  google.ima.AdEvent.Type.AD_BREAK_READY, (e) => {
376
- this.onEvent_(new shaka.util.FakeEvent(
377
- shaka.ads.AdManager.AD_BREAK_READY,
378
- {'originalEvent': e}));
374
+ convertEventAndSend(e, shaka.ads.AdManager.AD_BREAK_READY);
379
375
  });
380
376
 
381
377
  this.eventManager_.listen(this.imaAdsManager_,
382
378
  google.ima.AdEvent.Type.INTERACTION, (e) => {
383
- this.onEvent_(new shaka.util.FakeEvent(
384
- shaka.ads.AdManager.AD_INTERACTION,
385
- {'originalEvent': e}));
379
+ convertEventAndSend(e, shaka.ads.AdManager.AD_INTERACTION);
386
380
  });
387
381
  }
388
382
 
@@ -396,12 +390,12 @@ shaka.ads.ClientSideAdManager = class {
396
390
 
397
391
  const imaAd = e.getAd();
398
392
  this.ad_ = new shaka.ads.ClientSideAd(imaAd, this.imaAdsManager_);
399
- this.onEvent_(new shaka.util.FakeEvent(shaka.ads.AdManager.AD_STARTED,
400
- {
401
- 'ad': this.ad_,
402
- 'sdkAdObject': imaAd,
403
- 'originalEvent': e,
404
- }));
393
+ const data = new Map()
394
+ .set('ad', this.ad_)
395
+ .set('sdkAdObject', imaAd)
396
+ .set('originalEvent', e);
397
+ this.onEvent_(new shaka.util.FakeEvent(
398
+ shaka.ads.AdManager.AD_STARTED, data));
405
399
  this.adContainer_.setAttribute('ad-active', 'true');
406
400
  this.video_.pause();
407
401
  }
@@ -412,7 +406,7 @@ shaka.ads.ClientSideAdManager = class {
412
406
  */
413
407
  onAdComplete_(e) {
414
408
  this.onEvent_(new shaka.util.FakeEvent(shaka.ads.AdManager.AD_STOPPED,
415
- {'originalEvent': e}));
409
+ (new Map()).set('originalEvent', e)));
416
410
  this.adContainer_.removeAttribute('ad-active');
417
411
  this.video_.play();
418
412
  }
@@ -14,10 +14,12 @@ goog.provide('shaka.ads.ServerSideAdManager');
14
14
  goog.require('goog.asserts');
15
15
  goog.require('shaka.ads.ServerSideAd');
16
16
  goog.require('shaka.log');
17
+ goog.require('shaka.util.IReleasable');
17
18
 
18
19
 
19
20
  /**
20
21
  * A class responsible for server-side ad interactions.
22
+ * @implements {shaka.util.IReleasable}
21
23
  */
22
24
  shaka.ads.ServerSideAdManager = class {
23
25
  /**
@@ -62,6 +64,9 @@ shaka.ads.ServerSideAdManager = class {
62
64
  /** @private {string} */
63
65
  this.backupUrl_ = '';
64
66
 
67
+ /** @private {!Array.<!shaka.extern.AdCuePoint>} */
68
+ this.currentCuePoints_ = [];
69
+
65
70
  /** @private {shaka.util.EventManager} */
66
71
  this.eventManager_ = new shaka.util.EventManager();
67
72
 
@@ -75,9 +80,7 @@ shaka.ads.ServerSideAdManager = class {
75
80
 
76
81
  this.onEvent_(new shaka.util.FakeEvent(
77
82
  shaka.ads.AdManager.IMA_STREAM_MANAGER_LOADED,
78
- {
79
- 'imaStreamManager': this.streamManager_,
80
- }));
83
+ (new Map()).set('imaStreamManager', this.streamManager_)));
81
84
 
82
85
  // Events
83
86
  this.eventManager_.listen(this.streamManager_,
@@ -201,9 +204,26 @@ shaka.ads.ServerSideAdManager = class {
201
204
  * Resets the stream manager and removes any continuous polling.
202
205
  */
203
206
  stop() {
204
- this.streamManager_.reset();
207
+ // TODO:
208
+ // For SS DAI streams, if a different asset gets unloaded as
209
+ // part of the process
210
+ // of loading a DAI asset, stream manager state gets reset and we
211
+ // don't get any ad events.
212
+ // We need to figure out if it makes sense to stop the SS
213
+ // manager on unload, and, if it does, find
214
+ // a way to do it safely.
215
+ // this.streamManager_.reset();
205
216
  this.backupUrl_ = '';
206
217
  this.snapForwardTime_ = null;
218
+ this.currentCuePoints_ = [];
219
+ }
220
+
221
+ /** @override */
222
+ release() {
223
+ this.stop();
224
+ if (this.eventManager_) {
225
+ this.eventManager_.release();
226
+ }
207
227
  }
208
228
 
209
229
  /**
@@ -233,6 +253,13 @@ shaka.ads.ServerSideAdManager = class {
233
253
  }
234
254
  }
235
255
 
256
+ /**
257
+ * @return {!Array.<!shaka.extern.AdCuePoint>}
258
+ */
259
+ getCuePoints() {
260
+ return this.currentCuePoints_;
261
+ }
262
+
236
263
  /**
237
264
  * If a seek jumped over the ad break, return to the start of the
238
265
  * ad break, then complete the seek after the ad played through.
@@ -280,7 +307,7 @@ shaka.ads.ServerSideAdManager = class {
280
307
  }
281
308
 
282
309
  this.onEvent_(new shaka.util.FakeEvent(shaka.ads.AdManager.AD_STARTED,
283
- {'ad': this.ad_}));
310
+ (new Map()).set('ad', this.ad_)));
284
311
  this.adContainer_.setAttribute('ad-active', 'true');
285
312
  }
286
313
 
@@ -306,9 +333,8 @@ shaka.ads.ServerSideAdManager = class {
306
333
  onLoaded_(e) {
307
334
  const now = Date.now() / 1000;
308
335
  const loadTime = now - this.streamRequestStartTime_;
309
- this.onEvent_(
310
- new shaka.util.FakeEvent(shaka.ads.AdManager.ADS_LOADED,
311
- {'loadTime': loadTime}));
336
+ this.onEvent_(new shaka.util.FakeEvent(shaka.ads.AdManager.ADS_LOADED,
337
+ (new Map()).set('loadTime', loadTime)));
312
338
 
313
339
  const streamData = e.getStreamData();
314
340
  const url = streamData.url;
@@ -361,15 +387,21 @@ shaka.ads.ServerSideAdManager = class {
361
387
  onCuePointsChanged_(e) {
362
388
  const streamData = e.getStreamData();
363
389
 
364
- /** @type {!Array.<!shaka.ads.CuePoint>} */
390
+ /** @type {!Array.<!shaka.extern.AdCuePoint>} */
365
391
  const cuePoints = [];
366
392
  for (const point of streamData.cuepoints) {
367
- const shakaCuePoint = new shaka.ads.CuePoint(point.start, point.end);
393
+ /** @type {shaka.extern.AdCuePoint} */
394
+ const shakaCuePoint = {
395
+ start: point.start,
396
+ end: point.end,
397
+ };
368
398
  cuePoints.push(shakaCuePoint);
369
399
  }
370
400
 
371
- this.onEvent_(
372
- new shaka.util.FakeEvent(shaka.ads.AdManager.CUEPOINTS_CHANGED,
373
- {'cuepoints': cuePoints}));
401
+ this.currentCuePoints_ = cuePoints;
402
+
403
+ this.onEvent_(new shaka.util.FakeEvent(
404
+ shaka.ads.AdManager.CUEPOINTS_CHANGED,
405
+ (new Map()).set('cuepoints', cuePoints)));
374
406
  }
375
407
  };
@@ -120,6 +120,9 @@ shaka.cast.CastProxy = class extends shaka.util.FakeEventTarget {
120
120
  this.videoProxy_ = null;
121
121
  this.playerProxy_ = null;
122
122
 
123
+ // FakeEventTarget implements IReleasable
124
+ super.release();
125
+
123
126
  return Promise.all(waitFor);
124
127
  }
125
128
 
@@ -525,7 +528,8 @@ shaka.cast.CastProxy = class extends shaka.util.FakeEventTarget {
525
528
  goog.asserts.assert(error instanceof shaka.util.Error,
526
529
  'Wrong error type!');
527
530
  const eventType = shaka.Player.EventName.Error;
528
- const event = new shaka.util.FakeEvent(eventType, {'detail': error});
531
+ const data = (new Map()).set('detail', error);
532
+ const event = new shaka.util.FakeEvent(eventType, data);
529
533
  this.localPlayer_.dispatchEvent(event);
530
534
  });
531
535
  }
@@ -598,7 +602,7 @@ shaka.cast.CastProxy = class extends shaka.util.FakeEventTarget {
598
602
 
599
603
  // Convert this real Event into a FakeEvent for dispatch from our
600
604
  // FakeEventListener.
601
- const fakeEvent = new shaka.util.FakeEvent(event.type, event);
605
+ const fakeEvent = shaka.util.FakeEvent.fromRealEvent(event);
602
606
  this.videoEventTarget_.dispatchEvent(fakeEvent);
603
607
  }
604
608
 
@@ -238,6 +238,9 @@ shaka.cast.CastReceiver = class extends shaka.util.FakeEventTarget {
238
238
  this.shakaBus_ = null;
239
239
  this.genericBus_ = null;
240
240
 
241
+ // FakeEventTarget implements IReleasable
242
+ super.release();
243
+
241
244
  await Promise.all(waitFor);
242
245
 
243
246
  const manager = cast.receiver.CastReceiverManager.getInstance();
@@ -407,7 +410,8 @@ shaka.cast.CastReceiver = class extends shaka.util.FakeEventTarget {
407
410
  goog.asserts.assert(error instanceof shaka.util.Error,
408
411
  'Wrong error type!');
409
412
  const eventType = shaka.Player.EventName.Error;
410
- const event = new shaka.util.FakeEvent(eventType, {'detail': error});
413
+ const data = (new Map()).set('detail', error);
414
+ const event = new shaka.util.FakeEvent(eventType, data);
411
415
  // Only dispatch the event if the player still exists.
412
416
  if (this.player_) {
413
417
  this.player_.dispatchEvent(event);
@@ -647,7 +647,7 @@ shaka.cast.CastSender = class {
647
647
  case 'event': {
648
648
  const targetName = message['targetName'];
649
649
  const event = message['event'];
650
- const fakeEvent = new shaka.util.FakeEvent(event['type'], event);
650
+ const fakeEvent = shaka.util.FakeEvent.fromRealEvent(event);
651
651
  this.onRemoteEvent_(targetName, fakeEvent);
652
652
  break;
653
653
  }
@@ -725,7 +725,7 @@ shaka.cast.CastSender = class {
725
725
 
726
726
  // Dispatch it through the Player proxy
727
727
  const fakeEvent = new shaka.util.FakeEvent(
728
- 'error', {'detail': shakaError});
728
+ 'error', (new Map()).set('detail', shakaError));
729
729
  this.onRemoteEvent_('player', fakeEvent);
730
730
 
731
731
  // Force this session to disconnect and transfer playback to the local
@@ -17,31 +17,31 @@ shaka.cea.SeiProcessor = class {
17
17
  * @return {!Iterable.<!Uint8Array>}
18
18
  */
19
19
  * process(naluData) {
20
- const emuCount = this.removeEmu_(naluData);
20
+ const naluClone = this.removeEmu(naluData);
21
21
 
22
22
  // The following is an implementation of section 7.3.2.3.1
23
23
  // in Rec. ITU-T H.264 (06/2019), the H.264 spec.
24
24
  let offset = 0;
25
25
 
26
- while (offset + emuCount < naluData.length) {
26
+ while (offset < naluClone.length) {
27
27
  let payloadType = 0; // SEI payload type as defined by H.264 spec
28
- while (naluData[offset] == 0xFF) {
28
+ while (naluClone[offset] == 0xFF) {
29
29
  payloadType += 255;
30
30
  offset++;
31
31
  }
32
- payloadType += naluData[offset++];
32
+ payloadType += naluClone[offset++];
33
33
 
34
34
  let payloadSize = 0; // SEI payload size as defined by H.264 spec
35
- while (naluData[offset] == 0xFF) {
35
+ while (naluClone[offset] == 0xFF) {
36
36
  payloadSize += 255;
37
37
  offset++;
38
38
  }
39
- payloadSize += naluData[offset++];
39
+ payloadSize += naluClone[offset++];
40
40
 
41
41
  // Payload type 4 is user_data_registered_itu_t_t35, as per the H.264
42
42
  // spec. This payload type contains caption data.
43
43
  if (payloadType == 0x04) {
44
- yield naluData.subarray(offset, offset + payloadSize);
44
+ yield naluClone.subarray(offset, offset + payloadSize);
45
45
  }
46
46
  offset += payloadSize;
47
47
  }
@@ -50,28 +50,35 @@ shaka.cea.SeiProcessor = class {
50
50
 
51
51
  /**
52
52
  * Removes H.264 emulation prevention bytes from the byte array.
53
+ *
54
+ * Note: Remove bytes by shifting will cause Chromium (VDA) to complain
55
+ * about conformance. Recreating a new array solves it.
56
+ *
53
57
  * @param {!Uint8Array} naluData NALU from which EMUs should be removed.
54
- * @return {number} The number of removed emulation prevention bytes.
55
- * @private
58
+ * @return {!Uint8Array} The NALU with the emulation prevention byte removed.
56
59
  */
57
- removeEmu_(naluData) {
60
+ removeEmu(naluData) {
61
+ let naluClone = naluData;
58
62
  let zeroCount = 0;
59
63
  let src = 0;
60
- let dst = 0;
61
- while (src < naluData.length) {
62
- if (zeroCount == 2 && naluData[src] == 0x03) {
64
+ while (src < naluClone.length) {
65
+ if (zeroCount == 2 && naluClone[src] == 0x03) {
66
+ // 0x00, 0x00, 0x03 pattern detected
63
67
  zeroCount = 0;
68
+
69
+ // Splice the array and recreate a new one, instead of shifting bytes
70
+ const newArr = [...naluClone];
71
+ newArr.splice(src, 1);
72
+ naluClone = new Uint8Array(newArr);
64
73
  } else {
65
- if (naluData[src] == 0x00) {
74
+ if (naluClone[src] == 0x00) {
66
75
  zeroCount++;
67
76
  } else {
68
77
  zeroCount = 0;
69
78
  }
70
- naluData[dst] = naluData[src];
71
- dst++;
72
79
  }
73
80
  src++;
74
81
  }
75
- return (src - dst);
82
+ return naluClone;
76
83
  }
77
84
  };