shaka-player 3.2.13 → 3.2.15

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 (57) hide show
  1. package/.github/workflows/build-and-test.yaml +11 -6
  2. package/.github/workflows/compute-incremental-coverage.py +14 -3
  3. package/.github/workflows/demo-version-index.yaml +9 -1
  4. package/.github/workflows/nightly-demo.yaml +1 -1
  5. package/.github/workflows/release-please.yaml +4 -4
  6. package/.github/workflows/report-incremental-coverage.yaml +1 -1
  7. package/.github/workflows/selenium-lab-tests.yaml +8 -6
  8. package/CHANGELOG.md +24 -0
  9. package/dist/deps.js +4 -4
  10. package/dist/locales.js +31 -31
  11. package/dist/shaka-player.compiled.d.ts +2 -1
  12. package/dist/shaka-player.compiled.debug.d.ts +2 -1
  13. package/dist/shaka-player.compiled.debug.externs.js +3 -1
  14. package/dist/shaka-player.compiled.debug.js +102 -100
  15. package/dist/shaka-player.compiled.debug.map +4 -4
  16. package/dist/shaka-player.compiled.externs.js +3 -1
  17. package/dist/shaka-player.compiled.js +703 -701
  18. package/dist/shaka-player.compiled.map +4 -4
  19. package/dist/shaka-player.ui.d.ts +2 -1
  20. package/dist/shaka-player.ui.debug.d.ts +2 -1
  21. package/dist/shaka-player.ui.debug.externs.js +3 -1
  22. package/dist/shaka-player.ui.debug.js +220 -218
  23. package/dist/shaka-player.ui.debug.map +4 -4
  24. package/dist/shaka-player.ui.externs.js +3 -1
  25. package/dist/shaka-player.ui.js +816 -814
  26. package/dist/shaka-player.ui.map +4 -4
  27. package/docs/tutorials/welcome.md +2 -2
  28. package/lib/ads/client_side_ad_manager.js +5 -2
  29. package/lib/cast/cast_proxy.js +2 -1
  30. package/lib/cast/cast_receiver.js +19 -5
  31. package/lib/cast/cast_sender.js +2 -1
  32. package/lib/cast/cast_utils.js +16 -6
  33. package/lib/hls/hls_parser.js +1 -0
  34. package/lib/media/drm_engine.js +4 -2
  35. package/lib/media/playhead.js +23 -1
  36. package/lib/media/streaming_engine.js +3 -12
  37. package/lib/offline/indexeddb/storage_mechanism.js +24 -0
  38. package/lib/player.js +24 -11
  39. package/lib/polyfill/media_capabilities.js +1 -0
  40. package/lib/text/ttml_text_parser.js +69 -93
  41. package/lib/util/error.js +7 -0
  42. package/lib/util/manifest_parser_utils.js +4 -0
  43. package/lib/util/periods.js +9 -9
  44. package/lib/util/xml_utils.js +45 -6
  45. package/package.json +1 -1
  46. package/test/cast/cast_receiver_integration.js +97 -16
  47. package/test/cast/cast_receiver_unit.js +17 -12
  48. package/test/cast/cast_utils_unit.js +1 -0
  49. package/test/media/playhead_unit.js +41 -0
  50. package/test/media/streaming_engine_integration.js +13 -0
  51. package/test/media/streaming_engine_unit.js +23 -8
  52. package/test/offline/storage_integration.js +30 -0
  53. package/test/player_unit.js +87 -0
  54. package/test/test/util/simple_fakes.js +3 -0
  55. package/test/util/periods_unit.js +149 -11
  56. package/test/util/xml_utils_unit.js +64 -0
  57. package/ui/controls.js +4 -1
@@ -66,8 +66,8 @@ python build/all.py
66
66
 
67
67
  Alternatively you can use a docker container:
68
68
  ```sh
69
- cd build/docker
70
- docker-compose run --rm shaka-compiler
69
+ docker build -t shaka-player-build build/docker
70
+ docker run -v $(pwd):/usr/src --user $(id -u):$(id -g) shaka-player-build
71
71
  ```
72
72
 
73
73
  The output is:
@@ -15,7 +15,6 @@ goog.require('goog.asserts');
15
15
  goog.require('shaka.ads.ClientSideAd');
16
16
  goog.require('shaka.util.IReleasable');
17
17
 
18
-
19
18
  /**
20
19
  * A class responsible for client-side ad interactions.
21
20
  * @implements {shaka.util.IReleasable}
@@ -211,7 +210,11 @@ shaka.ads.ClientSideAdManager = class {
211
210
  // TODO (ismena): Need a better inderstanding of what this does.
212
211
  // The docs say it's called to 'start playing the ads,' but I haven't
213
212
  // seen the ads actually play until requestAds() is called.
214
- this.imaAdsManager_.start();
213
+ // Note: We listen for a play event to avoid autoplay issues that might
214
+ // crash IMA.
215
+ this.video_.addEventListener('play', () => {
216
+ this.imaAdsManager_.start();
217
+ }, {once: true});
215
218
  } catch (adError) {
216
219
  // If there was a problem with the VAST response,
217
220
  // we we won't be getting an ad. Hide ad UI if we showed it already
@@ -694,7 +694,8 @@ shaka.cast.CastProxy = class extends shaka.util.FakeEventTarget {
694
694
  // If we are casting, but the first update has not come in yet, use local
695
695
  // getters, but not local methods.
696
696
  if (this.sender_.isCasting() && !this.sender_.hasRemoteProperties()) {
697
- if (shaka.cast.CastUtils.PlayerGetterMethods[name]) {
697
+ if (shaka.cast.CastUtils.PlayerGetterMethods[name] ||
698
+ shaka.cast.CastUtils.LargePlayerGetterMethods[name]) {
698
699
  const value = /** @type {Object} */(this.localPlayer_)[name];
699
700
  goog.asserts.assert(typeof value == 'function',
700
701
  'only methods on Player');
@@ -518,6 +518,25 @@ shaka.cast.CastReceiver = class extends shaka.util.FakeEventTarget {
518
518
  update['video']['muted'] = systemVolume.muted;
519
519
  }
520
520
 
521
+ this.sendMessage_({
522
+ 'type': 'update',
523
+ 'update': update,
524
+ }, this.shakaBus_);
525
+
526
+ // Getters with large outputs each get sent in their own update message.
527
+ for (const name in shaka.cast.CastUtils.LargePlayerGetterMethods) {
528
+ const frequency = shaka.cast.CastUtils.LargePlayerGetterMethods[name];
529
+ if (this.updateNumber_ % frequency == 0) {
530
+ const update = {'player': {}};
531
+ update['player'][name] = /** @type {Object} */ (this.player_)[name]();
532
+
533
+ this.sendMessage_({
534
+ 'type': 'update',
535
+ 'update': update,
536
+ }, this.shakaBus_);
537
+ }
538
+ }
539
+
521
540
  // Only start progressing the update number once data is loaded,
522
541
  // just in case any of the "rarely changing" properties with less frequent
523
542
  // update messages changes significantly during the loading process.
@@ -525,11 +544,6 @@ shaka.cast.CastReceiver = class extends shaka.util.FakeEventTarget {
525
544
  this.updateNumber_ += 1;
526
545
  }
527
546
 
528
- this.sendMessage_({
529
- 'type': 'update',
530
- 'update': update,
531
- }, this.shakaBus_);
532
-
533
547
  this.maybeSendMediaInfoMessage_();
534
548
  }
535
549
 
@@ -360,7 +360,8 @@ shaka.cast.CastSender = class {
360
360
  return (...args) =>
361
361
  this.remoteAsyncCall_(targetName, property, ...args);
362
362
  }
363
- if (CastUtils.PlayerGetterMethods[property]) {
363
+ if (CastUtils.PlayerGetterMethods[property] ||
364
+ CastUtils.LargePlayerGetterMethods[property]) {
364
365
  return () => this.propertyGetter_(targetName, property);
365
366
  }
366
367
  }
@@ -307,9 +307,6 @@ shaka.cast.CastUtils.PlayerGetterMethods = {
307
307
  'getAudioLanguagesAndRoles': 4,
308
308
  'getBufferFullness': 1,
309
309
  'getBufferedInfo': 2,
310
- // NOTE: The 'getSharedConfiguration' property is not proxied as it would
311
- // not be possible to share a reference.
312
- 'getConfiguration': 4,
313
310
  'getExpiration': 2,
314
311
  'getKeyStatuses': 2,
315
312
  // NOTE: The 'getManifest' property is not proxied, as it is very large.
@@ -318,9 +315,6 @@ shaka.cast.CastUtils.PlayerGetterMethods = {
318
315
  'getPlaybackRate': 2,
319
316
  'getTextLanguages': 4,
320
317
  'getTextLanguagesAndRoles': 4,
321
- 'getTextTracks': 2,
322
- 'getStats': 5,
323
- 'getVariantTracks': 2,
324
318
  'getImageTracks': 2,
325
319
  'getThumbnails': 2,
326
320
  'isAudioOnly': 10,
@@ -334,6 +328,22 @@ shaka.cast.CastUtils.PlayerGetterMethods = {
334
328
  };
335
329
 
336
330
 
331
+ /**
332
+ * Player getter methods with data large enough to be sent in their own update
333
+ * messages, to reduce the size of each message. The format of this is
334
+ * identical to PlayerGetterMethods.
335
+ * @const {!Object.<string, number>}
336
+ */
337
+ shaka.cast.CastUtils.LargePlayerGetterMethods = {
338
+ // NOTE: The 'getSharedConfiguration' property is not proxied as it would
339
+ // not be possible to share a reference.
340
+ 'getConfiguration': 4,
341
+ 'getStats': 5,
342
+ 'getTextTracks': 2,
343
+ 'getVariantTracks': 2,
344
+ };
345
+
346
+
337
347
  /**
338
348
  * Player getter methods that are proxied while casting, but only when casting
339
349
  * a livestream.
@@ -2969,6 +2969,7 @@ shaka.hls.HlsParser.TEXT_EXTENSIONS_TO_MIME_TYPES_ = {
2969
2969
  'm4f': 'application/mp4',
2970
2970
  'cmft': 'application/mp4',
2971
2971
  'vtt': 'text/vtt',
2972
+ 'webvtt': 'txt/vtt',
2972
2973
  'ttml': 'application/ttml+xml',
2973
2974
  };
2974
2975
 
@@ -27,6 +27,7 @@ goog.require('shaka.util.StreamUtils');
27
27
  goog.require('shaka.util.StringUtils');
28
28
  goog.require('shaka.util.Timer');
29
29
  goog.require('shaka.util.Uint8ArrayUtils');
30
+ goog.require('shaka.util.XmlUtils');
30
31
 
31
32
 
32
33
  /** @implements {shaka.util.IDestroyable} */
@@ -1439,7 +1440,8 @@ shaka.media.DrmEngine = class {
1439
1440
  return;
1440
1441
  }
1441
1442
  shaka.log.debug('Unwrapping PlayReady request.');
1442
- const dom = new DOMParser().parseFromString(xml, 'application/xml');
1443
+ const dom = shaka.util.XmlUtils.parseXmlString(xml, 'PlayReadyKeyMessage');
1444
+ goog.asserts.assert(dom, 'Failed to parse PlayReady XML!');
1443
1445
 
1444
1446
  // Set request headers.
1445
1447
  const headers = dom.getElementsByTagName('HttpHeader');
@@ -1523,7 +1525,7 @@ shaka.media.DrmEngine = class {
1523
1525
  hasExpiredKeys = true;
1524
1526
  }
1525
1527
 
1526
- const keyIdHex = shaka.util.Uint8ArrayUtils.toHex(keyId);
1528
+ const keyIdHex = shaka.util.Uint8ArrayUtils.toHex(keyId).slice(0, 32);
1527
1529
 
1528
1530
  this.keyStatusByKeyId_.set(keyIdHex, status);
1529
1531
  });
@@ -33,6 +33,14 @@ goog.requireType('shaka.media.PresentationTimeline');
33
33
  * @interface
34
34
  */
35
35
  shaka.media.Playhead = class {
36
+ /**
37
+ * Called when the Player is ready to begin playback. Anything that depends
38
+ * on setStartTime() should be done here, not in the constructor.
39
+ *
40
+ * @see https://github.com/shaka-project/shaka-player/issues/4244
41
+ */
42
+ ready() {}
43
+
36
44
  /**
37
45
  * Set the start time. If the content has already started playback, this will
38
46
  * be ignored.
@@ -76,6 +84,14 @@ shaka.media.SrcEqualsPlayhead = class {
76
84
 
77
85
  /** @private {shaka.util.EventManager} */
78
86
  this.eventManager_ = new shaka.util.EventManager();
87
+ }
88
+
89
+ /** @override */
90
+ ready() {
91
+ goog.asserts.assert(
92
+ this.mediaElement_ != null,
93
+ 'Playhead should not be released before calling ready()',
94
+ );
79
95
 
80
96
  // We listen for the loaded-data-event so that we know when we can
81
97
  // interact with |currentTime|.
@@ -88,6 +104,7 @@ shaka.media.SrcEqualsPlayhead = class {
88
104
  this.eventManager_.listenOnce(this.mediaElement_, 'seeking', () => {
89
105
  this.started_ = true;
90
106
  });
107
+
91
108
  const currentTime = this.mediaElement_.currentTime;
92
109
  // Using the currentTime allows using a negative number in Live HLS
93
110
  const newTime = Math.max(0, currentTime + this.startTime_);
@@ -211,7 +228,12 @@ shaka.media.MediaSourcePlayhead = class {
211
228
  /** @type {shaka.util.Timer} */
212
229
  this.checkWindowTimer_ = new shaka.util.Timer(() => {
213
230
  this.onPollWindow_();
214
- }).tickEvery(/* seconds= */ 0.25);
231
+ });
232
+ }
233
+
234
+ /** @override */
235
+ ready() {
236
+ this.checkWindowTimer_.tickEvery(/* seconds= */ 0.25);
215
237
  }
216
238
 
217
239
  /** @override */
@@ -1355,16 +1355,6 @@ shaka.media.StreamingEngine = class {
1355
1355
  this.mediaStates_.delete(ContentType.TEXT);
1356
1356
  } else if (error.code == shaka.util.Error.Code.QUOTA_EXCEEDED_ERROR) {
1357
1357
  this.handleQuotaExceeded_(mediaState, error);
1358
- } else if (error.code == shaka.util.Error.Code.BAD_HTTP_STATUS &&
1359
- error.data && error.data[1] == 404) {
1360
- // The segment could not be found, does not exist, or is not available.
1361
- // In any case just try again.
1362
- // The current segment is not available. Schedule another update to
1363
- // fetch the segment again.
1364
- shaka.log.v2(logPrefix, 'segment not available.');
1365
- mediaState.performingUpdate = false;
1366
- mediaState.updateTimer = null;
1367
- this.scheduleUpdate_(mediaState, 1);
1368
1358
  } else {
1369
1359
  shaka.log.error(logPrefix, 'failed fetch and append: code=' +
1370
1360
  error.code);
@@ -1379,9 +1369,10 @@ shaka.media.StreamingEngine = class {
1379
1369
 
1380
1370
  /**
1381
1371
  * Clear per-stream error states and retry any failed streams.
1372
+ * @param {number} delaySeconds
1382
1373
  * @return {boolean} False if unable to retry.
1383
1374
  */
1384
- retry() {
1375
+ retry(delaySeconds) {
1385
1376
  if (this.destroyer_.destroyed()) {
1386
1377
  shaka.log.error('Unable to retry after StreamingEngine is destroyed!');
1387
1378
  return false;
@@ -1398,7 +1389,7 @@ shaka.media.StreamingEngine = class {
1398
1389
  if (mediaState.hasError) {
1399
1390
  shaka.log.info(logPrefix, 'Retrying after failure...');
1400
1391
  mediaState.hasError = false;
1401
- this.scheduleUpdate_(mediaState, 0.1);
1392
+ this.scheduleUpdate_(mediaState, delaySeconds);
1402
1393
  }
1403
1394
  }
1404
1395
 
@@ -16,6 +16,7 @@ goog.require('shaka.offline.indexeddb.V5StorageCell');
16
16
  goog.require('shaka.util.Error');
17
17
  goog.require('shaka.util.PublicPromise');
18
18
  goog.require('shaka.util.Platform');
19
+ goog.require('shaka.util.Timer');
19
20
 
20
21
 
21
22
  /**
@@ -55,8 +56,25 @@ shaka.offline.indexeddb.StorageMechanism = class {
55
56
  const version = shaka.offline.indexeddb.StorageMechanism.VERSION;
56
57
 
57
58
  const p = new shaka.util.PublicPromise();
59
+
60
+ // Add a timeout mechanism, for the (rare?) case where no callbacks are
61
+ // called at all, so that this method doesn't hang forever.
62
+ let timedOut = false;
63
+ const timeOutTimer = new shaka.util.Timer(() => {
64
+ timedOut = true;
65
+ p.reject(new shaka.util.Error(
66
+ shaka.util.Error.Severity.CRITICAL,
67
+ shaka.util.Error.Category.STORAGE,
68
+ shaka.util.Error.Code.INDEXED_DB_INIT_TIMED_OUT));
69
+ });
70
+ timeOutTimer.tickAfter(2);
71
+
58
72
  const open = window.indexedDB.open(name, version);
59
73
  open.onsuccess = (event) => {
74
+ if (timedOut) {
75
+ // Too late, we have already given up on opening the storage mechanism.
76
+ return;
77
+ }
60
78
  const db = open.result;
61
79
  this.db_ = db;
62
80
  this.v1_ = shaka.offline.indexeddb.StorageMechanism.createV1_(db);
@@ -68,6 +86,7 @@ shaka.offline.indexeddb.StorageMechanism = class {
68
86
  this.v5_ = shaka.offline.indexeddb.StorageMechanism.createV5_(db);
69
87
  this.sessions_ =
70
88
  shaka.offline.indexeddb.StorageMechanism.createEmeSessionCell_(db);
89
+ timeOutTimer.stop();
71
90
  p.resolve();
72
91
  };
73
92
  open.onupgradeneeded = (event) => {
@@ -75,11 +94,16 @@ shaka.offline.indexeddb.StorageMechanism = class {
75
94
  this.createStores_(open.result);
76
95
  };
77
96
  open.onerror = (event) => {
97
+ if (timedOut) {
98
+ // Too late, we have already given up on opening the storage mechanism.
99
+ return;
100
+ }
78
101
  p.reject(new shaka.util.Error(
79
102
  shaka.util.Error.Severity.CRITICAL,
80
103
  shaka.util.Error.Category.STORAGE,
81
104
  shaka.util.Error.Code.INDEXED_DB_ERROR,
82
105
  open.error));
106
+ timeOutTimer.stop();
83
107
 
84
108
  // Firefox will raise an error on the main thread unless we stop it here.
85
109
  event.preventDefault();
package/lib/player.js CHANGED
@@ -1933,6 +1933,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1933
1933
  shaka.util.StreamUtils.variantToTrack(initialVariant));
1934
1934
  }
1935
1935
 
1936
+ this.playhead_.ready();
1937
+
1936
1938
  // Decide if text should be shown automatically.
1937
1939
  // similar to video/audio track, we would skip switch initial text track
1938
1940
  // if user already pick text track (via selectTextTrack api)
@@ -2263,6 +2265,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2263
2265
  HTMLMediaElement.HAVE_METADATA,
2264
2266
  this.loadEventManager_,
2265
2267
  () => {
2268
+ this.playhead_.ready();
2266
2269
  fullyLoaded.resolve();
2267
2270
  });
2268
2271
 
@@ -4792,12 +4795,13 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
4792
4795
  * If the player has loaded content, and streaming seen an error, but the
4793
4796
  * could not resume streaming, this will return <code>false</code>.
4794
4797
  *
4798
+ * @param {number=} retryDelaySeconds
4795
4799
  * @return {boolean}
4796
4800
  * @export
4797
4801
  */
4798
- retryStreaming() {
4802
+ retryStreaming(retryDelaySeconds = 0.1) {
4799
4803
  return this.loadMode_ == shaka.Player.LoadMode.MEDIA_SOURCE ?
4800
- this.streamingEngine_.retry() :
4804
+ this.streamingEngine_.retry(retryDelaySeconds) :
4801
4805
  false;
4802
4806
  }
4803
4807
 
@@ -4893,17 +4897,26 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
4893
4897
  * @private
4894
4898
  */
4895
4899
  defaultStreamingFailureCallback_(error) {
4896
- const retryErrorCodes = [
4897
- shaka.util.Error.Code.BAD_HTTP_STATUS,
4898
- shaka.util.Error.Code.HTTP_ERROR,
4899
- shaka.util.Error.Code.TIMEOUT,
4900
- ];
4900
+ // For live streams, we retry streaming automatically for certain errors.
4901
+ // For VOD streams, all streaming failures are fatal.
4902
+ if (!this.isLive()) {
4903
+ return;
4904
+ }
4901
4905
 
4902
- if (this.isLive() && retryErrorCodes.includes(error.code)) {
4903
- error.severity = shaka.util.Error.Severity.RECOVERABLE;
4906
+ let retryDelaySeconds = null;
4907
+ if (error.code == shaka.util.Error.Code.BAD_HTTP_STATUS ||
4908
+ error.code == shaka.util.Error.Code.HTTP_ERROR) {
4909
+ // These errors can be near-instant, so delay a bit before retrying.
4910
+ retryDelaySeconds = 1;
4911
+ } else if (error.code == shaka.util.Error.Code.TIMEOUT) {
4912
+ // We already waited for a timeout, so retry quickly.
4913
+ retryDelaySeconds = 0.1;
4914
+ }
4904
4915
 
4916
+ if (retryDelaySeconds != null) {
4917
+ error.severity = shaka.util.Error.Severity.RECOVERABLE;
4905
4918
  shaka.log.warning('Live streaming error. Retrying automatically...');
4906
- this.retryStreaming();
4919
+ this.retryStreaming(retryDelaySeconds);
4907
4920
  }
4908
4921
  }
4909
4922
 
@@ -6431,7 +6444,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
6431
6444
  * @define {string} A version number taken from git at compile time.
6432
6445
  * @export
6433
6446
  */
6434
- shaka.Player.version = 'v3.2.13-uncompiled';
6447
+ shaka.Player.version = 'v3.2.15-uncompiled';
6435
6448
 
6436
6449
  // Initialize the deprecation system using the version string we just set
6437
6450
  // on the player.
@@ -43,6 +43,7 @@ shaka.polyfill.MediaCapabilities = class {
43
43
  !shaka.util.Platform.isPS5() &&
44
44
  !shaka.util.Platform.isWebOS() &&
45
45
  !shaka.util.Platform.isTizen() &&
46
+ !shaka.util.Platform.isEOS() &&
46
47
  navigator.mediaCapabilities) {
47
48
  shaka.log.info(
48
49
  'MediaCapabilities: Native mediaCapabilities support found.');
@@ -41,8 +41,6 @@ shaka.text.TtmlTextParser = class {
41
41
  const ttsNs = TtmlTextParser.styleNs_;
42
42
  const str = shaka.util.StringUtils.fromUTF8(data);
43
43
  const cues = [];
44
- const parser = new DOMParser();
45
- let xml = null;
46
44
 
47
45
  // dont try to parse empty string as
48
46
  // DOMParser will not throw error but return an errored xml
@@ -50,9 +48,8 @@ shaka.text.TtmlTextParser = class {
50
48
  return cues;
51
49
  }
52
50
 
53
- try {
54
- xml = parser.parseFromString(str, 'text/xml');
55
- } catch (exception) {
51
+ const tt = XmlUtils.parseXmlString(str, 'tt');
52
+ if (!tt) {
56
53
  throw new shaka.util.Error(
57
54
  shaka.util.Error.Severity.CRITICAL,
58
55
  shaka.util.Error.Category.TEXT,
@@ -60,110 +57,89 @@ shaka.text.TtmlTextParser = class {
60
57
  'Failed to parse TTML.');
61
58
  }
62
59
 
63
- if (xml) {
64
- const parserError = xml.getElementsByTagName('parsererror')[0];
65
- if (parserError) {
66
- throw new shaka.util.Error(
67
- shaka.util.Error.Severity.CRITICAL,
68
- shaka.util.Error.Category.TEXT,
69
- shaka.util.Error.Code.INVALID_XML,
70
- parserError.textContent);
71
- }
72
-
73
- const tt = xml.getElementsByTagName('tt')[0];
74
- // TTML should always have tt element.
75
- if (!tt) {
76
- throw new shaka.util.Error(
77
- shaka.util.Error.Severity.CRITICAL,
78
- shaka.util.Error.Category.TEXT,
79
- shaka.util.Error.Code.INVALID_XML,
80
- 'TTML does not contain <tt> tag.');
81
- }
82
-
83
- const body = tt.getElementsByTagName('body')[0];
84
- if (!body) {
85
- return [];
86
- }
60
+ const body = tt.getElementsByTagName('body')[0];
61
+ if (!body) {
62
+ return [];
63
+ }
87
64
 
88
- // Get the framerate, subFrameRate and frameRateMultiplier if applicable.
89
- const frameRate = XmlUtils.getAttributeNSList(tt, ttpNs, 'frameRate');
90
- const subFrameRate = XmlUtils.getAttributeNSList(
91
- tt, ttpNs, 'subFrameRate');
92
- const frameRateMultiplier =
93
- XmlUtils.getAttributeNSList(tt, ttpNs, 'frameRateMultiplier');
94
- const tickRate = XmlUtils.getAttributeNSList(tt, ttpNs, 'tickRate');
65
+ // Get the framerate, subFrameRate and frameRateMultiplier if applicable.
66
+ const frameRate = XmlUtils.getAttributeNSList(tt, ttpNs, 'frameRate');
67
+ const subFrameRate = XmlUtils.getAttributeNSList(
68
+ tt, ttpNs, 'subFrameRate');
69
+ const frameRateMultiplier =
70
+ XmlUtils.getAttributeNSList(tt, ttpNs, 'frameRateMultiplier');
71
+ const tickRate = XmlUtils.getAttributeNSList(tt, ttpNs, 'tickRate');
95
72
 
96
- const cellResolution = XmlUtils.getAttributeNSList(
97
- tt, ttpNs, 'cellResolution');
98
- const spaceStyle = tt.getAttribute('xml:space') || 'default';
99
- const extent = XmlUtils.getAttributeNSList(tt, ttsNs, 'extent');
73
+ const cellResolution = XmlUtils.getAttributeNSList(
74
+ tt, ttpNs, 'cellResolution');
75
+ const spaceStyle = tt.getAttribute('xml:space') || 'default';
76
+ const extent = XmlUtils.getAttributeNSList(tt, ttsNs, 'extent');
100
77
 
101
- if (spaceStyle != 'default' && spaceStyle != 'preserve') {
102
- throw new shaka.util.Error(
103
- shaka.util.Error.Severity.CRITICAL,
104
- shaka.util.Error.Category.TEXT,
105
- shaka.util.Error.Code.INVALID_XML,
106
- 'Invalid xml:space value: ' + spaceStyle);
107
- }
108
- const whitespaceTrim = spaceStyle == 'default';
78
+ if (spaceStyle != 'default' && spaceStyle != 'preserve') {
79
+ throw new shaka.util.Error(
80
+ shaka.util.Error.Severity.CRITICAL,
81
+ shaka.util.Error.Category.TEXT,
82
+ shaka.util.Error.Code.INVALID_XML,
83
+ 'Invalid xml:space value: ' + spaceStyle);
84
+ }
85
+ const whitespaceTrim = spaceStyle == 'default';
109
86
 
110
- const rateInfo = new TtmlTextParser.RateInfo_(
111
- frameRate, subFrameRate, frameRateMultiplier, tickRate);
87
+ const rateInfo = new TtmlTextParser.RateInfo_(
88
+ frameRate, subFrameRate, frameRateMultiplier, tickRate);
112
89
 
113
- const cellResolutionInfo =
114
- TtmlTextParser.getCellResolution_(cellResolution);
90
+ const cellResolutionInfo =
91
+ TtmlTextParser.getCellResolution_(cellResolution);
115
92
 
116
- const metadata = tt.getElementsByTagName('metadata')[0];
117
- const metadataElements = metadata ? XmlUtils.getChildren(metadata) : [];
118
- const styles = Array.from(tt.getElementsByTagName('style'));
119
- const regionElements = Array.from(tt.getElementsByTagName('region'));
93
+ const metadata = tt.getElementsByTagName('metadata')[0];
94
+ const metadataElements = metadata ? XmlUtils.getChildren(metadata) : [];
95
+ const styles = Array.from(tt.getElementsByTagName('style'));
96
+ const regionElements = Array.from(tt.getElementsByTagName('region'));
120
97
 
121
- const cueRegions = [];
122
- for (const region of regionElements) {
123
- const cueRegion =
124
- TtmlTextParser.parseCueRegion_(region, styles, extent);
125
- if (cueRegion) {
126
- cueRegions.push(cueRegion);
127
- }
98
+ const cueRegions = [];
99
+ for (const region of regionElements) {
100
+ const cueRegion =
101
+ TtmlTextParser.parseCueRegion_(region, styles, extent);
102
+ if (cueRegion) {
103
+ cueRegions.push(cueRegion);
128
104
  }
105
+ }
129
106
 
130
- // A <body> element should only contain <div> elements, not <p> or <span>
131
- // elements. We used to allow this, but it is non-compliant, and the
132
- // loose nature of our previous parser made it difficult to implement TTML
133
- // nesting more fully.
134
- if (XmlUtils.findChildren(body, 'p').length) {
107
+ // A <body> element should only contain <div> elements, not <p> or <span>
108
+ // elements. We used to allow this, but it is non-compliant, and the
109
+ // loose nature of our previous parser made it difficult to implement TTML
110
+ // nesting more fully.
111
+ if (XmlUtils.findChildren(body, 'p').length) {
112
+ throw new shaka.util.Error(
113
+ shaka.util.Error.Severity.CRITICAL,
114
+ shaka.util.Error.Category.TEXT,
115
+ shaka.util.Error.Code.INVALID_TEXT_CUE,
116
+ '<p> can only be inside <div> in TTML');
117
+ }
118
+
119
+ for (const div of XmlUtils.findChildren(body, 'div')) {
120
+ // A <div> element should only contain <p>, not <span>.
121
+ if (XmlUtils.findChildren(div, 'span').length) {
135
122
  throw new shaka.util.Error(
136
123
  shaka.util.Error.Severity.CRITICAL,
137
124
  shaka.util.Error.Category.TEXT,
138
125
  shaka.util.Error.Code.INVALID_TEXT_CUE,
139
- '<p> can only be inside <div> in TTML');
140
- }
141
-
142
- for (const div of XmlUtils.findChildren(body, 'div')) {
143
- // A <div> element should only contain <p>, not <span>.
144
- if (XmlUtils.findChildren(div, 'span').length) {
145
- throw new shaka.util.Error(
146
- shaka.util.Error.Severity.CRITICAL,
147
- shaka.util.Error.Category.TEXT,
148
- shaka.util.Error.Code.INVALID_TEXT_CUE,
149
- '<span> can only be inside <p> in TTML');
150
- }
126
+ '<span> can only be inside <p> in TTML');
151
127
  }
128
+ }
152
129
 
153
- const cue = TtmlTextParser.parseCue_(
154
- body, time.periodStart, rateInfo, metadataElements, styles,
155
- regionElements, cueRegions, whitespaceTrim,
156
- cellResolutionInfo, /* parentCueElement= */ null,
157
- /* isContent= */ false);
158
- if (cue) {
159
- // According to the TTML spec, backgrounds default to transparent.
160
- // So default the background of the top-level element to transparent.
161
- // Nested elements may override that background color already.
162
- if (!cue.backgroundColor) {
163
- cue.backgroundColor = 'transparent';
164
- }
165
- cues.push(cue);
130
+ const cue = TtmlTextParser.parseCue_(
131
+ body, time.periodStart, rateInfo, metadataElements, styles,
132
+ regionElements, cueRegions, whitespaceTrim,
133
+ cellResolutionInfo, /* parentCueElement= */ null,
134
+ /* isContent= */ false);
135
+ if (cue) {
136
+ // According to the TTML spec, backgrounds default to transparent.
137
+ // So default the background of the top-level element to transparent.
138
+ // Nested elements may override that background color already.
139
+ if (!cue.backgroundColor) {
140
+ cue.backgroundColor = 'transparent';
166
141
  }
142
+ cues.push(cue);
167
143
  }
168
144
 
169
145
  return cues;
package/lib/util/error.js CHANGED
@@ -993,6 +993,13 @@ shaka.util.Error.Code = {
993
993
  */
994
994
  'DOWNLOAD_SIZE_CALLBACK_ERROR': 9015,
995
995
 
996
+ /**
997
+ * When attempting to open an indexedDB instance, nothing happened for long
998
+ * enough for us to time out. This keeps the storage mechanism from hanging
999
+ * indefinitely, if neither the success nor error callbacks are called.
1000
+ */
1001
+ 'INDEXED_DB_INIT_TIMED_OUT': 9017,
1002
+
996
1003
  /**
997
1004
  * CS IMA SDK, required for ad insertion, has not been included on the page.
998
1005
  */
@@ -168,6 +168,7 @@ shaka.util.ManifestParserUtils.VIDEO_CODEC_REGEXPS_ = [
168
168
  /^hvc/,
169
169
  /^vp0?[89]/,
170
170
  /^av01/,
171
+ /^dvhe/,
171
172
  ];
172
173
 
173
174
 
@@ -183,6 +184,9 @@ shaka.util.ManifestParserUtils.AUDIO_CODEC_REGEXPS_ = [
183
184
  /^flac$/,
184
185
  /^mp4a/,
185
186
  /^[ae]c-3$/,
187
+ /^ac-4$/,
188
+ /^dts[cx]$/, // DTS Surround Sound
189
+ /^[du]dts$/, // DTS Surround Sound
186
190
  ];
187
191
 
188
192