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
@@ -21,6 +21,7 @@ goog.require('shaka.util.Lazy');
21
21
  goog.require('shaka.util.ManifestParserUtils');
22
22
  goog.require('shaka.util.MapUtils');
23
23
  goog.require('shaka.util.MimeUtils');
24
+ goog.require('shaka.util.ObjectUtils');
24
25
  goog.require('shaka.util.Platform');
25
26
  goog.require('shaka.util.Pssh');
26
27
  goog.require('shaka.util.PublicPromise');
@@ -1492,6 +1493,13 @@ shaka.media.DrmEngine = class {
1492
1493
  request.initData = metadata.initData;
1493
1494
  request.initDataType = metadata.initDataType;
1494
1495
  }
1496
+ if (advancedConfig && advancedConfig.headers) {
1497
+ // Add these to the existing headers. Do not clobber them!
1498
+ // For PlayReady, there will already be headers in the request.
1499
+ for (const header in advancedConfig.headers) {
1500
+ request.headers[header] = advancedConfig.headers[header];
1501
+ }
1502
+ }
1495
1503
  // NOTE: allowCrossSiteCredentials can be set in a request filter.
1496
1504
 
1497
1505
  if (shaka.media.DrmEngine.isPlayReadyKeySystem(
@@ -1801,33 +1809,200 @@ shaka.media.DrmEngine = class {
1801
1809
  'com.widevine.alpha',
1802
1810
  'com.microsoft.playready',
1803
1811
  'com.microsoft.playready.recommendation',
1812
+ 'com.chromecast.playready',
1804
1813
  'com.apple.fps.1_0',
1805
1814
  'com.apple.fps',
1806
1815
  ];
1807
1816
 
1817
+ const widevineRobustness = [
1818
+ 'SW_SECURE_CRYPTO',
1819
+ 'SW_SECURE_DECODE',
1820
+ 'HW_SECURE_CRYPTO',
1821
+ 'HW_SECURE_DECODE',
1822
+ 'HW_SECURE_ALL',
1823
+ ];
1824
+
1825
+ const playreadyRobustness = [
1826
+ '150',
1827
+ '2000',
1828
+ '3000',
1829
+ ];
1830
+
1831
+ const testRobustness = {
1832
+ 'com.widevine.alpha': widevineRobustness,
1833
+ 'com.microsoft.playready': playreadyRobustness,
1834
+ 'com.microsoft.playready.recommendation': playreadyRobustness,
1835
+ 'com.chromecast.playready': playreadyRobustness,
1836
+ };
1837
+
1808
1838
  const basicVideoCapabilities = [
1809
1839
  {contentType: 'video/mp4; codecs="avc1.42E01E"'},
1810
1840
  {contentType: 'video/webm; codecs="vp8"'},
1811
1841
  ];
1812
1842
 
1813
- const basicConfig = {
1814
- initDataTypes: ['cenc'],
1815
- videoCapabilities: basicVideoCapabilities,
1816
- };
1817
- const offlineConfig = {
1843
+ const basicAudioCapabilities = [
1844
+ {contentType: 'audio/mp4; codecs="mp4a.40.2"'},
1845
+ {contentType: 'audio/webm; codecs="opus"'},
1846
+ ];
1847
+
1848
+ const basicConfigTemplate = {
1818
1849
  videoCapabilities: basicVideoCapabilities,
1819
- persistentState: 'required',
1820
- sessionTypes: ['persistent-license'],
1850
+ audioCapabilities: basicAudioCapabilities,
1851
+ initDataTypes: ['cenc', 'sinf', 'skd', 'keyids'],
1821
1852
  };
1822
1853
 
1823
- // Try the offline config first, then fall back to the basic config.
1824
- const configs = [offlineConfig, basicConfig];
1854
+ const testEncryptionSchemes = [
1855
+ null,
1856
+ 'cenc',
1857
+ 'cbcs',
1858
+ 'cbcs-1-9',
1859
+ ];
1825
1860
 
1826
1861
  /** @type {!Map.<string, ?shaka.extern.DrmSupportType>} */
1827
1862
  const support = new Map();
1828
1863
 
1829
- const testSystem = async (keySystem) => {
1864
+ /**
1865
+ * @param {string} keySystem
1866
+ * @param {MediaKeySystemAccess} access
1867
+ * @return {!Promise}
1868
+ */
1869
+ const processMediaKeySystemAccess = async (keySystem, access) => {
1870
+ try {
1871
+ await access.createMediaKeys();
1872
+ } catch (error) {
1873
+ // In some cases, we can get a successful access object but fail to
1874
+ // create a MediaKeys instance. When this happens, don't update the
1875
+ // support structure. If a previous test succeeded, we won't overwrite
1876
+ // any of the results.
1877
+ return;
1878
+ }
1879
+
1880
+ // If sessionTypes is missing, assume no support for persistent-license.
1881
+ const sessionTypes = access.getConfiguration().sessionTypes;
1882
+ let persistentState = sessionTypes ?
1883
+ sessionTypes.includes('persistent-license') : false;
1884
+
1885
+ // Tizen 3.0 doesn't support persistent licenses, but reports that it
1886
+ // does. It doesn't fail until you call update() with a license
1887
+ // response, which is way too late.
1888
+ // This is a work-around for #894.
1889
+ if (shaka.util.Platform.isTizen3()) {
1890
+ persistentState = false;
1891
+ }
1892
+
1893
+ const videoCapabilities = access.getConfiguration().videoCapabilities;
1894
+ const audioCapabilities = access.getConfiguration().audioCapabilities;
1895
+
1896
+ let supportValue = {
1897
+ persistentState,
1898
+ encryptionSchemes: [],
1899
+ videoRobustnessLevels: [],
1900
+ audioRobustnessLevels: [],
1901
+ };
1902
+ if (support.has(keySystem) && support.get(keySystem)) {
1903
+ // Update the existing non-null value.
1904
+ supportValue = support.get(keySystem);
1905
+ } else {
1906
+ // Set a new one.
1907
+ support.set(keySystem, supportValue);
1908
+ }
1909
+
1910
+ // If the returned config doesn't mention encryptionScheme, the field
1911
+ // is not supported. If installed, our polyfills should make sure this
1912
+ // doesn't happen.
1913
+ const returnedScheme = videoCapabilities[0].encryptionScheme;
1914
+ if (returnedScheme &&
1915
+ !supportValue.encryptionSchemes.includes(returnedScheme)) {
1916
+ supportValue.encryptionSchemes.push(returnedScheme);
1917
+ }
1918
+
1919
+ const videoRobustness = videoCapabilities[0].robustness;
1920
+ if (videoRobustness &&
1921
+ !supportValue.videoRobustnessLevels.includes(videoRobustness)) {
1922
+ supportValue.videoRobustnessLevels.push(videoRobustness);
1923
+ }
1924
+
1925
+ const audioRobustness = audioCapabilities[0].robustness;
1926
+ if (audioRobustness &&
1927
+ !supportValue.audioRobustnessLevels.includes(audioRobustness)) {
1928
+ supportValue.audioRobustnessLevels.push(audioRobustness);
1929
+ }
1930
+ };
1931
+
1932
+ const testSystemEme = async (keySystem, encryptionScheme,
1933
+ videoRobustness, audioRobustness) => {
1934
+ try {
1935
+ const basicConfig =
1936
+ shaka.util.ObjectUtils.cloneObject(basicConfigTemplate);
1937
+ for (const cap of basicConfig.videoCapabilities) {
1938
+ cap.encryptionScheme = encryptionScheme;
1939
+ cap.robustness = videoRobustness;
1940
+ }
1941
+ for (const cap of basicConfig.audioCapabilities) {
1942
+ cap.encryptionScheme = encryptionScheme;
1943
+ cap.robustness = audioRobustness;
1944
+ }
1945
+
1946
+ const offlineConfig = shaka.util.ObjectUtils.cloneObject(basicConfig);
1947
+ offlineConfig.persistentState = 'required';
1948
+ offlineConfig.sessionTypes = ['persistent-license'];
1949
+
1950
+ const configs = [offlineConfig, basicConfig];
1951
+
1952
+ const access = await navigator.requestMediaKeySystemAccess(
1953
+ keySystem, configs);
1954
+ await processMediaKeySystemAccess(keySystem, access);
1955
+ } catch (error) {} // Ignore errors.
1956
+ };
1957
+
1958
+ const testSystemMcap = async (keySystem, encryptionScheme,
1959
+ videoRobustness, audioRobustness) => {
1830
1960
  try {
1961
+ const decodingConfig = {
1962
+ type: 'media-source',
1963
+ video: {
1964
+ contentType: basicVideoCapabilities[0].contentType,
1965
+ width: 640,
1966
+ height: 480,
1967
+ bitrate: 1,
1968
+ framerate: 1,
1969
+ },
1970
+ audio: {
1971
+ contentType: basicAudioCapabilities[0].contentType,
1972
+ channels: 2,
1973
+ bitrate: 1,
1974
+ samplerate: 1,
1975
+ },
1976
+ keySystemConfiguration: {
1977
+ keySystem,
1978
+ video: {
1979
+ encryptionScheme,
1980
+ robustness: videoRobustness,
1981
+ },
1982
+ audio: {
1983
+ encryptionScheme,
1984
+ robustness: audioRobustness,
1985
+ },
1986
+ },
1987
+ };
1988
+
1989
+ const decodingInfo =
1990
+ await navigator.mediaCapabilities.decodingInfo(decodingConfig);
1991
+
1992
+ const access = decodingInfo.keySystemAccess;
1993
+ await processMediaKeySystemAccess(keySystem, access);
1994
+ } catch (error) {} // Ignore errors.
1995
+ };
1996
+
1997
+ // Initialize the support structure for each key system.
1998
+ for (const keySystem of testKeySystems) {
1999
+ support.set(keySystem, null);
2000
+ }
2001
+
2002
+ // Test each key system and encryption scheme.
2003
+ const tests = [];
2004
+ for (const encryptionScheme of testEncryptionSchemes) {
2005
+ for (const keySystem of testKeySystems) {
1831
2006
  // Our Polyfill will reject anything apart com.apple.fps key systems.
1832
2007
  // It seems the Safari modern EME API will allow to request a
1833
2008
  // MediaKeySystemAccess for the ClearKey CDM, create and update a key
@@ -1835,40 +2010,22 @@ shaka.media.DrmEngine = class {
1835
2010
  // Safari bug: https://bugs.webkit.org/show_bug.cgi?id=231006
1836
2011
  if (keySystem === 'org.w3.clearkey' &&
1837
2012
  shaka.util.Platform.isSafari()) {
1838
- throw new Error('Unsupported keySystem');
1839
- }
1840
-
1841
- const access = await navigator.requestMediaKeySystemAccess(
1842
- keySystem, configs);
1843
-
1844
- // Edge doesn't return supported session types, but current versions
1845
- // do not support persistent-license. If sessionTypes is missing,
1846
- // assume no support for persistent-license.
1847
- // TODO: Polyfill Edge to return known supported session types.
1848
- // Edge bug: https://bit.ly/2IeKzho
1849
- const sessionTypes = access.getConfiguration().sessionTypes;
1850
- let persistentState = sessionTypes ?
1851
- sessionTypes.includes('persistent-license') : false;
1852
-
1853
- // Tizen 3.0 doesn't support persistent licenses, but reports that it
1854
- // does. It doesn't fail until you call update() with a license
1855
- // response, which is way too late.
1856
- // This is a work-around for #894.
1857
- if (shaka.util.Platform.isTizen3()) {
1858
- persistentState = false;
2013
+ continue;
1859
2014
  }
2015
+ tests.push(testSystemEme(keySystem, encryptionScheme, '', ''));
2016
+ tests.push(testSystemMcap(keySystem, encryptionScheme, '', ''));
2017
+ }
2018
+ }
1860
2019
 
1861
- support.set(keySystem, {persistentState: persistentState});
1862
- await access.createMediaKeys();
1863
- } catch (e) {
1864
- // Either the request failed or createMediaKeys failed.
1865
- // Either way, write null to the support object.
1866
- support.set(keySystem, null);
2020
+ for (const keySystem of testKeySystems) {
2021
+ for (const robustness of (testRobustness[keySystem] || [])) {
2022
+ tests.push(testSystemEme(keySystem, null, robustness, ''));
2023
+ tests.push(testSystemEme(keySystem, null, '', robustness));
2024
+ tests.push(testSystemMcap(keySystem, null, robustness, ''));
2025
+ tests.push(testSystemMcap(keySystem, null, '', robustness));
1867
2026
  }
1868
- };
2027
+ }
1869
2028
 
1870
- // Test each key system.
1871
- const tests = testKeySystems.map((keySystem) => testSystem(keySystem));
1872
2029
  await Promise.all(tests);
1873
2030
  return shaka.util.MapUtils.asObject(support);
1874
2031
  }
@@ -2562,6 +2719,73 @@ shaka.media.DrmEngine = class {
2562
2719
  this.newInitData('cenc', combinedData);
2563
2720
  return this.allSessionsLoaded_;
2564
2721
  }
2722
+
2723
+ /**
2724
+ * A method for generating a key for the MediaKeySystemAccessRequests cache.
2725
+ *
2726
+ * @param {string} videoCodec
2727
+ * @param {string} audioCodec
2728
+ * @param {string} keySystem
2729
+ * @return {string}
2730
+ * @private
2731
+ */
2732
+ static generateKeySystemCacheKey_(videoCodec, audioCodec, keySystem) {
2733
+ return `${videoCodec}#${audioCodec}#${keySystem}`;
2734
+ }
2735
+
2736
+ /**
2737
+ * Check does MediaKeySystemAccess cache contains something for following
2738
+ * attributes.
2739
+ *
2740
+ * @param {string} videoCodec
2741
+ * @param {string} audioCodec
2742
+ * @param {string} keySystem
2743
+ * @return {boolean}
2744
+ */
2745
+ static hasMediaKeySystemAccess(videoCodec, audioCodec, keySystem) {
2746
+ const DrmEngine = shaka.media.DrmEngine;
2747
+ const key = DrmEngine.generateKeySystemCacheKey_(
2748
+ videoCodec, audioCodec, keySystem);
2749
+ return DrmEngine.memoizedMediaKeySystemAccessRequests_.has(key);
2750
+ }
2751
+
2752
+ /**
2753
+ * Get MediaKeySystemAccess object for following attributes.
2754
+ *
2755
+ * @param {string} videoCodec
2756
+ * @param {string} audioCodec
2757
+ * @param {string} keySystem
2758
+ * @return {?MediaKeySystemAccess}
2759
+ */
2760
+ static getMediaKeySystemAccess(videoCodec, audioCodec, keySystem) {
2761
+ const DrmEngine = shaka.media.DrmEngine;
2762
+ const key = DrmEngine.generateKeySystemCacheKey_(
2763
+ videoCodec, audioCodec, keySystem);
2764
+ return DrmEngine.memoizedMediaKeySystemAccessRequests_.get(key) || null;
2765
+ }
2766
+
2767
+ /**
2768
+ * Store MediaKeySystemAccess object associated with specified attributes.
2769
+ *
2770
+ * @param {string} videoCodec
2771
+ * @param {string} audioCodec
2772
+ * @param {string} keySystem
2773
+ * @param {!MediaKeySystemAccess} mksa
2774
+ */
2775
+ static setMediaKeySystemAccess(videoCodec, audioCodec, keySystem, mksa) {
2776
+ const DrmEngine = shaka.media.DrmEngine;
2777
+ const key = DrmEngine.generateKeySystemCacheKey_(
2778
+ videoCodec, audioCodec, keySystem);
2779
+ return DrmEngine.memoizedMediaKeySystemAccessRequests_.set(key, mksa);
2780
+ }
2781
+
2782
+ /**
2783
+ * Clears underlying cache.
2784
+ */
2785
+ static clearMediaKeySystemAccessMap() {
2786
+ const DrmEngine = shaka.media.DrmEngine;
2787
+ DrmEngine.memoizedMediaKeySystemAccessRequests_.clear();
2788
+ }
2565
2789
  };
2566
2790
 
2567
2791
 
@@ -2655,3 +2879,13 @@ shaka.media.DrmEngine.KEY_STATUS_BATCH_TIME = 0.5;
2655
2879
  */
2656
2880
  shaka.media.DrmEngine.DUMMY_KEY_ID = new shaka.util.Lazy(
2657
2881
  () => shaka.util.BufferUtils.toArrayBuffer(new Uint8Array([0])));
2882
+
2883
+
2884
+ /**
2885
+ * A cache that stores the MediaKeySystemAccess result of calling
2886
+ * `navigator.requestMediaKeySystemAccess` by a key combination of
2887
+ * video/audio codec and key system string.
2888
+ *
2889
+ * @private {!Map<string, !MediaKeySystemAccess>}
2890
+ */
2891
+ shaka.media.DrmEngine.memoizedMediaKeySystemAccessRequests_ = new Map();
@@ -19,7 +19,7 @@ goog.require('shaka.util.Error');
19
19
  shaka.media.ManifestFilterer = class {
20
20
  /**
21
21
  * @param {?shaka.extern.PlayerConfiguration} config
22
- * @param {{width: number, height: number}} maxHwRes
22
+ * @param {shaka.extern.Resolution} maxHwRes
23
23
  * @param {?shaka.media.DrmEngine} drmEngine
24
24
  */
25
25
  constructor(config, maxHwRes, drmEngine) {
@@ -28,7 +28,7 @@ shaka.media.ManifestFilterer = class {
28
28
  /** @private {!shaka.extern.PlayerConfiguration} */
29
29
  this.config_ = config;
30
30
 
31
- /** @private {{width: number, height: number}} */
31
+ /** @private {shaka.extern.Resolution} */
32
32
  this.maxHwRes_ = maxHwRes;
33
33
 
34
34
  /** @private {?shaka.media.DrmEngine} drmEngine */
@@ -41,27 +41,23 @@ shaka.media.ManifestFilterer = class {
41
41
  }
42
42
 
43
43
  /**
44
- * Filters a manifest, removing unplayable streams/variants.
44
+ * Filters a manifest, removing unplayable streams/variants and choosing
45
+ * the codecs.
45
46
  *
46
47
  * @param {?shaka.extern.Manifest} manifest
47
48
  * @return {!Promise.<boolean>} tracksChanged
48
49
  */
49
50
  async filterManifest(manifest) {
50
- await this.filterManifestWithStreamUtils_(manifest);
51
- return this.filterManifestWithRestrictions(manifest);
52
- }
53
-
54
- /**
55
- * Filters a manifest, removing unplayable streams/variants.
56
- *
57
- * @param {?shaka.extern.Manifest} manifest
58
- * @private
59
- */
60
- async filterManifestWithStreamUtils_(manifest) {
61
51
  goog.asserts.assert(manifest, 'Manifest should exist!');
62
52
  await shaka.util.StreamUtils.filterManifest(this.drmEngine_, manifest,
63
53
  this.config_.drm.preferredKeySystems);
54
+ shaka.util.StreamUtils.chooseCodecsAndFilterManifest(
55
+ manifest,
56
+ this.config_.preferredVideoCodecs,
57
+ this.config_.preferredAudioCodecs,
58
+ this.config_.preferredDecodingAttributes);
64
59
  this.checkPlayableVariants_(manifest);
60
+ return this.filterManifestWithRestrictions(manifest);
65
61
  }
66
62
 
67
63
 
@@ -553,7 +553,9 @@ shaka.media.PreloadManager = class extends shaka.util.FakeEventTarget {
553
553
 
554
554
  const tracksChangedInitial = this.manifestFilterer_.applyRestrictions(
555
555
  this.manifest_);
556
- if (tracksChangedInitial) {
556
+ const tracksChangedBefore = await this.manifestFilterer_.filterManifest(
557
+ this.manifest_);
558
+ if (tracksChangedInitial || tracksChangedBefore) {
557
559
  const event = this.makeEvent_(
558
560
  shaka.util.FakeEvent.EventName.TracksChanged);
559
561
  await Promise.resolve();
@@ -568,7 +570,6 @@ shaka.media.PreloadManager = class extends shaka.util.FakeEventTarget {
568
570
 
569
571
  // Now that we have drm information, filter the manifest (again) so that
570
572
  // we can ensure we only use variants with the selected key system.
571
- // const tracksChanged =
572
573
  const tracksChangedAfter = await this.manifestFilterer_.filterManifest(
573
574
  this.manifest_);
574
575
  if (tracksChangedAfter) {
@@ -630,14 +631,6 @@ shaka.media.PreloadManager = class extends shaka.util.FakeEventTarget {
630
631
  this.config_.preferredVideoLabel,
631
632
  this.config_.mediaSource.codecSwitchingStrategy,
632
633
  this.config_.manifest.dash.enableAudioGroups);
633
-
634
- // If the content is multi-codec and the browser can play more than one of
635
- // them, choose codecs now before we initialize streaming.
636
- shaka.util.StreamUtils.chooseCodecsAndFilterManifest(
637
- this.manifest_,
638
- this.config_.preferredVideoCodecs,
639
- this.config_.preferredAudioCodecs,
640
- this.config_.preferredDecodingAttributes);
641
634
  }
642
635
 
643
636
  // Make the ABR manager.
@@ -41,6 +41,7 @@ shaka.media.SegmentUtils = class {
41
41
  sampleRate: null,
42
42
  closedCaptions: new Map(),
43
43
  videoRange: null,
44
+ colorGamut: null,
44
45
  };
45
46
  }
46
47
 
@@ -125,6 +126,7 @@ shaka.media.SegmentUtils = class {
125
126
  sampleRate: null,
126
127
  closedCaptions: closedCaptions,
127
128
  videoRange: null,
129
+ colorGamut: null,
128
130
  };
129
131
  }
130
132
 
@@ -199,6 +201,8 @@ shaka.media.SegmentUtils = class {
199
201
  let sampleRate = null;
200
202
  /** @type {?string} */
201
203
  let realVideoRange = null;
204
+ /** @type {?string} */
205
+ let realColorGamut = null;
202
206
 
203
207
  /** @type {?string} */
204
208
  let baseBox;
@@ -371,8 +375,10 @@ shaka.media.SegmentUtils = class {
371
375
  })
372
376
 
373
377
  .box('colr', (box) => {
374
- const {videoRange} = shaka.util.Mp4BoxParsers.parseCOLR(box.reader);
378
+ const {videoRange, colorGamut} =
379
+ shaka.util.Mp4BoxParsers.parseCOLR(box.reader);
375
380
  realVideoRange = videoRange;
381
+ realColorGamut = colorGamut;
376
382
  })
377
383
 
378
384
  .parse(initData || data, /* partialOkay= */ true);
@@ -404,6 +410,7 @@ shaka.media.SegmentUtils = class {
404
410
  sampleRate: sampleRate,
405
411
  closedCaptions: closedCaptions,
406
412
  videoRange: realVideoRange,
413
+ colorGamut: realColorGamut,
407
414
  };
408
415
  }
409
416
 
@@ -465,7 +472,9 @@ shaka.media.SegmentUtils = class {
465
472
  return codecs;
466
473
  }
467
474
  const dolbyVision = codecs.find((codec) => {
468
- return codec.startsWith('dvh1.') || codec.startsWith('dvhe.');
475
+ return codec.startsWith('dvh1.') ||
476
+ codec.startsWith('dvhe.') ||
477
+ codec.startsWith('dav1.');
469
478
  });
470
479
  if (!dolbyVision) {
471
480
  return codecs;
@@ -518,7 +527,8 @@ shaka.media.SegmentUtils = class {
518
527
  * channelCount: ?number,
519
528
  * sampleRate: ?number,
520
529
  * closedCaptions: Map.<string, string>,
521
- * videoRange: ?string
530
+ * videoRange: ?string,
531
+ * colorGamut: ?string
522
532
  * }}
523
533
  *
524
534
  * @property {string} type
@@ -531,5 +541,6 @@ shaka.media.SegmentUtils = class {
531
541
  * @property {?number} sampleRate
532
542
  * @property {Map.<string, string>} closedCaptions
533
543
  * @property {?string} videoRange
544
+ * @property {?string} colorGamut
534
545
  */
535
546
  shaka.media.SegmentUtils.BasicInfo;
@@ -419,6 +419,7 @@ shaka.mss.MssParser = class {
419
419
  ignoreManifestTimestampsInSegmentsMode: false,
420
420
  type: shaka.media.ManifestParser.MSS,
421
421
  serviceDescription: null,
422
+ nextUrl: null,
422
423
  };
423
424
 
424
425
  // This is the first point where we have a meaningful presentation start
@@ -570,6 +571,7 @@ shaka.mss.MssParser = class {
570
571
  spatialAudio: false,
571
572
  closedCaptions: null,
572
573
  hdr: undefined,
574
+ colorGamut: undefined,
573
575
  videoLayout: undefined,
574
576
  tilesLayout: undefined,
575
577
  matchedStreams: [],
@@ -58,7 +58,8 @@ shaka.net.HttpPluginUtils = class {
58
58
  status,
59
59
  responseText,
60
60
  headers,
61
- requestType);
61
+ requestType,
62
+ responseURL || uri);
62
63
  }
63
64
  }
64
65
  };
@@ -96,10 +96,21 @@ shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
96
96
  /** @private {?shaka.net.NetworkingEngine.OnResponse} */
97
97
  this.onResponse_ = onResponse || null;
98
98
 
99
+ /** @private {boolean} */
100
+ this.forceHTTP_ = false;
101
+
99
102
  /** @private {boolean} */
100
103
  this.forceHTTPS_ = false;
101
104
  }
102
105
 
106
+ /**
107
+ * @param {boolean} forceHTTP
108
+ * @export
109
+ */
110
+ setForceHTTP(forceHTTP) {
111
+ this.forceHTTP_ = forceHTTP;
112
+ }
113
+
103
114
  /**
104
115
  * @param {boolean} forceHTTPS
105
116
  * @export
@@ -146,6 +157,19 @@ shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
146
157
  delete shaka.net.NetworkingEngine.schemes_[scheme];
147
158
  }
148
159
 
160
+ /**
161
+ * Copies all of the filters from this networking engine into another.
162
+ * @param {!shaka.net.NetworkingEngine} other
163
+ */
164
+ copyFiltersInto(other) {
165
+ for (const filter of this.requestFilters_) {
166
+ other.requestFilters_.add(filter);
167
+ }
168
+ for (const filter of this.responseFilters_) {
169
+ other.responseFilters_.add(filter);
170
+ }
171
+ }
172
+
149
173
  /**
150
174
  * Registers a new request filter. All filters are applied in the order they
151
175
  * are registered.
@@ -450,6 +474,9 @@ shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
450
474
  */
451
475
  send_(type, request, context, backoff, index, lastError,
452
476
  numBytesRemainingObj) {
477
+ if (this.forceHTTP_) {
478
+ request.uris[index] = request.uris[index].replace('https://', 'http://');
479
+ }
453
480
  if (this.forceHTTPS_) {
454
481
  request.uris[index] = request.uris[index].replace('http://', 'https://');
455
482
  }
@@ -158,6 +158,7 @@ shaka.offline.indexeddb.V1StorageCell = class
158
158
  frameRate: old.frameRate,
159
159
  pixelAspectRatio: undefined,
160
160
  hdr: undefined,
161
+ colorGamut: undefined,
161
162
  videoLayout: undefined,
162
163
  kind: old.kind,
163
164
  language: old.language,
@@ -107,6 +107,7 @@ shaka.offline.indexeddb.V2StorageCell = class
107
107
  frameRate: old.frameRate,
108
108
  pixelAspectRatio: old.pixelAspectRatio,
109
109
  hdr: undefined,
110
+ colorGamut: undefined,
110
111
  videoLayout: undefined,
111
112
  kind: old.kind,
112
113
  language: old.language,
@@ -93,6 +93,7 @@ shaka.offline.ManifestConverter = class {
93
93
  ignoreManifestTimestampsInSegmentsMode: false,
94
94
  type: manifestDB.type || shaka.media.ManifestParser.UNKNOWN,
95
95
  serviceDescription: null,
96
+ nextUrl: null,
96
97
  };
97
98
  }
98
99
 
@@ -191,6 +192,7 @@ shaka.offline.ManifestConverter = class {
191
192
  frameRate: streamDB.frameRate,
192
193
  pixelAspectRatio: streamDB.pixelAspectRatio,
193
194
  hdr: streamDB.hdr,
195
+ colorGamut: streamDB.colorGamut,
194
196
  videoLayout: streamDB.videoLayout,
195
197
  kind: streamDB.kind,
196
198
  encrypted: streamDB.encrypted,
@@ -1316,6 +1316,7 @@ shaka.offline.Storage = class {
1316
1316
  frameRate: stream.frameRate,
1317
1317
  pixelAspectRatio: stream.pixelAspectRatio,
1318
1318
  hdr: stream.hdr,
1319
+ colorGamut: stream.colorGamut,
1319
1320
  videoLayout: stream.videoLayout,
1320
1321
  kind: stream.kind,
1321
1322
  language: stream.language,