shaka-player 4.12.2 → 4.12.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 (51) 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 +5 -4
  5. package/dist/locales.js +35 -35
  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 +299 -297
  9. package/dist/shaka-player.compiled.debug.map +4 -4
  10. package/dist/shaka-player.compiled.js +1287 -1282
  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 +302 -299
  15. package/dist/shaka-player.ui.debug.map +4 -4
  16. package/dist/shaka-player.ui.js +1515 -1510
  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.js +4 -2
  22. package/lib/ads/interstitial_ad_manager.js +79 -42
  23. package/lib/cast/cast_utils.js +4 -2
  24. package/lib/media/drm_engine.js +46 -6
  25. package/lib/media/media_source_capabilities.js +6 -8
  26. package/lib/media/media_source_engine.js +3 -4
  27. package/lib/media/preload_manager.js +9 -3
  28. package/lib/media/segment_utils.js +7 -3
  29. package/lib/net/http_xhr_plugin.js +1 -0
  30. package/lib/player.js +45 -39
  31. package/lib/text/vtt_text_parser.js +9 -4
  32. package/lib/util/abortable_operation.js +3 -0
  33. package/lib/util/buffer_utils.js +10 -8
  34. package/lib/util/functional.js +27 -0
  35. package/lib/util/mime_utils.js +2 -0
  36. package/lib/util/number_utils.js +37 -0
  37. package/lib/util/object_utils.js +1 -1
  38. package/lib/util/stream_utils.js +8 -1
  39. package/lib/util/uint8array_utils.js +3 -2
  40. package/package.json +2 -2
  41. package/ui/ad_position.js +2 -2
  42. package/ui/controls.js +5 -3
  43. package/ui/language_utils.js +3 -1
  44. package/ui/less/ad_controls.less +3 -3
  45. package/ui/less/buttons.less +4 -4
  46. package/ui/less/containers.less +7 -7
  47. package/ui/less/overflow_menu.less +4 -4
  48. package/ui/less/thumbnails.less +3 -3
  49. package/ui/less/tooltip.less +1 -1
  50. package/ui/play_button.js +5 -3
  51. package/ui/presentation_time.js +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,
@@ -144,7 +144,7 @@ shaka.ads.InterstitialAd = class {
144
144
  * @export
145
145
  */
146
146
  canSkipNow() {
147
- return this.getTimeUntilSkippable() == 0;
147
+ return this.isSkippable_ && this.getTimeUntilSkippable() == 0;
148
148
  }
149
149
 
150
150
  /**
@@ -152,7 +152,9 @@ shaka.ads.InterstitialAd = class {
152
152
  * @export
153
153
  */
154
154
  skip() {
155
- this.onSkip_();
155
+ if (this.canSkipNow()) {
156
+ this.onSkip_();
157
+ }
156
158
  }
157
159
 
158
160
  /**
@@ -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 {
@@ -673,7 +704,6 @@ shaka.ads.InterstitialAdManager = class {
673
704
  this.onEvent_(new shaka.util.FakeEvent(
674
705
  shaka.ads.Utils.AD_SKIP_STATE_CHANGED));
675
706
  }
676
- const eventsSent = new Set();
677
707
  this.adEventManager_.listenOnce(this.player_, 'error', error);
678
708
  this.adEventManager_.listen(this.video_, 'timeupdate', () => {
679
709
  const duration = this.video_.duration;
@@ -687,23 +717,21 @@ shaka.ads.InterstitialAdManager = class {
687
717
  new shaka.util.FakeEvent(shaka.ads.Utils.AD_SKIP_STATE_CHANGED));
688
718
  }
689
719
  prevCanSkipNow = currentCanSkipNow;
690
- const currentPercent = 100 * this.video_.currentTime / duration;
691
- if (currentPercent >= 25 && !eventsSent.has('firstquartile')) {
692
- updateBaseVideoTime();
693
- this.onEvent_(
694
- new shaka.util.FakeEvent(shaka.ads.Utils.AD_FIRST_QUARTILE));
695
- eventsSent.add('firstquartile');
696
- } else if (currentPercent >= 50 && !eventsSent.has('midpoint')) {
697
- updateBaseVideoTime();
698
- this.onEvent_(
699
- new shaka.util.FakeEvent(shaka.ads.Utils.AD_MIDPOINT));
700
- eventsSent.add('midpoint');
701
- } else if (currentPercent >= 75 && !eventsSent.has('thirdquartile')) {
702
- updateBaseVideoTime();
703
- this.onEvent_(
704
- new shaka.util.FakeEvent(shaka.ads.Utils.AD_THIRD_QUARTILE));
705
- eventsSent.add('thirdquartile');
706
- }
720
+ });
721
+ this.adEventManager_.listenOnce(this.player_, 'firstquartile', () => {
722
+ updateBaseVideoTime();
723
+ this.onEvent_(
724
+ new shaka.util.FakeEvent(shaka.ads.Utils.AD_FIRST_QUARTILE));
725
+ });
726
+ this.adEventManager_.listenOnce(this.player_, 'midpoint', () => {
727
+ updateBaseVideoTime();
728
+ this.onEvent_(
729
+ new shaka.util.FakeEvent(shaka.ads.Utils.AD_MIDPOINT));
730
+ });
731
+ this.adEventManager_.listenOnce(this.player_, 'thirdquartile', () => {
732
+ updateBaseVideoTime();
733
+ this.onEvent_(
734
+ new shaka.util.FakeEvent(shaka.ads.Utils.AD_THIRD_QUARTILE));
707
735
  });
708
736
  this.adEventManager_.listenOnce(this.player_, 'complete', complete);
709
737
  this.adEventManager_.listen(this.video_, 'play', () => {
@@ -754,10 +782,16 @@ shaka.ads.InterstitialAdManager = class {
754
782
  if (preloadManager) {
755
783
  await this.player_.load(preloadManager);
756
784
  } else {
757
- await this.player_.load(interstitial.uri);
785
+ await this.player_.load(
786
+ interstitial.uri,
787
+ /* startTime= */ null,
788
+ interstitial.mimeType || undefined);
758
789
  }
759
790
  } else {
760
- await this.player_.load(interstitial.uri);
791
+ await this.player_.load(
792
+ interstitial.uri,
793
+ /* startTime= */ null,
794
+ interstitial.mimeType || undefined);
761
795
  }
762
796
  if (interstitial.playoutLimit) {
763
797
  if (playoutLimitTimer) {
@@ -887,6 +921,7 @@ shaka.ads.InterstitialAdManager = class {
887
921
  startTime,
888
922
  endTime,
889
923
  uri,
924
+ mimeType: null,
890
925
  isSkippable,
891
926
  skipOffset,
892
927
  skipFor,
@@ -916,27 +951,29 @@ shaka.ads.InterstitialAdManager = class {
916
951
  const skipControl = dataAsJson['SKIP-CONTROL'];
917
952
  if (skipControl) {
918
953
  const enableSkipAfterValue = skipControl['ENABLE-SKIP-AFTER'];
919
- if (enableSkipAfterValue instanceof Number) {
954
+ if ((typeof enableSkipAfterValue) == 'number') {
920
955
  skipOffset = parseFloat(enableSkipAfterValue);
921
956
  if (isNaN(enableSkipAfterValue)) {
922
957
  skipOffset = isSkippable ? 0 : null;
923
958
  }
924
959
  }
925
- const enableSkipForValue = skipControl['X-ENABLE-SKIP-FOR'];
926
- if (enableSkipForValue instanceof Number) {
960
+ const enableSkipForValue = skipControl['ENABLE-SKIP-FOR'];
961
+ if ((typeof enableSkipForValue) == 'number') {
927
962
  skipFor = parseFloat(enableSkipForValue);
928
963
  if (isNaN(enableSkipForValue)) {
929
964
  skipFor = null;
930
965
  }
931
966
  }
932
967
  }
933
- for (const asset of dataAsJson['ASSETS']) {
968
+ for (let i = 0; i < dataAsJson['ASSETS'].length; i++) {
969
+ const asset = dataAsJson['ASSETS'][i];
934
970
  if (asset['URI']) {
935
971
  interstitialsAd.push({
936
- id,
972
+ id: id + '_asset_' + i,
937
973
  startTime,
938
974
  endTime,
939
975
  uri: asset['URI'],
976
+ mimeType: null,
940
977
  isSkippable,
941
978
  skipOffset,
942
979
  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') {
@@ -9,12 +9,14 @@ goog.provide('shaka.media.DrmEngine');
9
9
  goog.require('goog.asserts');
10
10
  goog.require('shaka.log');
11
11
  goog.require('shaka.net.NetworkingEngine');
12
+ goog.require('shaka.util.ArrayUtils');
12
13
  goog.require('shaka.util.BufferUtils');
13
14
  goog.require('shaka.util.Destroyer');
14
15
  goog.require('shaka.util.DrmUtils');
15
16
  goog.require('shaka.util.Error');
16
17
  goog.require('shaka.util.EventManager');
17
18
  goog.require('shaka.util.FakeEvent');
19
+ goog.require('shaka.util.Functional');
18
20
  goog.require('shaka.util.IDestroyable');
19
21
  goog.require('shaka.util.Iterables');
20
22
  goog.require('shaka.util.ManifestParserUtils');
@@ -66,12 +68,18 @@ shaka.media.DrmEngine = class {
66
68
  */
67
69
  this.activeSessions_ = new Map();
68
70
 
71
+ /** @private {!Array.<!shaka.net.NetworkingEngine.PendingRequest>} */
72
+ this.activeRequests_ = [];
73
+
69
74
  /**
70
75
  * @private {!Map<string,
71
76
  * {initData: ?Uint8Array, initDataType: ?string}>}
72
77
  */
73
78
  this.storedPersistentSessions_ = new Map();
74
79
 
80
+ /** @private {boolean} */
81
+ this.hasInitData_ = false;
82
+
75
83
  /** @private {!shaka.util.PublicPromise} */
76
84
  this.allSessionsLoaded_ = new shaka.util.PublicPromise();
77
85
 
@@ -705,6 +713,7 @@ shaka.media.DrmEngine = class {
705
713
 
706
714
  // Reset the promise for the next sessions to come if key needs aren't
707
715
  // satisfied with persistent sessions
716
+ this.hasInitData_ = false;
708
717
  this.allSessionsLoaded_ = new shaka.util.PublicPromise();
709
718
  this.allSessionsLoaded_.catch(() => {});
710
719
  }
@@ -766,11 +775,16 @@ shaka.media.DrmEngine = class {
766
775
  }
767
776
  }
768
777
 
778
+ // Mark that there is init data, so that the preloader will know to wait
779
+ // for sessions to be loaded.
780
+ this.hasInitData_ = true;
781
+
769
782
  // If there are pre-existing sessions that have all been loaded
770
783
  // then reset the allSessionsLoaded_ promise, which can now be
771
784
  // used to wait for new sesssions to be loaded
772
785
  if (this.activeSessions_.size > 0 && this.areAllSessionsLoaded_()) {
773
786
  this.allSessionsLoaded_.resolve();
787
+ this.hasInitData_ = false;
774
788
  this.allSessionsLoaded_ = new shaka.util.PublicPromise();
775
789
  this.allSessionsLoaded_.catch(() => {});
776
790
  }
@@ -1380,6 +1394,14 @@ shaka.media.DrmEngine = class {
1380
1394
  this.video_.paused && !this.initialRequestsSent_);
1381
1395
  }
1382
1396
 
1397
+ /** @return {!Promise} */
1398
+ async waitForActiveRequests() {
1399
+ if (this.hasInitData_) {
1400
+ await this.allSessionsLoaded_;
1401
+ await Promise.all(this.activeRequests_.map((req) => req.promise));
1402
+ }
1403
+ }
1404
+
1383
1405
  /**
1384
1406
  * Sends a license request.
1385
1407
  * @param {!MediaKeyMessageEvent} event
@@ -1439,7 +1461,9 @@ shaka.media.DrmEngine = class {
1439
1461
  try {
1440
1462
  const req = this.playerInterface_.netEngine.request(
1441
1463
  requestType, request, {isPreload: this.isPreload_()});
1464
+ this.activeRequests_.push(req);
1442
1465
  response = await req.promise;
1466
+ shaka.util.ArrayUtils.remove(this.activeRequests_, req);
1443
1467
  } catch (error) {
1444
1468
  if (this.destroyer_.destroyed()) {
1445
1469
  return;
@@ -1859,9 +1883,16 @@ shaka.media.DrmEngine = class {
1859
1883
  offlineConfig.sessionTypes = ['persistent-license'];
1860
1884
 
1861
1885
  const configs = [offlineConfig, basicConfig];
1862
-
1863
- const access = await navigator.requestMediaKeySystemAccess(
1864
- keySystem, configs);
1886
+ // On some (Android) WebView environments,
1887
+ // requestMediaKeySystemAccess will
1888
+ // not resolve or reject, at least if RESOURCE_PROTECTED_MEDIA_ID
1889
+ // is not set. This is a workaround for that issue.
1890
+ const TIMEOUT_FOR_CHECKACCESS_IN_SECONDS = 1;
1891
+ const access =
1892
+ await shaka.util.Functional.promiseWithTimeout(
1893
+ TIMEOUT_FOR_CHECKACCESS_IN_SECONDS,
1894
+ navigator.requestMediaKeySystemAccess(keySystem, configs),
1895
+ );
1865
1896
  await processMediaKeySystemAccess(keySystem, access);
1866
1897
  } catch (error) {} // Ignore errors.
1867
1898
  };
@@ -1896,13 +1927,22 @@ shaka.media.DrmEngine = class {
1896
1927
  },
1897
1928
  },
1898
1929
  };
1899
-
1930
+ // On some (Android) WebView environments, decodingInfo will
1931
+ // not resolve or reject, at least if RESOURCE_PROTECTED_MEDIA_ID
1932
+ // is not set. This is a workaround for that issue.
1933
+ const TIMEOUT_FOR_DECODING_INFO_IN_SECONDS = 1;
1900
1934
  const decodingInfo =
1901
- await navigator.mediaCapabilities.decodingInfo(decodingConfig);
1935
+ await shaka.util.Functional.promiseWithTimeout(
1936
+ TIMEOUT_FOR_DECODING_INFO_IN_SECONDS,
1937
+ navigator.mediaCapabilities.decodingInfo(decodingConfig),
1938
+ );
1902
1939
 
1903
1940
  const access = decodingInfo.keySystemAccess;
1904
1941
  await processMediaKeySystemAccess(keySystem, access);
1905
- } catch (error) {} // Ignore errors.
1942
+ } catch (error) {
1943
+ // Ignore errors.
1944
+ shaka.log.v2('Failed to probe support for', keySystem, error);
1945
+ }
1906
1946
  };
1907
1947
 
1908
1948
  // Initialize the support structure for each key system.
@@ -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);
@@ -398,7 +398,13 @@ shaka.media.PreloadManager = class extends shaka.util.FakeEventTarget {
398
398
  await this.initializeDrmInner_();
399
399
  this.throwIfDestroyed_();
400
400
 
401
- await this.chooseInitialVariantInner_();
401
+ await this.chooseInitialVariantAndPrefetchInner_();
402
+ this.throwIfDestroyed_();
403
+
404
+ // We don't need the drm keys to load completely for the initial variant
405
+ // to be chosen, but we won't mark the load as a success until it has
406
+ // been loaded. So wait for it here, not inside initializeDrmInner_.
407
+ await this.drmEngine_.waitForActiveRequests();
402
408
  this.throwIfDestroyed_();
403
409
 
404
410
  this.successPromise_.resolve();
@@ -610,12 +616,12 @@ shaka.media.PreloadManager = class extends shaka.util.FakeEventTarget {
610
616
 
611
617
  /**
612
618
  * Performs a final filtering of the manifest, and chooses the initial
613
- * variant.
619
+ * variant. Also prefetches segments.
614
620
  *
615
621
  * @return {!Promise}
616
622
  * @private
617
623
  */
618
- async chooseInitialVariantInner_() {
624
+ async chooseInitialVariantAndPrefetchInner_() {
619
625
  goog.asserts.assert(
620
626
  this.manifest_, 'The manifest should already be parsed.');
621
627
 
@@ -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;