shaka-player 4.8.0 → 4.8.2

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 (38) hide show
  1. package/AUTHORS +1 -0
  2. package/CONTRIBUTORS +1 -0
  3. package/dist/deps.js +3 -3
  4. package/dist/locales.js +33 -33
  5. package/dist/shaka-player.compiled.d.ts +3 -2
  6. package/dist/shaka-player.compiled.debug.d.ts +3 -2
  7. package/dist/shaka-player.compiled.debug.externs.js +8 -2
  8. package/dist/shaka-player.compiled.debug.js +208 -199
  9. package/dist/shaka-player.compiled.debug.map +3 -3
  10. package/dist/shaka-player.compiled.externs.js +8 -2
  11. package/dist/shaka-player.compiled.js +1017 -1016
  12. package/dist/shaka-player.compiled.map +3 -3
  13. package/dist/shaka-player.ui.d.ts +3 -2
  14. package/dist/shaka-player.ui.debug.d.ts +3 -2
  15. package/dist/shaka-player.ui.debug.externs.js +8 -2
  16. package/dist/shaka-player.ui.debug.js +149 -140
  17. package/dist/shaka-player.ui.debug.map +3 -3
  18. package/dist/shaka-player.ui.externs.js +8 -2
  19. package/dist/shaka-player.ui.js +1282 -1281
  20. package/dist/shaka-player.ui.map +3 -3
  21. package/lib/dash/dash_parser.js +9 -3
  22. package/lib/dash/mpd_utils.js +0 -6
  23. package/lib/dash/segment_template.js +24 -1
  24. package/lib/media/playhead.js +3 -3
  25. package/lib/media/segment_index.js +54 -18
  26. package/lib/media/segment_prefetch.js +20 -4
  27. package/lib/media/segment_reference.js +12 -0
  28. package/lib/media/streaming_engine.js +41 -3
  29. package/lib/mss/mss_parser.js +7 -2
  30. package/lib/player.js +2 -1
  31. package/lib/text/ui_text_displayer.js +12 -1
  32. package/lib/util/cmcd_manager.js +3 -2
  33. package/lib/util/platform.js +21 -0
  34. package/lib/util/player_configuration.js +28 -20
  35. package/lib/util/stream_utils.js +6 -2
  36. package/lib/util/tXml.js +1 -1
  37. package/package.json +1 -1
  38. package/ui/resolution_selection.js +1 -0
@@ -30,6 +30,7 @@ goog.require('shaka.util.MimeUtils');
30
30
  goog.require('shaka.util.Networking');
31
31
  goog.require('shaka.util.OperationManager');
32
32
  goog.require('shaka.util.PeriodCombiner');
33
+ goog.require('shaka.util.PlayerConfiguration');
33
34
  goog.require('shaka.util.StringUtils');
34
35
  goog.require('shaka.util.Timer');
35
36
  goog.require('shaka.util.TXml');
@@ -338,7 +339,9 @@ shaka.dash.DashParser = class {
338
339
  async parseManifest_(data, finalManifestUri) {
339
340
  let manifestData = data;
340
341
  const manifestPreprocessor = this.config_.dash.manifestPreprocessor;
341
- if (manifestPreprocessor) {
342
+ const defaultManifestPreprocessor =
343
+ shaka.util.PlayerConfiguration.defaultManifestPreprocessor;
344
+ if (manifestPreprocessor != defaultManifestPreprocessor) {
342
345
  shaka.Deprecate.deprecateFeature(5,
343
346
  'manifest.dash.manifestPreprocessor configuration',
344
347
  'Please Use manifest.dash.manifestPreprocessorTXml instead.');
@@ -364,7 +367,9 @@ shaka.dash.DashParser = class {
364
367
  }
365
368
  const manifestPreprocessorTXml =
366
369
  this.config_.dash.manifestPreprocessorTXml;
367
- if (manifestPreprocessorTXml) {
370
+ const defaultManifestPreprocessorTXml =
371
+ shaka.util.PlayerConfiguration.defaultManifestPreprocessorTXml;
372
+ if (manifestPreprocessorTXml != defaultManifestPreprocessorTXml) {
368
373
  manifestPreprocessorTXml(mpd);
369
374
  }
370
375
 
@@ -2074,8 +2079,9 @@ shaka.dash.DashParser = class {
2074
2079
  * @private
2075
2080
  */
2076
2081
  async requestForTiming_(getBaseUris, uri, method) {
2082
+ const uris = [shaka.util.StringUtils.htmlUnescape(uri)];
2077
2083
  const requestUris =
2078
- shaka.util.ManifestParserUtils.resolveUris(getBaseUris(), [uri]);
2084
+ shaka.util.ManifestParserUtils.resolveUris(getBaseUris(), uris);
2079
2085
  const request = shaka.net.NetworkingEngine.makeRequest(
2080
2086
  requestUris, this.config_.retryParameters);
2081
2087
  request.method = method;
@@ -282,12 +282,6 @@ shaka.dash.MpdUtils = class {
282
282
  const durationStr =
283
283
  MpdUtils.inheritAttribute(context, callback, 'duration');
284
284
  let segmentDuration = TXml.parsePositiveInt(durationStr || '');
285
- const ContentType = shaka.util.ManifestParserUtils.ContentType;
286
- // TODO: The specification is not clear, check this once it is resolved:
287
- // https://github.com/Dash-Industry-Forum/DASH-IF-IOP/issues/404
288
- if (context.representation.contentType == ContentType.IMAGE) {
289
- segmentDuration = TXml.parseFloat(durationStr || '');
290
- }
291
285
  if (segmentDuration) {
292
286
  segmentDuration /= timescale;
293
287
  }
@@ -398,6 +398,11 @@ shaka.dash.SegmentTemplate = class {
398
398
  Math.ceil(availablePeriodTimes[1] / segmentDuration) - 1,
399
399
  ];
400
400
 
401
+ // For Low Latency we can request the partial current position.
402
+ if (context.representation.availabilityTimeOffset) {
403
+ availablePeriodPositions[1]++;
404
+ }
405
+
401
406
  // In absolute positions.
402
407
  const availablePresentationPositions =
403
408
  availablePeriodPositions.map((x) => x + startNumber);
@@ -701,7 +706,7 @@ shaka.dash.TimelineSegmentIndex = class extends shaka.media.SegmentIndex {
701
706
  */
702
707
  appendTemplateInfo(info, periodStart, periodEnd, shouldFit,
703
708
  initSegmentReference) {
704
- this.initSegmentReference_ = initSegmentReference;
709
+ this.updateInitSegmentReference(initSegmentReference);
705
710
  if (!this.templateInfo_) {
706
711
  this.templateInfo_ = info;
707
712
  this.periodStart_ = periodStart;
@@ -732,6 +737,24 @@ shaka.dash.TimelineSegmentIndex = class extends shaka.media.SegmentIndex {
732
737
  }
733
738
  }
734
739
 
740
+ /**
741
+ * Updates the init segment reference and propagates the update to all
742
+ * references.
743
+ * @param {shaka.media.InitSegmentReference} initSegmentReference
744
+ */
745
+ updateInitSegmentReference(initSegmentReference) {
746
+ if (this.initSegmentReference_ === initSegmentReference) {
747
+ return;
748
+ }
749
+
750
+ this.initSegmentReference_ = initSegmentReference;
751
+ for (const reference of this.references) {
752
+ if (reference) {
753
+ reference.updateInitSegmentReference(initSegmentReference);
754
+ }
755
+ }
756
+ }
757
+
735
758
  /**
736
759
  *
737
760
  * @param {number} time
@@ -382,10 +382,10 @@ shaka.media.MediaSourcePlayhead = class {
382
382
  seekStart = seekEnd - this.minSeekRange_;
383
383
  }
384
384
 
385
- if (currentTime < seekStart || currentTime > seekEnd) {
386
- // playhead is outside of the seekrange. Move playhead to catch up.
385
+ if (currentTime < seekStart) {
386
+ // The seek range has moved past the playhead. Move ahead to catch up.
387
387
  const targetTime = this.reposition_(currentTime);
388
- shaka.log.info('Jumping by ' + (targetTime - currentTime) +
388
+ shaka.log.info('Jumping forward ' + (targetTime - currentTime) +
389
389
  ' seconds to catch up with the seek range.');
390
390
  this.mediaElement_.currentTime = targetTime;
391
391
  }
@@ -436,10 +436,11 @@ shaka.media.SegmentIndex = class {
436
436
  *
437
437
  * @param {number} time
438
438
  * @param {boolean=} allowNonIndepedent
439
+ * @param {boolean=} reverse
439
440
  * @return {?shaka.media.SegmentIterator}
440
441
  * @export
441
442
  */
442
- getIteratorForTime(time, allowNonIndepedent = false) {
443
+ getIteratorForTime(time, allowNonIndepedent = false, reverse = false) {
443
444
  let index = this.find(time);
444
445
  if (index == null) {
445
446
  return null;
@@ -476,7 +477,8 @@ shaka.media.SegmentIndex = class {
476
477
  }
477
478
  }
478
479
  }
479
- return new shaka.media.SegmentIterator(this, index, partialSegmentIndex);
480
+ return new shaka.media.SegmentIterator(
481
+ this, index, partialSegmentIndex, reverse);
480
482
  }
481
483
 
482
484
  /**
@@ -552,8 +554,9 @@ shaka.media.SegmentIterator = class {
552
554
  * @param {shaka.media.SegmentIndex} segmentIndex
553
555
  * @param {number} index
554
556
  * @param {number} partialSegmentIndex
557
+ * @param {boolean} reverse
555
558
  */
556
- constructor(segmentIndex, index, partialSegmentIndex) {
559
+ constructor(segmentIndex, index, partialSegmentIndex, reverse) {
557
560
  /** @private {shaka.media.SegmentIndex} */
558
561
  this.segmentIndex_ = segmentIndex;
559
562
 
@@ -562,6 +565,17 @@ shaka.media.SegmentIterator = class {
562
565
 
563
566
  /** @private {number} */
564
567
  this.currentPartialPosition_ = partialSegmentIndex;
568
+
569
+ /** @private {boolean} */
570
+ this.reverse = reverse;
571
+ }
572
+
573
+ /**
574
+ * @param {boolean} reverse
575
+ * @export
576
+ */
577
+ setReverse(reverse) {
578
+ this.reverse = reverse;
565
579
  }
566
580
 
567
581
  /**
@@ -606,25 +620,47 @@ shaka.media.SegmentIterator = class {
606
620
  next() {
607
621
  const ref = this.segmentIndex_.get(this.currentPosition_);
608
622
 
609
- if (ref && ref.hasPartialSegments()) {
610
- // If the regular segment contains partial segments, move to the next
611
- // partial SegmentReference.
612
- this.currentPartialPosition_++;
613
- // If the current regular segment has been published completely, and
614
- // we've reached the end of its partial segments list, move to the next
615
- // regular segment.
616
- // If the Partial Segments list is still on the fly, do not move to
617
- // the next regular segment.
618
- if (ref.hasAllPartialSegments() &&
619
- this.currentPartialPosition_ == ref.partialReferences.length) {
623
+ if (!this.reverse) {
624
+ if (ref && ref.hasPartialSegments()) {
625
+ // If the regular segment contains partial segments, move to the next
626
+ // partial SegmentReference.
627
+ this.currentPartialPosition_++;
628
+ // If the current regular segment has been published completely, and
629
+ // we've reached the end of its partial segments list, move to the next
630
+ // regular segment.
631
+ // If the Partial Segments list is still on the fly, do not move to
632
+ // the next regular segment.
633
+ if (ref.hasAllPartialSegments() &&
634
+ this.currentPartialPosition_ == ref.partialReferences.length) {
635
+ this.currentPosition_++;
636
+ this.currentPartialPosition_ = 0;
637
+ }
638
+ } else {
639
+ // If the regular segment doesn't contain partial segments, move to the
640
+ // next regular segment.
620
641
  this.currentPosition_++;
621
642
  this.currentPartialPosition_ = 0;
622
643
  }
623
644
  } else {
624
- // If the regular segment doesn't contain partial segments, move to the
625
- // next regular segment.
626
- this.currentPosition_++;
627
- this.currentPartialPosition_ = 0;
645
+ if (ref && ref.hasPartialSegments()) {
646
+ // If the regular segment contains partial segments, move to the
647
+ // previous partial SegmentReference.
648
+ this.currentPartialPosition_--;
649
+ if (this.currentPartialPosition_ < 0) {
650
+ this.currentPosition_--;
651
+ const prevRef = this.segmentIndex_.get(this.currentPosition_);
652
+ if (prevRef && prevRef.hasPartialSegments()) {
653
+ this.currentPartialPosition_ = prevRef.partialReferences.length - 1;
654
+ } else {
655
+ this.currentPartialPosition_ = 0;
656
+ }
657
+ }
658
+ } else {
659
+ // If the regular segment doesn't contain partial segments, move to the
660
+ // previous regular segment.
661
+ this.currentPosition_--;
662
+ this.currentPartialPosition_ = 0;
663
+ }
628
664
  }
629
665
 
630
666
  const res = this.current();
@@ -147,7 +147,12 @@ shaka.media.SegmentPrefetch = class {
147
147
  }
148
148
 
149
149
  // init segments are ignored from the prefetch limit
150
- if (!this.initSegmentPrefetchMap_.has(initSegmentReference)) {
150
+ const initSegments = Array.from(this.initSegmentPrefetchMap_.keys());
151
+ const someReference = initSegments.some((reference) => {
152
+ return shaka.media.InitSegmentReference.equal(
153
+ reference, initSegmentReference);
154
+ });
155
+ if (!someReference) {
151
156
  const segmentPrefetchOperation =
152
157
  new shaka.media.SegmentPrefetchOperation(this.fetchDispatcher_);
153
158
  segmentPrefetchOperation.dispatchFetch(
@@ -233,6 +238,15 @@ shaka.media.SegmentPrefetch = class {
233
238
  this.prefetchPosTime_ = 0;
234
239
  }
235
240
 
241
+ /**
242
+ * Remove a reference of prefetched segment if already exists.
243
+ * @param {!shaka.media.SegmentReference} reference
244
+ * @public
245
+ */
246
+ removeReference(reference) {
247
+ this.abortPrefetchedSegment_(reference);
248
+ }
249
+
236
250
  /**
237
251
  * @param {number} time
238
252
  * @param {boolean=} clearInitSegments
@@ -261,9 +275,11 @@ shaka.media.SegmentPrefetch = class {
261
275
  const segmentReferences = Array.from(this.segmentPrefetchMap_.keys());
262
276
  for (const initSegmentReference of this.initSegmentPrefetchMap_.keys()) {
263
277
  // if no segment references this init segment, we should remove it.
264
- if (!segmentReferences.some(
265
- (segmentReference) =>
266
- segmentReference.initSegmentReference === initSegmentReference)) {
278
+ const someReference = segmentReferences.some((segmentReference) => {
279
+ return shaka.media.InitSegmentReference.equal(
280
+ segmentReference.initSegmentReference, initSegmentReference);
281
+ });
282
+ if (!someReference) {
267
283
  this.abortPrefetchedSegment_(initSegmentReference);
268
284
  }
269
285
  }
@@ -621,6 +621,18 @@ shaka.media.SegmentReference = class {
621
621
  getSegmentData() {
622
622
  return this.segmentData;
623
623
  }
624
+
625
+ /**
626
+ * Updates the init segment reference and propagates the update to all partial
627
+ * references.
628
+ * @param {shaka.media.InitSegmentReference} initSegmentReference
629
+ */
630
+ updateInitSegmentReference(initSegmentReference) {
631
+ this.initSegmentReference = initSegmentReference;
632
+ for (const partialReference of this.partialReferences) {
633
+ partialReference.updateInitSegmentReference(initSegmentReference);
634
+ }
635
+ }
624
636
  };
625
637
 
626
638
 
@@ -360,6 +360,8 @@ shaka.media.StreamingEngine = class {
360
360
  setTrickPlay(on) {
361
361
  const ContentType = shaka.util.ManifestParserUtils.ContentType;
362
362
 
363
+ this.updateSegmentIteratorReverse_();
364
+
363
365
  const mediaState = this.mediaStates_.get(ContentType.VIDEO);
364
366
  if (!mediaState) {
365
367
  return;
@@ -1422,8 +1424,10 @@ shaka.media.StreamingEngine = class {
1422
1424
  shaka.log.v1(
1423
1425
  logPrefix, 'looking up segment from new stream endTime:', time);
1424
1426
 
1427
+ const reverse = this.playerInterface_.getPlaybackRate() < 0;
1425
1428
  mediaState.segmentIterator =
1426
- mediaState.stream.segmentIndex.getIteratorForTime(time);
1429
+ mediaState.stream.segmentIndex.getIteratorForTime(
1430
+ time, /* allowNonIndepedent= */ false, reverse);
1427
1431
  const ref = mediaState.segmentIterator &&
1428
1432
  mediaState.segmentIterator.next().value;
1429
1433
  if (ref == null) {
@@ -1443,10 +1447,12 @@ shaka.media.StreamingEngine = class {
1443
1447
  'lookupTime:', lookupTime,
1444
1448
  'presentationTime:', presentationTime);
1445
1449
 
1450
+ const reverse = this.playerInterface_.getPlaybackRate() < 0;
1446
1451
  let ref = null;
1447
1452
  if (inaccurateTolerance) {
1448
1453
  mediaState.segmentIterator =
1449
- mediaState.stream.segmentIndex.getIteratorForTime(lookupTime);
1454
+ mediaState.stream.segmentIndex.getIteratorForTime(
1455
+ lookupTime, /* allowNonIndepedent= */ false, reverse);
1450
1456
  ref = mediaState.segmentIterator &&
1451
1457
  mediaState.segmentIterator.next().value;
1452
1458
  }
@@ -1454,7 +1460,8 @@ shaka.media.StreamingEngine = class {
1454
1460
  // If we can't find a valid segment with the drifted time, look for a
1455
1461
  // segment with the presentation time.
1456
1462
  mediaState.segmentIterator =
1457
- mediaState.stream.segmentIndex.getIteratorForTime(presentationTime);
1463
+ mediaState.stream.segmentIndex.getIteratorForTime(
1464
+ presentationTime, /* allowNonIndepedent= */ false, reverse);
1458
1465
  ref = mediaState.segmentIterator &&
1459
1466
  mediaState.segmentIterator.next().value;
1460
1467
  }
@@ -1695,6 +1702,11 @@ shaka.media.StreamingEngine = class {
1695
1702
  error.code);
1696
1703
  mediaState.hasError = true;
1697
1704
 
1705
+ if (error.category == shaka.util.Error.Category.NETWORK &&
1706
+ mediaState.segmentPrefetch) {
1707
+ mediaState.segmentPrefetch.removeReference(reference);
1708
+ }
1709
+
1698
1710
  error.severity = shaka.util.Error.Severity.CRITICAL;
1699
1711
  await this.handleStreamingError_(mediaState, error);
1700
1712
  }
@@ -2752,6 +2764,29 @@ shaka.media.StreamingEngine = class {
2752
2764
  }
2753
2765
  }
2754
2766
 
2767
+ /**
2768
+ * Update the segment iterator direction.
2769
+ *
2770
+ * @private
2771
+ */
2772
+ updateSegmentIteratorReverse_() {
2773
+ const ContentType = shaka.util.ManifestParserUtils.ContentType;
2774
+
2775
+ const reverse = this.playerInterface_.getPlaybackRate() < 0;
2776
+ const videoState = this.mediaStates_.get(ContentType.VIDEO);
2777
+ if (videoState && videoState.segmentIterator) {
2778
+ videoState.segmentIterator.setReverse(reverse);
2779
+ }
2780
+ const audioState = this.mediaStates_.get(ContentType.AUDIO);
2781
+ if (audioState && audioState.segmentIterator) {
2782
+ audioState.segmentIterator.setReverse(reverse);
2783
+ }
2784
+ const textState = this.mediaStates_.get(ContentType.TEXT);
2785
+ if (textState && textState.segmentIterator) {
2786
+ textState.segmentIterator.setReverse(reverse);
2787
+ }
2788
+ }
2789
+
2755
2790
  /**
2756
2791
  * @param {shaka.media.StreamingEngine.MediaState_} mediaState
2757
2792
  * @return {string} A log prefix of the form ($CONTENT_TYPE:$STREAM_ID), e.g.,
@@ -2768,6 +2803,7 @@ shaka.media.StreamingEngine = class {
2768
2803
  * @typedef {{
2769
2804
  * getPresentationTime: function():number,
2770
2805
  * getBandwidthEstimate: function():number,
2806
+ * getPlaybackRate: function():number,
2771
2807
  * mediaSourceEngine: !shaka.media.MediaSourceEngine,
2772
2808
  * netEngine: shaka.net.NetworkingEngine,
2773
2809
  * onError: function(!shaka.util.Error),
@@ -2787,6 +2823,8 @@ shaka.media.StreamingEngine = class {
2787
2823
  * viewer is seeing on screen right now.
2788
2824
  * @property {function():number} getBandwidthEstimate
2789
2825
  * Get the estimated bandwidth in bits per second.
2826
+ * @property {function():number} getPlaybackRate
2827
+ * Get the playback rate
2790
2828
  * @property {!shaka.media.MediaSourceEngine} mediaSourceEngine
2791
2829
  * The MediaSourceEngine. The caller retains ownership.
2792
2830
  * @property {shaka.net.NetworkingEngine} netEngine
@@ -23,6 +23,7 @@ goog.require('shaka.util.ManifestParserUtils');
23
23
  goog.require('shaka.util.MimeUtils');
24
24
  goog.require('shaka.util.Mp4Generator');
25
25
  goog.require('shaka.util.OperationManager');
26
+ goog.require('shaka.util.PlayerConfiguration');
26
27
  goog.require('shaka.util.Timer');
27
28
  goog.require('shaka.util.TXml');
28
29
  goog.require('shaka.util.XmlUtils');
@@ -282,7 +283,9 @@ shaka.mss.MssParser = class {
282
283
  parseManifest_(data, finalManifestUri) {
283
284
  let manifestData = data;
284
285
  const manifestPreprocessor = this.config_.mss.manifestPreprocessor;
285
- if (manifestPreprocessor) {
286
+ const defaultManifestPreprocessor =
287
+ shaka.util.PlayerConfiguration.defaultManifestPreprocessor;
288
+ if (manifestPreprocessor != defaultManifestPreprocessor) {
286
289
  shaka.Deprecate.deprecateFeature(5,
287
290
  'manifest.mss.manifestPreprocessor configuration',
288
291
  'Please Use manifest.mss.manifestPreprocessorTXml instead.');
@@ -307,7 +310,9 @@ shaka.mss.MssParser = class {
307
310
  finalManifestUri);
308
311
  }
309
312
  const manifestPreprocessorTXml = this.config_.mss.manifestPreprocessorTXml;
310
- if (manifestPreprocessorTXml) {
313
+ const defaultManifestPreprocessorTXml =
314
+ shaka.util.PlayerConfiguration.defaultManifestPreprocessorTXml;
315
+ if (manifestPreprocessorTXml != defaultManifestPreprocessorTXml) {
311
316
  manifestPreprocessorTXml(mss);
312
317
  }
313
318
  this.processManifest_(mss, finalManifestUri);
package/lib/player.js CHANGED
@@ -3246,6 +3246,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
3246
3246
  const playerInterface = {
3247
3247
  getPresentationTime: () => this.playhead_ ? this.playhead_.getTime() : 0,
3248
3248
  getBandwidthEstimate: () => this.abrManager_.getBandwidthEstimate(),
3249
+ getPlaybackRate: () => this.getPlaybackRate(),
3249
3250
  mediaSourceEngine: this.mediaSourceEngine_,
3250
3251
  netEngine: this.networkingEngine_,
3251
3252
  onError: (error) => this.onError_(error),
@@ -7181,7 +7182,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
7181
7182
  * @export
7182
7183
  */
7183
7184
  // eslint-disable-next-line no-useless-concat
7184
- shaka.Player.version = 'v4.8.0' + '-uncompiled'; // x-release-please-version
7185
+ shaka.Player.version = 'v4.8.2' + '-uncompiled'; // x-release-please-version
7185
7186
 
7186
7187
  // Initialize the deprecation system using the version string we just set
7187
7188
  // on the player.
@@ -8,6 +8,7 @@
8
8
  goog.provide('shaka.text.UITextDisplayer');
9
9
 
10
10
  goog.require('goog.asserts');
11
+ goog.require('shaka.Deprecate');
11
12
  goog.require('shaka.text.Cue');
12
13
  goog.require('shaka.text.CueRegion');
13
14
  goog.require('shaka.util.Dom');
@@ -65,12 +66,22 @@ shaka.text.UITextDisplayer = class {
65
66
 
66
67
  this.videoContainer_.appendChild(this.textContainer_);
67
68
 
69
+ if (!config || !config.captionsUpdatePeriod) {
70
+ shaka.Deprecate.deprecateFeature(5,
71
+ 'UITextDisplayer w/ config',
72
+ 'Please migrate to initializing UITextDisplayer with a config.');
73
+ }
74
+
75
+ /** @private {number} */
76
+ const updatePeriod = (config && config.captionsUpdatePeriod) ?
77
+ config.captionsUpdatePeriod : 0.25;
78
+
68
79
  /** @private {shaka.util.Timer} */
69
80
  this.captionsTimer_ = new shaka.util.Timer(() => {
70
81
  if (!this.video_.paused) {
71
82
  this.updateCaptions_();
72
83
  }
73
- }).tickEvery(config.captionsUpdatePeriod);
84
+ }).tickEvery(updatePeriod);
74
85
 
75
86
  /**
76
87
  * Maps cues to cue elements. Specifically points out the wrapper element of
@@ -187,12 +187,12 @@ shaka.util.CmcdManager = class {
187
187
 
188
188
  const stream = context.stream;
189
189
  if (stream) {
190
+ const playbackRate = this.playerInterface_.getPlaybackRate();
190
191
  if (isMedia) {
191
192
  data.bl = this.getBufferLength_(stream.type);
192
193
  if (data.ot !== ObjectType.TIMED_TEXT) {
193
194
  const remainingBufferLength =
194
195
  this.getRemainingBufferLength_(stream.type);
195
- const playbackRate = this.playerInterface_.getPlaybackRate();
196
196
  if (playbackRate) {
197
197
  data.dl = remainingBufferLength / Math.abs(playbackRate);
198
198
  } else {
@@ -206,8 +206,9 @@ shaka.util.CmcdManager = class {
206
206
  }
207
207
 
208
208
  if (stream.segmentIndex && segment) {
209
+ const reverse = playbackRate < 0;
209
210
  const iterator = stream.segmentIndex.getIteratorForTime(
210
- segment.endTime, /* allowNonIndepedent= */ true);
211
+ segment.endTime, /* allowNonIndepedent= */ true, reverse);
211
212
  if (iterator) {
212
213
  const nextSegment = iterator.next().value;
213
214
  if (nextSegment && nextSegment != segment) {
@@ -186,6 +186,17 @@ shaka.util.Platform = class {
186
186
  return shaka.util.Platform.userAgentContains_('CrKey');
187
187
  }
188
188
 
189
+ /**
190
+ * Check if the current platform is a Google Chromecast with Android
191
+ * (i.e. Chromecast with GoogleTV).
192
+ *
193
+ * @return {boolean}
194
+ */
195
+ static isAndroidCastDevice() {
196
+ const Platform = shaka.util.Platform;
197
+ return Platform.isChromecast() && Platform.isAndroid();
198
+ }
199
+
189
200
  /**
190
201
  * Returns a major version number for Chrome, or Chromium-based browsers.
191
202
  *
@@ -416,6 +427,9 @@ shaka.util.Platform = class {
416
427
  return navigator.userAgentData.platform.toLowerCase() == 'macos';
417
428
  }
418
429
  // Fall back to the old API, with less strict matching.
430
+ if (!navigator.platform) {
431
+ return false;
432
+ }
419
433
  return navigator.platform.toLowerCase().includes('mac');
420
434
  }
421
435
 
@@ -430,6 +444,9 @@ shaka.util.Platform = class {
430
444
  return navigator.userAgentData.platform.toLowerCase() == 'windows';
431
445
  }
432
446
  // Fall back to the old API, with less strict matching.
447
+ if (!navigator.platform) {
448
+ return false;
449
+ }
433
450
  return navigator.platform.toLowerCase().includes('windows');
434
451
  }
435
452
 
@@ -556,6 +573,10 @@ shaka.util.Platform = class {
556
573
  Platform.isWebOS4() || Platform.isWebOS5()) {
557
574
  return false;
558
575
  }
576
+ // Older chromecasts without GoogleTV seem to not support SMOOTH properly.
577
+ if (Platform.isChromecast() && !Platform.isAndroidCastDevice()) {
578
+ return false;
579
+ }
559
580
  // See: https://chromium-review.googlesource.com/c/chromium/src/+/4577759
560
581
  if (Platform.isWindows() && Platform.isEdge()) {
561
582
  return false;
@@ -141,16 +141,10 @@ shaka.util.PlayerConfiguration = class {
141
141
  'urn:uuid:79f0049a-4098-8642-ab92-e65be0885f95':
142
142
  'com.microsoft.playready',
143
143
  },
144
- manifestPreprocessor: (element) => {
145
- return shaka.util.ConfigUtils.referenceParametersAndReturn(
146
- [element],
147
- element);
148
- },
149
- manifestPreprocessorTXml: (element) => {
150
- return shaka.util.ConfigUtils.referenceParametersAndReturn(
151
- [element],
152
- element);
153
- },
144
+ manifestPreprocessor:
145
+ shaka.util.PlayerConfiguration.defaultManifestPreprocessor,
146
+ manifestPreprocessorTXml:
147
+ shaka.util.PlayerConfiguration.defaultManifestPreprocessorTXml,
154
148
  sequenceMode: false,
155
149
  enableAudioGroups: false,
156
150
  multiTypeVariantsAllowed,
@@ -174,16 +168,10 @@ shaka.util.PlayerConfiguration = class {
174
168
  allowLowLatencyByteRangeOptimization: true,
175
169
  },
176
170
  mss: {
177
- manifestPreprocessor: (element) => {
178
- return shaka.util.ConfigUtils.referenceParametersAndReturn(
179
- [element],
180
- element);
181
- },
182
- manifestPreprocessorTXml: (element) => {
183
- return shaka.util.ConfigUtils.referenceParametersAndReturn(
184
- [element],
185
- element);
186
- },
171
+ manifestPreprocessor:
172
+ shaka.util.PlayerConfiguration.defaultManifestPreprocessor,
173
+ manifestPreprocessorTXml:
174
+ shaka.util.PlayerConfiguration.defaultManifestPreprocessorTXml,
187
175
  sequenceMode: false,
188
176
  keySystemsBySystemId: {
189
177
  '9a04f079-9840-4286-ab92-e65be0885f95':
@@ -615,4 +603,24 @@ shaka.util.PlayerConfiguration = class {
615
603
 
616
604
  return selectedTracks;
617
605
  }
606
+
607
+ /**
608
+ * @param {!Element} element
609
+ * @return {!Element}
610
+ */
611
+ static defaultManifestPreprocessor(element) {
612
+ return shaka.util.ConfigUtils.referenceParametersAndReturn(
613
+ [element],
614
+ element);
615
+ }
616
+
617
+ /**
618
+ * @param {!shaka.extern.xml.Node} element
619
+ * @return {!shaka.extern.xml.Node}
620
+ */
621
+ static defaultManifestPreprocessorTXml(element) {
622
+ return shaka.util.ConfigUtils.referenceParametersAndReturn(
623
+ [element],
624
+ element);
625
+ }
618
626
  };
@@ -983,8 +983,12 @@ shaka.util.StreamUtils = class {
983
983
  // currently don't support 'fLaC', while 'flac' is supported by most
984
984
  // major browsers.
985
985
  // See https://bugs.chromium.org/p/chromium/issues/detail?id=1422728
986
- if (codecs === 'fLaC' && !shaka.util.Platform.isSafari()) {
987
- return 'flac';
986
+ if (codecs.toLowerCase() == 'flac') {
987
+ if (!shaka.util.Platform.isSafari()) {
988
+ return 'flac';
989
+ } else {
990
+ return 'fLaC';
991
+ }
988
992
  }
989
993
 
990
994
  // The same is true for 'Opus'.
package/lib/util/tXml.js CHANGED
@@ -779,7 +779,7 @@ shaka.util.TXml = class {
779
779
  element.setAttribute(k, v);
780
780
  }
781
781
 
782
- if (doParents && node.parent) {
782
+ if (doParents && node.parent && node.parent.tagName != '?xml') {
783
783
  const parentElement = TXml.txmlNodeToDomElement(
784
784
  node.parent, /* doParents= */ true, /* doChildren= */ false);
785
785
  parentElement.appendChild(element);
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.8.0",
4
+ "version": "4.8.2",
5
5
  "homepage": "https://github.com/shaka-project/shaka-player",
6
6
  "author": "Google",
7
7
  "maintainers": [
@@ -116,6 +116,7 @@ shaka.ui.ResolutionSelection = class extends shaka.ui.SettingsMenu {
116
116
  } else {
117
117
  otherIdx = tracks.findIndex((t) => {
118
118
  return t.height == track.height &&
119
+ t.videoBandwidth == track.videoBandwidth &&
119
120
  t.frameRate == track.frameRate &&
120
121
  t.hdr == track.hdr &&
121
122
  t.videoLayout == track.videoLayout;