shaka-player 4.8.8 → 4.9.0

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 (60) hide show
  1. package/README.md +5 -0
  2. package/dist/controls.css +1 -1
  3. package/dist/controls.css.map +1 -1
  4. package/dist/deps.js +5 -5
  5. package/dist/locales.js +33 -33
  6. package/dist/shaka-player.compiled.d.ts +39 -17
  7. package/dist/shaka-player.compiled.debug.d.ts +39 -17
  8. package/dist/shaka-player.compiled.debug.externs.js +32 -11
  9. package/dist/shaka-player.compiled.debug.js +574 -516
  10. package/dist/shaka-player.compiled.debug.map +4 -4
  11. package/dist/shaka-player.compiled.externs.js +32 -11
  12. package/dist/shaka-player.compiled.js +1270 -1236
  13. package/dist/shaka-player.compiled.map +4 -4
  14. package/dist/shaka-player.ui.d.ts +39 -17
  15. package/dist/shaka-player.ui.debug.d.ts +39 -17
  16. package/dist/shaka-player.ui.debug.externs.js +43 -22
  17. package/dist/shaka-player.ui.debug.js +601 -543
  18. package/dist/shaka-player.ui.debug.map +4 -4
  19. package/dist/shaka-player.ui.externs.js +43 -22
  20. package/dist/shaka-player.ui.js +1483 -1448
  21. package/dist/shaka-player.ui.map +4 -4
  22. package/externs/shaka/manifest.js +11 -1
  23. package/externs/shaka/offline.js +3 -0
  24. package/externs/shaka/player.js +105 -12
  25. package/externs/tizen.js +1 -1
  26. package/lib/abr/simple_abr_manager.js +5 -0
  27. package/lib/ads/media_tailor_ad.js +11 -0
  28. package/lib/ads/media_tailor_ad_manager.js +37 -3
  29. package/lib/cea/cea608_data_channel.js +9 -0
  30. package/lib/cea/cea_decoder.js +6 -2
  31. package/lib/dash/dash_parser.js +655 -46
  32. package/lib/dash/mpd_utils.js +7 -8
  33. package/lib/dash/segment_template.js +65 -10
  34. package/lib/hls/hls_parser.js +92 -34
  35. package/lib/media/drm_engine.js +274 -40
  36. package/lib/media/manifest_filterer.js +10 -14
  37. package/lib/media/preload_manager.js +3 -10
  38. package/lib/media/segment_utils.js +14 -3
  39. package/lib/mss/mss_parser.js +2 -0
  40. package/lib/net/http_plugin_utils.js +2 -1
  41. package/lib/net/networking_engine.js +27 -0
  42. package/lib/offline/indexeddb/v1_storage_cell.js +1 -0
  43. package/lib/offline/indexeddb/v2_storage_cell.js +1 -0
  44. package/lib/offline/manifest_converter.js +2 -0
  45. package/lib/offline/storage.js +1 -0
  46. package/lib/player.js +221 -14
  47. package/lib/polyfill/media_capabilities.js +10 -35
  48. package/lib/util/config_utils.js +74 -6
  49. package/lib/util/error.js +7 -0
  50. package/lib/util/mp4_box_parsers.js +23 -2
  51. package/lib/util/periods.js +51 -28
  52. package/lib/util/platform.js +3 -1
  53. package/lib/util/player_configuration.js +20 -8
  54. package/lib/util/stream_utils.js +62 -30
  55. package/lib/util/tXml.js +74 -6
  56. package/package.json +2 -2
  57. package/support.html +1 -0
  58. package/ui/controls.js +4 -0
  59. package/ui/less/ad_controls.less +2 -1
  60. package/ui/resolution_selection.js +12 -3
package/lib/player.js CHANGED
@@ -582,6 +582,12 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
582
582
  */
583
583
  this.trickPlayEventManager_ = new shaka.util.EventManager();
584
584
 
585
+ /**
586
+ * For listeners scoped to the lifetime of the ad manager.
587
+ * @private {shaka.util.EventManager}
588
+ */
589
+ this.adManagerEventManager_ = new shaka.util.EventManager();
590
+
585
591
  /** @private {shaka.net.NetworkingEngine} */
586
592
  this.networkingEngine_ = null;
587
593
 
@@ -708,7 +714,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
708
714
  */
709
715
  this.lastTextFactory_;
710
716
 
711
- /** @private {{width: number, height: number}} */
717
+ /** @private {shaka.extern.Resolution} */
712
718
  this.maxHwRes_ = {width: Infinity, height: Infinity};
713
719
 
714
720
  /** @private {!shaka.media.ManifestFilterer} */
@@ -758,14 +764,78 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
758
764
  this.cmsdManager_ = this.createCmsd_();
759
765
 
760
766
  this.networkingEngine_ = this.createNetworkingEngine();
767
+ this.networkingEngine_.setForceHTTP(this.config_.streaming.forceHTTP);
761
768
  this.networkingEngine_.setForceHTTPS(this.config_.streaming.forceHTTPS);
762
769
 
763
770
  /** @private {shaka.extern.IAdManager} */
764
771
  this.adManager_ = null;
765
772
 
773
+ /** @private {?shaka.media.PreloadManager} */
774
+ this.preloadDueAdManager_ = null;
775
+
776
+ /** @private {HTMLMediaElement} */
777
+ this.preloadDueAdManagerVideo_ = null;
778
+
779
+ /** @private {shaka.util.Timer} */
780
+ this.preloadDueAdManagerTimer_ = new shaka.util.Timer(async () => {
781
+ if (this.preloadDueAdManager_) {
782
+ goog.asserts.assert(this.preloadDueAdManagerVideo_, 'Must have video');
783
+ await this.attach(
784
+ this.preloadDueAdManagerVideo_, /* initializeMediaSource= */ true);
785
+ await this.load(this.preloadDueAdManager_);
786
+ this.preloadDueAdManagerVideo_.play();
787
+ this.preloadDueAdManager_ = null;
788
+ }
789
+ });
790
+
766
791
  if (shaka.Player.adManagerFactory_) {
767
792
  this.adManager_ = shaka.Player.adManagerFactory_();
768
793
  this.adManager_.configure(this.config_.ads);
794
+
795
+ // Note: we don't use shaka.ads.AdManager.AD_STARTED to avoid add a
796
+ // optional module in the player.
797
+ this.adManagerEventManager_.listen(
798
+ this.adManager_, 'ad-started', async (e) => {
799
+ if (this.config_.ads.supportsMultipleMediaElements) {
800
+ return;
801
+ }
802
+ const event = /** @type {?google.ima.AdEvent} */
803
+ (e['originalEvent']);
804
+ if (!event) {
805
+ return;
806
+ }
807
+ const contentPauseRequested =
808
+ google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED;
809
+ if (event.type != contentPauseRequested) {
810
+ return;
811
+ }
812
+ this.preloadDueAdManagerTimer_.stop();
813
+ if (!this.preloadDueAdManager_) {
814
+ this.preloadDueAdManagerVideo_ = this.video_;
815
+ this.preloadDueAdManager_ =
816
+ await this.detachAndSavePreload(true);
817
+ }
818
+ });
819
+
820
+ // Note: we don't use shaka.ads.AdManager.AD_STOPPED to avoid add a
821
+ // optional module in the player.
822
+ this.adManagerEventManager_.listen(
823
+ this.adManager_, 'ad-stopped', (e) => {
824
+ if (this.config_.ads.supportsMultipleMediaElements) {
825
+ return;
826
+ }
827
+ const event = /** @type {?google.ima.AdEvent} */
828
+ (e['originalEvent']);
829
+ if (!event) {
830
+ return;
831
+ }
832
+ const contentResumeRequested =
833
+ google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED;
834
+ if (event.type != contentResumeRequested) {
835
+ return;
836
+ }
837
+ this.preloadDueAdManagerTimer_.tickAfter(0.1);
838
+ });
769
839
  }
770
840
 
771
841
  // If the browser comes back online after being offline, then try to play
@@ -779,6 +849,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
779
849
  this.checkVariantsTimer_ =
780
850
  new shaka.util.Timer(() => this.checkVariants_());
781
851
 
852
+ /** @private {?shaka.media.PreloadManager} */
853
+ this.preloadNextUrl_ = null;
854
+
782
855
  // Even though |attach| will start in later interpreter cycles, it should be
783
856
  // the LAST thing we do in the constructor because conceptually it relies on
784
857
  // player having been initialized.
@@ -913,6 +986,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
913
986
  this.trickPlayEventManager_.release();
914
987
  this.trickPlayEventManager_ = null;
915
988
  }
989
+ if (this.adManagerEventManager_) {
990
+ this.adManagerEventManager_.release();
991
+ this.adManagerEventManager_ = null;
992
+ }
916
993
 
917
994
  this.abrManagerFactory_ = null;
918
995
  this.config_ = null;
@@ -1030,10 +1107,15 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1030
1107
  }
1031
1108
  const manifest = shaka.media.ManifestParser.probeSupport();
1032
1109
  const media = shaka.media.MediaSourceEngine.probeSupport();
1110
+ const hardwareResolution =
1111
+ await shaka.util.Platform.detectMaxHardwareResolution();
1112
+
1113
+ /** @type {shaka.extern.SupportType} */
1033
1114
  const ret = {
1034
- manifest: manifest,
1035
- media: media,
1036
- drm: drm,
1115
+ manifest,
1116
+ media,
1117
+ drm,
1118
+ hardwareResolution,
1037
1119
  };
1038
1120
 
1039
1121
  const plugins = shaka.Player.supportPlugins_;
@@ -1301,6 +1383,15 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1301
1383
  this.adManager_.onAssetUnload();
1302
1384
  }
1303
1385
 
1386
+ if (this.preloadDueAdManager_ && !keepAdManager) {
1387
+ this.preloadDueAdManager_.destroy();
1388
+ this.preloadDueAdManager_ = null;
1389
+ }
1390
+
1391
+ if (!keepAdManager) {
1392
+ this.preloadDueAdManagerTimer_.stop();
1393
+ }
1394
+
1304
1395
  if (this.cmsdManager_) {
1305
1396
  this.cmsdManager_.reset();
1306
1397
  }
@@ -1332,6 +1423,14 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1332
1423
  this.drmEngine_ = null;
1333
1424
  }
1334
1425
 
1426
+ if (this.preloadNextUrl_ &&
1427
+ this.assetUri_ != this.preloadNextUrl_.getAssetUri()) {
1428
+ if (!this.preloadNextUrl_.isDestroyed()) {
1429
+ this.preloadNextUrl_.destroy();
1430
+ }
1431
+ this.preloadNextUrl_ = null;
1432
+ }
1433
+
1335
1434
  this.assetUri_ = null;
1336
1435
  this.mimeType_ = null;
1337
1436
  this.bufferObserver_ = null;
@@ -1351,6 +1450,15 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1351
1450
  }
1352
1451
  }
1353
1452
 
1453
+ // On some devices, cached MediaKeySystemAccess objects may corrupt
1454
+ // after several playbacks, and they are not able anymore to properly
1455
+ // create MediaKeys objects. To prevent it, clear the cache after
1456
+ // each playback.
1457
+ if (this.config_.streaming.clearDecodingCache) {
1458
+ shaka.util.StreamUtils.clearDecodingConfigCache();
1459
+ shaka.media.DrmEngine.clearMediaKeySystemAccessMap();
1460
+ }
1461
+
1354
1462
  this.manifest_ = null;
1355
1463
  this.stats_ = new shaka.util.Stats(); // Replace with a clean object.
1356
1464
  this.lastTextFactory_ = null;
@@ -1426,6 +1534,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1426
1534
  }
1427
1535
 
1428
1536
  if (this.assetUri_) {
1537
+ // Note: This is used to avoid the destruction of the nextUrl
1538
+ // preloadManager that can be the current one.
1539
+ this.assetUri_ = assetUri;
1429
1540
  await this.unload(/* initializeMediaSource= */ false);
1430
1541
  }
1431
1542
 
@@ -1627,6 +1738,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1627
1738
  // properly destroyed or released.
1628
1739
  await preloadManager.destroy();
1629
1740
  }
1741
+ this.preloadNextUrl_ = null;
1630
1742
  }
1631
1743
  }
1632
1744
 
@@ -1664,6 +1776,34 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1664
1776
  */
1665
1777
  async unloadAndSavePreload(
1666
1778
  initializeMediaSource = true, keepAdManager = false) {
1779
+ const preloadManager = await this.savePreload_();
1780
+ await this.unload(initializeMediaSource, keepAdManager);
1781
+ return preloadManager;
1782
+ }
1783
+
1784
+ /**
1785
+ * Detach the player from the current media element, if any, and returns a
1786
+ * PreloadManager that contains the loaded manifest of that asset, if any.
1787
+ * Allows for the asset to be re-loaded by this player faster, in the future.
1788
+ * When in src= mode, this detach but does not make a PreloadManager.
1789
+ * Leaves the player in a state where it cannot play media, until it has been
1790
+ * attached to something else.
1791
+ *
1792
+ * @param {boolean=} keepAdManager
1793
+ * @return {!Promise.<?shaka.media.PreloadManager>}
1794
+ * @export
1795
+ */
1796
+ async detachAndSavePreload(keepAdManager = false) {
1797
+ const preloadManager = await this.savePreload_();
1798
+ await this.detach(keepAdManager);
1799
+ return preloadManager;
1800
+ }
1801
+
1802
+ /**
1803
+ * @return {!Promise.<?shaka.media.PreloadManager>}
1804
+ * @private
1805
+ */
1806
+ async savePreload_() {
1667
1807
  let preloadManager = null;
1668
1808
  if (this.manifest_ && this.parser_ && this.parserFactory_ &&
1669
1809
  this.assetUri_) {
@@ -1698,7 +1838,6 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1698
1838
  this.abrManager_ = null;
1699
1839
  this.abrManagerFactory_ = null;
1700
1840
  }
1701
- await this.unload(initializeMediaSource, keepAdManager);
1702
1841
  return preloadManager;
1703
1842
  }
1704
1843
 
@@ -1968,6 +2107,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1968
2107
  // Sadly, as the network engine creation code must be replaceable by tests,
1969
2108
  // it cannot be made and use the utilities defined in this function.
1970
2109
  const networkingEngine = this.createNetworkingEngine(getPreloadManager);
2110
+ this.networkingEngine_.copyFiltersInto(networkingEngine);
1971
2111
 
1972
2112
  /** @return {!shaka.media.DrmEngine} */
1973
2113
  const createDrmEngine = () => {
@@ -2375,6 +2515,28 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2375
2515
  this.loadEventManager_.listen(
2376
2516
  mediaElement, 'timeupdate', onVideoProgress);
2377
2517
  this.onVideoProgress_();
2518
+ if (this.manifest_.nextUrl) {
2519
+ if (this.config_.streaming.preloadNextUrlWindow > 0) {
2520
+ const onTimeUpdate = async () => {
2521
+ const timeToEnd = this.video_.duration - this.video_.currentTime;
2522
+ if (!isNaN(timeToEnd)) {
2523
+ if (timeToEnd <= this.config_.streaming.preloadNextUrlWindow) {
2524
+ this.loadEventManager_.unlisten(
2525
+ mediaElement, 'timeupdate', onTimeUpdate);
2526
+ goog.asserts.assert(this.manifest_.nextUrl,
2527
+ 'this.manifest_.nextUrl should be valid.');
2528
+ this.preloadNextUrl_ =
2529
+ await this.preload(this.manifest_.nextUrl);
2530
+ }
2531
+ }
2532
+ };
2533
+ this.loadEventManager_.listen(
2534
+ mediaElement, 'timeupdate', onTimeUpdate);
2535
+ }
2536
+ this.loadEventManager_.listen(mediaElement, 'ended', () => {
2537
+ this.load(this.preloadNextUrl_ || this.manifest_.nextUrl);
2538
+ });
2539
+ }
2378
2540
  }
2379
2541
 
2380
2542
  if (this.adManager_) {
@@ -3412,10 +3574,12 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
3412
3574
  }
3413
3575
 
3414
3576
  // If lowLatencyMode is enabled, and inaccurateManifestTolerance and
3415
- // rebufferingGoal and segmentPrefetchLimit and baseDelay are not
3416
- // specified, set inaccurateManifestTolerance to 0 and rebufferingGoal
3417
- // to 0.01 and segmentPrefetchLimit to 2 and updateIntervalSeconds to
3418
- // 0.1 and baseDelay to 100 by default for low latency streaming.
3577
+ // rebufferingGoal and segmentPrefetchLimit and baseDelay and
3578
+ // autoCorrectDrift and maxDisabledTime are not specified, set
3579
+ // inaccurateManifestTolerance to 0 and rebufferingGoal to 0.01 and
3580
+ // segmentPrefetchLimit to 2 and updateIntervalSeconds to 0.1 and and
3581
+ // baseDelay to 100 and autoCorrectDrift to false and maxDisabledTime
3582
+ // to 1 by default for low latency streaming.
3419
3583
  if (config['streaming'] && config['streaming']['lowLatencyMode']) {
3420
3584
  if (config['streaming']['inaccurateManifestTolerance'] == undefined) {
3421
3585
  config['streaming']['inaccurateManifestTolerance'] = 0;
@@ -3429,6 +3593,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
3429
3593
  if (config['streaming']['updateIntervalSeconds'] == undefined) {
3430
3594
  config['streaming']['updateIntervalSeconds'] = 0.1;
3431
3595
  }
3596
+ if (config['streaming']['maxDisabledTime'] == undefined) {
3597
+ config['streaming']['maxDisabledTime'] = 1;
3598
+ }
3432
3599
  if (config['streaming']['retryParameters'] == undefined) {
3433
3600
  config['streaming']['retryParameters'] = {};
3434
3601
  }
@@ -3438,6 +3605,12 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
3438
3605
  if (config['manifest'] == undefined) {
3439
3606
  config['manifest'] = {};
3440
3607
  }
3608
+ if (config['manifest']['dash'] == undefined) {
3609
+ config['manifest']['dash'] = {};
3610
+ }
3611
+ if (config['manifest']['dash']['autoCorrectDrift'] == undefined) {
3612
+ config['manifest']['dash']['autoCorrectDrift'] = false;
3613
+ }
3441
3614
  if (config['manifest']['retryParameters'] == undefined) {
3442
3615
  config['manifest']['retryParameters'] = {};
3443
3616
  }
@@ -3512,6 +3685,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
3512
3685
  }
3513
3686
  }
3514
3687
  if (this.networkingEngine_) {
3688
+ this.networkingEngine_.setForceHTTP(this.config_.streaming.forceHTTP);
3515
3689
  this.networkingEngine_.setForceHTTPS(this.config_.streaming.forceHTTPS);
3516
3690
  }
3517
3691
 
@@ -3599,6 +3773,24 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
3599
3773
  return ret;
3600
3774
  }
3601
3775
 
3776
+ /**
3777
+ * Return a copy of the current non default configuration. Modifications of
3778
+ * the returned value will not affect the Player's active configuration.
3779
+ * You must call <code>player.configure()</code> to make changes.
3780
+ *
3781
+ * @return {!Object}
3782
+ * @export
3783
+ */
3784
+ getNonDefaultConfiguration() {
3785
+ goog.asserts.assert(this.config_, 'Config must not be null!');
3786
+
3787
+ const ret = this.defaultConfig_();
3788
+ shaka.util.PlayerConfiguration.mergeConfigObjects(
3789
+ ret, this.config_, this.defaultConfig_());
3790
+ return shaka.util.ConfigUtils.getDifferenceFromConfigObjects(
3791
+ this.config_, this.defaultConfig_());
3792
+ }
3793
+
3602
3794
  /**
3603
3795
  * Return a reference to the current configuration. Modifications to the
3604
3796
  * returned value will affect the Player's active configuration. This method
@@ -5733,6 +5925,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5733
5925
  error.code == shaka.util.Error.Code.HTTP_ERROR) {
5734
5926
  // These errors can be near-instant, so delay a bit before retrying.
5735
5927
  retryDelaySeconds = 1;
5928
+ if (this.config_.streaming.lowLatencyMode) {
5929
+ retryDelaySeconds = 0.1;
5930
+ }
5736
5931
  } else if (error.code == shaka.util.Error.Code.TIMEOUT) {
5737
5932
  // We already waited for a timeout, so retry quickly.
5738
5933
  retryDelaySeconds = 0.1;
@@ -6026,8 +6221,14 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
6026
6221
  // serviceDescription must override if it is defined in the MPD and
6027
6222
  // liveSync configuration is not set.
6028
6223
  if (this.manifest_ && this.manifest_.serviceDescription) {
6029
- liveSyncMaxLatency = this.manifest_.serviceDescription.maxLatency ||
6030
- this.config_.streaming.liveSyncMaxLatency;
6224
+ liveSyncMaxLatency = this.config_.streaming.liveSyncMaxLatency;
6225
+ if (this.manifest_.serviceDescription.targetLatency != null) {
6226
+ liveSyncMaxLatency =
6227
+ this.manifest_.serviceDescription.targetLatency +
6228
+ this.config_.streaming.liveSyncTargetLatencyTolerance;
6229
+ } else if (this.manifest_.serviceDescription.maxLatency != null) {
6230
+ liveSyncMaxLatency = this.manifest_.serviceDescription.maxLatency;
6231
+ }
6031
6232
  liveSyncPlaybackRate =
6032
6233
  this.manifest_.serviceDescription.maxPlaybackRate ||
6033
6234
  this.config_.streaming.liveSyncPlaybackRate;
@@ -6043,8 +6244,14 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
6043
6244
  // serviceDescription must override if it is defined in the MPD and
6044
6245
  // liveSync configuration is not set.
6045
6246
  if (this.manifest_ && this.manifest_.serviceDescription) {
6046
- liveSyncMinLatency = this.manifest_.serviceDescription.minLatency ||
6047
- this.config_.streaming.liveSyncMinLatency;
6247
+ liveSyncMinLatency = this.config_.streaming.liveSyncMinLatency;
6248
+ if (this.manifest_.serviceDescription.targetLatency != null) {
6249
+ liveSyncMinLatency =
6250
+ this.manifest_.serviceDescription.targetLatency -
6251
+ this.config_.streaming.liveSyncTargetLatencyTolerance;
6252
+ } else if (this.manifest_.serviceDescription.minLatency != null) {
6253
+ liveSyncMinLatency = this.manifest_.serviceDescription.minLatency;
6254
+ }
6048
6255
  liveSyncMinPlaybackRate =
6049
6256
  this.manifest_.serviceDescription.minPlaybackRate ||
6050
6257
  this.config_.streaming.liveSyncMinPlaybackRate;
@@ -7239,7 +7446,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
7239
7446
  * @export
7240
7447
  */
7241
7448
  // eslint-disable-next-line no-useless-concat
7242
- shaka.Player.version = 'v4.8.8' + '-uncompiled'; // x-release-please-version
7449
+ shaka.Player.version = 'v4.9.0' + '-uncompiled'; // x-release-please-version
7243
7450
 
7244
7451
  // Initialize the deprecation system using the version string we just set
7245
7452
  // on the player.
@@ -8,6 +8,7 @@ goog.provide('shaka.polyfill.MediaCapabilities');
8
8
 
9
9
  goog.require('shaka.log');
10
10
  goog.require('shaka.media.Capabilities');
11
+ goog.require('shaka.media.DrmEngine');
11
12
  goog.require('shaka.polyfill');
12
13
  goog.require('shaka.util.Platform');
13
14
 
@@ -276,25 +277,22 @@ shaka.polyfill.MediaCapabilities = class {
276
277
  mediaKeySystemConfig.videoCapabilities = videoCapabilities;
277
278
  }
278
279
 
279
- const cacheKey = shaka.polyfill.MediaCapabilities
280
- .generateKeySystemCacheKey_(
281
- videoConfig ? videoConfig.contentType : '',
282
- audioConfig ? audioConfig.contentType : '',
283
- mcapKeySystemConfig.keySystem);
280
+ const videoCodec = videoConfig ? videoConfig.contentType : '';
281
+ const audioCodec = audioConfig ? audioConfig.contentType : '';
282
+ const keySystem = mcapKeySystemConfig.keySystem;
284
283
 
285
284
  /** @type {MediaKeySystemAccess} */
286
285
  let keySystemAccess = null;
287
286
  try {
288
- if (cacheKey in shaka.polyfill.MediaCapabilities
289
- .memoizedMediaKeySystemAccessRequests_) {
290
- keySystemAccess = shaka.polyfill.MediaCapabilities
291
- .memoizedMediaKeySystemAccessRequests_[cacheKey];
287
+ if (shaka.media.DrmEngine.hasMediaKeySystemAccess(
288
+ videoCodec, audioCodec, keySystem)) {
289
+ keySystemAccess = shaka.media.DrmEngine.getMediaKeySystemAccess(
290
+ videoCodec, audioCodec, keySystem);
292
291
  } else {
293
292
  keySystemAccess = await navigator.requestMediaKeySystemAccess(
294
293
  mcapKeySystemConfig.keySystem, [mediaKeySystemConfig]);
295
- shaka.polyfill.MediaCapabilities
296
- .memoizedMediaKeySystemAccessRequests_[cacheKey] =
297
- keySystemAccess;
294
+ shaka.media.DrmEngine.setMediaKeySystemAccess(
295
+ videoCodec, audioCodec, keySystem, keySystemAccess);
298
296
  }
299
297
  } catch (e) {
300
298
  shaka.log.info('navigator.requestMediaKeySystemAccess failed.');
@@ -346,19 +344,6 @@ shaka.polyfill.MediaCapabilities = class {
346
344
  }
347
345
  return result;
348
346
  }
349
-
350
- /**
351
- * A method for generating a key for the MediaKeySystemAccessRequests cache.
352
- *
353
- * @param {!string} videoCodec
354
- * @param {!string} audioCodec
355
- * @param {!string} keySystem
356
- * @return {!string}
357
- * @private
358
- */
359
- static generateKeySystemCacheKey_(videoCodec, audioCodec, keySystem) {
360
- return `${videoCodec}#${audioCodec}#${keySystem}`;
361
- }
362
347
  };
363
348
 
364
349
  /**
@@ -371,16 +356,6 @@ shaka.polyfill.MediaCapabilities = class {
371
356
  */
372
357
  shaka.polyfill.MediaCapabilities.originalMcap = null;
373
358
 
374
- /**
375
- * A cache that stores the MediaKeySystemAccess result of calling
376
- * `navigator.requestMediaKeySystemAccess` by a key combination of
377
- * video/audio codec and key system string.
378
- *
379
- * @type {(Object<(!string), (!MediaKeySystemAccess)>)}
380
- * @export
381
- */
382
- shaka.polyfill.MediaCapabilities.memoizedMediaKeySystemAccessRequests_ = {};
383
-
384
359
  /**
385
360
  * A cache that stores the canDisplayType result of calling
386
361
  * `cast.__platform__.canDisplayType`.
@@ -28,17 +28,28 @@ shaka.util.ConfigUtils = class {
28
28
  static mergeConfigObjects(destination, source, template, overrides, path) {
29
29
  goog.asserts.assert(destination, 'Destination config must not be null!');
30
30
 
31
- /**
32
- * @type {boolean}
33
- * If true, don't validate the keys in the next level.
34
- */
35
- const ignoreKeys = path in overrides;
31
+ // If true, override the template.
32
+ const overrideTemplate = path in overrides;
33
+
34
+ // If true, treat the source as a generic object to be copied without
35
+ // descending more deeply.
36
+ let genericObject = false;
37
+ if (overrideTemplate) {
38
+ genericObject = template.constructor == Object &&
39
+ Object.keys(overrides).length == 0;
40
+ } else {
41
+ genericObject = template.constructor == Object &&
42
+ Object.keys(template).length == 0;
43
+ }
44
+
45
+ // If true, don't validate the keys in the next level.
46
+ const ignoreKeys = overrideTemplate || genericObject;
36
47
 
37
48
  let isValid = true;
38
49
 
39
50
  for (const k in source) {
40
51
  const subPath = path + '.' + k;
41
- const subTemplate = ignoreKeys ? overrides[path] : template[k];
52
+ const subTemplate = overrideTemplate ? overrides[path] : template[k];
42
53
 
43
54
  // The order of these checks is important.
44
55
  if (!ignoreKeys && !(k in template)) {
@@ -55,6 +66,10 @@ shaka.util.ConfigUtils = class {
55
66
  // There is something in the template, so go back to that.
56
67
  destination[k] = shaka.util.ObjectUtils.cloneObject(subTemplate);
57
68
  }
69
+ } else if (genericObject) {
70
+ // Copy the fields of a generic object directly without a template and
71
+ // without descending any deeper.
72
+ destination[k] = source[k];
58
73
  } else if (subTemplate.constructor == Object &&
59
74
  source[k] &&
60
75
  source[k].constructor == Object) {
@@ -152,4 +167,57 @@ shaka.util.ConfigUtils = class {
152
167
  static referenceParametersAndReturn(parameters, returnValue) {
153
168
  return parameters && returnValue;
154
169
  }
170
+
171
+ /**
172
+ * @param {!Object} object
173
+ * @param {!Object} base
174
+ * @return {!Object}
175
+ * @export
176
+ */
177
+ static getDifferenceFromConfigObjects(object, base) {
178
+ const isObject = (obj) => {
179
+ return obj && typeof obj === 'object' && !Array.isArray(obj);
180
+ };
181
+
182
+ const isArrayEmpty = (array) => {
183
+ return Array.isArray(array) && array.length === 0;
184
+ };
185
+
186
+ const changes = (object, base) => {
187
+ return Object.keys(object).reduce((acc, key) => {
188
+ const value = object[key];
189
+ // eslint-disable-next-line no-prototype-builtins
190
+ if (!base.hasOwnProperty(key)) {
191
+ acc[key] = value;
192
+ } else if (isObject(value) && isObject(base[key])) {
193
+ const diff = changes(value, base[key]);
194
+ if (Object.keys(diff).length > 0 || !isObject(diff)) {
195
+ acc[key] = diff;
196
+ }
197
+ } else if (isArrayEmpty(value) && isArrayEmpty(base[key])) {
198
+ // Do nothing if both are empty arrays
199
+ } else if (value !== base[key]) {
200
+ acc[key] = value;
201
+ }
202
+ return acc;
203
+ }, {});
204
+ };
205
+
206
+ const diff = changes(object, base);
207
+
208
+ const removeEmpty = (obj) => {
209
+ for (const key of Object.keys(obj)) {
210
+ if (isObject(obj[key]) && Object.keys(obj[key]).length === 0) {
211
+ delete obj[key];
212
+ } else if (isArrayEmpty(obj[key])) {
213
+ delete obj[key];
214
+ } else if (isObject(obj[key])) {
215
+ removeEmpty(obj[key]);
216
+ }
217
+ }
218
+ };
219
+
220
+ removeEmpty(diff);
221
+ return diff;
222
+ }
155
223
  };
package/lib/util/error.js CHANGED
@@ -212,6 +212,8 @@ shaka.util.Error.Code = {
212
212
  * <br> error.data[3] is the map of response headers.
213
213
  * <br> error.data[4] is the NetworkingEngine.RequestType of the request,
214
214
  * if one was provided.
215
+ * <br> error.data[5] is the final URI. This may be different if the initial
216
+ * URI (error.data[0]) issued a redirect.
215
217
  */
216
218
  'BAD_HTTP_STATUS': 1001,
217
219
 
@@ -770,6 +772,11 @@ shaka.util.Error.Code = {
770
772
  */
771
773
  'DASH_UNSUPPORTED_AES_128': 4051,
772
774
 
775
+ /**
776
+ * Patch requested during an update did not match original manifest.
777
+ */
778
+ 'DASH_INVALID_PATCH': 4052,
779
+
773
780
 
774
781
  // RETIRED: 'INCONSISTENT_BUFFER_STATE': 5000,
775
782
  // RETIRED: 'INVALID_SEGMENT_INDEX': 5001,
@@ -555,6 +555,7 @@ shaka.util.Mp4BoxParsers = class {
555
555
  * @return {!shaka.util.ParsedCOLRBox}
556
556
  */
557
557
  static parseCOLR(reader) {
558
+ let colorGamut = null;
558
559
  let videoRange = null;
559
560
  const data = reader.readBytes(4);
560
561
  let colorType = '';
@@ -563,7 +564,22 @@ shaka.util.Mp4BoxParsers = class {
563
564
  colorType += String.fromCharCode(data[2]);
564
565
  colorType += String.fromCharCode(data[3]);
565
566
  if (colorType === 'nclx') {
566
- reader.readUint16(); // colour_primaries
567
+ const colorPrimaries = reader.readUint16();
568
+ switch (colorPrimaries) {
569
+ case 1:
570
+ case 5:
571
+ case 6:
572
+ case 7:
573
+ colorGamut = 'srgb';
574
+ break;
575
+ case 9:
576
+ colorGamut = 'rec2020';
577
+ break;
578
+ case 11:
579
+ case 12:
580
+ colorGamut = 'p3';
581
+ break;
582
+ }
567
583
  const transferCharacteristics = reader.readUint16();
568
584
  reader.readUint16(); // matrix_coefficients
569
585
  switch (transferCharacteristics) {
@@ -582,7 +598,10 @@ shaka.util.Mp4BoxParsers = class {
582
598
  break;
583
599
  }
584
600
  }
585
- return {videoRange};
601
+ return {
602
+ colorGamut,
603
+ videoRange,
604
+ };
586
605
  }
587
606
 
588
607
  /**
@@ -913,9 +932,11 @@ shaka.util.ParsedHFOVBox;
913
932
 
914
933
  /**
915
934
  * @typedef {{
935
+ * colorGamut: ?string,
916
936
  * videoRange: ?string
917
937
  * }}
918
938
  *
939
+ * @property {?string} colorGamut
919
940
  * @property {?string} videoRange
920
941
  *
921
942
  * @exportDoc