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
@@ -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');
@@ -31,6 +32,7 @@ goog.require('shaka.util.OperationManager');
31
32
  goog.require('shaka.util.PeriodCombiner');
32
33
  goog.require('shaka.util.StringUtils');
33
34
  goog.require('shaka.util.Timer');
35
+ goog.require('shaka.util.TXml');
34
36
  goog.require('shaka.util.XmlUtils');
35
37
 
36
38
 
@@ -129,9 +131,14 @@ shaka.dash.DashParser = class {
129
131
  configure(config) {
130
132
  goog.asserts.assert(config.dash != null,
131
133
  'DashManifestConfiguration should not be null!');
132
-
134
+ const needFireUpdate = this.playerInterface_ &&
135
+ config.dash.updatePeriod != this.config_.dash.updatePeriod &&
136
+ config.dash.updatePeriod >= 0;
133
137
  this.config_ = config;
134
-
138
+ if (needFireUpdate && this.manifest_ &&
139
+ this.manifest_.presentationTimeline.isLive()) {
140
+ this.updateNow_();
141
+ }
135
142
  if (this.contentSteeringManager_) {
136
143
  this.contentSteeringManager_.configure(this.config_);
137
144
  }
@@ -140,6 +147,8 @@ shaka.dash.DashParser = class {
140
147
  this.periodCombiner_.setAllowMultiTypeVariants(
141
148
  this.config_.dash.multiTypeVariantsAllowed &&
142
149
  shaka.media.Capabilities.isChangeTypeSupported());
150
+ this.periodCombiner_.setUseStreamOnce(
151
+ this.config_.dash.useStreamOnceInPeriodFlattening);
143
152
  }
144
153
  }
145
154
 
@@ -327,15 +336,38 @@ shaka.dash.DashParser = class {
327
336
  * @private
328
337
  */
329
338
  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');
339
+ let manifestData = data;
340
+ const manifestPreprocessor = this.config_.dash.manifestPreprocessor;
341
+ if (manifestPreprocessor) {
342
+ shaka.Deprecate.deprecateFeature(5,
343
+ 'manifest.dash.manifestPreprocessor configuration',
344
+ 'Please Use manifest.dash.manifestPreprocessorTXml instead.');
345
+ const mpdElement =
346
+ shaka.util.XmlUtils.parseXml(manifestData, 'MPD');
347
+ if (!mpdElement) {
348
+ throw new shaka.util.Error(
349
+ shaka.util.Error.Severity.CRITICAL,
350
+ shaka.util.Error.Category.MANIFEST,
351
+ shaka.util.Error.Code.DASH_INVALID_XML,
352
+ finalManifestUri);
353
+ }
354
+ manifestPreprocessor(mpdElement);
355
+ manifestData = shaka.util.XmlUtils.toArrayBuffer(mpdElement);
356
+ }
357
+ const mpd = shaka.util.TXml.parseXml(manifestData, 'MPD');
334
358
  if (!mpd) {
335
- throw new Error(
336
- Error.Severity.CRITICAL, Error.Category.MANIFEST,
337
- Error.Code.DASH_INVALID_XML, finalManifestUri);
359
+ throw new shaka.util.Error(
360
+ shaka.util.Error.Severity.CRITICAL,
361
+ shaka.util.Error.Category.MANIFEST,
362
+ shaka.util.Error.Code.DASH_INVALID_XML,
363
+ finalManifestUri);
338
364
  }
365
+ const manifestPreprocessorTXml =
366
+ this.config_.dash.manifestPreprocessorTXml;
367
+ if (manifestPreprocessorTXml) {
368
+ manifestPreprocessorTXml(mpd);
369
+ }
370
+
339
371
  const disableXlinkProcessing = this.config_.dash.disableXlinkProcessing;
340
372
  if (disableXlinkProcessing) {
341
373
  return this.processManifest_(mpd, finalManifestUri);
@@ -343,7 +375,7 @@ shaka.dash.DashParser = class {
343
375
 
344
376
  // Process the mpd to account for xlink connections.
345
377
  const failGracefully = this.config_.dash.xlinkFailGracefully;
346
- const xlinkOperation = MpdUtils.processXlinks(
378
+ const xlinkOperation = shaka.dash.MpdUtils.processXlinks(
347
379
  mpd, this.config_.retryParameters, failGracefully, finalManifestUri,
348
380
  this.playerInterface_.networkingEngine);
349
381
  this.operationManager_.manage(xlinkOperation);
@@ -355,19 +387,17 @@ shaka.dash.DashParser = class {
355
387
  /**
356
388
  * Takes a formatted MPD and converts it into a manifest.
357
389
  *
358
- * @param {!Element} mpd
390
+ * @param {!shaka.extern.xml.Node} mpd
359
391
  * @param {string} finalManifestUri The final manifest URI, which may
360
392
  * differ from this.manifestUri_ if there has been a redirect.
361
393
  * @return {!Promise}
362
394
  * @private
363
395
  */
364
396
  async processManifest_(mpd, finalManifestUri) {
365
- const XmlUtils = shaka.util.XmlUtils;
397
+ const TXml = shaka.util.TXml;
366
398
 
367
- const manifestPreprocessor = this.config_.dash.manifestPreprocessor;
368
- if (manifestPreprocessor) {
369
- manifestPreprocessor(mpd);
370
- }
399
+ goog.asserts.assert(this.config_,
400
+ 'Must call configure() before processManifest_()!');
371
401
 
372
402
  if (this.contentSteeringManager_) {
373
403
  this.contentSteeringManager_.clearPreviousLocations();
@@ -381,10 +411,10 @@ shaka.dash.DashParser = class {
381
411
  const locations = [];
382
412
  /** @type {!Map.<string, string>} */
383
413
  const locationsMapping = new Map();
384
- const locationsObjs = XmlUtils.findChildren(mpd, 'Location');
414
+ const locationsObjs = TXml.findChildren(mpd, 'Location');
385
415
  for (const locationsObj of locationsObjs) {
386
- const serviceLocation = locationsObj.getAttribute('serviceLocation');
387
- const uri = XmlUtils.getContents(locationsObj);
416
+ const serviceLocation = locationsObj.attributes['serviceLocation'];
417
+ const uri = TXml.getContents(locationsObj);
388
418
  if (!uri) {
389
419
  continue;
390
420
  }
@@ -414,10 +444,10 @@ shaka.dash.DashParser = class {
414
444
 
415
445
  let contentSteeringPromise = Promise.resolve();
416
446
 
417
- const contentSteering = XmlUtils.findChild(mpd, 'ContentSteering');
447
+ const contentSteering = TXml.findChild(mpd, 'ContentSteering');
418
448
  if (contentSteering && this.playerInterface_) {
419
449
  const defaultPathwayId =
420
- contentSteering.getAttribute('defaultServiceLocation');
450
+ contentSteering.attributes['defaultServiceLocation'];
421
451
  if (!this.contentSteeringManager_) {
422
452
  this.contentSteeringManager_ =
423
453
  new shaka.util.ContentSteeringManager(this.playerInterface_);
@@ -426,11 +456,11 @@ shaka.dash.DashParser = class {
426
456
  shaka.media.ManifestParser.DASH);
427
457
  this.contentSteeringManager_.setBaseUris(manifestBaseUris);
428
458
  this.contentSteeringManager_.setDefaultPathwayId(defaultPathwayId);
429
- const uri = XmlUtils.getContents(contentSteering);
459
+ const uri = TXml.getContents(contentSteering);
430
460
  if (uri) {
431
461
  const queryBeforeStart =
432
- XmlUtils.parseAttr(contentSteering, 'queryBeforeStart',
433
- XmlUtils.parseBoolean, /* defaultValue= */ false);
462
+ TXml.parseAttr(contentSteering, 'queryBeforeStart',
463
+ TXml.parseBoolean, /* defaultValue= */ false);
434
464
  if (queryBeforeStart) {
435
465
  contentSteeringPromise =
436
466
  this.contentSteeringManager_.requestInfo(uri);
@@ -449,13 +479,13 @@ shaka.dash.DashParser = class {
449
479
  }
450
480
  }
451
481
 
452
- const uriObjs = XmlUtils.findChildren(mpd, 'BaseURL');
482
+ const uriObjs = TXml.findChildren(mpd, 'BaseURL');
453
483
  let calculatedBaseUris;
454
484
  let someLocationValid = false;
455
485
  if (this.contentSteeringManager_) {
456
486
  for (const uriObj of uriObjs) {
457
- const serviceLocation = uriObj.getAttribute('serviceLocation');
458
- const uri = XmlUtils.getContents(uriObj);
487
+ const serviceLocation = uriObj.attributes['serviceLocation'];
488
+ const uri = TXml.getContents(uriObj);
459
489
  if (serviceLocation && uri) {
460
490
  this.contentSteeringManager_.addLocation(
461
491
  'BaseURL', serviceLocation, uri);
@@ -464,7 +494,7 @@ shaka.dash.DashParser = class {
464
494
  }
465
495
  }
466
496
  if (!someLocationValid || !this.contentSteeringManager_) {
467
- const uris = uriObjs.map(XmlUtils.getContents);
497
+ const uris = uriObjs.map(TXml.getContents);
468
498
  calculatedBaseUris = shaka.util.ManifestParserUtils.resolveUris(
469
499
  manifestBaseUris, uris);
470
500
  }
@@ -481,41 +511,41 @@ shaka.dash.DashParser = class {
481
511
 
482
512
  let availabilityTimeOffset = 0;
483
513
  if (uriObjs && uriObjs.length) {
484
- availabilityTimeOffset = XmlUtils.parseAttr(
485
- uriObjs[0], 'availabilityTimeOffset', XmlUtils.parseFloat) || 0;
514
+ availabilityTimeOffset = TXml.parseAttr(uriObjs[0],
515
+ 'availabilityTimeOffset', TXml.parseFloat) || 0;
486
516
  }
487
517
 
488
518
  const ignoreMinBufferTime = this.config_.dash.ignoreMinBufferTime;
489
519
  let minBufferTime = 0;
490
520
  if (!ignoreMinBufferTime) {
491
521
  minBufferTime =
492
- XmlUtils.parseAttr(mpd, 'minBufferTime', XmlUtils.parseDuration) || 0;
522
+ TXml.parseAttr(mpd, 'minBufferTime', TXml.parseDuration) || 0;
493
523
  }
494
524
 
495
- this.updatePeriod_ = /** @type {number} */ (XmlUtils.parseAttr(
496
- mpd, 'minimumUpdatePeriod', XmlUtils.parseDuration, -1));
525
+ this.updatePeriod_ = /** @type {number} */ (TXml.parseAttr(
526
+ mpd, 'minimumUpdatePeriod', TXml.parseDuration, -1));
497
527
 
498
- const presentationStartTime = XmlUtils.parseAttr(
499
- mpd, 'availabilityStartTime', XmlUtils.parseDate);
500
- let segmentAvailabilityDuration = XmlUtils.parseAttr(
501
- mpd, 'timeShiftBufferDepth', XmlUtils.parseDuration);
528
+ const presentationStartTime = TXml.parseAttr(
529
+ mpd, 'availabilityStartTime', TXml.parseDate);
530
+ let segmentAvailabilityDuration = TXml.parseAttr(
531
+ mpd, 'timeShiftBufferDepth', TXml.parseDuration);
502
532
 
503
533
  const ignoreSuggestedPresentationDelay =
504
534
  this.config_.dash.ignoreSuggestedPresentationDelay;
505
535
  let suggestedPresentationDelay = null;
506
536
  if (!ignoreSuggestedPresentationDelay) {
507
- suggestedPresentationDelay = XmlUtils.parseAttr(
508
- mpd, 'suggestedPresentationDelay', XmlUtils.parseDuration);
537
+ suggestedPresentationDelay = TXml.parseAttr(
538
+ mpd, 'suggestedPresentationDelay', TXml.parseDuration);
509
539
  }
510
540
 
511
541
  const ignoreMaxSegmentDuration =
512
542
  this.config_.dash.ignoreMaxSegmentDuration;
513
543
  let maxSegmentDuration = null;
514
544
  if (!ignoreMaxSegmentDuration) {
515
- maxSegmentDuration = XmlUtils.parseAttr(
516
- mpd, 'maxSegmentDuration', XmlUtils.parseDuration);
545
+ maxSegmentDuration = TXml.parseAttr(
546
+ mpd, 'maxSegmentDuration', TXml.parseDuration);
517
547
  }
518
- const mpdType = mpd.getAttribute('type') || 'static';
548
+ const mpdType = mpd.attributes['type'] || 'static';
519
549
 
520
550
  /** @type {!shaka.media.PresentationTimeline} */
521
551
  let presentationTimeline;
@@ -575,7 +605,7 @@ shaka.dash.DashParser = class {
575
605
  presentationTimeline.setSegmentAvailabilityDuration(
576
606
  segmentAvailabilityDuration);
577
607
 
578
- const profiles = mpd.getAttribute('profiles') || '';
608
+ const profiles = mpd.attributes['profiles'] || '';
579
609
 
580
610
  /** @type {shaka.dash.DashParser.Context} */
581
611
  const context = {
@@ -666,8 +696,8 @@ shaka.dash.DashParser = class {
666
696
  // We only need to do clock sync when we're using presentation start
667
697
  // time. This condition also excludes VOD streams.
668
698
  if (presentationTimeline.usingPresentationStartTime()) {
669
- const XmlUtils = shaka.util.XmlUtils;
670
- const timingElements = XmlUtils.findChildren(mpd, 'UTCTiming');
699
+ const TXml = shaka.util.TXml;
700
+ const timingElements = TXml.findChildren(mpd, 'UTCTiming');
671
701
  const offset = await this.parseUtcTiming_(getBaseUris, timingElements);
672
702
  // Detect calls to stop().
673
703
  if (!this.playerInterface_) {
@@ -706,33 +736,33 @@ shaka.dash.DashParser = class {
706
736
  * Reads maxLatency and maxPlaybackRate properties from service
707
737
  * description element.
708
738
  *
709
- * @param {!Element} mpd
739
+ * @param {!shaka.extern.xml.Node} mpd
710
740
  * @return {?shaka.extern.ServiceDescription}
711
741
  * @private
712
742
  */
713
743
  parseServiceDescription_(mpd) {
714
- const XmlUtils = shaka.util.XmlUtils;
715
- const elem = XmlUtils.findChild(mpd, 'ServiceDescription');
744
+ const TXml = shaka.util.TXml;
745
+ const elem = TXml.findChild(mpd, 'ServiceDescription');
716
746
 
717
747
  if (!elem ) {
718
748
  return null;
719
749
  }
720
750
 
721
- const latencyNode = XmlUtils.findChild(elem, 'Latency');
722
- const playbackRateNode = XmlUtils.findChild(elem, 'PlaybackRate');
751
+ const latencyNode = TXml.findChild(elem, 'Latency');
752
+ const playbackRateNode = TXml.findChild(elem, 'PlaybackRate');
723
753
 
724
- if ((latencyNode && latencyNode.getAttribute('max')) || playbackRateNode) {
725
- const maxLatency = latencyNode && latencyNode.getAttribute('max') ?
726
- parseInt(latencyNode.getAttribute('max'), 10) / 1000 :
754
+ if ((latencyNode && latencyNode.attributes['max']) || playbackRateNode) {
755
+ const maxLatency = latencyNode && latencyNode.attributes['max'] ?
756
+ parseInt(latencyNode.attributes['max'], 10) / 1000 :
727
757
  null;
728
758
  const maxPlaybackRate = playbackRateNode ?
729
- parseFloat(playbackRateNode.getAttribute('max')) :
759
+ parseFloat(playbackRateNode.attributes['max']) :
730
760
  null;
731
- const minLatency = latencyNode && latencyNode.getAttribute('min') ?
732
- parseInt(latencyNode.getAttribute('min'), 10) / 1000 :
761
+ const minLatency = latencyNode && latencyNode.attributes['min'] ?
762
+ parseInt(latencyNode.attributes['min'], 10) / 1000 :
733
763
  null;
734
764
  const minPlaybackRate = playbackRateNode ?
735
- parseFloat(playbackRateNode.getAttribute('min')) :
765
+ parseFloat(playbackRateNode.attributes['min']) :
736
766
  null;
737
767
 
738
768
  return {
@@ -753,7 +783,7 @@ shaka.dash.DashParser = class {
753
783
  *
754
784
  * @param {shaka.dash.DashParser.Context} context
755
785
  * @param {function():!Array.<string>} getBaseUris
756
- * @param {!Element} mpd
786
+ * @param {!shaka.extern.xml.Node} mpd
757
787
  * @return {{
758
788
  * periods: !Array.<shaka.extern.Period>,
759
789
  * duration: ?number,
@@ -762,21 +792,21 @@ shaka.dash.DashParser = class {
762
792
  * @private
763
793
  */
764
794
  parsePeriods_(context, getBaseUris, mpd) {
765
- const XmlUtils = shaka.util.XmlUtils;
766
- const presentationDuration = XmlUtils.parseAttr(
767
- mpd, 'mediaPresentationDuration', XmlUtils.parseDuration);
795
+ const TXml = shaka.util.TXml;
796
+ const presentationDuration = TXml.parseAttr(
797
+ mpd, 'mediaPresentationDuration', TXml.parseDuration);
768
798
 
769
799
  const periods = [];
770
800
  let prevEnd = 0;
771
- const periodNodes = XmlUtils.findChildren(mpd, 'Period');
801
+ const periodNodes = TXml.findChildren(mpd, 'Period');
772
802
  for (let i = 0; i < periodNodes.length; i++) {
773
803
  const elem = periodNodes[i];
774
804
  const next = periodNodes[i + 1];
775
805
  const start = /** @type {number} */ (
776
- XmlUtils.parseAttr(elem, 'start', XmlUtils.parseDuration, prevEnd));
777
- const periodId = elem.id;
806
+ TXml.parseAttr(elem, 'start', TXml.parseDuration, prevEnd));
807
+ const periodId = elem.attributes['id'];
778
808
  const givenDuration =
779
- XmlUtils.parseAttr(elem, 'duration', XmlUtils.parseDuration);
809
+ TXml.parseAttr(elem, 'duration', TXml.parseDuration);
780
810
 
781
811
  let periodDuration = null;
782
812
  if (next) {
@@ -784,7 +814,7 @@ shaka.dash.DashParser = class {
784
814
  // of the following Period is the duration of the media content
785
815
  // represented by this Period."
786
816
  const nextStart =
787
- XmlUtils.parseAttr(next, 'start', XmlUtils.parseDuration);
817
+ TXml.parseAttr(next, 'start', TXml.parseDuration);
788
818
  if (nextStart != null) {
789
819
  periodDuration = nextStart - start;
790
820
  }
@@ -912,7 +942,7 @@ shaka.dash.DashParser = class {
912
942
  */
913
943
  parsePeriod_(context, getBaseUris, periodInfo) {
914
944
  const Functional = shaka.util.Functional;
915
- const XmlUtils = shaka.util.XmlUtils;
945
+ const TXml = shaka.util.TXml;
916
946
  const ContentType = shaka.util.ManifestParserUtils.ContentType;
917
947
 
918
948
  context.period = this.createFrame_(periodInfo.node, null, getBaseUris);
@@ -928,7 +958,7 @@ shaka.dash.DashParser = class {
928
958
  }
929
959
 
930
960
  const eventStreamNodes =
931
- XmlUtils.findChildren(periodInfo.node, 'EventStream');
961
+ TXml.findChildren(periodInfo.node, 'EventStream');
932
962
  const availabilityStart =
933
963
  context.presentationTimeline.getSegmentAvailabilityStart();
934
964
 
@@ -938,7 +968,7 @@ shaka.dash.DashParser = class {
938
968
  }
939
969
 
940
970
  const adaptationSetNodes =
941
- XmlUtils.findChildren(periodInfo.node, 'AdaptationSet');
971
+ TXml.findChildren(periodInfo.node, 'AdaptationSet');
942
972
  const adaptationSets = adaptationSetNodes
943
973
  .map((node) => this.parseAdaptationSet_(context, node))
944
974
  .filter(Functional.isNotNull);
@@ -1047,12 +1077,12 @@ shaka.dash.DashParser = class {
1047
1077
  * Parses an AdaptationSet XML element.
1048
1078
  *
1049
1079
  * @param {shaka.dash.DashParser.Context} context
1050
- * @param {!Element} elem The AdaptationSet element.
1080
+ * @param {!shaka.extern.xml.Node} elem The AdaptationSet element.
1051
1081
  * @return {?shaka.dash.DashParser.AdaptationInfo}
1052
1082
  * @private
1053
1083
  */
1054
1084
  parseAdaptationSet_(context, elem) {
1055
- const XmlUtils = shaka.util.XmlUtils;
1085
+ const TXml = shaka.util.TXml;
1056
1086
  const Functional = shaka.util.Functional;
1057
1087
  const ManifestParserUtils = shaka.util.ManifestParserUtils;
1058
1088
  const ContentType = ManifestParserUtils.ContentType;
@@ -1061,9 +1091,9 @@ shaka.dash.DashParser = class {
1061
1091
  context.adaptationSet = this.createFrame_(elem, context.period, null);
1062
1092
 
1063
1093
  let main = false;
1064
- const roleElements = XmlUtils.findChildren(elem, 'Role');
1094
+ const roleElements = TXml.findChildren(elem, 'Role');
1065
1095
  const roleValues = roleElements.map((role) => {
1066
- return role.getAttribute('value');
1096
+ return role.attributes['value'];
1067
1097
  }).filter(Functional.isNotNull);
1068
1098
 
1069
1099
  // Default kind for text streams is 'subtitle' if unspecified in the
@@ -1075,12 +1105,12 @@ shaka.dash.DashParser = class {
1075
1105
  }
1076
1106
 
1077
1107
  for (const roleElement of roleElements) {
1078
- const scheme = roleElement.getAttribute('schemeIdUri');
1108
+ const scheme = roleElement.attributes['schemeIdUri'];
1079
1109
  if (scheme == null || scheme == 'urn:mpeg:dash:role:2011') {
1080
1110
  // These only apply for the given scheme, but allow them to be specified
1081
1111
  // if there is no scheme specified.
1082
1112
  // See: DASH section 5.8.5.5
1083
- const value = roleElement.getAttribute('value');
1113
+ const value = roleElement.attributes['value'];
1084
1114
  switch (value) {
1085
1115
  case 'main':
1086
1116
  main = true;
@@ -1125,18 +1155,18 @@ shaka.dash.DashParser = class {
1125
1155
  };
1126
1156
 
1127
1157
  const essentialProperties =
1128
- XmlUtils.findChildren(elem, 'EssentialProperty');
1158
+ TXml.findChildren(elem, 'EssentialProperty');
1129
1159
  // ID of real AdaptationSet if this is a trick mode set:
1130
1160
  let trickModeFor = null;
1131
1161
  let isFastSwitching = false;
1132
1162
  let unrecognizedEssentialProperty = false;
1133
1163
  for (const prop of essentialProperties) {
1134
- const schemeId = prop.getAttribute('schemeIdUri');
1164
+ const schemeId = prop.attributes['schemeIdUri'];
1135
1165
  if (schemeId == 'http://dashif.org/guidelines/trickmode') {
1136
- trickModeFor = prop.getAttribute('value');
1166
+ trickModeFor = prop.attributes['value'];
1137
1167
  } else if (schemeId == transferCharacteristicsScheme) {
1138
1168
  videoRange = getVideoRangeFromTransferCharacteristicCICP(
1139
- parseInt(prop.getAttribute('value'), 10),
1169
+ parseInt(prop.attributes['value'], 10),
1140
1170
  );
1141
1171
  } else if (schemeId == colourPrimariesScheme ||
1142
1172
  schemeId == matrixCoefficientsScheme) {
@@ -1148,25 +1178,29 @@ shaka.dash.DashParser = class {
1148
1178
  }
1149
1179
  }
1150
1180
 
1181
+ let lastSegmentNumber = null;
1182
+
1151
1183
  const supplementalProperties =
1152
- XmlUtils.findChildren(elem, 'SupplementalProperty');
1184
+ TXml.findChildren(elem, 'SupplementalProperty');
1153
1185
  for (const prop of supplementalProperties) {
1154
- const schemeId = prop.getAttribute('schemeIdUri');
1155
- if (schemeId == transferCharacteristicsScheme) {
1186
+ const schemeId = prop.attributes['schemeIdUri'];
1187
+ if (schemeId == 'http://dashif.org/guidelines/last-segment-number') {
1188
+ lastSegmentNumber = parseInt(prop.attributes['value'], 10) - 1;
1189
+ } else if (schemeId == transferCharacteristicsScheme) {
1156
1190
  videoRange = getVideoRangeFromTransferCharacteristicCICP(
1157
- parseInt(prop.getAttribute('value'), 10),
1191
+ parseInt(prop.attributes['value'], 10),
1158
1192
  );
1159
1193
  }
1160
1194
  }
1161
1195
 
1162
- const accessibilities = XmlUtils.findChildren(elem, 'Accessibility');
1196
+ const accessibilities = TXml.findChildren(elem, 'Accessibility');
1163
1197
  const LanguageUtils = shaka.util.LanguageUtils;
1164
1198
  const closedCaptions = new Map();
1165
1199
  /** @type {?shaka.media.ManifestParser.AccessibilityPurpose} */
1166
1200
  let accessibilityPurpose;
1167
1201
  for (const prop of accessibilities) {
1168
- const schemeId = prop.getAttribute('schemeIdUri');
1169
- const value = prop.getAttribute('value');
1202
+ const schemeId = prop.attributes['schemeIdUri'];
1203
+ const value = prop.attributes['value'];
1170
1204
  if (schemeId == 'urn:scte:dash:cc:cea-608:2015' ) {
1171
1205
  let channelId = 1;
1172
1206
  if (value != null) {
@@ -1270,7 +1304,7 @@ shaka.dash.DashParser = class {
1270
1304
  }
1271
1305
 
1272
1306
  const contentProtectionElems =
1273
- XmlUtils.findChildren(elem, 'ContentProtection');
1307
+ TXml.findChildren(elem, 'ContentProtection');
1274
1308
  const contentProtection = ContentProtection.parseFromAdaptationSet(
1275
1309
  contentProtectionElems,
1276
1310
  this.config_.dash.ignoreDrmInfo,
@@ -1280,24 +1314,26 @@ shaka.dash.DashParser = class {
1280
1314
  context.adaptationSet.language || 'und');
1281
1315
 
1282
1316
  // This attribute is currently non-standard, but it is supported by Kaltura.
1283
- let label = elem.getAttribute('label');
1317
+ let label = elem.attributes['label'];
1284
1318
 
1285
1319
  // 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');
1320
+ const labelElements = TXml.findChildren(elem, 'Label');
1287
1321
  if (labelElements && labelElements.length) {
1288
1322
  // NOTE: Right now only one label field is supported.
1289
1323
  const firstLabelElement = labelElements[0];
1290
- if (firstLabelElement.textContent) {
1291
- label = firstLabelElement.textContent;
1324
+ const textContent = shaka.util.TXml.getTextContents(firstLabelElement);
1325
+ if (textContent) {
1326
+ label = textContent;
1292
1327
  }
1293
1328
  }
1294
1329
 
1295
1330
  // Parse Representations into Streams.
1296
- const representations = XmlUtils.findChildren(elem, 'Representation');
1331
+ const representations = TXml.findChildren(elem, 'Representation');
1297
1332
  const streams = representations.map((representation) => {
1298
1333
  const parsedRepresentation = this.parseRepresentation_(context,
1299
1334
  contentProtection, kind, language, label, main, roleValues,
1300
- closedCaptions, representation, accessibilityPurpose);
1335
+ closedCaptions, representation, accessibilityPurpose,
1336
+ lastSegmentNumber);
1301
1337
  if (parsedRepresentation) {
1302
1338
  parsedRepresentation.hdr = parsedRepresentation.hdr || videoRange;
1303
1339
  parsedRepresentation.fastSwitching = isFastSwitching;
@@ -1353,7 +1389,7 @@ shaka.dash.DashParser = class {
1353
1389
  }
1354
1390
 
1355
1391
  const repIds = representations
1356
- .map((node) => { return node.getAttribute('id'); })
1392
+ .map((node) => { return node.attributes['id']; })
1357
1393
  .filter(shaka.util.Functional.isNotNull);
1358
1394
 
1359
1395
  return {
@@ -1379,17 +1415,19 @@ shaka.dash.DashParser = class {
1379
1415
  * @param {boolean} isPrimary
1380
1416
  * @param {!Array.<string>} roles
1381
1417
  * @param {Map.<string, string>} closedCaptions
1382
- * @param {!Element} node
1418
+ * @param {!shaka.extern.xml.Node} node
1383
1419
  * @param {?shaka.media.ManifestParser.AccessibilityPurpose}
1384
1420
  * accessibilityPurpose
1421
+ * @param {?number} lastSegmentNumber
1385
1422
  *
1386
1423
  * @return {?shaka.extern.Stream} The Stream, or null when there is a
1387
1424
  * non-critical parsing error.
1388
1425
  * @private
1389
1426
  */
1390
1427
  parseRepresentation_(context, contentProtection, kind, language, label,
1391
- isPrimary, roles, closedCaptions, node, accessibilityPurpose) {
1392
- const XmlUtils = shaka.util.XmlUtils;
1428
+ isPrimary, roles, closedCaptions, node, accessibilityPurpose,
1429
+ lastSegmentNumber) {
1430
+ const TXml = shaka.util.TXml;
1393
1431
  const ContentType = shaka.util.ManifestParserUtils.ContentType;
1394
1432
 
1395
1433
  context.representation =
@@ -1411,7 +1449,7 @@ shaka.dash.DashParser = class {
1411
1449
  // To avoid NaN at the variant level on broken content, fall back to zero.
1412
1450
  // https://github.com/shaka-project/shaka-player/issues/938#issuecomment-317278180
1413
1451
  context.bandwidth =
1414
- XmlUtils.parseAttr(node, 'bandwidth', XmlUtils.parsePositiveInt) || 0;
1452
+ TXml.parseAttr(node, 'bandwidth', TXml.parsePositiveInt) || 0;
1415
1453
 
1416
1454
  /** @type {?shaka.dash.DashParser.StreamInfo} */
1417
1455
  let streamInfo;
@@ -1422,8 +1460,8 @@ shaka.dash.DashParser = class {
1422
1460
  const isImage = contentType == ContentType.IMAGE;
1423
1461
 
1424
1462
  try {
1425
- /** @type {shaka.extern.aes128Key|undefined} */
1426
- let aes128Key = undefined;
1463
+ /** @type {shaka.extern.aesKey|undefined} */
1464
+ let aesKey = undefined;
1427
1465
  if (contentProtection.aes128Info) {
1428
1466
  const getBaseUris = context.representation.getBaseUris;
1429
1467
  const uris = shaka.util.ManifestParserUtils.resolveUris(
@@ -1432,15 +1470,16 @@ shaka.dash.DashParser = class {
1432
1470
  const request = shaka.net.NetworkingEngine.makeRequest(
1433
1471
  uris, this.config_.retryParameters);
1434
1472
 
1435
- aes128Key = {
1436
- method: 'AES-128',
1473
+ aesKey = {
1474
+ bitsKey: 128,
1475
+ blockCipherMode: 'CBC',
1437
1476
  iv: contentProtection.aes128Info.iv,
1438
1477
  firstMediaSequenceNumber: 0,
1439
1478
  };
1440
1479
 
1441
1480
  // Don't download the key object until the segment is parsed, to
1442
1481
  // avoid a startup delay for long manifests with lots of keys.
1443
- aes128Key.fetchKey = async () => {
1482
+ aesKey.fetchKey = async () => {
1444
1483
  const keyResponse =
1445
1484
  await this.makeNetworkRequest_(request, requestType);
1446
1485
 
@@ -1456,9 +1495,9 @@ shaka.dash.DashParser = class {
1456
1495
  const algorithm = {
1457
1496
  name: 'AES-CBC',
1458
1497
  };
1459
- aes128Key.cryptoKey = await window.crypto.subtle.importKey(
1498
+ aesKey.cryptoKey = await window.crypto.subtle.importKey(
1460
1499
  'raw', keyResponse.data, algorithm, true, ['decrypt']);
1461
- aes128Key.fetchKey = undefined; // No longer needed.
1500
+ aesKey.fetchKey = undefined; // No longer needed.
1462
1501
  };
1463
1502
  }
1464
1503
  const requestSegment = (uris, startByte, endByte, isInit) => {
@@ -1466,17 +1505,17 @@ shaka.dash.DashParser = class {
1466
1505
  };
1467
1506
  if (context.representation.segmentBase) {
1468
1507
  streamInfo = shaka.dash.SegmentBase.createStreamInfo(
1469
- context, requestSegment, aes128Key);
1508
+ context, requestSegment, aesKey);
1470
1509
  } else if (context.representation.segmentList) {
1471
1510
  streamInfo = shaka.dash.SegmentList.createStreamInfo(
1472
- context, this.streamMap_, aes128Key);
1511
+ context, this.streamMap_, aesKey);
1473
1512
  } else if (context.representation.segmentTemplate) {
1474
1513
  const hasManifest = !!this.manifest_;
1475
1514
 
1476
1515
  streamInfo = shaka.dash.SegmentTemplate.createStreamInfo(
1477
1516
  context, requestSegment, this.streamMap_, hasManifest,
1478
1517
  this.config_.dash.initialSegmentLimit, this.periodDurations_,
1479
- aes128Key);
1518
+ aesKey, lastSegmentNumber);
1480
1519
  } else {
1481
1520
  goog.asserts.assert(isText,
1482
1521
  'Must have Segment* with non-text streams.');
@@ -1485,8 +1524,13 @@ shaka.dash.DashParser = class {
1485
1524
  const getBaseUris = context.representation.getBaseUris;
1486
1525
  streamInfo = {
1487
1526
  generateSegmentIndex: () => {
1488
- return Promise.resolve(shaka.media.SegmentIndex.forSingleSegment(
1489
- periodStart, duration, getBaseUris()));
1527
+ const segmentIndex = shaka.media.SegmentIndex.forSingleSegment(
1528
+ periodStart, duration, getBaseUris());
1529
+ segmentIndex.forEachTopLevelReference((ref) => {
1530
+ ref.mimeType = context.representation.mimeType;
1531
+ ref.codecs = context.representation.codecs;
1532
+ });
1533
+ return Promise.resolve(segmentIndex);
1490
1534
  },
1491
1535
  };
1492
1536
  }
@@ -1503,7 +1547,7 @@ shaka.dash.DashParser = class {
1503
1547
  }
1504
1548
 
1505
1549
  const contentProtectionElems =
1506
- XmlUtils.findChildren(node, 'ContentProtection');
1550
+ TXml.findChildren(node, 'ContentProtection');
1507
1551
  const keyId = shaka.dash.ContentProtection.parseFromRepresentation(
1508
1552
  contentProtectionElems, contentProtection,
1509
1553
  this.config_.dash.ignoreDrmInfo,
@@ -1513,12 +1557,12 @@ shaka.dash.DashParser = class {
1513
1557
  // Detect the presence of E-AC3 JOC audio content, using DD+JOC signaling.
1514
1558
  // See: ETSI TS 103 420 V1.2.1 (2018-10)
1515
1559
  const supplementalPropertyElems =
1516
- XmlUtils.findChildren(node, 'SupplementalProperty');
1560
+ TXml.findChildren(node, 'SupplementalProperty');
1517
1561
  const hasJoc = supplementalPropertyElems.some((element) => {
1518
1562
  const expectedUri = 'tag:dolby.com,2018:dash:EC3_ExtensionType:2018';
1519
1563
  const expectedValue = 'JOC';
1520
- return element.getAttribute('schemeIdUri') == expectedUri &&
1521
- element.getAttribute('value') == expectedValue;
1564
+ return element.attributes['schemeIdUri'] == expectedUri &&
1565
+ element.attributes['value'] == expectedValue;
1522
1566
  });
1523
1567
  let spatialAudio = false;
1524
1568
  if (hasJoc) {
@@ -1536,16 +1580,16 @@ shaka.dash.DashParser = class {
1536
1580
  let tilesLayout;
1537
1581
  if (isImage) {
1538
1582
  const essentialPropertyElems =
1539
- XmlUtils.findChildren(node, 'EssentialProperty');
1583
+ TXml.findChildren(node, 'EssentialProperty');
1540
1584
  const thumbnailTileElem = essentialPropertyElems.find((element) => {
1541
1585
  const expectedUris = [
1542
1586
  'http://dashif.org/thumbnail_tile',
1543
1587
  'http://dashif.org/guidelines/thumbnail_tile',
1544
1588
  ];
1545
- return expectedUris.includes(element.getAttribute('schemeIdUri'));
1589
+ return expectedUris.includes(element.attributes['schemeIdUri']);
1546
1590
  });
1547
1591
  if (thumbnailTileElem) {
1548
- tilesLayout = thumbnailTileElem.getAttribute('value');
1592
+ tilesLayout = thumbnailTileElem.attributes['value'];
1549
1593
  }
1550
1594
  // Filter image adaptation sets that has no tilesLayout.
1551
1595
  if (!tilesLayout) {
@@ -1585,7 +1629,7 @@ shaka.dash.DashParser = class {
1585
1629
  },
1586
1630
  segmentIndex: null,
1587
1631
  mimeType: context.representation.mimeType,
1588
- codecs: context.representation.codecs,
1632
+ codecs,
1589
1633
  frameRate: context.representation.frameRate,
1590
1634
  pixelAspectRatio: context.representation.pixelAspectRatio,
1591
1635
  bandwidth: context.bandwidth,
@@ -1615,6 +1659,8 @@ shaka.dash.DashParser = class {
1615
1659
  accessibilityPurpose,
1616
1660
  external: false,
1617
1661
  fastSwitching: false,
1662
+ fullMimeTypes: new Set([shaka.util.MimeUtils.getFullType(
1663
+ context.representation.mimeType, context.representation.codecs)]),
1618
1664
  };
1619
1665
  }
1620
1666
 
@@ -1675,6 +1721,15 @@ shaka.dash.DashParser = class {
1675
1721
  this.setUpdateTimer_(updateDelay);
1676
1722
  }
1677
1723
 
1724
+ /**
1725
+ * Update now the manifest
1726
+ *
1727
+ * @private
1728
+ */
1729
+ updateNow_() {
1730
+ this.updateTimer_.tickNow();
1731
+ }
1732
+
1678
1733
  /**
1679
1734
  * Sets the update timer. Does nothing if the manifest does not specify an
1680
1735
  * update period.
@@ -1691,9 +1746,13 @@ shaka.dash.DashParser = class {
1691
1746
  if (this.updatePeriod_ < 0) {
1692
1747
  return;
1693
1748
  }
1749
+ let updateTime = this.updatePeriod_;
1750
+ if (this.config_.dash.updatePeriod >= 0) {
1751
+ updateTime = this.config_.dash.updatePeriod;
1752
+ }
1694
1753
 
1695
1754
  const finalDelay = Math.max(
1696
- this.updatePeriod_ - offset,
1755
+ updateTime - offset,
1697
1756
  this.averageUpdateDuration_.getEstimate());
1698
1757
 
1699
1758
  // We do not run the timer as repeating because part of update is async and
@@ -1704,7 +1763,7 @@ shaka.dash.DashParser = class {
1704
1763
  /**
1705
1764
  * Creates a new inheritance frame for the given element.
1706
1765
  *
1707
- * @param {!Element} elem
1766
+ * @param {!shaka.extern.xml.Node} elem
1708
1767
  * @param {?shaka.dash.DashParser.InheritanceFrame} parent
1709
1768
  * @param {?function():!Array.<string>} getBaseUris
1710
1769
  * @return {shaka.dash.DashParser.InheritanceFrame}
@@ -1714,7 +1773,7 @@ shaka.dash.DashParser = class {
1714
1773
  goog.asserts.assert(parent || getBaseUris,
1715
1774
  'Must provide either parent or getBaseUris');
1716
1775
  const ManifestParserUtils = shaka.util.ManifestParserUtils;
1717
- const XmlUtils = shaka.util.XmlUtils;
1776
+ const TXml = shaka.util.TXml;
1718
1777
  parent = parent || /** @type {shaka.dash.DashParser.InheritanceFrame} */ ({
1719
1778
  contentType: '',
1720
1779
  mimeType: '',
@@ -1729,17 +1788,17 @@ shaka.dash.DashParser = class {
1729
1788
  });
1730
1789
  getBaseUris = getBaseUris || parent.getBaseUris;
1731
1790
 
1732
- const parseNumber = XmlUtils.parseNonNegativeInt;
1733
- const evalDivision = XmlUtils.evalDivision;
1791
+ const parseNumber = TXml.parseNonNegativeInt;
1792
+ const evalDivision = TXml.evalDivision;
1734
1793
 
1735
- const id = elem.getAttribute('id');
1736
- const uriObjs = XmlUtils.findChildren(elem, 'BaseURL');
1794
+ const id = elem.attributes['id'];
1795
+ const uriObjs = TXml.findChildren(elem, 'BaseURL');
1737
1796
  let calculatedBaseUris;
1738
1797
  let someLocationValid = false;
1739
1798
  if (this.contentSteeringManager_) {
1740
1799
  for (const uriObj of uriObjs) {
1741
- const serviceLocation = uriObj.getAttribute('serviceLocation');
1742
- const uri = XmlUtils.getContents(uriObj);
1800
+ const serviceLocation = uriObj.attributes['serviceLocation'];
1801
+ const uri = TXml.getContents(uriObj);
1743
1802
  if (serviceLocation && uri) {
1744
1803
  this.contentSteeringManager_.addLocation(
1745
1804
  id, serviceLocation, uri);
@@ -1748,7 +1807,7 @@ shaka.dash.DashParser = class {
1748
1807
  }
1749
1808
  }
1750
1809
  if (!someLocationValid || !this.contentSteeringManager_) {
1751
- calculatedBaseUris = uriObjs.map(XmlUtils.getContents);
1810
+ calculatedBaseUris = uriObjs.map(TXml.getContents);
1752
1811
  }
1753
1812
 
1754
1813
  const getFrameUris = () => {
@@ -1764,55 +1823,55 @@ shaka.dash.DashParser = class {
1764
1823
  return [];
1765
1824
  };
1766
1825
 
1767
- let contentType = elem.getAttribute('contentType') || parent.contentType;
1768
- const mimeType = elem.getAttribute('mimeType') || parent.mimeType;
1769
- const codecs = elem.getAttribute('codecs') || parent.codecs;
1826
+ let contentType = elem.attributes['contentType'] || parent.contentType;
1827
+ const mimeType = elem.attributes['mimeType'] || parent.mimeType;
1828
+ const codecs = elem.attributes['codecs'] || parent.codecs;
1770
1829
  const frameRate =
1771
- XmlUtils.parseAttr(elem, 'frameRate', evalDivision) || parent.frameRate;
1830
+ TXml.parseAttr(elem, 'frameRate', evalDivision) || parent.frameRate;
1772
1831
  const pixelAspectRatio =
1773
- elem.getAttribute('sar') || parent.pixelAspectRatio;
1832
+ elem.attributes['sar'] || parent.pixelAspectRatio;
1774
1833
  const emsgSchemeIdUris = this.emsgSchemeIdUris_(
1775
- XmlUtils.findChildren(elem, 'InbandEventStream'),
1834
+ TXml.findChildren(elem, 'InbandEventStream'),
1776
1835
  parent.emsgSchemeIdUris);
1777
1836
  const audioChannelConfigs =
1778
- XmlUtils.findChildren(elem, 'AudioChannelConfiguration');
1837
+ TXml.findChildren(elem, 'AudioChannelConfiguration');
1779
1838
  const numChannels =
1780
1839
  this.parseAudioChannels_(audioChannelConfigs) || parent.numChannels;
1781
1840
  const audioSamplingRate =
1782
- XmlUtils.parseAttr(elem, 'audioSamplingRate', parseNumber) ||
1841
+ TXml.parseAttr(elem, 'audioSamplingRate', parseNumber) ||
1783
1842
  parent.audioSamplingRate;
1784
1843
 
1785
1844
  if (!contentType) {
1786
1845
  contentType = shaka.dash.DashParser.guessContentType_(mimeType, codecs);
1787
1846
  }
1788
1847
 
1789
- const segmentBase = XmlUtils.findChild(elem, 'SegmentBase');
1790
- const segmentTemplate = XmlUtils.findChild(elem, 'SegmentTemplate');
1848
+ const segmentBase = TXml.findChild(elem, 'SegmentBase');
1849
+ const segmentTemplate = TXml.findChild(elem, 'SegmentTemplate');
1791
1850
 
1792
1851
  // The availabilityTimeOffset is the sum of all @availabilityTimeOffset
1793
1852
  // values that apply to the adaptation set, via BaseURL, SegmentBase,
1794
1853
  // or SegmentTemplate elements.
1795
1854
  const segmentBaseAto = segmentBase ?
1796
- (XmlUtils.parseAttr(segmentBase, 'availabilityTimeOffset',
1797
- XmlUtils.parseFloat) || 0) : 0;
1855
+ (TXml.parseAttr(segmentBase, 'availabilityTimeOffset',
1856
+ TXml.parseFloat) || 0) : 0;
1798
1857
  const segmentTemplateAto = segmentTemplate ?
1799
- (XmlUtils.parseAttr(segmentTemplate, 'availabilityTimeOffset',
1800
- XmlUtils.parseFloat) || 0) : 0;
1858
+ (TXml.parseAttr(segmentTemplate, 'availabilityTimeOffset',
1859
+ TXml.parseFloat) || 0) : 0;
1801
1860
  const baseUriAto = uriObjs && uriObjs.length ?
1802
- (XmlUtils.parseAttr(uriObjs[0], 'availabilityTimeOffset',
1803
- XmlUtils.parseFloat) || 0) : 0;
1861
+ (TXml.parseAttr(uriObjs[0], 'availabilityTimeOffset',
1862
+ TXml.parseFloat) || 0) : 0;
1804
1863
 
1805
1864
  const availabilityTimeOffset = parent.availabilityTimeOffset + baseUriAto +
1806
1865
  segmentBaseAto + segmentTemplateAto;
1807
1866
 
1808
1867
  let segmentSequenceCadence = null;
1809
1868
  const segmentSequenceProperties =
1810
- XmlUtils.findChild(elem, 'SegmentSequenceProperties');
1869
+ TXml.findChild(elem, 'SegmentSequenceProperties');
1811
1870
  if (segmentSequenceProperties) {
1812
- const sap = XmlUtils.findChild(segmentSequenceProperties, 'SAP');
1871
+ const sap = TXml.findChild(segmentSequenceProperties, 'SAP');
1813
1872
  if (sap) {
1814
- segmentSequenceCadence = XmlUtils.parseAttr(sap, 'cadence',
1815
- XmlUtils.parseInt);
1873
+ segmentSequenceCadence = TXml.parseAttr(sap, 'cadence',
1874
+ TXml.parseInt);
1816
1875
  }
1817
1876
  }
1818
1877
 
@@ -1821,10 +1880,10 @@ shaka.dash.DashParser = class {
1821
1880
  () => ManifestParserUtils.resolveUris(getBaseUris(), getFrameUris()),
1822
1881
  segmentBase: segmentBase || parent.segmentBase,
1823
1882
  segmentList:
1824
- XmlUtils.findChild(elem, 'SegmentList') || parent.segmentList,
1883
+ TXml.findChild(elem, 'SegmentList') || parent.segmentList,
1825
1884
  segmentTemplate: segmentTemplate || parent.segmentTemplate,
1826
- width: XmlUtils.parseAttr(elem, 'width', parseNumber) || parent.width,
1827
- height: XmlUtils.parseAttr(elem, 'height', parseNumber) || parent.height,
1885
+ width: TXml.parseAttr(elem, 'width', parseNumber) || parent.width,
1886
+ height: TXml.parseAttr(elem, 'height', parseNumber) || parent.height,
1828
1887
  contentType: contentType,
1829
1888
  mimeType: mimeType,
1830
1889
  codecs: codecs,
@@ -1832,7 +1891,7 @@ shaka.dash.DashParser = class {
1832
1891
  pixelAspectRatio: pixelAspectRatio,
1833
1892
  emsgSchemeIdUris: emsgSchemeIdUris,
1834
1893
  id: id,
1835
- language: elem.getAttribute('lang'),
1894
+ language: elem.attributes['lang'],
1836
1895
  numChannels: numChannels,
1837
1896
  audioSamplingRate: audioSamplingRate,
1838
1897
  availabilityTimeOffset: availabilityTimeOffset,
@@ -1846,7 +1905,8 @@ shaka.dash.DashParser = class {
1846
1905
  * of the ones parsed from inBandEventStreams and the ones provided in
1847
1906
  * emsgSchemeIdUris.
1848
1907
  *
1849
- * @param {!Array.<!Element>} inBandEventStreams Array of InbandEventStream
1908
+ * @param {!Array.<!shaka.extern.xml.Node>} inBandEventStreams
1909
+ * Array of InbandEventStream
1850
1910
  * elements to parse and add to the returned array.
1851
1911
  * @param {!Array.<string>} emsgSchemeIdUris Array of parsed
1852
1912
  * InbandEventStream schemeIdUri attributes to add to the returned array.
@@ -1857,7 +1917,7 @@ shaka.dash.DashParser = class {
1857
1917
  emsgSchemeIdUris_(inBandEventStreams, emsgSchemeIdUris) {
1858
1918
  const schemeIdUris = emsgSchemeIdUris.slice();
1859
1919
  for (const event of inBandEventStreams) {
1860
- const schemeIdUri = event.getAttribute('schemeIdUri');
1920
+ const schemeIdUri = event.attributes['schemeIdUri'];
1861
1921
  if (!schemeIdUris.includes(schemeIdUri)) {
1862
1922
  schemeIdUris.push(schemeIdUri);
1863
1923
  }
@@ -1866,19 +1926,19 @@ shaka.dash.DashParser = class {
1866
1926
  }
1867
1927
 
1868
1928
  /**
1869
- * @param {!Array.<!Element>} audioChannelConfigs An array of
1929
+ * @param {!Array.<!shaka.extern.xml.Node>} audioChannelConfigs An array of
1870
1930
  * AudioChannelConfiguration elements.
1871
1931
  * @return {?number} The number of audio channels, or null if unknown.
1872
1932
  * @private
1873
1933
  */
1874
1934
  parseAudioChannels_(audioChannelConfigs) {
1875
1935
  for (const elem of audioChannelConfigs) {
1876
- const scheme = elem.getAttribute('schemeIdUri');
1936
+ const scheme = elem.attributes['schemeIdUri'];
1877
1937
  if (!scheme) {
1878
1938
  continue;
1879
1939
  }
1880
1940
 
1881
- const value = elem.getAttribute('value');
1941
+ const value = elem.attributes['value'];
1882
1942
  if (!value) {
1883
1943
  continue;
1884
1944
  }
@@ -2049,15 +2109,15 @@ shaka.dash.DashParser = class {
2049
2109
  * Parses an array of UTCTiming elements.
2050
2110
  *
2051
2111
  * @param {function():!Array.<string>} getBaseUris
2052
- * @param {!Array.<!Element>} elems
2112
+ * @param {!Array.<!shaka.extern.xml.Node>} elems
2053
2113
  * @return {!Promise.<number>}
2054
2114
  * @private
2055
2115
  */
2056
2116
  async parseUtcTiming_(getBaseUris, elems) {
2057
2117
  const schemesAndValues = elems.map((elem) => {
2058
2118
  return {
2059
- scheme: elem.getAttribute('schemeIdUri'),
2060
- value: elem.getAttribute('value'),
2119
+ scheme: elem.attributes['schemeIdUri'],
2120
+ value: elem.attributes['value'],
2061
2121
  };
2062
2122
  });
2063
2123
 
@@ -2121,23 +2181,23 @@ shaka.dash.DashParser = class {
2121
2181
  *
2122
2182
  * @param {number} periodStart
2123
2183
  * @param {?number} periodDuration
2124
- * @param {!Element} elem
2184
+ * @param {!shaka.extern.xml.Node} elem
2125
2185
  * @param {number} availabilityStart
2126
2186
  * @private
2127
2187
  */
2128
2188
  parseEventStream_(periodStart, periodDuration, elem, availabilityStart) {
2129
- const XmlUtils = shaka.util.XmlUtils;
2130
- const parseNumber = XmlUtils.parseNonNegativeInt;
2189
+ const TXml = shaka.util.TXml;
2190
+ const parseNumber = shaka.util.TXml.parseNonNegativeInt;
2131
2191
 
2132
- const schemeIdUri = elem.getAttribute('schemeIdUri') || '';
2133
- const value = elem.getAttribute('value') || '';
2134
- const timescale = XmlUtils.parseAttr(elem, 'timescale', parseNumber) || 1;
2192
+ const schemeIdUri = elem.attributes['schemeIdUri'] || '';
2193
+ const value = elem.attributes['value'] || '';
2194
+ const timescale = TXml.parseAttr(elem, 'timescale', parseNumber) || 1;
2135
2195
 
2136
- for (const eventNode of XmlUtils.findChildren(elem, 'Event')) {
2196
+ for (const eventNode of TXml.findChildren(elem, 'Event')) {
2137
2197
  const presentationTime =
2138
- XmlUtils.parseAttr(eventNode, 'presentationTime', parseNumber) || 0;
2198
+ TXml.parseAttr(eventNode, 'presentationTime', parseNumber) || 0;
2139
2199
  const duration =
2140
- XmlUtils.parseAttr(eventNode, 'duration', parseNumber) || 0;
2200
+ TXml.parseAttr(eventNode, 'duration', parseNumber) || 0;
2141
2201
 
2142
2202
  let startTime = presentationTime / timescale + periodStart;
2143
2203
  let endTime = startTime + (duration / timescale);
@@ -2159,8 +2219,9 @@ shaka.dash.DashParser = class {
2159
2219
  value: value,
2160
2220
  startTime: startTime,
2161
2221
  endTime: endTime,
2162
- id: eventNode.getAttribute('id') || '',
2163
- eventElement: eventNode,
2222
+ id: eventNode.attributes['id'] || '',
2223
+ eventElement: TXml.txmlNodeToDomElement(eventNode),
2224
+ eventNode: eventNode,
2164
2225
  };
2165
2226
 
2166
2227
  this.playerInterface_.onTimelineRegionAdded(region);
@@ -2248,9 +2309,9 @@ shaka.dash.DashParser.RequestSegmentCallback;
2248
2309
 
2249
2310
  /**
2250
2311
  * @typedef {{
2251
- * segmentBase: Element,
2252
- * segmentList: Element,
2253
- * segmentTemplate: Element,
2312
+ * segmentBase: ?shaka.extern.xml.Node,
2313
+ * segmentList: ?shaka.extern.xml.Node,
2314
+ * segmentTemplate: ?shaka.extern.xml.Node,
2254
2315
  * getBaseUris: function():!Array.<string>,
2255
2316
  * width: (number|undefined),
2256
2317
  * height: (number|undefined),
@@ -2272,11 +2333,11 @@ shaka.dash.DashParser.RequestSegmentCallback;
2272
2333
  * A collection of elements and properties which are inherited across levels
2273
2334
  * of a DASH manifest.
2274
2335
  *
2275
- * @property {Element} segmentBase
2336
+ * @property {?shaka.extern.xml.Node} segmentBase
2276
2337
  * The XML node for SegmentBase.
2277
- * @property {Element} segmentList
2338
+ * @property {?shaka.extern.xml.Node} segmentList
2278
2339
  * The XML node for SegmentList.
2279
- * @property {Element} segmentTemplate
2340
+ * @property {?shaka.extern.xml.Node} segmentTemplate
2280
2341
  * The XML node for SegmentTemplate.
2281
2342
  * @property {function():!Array.<string>} getBaseUris
2282
2343
  * Function than returns an array of absolute base URIs for the frame.
@@ -2361,7 +2422,7 @@ shaka.dash.DashParser.Context;
2361
2422
  * @typedef {{
2362
2423
  * start: number,
2363
2424
  * duration: ?number,
2364
- * node: !Element,
2425
+ * node: !shaka.extern.xml.Node,
2365
2426
  * isLastPeriod: boolean
2366
2427
  * }}
2367
2428
  *
@@ -2373,7 +2434,7 @@ shaka.dash.DashParser.Context;
2373
2434
  * @property {?number} duration
2374
2435
  * The duration of the period; or null if the duration is not given. This
2375
2436
  * will be non-null for all periods except the last.
2376
- * @property {!Element} node
2437
+ * @property {!shaka.extern.xml.Node} node
2377
2438
  * The XML Node for the Period.
2378
2439
  * @property {boolean} isLastPeriod
2379
2440
  * Whether this Period is the last one in the manifest.