shaka-player 3.0.14 → 3.0.15

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 (82) hide show
  1. package/AUTHORS +2 -0
  2. package/CHANGELOG.md +39 -0
  3. package/CONTRIBUTORS +2 -0
  4. package/build/generateExterns.js +1 -1
  5. package/demo/common/assets.js +1 -1
  6. package/dist/controls.css +1 -1
  7. package/dist/demo.compiled.debug.js +1 -1
  8. package/dist/demo.compiled.js +1 -1
  9. package/dist/deps.js +1 -1
  10. package/dist/locales.js +34 -34
  11. package/dist/receiver.compiled.debug.js +1 -1
  12. package/dist/receiver.compiled.js +1 -1
  13. package/dist/shaka-player.compiled.d.ts +1 -1
  14. package/dist/shaka-player.compiled.debug.d.ts +1 -1
  15. package/dist/shaka-player.compiled.debug.externs.js +1 -1
  16. package/dist/shaka-player.compiled.debug.js +181 -178
  17. package/dist/shaka-player.compiled.debug.map +3 -3
  18. package/dist/shaka-player.compiled.externs.js +1 -1
  19. package/dist/shaka-player.compiled.js +688 -686
  20. package/dist/shaka-player.compiled.map +3 -3
  21. package/dist/shaka-player.ui.d.ts +1 -1
  22. package/dist/shaka-player.ui.debug.d.ts +1 -1
  23. package/dist/shaka-player.ui.debug.externs.js +3 -3
  24. package/dist/shaka-player.ui.debug.js +169 -166
  25. package/dist/shaka-player.ui.debug.map +4 -4
  26. package/dist/shaka-player.ui.externs.js +3 -3
  27. package/dist/shaka-player.ui.js +815 -813
  28. package/dist/shaka-player.ui.map +4 -4
  29. package/docs/tutorials/fairplay.md +1 -1
  30. package/karma.conf.js +1 -0
  31. package/lib/ads/client_side_ad_manager.js +46 -86
  32. package/lib/ads/server_side_ad_manager.js +7 -10
  33. package/lib/cast/cast_proxy.js +3 -2
  34. package/lib/cast/cast_receiver.js +6 -2
  35. package/lib/cast/cast_sender.js +2 -2
  36. package/lib/dash/dash_parser.js +52 -0
  37. package/lib/dash/segment_list.js +1 -1
  38. package/lib/dash/segment_template.js +1 -1
  39. package/lib/hls/hls_parser.js +25 -10
  40. package/lib/media/drm_engine.js +7 -0
  41. package/lib/media/gap_jumping_controller.js +4 -2
  42. package/lib/media/streaming_engine.js +2 -1
  43. package/lib/net/networking_engine.js +2 -1
  44. package/lib/player.js +18 -17
  45. package/lib/polyfill/patchedmediakeys_apple.js +4 -4
  46. package/lib/polyfill/patchedmediakeys_ms.js +4 -4
  47. package/lib/polyfill/patchedmediakeys_webkit.js +4 -4
  48. package/lib/text/mp4_ttml_parser.js +3 -0
  49. package/lib/text/ttml_text_parser.js +5 -0
  50. package/lib/text/ui_text_displayer.js +6 -1
  51. package/lib/util/fake_event.js +53 -7
  52. package/lib/util/periods.js +4 -1
  53. package/package.json +1 -1
  54. package/test/cast/cast_proxy_unit.js +12 -6
  55. package/test/cast/cast_utils_unit.js +1 -1
  56. package/test/dash/dash_parser_manifest_unit.js +73 -0
  57. package/test/hls/hls_parser_unit.js +5 -1
  58. package/test/media/drm_engine_unit.js +43 -0
  59. package/test/player_integration.js +38 -0
  60. package/test/test/assets/3675/chunk-stream0-00001.m4s +0 -0
  61. package/test/test/assets/3675/chunk-stream0-00002.m4s +0 -0
  62. package/test/test/assets/3675/chunk-stream0-00003.m4s +0 -0
  63. package/test/test/assets/3675/chunk-stream0-00004.m4s +0 -0
  64. package/test/test/assets/3675/chunk-stream0-00005.m4s +0 -0
  65. package/test/test/assets/3675/chunk-stream0-00006.m4s +0 -0
  66. package/test/test/assets/3675/chunk-stream0-00007.m4s +0 -0
  67. package/test/test/assets/3675/dash_0.mpd +30 -0
  68. package/test/test/assets/3675/dash_1.mpd +30 -0
  69. package/test/test/assets/3675/dash_2.mpd +30 -0
  70. package/test/test/assets/3675/dash_3.mpd +30 -0
  71. package/test/test/assets/3675/dash_4.mpd +30 -0
  72. package/test/test/assets/3675/dash_5.mpd +26 -0
  73. package/test/test/assets/3675/init-stream0.m4s +0 -0
  74. package/test/test/util/fake_ad_manager.js +1 -1
  75. package/test/text/ttml_text_parser_unit.js +9 -0
  76. package/test/text/ui_text_displayer_unit.js +18 -0
  77. package/test/ui/localization_unit.js +15 -14
  78. package/ui/cast_button.js +2 -3
  79. package/ui/controls.js +4 -6
  80. package/ui/locales/ru.json +6 -6
  81. package/ui/localization.js +28 -51
  82. package/ui/pip_button.js +2 -3
@@ -31,7 +31,7 @@ player.configure('drm.initDataTransform', (initData, initDataType) => {
31
31
 
32
32
  // 'initData' is a buffer containing an 'skd://' URL as a UTF-8 string.
33
33
  const skdUri = shaka.util.StringUtils.fromBytesAutoDetect(initData);
34
- const contentId = getMyContentId(sdkUri);
34
+ const contentId = getMyContentId(skdUri);
35
35
  const cert = player.drmInfo().serverCertificate;
36
36
  return shaka.util.FairPlayUtils.initDataTransform(initData, contentId, cert);
37
37
  });
package/karma.conf.js CHANGED
@@ -136,6 +136,7 @@ module.exports = (config) => {
136
136
  included: false,
137
137
  },
138
138
  {pattern: 'test/test/assets/*', included: false},
139
+ {pattern: 'test/test/assets/3675/*', included: false},
139
140
  {pattern: 'dist/shaka-player.ui.js', included: false},
140
141
  {pattern: 'dist/locales.js', included: false},
141
142
  {pattern: 'demo/locales/en.json', included: false},
@@ -138,7 +138,7 @@ shaka.ads.ClientSideAdManager = class {
138
138
  // Remove ad breaks from the timeline
139
139
  this.onEvent_(
140
140
  new shaka.util.FakeEvent(shaka.ads.AdManager.CUEPOINTS_CHANGED,
141
- {'cuepoints': []}));
141
+ (new Map()).set('cuepoints', [])));
142
142
  }
143
143
 
144
144
 
@@ -151,17 +151,14 @@ shaka.ads.ClientSideAdManager = class {
151
151
 
152
152
  const now = Date.now() / 1000;
153
153
  const loadTime = now - this.requestAdsStartTime_;
154
- this.onEvent_(
155
- new shaka.util.FakeEvent(shaka.ads.AdManager.ADS_LOADED,
156
- {'loadTime': loadTime}));
154
+ this.onEvent_(new shaka.util.FakeEvent(shaka.ads.AdManager.ADS_LOADED,
155
+ (new Map()).set('loadTime', loadTime)));
157
156
 
158
157
  this.imaAdsManager_ = e.getAdsManager(this.video_);
159
158
 
160
159
  this.onEvent_(new shaka.util.FakeEvent(
161
160
  shaka.ads.AdManager.IMA_AD_MANAGER_LOADED,
162
- {
163
- 'imaAdManager': this.imaAdsManager_,
164
- }));
161
+ (new Map()).set('imaAdManager', this.imaAdsManager_)));
165
162
 
166
163
  const cuePointStarts = this.imaAdsManager_.getCuePoints();
167
164
  if (cuePointStarts.length) {
@@ -172,9 +169,9 @@ shaka.ads.ClientSideAdManager = class {
172
169
  cuePoints.push(shakaCuePoint);
173
170
  }
174
171
 
175
- this.onEvent_(
176
- new shaka.util.FakeEvent(shaka.ads.AdManager.CUEPOINTS_CHANGED,
177
- {'cuepoints': cuePoints}));
172
+ this.onEvent_(new shaka.util.FakeEvent(
173
+ shaka.ads.AdManager.CUEPOINTS_CHANGED,
174
+ (new Map()).set('cuepoints', cuePoints)));
178
175
  }
179
176
 
180
177
  this.addImaEventListeners_();
@@ -224,6 +221,15 @@ shaka.ads.ClientSideAdManager = class {
224
221
  * @private
225
222
  */
226
223
  addImaEventListeners_() {
224
+ /**
225
+ * @param {!Event} e
226
+ * @param {string} type
227
+ */
228
+ const convertEventAndSend = (e, type) => {
229
+ const data = (new Map()).set('originalEvent', e);
230
+ this.onEvent_(new shaka.util.FakeEvent(type, data));
231
+ };
232
+
227
233
  this.eventManager_.listen(this.imaAdsManager_,
228
234
  google.ima.AdErrorEvent.Type.AD_ERROR, (error) => {
229
235
  this.onAdError_(/** @type {!google.ima.AdErrorEvent} */ (error));
@@ -241,30 +247,22 @@ shaka.ads.ClientSideAdManager = class {
241
247
 
242
248
  this.eventManager_.listen(this.imaAdsManager_,
243
249
  google.ima.AdEvent.Type.FIRST_QUARTILE, (e) => {
244
- this.onEvent_(
245
- new shaka.util.FakeEvent(shaka.ads.AdManager.AD_FIRST_QUARTILE,
246
- {'originalEvent': e}));
250
+ convertEventAndSend(e, shaka.ads.AdManager.AD_FIRST_QUARTILE);
247
251
  });
248
252
 
249
253
  this.eventManager_.listen(this.imaAdsManager_,
250
254
  google.ima.AdEvent.Type.MIDPOINT, (e) => {
251
- this.onEvent_(
252
- new shaka.util.FakeEvent(shaka.ads.AdManager.AD_MIDPOINT,
253
- {'originalEvent': e}));
255
+ convertEventAndSend(e, shaka.ads.AdManager.AD_MIDPOINT);
254
256
  });
255
257
 
256
258
  this.eventManager_.listen(this.imaAdsManager_,
257
259
  google.ima.AdEvent.Type.THIRD_QUARTILE, (e) => {
258
- this.onEvent_(
259
- new shaka.util.FakeEvent(shaka.ads.AdManager.AD_THIRD_QUARTILE,
260
- {'originalEvent': e}));
260
+ convertEventAndSend(e, shaka.ads.AdManager.AD_THIRD_QUARTILE);
261
261
  });
262
262
 
263
263
  this.eventManager_.listen(this.imaAdsManager_,
264
264
  google.ima.AdEvent.Type.COMPLETE, (e) => {
265
- this.onEvent_(
266
- new shaka.util.FakeEvent(shaka.ads.AdManager.AD_COMPLETE,
267
- {'originalEvent': e}));
265
+ convertEventAndSend(e, shaka.ads.AdManager.AD_COMPLETE);
268
266
  });
269
267
 
270
268
  this.eventManager_.listen(this.imaAdsManager_,
@@ -279,140 +277,102 @@ shaka.ads.ClientSideAdManager = class {
279
277
 
280
278
  this.eventManager_.listen(this.imaAdsManager_,
281
279
  google.ima.AdEvent.Type.SKIPPED, (e) => {
282
- this.onEvent_(
283
- new shaka.util.FakeEvent(shaka.ads.AdManager.AD_SKIPPED,
284
- {'originalEvent': e}));
280
+ convertEventAndSend(e, shaka.ads.AdManager.AD_SKIPPED);
285
281
  });
286
282
 
287
283
  this.eventManager_.listen(this.imaAdsManager_,
288
284
  google.ima.AdEvent.Type.VOLUME_CHANGED, (e) => {
289
- this.onEvent_(
290
- new shaka.util.FakeEvent(shaka.ads.AdManager.AD_VOLUME_CHANGED,
291
- {'originalEvent': e}));
285
+ convertEventAndSend(e, shaka.ads.AdManager.AD_VOLUME_CHANGED);
292
286
  });
293
287
 
294
288
  this.eventManager_.listen(this.imaAdsManager_,
295
289
  google.ima.AdEvent.Type.VOLUME_MUTED, (e) => {
296
- this.onEvent_(
297
- new shaka.util.FakeEvent(shaka.ads.AdManager.AD_MUTED,
298
- {'originalEvent': e}));
290
+ convertEventAndSend(e, shaka.ads.AdManager.AD_MUTED);
299
291
  });
300
292
 
301
293
  this.eventManager_.listen(this.imaAdsManager_,
302
294
  google.ima.AdEvent.Type.PAUSED, (e) => {
303
295
  goog.asserts.assert(this.ad_ != null, 'Ad should not be null!');
304
296
  this.ad_.setPaused(true);
305
- this.onEvent_(
306
- new shaka.util.FakeEvent(shaka.ads.AdManager.AD_PAUSED,
307
- {'originalEvent': e}));
297
+ convertEventAndSend(e, shaka.ads.AdManager.AD_PAUSED);
308
298
  });
309
299
 
310
300
  this.eventManager_.listen(this.imaAdsManager_,
311
301
  google.ima.AdEvent.Type.RESUMED, (e) => {
312
302
  goog.asserts.assert(this.ad_ != null, 'Ad should not be null!');
313
303
  this.ad_.setPaused(false);
314
- this.onEvent_(
315
- new shaka.util.FakeEvent(shaka.ads.AdManager.AD_RESUMED,
316
- {'originalEvent': e}));
304
+ convertEventAndSend(e, shaka.ads.AdManager.AD_RESUMED);
317
305
  });
318
306
 
319
307
  this.eventManager_.listen(this.imaAdsManager_,
320
308
  google.ima.AdEvent.Type.SKIPPABLE_STATE_CHANGED, (e) => {
321
309
  goog.asserts.assert(this.ad_ != null, 'Ad should not be null!');
322
- this.onEvent_(new shaka.util.FakeEvent(
323
- shaka.ads.AdManager.AD_SKIP_STATE_CHANGED,
324
- {'originalEvent': e}));
310
+ convertEventAndSend(e, shaka.ads.AdManager.AD_SKIP_STATE_CHANGED);
325
311
  });
326
312
 
327
313
  this.eventManager_.listen(this.imaAdsManager_,
328
314
  google.ima.AdEvent.Type.CLICK, (e) => {
329
- this.onEvent_(new shaka.util.FakeEvent(
330
- shaka.ads.AdManager.AD_CLICKED,
331
- {'originalEvent': e}));
315
+ convertEventAndSend(e, shaka.ads.AdManager.AD_CLICKED);
332
316
  });
333
317
 
334
318
  this.eventManager_.listen(this.imaAdsManager_,
335
319
  google.ima.AdEvent.Type.AD_PROGRESS, (e) => {
336
- this.onEvent_(new shaka.util.FakeEvent(
337
- shaka.ads.AdManager.AD_PROGRESS,
338
- {'originalEvent': e}));
320
+ convertEventAndSend(e, shaka.ads.AdManager.AD_PROGRESS);
339
321
  });
340
322
 
341
323
  this.eventManager_.listen(this.imaAdsManager_,
342
324
  google.ima.AdEvent.Type.AD_BUFFERING, (e) => {
343
- this.onEvent_(new shaka.util.FakeEvent(
344
- shaka.ads.AdManager.AD_BUFFERING,
345
- {'originalEvent': e}));
325
+ convertEventAndSend(e, shaka.ads.AdManager.AD_BUFFERING);
346
326
  });
347
327
 
348
328
  this.eventManager_.listen(this.imaAdsManager_,
349
329
  google.ima.AdEvent.Type.IMPRESSION, (e) => {
350
- this.onEvent_(new shaka.util.FakeEvent(
351
- shaka.ads.AdManager.AD_IMPRESSION,
352
- {'originalEvent': e}));
330
+ convertEventAndSend(e, shaka.ads.AdManager.AD_IMPRESSION);
353
331
  });
354
332
 
355
333
  this.eventManager_.listen(this.imaAdsManager_,
356
334
  google.ima.AdEvent.Type.DURATION_CHANGE, (e) => {
357
- this.onEvent_(new shaka.util.FakeEvent(
358
- shaka.ads.AdManager.AD_DURATION_CHANGED,
359
- {'originalEvent': e}));
335
+ convertEventAndSend(e, shaka.ads.AdManager.AD_DURATION_CHANGED);
360
336
  });
361
337
 
362
338
  this.eventManager_.listen(this.imaAdsManager_,
363
339
  google.ima.AdEvent.Type.USER_CLOSE, (e) => {
364
- this.onEvent_(new shaka.util.FakeEvent(
365
- shaka.ads.AdManager.AD_CLOSED,
366
- {'originalEvent': e}));
340
+ convertEventAndSend(e, shaka.ads.AdManager.AD_CLOSED);
367
341
  });
368
342
 
369
343
  this.eventManager_.listen(this.imaAdsManager_,
370
344
  google.ima.AdEvent.Type.LOADED, (e) => {
371
- this.onEvent_(new shaka.util.FakeEvent(
372
- shaka.ads.AdManager.AD_LOADED,
373
- {'originalEvent': e}));
345
+ convertEventAndSend(e, shaka.ads.AdManager.AD_LOADED);
374
346
  });
375
347
 
376
348
  this.eventManager_.listen(this.imaAdsManager_,
377
349
  google.ima.AdEvent.Type.ALL_ADS_COMPLETED, (e) => {
378
- this.onEvent_(new shaka.util.FakeEvent(
379
- shaka.ads.AdManager.ALL_ADS_COMPLETED,
380
- {'originalEvent': e}));
350
+ convertEventAndSend(e, shaka.ads.AdManager.ALL_ADS_COMPLETED);
381
351
  });
382
352
 
383
353
  this.eventManager_.listen(this.imaAdsManager_,
384
354
  google.ima.AdEvent.Type.LINEAR_CHANGED, (e) => {
385
- this.onEvent_(new shaka.util.FakeEvent(
386
- shaka.ads.AdManager.AD_LINEAR_CHANGED,
387
- {'originalEvent': e}));
355
+ convertEventAndSend(e, shaka.ads.AdManager.AD_LINEAR_CHANGED);
388
356
  });
389
357
 
390
358
  this.eventManager_.listen(this.imaAdsManager_,
391
359
  google.ima.AdEvent.Type.AD_METADATA, (e) => {
392
- this.onEvent_(new shaka.util.FakeEvent(
393
- shaka.ads.AdManager.AD_METADATA,
394
- {'originalEvent': e}));
360
+ convertEventAndSend(e, shaka.ads.AdManager.AD_METADATA);
395
361
  });
396
362
 
397
363
  this.eventManager_.listen(this.imaAdsManager_,
398
364
  google.ima.AdEvent.Type.LOG, (e) => {
399
- this.onEvent_(new shaka.util.FakeEvent(
400
- shaka.ads.AdManager.AD_RECOVERABLE_ERROR,
401
- {'originalEvent': e}));
365
+ convertEventAndSend(e, shaka.ads.AdManager.AD_RECOVERABLE_ERROR);
402
366
  });
403
367
 
404
368
  this.eventManager_.listen(this.imaAdsManager_,
405
369
  google.ima.AdEvent.Type.AD_BREAK_READY, (e) => {
406
- this.onEvent_(new shaka.util.FakeEvent(
407
- shaka.ads.AdManager.AD_BREAK_READY,
408
- {'originalEvent': e}));
370
+ convertEventAndSend(e, shaka.ads.AdManager.AD_BREAK_READY);
409
371
  });
410
372
 
411
373
  this.eventManager_.listen(this.imaAdsManager_,
412
374
  google.ima.AdEvent.Type.INTERACTION, (e) => {
413
- this.onEvent_(new shaka.util.FakeEvent(
414
- shaka.ads.AdManager.AD_INTERACTION,
415
- {'originalEvent': e}));
375
+ convertEventAndSend(e, shaka.ads.AdManager.AD_INTERACTION);
416
376
  });
417
377
  }
418
378
 
@@ -426,12 +386,12 @@ shaka.ads.ClientSideAdManager = class {
426
386
 
427
387
  const imaAd = e.getAd();
428
388
  this.ad_ = new shaka.ads.ClientSideAd(imaAd, this.imaAdsManager_);
429
- this.onEvent_(new shaka.util.FakeEvent(shaka.ads.AdManager.AD_STARTED,
430
- {
431
- 'ad': this.ad_,
432
- 'sdkAdObject': imaAd,
433
- 'originalEvent': e,
434
- }));
389
+ const data = new Map()
390
+ .set('ad', this.ad_)
391
+ .set('sdkAdObject', imaAd)
392
+ .set('originalEvent', e);
393
+ this.onEvent_(new shaka.util.FakeEvent(
394
+ shaka.ads.AdManager.AD_STARTED, data));
435
395
  this.adContainer_.setAttribute('ad-active', 'true');
436
396
  this.video_.pause();
437
397
  }
@@ -442,7 +402,7 @@ shaka.ads.ClientSideAdManager = class {
442
402
  */
443
403
  onAdComplete_(e) {
444
404
  this.onEvent_(new shaka.util.FakeEvent(shaka.ads.AdManager.AD_STOPPED,
445
- {'originalEvent': e}));
405
+ (new Map()).set('originalEvent', e)));
446
406
  this.adContainer_.removeAttribute('ad-active');
447
407
  this.video_.play();
448
408
  }
@@ -77,9 +77,7 @@ shaka.ads.ServerSideAdManager = class {
77
77
 
78
78
  this.onEvent_(new shaka.util.FakeEvent(
79
79
  shaka.ads.AdManager.IMA_STREAM_MANAGER_LOADED,
80
- {
81
- 'imaStreamManager': this.streamManager_,
82
- }));
80
+ (new Map()).set('imaStreamManager', this.streamManager_)));
83
81
 
84
82
  // Events
85
83
  this.eventManager_.listen(this.streamManager_,
@@ -298,7 +296,7 @@ shaka.ads.ServerSideAdManager = class {
298
296
  }
299
297
 
300
298
  this.onEvent_(new shaka.util.FakeEvent(shaka.ads.AdManager.AD_STARTED,
301
- {'ad': this.ad_}));
299
+ (new Map()).set('ad', this.ad_)));
302
300
  this.adContainer_.setAttribute('ad-active', 'true');
303
301
  }
304
302
 
@@ -324,9 +322,8 @@ shaka.ads.ServerSideAdManager = class {
324
322
  onLoaded_(e) {
325
323
  const now = Date.now() / 1000;
326
324
  const loadTime = now - this.streamRequestStartTime_;
327
- this.onEvent_(
328
- new shaka.util.FakeEvent(shaka.ads.AdManager.ADS_LOADED,
329
- {'loadTime': loadTime}));
325
+ this.onEvent_(new shaka.util.FakeEvent(shaka.ads.AdManager.ADS_LOADED,
326
+ (new Map()).set('loadTime', loadTime)));
330
327
 
331
328
  const streamData = e.getStreamData();
332
329
  const url = streamData.url;
@@ -386,8 +383,8 @@ shaka.ads.ServerSideAdManager = class {
386
383
  cuePoints.push(shakaCuePoint);
387
384
  }
388
385
 
389
- this.onEvent_(
390
- new shaka.util.FakeEvent(shaka.ads.AdManager.CUEPOINTS_CHANGED,
391
- {'cuepoints': cuePoints}));
386
+ this.onEvent_(new shaka.util.FakeEvent(
387
+ shaka.ads.AdManager.CUEPOINTS_CHANGED,
388
+ (new Map()).set('cuepoints', cuePoints)));
392
389
  }
393
390
  };
@@ -526,7 +526,8 @@ shaka.cast.CastProxy = class extends shaka.util.FakeEventTarget {
526
526
  goog.asserts.assert(error instanceof shaka.util.Error,
527
527
  'Wrong error type!');
528
528
  const eventType = shaka.Player.EventName.Error;
529
- const event = new shaka.util.FakeEvent(eventType, {'detail': error});
529
+ const data = (new Map()).set('detail', error);
530
+ const event = new shaka.util.FakeEvent(eventType, data);
530
531
  this.localPlayer_.dispatchEvent(event);
531
532
  });
532
533
  }
@@ -599,7 +600,7 @@ shaka.cast.CastProxy = class extends shaka.util.FakeEventTarget {
599
600
 
600
601
  // Convert this real Event into a FakeEvent for dispatch from our
601
602
  // FakeEventListener.
602
- const fakeEvent = new shaka.util.FakeEvent(event.type, event);
603
+ const fakeEvent = shaka.util.FakeEvent.fromRealEvent(event);
603
604
  this.videoEventTarget_.dispatchEvent(fakeEvent);
604
605
  }
605
606
 
@@ -407,8 +407,12 @@ shaka.cast.CastReceiver = class extends shaka.util.FakeEventTarget {
407
407
  goog.asserts.assert(error instanceof shaka.util.Error,
408
408
  'Wrong error type!');
409
409
  const eventType = shaka.Player.EventName.Error;
410
- const event = new shaka.util.FakeEvent(eventType, {'detail': error});
411
- this.player_.dispatchEvent(event);
410
+ const data = (new Map()).set('detail', error);
411
+ const event = new shaka.util.FakeEvent(eventType, data);
412
+ // Only dispatch the event if the player still exists.
413
+ if (this.player_) {
414
+ this.player_.dispatchEvent(event);
415
+ }
412
416
  return;
413
417
  }
414
418
  } else {
@@ -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
@@ -94,6 +94,18 @@ shaka.dash.DashParser = class {
94
94
 
95
95
  /** @private {!shaka.util.OperationManager} */
96
96
  this.operationManager_ = new shaka.util.OperationManager();
97
+
98
+ /**
99
+ * Largest period start time seen.
100
+ * @private {?number}
101
+ */
102
+ this.largestPeriodStartTime_ = null;
103
+
104
+ /**
105
+ * Period IDs seen in previous manifest.
106
+ * @private {!Array.<number>}
107
+ */
108
+ this.lastManifestUpdatePeriodIds_ = [];
97
109
  }
98
110
 
99
111
  /**
@@ -474,6 +486,7 @@ shaka.dash.DashParser = class {
474
486
  for (const {i, item: elem, next} of enumerate(periodNodes)) {
475
487
  const start = /** @type {number} */ (
476
488
  XmlUtils.parseAttr(elem, 'start', XmlUtils.parseDuration, prevEnd));
489
+ const periodId = elem.id;
477
490
  const givenDuration =
478
491
  XmlUtils.parseAttr(elem, 'duration', XmlUtils.parseDuration);
479
492
 
@@ -507,6 +520,42 @@ shaka.dash.DashParser = class {
507
520
  periodDuration = givenDuration;
508
521
  }
509
522
 
523
+ /**
524
+ * This is to improve robustness when the player observes manifest with
525
+ * past periods that are inconsistent to previous ones.
526
+ *
527
+ * This may happen when a CDN or proxy server switches its upstream from
528
+ * one encoder to another redundant encoder.
529
+ *
530
+ * Skip periods that match all of the following criteria:
531
+ * - Start time is earlier than latest period start time ever seen
532
+ * - Period ID is never seen in the previous manifest
533
+ * - Not the last period in the manifest
534
+ *
535
+ * Periods that meet the aforementioned criteria are considered invalid
536
+ * and should be safe to discard.
537
+ */
538
+
539
+ if (this.largestPeriodStartTime_ !== null &&
540
+ periodId !== null && start !== null &&
541
+ start < this.largestPeriodStartTime_ &&
542
+ !this.lastManifestUpdatePeriodIds_.includes(periodId) &&
543
+ i + 1 != periodNodes.length) {
544
+ shaka.log.debug(
545
+ `Skipping Period with ID ${periodId} as its start time is smaller` +
546
+ ' than the largest period start time that has been seen, and ID ' +
547
+ 'is unseen before');
548
+ continue;
549
+ }
550
+
551
+
552
+ // Save maximum period start time if it is the last period
553
+ if (start !== null &&
554
+ (this.largestPeriodStartTime_ === null ||
555
+ start > this.largestPeriodStartTime_)) {
556
+ this.largestPeriodStartTime_ = start;
557
+ }
558
+
510
559
  // Parse child nodes.
511
560
  const info = {
512
561
  start: start,
@@ -538,6 +587,9 @@ shaka.dash.DashParser = class {
538
587
  prevEnd = start + periodDuration;
539
588
  } // end of period parsing loop
540
589
 
590
+ // Replace previous seen periods with the current one.
591
+ this.lastManifestUpdatePeriodIds_ = periods.map((el) => el.id);
592
+
541
593
  if (presentationDuration != null) {
542
594
  if (prevEnd != presentationDuration) {
543
595
  shaka.log.warning(
@@ -63,12 +63,12 @@ shaka.dash.SegmentList = class {
63
63
  const start = context.presentationTimeline.getSegmentAvailabilityStart();
64
64
  segmentIndex.mergeAndEvict(references, start);
65
65
  } else {
66
- context.presentationTimeline.notifySegments(references);
67
66
  segmentIndex = new shaka.media.SegmentIndex(references);
68
67
  if (id && context.dynamic) {
69
68
  segmentIndexMap[id] = segmentIndex;
70
69
  }
71
70
  }
71
+ context.presentationTimeline.notifySegments(references);
72
72
 
73
73
  if (!context.dynamic || !context.periodInfo.isLastPeriod) {
74
74
  const periodStart = context.periodInfo.start;
@@ -116,12 +116,12 @@ shaka.dash.SegmentTemplate = class {
116
116
  segmentIndex.mergeAndEvict(references,
117
117
  context.presentationTimeline.getSegmentAvailabilityStart());
118
118
  } else {
119
- context.presentationTimeline.notifySegments(references);
120
119
  segmentIndex = new shaka.media.SegmentIndex(references);
121
120
  if (id && context.dynamic) {
122
121
  segmentIndexMap[id] = segmentIndex;
123
122
  }
124
123
  }
124
+ context.presentationTimeline.notifySegments(references);
125
125
 
126
126
  if (shouldFit) {
127
127
  segmentIndex.fit(periodStart, periodEnd);
@@ -475,6 +475,30 @@ shaka.hls.HlsParser = class {
475
475
  }
476
476
  }
477
477
 
478
+ // Now that the content has been fit, notify segments.
479
+ this.segmentsToNotifyByStream_ = [];
480
+ const streamsToNotify = [];
481
+ for (const variant of variants) {
482
+ for (const stream of [variant.video, variant.audio]) {
483
+ if (stream) {
484
+ streamsToNotify.push(stream);
485
+ }
486
+ }
487
+ }
488
+ await Promise.all(streamsToNotify.map(async (stream) => {
489
+ await stream.createSegmentIndex();
490
+ }));
491
+ for (const stream of streamsToNotify) {
492
+ this.segmentsToNotifyByStream_.push(stream.segmentIndex.references);
493
+ }
494
+ this.notifySegments_();
495
+
496
+ // This asserts that the live edge is being calculated from segment times.
497
+ // For VOD and event streams, this check should still pass.
498
+ goog.asserts.assert(
499
+ !this.presentationTimeline_.usingPresentationStartTime(),
500
+ 'We should not be using the presentation start time in HLS!');
501
+
478
502
  this.manifest_ = {
479
503
  presentationTimeline: this.presentationTimeline_,
480
504
  variants,
@@ -1064,7 +1088,7 @@ shaka.hls.HlsParser = class {
1064
1088
  // "AUTOSELECT=YES". A value of "AUTOSELECT=NO" would imply that it may
1065
1089
  // only be selected explicitly by the user, and we don't have a way to
1066
1090
  // represent that in our model.
1067
- const defaultAttrValue = tag.getAttribute('DEFAULT');
1091
+ const defaultAttrValue = tag.getAttributeValue('DEFAULT');
1068
1092
  const primary = defaultAttrValue == 'YES';
1069
1093
 
1070
1094
  const channelsCount = type == 'audio' ? this.getChannelsCount_(tag) : null;
@@ -1397,12 +1421,6 @@ shaka.hls.HlsParser = class {
1397
1421
  }
1398
1422
 
1399
1423
  this.notifySegments_();
1400
-
1401
- // This asserts that the live edge is being calculated from segment times.
1402
- // For VOD and event streams, this check should still pass.
1403
- goog.asserts.assert(
1404
- !this.presentationTimeline_.usingPresentationStartTime(),
1405
- 'We should not be using the presentation start time in HLS!');
1406
1424
  }
1407
1425
 
1408
1426
  /**
@@ -1638,9 +1656,6 @@ shaka.hls.HlsParser = class {
1638
1656
  references.push(reference);
1639
1657
  }
1640
1658
 
1641
- this.segmentsToNotifyByStream_.push(references);
1642
- this.notifySegments_();
1643
-
1644
1659
  return references;
1645
1660
  }
1646
1661
 
@@ -1751,9 +1751,16 @@ shaka.media.DrmEngine = class {
1751
1751
  // audio adaptations, so we shouldn't have to worry about checking
1752
1752
  // robustness.
1753
1753
  if (drm1.keySystem == drm2.keySystem) {
1754
+ /** @type {Array<shaka.extern.InitDataOverride>} */
1754
1755
  let initData = [];
1755
1756
  initData = initData.concat(drm1.initData || []);
1756
1757
  initData = initData.concat(drm2.initData || []);
1758
+ initData = initData.filter((d, i) => {
1759
+ return d.keyId === undefined || i === initData.findIndex((d2) => {
1760
+ return d2.keyId === d.keyId;
1761
+ });
1762
+ });
1763
+
1757
1764
  const keyIds = drm1.keyIds && drm2.keyIds ?
1758
1765
  new Set([...drm1.keyIds, ...drm2.keyIds]) :
1759
1766
  drm1.keyIds || drm2.keyIds;
@@ -216,8 +216,10 @@ shaka.media.GapJumpingController = class {
216
216
  this.didFireLargeGap_ = true;
217
217
 
218
218
  // Event firing is synchronous.
219
- const event = new shaka.util.FakeEvent(
220
- 'largegap', {'currentTime': currentTime, 'gapSize': jumpSize});
219
+ const data = new Map()
220
+ .set('currentTime', currentTime)
221
+ .set('gapSize', jumpSize);
222
+ const event = new shaka.util.FakeEvent('largegap', data);
221
223
  event.cancelable = true;
222
224
  this.onEvent_(event);
223
225
 
@@ -1615,7 +1615,8 @@ shaka.media.StreamingEngine = class {
1615
1615
 
1616
1616
  // Dispatch an event to notify the application about the emsg box.
1617
1617
  const eventName = shaka.Player.EventName.Emsg;
1618
- const event = new shaka.util.FakeEvent(eventName, {'detail': emsg});
1618
+ const data = (new Map()).set('detail', emsg);
1619
+ const event = new shaka.util.FakeEvent(eventName, data);
1619
1620
  this.playerInterface_.onEvent(event);
1620
1621
  }
1621
1622
  }
@@ -463,7 +463,8 @@ shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
463
463
  }
464
464
 
465
465
  if (error.severity == shaka.util.Error.Severity.RECOVERABLE) {
466
- const event = new shaka.util.FakeEvent('retry', {'error': error});
466
+ const data = (new Map()).set('error', error);
467
+ const event = new shaka.util.FakeEvent('retry', data);
467
468
  this.dispatchEvent(event);
468
469
 
469
470
  // Move to the next URI.