shaka-player 4.7.13 → 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 (114) 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 +34 -23
  8. package/dist/locales.js +44 -34
  9. package/dist/shaka-player.compiled.d.ts +194 -44
  10. package/dist/shaka-player.compiled.debug.d.ts +194 -44
  11. package/dist/shaka-player.compiled.debug.externs.js +474 -337
  12. package/dist/shaka-player.compiled.debug.js +949 -832
  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 +1237 -1171
  16. package/dist/shaka-player.compiled.map +4 -4
  17. package/dist/shaka-player.ui.d.ts +257 -47
  18. package/dist/shaka-player.ui.debug.d.ts +257 -47
  19. package/dist/shaka-player.ui.debug.externs.js +591 -376
  20. package/dist/shaka-player.ui.debug.js +1165 -965
  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 +1521 -1404
  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 +230 -176
  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 +26 -21
  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 +40 -30
  59. package/lib/media/segment_prefetch.js +140 -43
  60. package/lib/media/segment_reference.js +31 -8
  61. package/lib/media/segment_utils.js +208 -25
  62. package/lib/media/streaming_engine.js +319 -63
  63. package/lib/mss/content_protection.js +31 -25
  64. package/lib/mss/mss_parser.js +74 -55
  65. package/lib/net/networking_engine.js +49 -4
  66. package/lib/offline/storage.js +3 -3
  67. package/lib/player.js +882 -673
  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 +152 -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 +61 -3
  87. package/lib/util/stats.js +16 -0
  88. package/lib/util/stream_utils.js +16 -6
  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
@@ -85,6 +85,18 @@ shaka.extern.TextParser = class {
85
85
  shaka.extern.TextParser.TimeContext;
86
86
 
87
87
 
88
+ /**
89
+ * A callback used for editing cues before appending.
90
+ * Provides the cue, the URI of the captions file the cue was parsed from, and
91
+ * the time context that was used when generating that cue.
92
+ * You can edit the cue object passed in.
93
+ * @typedef {function(!shaka.text.Cue, ?string,
94
+ * !shaka.extern.TextParser.TimeContext)}
95
+ * @exportDoc
96
+ */
97
+ shaka.extern.TextParser.ModifyCueCallback;
98
+
99
+
88
100
  /**
89
101
  * @typedef {function():!shaka.extern.TextParser}
90
102
  * @exportDoc
@@ -120,6 +132,13 @@ shaka.extern.TextDisplayer = class {
120
132
  */
121
133
  destroy() {}
122
134
 
135
+ /**
136
+ * Sets the TextDisplayer configuration.
137
+ *
138
+ * @param {shaka.extern.TextDisplayerConfiguration} config
139
+ */
140
+ configure(config) {}
141
+
123
142
  /**
124
143
  * Append given text cues to the list of cues to be displayed.
125
144
  *
@@ -0,0 +1,41 @@
1
+ /*! @license
2
+ * Shaka Player
3
+ * Copyright 2016 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ /**
8
+ * @fileoverview Externs for Tizen
9
+ * @externs
10
+ */
11
+
12
+
13
+ /** @const */
14
+ var webapis = {};
15
+
16
+
17
+ /** @const */
18
+ webapis.systeminfo = {};
19
+
20
+
21
+ /**
22
+ * @return {{width: number, height: number}}
23
+ */
24
+ webapis.systeminfo.getMaxVideoResolution = function() {};
25
+
26
+
27
+ /** @const */
28
+ webapis.productinfo = {};
29
+
30
+
31
+ /**
32
+ * @return {boolean}
33
+ */
34
+ webapis.productinfo.is8KPanelSupported = function() {};
35
+
36
+
37
+ /**
38
+ * @return {boolean}
39
+ */
40
+ webapis.productinfo.isUdPanelSupported = function() {};
41
+
@@ -0,0 +1,19 @@
1
+ /*! @license
2
+ * Shaka Player
3
+ * Copyright 2016 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ /**
8
+ * @fileoverview Externs for WebOS
9
+ *
10
+ * @externs
11
+ */
12
+
13
+
14
+ /** @const */
15
+ var PalmSystem = {};
16
+
17
+
18
+ /** @type {string} */
19
+ PalmSystem.deviceInfo;
@@ -0,0 +1,43 @@
1
+ /*! @license
2
+ * Shaka Player
3
+ * Copyright 2016 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ /**
8
+ * @fileoverview Externs for Xbox
9
+ *
10
+ * @externs
11
+ */
12
+
13
+
14
+ /** @const */
15
+ var Windows = {};
16
+
17
+
18
+ /** @const */
19
+ Windows.Media = {};
20
+
21
+
22
+ /** @const */
23
+ Windows.Media.Protection = {};
24
+
25
+
26
+ /** @const */
27
+ Windows.Media.Protection.ProtectionCapabilities = class {
28
+ /**
29
+ * @param {string} type
30
+ * @param {string} keySystem
31
+ * @return {!Windows.Media.Protection.ProtectionCapabilityResult}
32
+ */
33
+ isTypeSupported(type, keySystem) {}
34
+ };
35
+
36
+ /**
37
+ * @enum {string}
38
+ */
39
+ Windows.Media.Protection.ProtectionCapabilityResult = {
40
+ notSupported: 'NotSupported',
41
+ maybe: 'Maybe',
42
+ probably: 'Probably',
43
+ };
@@ -297,11 +297,19 @@ shaka.abr.SimpleAbrManager = class {
297
297
 
298
298
 
299
299
  /**
300
+ * @param {number} deltaTimeMs The duration, in milliseconds, that the request
301
+ * took to complete.
302
+ * @param {number} numBytes The total number of bytes transferred.
303
+ * @param {boolean} allowSwitch Indicate if the segment is allowed to switch
304
+ * to another stream.
305
+ * @param {shaka.extern.Request=} request
306
+ * A reference to the request
300
307
  * @override
301
308
  * @export
302
309
  */
303
- segmentDownloaded(deltaTimeMs, numBytes, allowSwitch) {
310
+ segmentDownloaded(deltaTimeMs, numBytes, allowSwitch, request) {
304
311
  shaka.log.v2('Segment downloaded:',
312
+ 'contentType=' + (request && request.contentType),
305
313
  'deltaTimeMs=' + deltaTimeMs,
306
314
  'numBytes=' + numBytes,
307
315
  'lastTimeChosenMs=' + this.lastTimeChosenMs_,
@@ -560,6 +560,23 @@ shaka.ads.AdManager = class extends shaka.util.FakeEventTarget {
560
560
  }
561
561
 
562
562
 
563
+ /**
564
+ * @param {string} url
565
+ * @override
566
+ * @export
567
+ */
568
+ addMediaTailorTrackingUrl(url) {
569
+ if (!this.mtAdManager_) {
570
+ throw new shaka.util.Error(
571
+ shaka.util.Error.Severity.RECOVERABLE,
572
+ shaka.util.Error.Category.ADS,
573
+ shaka.util.Error.Code.MT_AD_MANAGER_NOT_INITIALIZED);
574
+ }
575
+
576
+ this.mtAdManager_.addTrackingUrl(url);
577
+ }
578
+
579
+
563
580
  /**
564
581
  * @override
565
582
  * @export
@@ -705,8 +722,8 @@ shaka.ads.AdManager = class extends shaka.util.FakeEventTarget {
705
722
  onDashTimedMetadata(region) {
706
723
  if (this.ssAdManager_ && region.schemeIdUri == 'urn:google:dai:2018') {
707
724
  const type = region.schemeIdUri;
708
- const data = region.eventElement ?
709
- region.eventElement.getAttribute('messageData') : null;
725
+ const data = region.eventNode ?
726
+ region.eventNode.attributes['messageData'] : null;
710
727
  const timestamp = region.startTime;
711
728
  this.ssAdManager_.onTimedMetadata(type, data, timestamp);
712
729
  }
@@ -308,7 +308,7 @@ shaka.ads.ClientSideAdManager = class {
308
308
  // seen the ads actually play until requestAds() is called.
309
309
  // Note: We listen for a play event to avoid autoplay issues that might
310
310
  // crash IMA.
311
- if (this.videoPlayed_) {
311
+ if (this.videoPlayed_ || this.config_.skipPlayDetection) {
312
312
  this.imaAdsManager_.start();
313
313
  } else {
314
314
  this.eventManager_.listenOnce(this.video_, 'play', () => {
@@ -533,7 +533,9 @@ shaka.ads.ClientSideAdManager = class {
533
533
  shaka.ads.AdManager.AD_STARTED, data));
534
534
  if (this.ad_.isLinear()) {
535
535
  this.adContainer_.setAttribute('ad-active', 'true');
536
- this.video_.pause();
536
+ if (!this.config_.customPlayheadTracker) {
537
+ this.video_.pause();
538
+ }
537
539
  if (this.video_.muted) {
538
540
  this.ad_.setInitialMuted(this.video_.volume);
539
541
  } else {
@@ -551,7 +553,7 @@ shaka.ads.ClientSideAdManager = class {
551
553
  (new Map()).set('originalEvent', e)));
552
554
  if (this.ad_ && this.ad_.isLinear()) {
553
555
  this.adContainer_.removeAttribute('ad-active');
554
- if (!this.video_.ended) {
556
+ if (!this.config_.customPlayheadTracker && !this.video_.ended) {
555
557
  this.video_.play();
556
558
  }
557
559
  }
@@ -130,6 +130,15 @@ shaka.ads.MediaTailorAdManager = class {
130
130
  return this.streamPromise_;
131
131
  }
132
132
 
133
+ /**
134
+ * @param {string} url
135
+ */
136
+ addTrackingUrl(url) {
137
+ this.trackingUrl_ = url;
138
+ this.onEvent_(new shaka.util.FakeEvent(shaka.ads.AdManager.ADS_LOADED,
139
+ (new Map()).set('loadTime', 0)));
140
+ }
141
+
133
142
  /**
134
143
  * Resets the MediaTailor manager and removes any continuous polling.
135
144
  */
@@ -287,20 +287,6 @@ shaka.cast.CastReceiver = class extends shaka.util.FakeEventTarget {
287
287
  this.player_, name, (event) => this.proxyEvent_('player', event));
288
288
  }
289
289
 
290
- // In our tests, the original Chromecast seems to have trouble decoding
291
- // above 1080p. It would be a waste to select a higher res anyway, given
292
- // that the device only outputs 1080p to begin with.
293
-
294
- // Chromecast has an extension to query the device/display's resolution.
295
- if (cast.__platform__ && cast.__platform__.canDisplayType(
296
- 'video/mp4; codecs="avc1.640028"; width=3840; height=2160')) {
297
- // The device and display can both do 4k. Assume a 4k limit.
298
- this.player_.setMaxHardwareResolution(3840, 2160);
299
- } else {
300
- // Chromecast has always been able to do 1080p. Assume a 1080p limit.
301
- this.player_.setMaxHardwareResolution(1920, 1080);
302
- }
303
-
304
290
  // Do not start excluding values from update messages until the video is
305
291
  // fully loaded.
306
292
  this.eventManager_.listen(this.video_, 'loadeddata', () => {
@@ -355,6 +355,7 @@ shaka.cast.CastUtils.LargePlayerGetterMethods = {
355
355
  shaka.cast.CastUtils.PlayerGetterMethodsThatRequireLive = {
356
356
  'getPlayheadTimeAsDate': 1,
357
357
  'getPresentationStartTimeAsDate': 20,
358
+ 'getSegmentAvailabilityDuration': 20,
358
359
  };
359
360
 
360
361
 
@@ -107,6 +107,8 @@ shaka.cea.Mp4CeaParser = class {
107
107
  .box('avc3', codecBoxParser)
108
108
  .box('hev1', codecBoxParser)
109
109
  .box('hvc1', codecBoxParser)
110
+ .box('dvav', codecBoxParser)
111
+ .box('dva1', codecBoxParser)
110
112
  .box('dvh1', codecBoxParser)
111
113
  .box('dvhe', codecBoxParser)
112
114
 
@@ -374,7 +376,10 @@ shaka.cea.Mp4CeaParser.CodecBitstreamMap_ = {
374
376
  'avc3': shaka.cea.Mp4CeaParser.BitstreamFormat.H264,
375
377
  'hev1': shaka.cea.Mp4CeaParser.BitstreamFormat.H265,
376
378
  'hvc1': shaka.cea.Mp4CeaParser.BitstreamFormat.H265,
377
- // Dobly vision is also H265.
379
+ // Dolby Vision based in AVC
380
+ 'dvav': shaka.cea.Mp4CeaParser.BitstreamFormat.H264,
381
+ 'dva1': shaka.cea.Mp4CeaParser.BitstreamFormat.H264,
382
+ // Dolby Vision based in HEVC
378
383
  'dvh1': shaka.cea.Mp4CeaParser.BitstreamFormat.H265,
379
384
  'dvhe': shaka.cea.Mp4CeaParser.BitstreamFormat.H265,
380
385
  };
@@ -13,8 +13,8 @@ goog.require('shaka.util.Error');
13
13
  goog.require('shaka.util.ManifestParserUtils');
14
14
  goog.require('shaka.util.Pssh');
15
15
  goog.require('shaka.util.StringUtils');
16
+ goog.require('shaka.util.TXml');
16
17
  goog.require('shaka.util.Uint8ArrayUtils');
17
- goog.require('shaka.util.XmlUtils');
18
18
 
19
19
 
20
20
  /**
@@ -25,7 +25,7 @@ shaka.dash.ContentProtection = class {
25
25
  /**
26
26
  * Parses info from the ContentProtection elements at the AdaptationSet level.
27
27
  *
28
- * @param {!Array.<!Element>} elems
28
+ * @param {!Array.<!shaka.extern.xml.Node>} elems
29
29
  * @param {boolean} ignoreDrmInfo
30
30
  * @param {!Object.<string, string>} keySystemsByURI
31
31
  * @return {shaka.dash.ContentProtection.Context}
@@ -47,6 +47,8 @@ shaka.dash.ContentProtection = class {
47
47
  // Remove any possible null value (elements may have no key ids).
48
48
  keyIds.delete(null);
49
49
 
50
+ let encryptionScheme = 'cenc';
51
+
50
52
  if (keyIds.size > 1) {
51
53
  throw new shaka.util.Error(
52
54
  shaka.util.Error.Severity.CRITICAL,
@@ -70,6 +72,14 @@ shaka.dash.ContentProtection = class {
70
72
  aes128Info = ContentProtection.parseAes128_(aes128Elements[0]);
71
73
  }
72
74
 
75
+ const mp4ProtectionParsed = parsed.find((elem) => {
76
+ return elem.schemeUri == ContentProtection.MP4Protection_;
77
+ });
78
+
79
+ if (mp4ProtectionParsed && mp4ProtectionParsed.encryptionScheme) {
80
+ encryptionScheme = mp4ProtectionParsed.encryptionScheme;
81
+ }
82
+
73
83
  // Find the default key ID and init data. Create a new array of all the
74
84
  // non-CENC elements.
75
85
  parsedNonCenc = parsed.filter((elem) => {
@@ -84,13 +94,14 @@ shaka.dash.ContentProtection = class {
84
94
  });
85
95
 
86
96
  if (parsedNonCenc.length) {
87
- drmInfos = ContentProtection.convertElements_(
88
- defaultInit, parsedNonCenc, keySystemsByURI, keyIds);
97
+ drmInfos = ContentProtection.convertElements_(defaultInit,
98
+ encryptionScheme, parsedNonCenc, keySystemsByURI, keyIds);
89
99
 
90
100
  // If there are no drmInfos after parsing, then add a dummy entry.
91
101
  // This may be removed in parseKeyIds.
92
102
  if (drmInfos.length == 0) {
93
- drmInfos = [ManifestParserUtils.createDrmInfo('', defaultInit)];
103
+ drmInfos = [ManifestParserUtils.createDrmInfo(
104
+ '', encryptionScheme, defaultInit)];
94
105
  }
95
106
  }
96
107
  }
@@ -106,8 +117,8 @@ shaka.dash.ContentProtection = class {
106
117
  // put clearkey in this list. Otherwise, it may be triggered when
107
118
  // a real key system should be used instead.
108
119
  if (keySystem != 'org.w3.clearkey') {
109
- const info =
110
- ManifestParserUtils.createDrmInfo(keySystem, defaultInit);
120
+ const info = ManifestParserUtils.createDrmInfo(
121
+ keySystem, encryptionScheme, defaultInit);
111
122
  drmInfos.push(info);
112
123
  }
113
124
  }
@@ -137,7 +148,7 @@ shaka.dash.ContentProtection = class {
137
148
  * Parses the given ContentProtection elements found at the Representation
138
149
  * level. This may update the |context|.
139
150
  *
140
- * @param {!Array.<!Element>} elems
151
+ * @param {!Array.<!shaka.extern.xml.Node>} elems
141
152
  * @param {shaka.dash.ContentProtection.Context} context
142
153
  * @param {boolean} ignoreDrmInfo
143
154
  * @param {!Object.<string, string>} keySystemsByURI
@@ -191,17 +202,22 @@ shaka.dash.ContentProtection = class {
191
202
  * @return {string}
192
203
  */
193
204
  static getWidevineLicenseUrl(element) {
194
- const dashIfLaurlNode = shaka.util.XmlUtils.findChildNS(
205
+ const StringUtils = shaka.util.StringUtils;
206
+ const dashIfLaurlNode = shaka.util.TXml.findChildNS(
195
207
  element.node, shaka.dash.ContentProtection.DashIfNamespaceUri_,
196
208
  'Laurl',
197
209
  );
198
- if (dashIfLaurlNode && dashIfLaurlNode.textContent) {
199
- return dashIfLaurlNode.textContent;
210
+ if (dashIfLaurlNode) {
211
+ const textContents = shaka.util.TXml.getTextContents(dashIfLaurlNode);
212
+ if (textContents) {
213
+ return textContents;
214
+ }
200
215
  }
201
- const mslaurlNode = shaka.util.XmlUtils.findChildNS(
216
+ const mslaurlNode = shaka.util.TXml.findChildNS(
202
217
  element.node, 'urn:microsoft', 'laurl');
203
218
  if (mslaurlNode) {
204
- return mslaurlNode.getAttribute('licenseUrl') || '';
219
+ return StringUtils.htmlUnescape(
220
+ mslaurlNode.attributes['licenseUrl']) || '';
205
221
  }
206
222
  return '';
207
223
  }
@@ -214,21 +230,27 @@ shaka.dash.ContentProtection = class {
214
230
  * @return {string}
215
231
  */
216
232
  static getClearKeyLicenseUrl(element) {
217
- const dashIfLaurlNode = shaka.util.XmlUtils.findChildNS(
233
+ const dashIfLaurlNode = shaka.util.TXml.findChildNS(
218
234
  element.node, shaka.dash.ContentProtection.DashIfNamespaceUri_,
219
235
  'Laurl',
220
236
  );
221
- if (dashIfLaurlNode && dashIfLaurlNode.textContent) {
222
- return dashIfLaurlNode.textContent;
237
+ if (dashIfLaurlNode) {
238
+ const textContents = shaka.util.TXml.getTextContents(dashIfLaurlNode);
239
+ if (textContents) {
240
+ return textContents;
241
+ }
223
242
  }
224
- const clearKeyLaurlNode = shaka.util.XmlUtils.findChildNS(
243
+ const clearKeyLaurlNode = shaka.util.TXml.findChildNS(
225
244
  element.node, shaka.dash.ContentProtection.ClearKeyNamespaceUri_,
226
245
  'Laurl',
227
246
  );
228
247
  if (clearKeyLaurlNode &&
229
- clearKeyLaurlNode.getAttribute('Lic_type') === 'EME-1.0') {
230
- if (clearKeyLaurlNode.textContent) {
231
- return clearKeyLaurlNode.textContent;
248
+ clearKeyLaurlNode.attributes['Lic_type'] === 'EME-1.0') {
249
+ if (clearKeyLaurlNode) {
250
+ const textContents = shaka.util.TXml.getTextContents(clearKeyLaurlNode);
251
+ if (textContents) {
252
+ return textContents;
253
+ }
232
254
  }
233
255
  }
234
256
  return '';
@@ -312,18 +334,21 @@ shaka.dash.ContentProtection = class {
312
334
  /**
313
335
  * PlayReady Header format: https://goo.gl/dBzxNA
314
336
  *
315
- * @param {!Element} xml
337
+ * @param {!shaka.extern.xml.Node} xml
316
338
  * @return {string}
317
339
  * @private
318
340
  */
319
341
  static getLaurl_(xml) {
342
+ const TXml = shaka.util.TXml;
320
343
  // LA_URL element is optional and no more than one is
321
344
  // allowed inside the DATA element. Only absolute URLs are allowed.
322
345
  // If the LA_URL element exists, it must not be empty.
323
- for (const elem of xml.getElementsByTagName('DATA')) {
324
- for (const child of elem.childNodes) {
325
- if (child instanceof Element && child.tagName == 'LA_URL') {
326
- return child.textContent;
346
+ for (const elem of TXml.getElementsByTagName(xml, 'DATA')) {
347
+ if (elem.children) {
348
+ for (const child of elem.children) {
349
+ if (child.tagName == 'LA_URL') {
350
+ return /** @type{string} */(shaka.util.TXml.getTextContents(child));
351
+ }
327
352
  }
328
353
  }
329
354
  }
@@ -340,25 +365,30 @@ shaka.dash.ContentProtection = class {
340
365
  * @return {string}
341
366
  */
342
367
  static getPlayReadyLicenseUrl(element) {
343
- const dashIfLaurlNode = shaka.util.XmlUtils.findChildNS(
368
+ const dashIfLaurlNode = shaka.util.TXml.findChildNS(
344
369
  element.node, shaka.dash.ContentProtection.DashIfNamespaceUri_,
345
370
  'Laurl',
346
371
  );
347
- if (dashIfLaurlNode && dashIfLaurlNode.textContent) {
348
- return dashIfLaurlNode.textContent;
372
+ if (dashIfLaurlNode) {
373
+ const textContents = shaka.util.TXml.getTextContents(dashIfLaurlNode);
374
+ if (textContents) {
375
+ return textContents;
376
+ }
349
377
  }
350
378
 
351
- const proNode = shaka.util.XmlUtils.findChildNS(
379
+ const proNode = shaka.util.TXml.findChildNS(
352
380
  element.node, 'urn:microsoft:playready', 'pro');
353
381
 
354
- if (!proNode) {
382
+ if (!proNode || !shaka.util.TXml.getTextContents(proNode)) {
355
383
  return '';
356
384
  }
357
385
 
358
386
  const ContentProtection = shaka.dash.ContentProtection;
359
387
  const PLAYREADY_RECORD_TYPES = ContentProtection.PLAYREADY_RECORD_TYPES;
360
388
 
361
- const bytes = shaka.util.Uint8ArrayUtils.fromBase64(proNode.textContent);
389
+ const textContent =
390
+ /** @type {string} */ (shaka.util.TXml.getTextContents(proNode));
391
+ const bytes = shaka.util.Uint8ArrayUtils.fromBase64(textContent);
362
392
  const records = ContentProtection.parseMsPro_(bytes);
363
393
  const record = records.filter((record) => {
364
394
  return record.type === PLAYREADY_RECORD_TYPES.RIGHTS_MANAGEMENT;
@@ -369,7 +399,7 @@ shaka.dash.ContentProtection = class {
369
399
  }
370
400
 
371
401
  const xml = shaka.util.StringUtils.fromUTF16(record.value, true);
372
- const rootElement = shaka.util.XmlUtils.parseXmlString(xml, 'WRMHEADER');
402
+ const rootElement = shaka.util.TXml.parseXmlString(xml, 'WRMHEADER');
373
403
  if (!rootElement) {
374
404
  return '';
375
405
  }
@@ -386,13 +416,16 @@ shaka.dash.ContentProtection = class {
386
416
  * @private
387
417
  */
388
418
  static getInitDataFromPro_(element) {
389
- const proNode = shaka.util.XmlUtils.findChildNS(
419
+ const proNode = shaka.util.TXml.findChildNS(
390
420
  element.node, 'urn:microsoft:playready', 'pro');
391
- if (!proNode) {
421
+ if (!proNode || !shaka.util.TXml.getTextContents(proNode)) {
392
422
  return null;
393
423
  }
424
+
394
425
  const Uint8ArrayUtils = shaka.util.Uint8ArrayUtils;
395
- const data = Uint8ArrayUtils.fromBase64(proNode.textContent);
426
+ const textContent =
427
+ /** @type{string} */ (shaka.util.TXml.getTextContents(proNode));
428
+ const data = Uint8ArrayUtils.fromBase64(textContent);
396
429
  const systemId = new Uint8Array([
397
430
  0x9a, 0x04, 0xf0, 0x79, 0x98, 0x40, 0x42, 0x86,
398
431
  0xab, 0x92, 0xe6, 0x5b, 0xe0, 0x88, 0x5f, 0x95,
@@ -445,13 +478,15 @@ shaka.dash.ContentProtection = class {
445
478
  * Creates DrmInfo objects from the given element.
446
479
  *
447
480
  * @param {Array.<shaka.extern.InitDataOverride>} defaultInit
481
+ * @param {string} encryptionScheme
448
482
  * @param {!Array.<shaka.dash.ContentProtection.Element>} elements
449
483
  * @param {!Object.<string, string>} keySystemsByURI
450
484
  * @param {!Set.<string>} keyIds
451
485
  * @return {!Array.<shaka.extern.DrmInfo>}
452
486
  * @private
453
487
  */
454
- static convertElements_(defaultInit, elements, keySystemsByURI, keyIds) {
488
+ static convertElements_(defaultInit, encryptionScheme, elements,
489
+ keySystemsByURI, keyIds) {
455
490
  const ContentProtection = shaka.dash.ContentProtection;
456
491
  const ManifestParserUtils = shaka.util.ManifestParserUtils;
457
492
  const licenseUrlParsers = ContentProtection.licenseUrlParsers_;
@@ -475,7 +510,8 @@ shaka.dash.ContentProtection = class {
475
510
  }
476
511
  const initData = element.init || defaultInit || proInitData ||
477
512
  clearKeyInitData;
478
- const info = ManifestParserUtils.createDrmInfo(keySystem, initData);
513
+ const info = ManifestParserUtils.createDrmInfo(
514
+ keySystem, encryptionScheme, initData);
479
515
  const licenseParser = licenseUrlParsers.get(keySystem);
480
516
  if (licenseParser) {
481
517
  info.licenseServerUri = licenseParser(element);
@@ -492,7 +528,7 @@ shaka.dash.ContentProtection = class {
492
528
  * Parses the given ContentProtection elements. If there is an error, it
493
529
  * removes those elements.
494
530
  *
495
- * @param {!Array.<!Element>} elems
531
+ * @param {!Array.<!shaka.extern.xml.Node>} elems
496
532
  * @return {!Array.<shaka.dash.ContentProtection.Element>}
497
533
  * @private
498
534
  */
@@ -513,20 +549,23 @@ shaka.dash.ContentProtection = class {
513
549
  /**
514
550
  * Parses the given ContentProtection element.
515
551
  *
516
- * @param {!Element} elem
552
+ * @param {!shaka.extern.xml.Node} elem
517
553
  * @return {?shaka.dash.ContentProtection.Element}
518
554
  * @private
519
555
  */
520
556
  static parseElement_(elem) {
521
557
  const NS = shaka.dash.ContentProtection.CencNamespaceUri_;
558
+ const TXml = shaka.util.TXml;
522
559
 
523
560
  /** @type {?string} */
524
- let schemeUri = elem.getAttribute('schemeIdUri');
561
+ let schemeUri = elem.attributes['schemeIdUri'];
525
562
  /** @type {?string} */
526
- let keyId = shaka.util.XmlUtils.getAttributeNS(elem, NS, 'default_KID');
563
+ let keyId = TXml.getAttributeNS(elem, NS, 'default_KID');
527
564
  /** @type {!Array.<string>} */
528
- const psshs = shaka.util.XmlUtils.findChildrenNS(elem, NS, 'pssh')
529
- .map(shaka.util.XmlUtils.getContents);
565
+ const psshs = TXml.findChildrenNS(elem, NS, 'pssh')
566
+ .map(TXml.getContents);
567
+
568
+ const encryptionScheme = elem.attributes['value'];
530
569
 
531
570
  if (!schemeUri) {
532
571
  shaka.log.error('Missing required schemeIdUri attribute on',
@@ -565,9 +604,10 @@ shaka.dash.ContentProtection = class {
565
604
 
566
605
  return {
567
606
  node: elem,
568
- schemeUri: schemeUri,
569
- keyId: keyId,
607
+ schemeUri,
608
+ keyId,
570
609
  init: (init.length > 0 ? init : null),
610
+ encryptionScheme,
571
611
  };
572
612
  }
573
613
 
@@ -590,7 +630,7 @@ shaka.dash.ContentProtection = class {
590
630
  }
591
631
 
592
632
  const namespace = 'urn:mpeg:dash:schema:sea:2012';
593
- const segmentEncryption = shaka.util.XmlUtils.findChildNS(
633
+ const segmentEncryption = shaka.util.TXml.findChildNS(
594
634
  element.node, namespace, 'SegmentEncryption');
595
635
 
596
636
  if (!segmentEncryption) {
@@ -602,7 +642,7 @@ shaka.dash.ContentProtection = class {
602
642
 
603
643
  const aesSchemeIdUri = 'urn:mpeg:dash:sea:aes128-cbc:2013';
604
644
  const segmentEncryptionSchemeIdUri =
605
- segmentEncryption.getAttribute('schemeIdUri');
645
+ segmentEncryption.attributes['schemeIdUri'];
606
646
  if (segmentEncryptionSchemeIdUri != aesSchemeIdUri) {
607
647
  throw new shaka.util.Error(
608
648
  shaka.util.Error.Severity.CRITICAL,
@@ -610,7 +650,7 @@ shaka.dash.ContentProtection = class {
610
650
  shaka.util.Error.Code.DASH_UNSUPPORTED_AES_128);
611
651
  }
612
652
 
613
- const cryptoPeriod = shaka.util.XmlUtils.findChildNS(
653
+ const cryptoPeriod = shaka.util.TXml.findChildNS(
614
654
  element.node, namespace, 'CryptoPeriod');
615
655
 
616
656
  if (!cryptoPeriod) {
@@ -620,8 +660,9 @@ shaka.dash.ContentProtection = class {
620
660
  shaka.util.Error.Code.DASH_UNSUPPORTED_AES_128);
621
661
  }
622
662
 
623
- const ivHex = cryptoPeriod.getAttribute('IV');
624
- const keyUri = cryptoPeriod.getAttribute('keyUriTemplate');
663
+ const ivHex = cryptoPeriod.attributes['IV'];
664
+ const keyUri = shaka.util.StringUtils.htmlUnescape(
665
+ cryptoPeriod.attributes['keyUriTemplate']);
625
666
  if (!ivHex || !keyUri) {
626
667
  throw new shaka.util.Error(
627
668
  shaka.util.Error.Severity.CRITICAL,
@@ -721,16 +762,17 @@ shaka.dash.ContentProtection.Aes128Info;
721
762
 
722
763
  /**
723
764
  * @typedef {{
724
- * node: !Element,
765
+ * node: !shaka.extern.xml.Node,
725
766
  * schemeUri: string,
726
767
  * keyId: ?string,
727
- * init: Array.<shaka.extern.InitDataOverride>
768
+ * init: Array.<shaka.extern.InitDataOverride>,
769
+ * encryptionScheme: ?string
728
770
  * }}
729
771
  *
730
772
  * @description
731
773
  * The parsed result of a single ContentProtection element.
732
774
  *
733
- * @property {!Element} node
775
+ * @property {!shaka.extern.xml.Node} node
734
776
  * The ContentProtection XML element.
735
777
  * @property {string} schemeUri
736
778
  * The scheme URI.
@@ -739,6 +781,8 @@ shaka.dash.ContentProtection.Aes128Info;
739
781
  * @property {Array.<shaka.extern.InitDataOverride>} init
740
782
  * The init data, if present. If there is no init data, it will be null. If
741
783
  * this is non-null, there is at least one element.
784
+ * @property {?string} encryptionScheme
785
+ * The encryption scheme, if present.
742
786
  */
743
787
  shaka.dash.ContentProtection.Element;
744
788