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
@@ -15,7 +15,7 @@ goog.require('shaka.text.TextEngine');
15
15
  goog.require('shaka.util.ArrayUtils');
16
16
  goog.require('shaka.util.Error');
17
17
  goog.require('shaka.util.StringUtils');
18
- goog.require('shaka.util.XmlUtils');
18
+ goog.require('shaka.util.TXml');
19
19
 
20
20
 
21
21
  /**
@@ -53,7 +53,7 @@ shaka.text.TtmlTextParser = class {
53
53
  */
54
54
  parseMedia(data, time, uri) {
55
55
  const TtmlTextParser = shaka.text.TtmlTextParser;
56
- const XmlUtils = shaka.util.XmlUtils;
56
+ const TXml = shaka.util.TXml;
57
57
  const ttpNs = TtmlTextParser.parameterNs_;
58
58
  const ttsNs = TtmlTextParser.styleNs_;
59
59
  const str = shaka.util.StringUtils.fromUTF8(data);
@@ -65,7 +65,7 @@ shaka.text.TtmlTextParser = class {
65
65
  return cues;
66
66
  }
67
67
 
68
- const tt = XmlUtils.parseXmlString(str, 'tt');
68
+ const tt = TXml.parseXmlString(str, 'tt');
69
69
  if (!tt) {
70
70
  throw new shaka.util.Error(
71
71
  shaka.util.Error.Severity.CRITICAL,
@@ -74,23 +74,23 @@ shaka.text.TtmlTextParser = class {
74
74
  'Failed to parse TTML.');
75
75
  }
76
76
 
77
- const body = tt.getElementsByTagName('body')[0];
77
+ const body = TXml.getElementsByTagName(tt, 'body')[0];
78
78
  if (!body) {
79
79
  return [];
80
80
  }
81
81
 
82
82
  // Get the framerate, subFrameRate and frameRateMultiplier if applicable.
83
- const frameRate = XmlUtils.getAttributeNSList(tt, ttpNs, 'frameRate');
84
- const subFrameRate = XmlUtils.getAttributeNSList(
83
+ const frameRate = TXml.getAttributeNSList(tt, ttpNs, 'frameRate');
84
+ const subFrameRate = TXml.getAttributeNSList(
85
85
  tt, ttpNs, 'subFrameRate');
86
86
  const frameRateMultiplier =
87
- XmlUtils.getAttributeNSList(tt, ttpNs, 'frameRateMultiplier');
88
- const tickRate = XmlUtils.getAttributeNSList(tt, ttpNs, 'tickRate');
87
+ TXml.getAttributeNSList(tt, ttpNs, 'frameRateMultiplier');
88
+ const tickRate = TXml.getAttributeNSList(tt, ttpNs, 'tickRate');
89
89
 
90
- const cellResolution = XmlUtils.getAttributeNSList(
90
+ const cellResolution = TXml.getAttributeNSList(
91
91
  tt, ttpNs, 'cellResolution');
92
- const spaceStyle = tt.getAttribute('xml:space') || 'default';
93
- const extent = XmlUtils.getAttributeNSList(tt, ttsNs, 'extent');
92
+ const spaceStyle = tt.attributes['xml:space'] || 'default';
93
+ const extent = TXml.getAttributeNSList(tt, ttsNs, 'extent');
94
94
 
95
95
  if (spaceStyle != 'default' && spaceStyle != 'preserve') {
96
96
  throw new shaka.util.Error(
@@ -99,7 +99,7 @@ shaka.text.TtmlTextParser = class {
99
99
  shaka.util.Error.Code.INVALID_XML,
100
100
  'Invalid xml:space value: ' + spaceStyle);
101
101
  }
102
- const whitespaceTrim = spaceStyle == 'default';
102
+ const collapseMultipleSpaces = spaceStyle == 'default';
103
103
 
104
104
  const rateInfo = new TtmlTextParser.RateInfo_(
105
105
  frameRate, subFrameRate, frameRateMultiplier, tickRate);
@@ -107,10 +107,10 @@ shaka.text.TtmlTextParser = class {
107
107
  const cellResolutionInfo =
108
108
  TtmlTextParser.getCellResolution_(cellResolution);
109
109
 
110
- const metadata = tt.getElementsByTagName('metadata')[0];
111
- const metadataElements = metadata ? XmlUtils.getChildren(metadata) : [];
112
- const styles = Array.from(tt.getElementsByTagName('style'));
113
- const regionElements = Array.from(tt.getElementsByTagName('region'));
110
+ const metadata = TXml.getElementsByTagName(tt, 'metadata')[0];
111
+ const metadataElements = metadata ? metadata.children : [];
112
+ const styles = TXml.getElementsByTagName(tt, 'style');
113
+ const regionElements = TXml.getElementsByTagName(tt, 'region');
114
114
 
115
115
  const cueRegions = [];
116
116
  for (const region of regionElements) {
@@ -125,7 +125,7 @@ shaka.text.TtmlTextParser = class {
125
125
  // elements. We used to allow this, but it is non-compliant, and the
126
126
  // loose nature of our previous parser made it difficult to implement TTML
127
127
  // nesting more fully.
128
- if (XmlUtils.findChildren(body, 'p').length) {
128
+ if (TXml.findChildren(body, 'p').length) {
129
129
  throw new shaka.util.Error(
130
130
  shaka.util.Error.Severity.CRITICAL,
131
131
  shaka.util.Error.Category.TEXT,
@@ -133,9 +133,9 @@ shaka.text.TtmlTextParser = class {
133
133
  '<p> can only be inside <div> in TTML');
134
134
  }
135
135
 
136
- for (const div of XmlUtils.findChildren(body, 'div')) {
136
+ for (const div of TXml.findChildren(body, 'div')) {
137
137
  // A <div> element should only contain <p>, not <span>.
138
- if (XmlUtils.findChildren(div, 'span').length) {
138
+ if (TXml.findChildren(div, 'span').length) {
139
139
  throw new shaka.util.Error(
140
140
  shaka.util.Error.Severity.CRITICAL,
141
141
  shaka.util.Error.Category.TEXT,
@@ -146,7 +146,7 @@ shaka.text.TtmlTextParser = class {
146
146
 
147
147
  const cue = TtmlTextParser.parseCue_(
148
148
  body, time, rateInfo, metadataElements, styles,
149
- regionElements, cueRegions, whitespaceTrim,
149
+ regionElements, cueRegions, collapseMultipleSpaces,
150
150
  cellResolutionInfo, /* parentCueElement= */ null,
151
151
  /* isContent= */ false, uri);
152
152
  if (cue) {
@@ -165,16 +165,16 @@ shaka.text.TtmlTextParser = class {
165
165
  /**
166
166
  * Parses a TTML node into a Cue.
167
167
  *
168
- * @param {!Node} cueNode
168
+ * @param {!shaka.extern.xml.Node} cueNode
169
169
  * @param {shaka.extern.TextParser.TimeContext} timeContext
170
170
  * @param {!shaka.text.TtmlTextParser.RateInfo_} rateInfo
171
- * @param {!Array.<!Element>} metadataElements
172
- * @param {!Array.<!Element>} styles
173
- * @param {!Array.<!Element>} regionElements
171
+ * @param {!Array.<!shaka.extern.xml.Node>} metadataElements
172
+ * @param {!Array.<!shaka.extern.xml.Node>} styles
173
+ * @param {!Array.<!shaka.extern.xml.Node>} regionElements
174
174
  * @param {!Array.<!shaka.text.CueRegion>} cueRegions
175
- * @param {boolean} whitespaceTrim
175
+ * @param {boolean} collapseMultipleSpaces
176
176
  * @param {?{columns: number, rows: number}} cellResolution
177
- * @param {?Element} parentCueElement
177
+ * @param {?shaka.extern.xml.Node} parentCueElement
178
178
  * @param {boolean} isContent
179
179
  * @param {?(string|undefined)} uri
180
180
  * @return {shaka.text.Cue}
@@ -182,19 +182,16 @@ shaka.text.TtmlTextParser = class {
182
182
  */
183
183
  static parseCue_(
184
184
  cueNode, timeContext, rateInfo, metadataElements, styles, regionElements,
185
- cueRegions, whitespaceTrim, cellResolution, parentCueElement, isContent,
186
- uri) {
187
- /** @type {Element} */
185
+ cueRegions, collapseMultipleSpaces, cellResolution, parentCueElement,
186
+ isContent, uri) {
187
+ const TXml = shaka.util.TXml;
188
+ const StringUtils = shaka.util.StringUtils;
189
+ /** @type {shaka.extern.xml.Node} */
188
190
  let cueElement;
189
- /** @type {Element} */
190
- let parentElement = /** @type {Element} */ (cueNode.parentNode);
191
+ /** @type {?shaka.extern.xml.Node} */
192
+ let parentElement = parentCueElement;
191
193
 
192
- if (cueNode.nodeType == Node.COMMENT_NODE) {
193
- // The comments do not contain information that interests us here.
194
- return null;
195
- }
196
-
197
- if (cueNode.nodeType == Node.TEXT_NODE) {
194
+ if (TXml.isText(cueNode)) {
198
195
  if (!isContent) {
199
196
  // Ignore text elements outside the content. For example, whitespace
200
197
  // on the same lexical level as the <p> elements, in a document with
@@ -205,13 +202,16 @@ shaka.text.TtmlTextParser = class {
205
202
  // So pretend the element was a <span>. parentElement was set above, so
206
203
  // we should still be able to correctly traverse up for timing
207
204
  // information later.
208
- const span = document.createElement('span');
209
- span.textContent = cueNode.textContent;
205
+ /** @type {shaka.extern.xml.Node} */
206
+ const span = {
207
+ tagName: 'span',
208
+ children: [TXml.getTextContents(cueNode)],
209
+ attributes: {},
210
+ parent: null,
211
+ };
210
212
  cueElement = span;
211
213
  } else {
212
- goog.asserts.assert(cueNode.nodeType == Node.ELEMENT_NODE,
213
- 'nodeType should be ELEMENT_NODE!');
214
- cueElement = /** @type {!Element} */(cueNode);
214
+ cueElement = cueNode;
215
215
  }
216
216
  goog.asserts.assert(cueElement, 'cueElement should be non-null!');
217
217
 
@@ -226,7 +226,7 @@ shaka.text.TtmlTextParser = class {
226
226
  }
227
227
 
228
228
  let imageUri = null;
229
- const backgroundImage = shaka.util.XmlUtils.getAttributeNSList(
229
+ const backgroundImage = TXml.getAttributeNSList(
230
230
  cueElement,
231
231
  shaka.text.TtmlTextParser.smpteNsList_,
232
232
  'backgroundImage');
@@ -239,27 +239,24 @@ shaka.text.TtmlTextParser = class {
239
239
  }
240
240
  }
241
241
 
242
- if (cueNode.nodeName == 'p' || imageElement || imageUri) {
242
+ if (cueNode.tagName == 'p' || imageElement || imageUri) {
243
243
  isContent = true;
244
244
  }
245
245
 
246
246
  const parentIsContent = isContent;
247
247
 
248
- const spaceStyle = cueElement.getAttribute('xml:space') ||
249
- (whitespaceTrim ? 'default' : 'preserve');
248
+ const spaceStyle = cueElement.attributes['xml:space'] ||
249
+ (collapseMultipleSpaces ? 'default' : 'preserve');
250
250
 
251
- const localWhitespaceTrim = spaceStyle == 'default';
251
+ const localCollapseMultipleSpaces = spaceStyle == 'default';
252
252
 
253
253
  // Parse any nested cues first.
254
- const isTextNode = (node) => {
255
- return node.nodeType == Node.TEXT_NODE;
256
- };
257
- const isLeafNode = Array.from(cueElement.childNodes).every(isTextNode);
254
+ const isLeafNode = cueElement.children.every(TXml.isText);
258
255
  const nestedCues = [];
259
256
  if (!isLeafNode) {
260
257
  // Otherwise, recurse into the children. Text nodes will convert into
261
258
  // anonymous spans, which will then be leaf nodes.
262
- for (const childNode of cueElement.childNodes) {
259
+ for (const childNode of cueElement.children) {
263
260
  const nestedCue = shaka.text.TtmlTextParser.parseCue_(
264
261
  childNode,
265
262
  timeContext,
@@ -268,7 +265,7 @@ shaka.text.TtmlTextParser = class {
268
265
  styles,
269
266
  regionElements,
270
267
  cueRegions,
271
- localWhitespaceTrim,
268
+ localCollapseMultipleSpaces,
272
269
  cellResolution,
273
270
  cueElement,
274
271
  isContent,
@@ -284,12 +281,16 @@ shaka.text.TtmlTextParser = class {
284
281
 
285
282
  const isNested = /** @type {boolean} */ (parentCueElement != null);
286
283
 
284
+ const textContent = TXml.getTextContents(cueElement);
287
285
  // In this regex, "\S" means "non-whitespace character".
288
- const hasTextContent = /\S/.test(cueElement.textContent);
286
+ const hasTextContent = cueElement.children.length &&
287
+ textContent &&
288
+ /\S/.test(textContent);
289
+
289
290
  const hasTimeAttributes =
290
- cueElement.hasAttribute('begin') ||
291
- cueElement.hasAttribute('end') ||
292
- cueElement.hasAttribute('dur');
291
+ cueElement.attributes['begin'] ||
292
+ cueElement.attributes['end'] ||
293
+ cueElement.attributes['dur'];
293
294
 
294
295
  if (!hasTimeAttributes && !hasTextContent && cueElement.tagName != 'br' &&
295
296
  nestedCues.length == 0) {
@@ -299,7 +300,7 @@ shaka.text.TtmlTextParser = class {
299
300
  // as some information could be held by its attributes.
300
301
  // <p /> won't, as it would not be displayed.
301
302
  return null;
302
- } else if (localWhitespaceTrim) {
303
+ } else if (localCollapseMultipleSpaces) {
303
304
  // Disregards empty anonymous spans when (local) trim is true.
304
305
  return null;
305
306
  }
@@ -310,11 +311,12 @@ shaka.text.TtmlTextParser = class {
310
311
  cueElement, rateInfo);
311
312
  // Resolve local time relative to parent elements. Time elements can appear
312
313
  // all the way up to 'body', but not 'tt'.
313
- while (parentElement && parentElement.nodeType == Node.ELEMENT_NODE &&
314
+ while (parentElement && TXml.isNode(parentElement) &&
314
315
  parentElement.tagName != 'tt') {
315
316
  ({start, end} = shaka.text.TtmlTextParser.resolveTime_(
316
317
  parentElement, rateInfo, start, end));
317
- parentElement = /** @type {Element} */(parentElement.parentNode);
318
+ parentElement =
319
+ /** @type {shaka.extern.xml.Node} */ (parentElement.parent);
318
320
  }
319
321
 
320
322
  if (start == null) {
@@ -357,10 +359,9 @@ shaka.text.TtmlTextParser = class {
357
359
  let payload = '';
358
360
  if (isLeafNode) {
359
361
  // If the childNodes are all text, this is a leaf node. Get the payload.
360
- payload = cueElement.textContent;
361
- if (localWhitespaceTrim) {
362
- // Trim leading and trailing whitespace.
363
- payload = payload.trim();
362
+ payload = StringUtils.htmlUnescape(
363
+ shaka.util.TXml.getTextContents(cueElement) || '');
364
+ if (localCollapseMultipleSpaces) {
364
365
  // Collapse multiple spaces into one.
365
366
  payload = payload.replace(/\s+/g, ' ');
366
367
  }
@@ -386,16 +387,16 @@ shaka.text.TtmlTextParser = class {
386
387
  // Do not actually apply that region unless it is non-inherited, though.
387
388
  // This makes it so that, if a parent element has a region, the children
388
389
  // don't also all independently apply the positioning of that region.
389
- if (cueElement.hasAttribute('region')) {
390
- if (regionElement && regionElement.getAttribute('xml:id')) {
391
- const regionId = regionElement.getAttribute('xml:id');
390
+ if (cueElement.attributes['region']) {
391
+ if (regionElement && regionElement.attributes['xml:id']) {
392
+ const regionId = regionElement.attributes['xml:id'];
392
393
  cue.region = cueRegions.filter((region) => region.id == regionId)[0];
393
394
  }
394
395
  }
395
396
 
396
397
  let regionElementForStyle = regionElement;
397
- if (parentCueElement && isNested && !cueElement.getAttribute('region') &&
398
- !cueElement.getAttribute('style')) {
398
+ if (parentCueElement && isNested && !cueElement.attributes['region'] &&
399
+ !cueElement.attributes['style']) {
399
400
  regionElementForStyle =
400
401
  shaka.text.TtmlTextParser.getElementsFromCollection_(
401
402
  parentCueElement, 'region', regionElements, /* prefix= */ '')[0];
@@ -405,7 +406,7 @@ shaka.text.TtmlTextParser = class {
405
406
  cue,
406
407
  cueElement,
407
408
  regionElementForStyle,
408
- imageElement,
409
+ /** @type {!shaka.extern.xml.Node} */(imageElement),
409
410
  imageUri,
410
411
  styles,
411
412
  /** isNested= */ parentIsContent, // "nested in a <div>" doesn't count.
@@ -417,9 +418,9 @@ shaka.text.TtmlTextParser = class {
417
418
  /**
418
419
  * Parses an Element into a TextTrackCue or VTTCue.
419
420
  *
420
- * @param {!Element} regionElement
421
- * @param {!Array.<!Element>} styles Defined in the top of tt element and
422
- * used principally for images.
421
+ * @param {!shaka.extern.xml.Node} regionElement
422
+ * @param {!Array.<!shaka.extern.xml.Node>} styles
423
+ * Defined in the top of tt element and used principally for images.
423
424
  * @param {?string} globalExtent
424
425
  * @return {shaka.text.CueRegion}
425
426
  * @private
@@ -427,7 +428,7 @@ shaka.text.TtmlTextParser = class {
427
428
  static parseCueRegion_(regionElement, styles, globalExtent) {
428
429
  const TtmlTextParser = shaka.text.TtmlTextParser;
429
430
  const region = new shaka.text.CueRegion();
430
- const id = regionElement.getAttribute('xml:id');
431
+ const id = regionElement.attributes['xml:id'];
431
432
  if (!id) {
432
433
  shaka.log.warning('TtmlTextParser parser encountered a region with ' +
433
434
  'no id. Region will be ignored.');
@@ -511,11 +512,11 @@ shaka.text.TtmlTextParser = class {
511
512
  * Adds applicable style properties to a cue.
512
513
  *
513
514
  * @param {!shaka.text.Cue} cue
514
- * @param {!Element} cueElement
515
- * @param {Element} region
516
- * @param {Element} imageElement
515
+ * @param {!shaka.extern.xml.Node} cueElement
516
+ * @param {shaka.extern.xml.Node} region
517
+ * @param {shaka.extern.xml.Node} imageElement
517
518
  * @param {?string} imageUri
518
- * @param {!Array.<!Element>} styles
519
+ * @param {!Array.<!shaka.extern.xml.Node>} styles
519
520
  * @param {boolean} isNested
520
521
  * @param {boolean} isLeaf
521
522
  * @private
@@ -524,6 +525,7 @@ shaka.text.TtmlTextParser = class {
524
525
  cue, cueElement, region, imageElement, imageUri, styles,
525
526
  isNested, isLeaf) {
526
527
  const TtmlTextParser = shaka.text.TtmlTextParser;
528
+ const TXml = shaka.util.TXml;
527
529
  const Cue = shaka.text.Cue;
528
530
 
529
531
  // Styles should be inherited from regions, if a style property is not
@@ -681,10 +683,10 @@ shaka.text.TtmlTextParser = class {
681
683
  // in PR #1859, in April 2019, and first released in v2.5.0.
682
684
  // Now we check for both, although only imageType (camelCase) is to spec.
683
685
  const backgroundImageType =
684
- imageElement.getAttribute('imageType') ||
685
- imageElement.getAttribute('imagetype');
686
- const backgroundImageEncoding = imageElement.getAttribute('encoding');
687
- const backgroundImageData = imageElement.textContent.trim();
686
+ imageElement.attributes['imageType'] ||
687
+ imageElement.attributes['imagetype'];
688
+ const backgroundImageEncoding = imageElement.attributes['encoding'];
689
+ const backgroundImageData = (TXml.getTextContents(imageElement)).trim();
688
690
  if (backgroundImageType == 'PNG' &&
689
691
  backgroundImageEncoding == 'Base64' &&
690
692
  backgroundImageData) {
@@ -823,9 +825,9 @@ shaka.text.TtmlTextParser = class {
823
825
  * Finds a specified attribute on either the original cue element or its
824
826
  * associated region and returns the value if the attribute was found.
825
827
  *
826
- * @param {!Element} cueElement
827
- * @param {Element} region
828
- * @param {!Array.<!Element>} styles
828
+ * @param {!shaka.extern.xml.Node} cueElement
829
+ * @param {shaka.extern.xml.Node} region
830
+ * @param {!Array.<!shaka.extern.xml.Node>} styles
829
831
  * @param {string} attribute
830
832
  * @param {boolean=} shouldInheritRegionStyles
831
833
  * @return {?string}
@@ -853,21 +855,21 @@ shaka.text.TtmlTextParser = class {
853
855
  * Finds a specified attribute on the element's associated region
854
856
  * and returns the value if the attribute was found.
855
857
  *
856
- * @param {Element} region
857
- * @param {!Array.<!Element>} styles
858
+ * @param {shaka.extern.xml.Node} region
859
+ * @param {!Array.<!shaka.extern.xml.Node>} styles
858
860
  * @param {string} attribute
859
861
  * @return {?string}
860
862
  * @private
861
863
  */
862
864
  static getStyleAttributeFromRegion_(region, styles, attribute) {
863
- const XmlUtils = shaka.util.XmlUtils;
865
+ const TXml = shaka.util.TXml;
864
866
  const ttsNs = shaka.text.TtmlTextParser.styleNs_;
865
867
 
866
868
  if (!region) {
867
869
  return null;
868
870
  }
869
871
 
870
- const attr = XmlUtils.getAttributeNSList(region, ttsNs, attribute);
872
+ const attr = TXml.getAttributeNSList(region, ttsNs, attribute);
871
873
  if (attr) {
872
874
  return attr;
873
875
  }
@@ -880,19 +882,19 @@ shaka.text.TtmlTextParser = class {
880
882
  * Finds a specified attribute on the cue element and returns the value
881
883
  * if the attribute was found.
882
884
  *
883
- * @param {!Element} cueElement
884
- * @param {!Array.<!Element>} styles
885
+ * @param {!shaka.extern.xml.Node} cueElement
886
+ * @param {!Array.<!shaka.extern.xml.Node>} styles
885
887
  * @param {string} attribute
886
888
  * @return {?string}
887
889
  * @private
888
890
  */
889
891
  static getStyleAttributeFromElement_(cueElement, styles, attribute) {
890
- const XmlUtils = shaka.util.XmlUtils;
892
+ const TXml = shaka.util.TXml;
891
893
  const ttsNs = shaka.text.TtmlTextParser.styleNs_;
892
894
 
893
895
  // Styling on elements should take precedence
894
896
  // over the main styling attributes
895
- const elementAttribute = XmlUtils.getAttributeNSList(
897
+ const elementAttribute = TXml.getAttributeNSList(
896
898
  cueElement,
897
899
  ttsNs,
898
900
  attribute);
@@ -908,14 +910,14 @@ shaka.text.TtmlTextParser = class {
908
910
  * Finds a specified attribute on an element's styles and the styles those
909
911
  * styles inherit from.
910
912
  *
911
- * @param {!Element} element
912
- * @param {!Array.<!Element>} styles
913
+ * @param {!shaka.extern.xml.Node} element
914
+ * @param {!Array.<!shaka.extern.xml.Node>} styles
913
915
  * @param {string} attribute
914
916
  * @return {?string}
915
917
  * @private
916
918
  */
917
919
  static getInheritedStyleAttribute_(element, styles, attribute) {
918
- const XmlUtils = shaka.util.XmlUtils;
920
+ const TXml = shaka.util.TXml;
919
921
  const ttsNs = shaka.text.TtmlTextParser.styleNs_;
920
922
  const ebuttsNs = shaka.text.TtmlTextParser.styleEbuttsNs_;
921
923
 
@@ -928,14 +930,14 @@ shaka.text.TtmlTextParser = class {
928
930
  // The last value in our styles stack takes the precedence over the others
929
931
  for (let i = 0; i < inheritedStyles.length; i++) {
930
932
  // Check ebu namespace first.
931
- let styleAttributeValue = XmlUtils.getAttributeNS(
933
+ let styleAttributeValue = TXml.getAttributeNS(
932
934
  inheritedStyles[i],
933
935
  ebuttsNs,
934
936
  attribute);
935
937
 
936
938
  if (!styleAttributeValue) {
937
939
  // Fall back to tts namespace.
938
- styleAttributeValue = XmlUtils.getAttributeNSList(
940
+ styleAttributeValue = TXml.getAttributeNSList(
939
941
  inheritedStyles[i],
940
942
  ttsNs,
941
943
  attribute);
@@ -962,12 +964,12 @@ shaka.text.TtmlTextParser = class {
962
964
  * Selects items from |collection| whose id matches |attributeName|
963
965
  * from |element|.
964
966
  *
965
- * @param {Element} element
967
+ * @param {shaka.extern.xml.Node} element
966
968
  * @param {string} attributeName
967
- * @param {!Array.<Element>} collection
969
+ * @param {!Array.<shaka.extern.xml.Node>} collection
968
970
  * @param {string} prefixName
969
971
  * @param {string=} nsName
970
- * @return {!Array.<!Element>}
972
+ * @return {!Array.<!shaka.extern.xml.Node>}
971
973
  * @private
972
974
  */
973
975
  static getElementsFromCollection_(
@@ -988,7 +990,7 @@ shaka.text.TtmlTextParser = class {
988
990
 
989
991
  for (const name of itemNames) {
990
992
  for (const item of collection) {
991
- if ((prefixName + item.getAttribute('xml:id')) == name) {
993
+ if ((prefixName + item.attributes['xml:id']) == name) {
992
994
  items.push(item);
993
995
  break;
994
996
  }
@@ -1003,7 +1005,7 @@ shaka.text.TtmlTextParser = class {
1003
1005
  /**
1004
1006
  * Traverses upwards from a given node until a given attribute is found.
1005
1007
  *
1006
- * @param {!Element} element
1008
+ * @param {!shaka.extern.xml.Node} element
1007
1009
  * @param {string} attributeName
1008
1010
  * @param {string=} nsName
1009
1011
  * @return {?string}
@@ -1011,19 +1013,19 @@ shaka.text.TtmlTextParser = class {
1011
1013
  */
1012
1014
  static getInheritedAttribute_(element, attributeName, nsName) {
1013
1015
  let ret = null;
1014
- const XmlUtils = shaka.util.XmlUtils;
1015
- while (element) {
1016
+ const TXml = shaka.util.TXml;
1017
+ while (!ret) {
1016
1018
  ret = nsName ?
1017
- XmlUtils.getAttributeNS(element, nsName, attributeName) :
1018
- element.getAttribute(attributeName);
1019
+ TXml.getAttributeNS(element, nsName, attributeName) :
1020
+ element.attributes[attributeName];
1019
1021
  if (ret) {
1020
1022
  break;
1021
1023
  }
1022
1024
 
1023
1025
  // Element.parentNode can lead to XMLDocument, which is not an Element and
1024
1026
  // has no getAttribute().
1025
- const parentNode = element.parentNode;
1026
- if (parentNode instanceof Element) {
1027
+ const parentNode = element.parent;
1028
+ if (parentNode) {
1027
1029
  element = parentNode;
1028
1030
  } else {
1029
1031
  break;
@@ -1036,7 +1038,7 @@ shaka.text.TtmlTextParser = class {
1036
1038
  * Factor parent/ancestor time attributes into the parsed time of a
1037
1039
  * child/descendent.
1038
1040
  *
1039
- * @param {!Element} parentElement
1041
+ * @param {!shaka.extern.xml.Node} parentElement
1040
1042
  * @param {!shaka.text.TtmlTextParser.RateInfo_} rateInfo
1041
1043
  * @param {?number} start The child's start time
1042
1044
  * @param {?number} end The child's end time
@@ -1074,18 +1076,18 @@ shaka.text.TtmlTextParser = class {
1074
1076
  /**
1075
1077
  * Parse TTML time attributes from the given element.
1076
1078
  *
1077
- * @param {!Element} element
1079
+ * @param {!shaka.extern.xml.Node} element
1078
1080
  * @param {!shaka.text.TtmlTextParser.RateInfo_} rateInfo
1079
1081
  * @return {{start: ?number, end: ?number}}
1080
1082
  * @private
1081
1083
  */
1082
1084
  static parseTime_(element, rateInfo) {
1083
1085
  const start = shaka.text.TtmlTextParser.parseTimeAttribute_(
1084
- element.getAttribute('begin'), rateInfo);
1086
+ element.attributes['begin'], rateInfo);
1085
1087
  let end = shaka.text.TtmlTextParser.parseTimeAttribute_(
1086
- element.getAttribute('end'), rateInfo);
1088
+ element.attributes['end'], rateInfo);
1087
1089
  const duration = shaka.text.TtmlTextParser.parseTimeAttribute_(
1088
- element.getAttribute('dur'), rateInfo);
1090
+ element.attributes['dur'], rateInfo);
1089
1091
  if (end == null && duration != null) {
1090
1092
  end = start + duration;
1091
1093
  }
@@ -28,8 +28,9 @@ shaka.text.UITextDisplayer = class {
28
28
  * Constructor.
29
29
  * @param {HTMLMediaElement} video
30
30
  * @param {HTMLElement} videoContainer
31
+ * @param {shaka.extern.TextDisplayerConfiguration} config
31
32
  */
32
- constructor(video, videoContainer) {
33
+ constructor(video, videoContainer, config) {
33
34
  goog.asserts.assert(videoContainer, 'videoContainer should be valid.');
34
35
 
35
36
  /** @private {boolean} */
@@ -64,16 +65,12 @@ shaka.text.UITextDisplayer = class {
64
65
 
65
66
  this.videoContainer_.appendChild(this.textContainer_);
66
67
 
67
- /**
68
- * The captions' update period in seconds.
69
- * @private {number}
70
- */
71
- const updatePeriod = 0.25;
72
-
73
68
  /** @private {shaka.util.Timer} */
74
69
  this.captionsTimer_ = new shaka.util.Timer(() => {
75
- this.updateCaptions_();
76
- }).tickEvery(updatePeriod);
70
+ if (!this.video_.paused) {
71
+ this.updateCaptions_();
72
+ }
73
+ }).tickEvery(config.captionsUpdatePeriod);
77
74
 
78
75
  /**
79
76
  * Maps cues to cue elements. Specifically points out the wrapper element of
@@ -93,6 +90,10 @@ shaka.text.UITextDisplayer = class {
93
90
  this.updateCaptions_(/* forceUpdate= */ true);
94
91
  });
95
92
 
93
+ this.eventManager_.listen(this.video_, 'seeking', () => {
94
+ this.updateCaptions_(/* forceUpdate= */ true);
95
+ });
96
+
96
97
  // From: https://html.spec.whatwg.org/multipage/media.html#dom-video-videowidth
97
98
  // Whenever the natural width or natural height of the video changes
98
99
  // (including, for example, because the selected video track was changed),
@@ -124,6 +125,17 @@ shaka.text.UITextDisplayer = class {
124
125
  }
125
126
 
126
127
 
128
+ /**
129
+ * @override
130
+ * @export
131
+ */
132
+ configure(config) {
133
+ if (this.captionsTimer_) {
134
+ this.captionsTimer_.tickEvery(config.captionsUpdatePeriod);
135
+ }
136
+ }
137
+
138
+
127
139
  /**
128
140
  * @override
129
141
  * @export
@@ -339,6 +351,9 @@ shaka.text.UITextDisplayer = class {
339
351
  const cueRegistry = this.currentCuesMap_.get(cue);
340
352
  goog.asserts.assert(cueRegistry, 'cueRegistry should exist.');
341
353
  if (cueRegistry.regionElement) {
354
+ if (cueRegistry.regionElement.contains(container)) {
355
+ cueRegistry.regionElement.removeChild(container);
356
+ }
342
357
  container.appendChild(cueRegistry.regionElement);
343
358
  cueRegistry.regionElement.appendChild(cueRegistry.cueElement);
344
359
  } else {
@@ -373,8 +388,10 @@ shaka.text.UITextDisplayer = class {
373
388
  if (this.isTextVisible_) {
374
389
  // Log currently attached cue elements for verification, later.
375
390
  const previousCuesMap = new Map();
376
- for (const cue of this.currentCuesMap_.keys()) {
377
- previousCuesMap.set(cue, this.currentCuesMap_.get(cue));
391
+ if (goog.DEBUG) {
392
+ for (const cue of this.currentCuesMap_.keys()) {
393
+ previousCuesMap.set(cue, this.currentCuesMap_.get(cue));
394
+ }
378
395
  }
379
396
 
380
397
  // Update the cues.