shaka-player 4.12.2 → 4.12.3

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 (42) hide show
  1. package/.csslintrc +2 -1
  2. package/dist/controls.css +3 -3
  3. package/dist/controls.css.map +1 -1
  4. package/dist/deps.js +3 -2
  5. package/dist/locales.js +36 -36
  6. package/dist/shaka-player.compiled.d.ts +1 -1
  7. package/dist/shaka-player.compiled.debug.d.ts +1 -1
  8. package/dist/shaka-player.compiled.debug.js +236 -237
  9. package/dist/shaka-player.compiled.debug.map +4 -4
  10. package/dist/shaka-player.compiled.js +1328 -1323
  11. package/dist/shaka-player.compiled.map +4 -4
  12. package/dist/shaka-player.ui.d.ts +1 -1
  13. package/dist/shaka-player.ui.debug.d.ts +1 -1
  14. package/dist/shaka-player.ui.debug.js +274 -275
  15. package/dist/shaka-player.ui.debug.map +4 -4
  16. package/dist/shaka-player.ui.js +1536 -1531
  17. package/dist/shaka-player.ui.map +4 -4
  18. package/externs/managedmediasource.js +6 -0
  19. package/externs/shaka/ads.js +3 -0
  20. package/lib/ads/ad_utils.js +1 -0
  21. package/lib/ads/interstitial_ad_manager.js +64 -24
  22. package/lib/cast/cast_utils.js +4 -2
  23. package/lib/media/media_source_capabilities.js +6 -8
  24. package/lib/media/media_source_engine.js +3 -4
  25. package/lib/media/segment_utils.js +7 -3
  26. package/lib/net/http_xhr_plugin.js +1 -0
  27. package/lib/player.js +42 -36
  28. package/lib/text/vtt_text_parser.js +9 -4
  29. package/lib/util/abortable_operation.js +3 -0
  30. package/lib/util/buffer_utils.js +10 -8
  31. package/lib/util/mime_utils.js +2 -0
  32. package/lib/util/number_utils.js +37 -0
  33. package/lib/util/object_utils.js +1 -1
  34. package/lib/util/uint8array_utils.js +3 -2
  35. package/package.json +2 -2
  36. package/ui/language_utils.js +3 -1
  37. package/ui/less/ad_controls.less +3 -3
  38. package/ui/less/buttons.less +4 -4
  39. package/ui/less/containers.less +7 -7
  40. package/ui/less/overflow_menu.less +4 -4
  41. package/ui/less/thumbnails.less +3 -3
  42. package/ui/less/tooltip.less +1 -1
@@ -22,3 +22,9 @@ function ManagedMediaSource() {}
22
22
  * @return {boolean}
23
23
  */
24
24
  ManagedMediaSource.isTypeSupported = function(type) {};
25
+
26
+ /**
27
+ * @constructor
28
+ * @extends {SourceBuffer}
29
+ */
30
+ function ManagedSourceBuffer() {}
@@ -63,6 +63,7 @@ shaka.extern.AdCuePoint;
63
63
  * startTime: number,
64
64
  * endTime: ?number,
65
65
  * uri: string,
66
+ * mimeType: ?string,
66
67
  * isSkippable: boolean,
67
68
  * skipOffset: ?number,
68
69
  * skipFor: ?number,
@@ -87,6 +88,8 @@ shaka.extern.AdCuePoint;
87
88
  * @property {string} uri
88
89
  * The uri of the interstitial, can be any type that
89
90
  * ShakaPlayer supports (either in MSE or src=)
91
+ * @property {?string} mimeType
92
+ * The mimeType of the interstitial if known.
90
93
  * @property {boolean} isSkippable
91
94
  * Indicate if the interstitial is skippable.
92
95
  * @property {?number} skipOffset
@@ -80,6 +80,7 @@ shaka.ads.Utils = class {
80
80
  startTime: startTime,
81
81
  endTime: null,
82
82
  uri: adUrl,
83
+ mimeType: media.attributes['type'] || null,
83
84
  isSkippable: skipOffset != null,
84
85
  skipOffset,
85
86
  skipFor: null,
@@ -102,7 +102,7 @@ shaka.ads.InterstitialAdManager = class {
102
102
  }
103
103
  this.lastTime_ = this.baseVideo_.currentTime;
104
104
  const currentInterstitial = this.getCurrentInterstitial_(
105
- /* needPreRoll= */ true, /* numberToSkip= */ 0, this.lastPlayedAd_);
105
+ /* needPreRoll= */ true);
106
106
  if (currentInterstitial) {
107
107
  this.setupAd_(currentInterstitial, /* sequenceLength= */ 1,
108
108
  /* adPosition= */ 1, /* initialTime= */ Date.now());
@@ -121,14 +121,17 @@ shaka.ads.InterstitialAdManager = class {
121
121
  this.lastTime_ < this.lastPlayedAd_.startTime) {
122
122
  this.lastPlayedAd_ = null;
123
123
  }
124
- const currentInterstitial = this.getCurrentInterstitial_(
125
- /* needPreRoll= */ false, /* numberToSkip= */ 0, this.lastPlayedAd_);
124
+ const currentInterstitial = this.getCurrentInterstitial_();
126
125
  if (currentInterstitial) {
127
126
  this.setupAd_(currentInterstitial, /* sequenceLength= */ 1,
128
127
  /* adPosition= */ 1, /* initialTime= */ Date.now());
129
128
  }
130
129
  };
131
130
 
131
+ this.eventManager_.listen(this.baseVideo_, 'ended', () => {
132
+ checkForInterstitials();
133
+ });
134
+
132
135
  /** @private {shaka.util.Timer} */
133
136
  this.timeUpdateTimer_ = new shaka.util.Timer(checkForInterstitials);
134
137
 
@@ -181,7 +184,10 @@ shaka.ads.InterstitialAdManager = class {
181
184
  if (difference > 0 && difference <= 10) {
182
185
  if (!this.preloadManagerInterstitials_.has(interstitial)) {
183
186
  this.preloadManagerInterstitials_.set(
184
- interstitial, this.player_.preload(interstitial.uri));
187
+ interstitial, this.player_.preload(
188
+ interstitial.uri,
189
+ /* startTime= */ null,
190
+ interstitial.mimeType || undefined));
185
191
  }
186
192
  }
187
193
  }
@@ -322,10 +328,11 @@ shaka.ads.InterstitialAdManager = class {
322
328
 
323
329
  /** @type {!shaka.extern.AdInterstitial} */
324
330
  const interstitial = {
325
- id: null,
331
+ id: region.id,
326
332
  startTime: region.startTime,
327
333
  endTime: region.endTime,
328
334
  uri: alternativeMPDUri,
335
+ mimeType: null,
329
336
  isSkippable: false,
330
337
  skipOffset: null,
331
338
  skipFor: null,
@@ -407,7 +414,10 @@ shaka.ads.InterstitialAdManager = class {
407
414
  if (shouldPreload) {
408
415
  if (!this.preloadManagerInterstitials_.has(interstitial)) {
409
416
  this.preloadManagerInterstitials_.set(
410
- interstitial, this.player_.preload(interstitial.uri));
417
+ interstitial, this.player_.preload(
418
+ interstitial.uri,
419
+ /* startTime= */ null,
420
+ interstitial.mimeType || undefined));
411
421
  }
412
422
  }
413
423
  }
@@ -436,13 +446,12 @@ shaka.ads.InterstitialAdManager = class {
436
446
 
437
447
 
438
448
  /**
439
- * @param {boolean} needPreRoll
440
- * @param {number} numberToSkip
441
- * @param {?shaka.extern.AdInterstitial=} lastPlayedAd
449
+ * @param {boolean=} needPreRoll
450
+ * @param {?number=} numberToSkip
442
451
  * @return {?shaka.extern.AdInterstitial}
443
452
  * @private
444
453
  */
445
- getCurrentInterstitial_(needPreRoll, numberToSkip, lastPlayedAd) {
454
+ getCurrentInterstitial_(needPreRoll = false, numberToSkip = null) {
446
455
  let skipped = 0;
447
456
  let currentInterstitial = null;
448
457
  if (this.interstitials_.size && this.lastTime_ != null) {
@@ -453,7 +462,15 @@ shaka.ads.InterstitialAdManager = class {
453
462
  const roundDecimals = (number) => {
454
463
  return Math.round(number * 1000) / 1000;
455
464
  };
456
- for (const interstitial of interstitials) {
465
+ let interstitialsToCheck = interstitials;
466
+ if (needPreRoll) {
467
+ interstitialsToCheck = interstitials.filter((i) => i.pre);
468
+ } else if (isEnded) {
469
+ interstitialsToCheck = interstitials.filter((i) => i.post);
470
+ } else {
471
+ interstitialsToCheck = interstitials.filter((i) => !i.pre && !i.post);
472
+ }
473
+ for (const interstitial of interstitialsToCheck) {
457
474
  let isValid = false;
458
475
  if (needPreRoll) {
459
476
  isValid = interstitial.pre;
@@ -464,18 +481,28 @@ shaka.ads.InterstitialAdManager = class {
464
481
  this.lastTime_ - roundDecimals(interstitial.startTime);
465
482
  if (difference > 0 &&
466
483
  (difference <= 1 || !interstitial.canJump)) {
467
- if (lastPlayedAd && !lastPlayedAd.pre && !lastPlayedAd.post &&
468
- lastPlayedAd.startTime >= interstitial.startTime) {
484
+ if (numberToSkip == null && this.lastPlayedAd_ &&
485
+ !this.lastPlayedAd_.pre && !this.lastPlayedAd_.post &&
486
+ this.lastPlayedAd_.startTime >= interstitial.startTime) {
469
487
  isValid = false;
470
488
  } else {
471
489
  isValid = true;
472
490
  }
473
491
  }
474
492
  }
475
- if (isValid) {
476
- if (skipped == numberToSkip) {
493
+ if (isValid && (!this.lastPlayedAd_ ||
494
+ interstitial.startTime >= this.lastPlayedAd_.startTime)) {
495
+ if (skipped == (numberToSkip || 0)) {
477
496
  currentInterstitial = interstitial;
478
- break;
497
+ } else if (currentInterstitial && !interstitial.canJump) {
498
+ const currentStartTime =
499
+ roundDecimals(currentInterstitial.startTime);
500
+ const newStartTime =
501
+ roundDecimals(interstitial.startTime);
502
+ if (newStartTime - currentStartTime > 0.001) {
503
+ currentInterstitial = interstitial;
504
+ skipped = 0;
505
+ }
479
506
  }
480
507
  skipped++;
481
508
  }
@@ -520,7 +547,7 @@ shaka.ads.InterstitialAdManager = class {
520
547
  }).length;
521
548
  }
522
549
 
523
- if (interstitial.once || interstitial.pre) {
550
+ if (interstitial.once) {
524
551
  oncePlayed++;
525
552
  this.interstitials_.delete(interstitial);
526
553
  this.cuepointsChanged_();
@@ -594,6 +621,10 @@ shaka.ads.InterstitialAdManager = class {
594
621
  this.setupAd_(nextCurrentInterstitial, sequenceLength,
595
622
  ++adPosition, initialTime, oncePlayed);
596
623
  } else {
624
+ if (interstitial.post) {
625
+ this.lastTime_ = null;
626
+ this.lastPlayedAd_ = null;
627
+ }
597
628
  if (this.usingBaseVideo_) {
598
629
  await this.player_.detach();
599
630
  } else {
@@ -754,10 +785,16 @@ shaka.ads.InterstitialAdManager = class {
754
785
  if (preloadManager) {
755
786
  await this.player_.load(preloadManager);
756
787
  } else {
757
- await this.player_.load(interstitial.uri);
788
+ await this.player_.load(
789
+ interstitial.uri,
790
+ /* startTime= */ null,
791
+ interstitial.mimeType || undefined);
758
792
  }
759
793
  } else {
760
- await this.player_.load(interstitial.uri);
794
+ await this.player_.load(
795
+ interstitial.uri,
796
+ /* startTime= */ null,
797
+ interstitial.mimeType || undefined);
761
798
  }
762
799
  if (interstitial.playoutLimit) {
763
800
  if (playoutLimitTimer) {
@@ -887,6 +924,7 @@ shaka.ads.InterstitialAdManager = class {
887
924
  startTime,
888
925
  endTime,
889
926
  uri,
927
+ mimeType: null,
890
928
  isSkippable,
891
929
  skipOffset,
892
930
  skipFor,
@@ -916,27 +954,29 @@ shaka.ads.InterstitialAdManager = class {
916
954
  const skipControl = dataAsJson['SKIP-CONTROL'];
917
955
  if (skipControl) {
918
956
  const enableSkipAfterValue = skipControl['ENABLE-SKIP-AFTER'];
919
- if (enableSkipAfterValue instanceof Number) {
957
+ if ((typeof enableSkipAfterValue) == 'number') {
920
958
  skipOffset = parseFloat(enableSkipAfterValue);
921
959
  if (isNaN(enableSkipAfterValue)) {
922
960
  skipOffset = isSkippable ? 0 : null;
923
961
  }
924
962
  }
925
- const enableSkipForValue = skipControl['X-ENABLE-SKIP-FOR'];
926
- if (enableSkipForValue instanceof Number) {
963
+ const enableSkipForValue = skipControl['ENABLE-SKIP-FOR'];
964
+ if ((typeof enableSkipForValue) == 'number') {
927
965
  skipFor = parseFloat(enableSkipForValue);
928
966
  if (isNaN(enableSkipForValue)) {
929
967
  skipFor = null;
930
968
  }
931
969
  }
932
970
  }
933
- for (const asset of dataAsJson['ASSETS']) {
971
+ for (let i = 0; i < dataAsJson['ASSETS'].length; i++) {
972
+ const asset = dataAsJson['ASSETS'][i];
934
973
  if (asset['URI']) {
935
974
  interstitialsAd.push({
936
- id,
975
+ id: id + '_asset_' + i,
937
976
  startTime,
938
977
  endTime,
939
978
  uri: asset['URI'],
979
+ mimeType: null,
940
980
  isSkippable,
941
981
  skipOffset,
942
982
  skipFor,
@@ -66,10 +66,12 @@ shaka.cast.CastUtils = class {
66
66
  return shaka.cast.CastUtils.unpackTimeRanges_(value);
67
67
  }
68
68
 
69
- if (value instanceof Uint8Array) {
69
+ if (ArrayBuffer.isView(value) &&
70
+ /** @type {TypedArray} */(value).BYTES_PER_ELEMENT === 1) {
70
71
  // Some of our code cares about Uint8Arrays actually being Uint8Arrays,
71
72
  // so this gives them special treatment.
72
- return shaka.cast.CastUtils.unpackUint8Array_(value);
73
+ return shaka.cast.CastUtils.unpackUint8Array_(
74
+ /** @type {!Uint8Array} */(value));
73
75
  }
74
76
 
75
77
  if (typeof value == 'number') {
@@ -23,12 +23,9 @@ shaka.media.Capabilities = class {
23
23
  if (supportMap.has(type)) {
24
24
  return supportMap.get(type);
25
25
  }
26
- if (window.ManagedMediaSource) {
27
- const currentSupport = ManagedMediaSource.isTypeSupported(type);
28
- supportMap.set(type, currentSupport);
29
- return currentSupport;
30
- } else if (window.MediaSource) {
31
- const currentSupport = MediaSource.isTypeSupported(type);
26
+ const mediaSource = window.ManagedMediaSource || window.MediaSource;
27
+ if (mediaSource) {
28
+ const currentSupport = mediaSource.isTypeSupported(type);
32
29
  supportMap.set(type, currentSupport);
33
30
  return currentSupport;
34
31
  }
@@ -40,9 +37,10 @@ shaka.media.Capabilities = class {
40
37
  * @return {boolean}
41
38
  */
42
39
  static isChangeTypeSupported() {
43
- return !!window.SourceBuffer &&
40
+ const sourceBuffer = window.ManagedSourceBuffer || window.SourceBuffer;
41
+ return !!sourceBuffer &&
44
42
  // eslint-disable-next-line no-restricted-syntax
45
- !!SourceBuffer.prototype && !!SourceBuffer.prototype.changeType;
43
+ !!sourceBuffer.prototype && !!sourceBuffer.prototype.changeType;
46
44
  }
47
45
  };
48
46
 
@@ -271,7 +271,7 @@ shaka.media.MediaSourceEngine = class {
271
271
  * @param {string} mimeType
272
272
  */
273
273
  addSecondarySource(uri, mimeType) {
274
- if (!this.video_ || !(this.mediaSource_ instanceof ManagedMediaSource)) {
274
+ if (!this.video_ || !window.ManagedMediaSource || !this.mediaSource_) {
275
275
  shaka.log.warning(
276
276
  'Secondary source is used only with ManagedMediaSource');
277
277
  return;
@@ -882,7 +882,7 @@ shaka.media.MediaSourceEngine = class {
882
882
  return frame.description ===
883
883
  'com.apple.streaming.transportStreamTimestamp';
884
884
  });
885
- if (metadataTimestamp && metadataTimestamp.data) {
885
+ if (metadataTimestamp) {
886
886
  timestamp = Math.round(metadataTimestamp.data) / 1000;
887
887
  }
888
888
  /** @private {shaka.extern.ID3Metadata} */
@@ -1139,8 +1139,7 @@ shaka.media.MediaSourceEngine = class {
1139
1139
  const ContentType = shaka.util.ManifestParserUtils.ContentType;
1140
1140
 
1141
1141
  if (contentType == ContentType.TEXT) {
1142
- if (this.manifestType_ == shaka.media.ManifestParser.HLS &&
1143
- reference.discontinuitySequence >= 0) {
1142
+ if (this.manifestType_ == shaka.media.ManifestParser.HLS) {
1144
1143
  // This won't be known until the first video segment is appended.
1145
1144
  const offset = await this.textSequenceModeOffset_;
1146
1145
  this.textEngine_.setTimestampOffset(offset);
@@ -417,9 +417,13 @@ shaka.media.SegmentUtils = class {
417
417
  if (initData) {
418
418
  captionParser.init(initData);
419
419
  }
420
- captionParser.parseFrom(data);
421
- for (const stream of captionParser.getStreams()) {
422
- closedCaptions.set(stream, stream);
420
+ try {
421
+ captionParser.parseFrom(data);
422
+ for (const stream of captionParser.getStreams()) {
423
+ closedCaptions.set(stream, stream);
424
+ }
425
+ } catch (e) {
426
+ shaka.log.debug('Error detecting CC streams', e);
423
427
  }
424
428
  captionParser.reset();
425
429
  }
@@ -64,6 +64,7 @@ shaka.net.HttpXHRPlugin = class {
64
64
  };
65
65
  xhr.onload = (event) => {
66
66
  const headers = shaka.net.HttpXHRPlugin.headersToGenericObject_(xhr);
67
+ // eslint-disable-next-line shaka-rules/buffersource-no-instanceof
67
68
  goog.asserts.assert(xhr.response instanceof ArrayBuffer,
68
69
  'XHR should have a response by now!');
69
70
  const xhrResponse = xhr.response;
package/lib/player.js CHANGED
@@ -58,6 +58,7 @@ goog.require('shaka.util.ManifestParserUtils');
58
58
  goog.require('shaka.util.MediaReadyState');
59
59
  goog.require('shaka.util.MimeUtils');
60
60
  goog.require('shaka.util.Mutex');
61
+ goog.require('shaka.util.NumberUtils');
61
62
  goog.require('shaka.util.ObjectUtils');
62
63
  goog.require('shaka.util.Platform');
63
64
  goog.require('shaka.util.PlayerConfiguration');
@@ -752,7 +753,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
752
753
  this.externalSrcEqualsThumbnailsStreams_ = [];
753
754
 
754
755
  /** @private {number} */
755
- this.completionPercent_ = NaN;
756
+ this.completionPercent_ = -1;
756
757
 
757
758
  /** @private {?shaka.extern.PlayerConfiguration} */
758
759
  this.config_ = this.defaultConfig_();
@@ -1522,7 +1523,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1522
1523
 
1523
1524
  this.externalSrcEqualsThumbnailsStreams_ = [];
1524
1525
 
1525
- this.completionPercent_ = NaN;
1526
+ this.completionPercent_ = -1;
1526
1527
 
1527
1528
  // Make sure that the app knows of the new buffering state.
1528
1529
  this.updateBufferState_();
@@ -7016,41 +7017,46 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
7016
7017
  if (!this.video_) {
7017
7018
  return;
7018
7019
  }
7019
- let hasNewCompletionPercent = false;
7020
- const completionRatio = this.video_.currentTime / this.video_.duration;
7021
- if (!isNaN(completionRatio)) {
7022
- const percent = Math.round(100 * completionRatio);
7023
- if (isNaN(this.completionPercent_)) {
7024
- this.completionPercent_ = percent;
7025
- hasNewCompletionPercent = true;
7026
- } else {
7027
- const newCompletionPercent = Math.max(this.completionPercent_, percent);
7028
- if (this.completionPercent_ != newCompletionPercent) {
7029
- this.completionPercent_ = newCompletionPercent;
7030
- hasNewCompletionPercent = true;
7031
- }
7020
+
7021
+ const isQuartile = (quartilePercent, currentPercent) => {
7022
+ const NumberUtils = shaka.util.NumberUtils;
7023
+
7024
+ if ((NumberUtils.isFloatEqual(quartilePercent, currentPercent) ||
7025
+ currentPercent > quartilePercent) &&
7026
+ this.completionPercent_ < quartilePercent) {
7027
+ this.completionPercent_ = quartilePercent;
7028
+ return true;
7032
7029
  }
7030
+ return false;
7031
+ };
7032
+
7033
+ const completionRatio = this.video_.currentTime / this.video_.duration;
7034
+
7035
+ if (isNaN(completionRatio)) {
7036
+ return;
7033
7037
  }
7034
- if (hasNewCompletionPercent) {
7035
- let event;
7036
- if (this.completionPercent_ == 0) {
7037
- event = shaka.Player.makeEvent_(shaka.util.FakeEvent.EventName.Started);
7038
- } else if (this.completionPercent_ == 25) {
7039
- event = shaka.Player.makeEvent_(
7040
- shaka.util.FakeEvent.EventName.FirstQuartile);
7041
- } else if (this.completionPercent_ == 50) {
7042
- event = shaka.Player.makeEvent_(
7043
- shaka.util.FakeEvent.EventName.Midpoint);
7044
- } else if (this.completionPercent_ == 75) {
7045
- event = shaka.Player.makeEvent_(
7046
- shaka.util.FakeEvent.EventName.ThirdQuartile);
7047
- } else if (this.completionPercent_ == 100) {
7048
- event = shaka.Player.makeEvent_(
7049
- shaka.util.FakeEvent.EventName.Complete);
7050
- }
7051
- if (event) {
7052
- this.dispatchEvent(event);
7053
- }
7038
+
7039
+ const percent = completionRatio * 100;
7040
+
7041
+ let event;
7042
+ if (isQuartile(0, percent)) {
7043
+ event = shaka.Player.makeEvent_(shaka.util.FakeEvent.EventName.Started);
7044
+ } else if (isQuartile(25, percent)) {
7045
+ event = shaka.Player.makeEvent_(
7046
+ shaka.util.FakeEvent.EventName.FirstQuartile);
7047
+ } else if (isQuartile(50, percent)) {
7048
+ event = shaka.Player.makeEvent_(
7049
+ shaka.util.FakeEvent.EventName.Midpoint);
7050
+ } else if (isQuartile(75, percent)) {
7051
+ event = shaka.Player.makeEvent_(
7052
+ shaka.util.FakeEvent.EventName.ThirdQuartile);
7053
+ } else if (isQuartile(100, percent)) {
7054
+ event = shaka.Player.makeEvent_(
7055
+ shaka.util.FakeEvent.EventName.Complete);
7056
+ }
7057
+
7058
+ if (event) {
7059
+ this.dispatchEvent(event);
7054
7060
  }
7055
7061
  }
7056
7062
 
@@ -8269,7 +8275,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
8269
8275
  * @export
8270
8276
  */
8271
8277
  // eslint-disable-next-line no-useless-concat, max-len
8272
- shaka.Player.version = 'v4.12.2' + '-uncompiled'; // x-release-please-version
8278
+ shaka.Player.version = 'v4.12.3' + '-uncompiled'; // x-release-please-version
8273
8279
 
8274
8280
  // Initialize the deprecation system using the version string we just set
8275
8281
  // on the player.
@@ -83,10 +83,15 @@ shaka.text.VttTextParser = class {
83
83
  // HLS content, which should use X-TIMESTAMP-MAP and periodStart instead.
84
84
  let offset = time.vttOffset;
85
85
 
86
- // Only use 'X-TIMESTAMP-MAP' with HLS. This overrides offset above.
87
- if (blocks[0].includes('X-TIMESTAMP-MAP') &&
88
- this.manifestType_ == shaka.media.ManifestParser.HLS) {
89
- offset = this.computeHlsOffset_(blocks[0], time);
86
+ if (this.manifestType_ == shaka.media.ManifestParser.HLS) {
87
+ // Only use 'X-TIMESTAMP-MAP' with HLS.
88
+ if (blocks[0].includes('X-TIMESTAMP-MAP')) {
89
+ offset = this.computeHlsOffset_(blocks[0], time);
90
+ } else if (time.periodStart && time.vttOffset == time.periodStart) {
91
+ // In the case where X-TIMESTAMP-MAP is not used and it is HLS, we
92
+ // should not use offset unless segment-relative times are used.
93
+ offset = 0;
94
+ }
90
95
  }
91
96
 
92
97
  // Parse VTT regions.
@@ -166,6 +166,9 @@ shaka.util.AbortableOperation = class {
166
166
  */
167
167
  chain(onSuccess, onError) {
168
168
  const newPromise = new shaka.util.PublicPromise();
169
+ // Silence uncaught rejection errors, which may otherwise occur any place
170
+ // we don't explicitly handle aborted operations.
171
+ newPromise.catch(() => {});
169
172
  const abortError = shaka.util.AbortableOperation.abortError();
170
173
 
171
174
  // If called before "this" completes, just abort "this".
@@ -62,10 +62,10 @@ shaka.util.BufferUtils = class {
62
62
  * @private
63
63
  */
64
64
  static unsafeGetArrayBuffer_(view) {
65
- if (view instanceof ArrayBuffer) {
66
- return view;
65
+ if (!ArrayBuffer.isView(view)) {
66
+ return /** @type {!ArrayBuffer} */(view);
67
67
  } else {
68
- return view.buffer;
68
+ return /** @type {!ArrayBufferView} */(view).buffer;
69
69
  }
70
70
  }
71
71
 
@@ -79,17 +79,19 @@ shaka.util.BufferUtils = class {
79
79
  * @export
80
80
  */
81
81
  static toArrayBuffer(view) {
82
- if (view instanceof ArrayBuffer) {
83
- return view;
82
+ if (!ArrayBuffer.isView(view)) {
83
+ return /** @type {!ArrayBuffer} */(view);
84
84
  } else {
85
- if (view.byteOffset == 0 && view.byteLength == view.buffer.byteLength) {
85
+ const aView = /** @type {!ArrayBufferView} */(view);
86
+ if (aView.byteOffset == 0 &&
87
+ aView.byteLength == aView.buffer.byteLength) {
86
88
  // This is a TypedArray over the whole buffer.
87
- return view.buffer;
89
+ return aView.buffer;
88
90
  }
89
91
  // This is a "view" on the buffer. Create a new buffer that only contains
90
92
  // the data. Note that since this isn't an ArrayBuffer, the "new" call
91
93
  // will allocate a new buffer to hold the copy.
92
- return new Uint8Array(view).buffer;
94
+ return new Uint8Array(aView).buffer;
93
95
  }
94
96
  }
95
97
 
@@ -166,6 +166,8 @@ shaka.util.MimeUtils = class {
166
166
  return 'dtsx'; // DTS:X
167
167
  case base === 'mp4a' && profile === 'a9':
168
168
  return 'dtsc'; // DTS Digital Surround
169
+ case base === 'vp09':
170
+ return 'vp9';
169
171
  case base === 'avc1':
170
172
  case base === 'avc3':
171
173
  return 'avc'; // H264
@@ -0,0 +1,37 @@
1
+ /*! @license
2
+ * Shaka Player
3
+ * Copyright 2016 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ goog.provide('shaka.util.NumberUtils');
8
+
9
+
10
+ shaka.util.NumberUtils = class {
11
+ /**
12
+ * Compare two float numbers, taking a configurable tolerance margin into
13
+ * account.
14
+ *
15
+ * @param {number} a
16
+ * @param {number} b
17
+ * @param {number=} tolerance
18
+ * @return {boolean}
19
+ */
20
+ static isFloatEqual(a, b, tolerance = Number.EPSILON) {
21
+ if (a === b) {
22
+ return true;
23
+ }
24
+
25
+ const error = Math.abs(a - b);
26
+
27
+ if (error <= tolerance) {
28
+ return true;
29
+ }
30
+
31
+ if (tolerance !== Number.EPSILON) {
32
+ return Math.abs(error - tolerance) <= Number.EPSILON;
33
+ }
34
+
35
+ return false;
36
+ }
37
+ };
@@ -41,7 +41,7 @@ shaka.util.ObjectUtils = class {
41
41
 
42
42
  // This covers Uint8Array and friends, even without a TypedArray
43
43
  // base-class constructor.
44
- const isTypedArray = val.buffer instanceof ArrayBuffer;
44
+ const isTypedArray = ArrayBuffer.isView(val);
45
45
  if (isTypedArray) {
46
46
  return val;
47
47
  }
@@ -119,8 +119,9 @@ shaka.util.Uint8ArrayUtils = class {
119
119
 
120
120
  for (let i = 0; i < varArgs.length; ++i) {
121
121
  const value = varArgs[i];
122
- if (value instanceof Uint8Array) {
123
- result.set(value, offset);
122
+ if (ArrayBuffer.isView(value) &&
123
+ /** @type {TypedArray} */ (value).BYTES_PER_ELEMENT === 1) {
124
+ result.set(/** @type {!Uint8Array} */(value), offset);
124
125
  } else {
125
126
  result.set(BufferUtils.toUint8(value), offset);
126
127
  }
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.12.2",
4
+ "version": "4.12.3",
5
5
  "homepage": "https://github.com/shaka-project/shaka-player",
6
6
  "author": "Google",
7
7
  "maintainers": [
@@ -105,7 +105,7 @@
105
105
  "prepublishOnly": "python build/checkversion.py && python build/all.py --force"
106
106
  },
107
107
  "dependencies": {
108
- "eme-encryption-scheme-polyfill": "^2.1.5"
108
+ "eme-encryption-scheme-polyfill": "^2.1.6"
109
109
  },
110
110
  "clean-package": {
111
111
  "remove": [
@@ -14,6 +14,7 @@ goog.require('shaka.ui.Overlay.TrackLabelFormat');
14
14
  goog.require('shaka.ui.Utils');
15
15
  goog.require('shaka.util.Dom');
16
16
  goog.require('shaka.util.LanguageUtils');
17
+ goog.require('shaka.util.MimeUtils');
17
18
  goog.requireType('shaka.ui.Localization');
18
19
 
19
20
 
@@ -48,7 +49,8 @@ shaka.ui.LanguageUtils = class {
48
49
  if (!codecsByLanguage.has(track.language)) {
49
50
  codecsByLanguage.set(track.language, new Set());
50
51
  }
51
- codecsByLanguage.get(track.language).add(track.audioCodec);
52
+ codecsByLanguage.get(track.language).add(
53
+ shaka.util.MimeUtils.getNormalizedCodec(track.audioCodec));
52
54
  }
53
55
  const hasDifferentAudioCodecs = (language) =>
54
56
  codecsByLanguage.has(language) && codecsByLanguage.get(language).size > 1;