shaka-player 4.9.12 → 4.9.13

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 (35) hide show
  1. package/dist/deps.js +9 -9
  2. package/dist/locales.js +32 -32
  3. package/dist/shaka-player.compiled.d.ts +1 -1
  4. package/dist/shaka-player.compiled.debug.d.ts +1 -1
  5. package/dist/shaka-player.compiled.debug.externs.js +1 -1
  6. package/dist/shaka-player.compiled.debug.js +119 -119
  7. package/dist/shaka-player.compiled.debug.map +3 -3
  8. package/dist/shaka-player.compiled.externs.js +1 -1
  9. package/dist/shaka-player.compiled.js +1125 -1120
  10. package/dist/shaka-player.compiled.map +4 -4
  11. package/dist/shaka-player.ui.d.ts +1 -1
  12. package/dist/shaka-player.ui.debug.d.ts +1 -1
  13. package/dist/shaka-player.ui.debug.externs.js +1 -1
  14. package/dist/shaka-player.ui.debug.js +144 -143
  15. package/dist/shaka-player.ui.debug.map +4 -4
  16. package/dist/shaka-player.ui.externs.js +1 -1
  17. package/dist/shaka-player.ui.js +1328 -1324
  18. package/dist/shaka-player.ui.map +4 -4
  19. package/lib/hls/hls_parser.js +3 -3
  20. package/lib/media/drm_engine.js +9 -197
  21. package/lib/player.js +15 -10
  22. package/lib/polyfill/media_capabilities.js +4 -4
  23. package/lib/polyfill/patchedmediakeys_apple.js +3 -3
  24. package/lib/polyfill/patchedmediakeys_webkit.js +3 -3
  25. package/lib/text/simple_text_displayer.js +3 -0
  26. package/lib/text/ssa_text_parser.js +22 -7
  27. package/lib/text/text_utils.js +17 -1
  28. package/lib/text/ttml_text_parser.js +5 -3
  29. package/lib/text/ui_text_displayer.js +6 -1
  30. package/lib/util/config_utils.js +5 -0
  31. package/lib/util/drm_utils.js +191 -0
  32. package/lib/util/periods.js +4 -4
  33. package/lib/util/ts_parser.js +4 -4
  34. package/package.json +1 -1
  35. package/ui/statistics_button.js +10 -3
@@ -16,7 +16,6 @@ goog.require('shaka.hls.PlaylistType');
16
16
  goog.require('shaka.hls.Tag');
17
17
  goog.require('shaka.hls.Utils');
18
18
  goog.require('shaka.log');
19
- goog.require('shaka.media.DrmEngine');
20
19
  goog.require('shaka.media.InitSegmentReference');
21
20
  goog.require('shaka.media.ManifestParser');
22
21
  goog.require('shaka.media.PresentationTimeline');
@@ -26,6 +25,7 @@ goog.require('shaka.net.DataUriPlugin');
26
25
  goog.require('shaka.net.NetworkingEngine');
27
26
  goog.require('shaka.util.ArrayUtils');
28
27
  goog.require('shaka.util.BufferUtils');
28
+ goog.require('shaka.util.DrmUtils');
29
29
  goog.require('shaka.util.ContentSteeringManager');
30
30
  goog.require('shaka.util.Error');
31
31
  goog.require('shaka.util.FakeEvent');
@@ -1844,7 +1844,7 @@ shaka.hls.HlsParser = class {
1844
1844
  audioInfos, videoInfos, bandwidth, width, height, frameRate, videoRange,
1845
1845
  videoLayout, drmInfos, keyIds) {
1846
1846
  const ContentType = shaka.util.ManifestParserUtils.ContentType;
1847
- const DrmEngine = shaka.media.DrmEngine;
1847
+ const DrmUtils = shaka.util.DrmUtils;
1848
1848
 
1849
1849
  for (const info of videoInfos) {
1850
1850
  this.addVideoAttributes_(
@@ -1887,7 +1887,7 @@ shaka.hls.HlsParser = class {
1887
1887
  const variantUriKey = videoStreamUri + ' - ' + audioStreamUri;
1888
1888
 
1889
1889
  if (audioStream && videoStream) {
1890
- if (!DrmEngine.areDrmCompatible(audioDrmInfos, videoDrmInfos)) {
1890
+ if (!DrmUtils.areDrmCompatible(audioDrmInfos, videoDrmInfos)) {
1891
1891
  shaka.log.warning(
1892
1892
  'Incompatible DRM info in HLS variant. Skipping.');
1893
1893
  continue;
@@ -18,7 +18,6 @@ goog.require('shaka.util.EventManager');
18
18
  goog.require('shaka.util.FakeEvent');
19
19
  goog.require('shaka.util.IDestroyable');
20
20
  goog.require('shaka.util.Iterables');
21
- goog.require('shaka.util.Lazy');
22
21
  goog.require('shaka.util.ManifestParserUtils');
23
22
  goog.require('shaka.util.MapUtils');
24
23
  goog.require('shaka.util.MimeUtils');
@@ -736,12 +735,13 @@ shaka.media.DrmEngine = class {
736
735
  // Note that some init data are extremely large and can't portably be used
737
736
  // as keys in a dictionary.
738
737
 
739
- const metadatas = this.activeSessions_.values();
740
- for (const metadata of metadatas) {
741
- if (shaka.util.BufferUtils.equal(initData, metadata.initData) &&
742
- this.config_.ignoreDuplicateInitData) {
743
- shaka.log.debug('Ignoring duplicate init data.');
744
- return;
738
+ if (this.config_.ignoreDuplicateInitData) {
739
+ const metadatas = this.activeSessions_.values();
740
+ for (const metadata of metadatas) {
741
+ if (shaka.util.BufferUtils.equal(initData, metadata.initData)) {
742
+ shaka.log.debug('Ignoring duplicate init data.');
743
+ return;
744
+ }
745
745
  }
746
746
  }
747
747
 
@@ -762,13 +762,6 @@ shaka.media.DrmEngine = class {
762
762
  return this.initialized_;
763
763
  }
764
764
 
765
- /**
766
- * @param {?shaka.extern.DrmInfo} drmInfo
767
- * @return {string} */
768
- static keySystem(drmInfo) {
769
- return drmInfo ? drmInfo.keySystem : '';
770
- }
771
-
772
765
  /**
773
766
  * Check if DrmEngine (as initialized) will likely be able to support the
774
767
  * given content type.
@@ -1763,30 +1756,13 @@ shaka.media.DrmEngine = class {
1763
1756
  this.playerInterface_.onKeyStatus(shaka.util.MapUtils.asObject(publicMap));
1764
1757
  }
1765
1758
 
1766
- /**
1767
- * Returns true if the browser has recent EME APIs.
1768
- *
1769
- * @return {boolean}
1770
- */
1771
- static isBrowserSupported() {
1772
- const basic =
1773
- !!window.MediaKeys &&
1774
- !!window.navigator &&
1775
- !!window.navigator.requestMediaKeySystemAccess &&
1776
- !!window.MediaKeySystemAccess &&
1777
- // eslint-disable-next-line no-restricted-syntax
1778
- !!window.MediaKeySystemAccess.prototype.getConfiguration;
1779
-
1780
- return basic;
1781
- }
1782
-
1783
1759
  /**
1784
1760
  * Returns a Promise to a map of EME support for well-known key systems.
1785
1761
  *
1786
1762
  * @return {!Promise.<!Object.<string, ?shaka.extern.DrmSupportType>>}
1787
1763
  */
1788
1764
  static async probeSupport() {
1789
- goog.asserts.assert(shaka.media.DrmEngine.isBrowserSupported(),
1765
+ goog.asserts.assert(shaka.util.DrmUtils.isBrowserSupported(),
1790
1766
  'Must have basic EME support');
1791
1767
 
1792
1768
  const testKeySystems = [
@@ -2115,91 +2091,13 @@ shaka.media.DrmEngine = class {
2115
2091
  }
2116
2092
  }
2117
2093
 
2118
- const keySystem = shaka.media.DrmEngine.keySystem(this.currentDrmInfo_);
2094
+ const keySystem = shaka.util.DrmUtils.keySystem(this.currentDrmInfo_);
2119
2095
  const drmInfos = this.getVariantDrmInfos_(variant);
2120
2096
 
2121
2097
  return drmInfos.length == 0 ||
2122
2098
  drmInfos.some((drmInfo) => drmInfo.keySystem == keySystem);
2123
2099
  }
2124
2100
 
2125
- /**
2126
- * Checks if two DrmInfos can be decrypted using the same key system.
2127
- * Clear content is considered compatible with every key system.
2128
- *
2129
- * @param {!Array.<!shaka.extern.DrmInfo>} drms1
2130
- * @param {!Array.<!shaka.extern.DrmInfo>} drms2
2131
- * @return {boolean}
2132
- */
2133
- static areDrmCompatible(drms1, drms2) {
2134
- if (!drms1.length || !drms2.length) {
2135
- return true;
2136
- }
2137
-
2138
- if (drms1 === drms2) {
2139
- return true;
2140
- }
2141
-
2142
- return shaka.media.DrmEngine.getCommonDrmInfos(
2143
- drms1, drms2).length > 0;
2144
- }
2145
-
2146
- /**
2147
- * Returns an array of drm infos that are present in both input arrays.
2148
- * If one of the arrays is empty, returns the other one since clear
2149
- * content is considered compatible with every drm info.
2150
- *
2151
- * @param {!Array.<!shaka.extern.DrmInfo>} drms1
2152
- * @param {!Array.<!shaka.extern.DrmInfo>} drms2
2153
- * @return {!Array.<!shaka.extern.DrmInfo>}
2154
- */
2155
- static getCommonDrmInfos(drms1, drms2) {
2156
- if (!drms1.length) {
2157
- return drms2;
2158
- }
2159
- if (!drms2.length) {
2160
- return drms1;
2161
- }
2162
-
2163
- const commonDrms = [];
2164
-
2165
- for (const drm1 of drms1) {
2166
- for (const drm2 of drms2) {
2167
- if (drm1.keySystem == drm2.keySystem) {
2168
- const initDataMap = new Map();
2169
- const bothInitDatas = (drm1.initData || [])
2170
- .concat(drm2.initData || []);
2171
- for (const d of bothInitDatas) {
2172
- initDataMap.set(d.keyId, d);
2173
- }
2174
- const initData = Array.from(initDataMap.values());
2175
-
2176
- const keyIds = drm1.keyIds && drm2.keyIds ?
2177
- new Set([...drm1.keyIds, ...drm2.keyIds]) :
2178
- drm1.keyIds || drm2.keyIds;
2179
- const mergedDrm = {
2180
- keySystem: drm1.keySystem,
2181
- licenseServerUri: drm1.licenseServerUri || drm2.licenseServerUri,
2182
- distinctiveIdentifierRequired: drm1.distinctiveIdentifierRequired ||
2183
- drm2.distinctiveIdentifierRequired,
2184
- persistentStateRequired: drm1.persistentStateRequired ||
2185
- drm2.persistentStateRequired,
2186
- videoRobustness: drm1.videoRobustness || drm2.videoRobustness,
2187
- audioRobustness: drm1.audioRobustness || drm2.audioRobustness,
2188
- serverCertificate: drm1.serverCertificate || drm2.serverCertificate,
2189
- serverCertificateUri: drm1.serverCertificateUri ||
2190
- drm2.serverCertificateUri,
2191
- initData,
2192
- keyIds,
2193
- };
2194
- commonDrms.push(mergedDrm);
2195
- break;
2196
- }
2197
- }
2198
- }
2199
-
2200
- return commonDrms;
2201
- }
2202
-
2203
2101
  /**
2204
2102
  * Concat the audio and video drmInfos in a variant.
2205
2103
  * @param {shaka.extern.Variant} variant
@@ -2703,73 +2601,6 @@ shaka.media.DrmEngine = class {
2703
2601
  this.newInitData('cenc', combinedData);
2704
2602
  return this.allSessionsLoaded_;
2705
2603
  }
2706
-
2707
- /**
2708
- * A method for generating a key for the MediaKeySystemAccessRequests cache.
2709
- *
2710
- * @param {string} videoCodec
2711
- * @param {string} audioCodec
2712
- * @param {string} keySystem
2713
- * @return {string}
2714
- * @private
2715
- */
2716
- static generateKeySystemCacheKey_(videoCodec, audioCodec, keySystem) {
2717
- return `${videoCodec}#${audioCodec}#${keySystem}`;
2718
- }
2719
-
2720
- /**
2721
- * Check does MediaKeySystemAccess cache contains something for following
2722
- * attributes.
2723
- *
2724
- * @param {string} videoCodec
2725
- * @param {string} audioCodec
2726
- * @param {string} keySystem
2727
- * @return {boolean}
2728
- */
2729
- static hasMediaKeySystemAccess(videoCodec, audioCodec, keySystem) {
2730
- const DrmEngine = shaka.media.DrmEngine;
2731
- const key = DrmEngine.generateKeySystemCacheKey_(
2732
- videoCodec, audioCodec, keySystem);
2733
- return DrmEngine.memoizedMediaKeySystemAccessRequests_.has(key);
2734
- }
2735
-
2736
- /**
2737
- * Get MediaKeySystemAccess object for following attributes.
2738
- *
2739
- * @param {string} videoCodec
2740
- * @param {string} audioCodec
2741
- * @param {string} keySystem
2742
- * @return {?MediaKeySystemAccess}
2743
- */
2744
- static getMediaKeySystemAccess(videoCodec, audioCodec, keySystem) {
2745
- const DrmEngine = shaka.media.DrmEngine;
2746
- const key = DrmEngine.generateKeySystemCacheKey_(
2747
- videoCodec, audioCodec, keySystem);
2748
- return DrmEngine.memoizedMediaKeySystemAccessRequests_.get(key) || null;
2749
- }
2750
-
2751
- /**
2752
- * Store MediaKeySystemAccess object associated with specified attributes.
2753
- *
2754
- * @param {string} videoCodec
2755
- * @param {string} audioCodec
2756
- * @param {string} keySystem
2757
- * @param {!MediaKeySystemAccess} mksa
2758
- */
2759
- static setMediaKeySystemAccess(videoCodec, audioCodec, keySystem, mksa) {
2760
- const DrmEngine = shaka.media.DrmEngine;
2761
- const key = DrmEngine.generateKeySystemCacheKey_(
2762
- videoCodec, audioCodec, keySystem);
2763
- return DrmEngine.memoizedMediaKeySystemAccessRequests_.set(key, mksa);
2764
- }
2765
-
2766
- /**
2767
- * Clears underlying cache.
2768
- */
2769
- static clearMediaKeySystemAccessMap() {
2770
- const DrmEngine = shaka.media.DrmEngine;
2771
- DrmEngine.memoizedMediaKeySystemAccessRequests_.clear();
2772
- }
2773
2604
  };
2774
2605
 
2775
2606
 
@@ -2854,22 +2685,3 @@ shaka.media.DrmEngine.SESSION_LOAD_TIMEOUT_ = 5;
2854
2685
  * @type {number}
2855
2686
  */
2856
2687
  shaka.media.DrmEngine.KEY_STATUS_BATCH_TIME = 0.5;
2857
-
2858
-
2859
- /**
2860
- * Contains the suggested "default" key ID used by EME polyfills that do not
2861
- * have a per-key key status. See w3c/encrypted-media#32.
2862
- * @type {!shaka.util.Lazy.<!ArrayBuffer>}
2863
- */
2864
- shaka.media.DrmEngine.DUMMY_KEY_ID = new shaka.util.Lazy(
2865
- () => shaka.util.BufferUtils.toArrayBuffer(new Uint8Array([0])));
2866
-
2867
-
2868
- /**
2869
- * A cache that stores the MediaKeySystemAccess result of calling
2870
- * `navigator.requestMediaKeySystemAccess` by a key combination of
2871
- * video/audio codec and key system string.
2872
- *
2873
- * @private {!Map<string, !MediaKeySystemAccess>}
2874
- */
2875
- shaka.media.DrmEngine.memoizedMediaKeySystemAccessRequests_ = new Map();
package/lib/player.js CHANGED
@@ -45,6 +45,7 @@ goog.require('shaka.util.CmcdManager');
45
45
  goog.require('shaka.util.CmsdManager');
46
46
  goog.require('shaka.util.ConfigUtils');
47
47
  goog.require('shaka.util.Dom');
48
+ goog.require('shaka.util.DrmUtils');
48
49
  goog.require('shaka.util.Error');
49
50
  goog.require('shaka.util.EventManager');
50
51
  goog.require('shaka.util.FakeEvent');
@@ -108,8 +109,8 @@ goog.requireType('shaka.media.PresentationTimeline');
108
109
  * 'downloadfailed'
109
110
  * @property {!shaka.extern.Request} request
110
111
  * @property {?shaka.util.Error} error
111
- * @param {number} httpResponseCode
112
- * @param {boolean} aborted
112
+ * @property {number} httpResponseCode
113
+ * @property {boolean} aborted
113
114
  * @exportDoc
114
115
  */
115
116
 
@@ -1057,7 +1058,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1057
1058
  // DRM support is not strictly necessary, but the APIs at least need to be
1058
1059
  // there. Our no-op DRM polyfill should handle that.
1059
1060
  // TODO(#1017): Consider making even DrmEngine optional.
1060
- const drmSupport = shaka.media.DrmEngine.isBrowserSupported();
1061
+ const drmSupport = shaka.util.DrmUtils.isBrowserSupported();
1061
1062
  if (!drmSupport) {
1062
1063
  return false;
1063
1064
  }
@@ -1454,7 +1455,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1454
1455
  // each playback.
1455
1456
  if (this.config_.streaming.clearDecodingCache) {
1456
1457
  shaka.util.StreamUtils.clearDecodingConfigCache();
1457
- shaka.media.DrmEngine.clearMediaKeySystemAccessMap();
1458
+ shaka.util.DrmUtils.clearMediaKeySystemAccessMap();
1458
1459
  }
1459
1460
 
1460
1461
  this.manifest_ = null;
@@ -4072,7 +4073,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
4072
4073
  * @export
4073
4074
  */
4074
4075
  keySystem() {
4075
- return shaka.media.DrmEngine.keySystem(this.drmInfo());
4076
+ return shaka.util.DrmUtils.keySystem(this.drmInfo());
4076
4077
  }
4077
4078
 
4078
4079
  /**
@@ -4734,9 +4735,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
4734
4735
  Math.abs((a.video.width || 0) - (b.video.width || 0));
4735
4736
  }
4736
4737
  };
4737
- // Find the variant whose size is closest to the active variant. This
4738
- // ensures we stay at about the same resolution when just changing the
4739
- // language/role.
4738
+ // Find the variant whose size is closest to the active variant. This
4739
+ // ensures we stay at about the same resolution when just changing the
4740
+ // language/role.
4740
4741
  const active = this.streamingEngine_.getCurrentVariant();
4741
4742
  const set =
4742
4743
  this.currentAdaptationSetCriteria_.create(this.manifest_.variants);
@@ -4747,6 +4748,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
4747
4748
  bestVariant = curVariant;
4748
4749
  }
4749
4750
  }
4751
+ if (bestVariant == active) {
4752
+ shaka.log.debug('Audio already selected.');
4753
+ return;
4754
+ }
4750
4755
  if (bestVariant) {
4751
4756
  const track = shaka.util.StreamUtils.variantToTrack(bestVariant);
4752
4757
  this.selectVariantTrack(track, /* clearBuffer= */ true, safeMargin);
@@ -5211,7 +5216,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5211
5216
  const element = /** @type {!HTMLVideoElement} */ (this.video_);
5212
5217
 
5213
5218
  const completionRatio = element.currentTime / element.duration;
5214
- if (!isNaN(completionRatio)) {
5219
+ if (!isNaN(completionRatio) && !this.isLive()) {
5215
5220
  this.stats_.setCompletionPercent(Math.round(100 * completionRatio));
5216
5221
  }
5217
5222
 
@@ -7468,7 +7473,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
7468
7473
  * @export
7469
7474
  */
7470
7475
  // eslint-disable-next-line no-useless-concat
7471
- shaka.Player.version = 'v4.9.12' + '-uncompiled'; // x-release-please-version
7476
+ shaka.Player.version = 'v4.9.13' + '-uncompiled'; // x-release-please-version
7472
7477
 
7473
7478
  // Initialize the deprecation system using the version string we just set
7474
7479
  // on the player.
@@ -8,8 +8,8 @@ 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');
12
11
  goog.require('shaka.polyfill');
12
+ goog.require('shaka.util.DrmUtils');
13
13
  goog.require('shaka.util.Platform');
14
14
 
15
15
 
@@ -284,14 +284,14 @@ shaka.polyfill.MediaCapabilities = class {
284
284
  /** @type {MediaKeySystemAccess} */
285
285
  let keySystemAccess = null;
286
286
  try {
287
- if (shaka.media.DrmEngine.hasMediaKeySystemAccess(
287
+ if (shaka.util.DrmUtils.hasMediaKeySystemAccess(
288
288
  videoCodec, audioCodec, keySystem)) {
289
- keySystemAccess = shaka.media.DrmEngine.getMediaKeySystemAccess(
289
+ keySystemAccess = shaka.util.DrmUtils.getMediaKeySystemAccess(
290
290
  videoCodec, audioCodec, keySystem);
291
291
  } else {
292
292
  keySystemAccess = await navigator.requestMediaKeySystemAccess(
293
293
  mcapKeySystemConfig.keySystem, [mediaKeySystemConfig]);
294
- shaka.media.DrmEngine.setMediaKeySystemAccess(
294
+ shaka.util.DrmUtils.setMediaKeySystemAccess(
295
295
  videoCodec, audioCodec, keySystem, keySystemAccess);
296
296
  }
297
297
  } catch (e) {
@@ -8,9 +8,9 @@ goog.provide('shaka.polyfill.PatchedMediaKeysApple');
8
8
 
9
9
  goog.require('goog.asserts');
10
10
  goog.require('shaka.log');
11
- goog.require('shaka.media.DrmEngine');
12
11
  goog.require('shaka.polyfill');
13
12
  goog.require('shaka.util.BufferUtils');
13
+ goog.require('shaka.util.DrmUtils');
14
14
  goog.require('shaka.util.EventManager');
15
15
  goog.require('shaka.util.FakeEvent');
16
16
  goog.require('shaka.util.FakeEventTarget');
@@ -746,7 +746,7 @@ shaka.polyfill.PatchedMediaKeysApple.MediaKeyStatusMap = class {
746
746
  /** @override */
747
747
  forEach(fn) {
748
748
  if (this.status_) {
749
- fn(this.status_, shaka.media.DrmEngine.DUMMY_KEY_ID.value());
749
+ fn(this.status_, shaka.util.DrmUtils.DUMMY_KEY_ID.value());
750
750
  }
751
751
  }
752
752
 
@@ -760,7 +760,7 @@ shaka.polyfill.PatchedMediaKeysApple.MediaKeyStatusMap = class {
760
760
 
761
761
  /** @override */
762
762
  has(keyId) {
763
- const fakeKeyId = shaka.media.DrmEngine.DUMMY_KEY_ID.value();
763
+ const fakeKeyId = shaka.util.DrmUtils.DUMMY_KEY_ID.value();
764
764
  if (this.status_ && shaka.util.BufferUtils.equal(keyId, fakeKeyId)) {
765
765
  return true;
766
766
  }
@@ -8,9 +8,9 @@ goog.provide('shaka.polyfill.PatchedMediaKeysWebkit');
8
8
 
9
9
  goog.require('goog.asserts');
10
10
  goog.require('shaka.log');
11
- goog.require('shaka.media.DrmEngine');
12
11
  goog.require('shaka.polyfill');
13
12
  goog.require('shaka.util.BufferUtils');
13
+ goog.require('shaka.util.DrmUtils');
14
14
  goog.require('shaka.util.EventManager');
15
15
  goog.require('shaka.util.FakeEvent');
16
16
  goog.require('shaka.util.FakeEventTarget');
@@ -924,7 +924,7 @@ shaka.polyfill.PatchedMediaKeysWebkit.MediaKeyStatusMap = class {
924
924
  /** @override */
925
925
  forEach(fn) {
926
926
  if (this.status_) {
927
- fn(this.status_, shaka.media.DrmEngine.DUMMY_KEY_ID.value());
927
+ fn(this.status_, shaka.util.DrmUtils.DUMMY_KEY_ID.value());
928
928
  }
929
929
  }
930
930
 
@@ -938,7 +938,7 @@ shaka.polyfill.PatchedMediaKeysWebkit.MediaKeyStatusMap = class {
938
938
 
939
939
  /** @override */
940
940
  has(keyId) {
941
- const fakeKeyId = shaka.media.DrmEngine.DUMMY_KEY_ID.value();
941
+ const fakeKeyId = shaka.util.DrmUtils.DUMMY_KEY_ID.value();
942
942
  if (this.status_ && shaka.util.BufferUtils.equal(keyId, fakeKeyId)) {
943
943
  return true;
944
944
  }
@@ -37,6 +37,9 @@ shaka.text.SimpleTextDisplayer = class {
37
37
  // If the video element has TextTracks, disable them. If we see one that
38
38
  // was created by a previous instance of Shaka Player, reuse it.
39
39
  for (const track of Array.from(video.textTracks)) {
40
+ if (track.kind === 'metadata' || track.kind === 'chapters') {
41
+ continue;
42
+ }
40
43
  // NOTE: There is no API available to remove a TextTrack from a video
41
44
  // element.
42
45
  track.mode = 'disabled';
@@ -60,24 +60,39 @@ shaka.text.SsaTextParser = class {
60
60
  events: '',
61
61
  };
62
62
 
63
+ let tag = null;
64
+ let lines = null;
63
65
  const parts = str.split(/\r?\n\s*\r?\n/);
64
66
  for (const part of parts) {
67
+ lines = part;
65
68
  // SSA content
66
69
  const match = SsaTextParser.ssaContent_.exec(part);
67
70
  if (match) {
68
- const tag = match[1];
69
- const lines = match[2];
70
- if (tag == 'V4 Styles' || tag == 'V4+ Styles') {
71
+ tag = match[1];
72
+ lines = match[2];
73
+ }
74
+ if (tag == 'V4 Styles' || tag == 'V4+ Styles') {
75
+ section.styles = lines;
76
+ if (section.events) {
77
+ section.styles += '\n' + lines;
78
+ } else {
71
79
  section.styles = lines;
72
- continue;
73
80
  }
74
- if (tag == 'Events') {
81
+ continue;
82
+ }
83
+ if (tag == 'Events') {
84
+ if (section.events) {
85
+ section.events += '\n' + lines;
86
+ } else {
75
87
  section.events = lines;
76
- continue;
77
88
  }
89
+ continue;
90
+ }
91
+ if (tag == 'Script Info') {
92
+ continue;
78
93
  }
79
94
  shaka.log.warning('SsaTextParser parser encountered an unknown part.',
80
- part);
95
+ lines);
81
96
  }
82
97
 
83
98
  // Process styles
@@ -148,7 +148,7 @@ shaka.text.Utils = class {
148
148
  */
149
149
  static getCuesToFlatten(cues, parentCue) {
150
150
  const result = [];
151
- for (const cue of cues) {
151
+ for (const cue of shaka.text.Utils.removeDuplicates(cues)) {
152
152
  if (cue.isContainer) {
153
153
  // Recurse to find the actual text payload cues.
154
154
  result.push(...shaka.text.Utils.getCuesToFlatten(cue.nestedCues, cue));
@@ -162,4 +162,20 @@ shaka.text.Utils = class {
162
162
  }
163
163
  return result;
164
164
  }
165
+
166
+ /**
167
+ * @param {!Array.<!shaka.text.Cue>} cues
168
+ * @return {!Array.<!shaka.text.Cue>}
169
+ */
170
+ static removeDuplicates(cues) {
171
+ const uniqueCues = [];
172
+ for (const cue of cues) {
173
+ const isValid = !uniqueCues.some(
174
+ (existingCue) => shaka.text.Cue.equal(cue, existingCue));
175
+ if (isValid) {
176
+ uniqueCues.push(cue);
177
+ }
178
+ }
179
+ return uniqueCues;
180
+ }
165
181
  };
@@ -630,7 +630,9 @@ shaka.text.TtmlTextParser = class {
630
630
  cue.fontFamily = 'serif';
631
631
  break;
632
632
  default:
633
- cue.fontFamily = fontFamily;
633
+ cue.fontFamily = fontFamily.split(',').filter((font) => {
634
+ return font != 'default';
635
+ }).join(',');
634
636
  break;
635
637
  }
636
638
  }
@@ -1300,9 +1302,9 @@ shaka.text.TtmlTextParser.percentValues_ =
1300
1302
  /**
1301
1303
  * @const
1302
1304
  * @private {!RegExp}
1303
- * @example 0.6% 90%
1305
+ * @example 0.6% 90% 300% 1000%
1304
1306
  */
1305
- shaka.text.TtmlTextParser.percentValue_ = /^(\d{1,2}(?:\.\d+)?|100)%$/;
1307
+ shaka.text.TtmlTextParser.percentValue_ = /^(\d{1,4}(?:\.\d+)?|100)%$/;
1306
1308
 
1307
1309
  /**
1308
1310
  * @const
@@ -11,6 +11,7 @@ goog.require('goog.asserts');
11
11
  goog.require('shaka.Deprecate');
12
12
  goog.require('shaka.text.Cue');
13
13
  goog.require('shaka.text.CueRegion');
14
+ goog.require('shaka.text.Utils');
14
15
  goog.require('shaka.util.Dom');
15
16
  goog.require('shaka.util.EventManager');
16
17
  goog.require('shaka.util.Timer');
@@ -156,7 +157,7 @@ shaka.text.UITextDisplayer = class {
156
157
  // list growing during the comparisons for duplicate cues.
157
158
  // See: https://github.com/shaka-project/shaka-player/issues/3018
158
159
  const cuesList = [...this.cues_];
159
- for (const cue of cues) {
160
+ for (const cue of shaka.text.Utils.removeDuplicates(cues)) {
160
161
  // When a VTT cue spans a segment boundary, the cue will be duplicated
161
162
  // into two segments.
162
163
  // To avoid displaying duplicate cues, if the current cue list already
@@ -654,6 +655,10 @@ shaka.text.UITextDisplayer = class {
654
655
  style.backgroundSize = 'contain';
655
656
  style.backgroundPosition = 'center';
656
657
 
658
+ if (cue.backgroundColor) {
659
+ style.backgroundColor = cue.backgroundColor;
660
+ }
661
+
657
662
  // Quoting https://www.w3.org/TR/ttml-imsc1.2/:
658
663
  // "The width and height (in pixels) of the image resource referenced by
659
664
  // smpte:backgroundImage SHALL be equal to the width and height expressed
@@ -196,6 +196,8 @@ shaka.util.ConfigUtils = class {
196
196
  }
197
197
  } else if (isArrayEmpty(value) && isArrayEmpty(base[key])) {
198
198
  // Do nothing if both are empty arrays
199
+ } else if (Number.isNaN(value) && Number.isNaN(base[key])) {
200
+ // Do nothing if both are NaN
199
201
  } else if (value !== base[key]) {
200
202
  acc[key] = value;
201
203
  }
@@ -215,6 +217,9 @@ shaka.util.ConfigUtils = class {
215
217
  delete obj[key];
216
218
  } else if (isObject(obj[key])) {
217
219
  removeEmpty(obj[key]);
220
+ if (Object.keys(obj[key]).length === 0) {
221
+ delete obj[key];
222
+ }
218
223
  }
219
224
  }
220
225
  };