shaka-player 4.11.2 → 4.11.4

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 (83) hide show
  1. package/dist/locales.js +56 -56
  2. package/dist/shaka-player.compiled.d.ts +10 -1
  3. package/dist/shaka-player.compiled.debug.d.ts +10 -1
  4. package/dist/shaka-player.compiled.debug.externs.js +13 -0
  5. package/dist/shaka-player.compiled.debug.js +316 -310
  6. package/dist/shaka-player.compiled.debug.map +3 -3
  7. package/dist/shaka-player.compiled.externs.js +13 -0
  8. package/dist/shaka-player.compiled.js +1014 -1008
  9. package/dist/shaka-player.compiled.map +3 -3
  10. package/dist/shaka-player.ui.d.ts +10 -1
  11. package/dist/shaka-player.ui.debug.d.ts +10 -1
  12. package/dist/shaka-player.ui.debug.externs.js +13 -0
  13. package/dist/shaka-player.ui.debug.js +373 -365
  14. package/dist/shaka-player.ui.debug.map +3 -3
  15. package/dist/shaka-player.ui.externs.js +13 -0
  16. package/dist/shaka-player.ui.js +1266 -1259
  17. package/dist/shaka-player.ui.map +3 -3
  18. package/externs/shaka/player.js +10 -1
  19. package/lib/ads/interstitial_ad_manager.js +27 -5
  20. package/lib/dash/dash_parser.js +10 -7
  21. package/lib/dash/mpd_utils.js +0 -3
  22. package/lib/dash/segment_template.js +11 -2
  23. package/lib/media/gap_jumping_controller.js +4 -2
  24. package/lib/media/presentation_timeline.js +16 -0
  25. package/lib/media/segment_index.js +4 -4
  26. package/lib/media/streaming_engine.js +17 -10
  27. package/lib/net/networking_utils.js +5 -0
  28. package/lib/offline/storage.js +12 -10
  29. package/lib/player.js +13 -4
  30. package/lib/text/ttml_text_parser.js +1 -1
  31. package/lib/util/periods.js +56 -3
  32. package/lib/util/player_configuration.js +5 -0
  33. package/lib/util/tXml.js +17 -9
  34. package/package.json +1 -1
  35. package/ui/controls.js +14 -2
  36. package/ui/locales/README.md +16 -0
  37. package/ui/locales/ar-XB.json +1 -1
  38. package/ui/locales/ar.json +1 -1
  39. package/ui/locales/bg.json +1 -1
  40. package/ui/locales/ca.json +5 -5
  41. package/ui/locales/cs.json +1 -1
  42. package/ui/locales/da.json +1 -1
  43. package/ui/locales/de.json +1 -1
  44. package/ui/locales/el.json +1 -1
  45. package/ui/locales/en-GB.json +1 -1
  46. package/ui/locales/en-XA.json +1 -1
  47. package/ui/locales/es-419.json +4 -4
  48. package/ui/locales/es.json +4 -4
  49. package/ui/locales/fa.json +1 -1
  50. package/ui/locales/fi.json +1 -1
  51. package/ui/locales/fil.json +1 -1
  52. package/ui/locales/fr.json +5 -5
  53. package/ui/locales/hi.json +1 -1
  54. package/ui/locales/hr.json +1 -1
  55. package/ui/locales/hu.json +1 -1
  56. package/ui/locales/id.json +1 -1
  57. package/ui/locales/it.json +1 -1
  58. package/ui/locales/iw.json +1 -1
  59. package/ui/locales/ja.json +1 -1
  60. package/ui/locales/ko.json +1 -1
  61. package/ui/locales/lt.json +1 -1
  62. package/ui/locales/lv.json +1 -1
  63. package/ui/locales/nl.json +1 -1
  64. package/ui/locales/no.json +1 -1
  65. package/ui/locales/pl.json +5 -5
  66. package/ui/locales/pt-BR.json +7 -7
  67. package/ui/locales/pt-PT.json +8 -8
  68. package/ui/locales/ro.json +1 -1
  69. package/ui/locales/ru.json +1 -1
  70. package/ui/locales/sjn-translations.yaml +50 -4
  71. package/ui/locales/sjn.json +8 -1
  72. package/ui/locales/sk.json +1 -1
  73. package/ui/locales/sl.json +1 -1
  74. package/ui/locales/sr.json +1 -1
  75. package/ui/locales/sv.json +1 -1
  76. package/ui/locales/sync-sjn.py +41 -0
  77. package/ui/locales/th.json +1 -1
  78. package/ui/locales/tr.json +1 -1
  79. package/ui/locales/uk.json +1 -1
  80. package/ui/locales/vi.json +1 -1
  81. package/ui/locales/zh-HK.json +1 -1
  82. package/ui/locales/zh-TW.json +1 -1
  83. package/ui/locales/zh.json +1 -1
@@ -1532,6 +1532,7 @@ shaka.extern.LiveSyncConfiguration;
1532
1532
  * alwaysStreamText: boolean,
1533
1533
  * startAtSegmentBoundary: boolean,
1534
1534
  * gapDetectionThreshold: number,
1535
+ * gapPadding: number,
1535
1536
  * gapJumpTimerTime: number,
1536
1537
  * durationBackoff: number,
1537
1538
  * safeSeekOffset: number,
@@ -1625,6 +1626,13 @@ shaka.extern.LiveSyncConfiguration;
1625
1626
  * jump.
1626
1627
  * <br>
1627
1628
  * Defaults to <code>0.5</code>.
1629
+ * @property {number} gapPadding
1630
+ * Padding added only for Xbox, Legacy Edge and Tizen.
1631
+ * Based on our research (specific to Tizen), the gapPadding value must be
1632
+ * greater than your GOP length.
1633
+ * It’s crucial to verify this value according to your actual stream.
1634
+ * <br>
1635
+ * Defaults to <code>0.01</code> for Xbox and Legacy Edge, Tizen at 2.
1628
1636
  * @property {number} gapJumpTimerTime
1629
1637
  * The polling time in seconds to check for gaps in the media.
1630
1638
  * <br>
@@ -2227,7 +2235,8 @@ shaka.extern.LcevcConfiguration;
2227
2235
  * <br>
2228
2236
  * Defaults to <code>true</code>.
2229
2237
  * @property {number} numberOfParallelDownloads
2230
- * Number of parallel downloads.
2238
+ * Number of parallel downloads. If the value is 0, downloads will be
2239
+ * sequential for each stream.
2231
2240
  * Note: normally browsers limit to 5 request in parallel, so putting a
2232
2241
  * number higher than this will not help it download faster.
2233
2242
  * <br>
@@ -89,13 +89,23 @@ shaka.ads.InterstitialAdManager = class {
89
89
  /** @private {?number} */
90
90
  this.lastTime_ = null;
91
91
 
92
+ /** @private {?shaka.extern.AdInterstitial} */
93
+ this.lastPlayedAd_ = null;
94
+
92
95
  this.eventManager_.listen(this.baseVideo_, 'timeupdate', () => {
93
96
  if (this.playingAd_) {
94
97
  return;
95
98
  }
96
99
  const needPreRoll = this.lastTime_ == null || this.lastTime_ == 0;
97
100
  this.lastTime_ = this.baseVideo_.currentTime;
98
- const currentInterstitial = this.getCurrentInterstitial_(needPreRoll);
101
+ // Remove last played add when the new time is before to the ad time.
102
+ if (this.lastPlayedAd_ &&
103
+ !this.lastPlayedAd_.pre && !this.lastPlayedAd_.post &&
104
+ this.lastTime_ < this.lastPlayedAd_.startTime) {
105
+ this.lastPlayedAd_ = null;
106
+ }
107
+ const currentInterstitial = this.getCurrentInterstitial_(
108
+ needPreRoll, /* numberToSkip= */ 0, this.lastPlayedAd_);
99
109
  if (currentInterstitial) {
100
110
  this.setupAd_(currentInterstitial, /* sequenceLength= */ 1,
101
111
  /* adPosition= */ 1, /* initialTime= */ Date.now());
@@ -187,6 +197,7 @@ shaka.ads.InterstitialAdManager = class {
187
197
  this.player_.detach();
188
198
  this.playingAd_ = false;
189
199
  this.lastTime_ = null;
200
+ this.lastPlayedAd_ = null;
190
201
  }
191
202
 
192
203
  /** @override */
@@ -374,11 +385,12 @@ shaka.ads.InterstitialAdManager = class {
374
385
 
375
386
  /**
376
387
  * @param {boolean} needPreRoll
377
- * @param {number=} numberToSkip
388
+ * @param {number} numberToSkip
389
+ * @param {?shaka.extern.AdInterstitial=} lastPlayedAd
378
390
  * @return {?shaka.extern.AdInterstitial}
379
391
  * @private
380
392
  */
381
- getCurrentInterstitial_(needPreRoll, numberToSkip = 0) {
393
+ getCurrentInterstitial_(needPreRoll, numberToSkip, lastPlayedAd) {
382
394
  let skipped = 0;
383
395
  let currentInterstitial = null;
384
396
  if (this.interstitials_.size && this.lastTime_ != null) {
@@ -398,8 +410,16 @@ shaka.ads.InterstitialAdManager = class {
398
410
  } else if (!interstitial.pre && !interstitial.post) {
399
411
  const difference =
400
412
  this.lastTime_ - roundDecimals(interstitial.startTime);
401
- isValid = difference > 0 &&
402
- (difference <= 1 || !interstitial.canJump);
413
+ if (difference > 0 &&
414
+ (difference <= 1 || !interstitial.canJump)) {
415
+ if (difference > 1 && lastPlayedAd &&
416
+ !lastPlayedAd.pre && !lastPlayedAd.post &&
417
+ lastPlayedAd.startTime >= interstitial.startTime) {
418
+ isValid = false;
419
+ } else {
420
+ isValid = true;
421
+ }
422
+ }
403
423
  }
404
424
  if (isValid) {
405
425
  if (skipped == numberToSkip) {
@@ -426,6 +446,8 @@ shaka.ads.InterstitialAdManager = class {
426
446
  oncePlayed = 0) {
427
447
  goog.asserts.assert(this.video_, 'Must have video');
428
448
 
449
+ this.lastPlayedAd_ = interstitial;
450
+
429
451
  const startTime = Date.now();
430
452
 
431
453
  if (!this.video_.parentElement && this.adContainer_) {
@@ -192,6 +192,9 @@ shaka.dash.DashParser = class {
192
192
 
193
193
  /** @private {function():boolean} */
194
194
  this.isPreloadFn_ = () => false;
195
+
196
+ /** @private {?Array.<string>} */
197
+ this.lastCalculatedBaseUris_ = [];
195
198
  }
196
199
 
197
200
  /**
@@ -602,7 +605,6 @@ shaka.dash.DashParser = class {
602
605
  }
603
606
 
604
607
  const uriObjs = TXml.findChildren(mpd, 'BaseURL');
605
- let calculatedBaseUris;
606
608
  let someLocationValid = false;
607
609
  if (this.contentSteeringManager_) {
608
610
  for (const uriObj of uriObjs) {
@@ -615,9 +617,10 @@ shaka.dash.DashParser = class {
615
617
  }
616
618
  }
617
619
  }
620
+ this.lastCalculatedBaseUris_ = null;
618
621
  if (!someLocationValid || !this.contentSteeringManager_) {
619
622
  const uris = uriObjs.map(TXml.getContents);
620
- calculatedBaseUris = shaka.util.ManifestParserUtils.resolveUris(
623
+ this.lastCalculatedBaseUris_ = shaka.util.ManifestParserUtils.resolveUris(
621
624
  manifestBaseUris, uris);
622
625
  }
623
626
 
@@ -625,8 +628,8 @@ shaka.dash.DashParser = class {
625
628
  if (this.contentSteeringManager_ && someLocationValid) {
626
629
  return this.contentSteeringManager_.getLocations('BaseURL');
627
630
  }
628
- if (calculatedBaseUris) {
629
- return calculatedBaseUris;
631
+ if (this.lastCalculatedBaseUris_) {
632
+ return this.lastCalculatedBaseUris_;
630
633
  }
631
634
  return [];
632
635
  };
@@ -1492,14 +1495,14 @@ shaka.dash.DashParser = class {
1492
1495
  */
1493
1496
  cleanStreamMap_() {
1494
1497
  const oldPeriodIds = Object.keys(this.indexStreamMap_);
1495
- if (oldPeriodIds.length === this.lastManifestUpdatePeriodIds_.length) {
1496
- return;
1497
- }
1498
1498
  const diffPeriodsIDs = oldPeriodIds.filter((pId) => {
1499
1499
  return !this.lastManifestUpdatePeriodIds_.includes(pId);
1500
1500
  });
1501
1501
  for (const pId of diffPeriodsIDs) {
1502
1502
  for (const contextId of this.indexStreamMap_[pId]) {
1503
+ if (this.periodCombiner_) {
1504
+ this.periodCombiner_.deleteStream(this.streamMap_[contextId], pId);
1505
+ }
1503
1506
  delete this.streamMap_[contextId];
1504
1507
  }
1505
1508
  delete this.indexStreamMap_[pId];
@@ -481,9 +481,6 @@ shaka.dash.MpdUtils = class {
481
481
  // Move the children of the loaded xml into the current element.
482
482
  while (rootElem.children.length) {
483
483
  const child = rootElem.children.shift();
484
- if (TXml.isNode(child)) {
485
- child.parent = element;
486
- }
487
484
  element.children.push(child);
488
485
  }
489
486
 
@@ -86,10 +86,19 @@ shaka.dash.SegmentTemplate = class {
86
86
  };
87
87
  } else if (info.segmentDuration) {
88
88
  if (!isUpdate && context.adaptationSet.contentType !== 'image') {
89
+ const periodStart = context.periodInfo.start;
90
+ const periodId = context.period.id;
91
+ const initialPeriodDuration = context.periodInfo.duration;
92
+ const periodDuration =
93
+ (periodId != null && periodDurationMap[periodId]) ||
94
+ initialPeriodDuration;
95
+ const periodEnd = periodDuration ?
96
+ (periodStart + periodDuration) : Infinity;
97
+
89
98
  context.presentationTimeline.notifyMaxSegmentDuration(
90
99
  info.segmentDuration);
91
- context.presentationTimeline.notifyMinSegmentStartTime(
92
- context.periodInfo.start);
100
+ context.presentationTimeline.notifyPeriodDuration(
101
+ periodStart, periodEnd);
93
102
  }
94
103
 
95
104
  return {
@@ -223,8 +223,10 @@ shaka.media.GapJumpingController = class {
223
223
  // often rounds value we want to set as currentTime and we are not able
224
224
  // to jump over the gap.
225
225
  if (shaka.util.Platform.isLegacyEdge() ||
226
- shaka.util.Platform.isXboxOne()) {
227
- jumpTo = Math.ceil((jumpTo + 0.01) * 100) / 100;
226
+ shaka.util.Platform.isXboxOne() ||
227
+ shaka.util.Platform.isTizen()) {
228
+ const gapPadding = this.config_.gapPadding;
229
+ jumpTo = Math.ceil((jumpTo + gapPadding) * 100) / 100;
228
230
  }
229
231
  const seekEnd = this.timeline_.getSeekRangeEnd();
230
232
  if (jumpTo >= seekEnd) {
@@ -331,6 +331,22 @@ shaka.media.PresentationTimeline = class {
331
331
  }
332
332
 
333
333
 
334
+ /**
335
+ * Gives PresentationTimeline an startTime and endTime of the period.
336
+ * This should be only set for Dash.
337
+ *
338
+ * @param {number} startTime
339
+ * @param {number} endTime
340
+ * @export
341
+ */
342
+ notifyPeriodDuration(startTime, endTime) {
343
+ this.notifyMinSegmentStartTime(startTime);
344
+ if (endTime != Infinity && !this.isLive()) {
345
+ this.maxSegmentEndTime_ = Math.max(this.maxSegmentEndTime_, endTime);
346
+ }
347
+ }
348
+
349
+
334
350
  /**
335
351
  * Gets the end time of the last available segment.
336
352
  *
@@ -834,10 +834,10 @@ shaka.media.MetaSegmentIndex = class extends shaka.media.SegmentIndex {
834
834
  * @export
835
835
  */
836
836
  evict(time) {
837
- // evict() is only used internally by the DASH and HLS parser on
838
- // SegmentIndexes, but never on MetaSegmentIndex.
839
- goog.asserts.assert(
840
- false, 'evict() should not be used in MetaSegmentIndex!');
837
+ for (const index of this.indexes_) {
838
+ index.evict(time);
839
+ }
840
+ this.evictEmpty();
841
841
  }
842
842
 
843
843
  /**
@@ -1519,9 +1519,11 @@ shaka.media.StreamingEngine = class {
1519
1519
  logPrefix, 'looking up segment from new stream endTime:', time);
1520
1520
 
1521
1521
  const reverse = this.playerInterface_.getPlaybackRate() < 0;
1522
- mediaState.segmentIterator =
1523
- mediaState.stream.segmentIndex.getIteratorForTime(
1524
- time, /* allowNonIndepedent= */ false, reverse);
1522
+ if (mediaState.stream.segmentIndex) {
1523
+ mediaState.segmentIterator =
1524
+ mediaState.stream.segmentIndex.getIteratorForTime(
1525
+ time, /* allowNonIndepedent= */ false, reverse);
1526
+ }
1525
1527
  const ref = mediaState.segmentIterator &&
1526
1528
  mediaState.segmentIterator.next().value;
1527
1529
  if (ref == null) {
@@ -1545,18 +1547,22 @@ shaka.media.StreamingEngine = class {
1545
1547
  const reverse = this.playerInterface_.getPlaybackRate() < 0;
1546
1548
  let ref = null;
1547
1549
  if (inaccurateTolerance) {
1548
- mediaState.segmentIterator =
1549
- mediaState.stream.segmentIndex.getIteratorForTime(
1550
- lookupTime, /* allowNonIndepedent= */ false, reverse);
1550
+ if (mediaState.stream.segmentIndex) {
1551
+ mediaState.segmentIterator =
1552
+ mediaState.stream.segmentIndex.getIteratorForTime(
1553
+ lookupTime, /* allowNonIndepedent= */ false, reverse);
1554
+ }
1551
1555
  ref = mediaState.segmentIterator &&
1552
1556
  mediaState.segmentIterator.next().value;
1553
1557
  }
1554
1558
  if (!ref) {
1555
1559
  // If we can't find a valid segment with the drifted time, look for a
1556
1560
  // segment with the presentation time.
1557
- mediaState.segmentIterator =
1558
- mediaState.stream.segmentIndex.getIteratorForTime(
1559
- presentationTime, /* allowNonIndepedent= */ false, reverse);
1561
+ if (mediaState.stream.segmentIndex) {
1562
+ mediaState.segmentIterator =
1563
+ mediaState.stream.segmentIndex.getIteratorForTime(
1564
+ presentationTime, /* allowNonIndepedent= */ false, reverse);
1565
+ }
1560
1566
  ref = mediaState.segmentIterator &&
1561
1567
  mediaState.segmentIterator.next().value;
1562
1568
  }
@@ -2505,7 +2511,8 @@ shaka.media.StreamingEngine = class {
2505
2511
  async evict_(mediaState, presentationTime) {
2506
2512
  const segmentIndex = mediaState.stream.segmentIndex;
2507
2513
  if (segmentIndex instanceof shaka.media.MetaSegmentIndex) {
2508
- segmentIndex.evictEmpty();
2514
+ segmentIndex.evict(
2515
+ this.manifest_.presentationTimeline.getSeekRangeStart());
2509
2516
  }
2510
2517
 
2511
2518
  const logPrefix = shaka.media.StreamingEngine.logPrefix_(mediaState);
@@ -103,4 +103,9 @@ shaka.net.NetworkingUtils.EXTENSIONS_TO_MIME_TYPES_ = {
103
103
  'lrc': 'application/x-subtitle-lrc',
104
104
  'ssa': 'text/x-ssa',
105
105
  'ass': 'text/x-ssa',
106
+ 'jpg': 'image/jpeg',
107
+ 'png': 'image/png',
108
+ 'svg': 'image/svg+xml',
109
+ 'webp': 'image/webp',
110
+ 'avif': 'image/avif',
106
111
  };
@@ -1346,7 +1346,7 @@ shaka.offline.Storage = class {
1346
1346
  manifest.presentationTimeline.getSegmentAvailabilityStart();
1347
1347
 
1348
1348
  const numberOfParallelDownloads = config.offline.numberOfParallelDownloads;
1349
- let groupId = 0;
1349
+ let groupId = numberOfParallelDownloads === 0 ? stream.id : 0;
1350
1350
 
1351
1351
  shaka.offline.Storage.forEachSegment_(stream, startTime, (segment) => {
1352
1352
  const pendingSegmentRefId =
@@ -1398,7 +1398,9 @@ shaka.offline.Storage = class {
1398
1398
  codecs: segment.codecs,
1399
1399
  };
1400
1400
  streamDb.segments.push(segmentDB);
1401
- groupId = (groupId + 1) % numberOfParallelDownloads;
1401
+ if (numberOfParallelDownloads !== 0) {
1402
+ groupId = (groupId + 1) % numberOfParallelDownloads;
1403
+ }
1402
1404
  });
1403
1405
 
1404
1406
  return streamDb;
@@ -1593,14 +1595,6 @@ shaka.offline.Storage = class {
1593
1595
  /** @type {!Set.<shaka.extern.Stream>} */
1594
1596
  const set = new Set();
1595
1597
 
1596
- for (const text of manifest.textStreams) {
1597
- set.add(text);
1598
- }
1599
-
1600
- for (const image of manifest.imageStreams) {
1601
- set.add(image);
1602
- }
1603
-
1604
1598
  for (const variant of manifest.variants) {
1605
1599
  if (variant.audio) {
1606
1600
  set.add(variant.audio);
@@ -1610,6 +1604,14 @@ shaka.offline.Storage = class {
1610
1604
  }
1611
1605
  }
1612
1606
 
1607
+ for (const text of manifest.textStreams) {
1608
+ set.add(text);
1609
+ }
1610
+
1611
+ for (const image of manifest.imageStreams) {
1612
+ set.add(image);
1613
+ }
1614
+
1613
1615
  return set;
1614
1616
  }
1615
1617
 
package/lib/player.js CHANGED
@@ -5962,6 +5962,13 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5962
5962
  references.push(reference);
5963
5963
  }
5964
5964
 
5965
+ let segmentMimeType = mimeType;
5966
+ if (references.length) {
5967
+ segmentMimeType = await shaka.net.NetworkingUtils.getMimeType(
5968
+ references[0].getUris()[0],
5969
+ this.networkingEngine_, this.config_.manifest.retryParameters);
5970
+ }
5971
+
5965
5972
  /** @type {shaka.extern.Stream} */
5966
5973
  const stream = {
5967
5974
  id: this.nextExternalStreamId_++,
@@ -5969,7 +5976,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5969
5976
  groupId: null,
5970
5977
  createSegmentIndex: () => Promise.resolve(),
5971
5978
  segmentIndex: new shaka.media.SegmentIndex(references),
5972
- mimeType: mimeType || '',
5979
+ mimeType: segmentMimeType || '',
5973
5980
  codecs: '',
5974
5981
  kind: '',
5975
5982
  encrypted: false,
@@ -5993,7 +6000,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5993
6000
  external: true,
5994
6001
  fastSwitching: false,
5995
6002
  fullMimeTypes: new Set([shaka.util.MimeUtils.getFullType(
5996
- mimeType || '', '')]),
6003
+ segmentMimeType || '', '')]),
5997
6004
  };
5998
6005
 
5999
6006
  if (this.loadMode_ == shaka.Player.LoadMode.SRC_EQUALS) {
@@ -7231,11 +7238,13 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
7231
7238
  this.playhead_.notifyOfBufferingChange();
7232
7239
  // Skip the initial buffer gap
7233
7240
  const startTime = this.mediaSourceEngine_.bufferStart(contentType);
7241
+ const ContentType = shaka.util.ManifestParserUtils.ContentType;
7234
7242
  if (
7235
7243
  !this.isLive() &&
7236
7244
  // If not paused then GapJumpingController will handle this gap.
7237
7245
  this.video_.paused &&
7238
7246
  startTime != null &&
7247
+ contentType != ContentType.TEXT &&
7239
7248
  startTime > 0 &&
7240
7249
  this.playhead_.getTime() < startTime
7241
7250
  ) {
@@ -8062,8 +8071,8 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
8062
8071
  * @define {string} A version number taken from git at compile time.
8063
8072
  * @export
8064
8073
  */
8065
- // eslint-disable-next-line no-useless-concat
8066
- shaka.Player.version = 'v4.11.2' + '-uncompiled'; // x-release-please-version
8074
+ // eslint-disable-next-line no-useless-concat, max-len
8075
+ shaka.Player.version = 'v4.11.4' + '-uncompiled'; // x-release-please-version
8067
8076
 
8068
8077
  // Initialize the deprecation system using the version string we just set
8069
8078
  // on the player.
@@ -65,7 +65,7 @@ shaka.text.TtmlTextParser = class {
65
65
  return cues;
66
66
  }
67
67
 
68
- const tt = TXml.parseXmlString(str, 'tt');
68
+ const tt = TXml.parseXmlString(str, 'tt', /* includeParent= */ true);
69
69
  if (!tt) {
70
70
  throw new shaka.util.Error(
71
71
  shaka.util.Error.Severity.CRITICAL,
@@ -107,6 +107,56 @@ shaka.util.PeriodCombiner = class {
107
107
  return this.imageStreams_;
108
108
  }
109
109
 
110
+ /**
111
+ * Deletes a stream from matchedStreams because it is no longer needed
112
+ *
113
+ * @param {?shaka.extern.Stream} stream
114
+ * @param {string} periodId
115
+ *
116
+ * @export
117
+ */
118
+ deleteStream(stream, periodId) {
119
+ if (!stream) {
120
+ return;
121
+ }
122
+ const ContentType = shaka.util.ManifestParserUtils.ContentType;
123
+ if (stream.type == ContentType.AUDIO) {
124
+ for (const audioStream of this.audioStreams_) {
125
+ audioStream.matchedStreams = audioStream.matchedStreams.filter((s) => {
126
+ return s !== stream;
127
+ });
128
+ }
129
+ } else if (stream.type == ContentType.VIDEO) {
130
+ for (const videoStream of this.videoStreams_) {
131
+ videoStream.matchedStreams = videoStream.matchedStreams.filter((s) => {
132
+ return s !== stream;
133
+ });
134
+ if (videoStream.trickModeVideo) {
135
+ videoStream.trickModeVideo.matchedStreams =
136
+ videoStream.trickModeVideo.matchedStreams.filter((s) => {
137
+ return s !== stream;
138
+ });
139
+ }
140
+ }
141
+ } else if (stream.type == ContentType.TEXT) {
142
+ for (const textStream of this.textStreams_) {
143
+ textStream.matchedStreams = textStream.matchedStreams.filter((s) => {
144
+ return s !== stream;
145
+ });
146
+ }
147
+ } else if (stream.type == ContentType.IMAGE) {
148
+ for (const imageStream of this.imageStreams_) {
149
+ imageStream.matchedStreams = imageStream.matchedStreams.filter((s) => {
150
+ return s !== stream;
151
+ });
152
+ }
153
+ }
154
+ if (stream.segmentIndex) {
155
+ stream.closeSegmentIndex();
156
+ }
157
+ this.usedPeriodIds_.delete(periodId);
158
+ }
159
+
110
160
  /**
111
161
  * Returns an object that contains arrays of streams by type
112
162
  * @param {!Array<shaka.extern.Period>} periods
@@ -600,7 +650,8 @@ shaka.util.PeriodCombiner = class {
600
650
  const streams = outputStream.matchedStreams;
601
651
  goog.asserts.assert(streams, 'matched streams should be valid');
602
652
 
603
- for (const stream of streams) {
653
+ for (let i = firstNewPeriodIndex; i < streams.length; i++) {
654
+ const stream = streams[i];
604
655
  operations.push(stream.createSegmentIndex());
605
656
  if (stream.trickModeVideo && !stream.trickModeVideo.segmentIndex) {
606
657
  operations.push(stream.trickModeVideo.createSegmentIndex());
@@ -783,7 +834,8 @@ shaka.util.PeriodCombiner = class {
783
834
  clone.segmentIndex = null;
784
835
  clone.emsgSchemeIdUris = [];
785
836
  clone.keyIds = new Set();
786
- clone.closedCaptions = null;
837
+ clone.closedCaptions = stream.closedCaptions ?
838
+ new Map(stream.closedCaptions) : null;
787
839
  clone.trickModeVideo = null;
788
840
 
789
841
  return clone;
@@ -808,7 +860,8 @@ shaka.util.PeriodCombiner = class {
808
860
  clone.keyIds = new Set();
809
861
  clone.segments = [];
810
862
  clone.variantIds = [];
811
- clone.closedCaptions = null;
863
+ clone.closedCaptions = streamDb.closedCaptions ?
864
+ new Map(streamDb.closedCaptions) : null;
812
865
 
813
866
  return clone;
814
867
  }
@@ -206,6 +206,7 @@ shaka.util.PlayerConfiguration = class {
206
206
  alwaysStreamText: false,
207
207
  startAtSegmentBoundary: false,
208
208
  gapDetectionThreshold: 0.5,
209
+ gapPadding: 0.01,
209
210
  gapJumpTimerTime: 0.25 /* seconds */,
210
211
  durationBackoff: 1,
211
212
  // Offset by 5 seconds since Chromecast takes a few seconds to start
@@ -283,6 +284,10 @@ shaka.util.PlayerConfiguration = class {
283
284
  streaming.stallSkip = 0;
284
285
  }
285
286
 
287
+ if (shaka.util.Platform.isTizen()) {
288
+ streaming.gapPadding = 2;
289
+ }
290
+
286
291
  const offline = {
287
292
  // We need to set this to a throw-away implementation for now as our
288
293
  // default implementation will need to reference other fields in the
package/lib/util/tXml.js CHANGED
@@ -44,11 +44,13 @@ shaka.util.TXml = class {
44
44
  * Parse some data
45
45
  * @param {BufferSource} data
46
46
  * @param {string=} expectedRootElemName
47
+ * @param {boolean=} includeParent
47
48
  * @return {shaka.extern.xml.Node | null}
48
49
  */
49
- static parseXml(data, expectedRootElemName) {
50
+ static parseXml(data, expectedRootElemName, includeParent = false) {
50
51
  const xmlString = shaka.util.StringUtils.fromBytesAutoDetect(data);
51
- return shaka.util.TXml.parseXmlString(xmlString, expectedRootElemName);
52
+ return shaka.util.TXml.parseXmlString(
53
+ xmlString, expectedRootElemName, includeParent);
52
54
  }
53
55
 
54
56
  /**
@@ -57,8 +59,9 @@ shaka.util.TXml = class {
57
59
  * @param {string=} expectedRootElemName
58
60
  * @return {shaka.extern.xml.Node | null}
59
61
  */
60
- static parseXmlString(xmlString, expectedRootElemName) {
61
- const result = shaka.util.TXml.parse(xmlString);
62
+ static parseXmlString(xmlString, expectedRootElemName,
63
+ includeParent = false) {
64
+ const result = shaka.util.TXml.parse(xmlString, includeParent);
62
65
  if (!expectedRootElemName && result.length) {
63
66
  return result[0];
64
67
  }
@@ -110,9 +113,10 @@ shaka.util.TXml = class {
110
113
  * parseXML / html into a DOM Object,
111
114
  * with no validation and some failure tolerance
112
115
  * @param {string} S your XML to parse
116
+ * @param {boolean} includeParent
113
117
  * @return {Array.<shaka.extern.xml.Node>}
114
118
  */
115
- static parse(S) {
119
+ static parse(S, includeParent) {
116
120
  let pos = 0;
117
121
 
118
122
  const openBracket = '<';
@@ -287,9 +291,11 @@ shaka.util.TXml = class {
287
291
  children,
288
292
  parent: null,
289
293
  };
290
- for (let i = 0; i < children.length; i++) {
291
- if (typeof children[i] !== 'string') {
292
- children[i].parent = node;
294
+ if (includeParent) {
295
+ for (let i = 0; i < children.length; i++) {
296
+ if (typeof children[i] !== 'string') {
297
+ children[i].parent = node;
298
+ }
293
299
  }
294
300
  }
295
301
  return node;
@@ -331,7 +337,9 @@ shaka.util.TXml = class {
331
337
  for (let i = 0; i < childrenLength; i++) {
332
338
  const childrenValue = children[i];
333
339
  if (typeof childrenValue !== 'string') {
334
- childrenValue.parent = node;
340
+ if (includeParent) {
341
+ childrenValue.parent = node;
342
+ }
335
343
  } else if (i == childrenLength - 1 && childrenValue == '\n') {
336
344
  children.pop();
337
345
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "shaka-player",
3
3
  "description": "DASH/EME video player library",
4
- "version": "4.11.2",
4
+ "version": "4.11.4",
5
5
  "homepage": "https://github.com/shaka-project/shaka-player",
6
6
  "author": "Google",
7
7
  "maintainers": [
package/ui/controls.js CHANGED
@@ -263,6 +263,16 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget {
263
263
  this.castProxy_ = null;
264
264
  }
265
265
 
266
+ if (this.spinnerContainer_) {
267
+ this.videoContainer_.removeChild(this.spinnerContainer_);
268
+ this.spinnerContainer_ = null;
269
+ }
270
+
271
+ if (this.clientAdContainer_) {
272
+ this.videoContainer_.removeChild(this.clientAdContainer_);
273
+ this.clientAdContainer_ = null;
274
+ }
275
+
266
276
  if (this.localPlayer_) {
267
277
  await this.localPlayer_.destroy();
268
278
  this.localPlayer_ = null;
@@ -524,6 +534,8 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget {
524
534
  * @export
525
535
  */
526
536
  getClientSideAdContainer() {
537
+ goog.asserts.assert(
538
+ this.clientAdContainer_, 'No client ad container after destruction!');
527
539
  return this.clientAdContainer_;
528
540
  }
529
541
 
@@ -982,7 +994,7 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget {
982
994
 
983
995
  /** @private */
984
996
  addBufferingSpinner_() {
985
- /** @private {!HTMLElement} */
997
+ /** @private {HTMLElement} */
986
998
  this.spinnerContainer_ = shaka.util.Dom.createHTMLElement('div');
987
999
  this.spinnerContainer_.classList.add('shaka-spinner-container');
988
1000
  this.videoContainer_.appendChild(this.spinnerContainer_);
@@ -1139,7 +1151,7 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget {
1139
1151
  * @private
1140
1152
  */
1141
1153
  addClientAdContainer_() {
1142
- /** @private {!HTMLElement} */
1154
+ /** @private {HTMLElement} */
1143
1155
  this.clientAdContainer_ = shaka.util.Dom.createHTMLElement('div');
1144
1156
  this.clientAdContainer_.classList.add('shaka-client-side-ad-container');
1145
1157
  shaka.ui.Utils.setDisplay(this.clientAdContainer_, false);