shaka-player 4.7.12 → 4.8.0

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 (120) hide show
  1. package/AUTHORS +1 -0
  2. package/CONTRIBUTORS +1 -0
  3. package/LICENSE +54 -0
  4. package/README.md +22 -1
  5. package/dist/controls.css +1 -1
  6. package/dist/controls.css.map +1 -1
  7. package/dist/deps.js +36 -25
  8. package/dist/locales.js +40 -30
  9. package/dist/shaka-player.compiled.d.ts +196 -46
  10. package/dist/shaka-player.compiled.debug.d.ts +196 -46
  11. package/dist/shaka-player.compiled.debug.externs.js +474 -337
  12. package/dist/shaka-player.compiled.debug.js +992 -871
  13. package/dist/shaka-player.compiled.debug.map +4 -4
  14. package/dist/shaka-player.compiled.externs.js +474 -337
  15. package/dist/shaka-player.compiled.js +1248 -1174
  16. package/dist/shaka-player.compiled.map +4 -4
  17. package/dist/shaka-player.ui.d.ts +259 -49
  18. package/dist/shaka-player.ui.debug.d.ts +259 -49
  19. package/dist/shaka-player.ui.debug.externs.js +591 -376
  20. package/dist/shaka-player.ui.debug.js +1208 -1003
  21. package/dist/shaka-player.ui.debug.map +4 -4
  22. package/dist/shaka-player.ui.externs.js +591 -376
  23. package/dist/shaka-player.ui.js +1533 -1408
  24. package/dist/shaka-player.ui.map +4 -4
  25. package/externs/device_sensor_event.js +18 -0
  26. package/externs/hisense.js +16 -0
  27. package/externs/playstation.js +31 -0
  28. package/externs/shaka/abr_manager.js +3 -1
  29. package/externs/shaka/ads.js +5 -0
  30. package/externs/shaka/codecs.js +13 -0
  31. package/externs/shaka/manifest.js +69 -7
  32. package/externs/shaka/net.js +13 -1
  33. package/externs/shaka/offline.js +7 -1
  34. package/externs/shaka/player.js +184 -13
  35. package/externs/shaka/text.js +19 -0
  36. package/externs/tizen.js +41 -0
  37. package/externs/webos.js +19 -0
  38. package/externs/xbox.js +43 -0
  39. package/lib/abr/simple_abr_manager.js +9 -1
  40. package/lib/ads/ad_manager.js +19 -2
  41. package/lib/ads/client_side_ad_manager.js +5 -3
  42. package/lib/ads/media_tailor_ad_manager.js +9 -0
  43. package/lib/cast/cast_receiver.js +0 -14
  44. package/lib/cast/cast_utils.js +1 -0
  45. package/lib/cea/mp4_cea_parser.js +6 -1
  46. package/lib/dash/content_protection.js +97 -53
  47. package/lib/dash/dash_parser.js +240 -179
  48. package/lib/dash/mpd_utils.js +72 -65
  49. package/lib/dash/segment_base.js +35 -22
  50. package/lib/dash/segment_list.js +39 -31
  51. package/lib/dash/segment_template.js +47 -23
  52. package/lib/hls/hls_parser.js +376 -165
  53. package/lib/media/buffering_observer.js +25 -1
  54. package/lib/media/drm_engine.js +91 -71
  55. package/lib/media/manifest_filterer.js +220 -0
  56. package/lib/media/media_source_engine.js +108 -29
  57. package/lib/media/playhead.js +3 -3
  58. package/lib/media/preload_manager.js +827 -0
  59. package/lib/media/presentation_timeline.js +11 -0
  60. package/lib/media/segment_index.js +60 -49
  61. package/lib/media/segment_prefetch.js +140 -43
  62. package/lib/media/segment_reference.js +31 -8
  63. package/lib/media/segment_utils.js +208 -25
  64. package/lib/media/streaming_engine.js +321 -77
  65. package/lib/mss/content_protection.js +31 -25
  66. package/lib/mss/mss_parser.js +78 -55
  67. package/lib/net/networking_engine.js +49 -4
  68. package/lib/offline/indexeddb/v1_storage_cell.js +2 -0
  69. package/lib/offline/indexeddb/v2_storage_cell.js +2 -0
  70. package/lib/offline/manifest_converter.js +10 -3
  71. package/lib/offline/storage.js +5 -3
  72. package/lib/player.js +896 -674
  73. package/lib/polyfill/media_capabilities.js +16 -1
  74. package/lib/text/simple_text_displayer.js +9 -0
  75. package/lib/text/stub_text_displayer.js +7 -0
  76. package/lib/text/text_engine.js +16 -3
  77. package/lib/text/ttml_text_parser.js +116 -114
  78. package/lib/text/ui_text_displayer.js +28 -11
  79. package/lib/text/vtt_text_parser.js +54 -115
  80. package/lib/transmuxer/opus.js +91 -0
  81. package/lib/transmuxer/ts_transmuxer.js +115 -0
  82. package/lib/util/cmcd_manager.js +152 -3
  83. package/lib/util/dom_utils.js +11 -0
  84. package/lib/util/error.js +5 -0
  85. package/lib/util/fake_event.js +2 -0
  86. package/lib/util/manifest_parser_utils.js +97 -3
  87. package/lib/util/mime_utils.js +10 -5
  88. package/lib/util/mp4_box_parsers.js +346 -17
  89. package/lib/util/mp4_generator.js +23 -0
  90. package/lib/util/periods.js +24 -37
  91. package/lib/util/platform.js +161 -9
  92. package/lib/util/player_configuration.js +61 -3
  93. package/lib/util/stats.js +16 -0
  94. package/lib/util/stream_utils.js +350 -277
  95. package/lib/util/string_utils.js +36 -0
  96. package/lib/util/tXml.js +814 -0
  97. package/lib/util/ts_parser.js +50 -0
  98. package/lib/util/xml_utils.js +10 -338
  99. package/package.json +2 -2
  100. package/shaka-player.uncompiled.js +8 -0
  101. package/ui/controls.js +187 -5
  102. package/ui/enums.js +2 -0
  103. package/ui/externs/ui.js +30 -3
  104. package/ui/gl_matrix/matrix_4x4.js +568 -0
  105. package/ui/gl_matrix/matrix_quaternion.js +45 -0
  106. package/ui/less/containers.less +11 -0
  107. package/ui/locales/en-GB.json +2 -0
  108. package/ui/locales/en.json +2 -0
  109. package/ui/locales/es-419.json +2 -0
  110. package/ui/locales/es.json +2 -0
  111. package/ui/locales/source.json +8 -0
  112. package/ui/recenter_vr.js +126 -0
  113. package/ui/resolution_selection.js +25 -5
  114. package/ui/statistics_button.js +12 -0
  115. package/ui/text_selection.js +1 -1
  116. package/ui/toggle_stereoscopic.js +127 -0
  117. package/ui/ui.js +74 -38
  118. package/ui/vr_manager.js +557 -0
  119. package/ui/vr_utils.js +232 -0
  120. package/ui/vr_webgl.js +697 -0
@@ -11,8 +11,8 @@ goog.require('shaka.util.BufferUtils');
11
11
  goog.require('shaka.util.ManifestParserUtils');
12
12
  goog.require('shaka.util.Pssh');
13
13
  goog.require('shaka.util.StringUtils');
14
+ goog.require('shaka.util.TXml');
14
15
  goog.require('shaka.util.Uint8ArrayUtils');
15
- goog.require('shaka.util.XmlUtils');
16
16
 
17
17
 
18
18
  /**
@@ -23,19 +23,19 @@ shaka.mss.ContentProtection = class {
23
23
  /**
24
24
  * Parses info from the Protection elements.
25
25
  *
26
- * @param {!Array.<!Element>} elems
26
+ * @param {!Array.<!shaka.extern.xml.Node>} elems
27
27
  * @param {!Object.<string, string>} keySystemsBySystemId
28
28
  * @return {!Array.<shaka.extern.DrmInfo>}
29
29
  */
30
30
  static parseFromProtection(elems, keySystemsBySystemId) {
31
31
  const ContentProtection = shaka.mss.ContentProtection;
32
- const XmlUtils = shaka.util.XmlUtils;
32
+ const TXml = shaka.util.TXml;
33
33
 
34
- /** @type {!Array.<!Element>} */
34
+ /** @type {!Array.<!shaka.extern.xml.Node>} */
35
35
  let protectionHeader = [];
36
36
  for (const elem of elems) {
37
37
  protectionHeader = protectionHeader.concat(
38
- XmlUtils.findChildren(elem, 'ProtectionHeader'));
38
+ TXml.findChildren(elem, 'ProtectionHeader'));
39
39
  }
40
40
  if (!protectionHeader.length) {
41
41
  return [];
@@ -123,18 +123,19 @@ shaka.mss.ContentProtection = class {
123
123
  * Parse a PlayReady Header format: https://goo.gl/dBzxNA
124
124
  * a try to find the LA_URL value.
125
125
  *
126
- * @param {!Element} xml
126
+ * @param {!shaka.extern.xml.Node} xml
127
127
  * @return {string}
128
128
  * @private
129
129
  */
130
130
  static getLaurl_(xml) {
131
+ const TXml = shaka.util.TXml;
131
132
  // LA_URL element is optional and no more than one is
132
133
  // allowed inside the DATA element. Only absolute URLs are allowed.
133
134
  // If the LA_URL element exists, it must not be empty.
134
- for (const elem of xml.getElementsByTagName('DATA')) {
135
- const laUrl = shaka.util.XmlUtils.findChild(elem, 'LA_URL');
135
+ for (const elem of TXml.getElementsByTagName(xml, 'DATA')) {
136
+ const laUrl = TXml.findChild(elem, 'LA_URL');
136
137
  if (laUrl) {
137
- return laUrl.textContent;
138
+ return /** @type {string} */ (shaka.util.TXml.getTextContents(laUrl));
138
139
  }
139
140
  }
140
141
 
@@ -148,7 +149,7 @@ shaka.mss.ContentProtection = class {
148
149
  * Gets a PlayReady license URL from a protection element
149
150
  * containing a PlayReady Header Object
150
151
  *
151
- * @param {!Element} element
152
+ * @param {!shaka.extern.xml.Node} element
152
153
  * @return {string}
153
154
  */
154
155
  static getPlayReadyLicenseUrl(element) {
@@ -165,19 +166,21 @@ shaka.mss.ContentProtection = class {
165
166
  * Parse a PlayReady Header format: https://goo.gl/dBzxNA
166
167
  * a try to find the KID value.
167
168
  *
168
- * @param {!Element} xml
169
+ * @param {!shaka.extern.xml.Node} xml
169
170
  * @return {?string}
170
171
  * @private
171
172
  */
172
173
  static getKID_(xml) {
174
+ const TXml = shaka.util.TXml;
173
175
  // KID element is optional and no more than one is
174
176
  // allowed inside the DATA element.
175
- for (const elem of xml.getElementsByTagName('DATA')) {
176
- const kid = shaka.util.XmlUtils.findChild(elem, 'KID');
177
+ for (const elem of TXml.getElementsByTagName(xml, 'DATA')) {
178
+ const kid = TXml.findChild(elem, 'KID');
177
179
  if (kid) {
178
180
  // GUID: [DWORD, WORD, WORD, 8-BYTE]
179
181
  const guidBytes =
180
- shaka.util.Uint8ArrayUtils.fromBase64(kid.textContent);
182
+ shaka.util.Uint8ArrayUtils.fromBase64(
183
+ /** @type{string} */ (shaka.util.TXml.getTextContents(kid)));
181
184
  // Reverse byte order from little-endian to big-endian
182
185
  const kidBytes = new Uint8Array([
183
186
  guidBytes[3], guidBytes[2], guidBytes[1], guidBytes[0],
@@ -197,7 +200,7 @@ shaka.mss.ContentProtection = class {
197
200
  * Gets a PlayReady KID from a protection element
198
201
  * containing a PlayReady Header Object
199
202
  *
200
- * @param {!Element} element
203
+ * @param {!shaka.extern.xml.Node} element
201
204
  * @return {?string}
202
205
  * @private
203
206
  */
@@ -214,15 +217,16 @@ shaka.mss.ContentProtection = class {
214
217
  /**
215
218
  * Gets a PlayReady Header Object from a protection element
216
219
  *
217
- * @param {!Element} element
218
- * @return {?Element}
220
+ * @param {!shaka.extern.xml.Node} element
221
+ * @return {?shaka.extern.xml.Node}
219
222
  * @private
220
223
  */
221
224
  static getPlayReadyHeaderObject_(element) {
222
225
  const ContentProtection = shaka.mss.ContentProtection;
223
226
  const PLAYREADY_RECORD_TYPES = ContentProtection.PLAYREADY_RECORD_TYPES;
224
227
 
225
- const bytes = shaka.util.Uint8ArrayUtils.fromBase64(element.textContent);
228
+ const bytes = shaka.util.Uint8ArrayUtils.fromBase64(
229
+ /** @type{string} */ (shaka.util.TXml.getTextContents(element)));
226
230
  const records = ContentProtection.parseMsPro_(bytes);
227
231
  const record = records.filter((record) => {
228
232
  return record.type === PLAYREADY_RECORD_TYPES.RIGHTS_MANAGEMENT;
@@ -233,7 +237,7 @@ shaka.mss.ContentProtection = class {
233
237
  }
234
238
 
235
239
  const xml = shaka.util.StringUtils.fromUTF16(record.value, true);
236
- const rootElement = shaka.util.XmlUtils.parseXmlString(xml, 'WRMHEADER');
240
+ const rootElement = shaka.util.TXml.parseXmlString(xml, 'WRMHEADER');
237
241
  if (!rootElement) {
238
242
  return null;
239
243
  }
@@ -243,7 +247,7 @@ shaka.mss.ContentProtection = class {
243
247
  /**
244
248
  * Gets a initData from a protection element.
245
249
  *
246
- * @param {!Element} element
250
+ * @param {!shaka.extern.xml.Node} element
247
251
  * @param {string} systemID
248
252
  * @param {?string} keyId
249
253
  * @return {?Array.<shaka.extern.InitDataOverride>}
@@ -251,7 +255,8 @@ shaka.mss.ContentProtection = class {
251
255
  */
252
256
  static getInitDataFromPro_(element, systemID, keyId) {
253
257
  const Uint8ArrayUtils = shaka.util.Uint8ArrayUtils;
254
- const data = Uint8ArrayUtils.fromBase64(element.textContent);
258
+ const data = Uint8ArrayUtils.fromBase64(
259
+ /** @type{string} */ (shaka.util.TXml.getTextContents(element)));
255
260
  const systemId = Uint8ArrayUtils.fromHex(systemID.replace(/-/g, ''));
256
261
  const keyIds = new Set();
257
262
  const psshVersion = 0;
@@ -269,7 +274,7 @@ shaka.mss.ContentProtection = class {
269
274
  /**
270
275
  * Creates DrmInfo objects from an array of elements.
271
276
  *
272
- * @param {!Array.<!Element>} elements
277
+ * @param {!Array.<!shaka.extern.xml.Node>} elements
273
278
  * @param {!Object.<string, string>} keySystemsBySystemId
274
279
  * @return {!Array.<shaka.extern.DrmInfo>}
275
280
  * @private
@@ -284,14 +289,15 @@ shaka.mss.ContentProtection = class {
284
289
 
285
290
  for (let i = 0; i < elements.length; i++) {
286
291
  const element = elements[i];
287
- const systemID = element.getAttribute('SystemID').toLowerCase();
292
+ const systemID = element.attributes['SystemID'].toLowerCase();
288
293
  const keySystem = keySystemsBySystemId[systemID];
289
294
  if (keySystem) {
290
295
  const KID = ContentProtection.getPlayReadyKID_(element);
291
296
  const initData = ContentProtection.getInitDataFromPro_(
292
297
  element, systemID, KID);
293
298
 
294
- const info = ManifestParserUtils.createDrmInfo(keySystem, initData);
299
+ const info = ManifestParserUtils.createDrmInfo(
300
+ keySystem, /* encryptionScheme= */ 'cenc', initData);
295
301
  if (KID) {
296
302
  info.keyIds.add(KID);
297
303
  }
@@ -338,7 +344,7 @@ shaka.mss.ContentProtection.PLAYREADY_RECORD_TYPES = {
338
344
  /**
339
345
  * A map of key system name to license server url parser.
340
346
  *
341
- * @const {!Map.<string, function(!Element)>}
347
+ * @const {!Map.<string, function(!shaka.extern.xml.Node)>}
342
348
  * @private
343
349
  */
344
350
  shaka.mss.ContentProtection.licenseUrlParsers_ = new Map()
@@ -7,6 +7,7 @@
7
7
  goog.provide('shaka.mss.MssParser');
8
8
 
9
9
  goog.require('goog.asserts');
10
+ goog.require('shaka.Deprecate');
10
11
  goog.require('shaka.abr.Ewma');
11
12
  goog.require('shaka.log');
12
13
  goog.require('shaka.media.InitSegmentReference');
@@ -19,9 +20,11 @@ goog.require('shaka.net.NetworkingEngine');
19
20
  goog.require('shaka.util.Error');
20
21
  goog.require('shaka.util.LanguageUtils');
21
22
  goog.require('shaka.util.ManifestParserUtils');
23
+ goog.require('shaka.util.MimeUtils');
22
24
  goog.require('shaka.util.Mp4Generator');
23
25
  goog.require('shaka.util.OperationManager');
24
26
  goog.require('shaka.util.Timer');
27
+ goog.require('shaka.util.TXml');
25
28
  goog.require('shaka.util.XmlUtils');
26
29
 
27
30
 
@@ -277,7 +280,25 @@ shaka.mss.MssParser = class {
277
280
  * @private
278
281
  */
279
282
  parseManifest_(data, finalManifestUri) {
280
- const mss = shaka.util.XmlUtils.parseXml(data, 'SmoothStreamingMedia');
283
+ let manifestData = data;
284
+ const manifestPreprocessor = this.config_.mss.manifestPreprocessor;
285
+ if (manifestPreprocessor) {
286
+ shaka.Deprecate.deprecateFeature(5,
287
+ 'manifest.mss.manifestPreprocessor configuration',
288
+ 'Please Use manifest.mss.manifestPreprocessorTXml instead.');
289
+ const mssElement =
290
+ shaka.util.XmlUtils.parseXml(manifestData, 'SmoothStreamingMedia');
291
+ if (!mssElement) {
292
+ throw new shaka.util.Error(
293
+ shaka.util.Error.Severity.CRITICAL,
294
+ shaka.util.Error.Category.MANIFEST,
295
+ shaka.util.Error.Code.MSS_INVALID_XML,
296
+ finalManifestUri);
297
+ }
298
+ manifestPreprocessor(mssElement);
299
+ manifestData = shaka.util.XmlUtils.toArrayBuffer(mssElement);
300
+ }
301
+ const mss = shaka.util.TXml.parseXml(manifestData, 'SmoothStreamingMedia');
281
302
  if (!mss) {
282
303
  throw new shaka.util.Error(
283
304
  shaka.util.Error.Severity.CRITICAL,
@@ -285,6 +306,10 @@ shaka.mss.MssParser = class {
285
306
  shaka.util.Error.Code.MSS_INVALID_XML,
286
307
  finalManifestUri);
287
308
  }
309
+ const manifestPreprocessorTXml = this.config_.mss.manifestPreprocessorTXml;
310
+ if (manifestPreprocessorTXml) {
311
+ manifestPreprocessorTXml(mss);
312
+ }
288
313
  this.processManifest_(mss, finalManifestUri);
289
314
  return Promise.resolve();
290
315
  }
@@ -293,26 +318,21 @@ shaka.mss.MssParser = class {
293
318
  /**
294
319
  * Takes a formatted MSS and converts it into a manifest.
295
320
  *
296
- * @param {!Element} mss
321
+ * @param {!shaka.extern.xml.Node} mss
297
322
  * @param {string} finalManifestUri The final manifest URI, which may
298
323
  * differ from this.manifestUri_ if there has been a redirect.
299
324
  * @private
300
325
  */
301
326
  processManifest_(mss, finalManifestUri) {
302
- const XmlUtils = shaka.util.XmlUtils;
303
-
304
- const manifestPreprocessor = this.config_.mss.manifestPreprocessor;
305
- if (manifestPreprocessor) {
306
- manifestPreprocessor(mss);
307
- }
327
+ const TXml = shaka.util.TXml;
308
328
 
309
329
  if (!this.presentationTimeline_) {
310
330
  this.presentationTimeline_ = new shaka.media.PresentationTimeline(
311
331
  /* presentationStartTime= */ null, /* delay= */ 0);
312
332
  }
313
333
 
314
- const isLive = XmlUtils.parseAttr(mss, 'IsLive',
315
- XmlUtils.parseBoolean, /* defaultValue= */ false);
334
+ const isLive = TXml.parseAttr(mss, 'IsLive',
335
+ TXml.parseBoolean, /* defaultValue= */ false);
316
336
 
317
337
  if (isLive) {
318
338
  throw new shaka.util.Error(
@@ -323,21 +343,21 @@ shaka.mss.MssParser = class {
323
343
 
324
344
  this.presentationTimeline_.setStatic(!isLive);
325
345
 
326
- const timescale = XmlUtils.parseAttr(mss, 'TimeScale',
327
- XmlUtils.parseNonNegativeInt, shaka.mss.MssParser.DEFAULT_TIME_SCALE_);
346
+ const timescale = TXml.parseAttr(mss, 'TimeScale',
347
+ TXml.parseNonNegativeInt, shaka.mss.MssParser.DEFAULT_TIME_SCALE_);
328
348
  goog.asserts.assert(timescale && timescale >= 0,
329
349
  'Timescale must be defined!');
330
350
 
331
- let dvrWindowLength = XmlUtils.parseAttr(mss, 'DVRWindowLength',
332
- XmlUtils.parseNonNegativeInt);
351
+ let dvrWindowLength = TXml.parseAttr(mss, 'DVRWindowLength',
352
+ TXml.parseNonNegativeInt);
333
353
  // If the DVRWindowLength field is omitted for a live presentation or set
334
354
  // to 0, the DVR window is effectively infinite
335
355
  if (isLive && (dvrWindowLength === 0 || isNaN(dvrWindowLength))) {
336
356
  dvrWindowLength = Infinity;
337
357
  }
338
358
  // Start-over
339
- const canSeek = XmlUtils.parseAttr(mss, 'CanSeek',
340
- XmlUtils.parseBoolean, /* defaultValue= */ false);
359
+ const canSeek = TXml.parseAttr(mss, 'CanSeek',
360
+ TXml.parseBoolean, /* defaultValue= */ false);
341
361
  if (dvrWindowLength === 0 && canSeek) {
342
362
  dvrWindowLength = Infinity;
343
363
  }
@@ -362,8 +382,8 @@ shaka.mss.MssParser = class {
362
382
  segmentAvailabilityDuration);
363
383
 
364
384
  // Duration in timescale units.
365
- const duration = XmlUtils.parseAttr(mss, 'Duration',
366
- XmlUtils.parseNonNegativeInt, Infinity);
385
+ const duration = TXml.parseAttr(mss, 'Duration',
386
+ TXml.parseNonNegativeInt, Infinity);
367
387
  goog.asserts.assert(duration && duration >= 0,
368
388
  'Duration must be defined!');
369
389
 
@@ -413,25 +433,25 @@ shaka.mss.MssParser = class {
413
433
  }
414
434
 
415
435
  /**
416
- * @param {!Element} mss
436
+ * @param {!shaka.extern.xml.Node} mss
417
437
  * @param {!shaka.mss.MssParser.Context} context
418
438
  * @private
419
439
  */
420
440
  parseStreamIndexes_(mss, context) {
421
441
  const ContentProtection = shaka.mss.ContentProtection;
422
- const XmlUtils = shaka.util.XmlUtils;
442
+ const TXml = shaka.util.TXml;
423
443
  const ContentType = shaka.util.ManifestParserUtils.ContentType;
424
444
 
425
- const protectionElems = XmlUtils.findChildren(mss, 'Protection');
445
+ const protectionElems = TXml.findChildren(mss, 'Protection');
426
446
  const drmInfos = ContentProtection.parseFromProtection(
427
447
  protectionElems, this.config_.mss.keySystemsBySystemId);
428
448
 
429
449
  const audioStreams = [];
430
450
  const videoStreams = [];
431
451
  const textStreams = [];
432
- const streamIndexes = XmlUtils.findChildren(mss, 'StreamIndex');
452
+ const streamIndexes = TXml.findChildren(mss, 'StreamIndex');
433
453
  for (const streamIndex of streamIndexes) {
434
- const qualityLevels = XmlUtils.findChildren(streamIndex, 'QualityLevel');
454
+ const qualityLevels = TXml.findChildren(streamIndex, 'QualityLevel');
435
455
  const timeline = this.createTimeline_(
436
456
  streamIndex, context.timescale, context.duration);
437
457
  // For each QualityLevel node, create a stream element
@@ -466,8 +486,8 @@ shaka.mss.MssParser = class {
466
486
  }
467
487
 
468
488
  /**
469
- * @param {!Element} streamIndex
470
- * @param {!Element} qualityLevel
489
+ * @param {!shaka.extern.xml.Node} streamIndex
490
+ * @param {!shaka.extern.xml.Node} qualityLevel
471
491
  * @param {!Array.<shaka.mss.MssParser.TimeRange>} timeline
472
492
  * @param {!Array.<shaka.extern.DrmInfo>} drmInfos
473
493
  * @param {!shaka.mss.MssParser.Context} context
@@ -475,11 +495,11 @@ shaka.mss.MssParser = class {
475
495
  * @private
476
496
  */
477
497
  createStream_(streamIndex, qualityLevel, timeline, drmInfos, context) {
478
- const XmlUtils = shaka.util.XmlUtils;
498
+ const TXml = shaka.util.TXml;
479
499
  const ContentType = shaka.util.ManifestParserUtils.ContentType;
480
500
  const MssParser = shaka.mss.MssParser;
481
501
 
482
- const type = streamIndex.getAttribute('Type');
502
+ const type = streamIndex.attributes['Type'];
483
503
  const isValidType = type === 'audio' || type === 'video' ||
484
504
  type === 'text';
485
505
  if (!isValidType) {
@@ -487,19 +507,19 @@ shaka.mss.MssParser = class {
487
507
  return null;
488
508
  }
489
509
 
490
- const lang = streamIndex.getAttribute('Language');
510
+ const lang = streamIndex.attributes['Language'];
491
511
  const id = this.globalId_++;
492
512
 
493
- const bandwidth = XmlUtils.parseAttr(
494
- qualityLevel, 'Bitrate', XmlUtils.parsePositiveInt);
495
- const width = XmlUtils.parseAttr(
496
- qualityLevel, 'MaxWidth', XmlUtils.parsePositiveInt);
497
- const height = XmlUtils.parseAttr(
498
- qualityLevel, 'MaxHeight', XmlUtils.parsePositiveInt);
499
- const channelsCount = XmlUtils.parseAttr(
500
- qualityLevel, 'Channels', XmlUtils.parsePositiveInt);
501
- const audioSamplingRate = XmlUtils.parseAttr(
502
- qualityLevel, 'SamplingRate', XmlUtils.parsePositiveInt);
513
+ const bandwidth = TXml.parseAttr(
514
+ qualityLevel, 'Bitrate', TXml.parsePositiveInt);
515
+ const width = TXml.parseAttr(
516
+ qualityLevel, 'MaxWidth', TXml.parsePositiveInt);
517
+ const height = TXml.parseAttr(
518
+ qualityLevel, 'MaxHeight', TXml.parsePositiveInt);
519
+ const channelsCount = TXml.parseAttr(
520
+ qualityLevel, 'Channels', TXml.parsePositiveInt);
521
+ const audioSamplingRate = TXml.parseAttr(
522
+ qualityLevel, 'SamplingRate', TXml.parsePositiveInt);
503
523
 
504
524
  let duration = context.duration;
505
525
  if (timeline.length) {
@@ -515,7 +535,7 @@ shaka.mss.MssParser = class {
515
535
  /** @type {!shaka.extern.Stream} */
516
536
  const stream = {
517
537
  id: id,
518
- originalId: streamIndex.getAttribute('Name') || String(id),
538
+ originalId: streamIndex.attributes['Name'] || String(id),
519
539
  groupId: null,
520
540
  createSegmentIndex: () => Promise.resolve(),
521
541
  closeSegmentIndex: () => Promise.resolve(),
@@ -556,10 +576,11 @@ shaka.mss.MssParser = class {
556
576
  accessibilityPurpose: null,
557
577
  external: false,
558
578
  fastSwitching: false,
579
+ fullMimeTypes: new Set(),
559
580
  };
560
581
 
561
582
  // This is specifically for text tracks.
562
- const subType = streamIndex.getAttribute('Subtype');
583
+ const subType = streamIndex.attributes['Subtype'];
563
584
  if (subType) {
564
585
  const role = MssParser.ROLE_MAPPING_[subType];
565
586
  if (role) {
@@ -570,12 +591,12 @@ shaka.mss.MssParser = class {
570
591
  }
571
592
  }
572
593
 
573
- let fourCCValue = qualityLevel.getAttribute('FourCC');
594
+ let fourCCValue = qualityLevel.attributes['FourCC'];
574
595
 
575
596
  // If FourCC not defined at QualityLevel level,
576
597
  // then get it from StreamIndex level
577
598
  if (fourCCValue === null || fourCCValue === '') {
578
- fourCCValue = streamIndex.getAttribute('FourCC');
599
+ fourCCValue = streamIndex.attributes['FourCC'];
579
600
  }
580
601
 
581
602
  // If still not defined (optional for audio stream,
@@ -634,6 +655,8 @@ shaka.mss.MssParser = class {
634
655
  }
635
656
  break;
636
657
  }
658
+ stream.fullMimeTypes.add(shaka.util.MimeUtils.getFullType(
659
+ stream.mimeType, stream.codecs));
637
660
 
638
661
  // Lazy-Load the segment index to avoid create all init segment at the
639
662
  // same time
@@ -696,7 +719,7 @@ shaka.mss.MssParser = class {
696
719
  }
697
720
 
698
721
  /**
699
- * @param {!Element} qualityLevel
722
+ * @param {!shaka.extern.xml.Node} qualityLevel
700
723
  * @param {string} type
701
724
  * @param {string} fourCCValue
702
725
  * @param {!shaka.extern.Stream} stream
@@ -704,7 +727,7 @@ shaka.mss.MssParser = class {
704
727
  * @private
705
728
  */
706
729
  getCodecPrivateData_(qualityLevel, type, fourCCValue, stream) {
707
- const codecPrivateData = qualityLevel.getAttribute('CodecPrivateData');
730
+ const codecPrivateData = qualityLevel.attributes['CodecPrivateData'];
708
731
  if (codecPrivateData) {
709
732
  return codecPrivateData;
710
733
  }
@@ -782,7 +805,7 @@ shaka.mss.MssParser = class {
782
805
  }
783
806
 
784
807
  /**
785
- * @param {!Element} qualityLevel
808
+ * @param {!shaka.extern.xml.Node} qualityLevel
786
809
  * @param {string} fourCCValue
787
810
  * @param {?string} codecPrivateData
788
811
  * @return {string}
@@ -811,7 +834,7 @@ shaka.mss.MssParser = class {
811
834
  }
812
835
 
813
836
  /**
814
- * @param {!Element} qualityLevel
837
+ * @param {!shaka.extern.xml.Node} qualityLevel
815
838
  * @param {?string} codecPrivateData
816
839
  * @return {string}
817
840
  * @private
@@ -837,14 +860,14 @@ shaka.mss.MssParser = class {
837
860
  /**
838
861
  * @param {!shaka.media.InitSegmentReference} initSegmentRef
839
862
  * @param {!shaka.extern.Stream} stream
840
- * @param {!Element} streamIndex
863
+ * @param {!shaka.extern.xml.Node} streamIndex
841
864
  * @param {!Array.<shaka.mss.MssParser.TimeRange>} timeline
842
865
  * @return {!Array.<!shaka.media.SegmentReference>}
843
866
  * @private
844
867
  */
845
868
  createSegments_(initSegmentRef, stream, streamIndex, timeline) {
846
869
  const ManifestParserUtils = shaka.util.ManifestParserUtils;
847
- const url = streamIndex.getAttribute('Url');
870
+ const url = streamIndex.attributes['Url'];
848
871
  goog.asserts.assert(url, 'Missing URL for segments');
849
872
 
850
873
  const mediaUrl = url.replace('{bitrate}', String(stream.bandwidth));
@@ -873,7 +896,7 @@ shaka.mss.MssParser = class {
873
896
  * Expands a streamIndex into an array-based timeline. The results are in
874
897
  * seconds.
875
898
  *
876
- * @param {!Element} streamIndex
899
+ * @param {!shaka.extern.xml.Node} streamIndex
877
900
  * @param {number} timescale
878
901
  * @param {number} duration The duration in seconds.
879
902
  * @return {!Array.<shaka.mss.MssParser.TimeRange>}
@@ -886,9 +909,9 @@ shaka.mss.MssParser = class {
886
909
  goog.asserts.assert(
887
910
  duration > 0, 'duration must be a positive integer');
888
911
 
889
- const XmlUtils = shaka.util.XmlUtils;
912
+ const TXml = shaka.util.TXml;
890
913
 
891
- const timePoints = XmlUtils.findChildren(streamIndex, 'c');
914
+ const timePoints = TXml.findChildren(streamIndex, 'c');
892
915
 
893
916
  /** @type {!Array.<shaka.mss.MssParser.TimeRange>} */
894
917
  const timeline = [];
@@ -898,10 +921,10 @@ shaka.mss.MssParser = class {
898
921
  const timePoint = timePoints[i];
899
922
  const next = timePoints[i + 1];
900
923
  const t =
901
- XmlUtils.parseAttr(timePoint, 't', XmlUtils.parseNonNegativeInt);
924
+ TXml.parseAttr(timePoint, 't', TXml.parseNonNegativeInt);
902
925
  const d =
903
- XmlUtils.parseAttr(timePoint, 'd', XmlUtils.parseNonNegativeInt);
904
- const r = XmlUtils.parseAttr(timePoint, 'r', XmlUtils.parseInt);
926
+ TXml.parseAttr(timePoint, 'd', TXml.parseNonNegativeInt);
927
+ const r = TXml.parseAttr(timePoint, 'r', TXml.parseInt);
905
928
 
906
929
  if (!d) {
907
930
  shaka.log.warning(
@@ -916,7 +939,7 @@ shaka.mss.MssParser = class {
916
939
  if (repeat < 0) {
917
940
  if (next) {
918
941
  const nextStartTime =
919
- XmlUtils.parseAttr(next, 't', XmlUtils.parseNonNegativeInt);
942
+ TXml.parseAttr(next, 't', TXml.parseNonNegativeInt);
920
943
  if (nextStartTime == null) {
921
944
  shaka.log.warning(
922
945
  'An "c" element cannot have a negative repeat',
@@ -46,7 +46,8 @@ goog.require('shaka.util.Timer');
46
46
  */
47
47
  shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
48
48
  /**
49
- * @param {function(number, number, boolean)=} onProgressUpdated Called when
49
+ * @param {shaka.net.NetworkingEngine.onProgressUpdated=} onProgressUpdated
50
+ * Called when
50
51
  * a progress event is triggered. Passed the duration, in milliseconds,
51
52
  * that the request took, the number of bytes transferred, and the boolean
52
53
  * of whether the switching is allowed.
@@ -58,9 +59,11 @@ shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
58
59
  * Called when a request is made
59
60
  * @param {shaka.net.NetworkingEngine.OnRetry=} onRetry
60
61
  * Called when a request retry is made
62
+ * @param {shaka.net.NetworkingEngine.OnResponse=} onResponse
63
+ * Called when receive the response
61
64
  */
62
65
  constructor(onProgressUpdated, onHeadersReceived, onDownloadFailed,
63
- onRequest, onRetry) {
66
+ onRequest, onRetry, onResponse) {
64
67
  super();
65
68
 
66
69
  /** @private {boolean} */
@@ -75,7 +78,7 @@ shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
75
78
  /** @private {!Set.<shaka.extern.ResponseFilter>} */
76
79
  this.responseFilters_ = new Set();
77
80
 
78
- /** @private {?function(number, number, boolean)} */
81
+ /** @private {?shaka.net.NetworkingEngine.onProgressUpdated} */
79
82
  this.onProgressUpdated_ = onProgressUpdated || null;
80
83
 
81
84
  /** @private {?shaka.net.NetworkingEngine.OnHeadersReceived} */
@@ -90,6 +93,9 @@ shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
90
93
  /** @private {?shaka.net.NetworkingEngine.OnRetry} */
91
94
  this.onRetry_ = onRetry || null;
92
95
 
96
+ /** @private {?shaka.net.NetworkingEngine.OnResponse} */
97
+ this.onResponse_ = onResponse || null;
98
+
93
99
  /** @private {boolean} */
94
100
  this.forceHTTPS_ = false;
95
101
  }
@@ -333,6 +339,9 @@ shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
333
339
  this.onProgressUpdated_(
334
340
  response.timeMs, response.data.byteLength, allowSwitch);
335
341
  }
342
+ if (this.onResponse_) {
343
+ this.onResponse_(type, response, context);
344
+ }
336
345
  return response;
337
346
  }, (e) => {
338
347
  // Any error thrown from elsewhere should be recategorized as CRITICAL
@@ -510,6 +519,7 @@ shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
510
519
 
511
520
  startTimeMs = Date.now();
512
521
  const segment = shaka.net.NetworkingEngine.RequestType.SEGMENT;
522
+ let packetNumber = 0;
513
523
 
514
524
  const progressUpdated = (time, bytes, numBytesRemaining) => {
515
525
  if (connectionTimer) {
@@ -519,8 +529,10 @@ shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
519
529
  stallTimer.tickAfter(stallTimeoutMs / 1000);
520
530
  }
521
531
  if (this.onProgressUpdated_ && type == segment) {
532
+ packetNumber++;
533
+ request.packetNumber = packetNumber;
522
534
  const allowSwitch = this.allowSwitch_(context);
523
- this.onProgressUpdated_(time, bytes, allowSwitch);
535
+ this.onProgressUpdated_(time, bytes, allowSwitch, request);
524
536
  gotProgress = true;
525
537
  numBytesRemainingObj.setBytes(numBytesRemaining);
526
538
  }
@@ -530,7 +542,11 @@ shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
530
542
  this.onHeadersReceived_(headers, request, type);
531
543
  }
532
544
  headersReceivedCalled = true;
545
+ request.timeToFirstByte = Date.now() -
546
+ /** @type {number} */ (request.requestStartTime);
533
547
  };
548
+ request.requestStartTime = Date.now();
549
+
534
550
  const requestPlugin = plugin(
535
551
  request.uris[index], request, type, progressUpdated, headersReceived);
536
552
 
@@ -899,6 +915,21 @@ shaka.net.NetworkingEngine.ResponseAndGotProgress;
899
915
  */
900
916
  shaka.net.NetworkingEngine.OnHeadersReceived;
901
917
 
918
+ /**
919
+ * @typedef {function(
920
+ * number,
921
+ * number,
922
+ * boolean,
923
+ * shaka.extern.Request=)}
924
+ *
925
+ * @description
926
+ * A callback that is passed the duration, in milliseconds,
927
+ * that the request took, the number of bytes transferred, a boolean
928
+ * representing whether the switching is allowed and a ref to the
929
+ * original request.
930
+ * @export
931
+ */
932
+ shaka.net.NetworkingEngine.onProgressUpdated;
902
933
 
903
934
  /**
904
935
  * @typedef {function(
@@ -941,3 +972,17 @@ shaka.net.NetworkingEngine.OnRequest;
941
972
  * @export
942
973
  */
943
974
  shaka.net.NetworkingEngine.OnRetry;
975
+
976
+
977
+ /**
978
+ * @typedef {function(
979
+ * !shaka.net.NetworkingEngine.RequestType,
980
+ * !shaka.extern.Response,
981
+ * (shaka.extern.RequestContext|undefined))}
982
+ *
983
+ * @description
984
+ * A callback function called on every request
985
+ * @export
986
+ */
987
+ shaka.net.NetworkingEngine.OnResponse;
988
+
@@ -211,6 +211,8 @@ shaka.offline.indexeddb.V1StorageCell = class
211
211
  appendWindowEnd,
212
212
  timestampOffset,
213
213
  tilesLayout: '',
214
+ mimeType: null,
215
+ codecs: null,
214
216
  };
215
217
  }
216
218
 
@@ -155,6 +155,8 @@ shaka.offline.indexeddb.V2StorageCell = class
155
155
  timestampOffset,
156
156
  dataKey: old.dataKey,
157
157
  tilesLayout: '',
158
+ mimeType: null,
159
+ codecs: null,
158
160
  };
159
161
  }
160
162
  };