shaka-player 4.7.13 → 4.8.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 (114) hide show
  1. package/AUTHORS +2 -0
  2. package/CONTRIBUTORS +2 -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 +34 -23
  8. package/dist/locales.js +41 -31
  9. package/dist/shaka-player.compiled.d.ts +196 -45
  10. package/dist/shaka-player.compiled.debug.d.ts +196 -45
  11. package/dist/shaka-player.compiled.debug.externs.js +491 -348
  12. package/dist/shaka-player.compiled.debug.js +969 -847
  13. package/dist/shaka-player.compiled.debug.map +4 -4
  14. package/dist/shaka-player.compiled.externs.js +491 -348
  15. package/dist/shaka-player.compiled.js +1238 -1171
  16. package/dist/shaka-player.compiled.map +4 -4
  17. package/dist/shaka-player.ui.d.ts +259 -48
  18. package/dist/shaka-player.ui.debug.d.ts +259 -48
  19. package/dist/shaka-player.ui.debug.externs.js +598 -377
  20. package/dist/shaka-player.ui.debug.js +1185 -980
  21. package/dist/shaka-player.ui.debug.map +4 -4
  22. package/dist/shaka-player.ui.externs.js +598 -377
  23. package/dist/shaka-player.ui.js +1526 -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 +58 -6
  32. package/externs/shaka/net.js +13 -1
  33. package/externs/shaka/player.js +184 -13
  34. package/externs/shaka/text.js +19 -0
  35. package/externs/tizen.js +41 -0
  36. package/externs/webos.js +19 -0
  37. package/externs/xbox.js +43 -0
  38. package/lib/abr/simple_abr_manager.js +9 -1
  39. package/lib/ads/ad_manager.js +19 -2
  40. package/lib/ads/client_side_ad_manager.js +5 -3
  41. package/lib/ads/media_tailor_ad_manager.js +9 -0
  42. package/lib/cast/cast_receiver.js +0 -14
  43. package/lib/cast/cast_utils.js +1 -0
  44. package/lib/cea/mp4_cea_parser.js +6 -1
  45. package/lib/dash/content_protection.js +97 -53
  46. package/lib/dash/dash_parser.js +237 -177
  47. package/lib/dash/mpd_utils.js +72 -65
  48. package/lib/dash/segment_base.js +27 -21
  49. package/lib/dash/segment_list.js +18 -16
  50. package/lib/dash/segment_template.js +45 -22
  51. package/lib/hls/hls_parser.js +358 -164
  52. package/lib/media/buffering_observer.js +25 -1
  53. package/lib/media/drm_engine.js +91 -71
  54. package/lib/media/manifest_filterer.js +220 -0
  55. package/lib/media/media_source_engine.js +34 -5
  56. package/lib/media/preload_manager.js +827 -0
  57. package/lib/media/presentation_timeline.js +11 -0
  58. package/lib/media/segment_index.js +93 -47
  59. package/lib/media/segment_prefetch.js +140 -43
  60. package/lib/media/segment_reference.js +43 -8
  61. package/lib/media/segment_utils.js +208 -25
  62. package/lib/media/streaming_engine.js +355 -66
  63. package/lib/mss/content_protection.js +31 -25
  64. package/lib/mss/mss_parser.js +79 -55
  65. package/lib/net/networking_engine.js +49 -4
  66. package/lib/offline/storage.js +3 -3
  67. package/lib/player.js +882 -672
  68. package/lib/polyfill/media_capabilities.js +16 -1
  69. package/lib/text/simple_text_displayer.js +9 -0
  70. package/lib/text/stub_text_displayer.js +7 -0
  71. package/lib/text/text_engine.js +16 -3
  72. package/lib/text/ttml_text_parser.js +116 -114
  73. package/lib/text/ui_text_displayer.js +28 -11
  74. package/lib/text/vtt_text_parser.js +54 -115
  75. package/lib/transmuxer/opus.js +91 -0
  76. package/lib/transmuxer/ts_transmuxer.js +115 -0
  77. package/lib/util/cmcd_manager.js +153 -3
  78. package/lib/util/dom_utils.js +11 -0
  79. package/lib/util/error.js +5 -0
  80. package/lib/util/fake_event.js +2 -0
  81. package/lib/util/manifest_parser_utils.js +97 -3
  82. package/lib/util/mp4_box_parsers.js +346 -17
  83. package/lib/util/mp4_generator.js +23 -0
  84. package/lib/util/periods.js +19 -0
  85. package/lib/util/platform.js +161 -9
  86. package/lib/util/player_configuration.js +79 -13
  87. package/lib/util/stats.js +16 -0
  88. package/lib/util/stream_utils.js +22 -8
  89. package/lib/util/string_utils.js +36 -0
  90. package/lib/util/tXml.js +814 -0
  91. package/lib/util/ts_parser.js +50 -0
  92. package/lib/util/xml_utils.js +10 -338
  93. package/package.json +2 -2
  94. package/shaka-player.uncompiled.js +8 -0
  95. package/ui/controls.js +187 -5
  96. package/ui/enums.js +2 -0
  97. package/ui/externs/ui.js +30 -3
  98. package/ui/gl_matrix/matrix_4x4.js +568 -0
  99. package/ui/gl_matrix/matrix_quaternion.js +45 -0
  100. package/ui/less/containers.less +11 -0
  101. package/ui/locales/en-GB.json +2 -0
  102. package/ui/locales/en.json +2 -0
  103. package/ui/locales/es-419.json +2 -0
  104. package/ui/locales/es.json +2 -0
  105. package/ui/locales/source.json +8 -0
  106. package/ui/recenter_vr.js +126 -0
  107. package/ui/resolution_selection.js +25 -5
  108. package/ui/statistics_button.js +12 -0
  109. package/ui/text_selection.js +1 -1
  110. package/ui/toggle_stereoscopic.js +127 -0
  111. package/ui/ui.js +74 -38
  112. package/ui/vr_manager.js +557 -0
  113. package/ui/vr_utils.js +232 -0
  114. package/ui/vr_webgl.js +697 -0
@@ -7,6 +7,7 @@
7
7
  goog.provide('shaka.dash.DashParser');
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.dash.ContentProtection');
12
13
  goog.require('shaka.dash.MpdUtils');
@@ -29,8 +30,10 @@ goog.require('shaka.util.MimeUtils');
29
30
  goog.require('shaka.util.Networking');
30
31
  goog.require('shaka.util.OperationManager');
31
32
  goog.require('shaka.util.PeriodCombiner');
33
+ goog.require('shaka.util.PlayerConfiguration');
32
34
  goog.require('shaka.util.StringUtils');
33
35
  goog.require('shaka.util.Timer');
36
+ goog.require('shaka.util.TXml');
34
37
  goog.require('shaka.util.XmlUtils');
35
38
 
36
39
 
@@ -129,9 +132,14 @@ shaka.dash.DashParser = class {
129
132
  configure(config) {
130
133
  goog.asserts.assert(config.dash != null,
131
134
  'DashManifestConfiguration should not be null!');
132
-
135
+ const needFireUpdate = this.playerInterface_ &&
136
+ config.dash.updatePeriod != this.config_.dash.updatePeriod &&
137
+ config.dash.updatePeriod >= 0;
133
138
  this.config_ = config;
134
-
139
+ if (needFireUpdate && this.manifest_ &&
140
+ this.manifest_.presentationTimeline.isLive()) {
141
+ this.updateNow_();
142
+ }
135
143
  if (this.contentSteeringManager_) {
136
144
  this.contentSteeringManager_.configure(this.config_);
137
145
  }
@@ -140,6 +148,8 @@ shaka.dash.DashParser = class {
140
148
  this.periodCombiner_.setAllowMultiTypeVariants(
141
149
  this.config_.dash.multiTypeVariantsAllowed &&
142
150
  shaka.media.Capabilities.isChangeTypeSupported());
151
+ this.periodCombiner_.setUseStreamOnce(
152
+ this.config_.dash.useStreamOnceInPeriodFlattening);
143
153
  }
144
154
  }
145
155
 
@@ -327,15 +337,42 @@ shaka.dash.DashParser = class {
327
337
  * @private
328
338
  */
329
339
  async parseManifest_(data, finalManifestUri) {
330
- const Error = shaka.util.Error;
331
- const MpdUtils = shaka.dash.MpdUtils;
332
-
333
- const mpd = shaka.util.XmlUtils.parseXml(data, 'MPD');
340
+ let manifestData = data;
341
+ const manifestPreprocessor = this.config_.dash.manifestPreprocessor;
342
+ const defaultManifestPreprocessor =
343
+ shaka.util.PlayerConfiguration.defaultManifestPreprocessor;
344
+ if (manifestPreprocessor != defaultManifestPreprocessor) {
345
+ shaka.Deprecate.deprecateFeature(5,
346
+ 'manifest.dash.manifestPreprocessor configuration',
347
+ 'Please Use manifest.dash.manifestPreprocessorTXml instead.');
348
+ const mpdElement =
349
+ shaka.util.XmlUtils.parseXml(manifestData, 'MPD');
350
+ if (!mpdElement) {
351
+ throw new shaka.util.Error(
352
+ shaka.util.Error.Severity.CRITICAL,
353
+ shaka.util.Error.Category.MANIFEST,
354
+ shaka.util.Error.Code.DASH_INVALID_XML,
355
+ finalManifestUri);
356
+ }
357
+ manifestPreprocessor(mpdElement);
358
+ manifestData = shaka.util.XmlUtils.toArrayBuffer(mpdElement);
359
+ }
360
+ const mpd = shaka.util.TXml.parseXml(manifestData, 'MPD');
334
361
  if (!mpd) {
335
- throw new Error(
336
- Error.Severity.CRITICAL, Error.Category.MANIFEST,
337
- Error.Code.DASH_INVALID_XML, finalManifestUri);
362
+ throw new shaka.util.Error(
363
+ shaka.util.Error.Severity.CRITICAL,
364
+ shaka.util.Error.Category.MANIFEST,
365
+ shaka.util.Error.Code.DASH_INVALID_XML,
366
+ finalManifestUri);
338
367
  }
368
+ const manifestPreprocessorTXml =
369
+ this.config_.dash.manifestPreprocessorTXml;
370
+ const defaultManifestPreprocessorTXml =
371
+ shaka.util.PlayerConfiguration.defaultManifestPreprocessorTXml;
372
+ if (manifestPreprocessorTXml != defaultManifestPreprocessorTXml) {
373
+ manifestPreprocessorTXml(mpd);
374
+ }
375
+
339
376
  const disableXlinkProcessing = this.config_.dash.disableXlinkProcessing;
340
377
  if (disableXlinkProcessing) {
341
378
  return this.processManifest_(mpd, finalManifestUri);
@@ -343,7 +380,7 @@ shaka.dash.DashParser = class {
343
380
 
344
381
  // Process the mpd to account for xlink connections.
345
382
  const failGracefully = this.config_.dash.xlinkFailGracefully;
346
- const xlinkOperation = MpdUtils.processXlinks(
383
+ const xlinkOperation = shaka.dash.MpdUtils.processXlinks(
347
384
  mpd, this.config_.retryParameters, failGracefully, finalManifestUri,
348
385
  this.playerInterface_.networkingEngine);
349
386
  this.operationManager_.manage(xlinkOperation);
@@ -355,19 +392,17 @@ shaka.dash.DashParser = class {
355
392
  /**
356
393
  * Takes a formatted MPD and converts it into a manifest.
357
394
  *
358
- * @param {!Element} mpd
395
+ * @param {!shaka.extern.xml.Node} mpd
359
396
  * @param {string} finalManifestUri The final manifest URI, which may
360
397
  * differ from this.manifestUri_ if there has been a redirect.
361
398
  * @return {!Promise}
362
399
  * @private
363
400
  */
364
401
  async processManifest_(mpd, finalManifestUri) {
365
- const XmlUtils = shaka.util.XmlUtils;
402
+ const TXml = shaka.util.TXml;
366
403
 
367
- const manifestPreprocessor = this.config_.dash.manifestPreprocessor;
368
- if (manifestPreprocessor) {
369
- manifestPreprocessor(mpd);
370
- }
404
+ goog.asserts.assert(this.config_,
405
+ 'Must call configure() before processManifest_()!');
371
406
 
372
407
  if (this.contentSteeringManager_) {
373
408
  this.contentSteeringManager_.clearPreviousLocations();
@@ -381,10 +416,10 @@ shaka.dash.DashParser = class {
381
416
  const locations = [];
382
417
  /** @type {!Map.<string, string>} */
383
418
  const locationsMapping = new Map();
384
- const locationsObjs = XmlUtils.findChildren(mpd, 'Location');
419
+ const locationsObjs = TXml.findChildren(mpd, 'Location');
385
420
  for (const locationsObj of locationsObjs) {
386
- const serviceLocation = locationsObj.getAttribute('serviceLocation');
387
- const uri = XmlUtils.getContents(locationsObj);
421
+ const serviceLocation = locationsObj.attributes['serviceLocation'];
422
+ const uri = TXml.getContents(locationsObj);
388
423
  if (!uri) {
389
424
  continue;
390
425
  }
@@ -414,10 +449,10 @@ shaka.dash.DashParser = class {
414
449
 
415
450
  let contentSteeringPromise = Promise.resolve();
416
451
 
417
- const contentSteering = XmlUtils.findChild(mpd, 'ContentSteering');
452
+ const contentSteering = TXml.findChild(mpd, 'ContentSteering');
418
453
  if (contentSteering && this.playerInterface_) {
419
454
  const defaultPathwayId =
420
- contentSteering.getAttribute('defaultServiceLocation');
455
+ contentSteering.attributes['defaultServiceLocation'];
421
456
  if (!this.contentSteeringManager_) {
422
457
  this.contentSteeringManager_ =
423
458
  new shaka.util.ContentSteeringManager(this.playerInterface_);
@@ -426,11 +461,11 @@ shaka.dash.DashParser = class {
426
461
  shaka.media.ManifestParser.DASH);
427
462
  this.contentSteeringManager_.setBaseUris(manifestBaseUris);
428
463
  this.contentSteeringManager_.setDefaultPathwayId(defaultPathwayId);
429
- const uri = XmlUtils.getContents(contentSteering);
464
+ const uri = TXml.getContents(contentSteering);
430
465
  if (uri) {
431
466
  const queryBeforeStart =
432
- XmlUtils.parseAttr(contentSteering, 'queryBeforeStart',
433
- XmlUtils.parseBoolean, /* defaultValue= */ false);
467
+ TXml.parseAttr(contentSteering, 'queryBeforeStart',
468
+ TXml.parseBoolean, /* defaultValue= */ false);
434
469
  if (queryBeforeStart) {
435
470
  contentSteeringPromise =
436
471
  this.contentSteeringManager_.requestInfo(uri);
@@ -449,13 +484,13 @@ shaka.dash.DashParser = class {
449
484
  }
450
485
  }
451
486
 
452
- const uriObjs = XmlUtils.findChildren(mpd, 'BaseURL');
487
+ const uriObjs = TXml.findChildren(mpd, 'BaseURL');
453
488
  let calculatedBaseUris;
454
489
  let someLocationValid = false;
455
490
  if (this.contentSteeringManager_) {
456
491
  for (const uriObj of uriObjs) {
457
- const serviceLocation = uriObj.getAttribute('serviceLocation');
458
- const uri = XmlUtils.getContents(uriObj);
492
+ const serviceLocation = uriObj.attributes['serviceLocation'];
493
+ const uri = TXml.getContents(uriObj);
459
494
  if (serviceLocation && uri) {
460
495
  this.contentSteeringManager_.addLocation(
461
496
  'BaseURL', serviceLocation, uri);
@@ -464,7 +499,7 @@ shaka.dash.DashParser = class {
464
499
  }
465
500
  }
466
501
  if (!someLocationValid || !this.contentSteeringManager_) {
467
- const uris = uriObjs.map(XmlUtils.getContents);
502
+ const uris = uriObjs.map(TXml.getContents);
468
503
  calculatedBaseUris = shaka.util.ManifestParserUtils.resolveUris(
469
504
  manifestBaseUris, uris);
470
505
  }
@@ -481,41 +516,41 @@ shaka.dash.DashParser = class {
481
516
 
482
517
  let availabilityTimeOffset = 0;
483
518
  if (uriObjs && uriObjs.length) {
484
- availabilityTimeOffset = XmlUtils.parseAttr(
485
- uriObjs[0], 'availabilityTimeOffset', XmlUtils.parseFloat) || 0;
519
+ availabilityTimeOffset = TXml.parseAttr(uriObjs[0],
520
+ 'availabilityTimeOffset', TXml.parseFloat) || 0;
486
521
  }
487
522
 
488
523
  const ignoreMinBufferTime = this.config_.dash.ignoreMinBufferTime;
489
524
  let minBufferTime = 0;
490
525
  if (!ignoreMinBufferTime) {
491
526
  minBufferTime =
492
- XmlUtils.parseAttr(mpd, 'minBufferTime', XmlUtils.parseDuration) || 0;
527
+ TXml.parseAttr(mpd, 'minBufferTime', TXml.parseDuration) || 0;
493
528
  }
494
529
 
495
- this.updatePeriod_ = /** @type {number} */ (XmlUtils.parseAttr(
496
- mpd, 'minimumUpdatePeriod', XmlUtils.parseDuration, -1));
530
+ this.updatePeriod_ = /** @type {number} */ (TXml.parseAttr(
531
+ mpd, 'minimumUpdatePeriod', TXml.parseDuration, -1));
497
532
 
498
- const presentationStartTime = XmlUtils.parseAttr(
499
- mpd, 'availabilityStartTime', XmlUtils.parseDate);
500
- let segmentAvailabilityDuration = XmlUtils.parseAttr(
501
- mpd, 'timeShiftBufferDepth', XmlUtils.parseDuration);
533
+ const presentationStartTime = TXml.parseAttr(
534
+ mpd, 'availabilityStartTime', TXml.parseDate);
535
+ let segmentAvailabilityDuration = TXml.parseAttr(
536
+ mpd, 'timeShiftBufferDepth', TXml.parseDuration);
502
537
 
503
538
  const ignoreSuggestedPresentationDelay =
504
539
  this.config_.dash.ignoreSuggestedPresentationDelay;
505
540
  let suggestedPresentationDelay = null;
506
541
  if (!ignoreSuggestedPresentationDelay) {
507
- suggestedPresentationDelay = XmlUtils.parseAttr(
508
- mpd, 'suggestedPresentationDelay', XmlUtils.parseDuration);
542
+ suggestedPresentationDelay = TXml.parseAttr(
543
+ mpd, 'suggestedPresentationDelay', TXml.parseDuration);
509
544
  }
510
545
 
511
546
  const ignoreMaxSegmentDuration =
512
547
  this.config_.dash.ignoreMaxSegmentDuration;
513
548
  let maxSegmentDuration = null;
514
549
  if (!ignoreMaxSegmentDuration) {
515
- maxSegmentDuration = XmlUtils.parseAttr(
516
- mpd, 'maxSegmentDuration', XmlUtils.parseDuration);
550
+ maxSegmentDuration = TXml.parseAttr(
551
+ mpd, 'maxSegmentDuration', TXml.parseDuration);
517
552
  }
518
- const mpdType = mpd.getAttribute('type') || 'static';
553
+ const mpdType = mpd.attributes['type'] || 'static';
519
554
 
520
555
  /** @type {!shaka.media.PresentationTimeline} */
521
556
  let presentationTimeline;
@@ -575,7 +610,7 @@ shaka.dash.DashParser = class {
575
610
  presentationTimeline.setSegmentAvailabilityDuration(
576
611
  segmentAvailabilityDuration);
577
612
 
578
- const profiles = mpd.getAttribute('profiles') || '';
613
+ const profiles = mpd.attributes['profiles'] || '';
579
614
 
580
615
  /** @type {shaka.dash.DashParser.Context} */
581
616
  const context = {
@@ -666,8 +701,8 @@ shaka.dash.DashParser = class {
666
701
  // We only need to do clock sync when we're using presentation start
667
702
  // time. This condition also excludes VOD streams.
668
703
  if (presentationTimeline.usingPresentationStartTime()) {
669
- const XmlUtils = shaka.util.XmlUtils;
670
- const timingElements = XmlUtils.findChildren(mpd, 'UTCTiming');
704
+ const TXml = shaka.util.TXml;
705
+ const timingElements = TXml.findChildren(mpd, 'UTCTiming');
671
706
  const offset = await this.parseUtcTiming_(getBaseUris, timingElements);
672
707
  // Detect calls to stop().
673
708
  if (!this.playerInterface_) {
@@ -706,33 +741,33 @@ shaka.dash.DashParser = class {
706
741
  * Reads maxLatency and maxPlaybackRate properties from service
707
742
  * description element.
708
743
  *
709
- * @param {!Element} mpd
744
+ * @param {!shaka.extern.xml.Node} mpd
710
745
  * @return {?shaka.extern.ServiceDescription}
711
746
  * @private
712
747
  */
713
748
  parseServiceDescription_(mpd) {
714
- const XmlUtils = shaka.util.XmlUtils;
715
- const elem = XmlUtils.findChild(mpd, 'ServiceDescription');
749
+ const TXml = shaka.util.TXml;
750
+ const elem = TXml.findChild(mpd, 'ServiceDescription');
716
751
 
717
752
  if (!elem ) {
718
753
  return null;
719
754
  }
720
755
 
721
- const latencyNode = XmlUtils.findChild(elem, 'Latency');
722
- const playbackRateNode = XmlUtils.findChild(elem, 'PlaybackRate');
756
+ const latencyNode = TXml.findChild(elem, 'Latency');
757
+ const playbackRateNode = TXml.findChild(elem, 'PlaybackRate');
723
758
 
724
- if ((latencyNode && latencyNode.getAttribute('max')) || playbackRateNode) {
725
- const maxLatency = latencyNode && latencyNode.getAttribute('max') ?
726
- parseInt(latencyNode.getAttribute('max'), 10) / 1000 :
759
+ if ((latencyNode && latencyNode.attributes['max']) || playbackRateNode) {
760
+ const maxLatency = latencyNode && latencyNode.attributes['max'] ?
761
+ parseInt(latencyNode.attributes['max'], 10) / 1000 :
727
762
  null;
728
763
  const maxPlaybackRate = playbackRateNode ?
729
- parseFloat(playbackRateNode.getAttribute('max')) :
764
+ parseFloat(playbackRateNode.attributes['max']) :
730
765
  null;
731
- const minLatency = latencyNode && latencyNode.getAttribute('min') ?
732
- parseInt(latencyNode.getAttribute('min'), 10) / 1000 :
766
+ const minLatency = latencyNode && latencyNode.attributes['min'] ?
767
+ parseInt(latencyNode.attributes['min'], 10) / 1000 :
733
768
  null;
734
769
  const minPlaybackRate = playbackRateNode ?
735
- parseFloat(playbackRateNode.getAttribute('min')) :
770
+ parseFloat(playbackRateNode.attributes['min']) :
736
771
  null;
737
772
 
738
773
  return {
@@ -753,7 +788,7 @@ shaka.dash.DashParser = class {
753
788
  *
754
789
  * @param {shaka.dash.DashParser.Context} context
755
790
  * @param {function():!Array.<string>} getBaseUris
756
- * @param {!Element} mpd
791
+ * @param {!shaka.extern.xml.Node} mpd
757
792
  * @return {{
758
793
  * periods: !Array.<shaka.extern.Period>,
759
794
  * duration: ?number,
@@ -762,21 +797,21 @@ shaka.dash.DashParser = class {
762
797
  * @private
763
798
  */
764
799
  parsePeriods_(context, getBaseUris, mpd) {
765
- const XmlUtils = shaka.util.XmlUtils;
766
- const presentationDuration = XmlUtils.parseAttr(
767
- mpd, 'mediaPresentationDuration', XmlUtils.parseDuration);
800
+ const TXml = shaka.util.TXml;
801
+ const presentationDuration = TXml.parseAttr(
802
+ mpd, 'mediaPresentationDuration', TXml.parseDuration);
768
803
 
769
804
  const periods = [];
770
805
  let prevEnd = 0;
771
- const periodNodes = XmlUtils.findChildren(mpd, 'Period');
806
+ const periodNodes = TXml.findChildren(mpd, 'Period');
772
807
  for (let i = 0; i < periodNodes.length; i++) {
773
808
  const elem = periodNodes[i];
774
809
  const next = periodNodes[i + 1];
775
810
  const start = /** @type {number} */ (
776
- XmlUtils.parseAttr(elem, 'start', XmlUtils.parseDuration, prevEnd));
777
- const periodId = elem.id;
811
+ TXml.parseAttr(elem, 'start', TXml.parseDuration, prevEnd));
812
+ const periodId = elem.attributes['id'];
778
813
  const givenDuration =
779
- XmlUtils.parseAttr(elem, 'duration', XmlUtils.parseDuration);
814
+ TXml.parseAttr(elem, 'duration', TXml.parseDuration);
780
815
 
781
816
  let periodDuration = null;
782
817
  if (next) {
@@ -784,7 +819,7 @@ shaka.dash.DashParser = class {
784
819
  // of the following Period is the duration of the media content
785
820
  // represented by this Period."
786
821
  const nextStart =
787
- XmlUtils.parseAttr(next, 'start', XmlUtils.parseDuration);
822
+ TXml.parseAttr(next, 'start', TXml.parseDuration);
788
823
  if (nextStart != null) {
789
824
  periodDuration = nextStart - start;
790
825
  }
@@ -912,7 +947,7 @@ shaka.dash.DashParser = class {
912
947
  */
913
948
  parsePeriod_(context, getBaseUris, periodInfo) {
914
949
  const Functional = shaka.util.Functional;
915
- const XmlUtils = shaka.util.XmlUtils;
950
+ const TXml = shaka.util.TXml;
916
951
  const ContentType = shaka.util.ManifestParserUtils.ContentType;
917
952
 
918
953
  context.period = this.createFrame_(periodInfo.node, null, getBaseUris);
@@ -928,7 +963,7 @@ shaka.dash.DashParser = class {
928
963
  }
929
964
 
930
965
  const eventStreamNodes =
931
- XmlUtils.findChildren(periodInfo.node, 'EventStream');
966
+ TXml.findChildren(periodInfo.node, 'EventStream');
932
967
  const availabilityStart =
933
968
  context.presentationTimeline.getSegmentAvailabilityStart();
934
969
 
@@ -938,7 +973,7 @@ shaka.dash.DashParser = class {
938
973
  }
939
974
 
940
975
  const adaptationSetNodes =
941
- XmlUtils.findChildren(periodInfo.node, 'AdaptationSet');
976
+ TXml.findChildren(periodInfo.node, 'AdaptationSet');
942
977
  const adaptationSets = adaptationSetNodes
943
978
  .map((node) => this.parseAdaptationSet_(context, node))
944
979
  .filter(Functional.isNotNull);
@@ -1047,12 +1082,12 @@ shaka.dash.DashParser = class {
1047
1082
  * Parses an AdaptationSet XML element.
1048
1083
  *
1049
1084
  * @param {shaka.dash.DashParser.Context} context
1050
- * @param {!Element} elem The AdaptationSet element.
1085
+ * @param {!shaka.extern.xml.Node} elem The AdaptationSet element.
1051
1086
  * @return {?shaka.dash.DashParser.AdaptationInfo}
1052
1087
  * @private
1053
1088
  */
1054
1089
  parseAdaptationSet_(context, elem) {
1055
- const XmlUtils = shaka.util.XmlUtils;
1090
+ const TXml = shaka.util.TXml;
1056
1091
  const Functional = shaka.util.Functional;
1057
1092
  const ManifestParserUtils = shaka.util.ManifestParserUtils;
1058
1093
  const ContentType = ManifestParserUtils.ContentType;
@@ -1061,9 +1096,9 @@ shaka.dash.DashParser = class {
1061
1096
  context.adaptationSet = this.createFrame_(elem, context.period, null);
1062
1097
 
1063
1098
  let main = false;
1064
- const roleElements = XmlUtils.findChildren(elem, 'Role');
1099
+ const roleElements = TXml.findChildren(elem, 'Role');
1065
1100
  const roleValues = roleElements.map((role) => {
1066
- return role.getAttribute('value');
1101
+ return role.attributes['value'];
1067
1102
  }).filter(Functional.isNotNull);
1068
1103
 
1069
1104
  // Default kind for text streams is 'subtitle' if unspecified in the
@@ -1075,12 +1110,12 @@ shaka.dash.DashParser = class {
1075
1110
  }
1076
1111
 
1077
1112
  for (const roleElement of roleElements) {
1078
- const scheme = roleElement.getAttribute('schemeIdUri');
1113
+ const scheme = roleElement.attributes['schemeIdUri'];
1079
1114
  if (scheme == null || scheme == 'urn:mpeg:dash:role:2011') {
1080
1115
  // These only apply for the given scheme, but allow them to be specified
1081
1116
  // if there is no scheme specified.
1082
1117
  // See: DASH section 5.8.5.5
1083
- const value = roleElement.getAttribute('value');
1118
+ const value = roleElement.attributes['value'];
1084
1119
  switch (value) {
1085
1120
  case 'main':
1086
1121
  main = true;
@@ -1125,18 +1160,18 @@ shaka.dash.DashParser = class {
1125
1160
  };
1126
1161
 
1127
1162
  const essentialProperties =
1128
- XmlUtils.findChildren(elem, 'EssentialProperty');
1163
+ TXml.findChildren(elem, 'EssentialProperty');
1129
1164
  // ID of real AdaptationSet if this is a trick mode set:
1130
1165
  let trickModeFor = null;
1131
1166
  let isFastSwitching = false;
1132
1167
  let unrecognizedEssentialProperty = false;
1133
1168
  for (const prop of essentialProperties) {
1134
- const schemeId = prop.getAttribute('schemeIdUri');
1169
+ const schemeId = prop.attributes['schemeIdUri'];
1135
1170
  if (schemeId == 'http://dashif.org/guidelines/trickmode') {
1136
- trickModeFor = prop.getAttribute('value');
1171
+ trickModeFor = prop.attributes['value'];
1137
1172
  } else if (schemeId == transferCharacteristicsScheme) {
1138
1173
  videoRange = getVideoRangeFromTransferCharacteristicCICP(
1139
- parseInt(prop.getAttribute('value'), 10),
1174
+ parseInt(prop.attributes['value'], 10),
1140
1175
  );
1141
1176
  } else if (schemeId == colourPrimariesScheme ||
1142
1177
  schemeId == matrixCoefficientsScheme) {
@@ -1148,25 +1183,29 @@ shaka.dash.DashParser = class {
1148
1183
  }
1149
1184
  }
1150
1185
 
1186
+ let lastSegmentNumber = null;
1187
+
1151
1188
  const supplementalProperties =
1152
- XmlUtils.findChildren(elem, 'SupplementalProperty');
1189
+ TXml.findChildren(elem, 'SupplementalProperty');
1153
1190
  for (const prop of supplementalProperties) {
1154
- const schemeId = prop.getAttribute('schemeIdUri');
1155
- if (schemeId == transferCharacteristicsScheme) {
1191
+ const schemeId = prop.attributes['schemeIdUri'];
1192
+ if (schemeId == 'http://dashif.org/guidelines/last-segment-number') {
1193
+ lastSegmentNumber = parseInt(prop.attributes['value'], 10) - 1;
1194
+ } else if (schemeId == transferCharacteristicsScheme) {
1156
1195
  videoRange = getVideoRangeFromTransferCharacteristicCICP(
1157
- parseInt(prop.getAttribute('value'), 10),
1196
+ parseInt(prop.attributes['value'], 10),
1158
1197
  );
1159
1198
  }
1160
1199
  }
1161
1200
 
1162
- const accessibilities = XmlUtils.findChildren(elem, 'Accessibility');
1201
+ const accessibilities = TXml.findChildren(elem, 'Accessibility');
1163
1202
  const LanguageUtils = shaka.util.LanguageUtils;
1164
1203
  const closedCaptions = new Map();
1165
1204
  /** @type {?shaka.media.ManifestParser.AccessibilityPurpose} */
1166
1205
  let accessibilityPurpose;
1167
1206
  for (const prop of accessibilities) {
1168
- const schemeId = prop.getAttribute('schemeIdUri');
1169
- const value = prop.getAttribute('value');
1207
+ const schemeId = prop.attributes['schemeIdUri'];
1208
+ const value = prop.attributes['value'];
1170
1209
  if (schemeId == 'urn:scte:dash:cc:cea-608:2015' ) {
1171
1210
  let channelId = 1;
1172
1211
  if (value != null) {
@@ -1270,7 +1309,7 @@ shaka.dash.DashParser = class {
1270
1309
  }
1271
1310
 
1272
1311
  const contentProtectionElems =
1273
- XmlUtils.findChildren(elem, 'ContentProtection');
1312
+ TXml.findChildren(elem, 'ContentProtection');
1274
1313
  const contentProtection = ContentProtection.parseFromAdaptationSet(
1275
1314
  contentProtectionElems,
1276
1315
  this.config_.dash.ignoreDrmInfo,
@@ -1280,24 +1319,26 @@ shaka.dash.DashParser = class {
1280
1319
  context.adaptationSet.language || 'und');
1281
1320
 
1282
1321
  // This attribute is currently non-standard, but it is supported by Kaltura.
1283
- let label = elem.getAttribute('label');
1322
+ let label = elem.attributes['label'];
1284
1323
 
1285
1324
  // See DASH IOP 4.3 here https://dashif.org/docs/DASH-IF-IOP-v4.3.pdf (page 35)
1286
- const labelElements = XmlUtils.findChildren(elem, 'Label');
1325
+ const labelElements = TXml.findChildren(elem, 'Label');
1287
1326
  if (labelElements && labelElements.length) {
1288
1327
  // NOTE: Right now only one label field is supported.
1289
1328
  const firstLabelElement = labelElements[0];
1290
- if (firstLabelElement.textContent) {
1291
- label = firstLabelElement.textContent;
1329
+ const textContent = shaka.util.TXml.getTextContents(firstLabelElement);
1330
+ if (textContent) {
1331
+ label = textContent;
1292
1332
  }
1293
1333
  }
1294
1334
 
1295
1335
  // Parse Representations into Streams.
1296
- const representations = XmlUtils.findChildren(elem, 'Representation');
1336
+ const representations = TXml.findChildren(elem, 'Representation');
1297
1337
  const streams = representations.map((representation) => {
1298
1338
  const parsedRepresentation = this.parseRepresentation_(context,
1299
1339
  contentProtection, kind, language, label, main, roleValues,
1300
- closedCaptions, representation, accessibilityPurpose);
1340
+ closedCaptions, representation, accessibilityPurpose,
1341
+ lastSegmentNumber);
1301
1342
  if (parsedRepresentation) {
1302
1343
  parsedRepresentation.hdr = parsedRepresentation.hdr || videoRange;
1303
1344
  parsedRepresentation.fastSwitching = isFastSwitching;
@@ -1353,7 +1394,7 @@ shaka.dash.DashParser = class {
1353
1394
  }
1354
1395
 
1355
1396
  const repIds = representations
1356
- .map((node) => { return node.getAttribute('id'); })
1397
+ .map((node) => { return node.attributes['id']; })
1357
1398
  .filter(shaka.util.Functional.isNotNull);
1358
1399
 
1359
1400
  return {
@@ -1379,17 +1420,19 @@ shaka.dash.DashParser = class {
1379
1420
  * @param {boolean} isPrimary
1380
1421
  * @param {!Array.<string>} roles
1381
1422
  * @param {Map.<string, string>} closedCaptions
1382
- * @param {!Element} node
1423
+ * @param {!shaka.extern.xml.Node} node
1383
1424
  * @param {?shaka.media.ManifestParser.AccessibilityPurpose}
1384
1425
  * accessibilityPurpose
1426
+ * @param {?number} lastSegmentNumber
1385
1427
  *
1386
1428
  * @return {?shaka.extern.Stream} The Stream, or null when there is a
1387
1429
  * non-critical parsing error.
1388
1430
  * @private
1389
1431
  */
1390
1432
  parseRepresentation_(context, contentProtection, kind, language, label,
1391
- isPrimary, roles, closedCaptions, node, accessibilityPurpose) {
1392
- const XmlUtils = shaka.util.XmlUtils;
1433
+ isPrimary, roles, closedCaptions, node, accessibilityPurpose,
1434
+ lastSegmentNumber) {
1435
+ const TXml = shaka.util.TXml;
1393
1436
  const ContentType = shaka.util.ManifestParserUtils.ContentType;
1394
1437
 
1395
1438
  context.representation =
@@ -1411,7 +1454,7 @@ shaka.dash.DashParser = class {
1411
1454
  // To avoid NaN at the variant level on broken content, fall back to zero.
1412
1455
  // https://github.com/shaka-project/shaka-player/issues/938#issuecomment-317278180
1413
1456
  context.bandwidth =
1414
- XmlUtils.parseAttr(node, 'bandwidth', XmlUtils.parsePositiveInt) || 0;
1457
+ TXml.parseAttr(node, 'bandwidth', TXml.parsePositiveInt) || 0;
1415
1458
 
1416
1459
  /** @type {?shaka.dash.DashParser.StreamInfo} */
1417
1460
  let streamInfo;
@@ -1422,8 +1465,8 @@ shaka.dash.DashParser = class {
1422
1465
  const isImage = contentType == ContentType.IMAGE;
1423
1466
 
1424
1467
  try {
1425
- /** @type {shaka.extern.aes128Key|undefined} */
1426
- let aes128Key = undefined;
1468
+ /** @type {shaka.extern.aesKey|undefined} */
1469
+ let aesKey = undefined;
1427
1470
  if (contentProtection.aes128Info) {
1428
1471
  const getBaseUris = context.representation.getBaseUris;
1429
1472
  const uris = shaka.util.ManifestParserUtils.resolveUris(
@@ -1432,15 +1475,16 @@ shaka.dash.DashParser = class {
1432
1475
  const request = shaka.net.NetworkingEngine.makeRequest(
1433
1476
  uris, this.config_.retryParameters);
1434
1477
 
1435
- aes128Key = {
1436
- method: 'AES-128',
1478
+ aesKey = {
1479
+ bitsKey: 128,
1480
+ blockCipherMode: 'CBC',
1437
1481
  iv: contentProtection.aes128Info.iv,
1438
1482
  firstMediaSequenceNumber: 0,
1439
1483
  };
1440
1484
 
1441
1485
  // Don't download the key object until the segment is parsed, to
1442
1486
  // avoid a startup delay for long manifests with lots of keys.
1443
- aes128Key.fetchKey = async () => {
1487
+ aesKey.fetchKey = async () => {
1444
1488
  const keyResponse =
1445
1489
  await this.makeNetworkRequest_(request, requestType);
1446
1490
 
@@ -1456,9 +1500,9 @@ shaka.dash.DashParser = class {
1456
1500
  const algorithm = {
1457
1501
  name: 'AES-CBC',
1458
1502
  };
1459
- aes128Key.cryptoKey = await window.crypto.subtle.importKey(
1503
+ aesKey.cryptoKey = await window.crypto.subtle.importKey(
1460
1504
  'raw', keyResponse.data, algorithm, true, ['decrypt']);
1461
- aes128Key.fetchKey = undefined; // No longer needed.
1505
+ aesKey.fetchKey = undefined; // No longer needed.
1462
1506
  };
1463
1507
  }
1464
1508
  const requestSegment = (uris, startByte, endByte, isInit) => {
@@ -1466,17 +1510,17 @@ shaka.dash.DashParser = class {
1466
1510
  };
1467
1511
  if (context.representation.segmentBase) {
1468
1512
  streamInfo = shaka.dash.SegmentBase.createStreamInfo(
1469
- context, requestSegment, aes128Key);
1513
+ context, requestSegment, aesKey);
1470
1514
  } else if (context.representation.segmentList) {
1471
1515
  streamInfo = shaka.dash.SegmentList.createStreamInfo(
1472
- context, this.streamMap_, aes128Key);
1516
+ context, this.streamMap_, aesKey);
1473
1517
  } else if (context.representation.segmentTemplate) {
1474
1518
  const hasManifest = !!this.manifest_;
1475
1519
 
1476
1520
  streamInfo = shaka.dash.SegmentTemplate.createStreamInfo(
1477
1521
  context, requestSegment, this.streamMap_, hasManifest,
1478
1522
  this.config_.dash.initialSegmentLimit, this.periodDurations_,
1479
- aes128Key);
1523
+ aesKey, lastSegmentNumber);
1480
1524
  } else {
1481
1525
  goog.asserts.assert(isText,
1482
1526
  'Must have Segment* with non-text streams.');
@@ -1508,7 +1552,7 @@ shaka.dash.DashParser = class {
1508
1552
  }
1509
1553
 
1510
1554
  const contentProtectionElems =
1511
- XmlUtils.findChildren(node, 'ContentProtection');
1555
+ TXml.findChildren(node, 'ContentProtection');
1512
1556
  const keyId = shaka.dash.ContentProtection.parseFromRepresentation(
1513
1557
  contentProtectionElems, contentProtection,
1514
1558
  this.config_.dash.ignoreDrmInfo,
@@ -1518,12 +1562,12 @@ shaka.dash.DashParser = class {
1518
1562
  // Detect the presence of E-AC3 JOC audio content, using DD+JOC signaling.
1519
1563
  // See: ETSI TS 103 420 V1.2.1 (2018-10)
1520
1564
  const supplementalPropertyElems =
1521
- XmlUtils.findChildren(node, 'SupplementalProperty');
1565
+ TXml.findChildren(node, 'SupplementalProperty');
1522
1566
  const hasJoc = supplementalPropertyElems.some((element) => {
1523
1567
  const expectedUri = 'tag:dolby.com,2018:dash:EC3_ExtensionType:2018';
1524
1568
  const expectedValue = 'JOC';
1525
- return element.getAttribute('schemeIdUri') == expectedUri &&
1526
- element.getAttribute('value') == expectedValue;
1569
+ return element.attributes['schemeIdUri'] == expectedUri &&
1570
+ element.attributes['value'] == expectedValue;
1527
1571
  });
1528
1572
  let spatialAudio = false;
1529
1573
  if (hasJoc) {
@@ -1541,16 +1585,16 @@ shaka.dash.DashParser = class {
1541
1585
  let tilesLayout;
1542
1586
  if (isImage) {
1543
1587
  const essentialPropertyElems =
1544
- XmlUtils.findChildren(node, 'EssentialProperty');
1588
+ TXml.findChildren(node, 'EssentialProperty');
1545
1589
  const thumbnailTileElem = essentialPropertyElems.find((element) => {
1546
1590
  const expectedUris = [
1547
1591
  'http://dashif.org/thumbnail_tile',
1548
1592
  'http://dashif.org/guidelines/thumbnail_tile',
1549
1593
  ];
1550
- return expectedUris.includes(element.getAttribute('schemeIdUri'));
1594
+ return expectedUris.includes(element.attributes['schemeIdUri']);
1551
1595
  });
1552
1596
  if (thumbnailTileElem) {
1553
- tilesLayout = thumbnailTileElem.getAttribute('value');
1597
+ tilesLayout = thumbnailTileElem.attributes['value'];
1554
1598
  }
1555
1599
  // Filter image adaptation sets that has no tilesLayout.
1556
1600
  if (!tilesLayout) {
@@ -1682,6 +1726,15 @@ shaka.dash.DashParser = class {
1682
1726
  this.setUpdateTimer_(updateDelay);
1683
1727
  }
1684
1728
 
1729
+ /**
1730
+ * Update now the manifest
1731
+ *
1732
+ * @private
1733
+ */
1734
+ updateNow_() {
1735
+ this.updateTimer_.tickNow();
1736
+ }
1737
+
1685
1738
  /**
1686
1739
  * Sets the update timer. Does nothing if the manifest does not specify an
1687
1740
  * update period.
@@ -1698,9 +1751,13 @@ shaka.dash.DashParser = class {
1698
1751
  if (this.updatePeriod_ < 0) {
1699
1752
  return;
1700
1753
  }
1754
+ let updateTime = this.updatePeriod_;
1755
+ if (this.config_.dash.updatePeriod >= 0) {
1756
+ updateTime = this.config_.dash.updatePeriod;
1757
+ }
1701
1758
 
1702
1759
  const finalDelay = Math.max(
1703
- this.updatePeriod_ - offset,
1760
+ updateTime - offset,
1704
1761
  this.averageUpdateDuration_.getEstimate());
1705
1762
 
1706
1763
  // We do not run the timer as repeating because part of update is async and
@@ -1711,7 +1768,7 @@ shaka.dash.DashParser = class {
1711
1768
  /**
1712
1769
  * Creates a new inheritance frame for the given element.
1713
1770
  *
1714
- * @param {!Element} elem
1771
+ * @param {!shaka.extern.xml.Node} elem
1715
1772
  * @param {?shaka.dash.DashParser.InheritanceFrame} parent
1716
1773
  * @param {?function():!Array.<string>} getBaseUris
1717
1774
  * @return {shaka.dash.DashParser.InheritanceFrame}
@@ -1721,7 +1778,7 @@ shaka.dash.DashParser = class {
1721
1778
  goog.asserts.assert(parent || getBaseUris,
1722
1779
  'Must provide either parent or getBaseUris');
1723
1780
  const ManifestParserUtils = shaka.util.ManifestParserUtils;
1724
- const XmlUtils = shaka.util.XmlUtils;
1781
+ const TXml = shaka.util.TXml;
1725
1782
  parent = parent || /** @type {shaka.dash.DashParser.InheritanceFrame} */ ({
1726
1783
  contentType: '',
1727
1784
  mimeType: '',
@@ -1736,17 +1793,17 @@ shaka.dash.DashParser = class {
1736
1793
  });
1737
1794
  getBaseUris = getBaseUris || parent.getBaseUris;
1738
1795
 
1739
- const parseNumber = XmlUtils.parseNonNegativeInt;
1740
- const evalDivision = XmlUtils.evalDivision;
1796
+ const parseNumber = TXml.parseNonNegativeInt;
1797
+ const evalDivision = TXml.evalDivision;
1741
1798
 
1742
- const id = elem.getAttribute('id');
1743
- const uriObjs = XmlUtils.findChildren(elem, 'BaseURL');
1799
+ const id = elem.attributes['id'];
1800
+ const uriObjs = TXml.findChildren(elem, 'BaseURL');
1744
1801
  let calculatedBaseUris;
1745
1802
  let someLocationValid = false;
1746
1803
  if (this.contentSteeringManager_) {
1747
1804
  for (const uriObj of uriObjs) {
1748
- const serviceLocation = uriObj.getAttribute('serviceLocation');
1749
- const uri = XmlUtils.getContents(uriObj);
1805
+ const serviceLocation = uriObj.attributes['serviceLocation'];
1806
+ const uri = TXml.getContents(uriObj);
1750
1807
  if (serviceLocation && uri) {
1751
1808
  this.contentSteeringManager_.addLocation(
1752
1809
  id, serviceLocation, uri);
@@ -1755,7 +1812,7 @@ shaka.dash.DashParser = class {
1755
1812
  }
1756
1813
  }
1757
1814
  if (!someLocationValid || !this.contentSteeringManager_) {
1758
- calculatedBaseUris = uriObjs.map(XmlUtils.getContents);
1815
+ calculatedBaseUris = uriObjs.map(TXml.getContents);
1759
1816
  }
1760
1817
 
1761
1818
  const getFrameUris = () => {
@@ -1771,55 +1828,55 @@ shaka.dash.DashParser = class {
1771
1828
  return [];
1772
1829
  };
1773
1830
 
1774
- let contentType = elem.getAttribute('contentType') || parent.contentType;
1775
- const mimeType = elem.getAttribute('mimeType') || parent.mimeType;
1776
- const codecs = elem.getAttribute('codecs') || parent.codecs;
1831
+ let contentType = elem.attributes['contentType'] || parent.contentType;
1832
+ const mimeType = elem.attributes['mimeType'] || parent.mimeType;
1833
+ const codecs = elem.attributes['codecs'] || parent.codecs;
1777
1834
  const frameRate =
1778
- XmlUtils.parseAttr(elem, 'frameRate', evalDivision) || parent.frameRate;
1835
+ TXml.parseAttr(elem, 'frameRate', evalDivision) || parent.frameRate;
1779
1836
  const pixelAspectRatio =
1780
- elem.getAttribute('sar') || parent.pixelAspectRatio;
1837
+ elem.attributes['sar'] || parent.pixelAspectRatio;
1781
1838
  const emsgSchemeIdUris = this.emsgSchemeIdUris_(
1782
- XmlUtils.findChildren(elem, 'InbandEventStream'),
1839
+ TXml.findChildren(elem, 'InbandEventStream'),
1783
1840
  parent.emsgSchemeIdUris);
1784
1841
  const audioChannelConfigs =
1785
- XmlUtils.findChildren(elem, 'AudioChannelConfiguration');
1842
+ TXml.findChildren(elem, 'AudioChannelConfiguration');
1786
1843
  const numChannels =
1787
1844
  this.parseAudioChannels_(audioChannelConfigs) || parent.numChannels;
1788
1845
  const audioSamplingRate =
1789
- XmlUtils.parseAttr(elem, 'audioSamplingRate', parseNumber) ||
1846
+ TXml.parseAttr(elem, 'audioSamplingRate', parseNumber) ||
1790
1847
  parent.audioSamplingRate;
1791
1848
 
1792
1849
  if (!contentType) {
1793
1850
  contentType = shaka.dash.DashParser.guessContentType_(mimeType, codecs);
1794
1851
  }
1795
1852
 
1796
- const segmentBase = XmlUtils.findChild(elem, 'SegmentBase');
1797
- const segmentTemplate = XmlUtils.findChild(elem, 'SegmentTemplate');
1853
+ const segmentBase = TXml.findChild(elem, 'SegmentBase');
1854
+ const segmentTemplate = TXml.findChild(elem, 'SegmentTemplate');
1798
1855
 
1799
1856
  // The availabilityTimeOffset is the sum of all @availabilityTimeOffset
1800
1857
  // values that apply to the adaptation set, via BaseURL, SegmentBase,
1801
1858
  // or SegmentTemplate elements.
1802
1859
  const segmentBaseAto = segmentBase ?
1803
- (XmlUtils.parseAttr(segmentBase, 'availabilityTimeOffset',
1804
- XmlUtils.parseFloat) || 0) : 0;
1860
+ (TXml.parseAttr(segmentBase, 'availabilityTimeOffset',
1861
+ TXml.parseFloat) || 0) : 0;
1805
1862
  const segmentTemplateAto = segmentTemplate ?
1806
- (XmlUtils.parseAttr(segmentTemplate, 'availabilityTimeOffset',
1807
- XmlUtils.parseFloat) || 0) : 0;
1863
+ (TXml.parseAttr(segmentTemplate, 'availabilityTimeOffset',
1864
+ TXml.parseFloat) || 0) : 0;
1808
1865
  const baseUriAto = uriObjs && uriObjs.length ?
1809
- (XmlUtils.parseAttr(uriObjs[0], 'availabilityTimeOffset',
1810
- XmlUtils.parseFloat) || 0) : 0;
1866
+ (TXml.parseAttr(uriObjs[0], 'availabilityTimeOffset',
1867
+ TXml.parseFloat) || 0) : 0;
1811
1868
 
1812
1869
  const availabilityTimeOffset = parent.availabilityTimeOffset + baseUriAto +
1813
1870
  segmentBaseAto + segmentTemplateAto;
1814
1871
 
1815
1872
  let segmentSequenceCadence = null;
1816
1873
  const segmentSequenceProperties =
1817
- XmlUtils.findChild(elem, 'SegmentSequenceProperties');
1874
+ TXml.findChild(elem, 'SegmentSequenceProperties');
1818
1875
  if (segmentSequenceProperties) {
1819
- const sap = XmlUtils.findChild(segmentSequenceProperties, 'SAP');
1876
+ const sap = TXml.findChild(segmentSequenceProperties, 'SAP');
1820
1877
  if (sap) {
1821
- segmentSequenceCadence = XmlUtils.parseAttr(sap, 'cadence',
1822
- XmlUtils.parseInt);
1878
+ segmentSequenceCadence = TXml.parseAttr(sap, 'cadence',
1879
+ TXml.parseInt);
1823
1880
  }
1824
1881
  }
1825
1882
 
@@ -1828,10 +1885,10 @@ shaka.dash.DashParser = class {
1828
1885
  () => ManifestParserUtils.resolveUris(getBaseUris(), getFrameUris()),
1829
1886
  segmentBase: segmentBase || parent.segmentBase,
1830
1887
  segmentList:
1831
- XmlUtils.findChild(elem, 'SegmentList') || parent.segmentList,
1888
+ TXml.findChild(elem, 'SegmentList') || parent.segmentList,
1832
1889
  segmentTemplate: segmentTemplate || parent.segmentTemplate,
1833
- width: XmlUtils.parseAttr(elem, 'width', parseNumber) || parent.width,
1834
- height: XmlUtils.parseAttr(elem, 'height', parseNumber) || parent.height,
1890
+ width: TXml.parseAttr(elem, 'width', parseNumber) || parent.width,
1891
+ height: TXml.parseAttr(elem, 'height', parseNumber) || parent.height,
1835
1892
  contentType: contentType,
1836
1893
  mimeType: mimeType,
1837
1894
  codecs: codecs,
@@ -1839,7 +1896,7 @@ shaka.dash.DashParser = class {
1839
1896
  pixelAspectRatio: pixelAspectRatio,
1840
1897
  emsgSchemeIdUris: emsgSchemeIdUris,
1841
1898
  id: id,
1842
- language: elem.getAttribute('lang'),
1899
+ language: elem.attributes['lang'],
1843
1900
  numChannels: numChannels,
1844
1901
  audioSamplingRate: audioSamplingRate,
1845
1902
  availabilityTimeOffset: availabilityTimeOffset,
@@ -1853,7 +1910,8 @@ shaka.dash.DashParser = class {
1853
1910
  * of the ones parsed from inBandEventStreams and the ones provided in
1854
1911
  * emsgSchemeIdUris.
1855
1912
  *
1856
- * @param {!Array.<!Element>} inBandEventStreams Array of InbandEventStream
1913
+ * @param {!Array.<!shaka.extern.xml.Node>} inBandEventStreams
1914
+ * Array of InbandEventStream
1857
1915
  * elements to parse and add to the returned array.
1858
1916
  * @param {!Array.<string>} emsgSchemeIdUris Array of parsed
1859
1917
  * InbandEventStream schemeIdUri attributes to add to the returned array.
@@ -1864,7 +1922,7 @@ shaka.dash.DashParser = class {
1864
1922
  emsgSchemeIdUris_(inBandEventStreams, emsgSchemeIdUris) {
1865
1923
  const schemeIdUris = emsgSchemeIdUris.slice();
1866
1924
  for (const event of inBandEventStreams) {
1867
- const schemeIdUri = event.getAttribute('schemeIdUri');
1925
+ const schemeIdUri = event.attributes['schemeIdUri'];
1868
1926
  if (!schemeIdUris.includes(schemeIdUri)) {
1869
1927
  schemeIdUris.push(schemeIdUri);
1870
1928
  }
@@ -1873,19 +1931,19 @@ shaka.dash.DashParser = class {
1873
1931
  }
1874
1932
 
1875
1933
  /**
1876
- * @param {!Array.<!Element>} audioChannelConfigs An array of
1934
+ * @param {!Array.<!shaka.extern.xml.Node>} audioChannelConfigs An array of
1877
1935
  * AudioChannelConfiguration elements.
1878
1936
  * @return {?number} The number of audio channels, or null if unknown.
1879
1937
  * @private
1880
1938
  */
1881
1939
  parseAudioChannels_(audioChannelConfigs) {
1882
1940
  for (const elem of audioChannelConfigs) {
1883
- const scheme = elem.getAttribute('schemeIdUri');
1941
+ const scheme = elem.attributes['schemeIdUri'];
1884
1942
  if (!scheme) {
1885
1943
  continue;
1886
1944
  }
1887
1945
 
1888
- const value = elem.getAttribute('value');
1946
+ const value = elem.attributes['value'];
1889
1947
  if (!value) {
1890
1948
  continue;
1891
1949
  }
@@ -2021,8 +2079,9 @@ shaka.dash.DashParser = class {
2021
2079
  * @private
2022
2080
  */
2023
2081
  async requestForTiming_(getBaseUris, uri, method) {
2082
+ const uris = [shaka.util.StringUtils.htmlUnescape(uri)];
2024
2083
  const requestUris =
2025
- shaka.util.ManifestParserUtils.resolveUris(getBaseUris(), [uri]);
2084
+ shaka.util.ManifestParserUtils.resolveUris(getBaseUris(), uris);
2026
2085
  const request = shaka.net.NetworkingEngine.makeRequest(
2027
2086
  requestUris, this.config_.retryParameters);
2028
2087
  request.method = method;
@@ -2056,15 +2115,15 @@ shaka.dash.DashParser = class {
2056
2115
  * Parses an array of UTCTiming elements.
2057
2116
  *
2058
2117
  * @param {function():!Array.<string>} getBaseUris
2059
- * @param {!Array.<!Element>} elems
2118
+ * @param {!Array.<!shaka.extern.xml.Node>} elems
2060
2119
  * @return {!Promise.<number>}
2061
2120
  * @private
2062
2121
  */
2063
2122
  async parseUtcTiming_(getBaseUris, elems) {
2064
2123
  const schemesAndValues = elems.map((elem) => {
2065
2124
  return {
2066
- scheme: elem.getAttribute('schemeIdUri'),
2067
- value: elem.getAttribute('value'),
2125
+ scheme: elem.attributes['schemeIdUri'],
2126
+ value: elem.attributes['value'],
2068
2127
  };
2069
2128
  });
2070
2129
 
@@ -2128,23 +2187,23 @@ shaka.dash.DashParser = class {
2128
2187
  *
2129
2188
  * @param {number} periodStart
2130
2189
  * @param {?number} periodDuration
2131
- * @param {!Element} elem
2190
+ * @param {!shaka.extern.xml.Node} elem
2132
2191
  * @param {number} availabilityStart
2133
2192
  * @private
2134
2193
  */
2135
2194
  parseEventStream_(periodStart, periodDuration, elem, availabilityStart) {
2136
- const XmlUtils = shaka.util.XmlUtils;
2137
- const parseNumber = XmlUtils.parseNonNegativeInt;
2195
+ const TXml = shaka.util.TXml;
2196
+ const parseNumber = shaka.util.TXml.parseNonNegativeInt;
2138
2197
 
2139
- const schemeIdUri = elem.getAttribute('schemeIdUri') || '';
2140
- const value = elem.getAttribute('value') || '';
2141
- const timescale = XmlUtils.parseAttr(elem, 'timescale', parseNumber) || 1;
2198
+ const schemeIdUri = elem.attributes['schemeIdUri'] || '';
2199
+ const value = elem.attributes['value'] || '';
2200
+ const timescale = TXml.parseAttr(elem, 'timescale', parseNumber) || 1;
2142
2201
 
2143
- for (const eventNode of XmlUtils.findChildren(elem, 'Event')) {
2202
+ for (const eventNode of TXml.findChildren(elem, 'Event')) {
2144
2203
  const presentationTime =
2145
- XmlUtils.parseAttr(eventNode, 'presentationTime', parseNumber) || 0;
2204
+ TXml.parseAttr(eventNode, 'presentationTime', parseNumber) || 0;
2146
2205
  const duration =
2147
- XmlUtils.parseAttr(eventNode, 'duration', parseNumber) || 0;
2206
+ TXml.parseAttr(eventNode, 'duration', parseNumber) || 0;
2148
2207
 
2149
2208
  let startTime = presentationTime / timescale + periodStart;
2150
2209
  let endTime = startTime + (duration / timescale);
@@ -2166,8 +2225,9 @@ shaka.dash.DashParser = class {
2166
2225
  value: value,
2167
2226
  startTime: startTime,
2168
2227
  endTime: endTime,
2169
- id: eventNode.getAttribute('id') || '',
2170
- eventElement: eventNode,
2228
+ id: eventNode.attributes['id'] || '',
2229
+ eventElement: TXml.txmlNodeToDomElement(eventNode),
2230
+ eventNode: eventNode,
2171
2231
  };
2172
2232
 
2173
2233
  this.playerInterface_.onTimelineRegionAdded(region);
@@ -2255,9 +2315,9 @@ shaka.dash.DashParser.RequestSegmentCallback;
2255
2315
 
2256
2316
  /**
2257
2317
  * @typedef {{
2258
- * segmentBase: Element,
2259
- * segmentList: Element,
2260
- * segmentTemplate: Element,
2318
+ * segmentBase: ?shaka.extern.xml.Node,
2319
+ * segmentList: ?shaka.extern.xml.Node,
2320
+ * segmentTemplate: ?shaka.extern.xml.Node,
2261
2321
  * getBaseUris: function():!Array.<string>,
2262
2322
  * width: (number|undefined),
2263
2323
  * height: (number|undefined),
@@ -2279,11 +2339,11 @@ shaka.dash.DashParser.RequestSegmentCallback;
2279
2339
  * A collection of elements and properties which are inherited across levels
2280
2340
  * of a DASH manifest.
2281
2341
  *
2282
- * @property {Element} segmentBase
2342
+ * @property {?shaka.extern.xml.Node} segmentBase
2283
2343
  * The XML node for SegmentBase.
2284
- * @property {Element} segmentList
2344
+ * @property {?shaka.extern.xml.Node} segmentList
2285
2345
  * The XML node for SegmentList.
2286
- * @property {Element} segmentTemplate
2346
+ * @property {?shaka.extern.xml.Node} segmentTemplate
2287
2347
  * The XML node for SegmentTemplate.
2288
2348
  * @property {function():!Array.<string>} getBaseUris
2289
2349
  * Function than returns an array of absolute base URIs for the frame.
@@ -2368,7 +2428,7 @@ shaka.dash.DashParser.Context;
2368
2428
  * @typedef {{
2369
2429
  * start: number,
2370
2430
  * duration: ?number,
2371
- * node: !Element,
2431
+ * node: !shaka.extern.xml.Node,
2372
2432
  * isLastPeriod: boolean
2373
2433
  * }}
2374
2434
  *
@@ -2380,7 +2440,7 @@ shaka.dash.DashParser.Context;
2380
2440
  * @property {?number} duration
2381
2441
  * The duration of the period; or null if the duration is not given. This
2382
2442
  * will be non-null for all periods except the last.
2383
- * @property {!Element} node
2443
+ * @property {!shaka.extern.xml.Node} node
2384
2444
  * The XML Node for the Period.
2385
2445
  * @property {boolean} isLastPeriod
2386
2446
  * Whether this Period is the last one in the manifest.