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
@@ -4,15 +4,12 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
 
7
- /**
8
- * @fileoverview
9
- * @suppress {missingRequire} TODO(b/152540451): this shouldn't be needed
10
- */
11
7
 
12
8
  goog.provide('shaka.ads.MediaTailorAdManager');
13
9
 
14
10
  goog.require('goog.asserts');
15
11
  goog.require('shaka.ads.MediaTailorAd');
12
+ goog.require('shaka.ads.Utils');
16
13
  goog.require('shaka.log');
17
14
  goog.require('shaka.net.NetworkingEngine');
18
15
  goog.require('goog.Uri');
@@ -135,7 +132,7 @@ shaka.ads.MediaTailorAdManager = class {
135
132
  */
136
133
  addTrackingUrl(url) {
137
134
  this.trackingUrl_ = url;
138
- this.onEvent_(new shaka.util.FakeEvent(shaka.ads.AdManager.ADS_LOADED,
135
+ this.onEvent_(new shaka.util.FakeEvent(shaka.ads.Utils.ADS_LOADED,
139
136
  (new Map()).set('loadTime', 0)));
140
137
  }
141
138
 
@@ -227,7 +224,7 @@ shaka.ads.MediaTailorAdManager = class {
227
224
  this.trackingUrl_ = baseUri.resolve(relativeTrackingUri).toString();
228
225
  const now = Date.now() / 1000;
229
226
  const loadTime = now - this.streamRequestStartTime_;
230
- this.onEvent_(new shaka.util.FakeEvent(shaka.ads.AdManager.ADS_LOADED,
227
+ this.onEvent_(new shaka.util.FakeEvent(shaka.ads.Utils.ADS_LOADED,
231
228
  (new Map()).set('loadTime', loadTime)));
232
229
  const relativeManifestUri = new goog.Uri(dataAsJson.manifestUrl);
233
230
  this.streamPromise_.resolve(
@@ -282,13 +279,13 @@ shaka.ads.MediaTailorAdManager = class {
282
279
  }
283
280
  cuepoints = this.getCuePoints();
284
281
  this.onEvent_(new shaka.util.FakeEvent(
285
- shaka.ads.AdManager.CUEPOINTS_CHANGED,
282
+ shaka.ads.Utils.CUEPOINTS_CHANGED,
286
283
  (new Map()).set('cuepoints', cuepoints)));
287
284
  }
288
285
  } else {
289
286
  if (this.adBreaks_.length) {
290
287
  this.onEvent_(new shaka.util.FakeEvent(
291
- shaka.ads.AdManager.CUEPOINTS_CHANGED,
288
+ shaka.ads.Utils.CUEPOINTS_CHANGED,
292
289
  (new Map()).set('cuepoints', cuepoints)));
293
290
  }
294
291
  this.onEnded_();
@@ -727,52 +724,52 @@ shaka.ads.MediaTailorAdManager = class {
727
724
  switch (eventType) {
728
725
  case shaka.ads.MediaTailorAdManager.IMPRESSION_:
729
726
  this.onEvent_(
730
- new shaka.util.FakeEvent(shaka.ads.AdManager.AD_IMPRESSION));
727
+ new shaka.util.FakeEvent(shaka.ads.Utils.AD_IMPRESSION));
731
728
  break;
732
729
  case shaka.ads.MediaTailorAdManager.START_:
733
730
  this.onEvent_(
734
- new shaka.util.FakeEvent(shaka.ads.AdManager.AD_STARTED,
731
+ new shaka.util.FakeEvent(shaka.ads.Utils.AD_STARTED,
735
732
  (new Map()).set('ad', this.ad_)));
736
733
  break;
737
734
  case shaka.ads.MediaTailorAdManager.MUTE_:
738
735
  this.onEvent_(
739
- new shaka.util.FakeEvent(shaka.ads.AdManager.AD_MUTED));
736
+ new shaka.util.FakeEvent(shaka.ads.Utils.AD_MUTED));
740
737
  break;
741
738
  case shaka.ads.MediaTailorAdManager.UNMUTE_:
742
739
  this.onEvent_(
743
- new shaka.util.FakeEvent(shaka.ads.AdManager.AD_VOLUME_CHANGED));
740
+ new shaka.util.FakeEvent(shaka.ads.Utils.AD_VOLUME_CHANGED));
744
741
  break;
745
742
  case shaka.ads.MediaTailorAdManager.RESUME_:
746
743
  this.onEvent_(
747
- new shaka.util.FakeEvent(shaka.ads.AdManager.AD_RESUMED));
744
+ new shaka.util.FakeEvent(shaka.ads.Utils.AD_RESUMED));
748
745
  break;
749
746
  case shaka.ads.MediaTailorAdManager.PAUSE_:
750
747
  this.onEvent_(
751
- new shaka.util.FakeEvent(shaka.ads.AdManager.AD_PAUSED));
748
+ new shaka.util.FakeEvent(shaka.ads.Utils.AD_PAUSED));
752
749
  break;
753
750
  case shaka.ads.MediaTailorAdManager.FIRSTQUARTILE_:
754
751
  this.onEvent_(
755
- new shaka.util.FakeEvent(shaka.ads.AdManager.AD_FIRST_QUARTILE));
752
+ new shaka.util.FakeEvent(shaka.ads.Utils.AD_FIRST_QUARTILE));
756
753
  break;
757
754
  case shaka.ads.MediaTailorAdManager.MIDPOINT_:
758
755
  this.onEvent_(
759
- new shaka.util.FakeEvent(shaka.ads.AdManager.AD_MIDPOINT));
756
+ new shaka.util.FakeEvent(shaka.ads.Utils.AD_MIDPOINT));
760
757
  break;
761
758
  case shaka.ads.MediaTailorAdManager.THIRDQUARTILE_:
762
759
  this.onEvent_(
763
- new shaka.util.FakeEvent(shaka.ads.AdManager.AD_THIRD_QUARTILE));
760
+ new shaka.util.FakeEvent(shaka.ads.Utils.AD_THIRD_QUARTILE));
764
761
  break;
765
762
  case shaka.ads.MediaTailorAdManager.COMPLETE_:
766
763
  this.onEvent_(
767
- new shaka.util.FakeEvent(shaka.ads.AdManager.AD_COMPLETE));
764
+ new shaka.util.FakeEvent(shaka.ads.Utils.AD_COMPLETE));
768
765
  this.onEvent_(
769
- new shaka.util.FakeEvent(shaka.ads.AdManager.AD_STOPPED));
766
+ new shaka.util.FakeEvent(shaka.ads.Utils.AD_STOPPED));
770
767
  break;
771
768
  case shaka.ads.MediaTailorAdManager.SKIPPED_:
772
769
  this.onEvent_(
773
- new shaka.util.FakeEvent(shaka.ads.AdManager.AD_SKIPPED));
770
+ new shaka.util.FakeEvent(shaka.ads.Utils.AD_SKIPPED));
774
771
  this.onEvent_(
775
- new shaka.util.FakeEvent(shaka.ads.AdManager.AD_STOPPED));
772
+ new shaka.util.FakeEvent(shaka.ads.Utils.AD_STOPPED));
776
773
  break;
777
774
  case shaka.ads.MediaTailorAdManager.BREAKSTART_:
778
775
  this.adContainer_.setAttribute('ad-active', 'true');
@@ -783,7 +780,7 @@ shaka.ads.MediaTailorAdManager = class {
783
780
  case shaka.ads.MediaTailorAdManager.SKIP_STATE_CHANGED_:
784
781
  this.onEvent_(
785
782
  new shaka.util.FakeEvent(
786
- shaka.ads.AdManager.AD_SKIP_STATE_CHANGED));
783
+ shaka.ads.Utils.AD_SKIP_STATE_CHANGED));
787
784
  break;
788
785
  }
789
786
  }
@@ -4,14 +4,11 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
 
7
- /**
8
- * @fileoverview
9
- * @suppress {missingRequire} TODO(b/152540451): this shouldn't be needed
10
- */
11
7
 
12
8
  goog.provide('shaka.ads.ServerSideAdManager');
13
9
 
14
10
  goog.require('goog.asserts');
11
+ goog.require('shaka.ads.Utils');
15
12
  goog.require('shaka.ads.ServerSideAd');
16
13
  goog.require('shaka.log');
17
14
  goog.require('shaka.util.EventManager');
@@ -86,7 +83,7 @@ shaka.ads.ServerSideAdManager = class {
86
83
  this.video_, this.adContainer_, uiSettings);
87
84
 
88
85
  this.onEvent_(new shaka.util.FakeEvent(
89
- shaka.ads.AdManager.IMA_STREAM_MANAGER_LOADED,
86
+ shaka.ads.Utils.IMA_STREAM_MANAGER_LOADED,
90
87
  (new Map()).set('imaStreamManager', this.streamManager_)));
91
88
 
92
89
  // Events
@@ -130,30 +127,30 @@ shaka.ads.ServerSideAdManager = class {
130
127
  google.ima.dai.api.StreamEvent.Type.FIRST_QUARTILE, () => {
131
128
  shaka.log.info('Ad event: First Quartile');
132
129
  this.onEvent_(
133
- new shaka.util.FakeEvent(shaka.ads.AdManager.AD_FIRST_QUARTILE));
130
+ new shaka.util.FakeEvent(shaka.ads.Utils.AD_FIRST_QUARTILE));
134
131
  });
135
132
 
136
133
  this.eventManager_.listen(this.streamManager_,
137
134
  google.ima.dai.api.StreamEvent.Type.MIDPOINT, () => {
138
135
  shaka.log.info('Ad event: Midpoint');
139
136
  this.onEvent_(
140
- new shaka.util.FakeEvent(shaka.ads.AdManager.AD_MIDPOINT));
137
+ new shaka.util.FakeEvent(shaka.ads.Utils.AD_MIDPOINT));
141
138
  });
142
139
 
143
140
  this.eventManager_.listen(this.streamManager_,
144
141
  google.ima.dai.api.StreamEvent.Type.THIRD_QUARTILE, () => {
145
142
  shaka.log.info('Ad event: Third Quartile');
146
143
  this.onEvent_(
147
- new shaka.util.FakeEvent(shaka.ads.AdManager.AD_THIRD_QUARTILE));
144
+ new shaka.util.FakeEvent(shaka.ads.Utils.AD_THIRD_QUARTILE));
148
145
  });
149
146
 
150
147
  this.eventManager_.listen(this.streamManager_,
151
148
  google.ima.dai.api.StreamEvent.Type.COMPLETE, () => {
152
149
  shaka.log.info('Ad event: Complete');
153
150
  this.onEvent_(
154
- new shaka.util.FakeEvent(shaka.ads.AdManager.AD_COMPLETE));
151
+ new shaka.util.FakeEvent(shaka.ads.Utils.AD_COMPLETE));
155
152
  this.onEvent_(
156
- new shaka.util.FakeEvent(shaka.ads.AdManager.AD_STOPPED));
153
+ new shaka.util.FakeEvent(shaka.ads.Utils.AD_STOPPED));
157
154
  this.adContainer_.removeAttribute('ad-active');
158
155
  this.ad_ = null;
159
156
  });
@@ -162,9 +159,9 @@ shaka.ads.ServerSideAdManager = class {
162
159
  google.ima.dai.api.StreamEvent.Type.SKIPPED, () => {
163
160
  shaka.log.info('Ad event: Skipped');
164
161
  this.onEvent_(
165
- new shaka.util.FakeEvent(shaka.ads.AdManager.AD_SKIPPED));
162
+ new shaka.util.FakeEvent(shaka.ads.Utils.AD_SKIPPED));
166
163
  this.onEvent_(
167
- new shaka.util.FakeEvent(shaka.ads.AdManager.AD_STOPPED));
164
+ new shaka.util.FakeEvent(shaka.ads.Utils.AD_STOPPED));
168
165
  });
169
166
 
170
167
  this.eventManager_.listen(this.streamManager_,
@@ -323,7 +320,7 @@ shaka.ads.ServerSideAdManager = class {
323
320
  this.ad_.setProgressData(this.adProgressData_);
324
321
  }
325
322
 
326
- this.onEvent_(new shaka.util.FakeEvent(shaka.ads.AdManager.AD_STARTED,
323
+ this.onEvent_(new shaka.util.FakeEvent(shaka.ads.Utils.AD_STARTED,
327
324
  (new Map()).set('ad', this.ad_)));
328
325
  this.adContainer_.setAttribute('ad-active', 'true');
329
326
  }
@@ -350,7 +347,7 @@ shaka.ads.ServerSideAdManager = class {
350
347
  onLoaded_(e) {
351
348
  const now = Date.now() / 1000;
352
349
  const loadTime = now - this.streamRequestStartTime_;
353
- this.onEvent_(new shaka.util.FakeEvent(shaka.ads.AdManager.ADS_LOADED,
350
+ this.onEvent_(new shaka.util.FakeEvent(shaka.ads.Utils.ADS_LOADED,
354
351
  (new Map()).set('loadTime', loadTime)));
355
352
 
356
353
  const streamData = e.getStreamData();
@@ -418,7 +415,7 @@ shaka.ads.ServerSideAdManager = class {
418
415
  this.currentCuePoints_ = cuePoints;
419
416
 
420
417
  this.onEvent_(new shaka.util.FakeEvent(
421
- shaka.ads.AdManager.CUEPOINTS_CHANGED,
418
+ shaka.ads.Utils.CUEPOINTS_CHANGED,
422
419
  (new Map()).set('cuepoints', cuePoints)));
423
420
  }
424
421
  };
@@ -326,6 +326,7 @@ shaka.cast.CastUtils.PlayerGetterMethods = {
326
326
  'seekRange': 1,
327
327
  'getLoadMode': 10,
328
328
  'getManifestType': 10,
329
+ 'isFullyLoaded': 1,
329
330
  };
330
331
 
331
332
 
@@ -288,6 +288,18 @@ shaka.cea.CeaUtils.H265_PREFIX_NALU_TYPE_SEI = 0x27;
288
288
  */
289
289
  shaka.cea.CeaUtils.H265_SUFFIX_NALU_TYPE_SEI = 0x28;
290
290
 
291
+ /**
292
+ * NALU type for Supplemental Enhancement Information (SEI) for H.266.
293
+ * @const {number}
294
+ */
295
+ shaka.cea.CeaUtils.H266_PREFIX_NALU_TYPE_SEI = 0x17;
296
+
297
+ /**
298
+ * NALU type for Supplemental Enhancement Information (SEI) for H.266.
299
+ * @const {number}
300
+ */
301
+ shaka.cea.CeaUtils.H266_SUFFIX_NALU_TYPE_SEI = 0x18;
302
+
291
303
  /**
292
304
  * Default timescale value for a track.
293
305
  * @const {number}
@@ -111,6 +111,8 @@ shaka.cea.Mp4CeaParser = class {
111
111
  .box('dva1', codecBoxParser)
112
112
  .box('dvh1', codecBoxParser)
113
113
  .box('dvhe', codecBoxParser)
114
+ .box('vvc1', codecBoxParser)
115
+ .box('vvi1', codecBoxParser)
114
116
 
115
117
  // This signals an encrypted sample, which we can go inside of to find
116
118
  // the codec used.
@@ -323,6 +325,15 @@ shaka.cea.Mp4CeaParser = class {
323
325
  naluType == CeaUtils.H265_SUFFIX_NALU_TYPE_SEI;
324
326
  break;
325
327
 
328
+ case BitstreamFormat.H266:
329
+ naluHeaderSize = 2;
330
+ reader.skip(1);
331
+ naluType = (naluHeader >> 1) & 0x3f;
332
+ isSeiMessage =
333
+ naluType == CeaUtils.H266_PREFIX_NALU_TYPE_SEI ||
334
+ naluType == CeaUtils.H266_SUFFIX_NALU_TYPE_SEI;
335
+ break;
336
+
326
337
  default:
327
338
  return;
328
339
  }
@@ -387,12 +398,15 @@ shaka.cea.Mp4CeaParser.BitstreamFormat = {
387
398
  UNKNOWN: 0,
388
399
  H264: 1,
389
400
  H265: 2,
401
+ H266: 3,
390
402
  };
391
403
 
392
404
  /** @private {Object.<string, shaka.cea.Mp4CeaParser.BitstreamFormat>} */
393
405
  shaka.cea.Mp4CeaParser.CodecBitstreamMap_ = {
406
+ // AVC
394
407
  'avc1': shaka.cea.Mp4CeaParser.BitstreamFormat.H264,
395
408
  'avc3': shaka.cea.Mp4CeaParser.BitstreamFormat.H264,
409
+ // HEVC
396
410
  'hev1': shaka.cea.Mp4CeaParser.BitstreamFormat.H265,
397
411
  'hvc1': shaka.cea.Mp4CeaParser.BitstreamFormat.H265,
398
412
  // Dolby Vision based in AVC
@@ -401,6 +415,9 @@ shaka.cea.Mp4CeaParser.CodecBitstreamMap_ = {
401
415
  // Dolby Vision based in HEVC
402
416
  'dvh1': shaka.cea.Mp4CeaParser.BitstreamFormat.H265,
403
417
  'dvhe': shaka.cea.Mp4CeaParser.BitstreamFormat.H265,
418
+ // VVC
419
+ 'vvc1': shaka.cea.Mp4CeaParser.BitstreamFormat.H266,
420
+ 'vvi1': shaka.cea.Mp4CeaParser.BitstreamFormat.H266,
404
421
  };
405
422
 
406
423
  /**
@@ -24,6 +24,7 @@ goog.require('shaka.net.NetworkingEngine');
24
24
  goog.require('shaka.text.TextEngine');
25
25
  goog.require('shaka.util.ContentSteeringManager');
26
26
  goog.require('shaka.util.Error');
27
+ goog.require('shaka.util.EventManager');
27
28
  goog.require('shaka.util.Functional');
28
29
  goog.require('shaka.util.LanguageUtils');
29
30
  goog.require('shaka.util.ManifestParserUtils');
@@ -122,6 +123,14 @@ shaka.dash.DashParser = class {
122
123
 
123
124
  /** @private {shaka.util.Timer} */
124
125
  this.updateTimer_ = new shaka.util.Timer(() => {
126
+ if (this.mediaElement_ && !this.config_.continueLoadingWhenPaused) {
127
+ this.eventManager_.unlisten(this.mediaElement_, 'timeupdate');
128
+ if (this.mediaElement_.paused) {
129
+ this.eventManager_.listenOnce(
130
+ this.mediaElement_, 'timeupdate', () => this.onUpdate_());
131
+ return;
132
+ }
133
+ }
125
134
  this.onUpdate_();
126
135
  });
127
136
 
@@ -152,6 +161,18 @@ shaka.dash.DashParser = class {
152
161
 
153
162
  /** @private {?shaka.util.ContentSteeringManager} */
154
163
  this.contentSteeringManager_ = null;
164
+
165
+ /** @private {number} */
166
+ this.gapCount_ = 0;
167
+
168
+ /** @private {boolean} */
169
+ this.isLowLatency_ = false;
170
+
171
+ /** @private {shaka.util.EventManager} */
172
+ this.eventManager_ = new shaka.util.EventManager();
173
+
174
+ /** @private {HTMLMediaElement} */
175
+ this.mediaElement_ = null;
155
176
  }
156
177
 
157
178
  /**
@@ -253,6 +274,11 @@ shaka.dash.DashParser = class {
253
274
  this.contentSteeringManager_.destroy();
254
275
  }
255
276
 
277
+ if (this.eventManager_) {
278
+ this.eventManager_.release();
279
+ this.eventManager_ = null;
280
+ }
281
+
256
282
  return this.operationManager_.destroy();
257
283
  }
258
284
 
@@ -317,6 +343,14 @@ shaka.dash.DashParser = class {
317
343
  }
318
344
  }
319
345
 
346
+ /**
347
+ * @override
348
+ * @exportInterface
349
+ */
350
+ setMediaElement(mediaElement) {
351
+ this.mediaElement_ = mediaElement;
352
+ }
353
+
320
354
  /**
321
355
  * Makes a network request for the manifest and parses the resulting data.
322
356
  *
@@ -326,13 +360,14 @@ shaka.dash.DashParser = class {
326
360
  */
327
361
  async requestManifest_() {
328
362
  const requestType = shaka.net.NetworkingEngine.RequestType.MANIFEST;
329
- const type = shaka.net.NetworkingEngine.AdvancedRequestType.MPD;
363
+ let type = shaka.net.NetworkingEngine.AdvancedRequestType.MPD;
330
364
  let rootElement = 'MPD';
331
365
  const patchLocationUris = this.getPatchLocationUris_();
332
366
  let manifestUris = this.manifestUris_;
333
367
  if (patchLocationUris.length) {
334
368
  manifestUris = patchLocationUris;
335
369
  rootElement = 'Patch';
370
+ type = shaka.net.NetworkingEngine.AdvancedRequestType.MPD_PATCH;
336
371
  } else if (this.manifestUris_.length > 1 && this.contentSteeringManager_) {
337
372
  const locations = this.contentSteeringManager_.getLocations(
338
373
  'Location', /* ignoreBaseUrls= */ true);
@@ -687,8 +722,10 @@ shaka.dash.DashParser = class {
687
722
  availabilityTimeOffset: availabilityTimeOffset,
688
723
  mediaPresentationDuration: null,
689
724
  profiles: profiles.split(','),
725
+ roles: null,
690
726
  };
691
727
 
728
+ this.gapCount_ = 0;
692
729
  const periodsAndDuration = this.parsePeriods_(context, getBaseUris, mpd);
693
730
  const duration = periodsAndDuration.duration;
694
731
  const periods = periodsAndDuration.periods;
@@ -704,7 +741,7 @@ shaka.dash.DashParser = class {
704
741
  // lowLatencyMode, but if it has been configured to activate the
705
742
  // lowLatencyMode if a stream of this type is detected, we automatically
706
743
  // activate the lowLatencyMode.
707
- if (this.minTotalAvailabilityTimeOffset_ && !this.lowLatencyMode_) {
744
+ if (this.isLowLatency_ && !this.lowLatencyMode_) {
708
745
  const autoLowLatencyMode = this.playerInterface_.isAutoLowLatencyMode();
709
746
  if (autoLowLatencyMode) {
710
747
  this.playerInterface_.enableLowLatencyMode();
@@ -715,14 +752,6 @@ shaka.dash.DashParser = class {
715
752
  if (this.lowLatencyMode_) {
716
753
  presentationTimeline.setAvailabilityTimeOffset(
717
754
  this.minTotalAvailabilityTimeOffset_);
718
- } else if (this.minTotalAvailabilityTimeOffset_) {
719
- // If the playlist contains AvailabilityTimeOffset value, the
720
- // streaming.lowLatencyMode value should be set to true to stream with low
721
- // latency mode.
722
- shaka.log.alwaysWarn('Low-latency DASH live stream detected, but ' +
723
- 'low-latency streaming mode is not enabled in Shaka Player. ' +
724
- 'Set streaming.lowLatencyMode configuration to true, and see ' +
725
- 'https://bit.ly/3clctcj for details.');
726
755
  }
727
756
 
728
757
  // Use @maxSegmentDuration to override smaller, derived values.
@@ -758,6 +787,9 @@ shaka.dash.DashParser = class {
758
787
  type: shaka.media.ManifestParser.DASH,
759
788
  serviceDescription: this.parseServiceDescription_(mpd),
760
789
  nextUrl: this.parseMpdChaining_(mpd),
790
+ periodCount: periods.length,
791
+ gapCount: this.gapCount_,
792
+ isLowLatency: this.isLowLatency_,
761
793
  };
762
794
 
763
795
  // We only need to do clock sync when we're using presentation start
@@ -778,8 +810,9 @@ shaka.dash.DashParser = class {
778
810
  // maintain consistency from here on.
779
811
  presentationTimeline.lockStartTime();
780
812
  } else {
781
- await this.postPeriodProcessing_(
782
- periodsAndDuration.periods, /* isPatchUpdate= */ false);
813
+ this.manifest_.periodCount = periods.length;
814
+ this.manifest_.gapCount = this.gapCount_;
815
+ await this.postPeriodProcessing_(periods, /* isPatchUpdate= */ false);
783
816
  }
784
817
 
785
818
  // Add text streams to correspond to closed captions. This happens right
@@ -900,6 +933,8 @@ shaka.dash.DashParser = class {
900
933
  }
901
934
 
902
935
  if (newPeriods.length) {
936
+ this.manifest_.periodCount += newPeriods.length;
937
+ this.manifest_.gapCount = this.gapCount_;
903
938
  await this.postPeriodProcessing_(newPeriods, /* isPatchUpdate= */ true);
904
939
  }
905
940
  if (this.manifestPatchContext_.type == 'static') {
@@ -966,6 +1001,7 @@ shaka.dash.DashParser = class {
966
1001
  profiles: this.manifestPatchContext_.profiles,
967
1002
  mediaPresentationDuration:
968
1003
  this.manifestPatchContext_.mediaPresentationDuration,
1004
+ roles: null,
969
1005
  };
970
1006
 
971
1007
  const periodsAndDuration = this.parsePeriods_(context,
@@ -980,6 +1016,7 @@ shaka.dash.DashParser = class {
980
1016
  */
981
1017
  removePatchPeriod_(periodId) {
982
1018
  const SegmentTemplate = shaka.dash.SegmentTemplate;
1019
+ this.manifest_.periodCount--;
983
1020
  for (const contextId of this.contextCache_.keys()) {
984
1021
  if (contextId.startsWith(periodId)) {
985
1022
  const context = this.contextCache_.get(contextId);
@@ -1264,6 +1301,12 @@ shaka.dash.DashParser = class {
1264
1301
  if (periodDuration && givenDuration &&
1265
1302
  Math.abs(periodDuration - givenDuration) > threshold) {
1266
1303
  shaka.log.warning('There is a gap/overlap between Periods', elem);
1304
+
1305
+ // This means it's a gap, the distance between period starts is
1306
+ // larger than the period's duration
1307
+ if (periodDuration > givenDuration) {
1308
+ this.gapCount_++;
1309
+ }
1267
1310
  }
1268
1311
  // Only use the @duration in the MPD if we can't calculate it. We should
1269
1312
  // favor the @start of the following Period. This ensures that there
@@ -1773,19 +1816,7 @@ shaka.dash.DashParser = class {
1773
1816
  const language = shaka.util.LanguageUtils.normalize(
1774
1817
  context.adaptationSet.language || 'und');
1775
1818
 
1776
- // This attribute is currently non-standard, but it is supported by Kaltura.
1777
- let label = elem.attributes['label'];
1778
-
1779
- // See DASH IOP 4.3 here https://dashif.org/docs/DASH-IF-IOP-v4.3.pdf (page 35)
1780
- const labelElements = TXml.findChildren(elem, 'Label');
1781
- if (labelElements && labelElements.length) {
1782
- // NOTE: Right now only one label field is supported.
1783
- const firstLabelElement = labelElements[0];
1784
- const textContent = shaka.util.TXml.getTextContents(firstLabelElement);
1785
- if (textContent) {
1786
- label = textContent;
1787
- }
1788
- }
1819
+ const label = context.adaptationSet.label;
1789
1820
 
1790
1821
  // Parse Representations into Streams.
1791
1822
  const representations = TXml.findChildren(elem, 'Representation');
@@ -1873,7 +1904,7 @@ shaka.dash.DashParser = class {
1873
1904
  * @param {shaka.dash.ContentProtection.Context} contentProtection
1874
1905
  * @param {(string|undefined)} kind
1875
1906
  * @param {string} language
1876
- * @param {string} label
1907
+ * @param {?string} label
1877
1908
  * @param {boolean} isPrimary
1878
1909
  * @param {!Array.<string>} roles
1879
1910
  * @param {Map.<string, string>} closedCaptions
@@ -1901,6 +1932,8 @@ shaka.dash.DashParser = class {
1901
1932
  Math.min(this.minTotalAvailabilityTimeOffset_,
1902
1933
  context.representation.availabilityTimeOffset);
1903
1934
 
1935
+ this.isLowLatency_ = this.minTotalAvailabilityTimeOffset_ > 0;
1936
+
1904
1937
  if (!this.verifyRepresentation_(context.representation)) {
1905
1938
  shaka.log.warning('Skipping Representation', context.representation);
1906
1939
  return null;
@@ -1915,6 +1948,8 @@ shaka.dash.DashParser = class {
1915
1948
  context.bandwidth =
1916
1949
  TXml.parseAttr(node, 'bandwidth', TXml.parsePositiveInt) || 0;
1917
1950
 
1951
+ context.roles = roles;
1952
+
1918
1953
  /** @type {?shaka.dash.DashParser.StreamInfo} */
1919
1954
  let streamInfo;
1920
1955
 
@@ -2408,6 +2443,19 @@ shaka.dash.DashParser = class {
2408
2443
  }
2409
2444
  }
2410
2445
 
2446
+ // This attribute is currently non-standard, but it is supported by Kaltura.
2447
+ let label = elem.attributes['label'];
2448
+
2449
+ // See DASH IOP 4.3 here https://dashif.org/docs/DASH-IF-IOP-v4.3.pdf (page 35)
2450
+ const labelElements = TXml.findChildren(elem, 'Label');
2451
+ if (labelElements && labelElements.length) {
2452
+ // NOTE: Right now only one label field is supported.
2453
+ const firstLabelElement = labelElements[0];
2454
+ if (TXml.getTextContents(firstLabelElement)) {
2455
+ label = TXml.getTextContents(firstLabelElement);
2456
+ }
2457
+ }
2458
+
2411
2459
  return {
2412
2460
  getBaseUris:
2413
2461
  () => ManifestParserUtils.resolveUris(getBaseUris(), getFrameUris()),
@@ -2431,6 +2479,7 @@ shaka.dash.DashParser = class {
2431
2479
  initialization: null,
2432
2480
  segmentSequenceCadence:
2433
2481
  segmentSequenceCadence || parent.segmentSequenceCadence,
2482
+ label: label || null,
2434
2483
  };
2435
2484
  }
2436
2485
 
@@ -2934,7 +2983,8 @@ shaka.dash.DashParser.RequestSegmentCallback;
2934
2983
  * availabilityTimeOffset: number,
2935
2984
  * initialization: ?string,
2936
2985
  * aesKey: (shaka.extern.aesKey|undefined),
2937
- * segmentSequenceCadence: number
2986
+ * segmentSequenceCadence: number,
2987
+ * label: ?string
2938
2988
  * }}
2939
2989
  *
2940
2990
  * @description
@@ -2982,6 +3032,8 @@ shaka.dash.DashParser.RequestSegmentCallback;
2982
3032
  * @property {number} segmentSequenceCadence
2983
3033
  * Specifies the cadence of independent segments in Segment Sequence
2984
3034
  * Representation.
3035
+ * @property {?string} label
3036
+ * Label or null if unknown.
2985
3037
  */
2986
3038
  shaka.dash.DashParser.InheritanceFrame;
2987
3039
 
@@ -2998,7 +3050,8 @@ shaka.dash.DashParser.InheritanceFrame;
2998
3050
  * indexRangeWarningGiven: boolean,
2999
3051
  * availabilityTimeOffset: number,
3000
3052
  * mediaPresentationDuration: ?number,
3001
- * profiles: !Array.<string>
3053
+ * profiles: !Array.<string>,
3054
+ * roles: ?Array.<string>
3002
3055
  * }}
3003
3056
  *
3004
3057
  * @description
@@ -386,6 +386,9 @@ shaka.dash.SegmentBase = class {
386
386
  channelsCount: representation.numChannels,
387
387
  pixelAspectRatio: representation.pixelAspectRatio || null,
388
388
  width: representation.width || null,
389
+ label: context.adaptationSet.label || null,
390
+ roles: context.roles || null,
391
+ language: context.adaptationSet.language || null,
389
392
  };
390
393
  }
391
394
  };