shaka-player 4.8.2 → 4.8.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.
@@ -1519,11 +1519,14 @@ shaka.extern.AdsConfiguration;
1519
1519
  * @property {boolean} enabled
1520
1520
  * If true, enable adaptation by the current AbrManager. Defaults to true.
1521
1521
  * @property {boolean} useNetworkInformation
1522
- * If true, use Network Information API in the current AbrManager.
1522
+ * If true, use the Network Information API in the current AbrManager, if it
1523
+ * is available in the browser environment. If the Network Information API is
1524
+ * used, Shaka Player will ignore the defaultBandwidthEstimate config.
1523
1525
  * Defaults to true.
1524
1526
  * @property {number} defaultBandwidthEstimate
1525
1527
  * The default bandwidth estimate to use if there is not enough data, in
1526
- * bit/sec.
1528
+ * bit/sec. Only used if useNetworkInformation is false, or if the Network
1529
+ * Information API is not available.
1527
1530
  * @property {shaka.extern.Restrictions} restrictions
1528
1531
  * The restrictions to apply to ABR decisions. These are "soft" restrictions.
1529
1532
  * Any track that fails to meet these restrictions will not be selected
@@ -97,6 +97,7 @@ shaka.cea.CeaDecoder = class {
97
97
  * @override
98
98
  */
99
99
  clear() {
100
+ shaka.log.debug('Clearing CEA decoder');
100
101
  this.badFrames_ = 0;
101
102
  this.cea608DataArray_ = [];
102
103
  this.cea708DataArray_ = [];
@@ -113,6 +114,7 @@ shaka.cea.CeaDecoder = class {
113
114
  * Resets the decoder.
114
115
  */
115
116
  reset() {
117
+ shaka.log.debug('Resetting CEA decoder');
116
118
  this.currentField1Channel_ = 0;
117
119
  this.currentField2Channel_ = 0;
118
120
  for (const stream of this.cea608ModeToStream_.values()) {
@@ -128,6 +130,7 @@ shaka.cea.CeaDecoder = class {
128
130
  */
129
131
  extract(userDataSeiMessage, pts) {
130
132
  if (this.waitingForFirstPacket_) {
133
+ shaka.log.debug('Setting first pts value to', pts);
131
134
  for (const stream of this.cea608ModeToStream_.values()) {
132
135
  stream.firstPts(pts);
133
136
  }
@@ -9,6 +9,7 @@ goog.provide('shaka.media.IClosedCaptionParser');
9
9
 
10
10
  goog.require('shaka.cea.DummyCaptionDecoder');
11
11
  goog.require('shaka.cea.DummyCeaParser');
12
+ goog.require('shaka.log');
12
13
  goog.require('shaka.util.BufferUtils');
13
14
 
14
15
 
@@ -22,7 +23,8 @@ goog.require('shaka.util.BufferUtils');
22
23
  */
23
24
  shaka.media.IClosedCaptionParser = class {
24
25
  /**
25
- * Initialize the caption parser. This should be called only once.
26
+ * Initialize the caption parser. This should be called whenever new init
27
+ * segment arrives.
26
28
  * @param {BufferSource} initSegment
27
29
  */
28
30
  init(initSegment) {}
@@ -88,6 +90,10 @@ shaka.media.ClosedCaptionParser = class {
88
90
  * @override
89
91
  */
90
92
  init(initSegment) {
93
+ shaka.log.debug('Passing new init segment to CEA parser');
94
+ // Reset underlying decoder when new init segment arrives
95
+ // to clear stored pts values.
96
+ this.reset();
91
97
  this.ceaParser_.init(initSegment);
92
98
  }
93
99
 
@@ -347,14 +347,11 @@ shaka.media.MediaSourceEngine = class {
347
347
 
348
348
  const support = {};
349
349
  for (const type of testMimeTypes) {
350
- if (shaka.util.Platform.supportsMediaSource()) {
351
- // Our TextEngine is only effective for MSE platforms at the moment.
352
- if (shaka.text.TextEngine.isTypeSupported(type)) {
353
- support[type] = true;
354
- } else {
355
- support[type] = shaka.media.Capabilities.isTypeSupported(type) ||
356
- shaka.transmuxer.TransmuxerEngine.isSupported(type);
357
- }
350
+ if (shaka.text.TextEngine.isTypeSupported(type)) {
351
+ support[type] = true;
352
+ } else if (shaka.util.Platform.supportsMediaSource()) {
353
+ support[type] = shaka.media.Capabilities.isTypeSupported(type) ||
354
+ shaka.transmuxer.TransmuxerEngine.isSupported(type);
358
355
  } else {
359
356
  support[type] = shaka.util.Platform.supportsMediaType(type);
360
357
  }
@@ -6,6 +6,7 @@
6
6
 
7
7
  goog.provide('shaka.media.PlayRateController');
8
8
 
9
+ goog.require('goog.asserts');
9
10
  goog.require('shaka.log');
10
11
  goog.require('shaka.util.IReleasable');
11
12
  goog.require('shaka.util.Timer');
@@ -27,6 +28,9 @@ shaka.media.PlayRateController = class {
27
28
  /** @private {?shaka.media.PlayRateController.Harness} */
28
29
  this.harness_ = harness;
29
30
 
31
+ /** @private {boolean} */
32
+ this.isBuffering_ = false;
33
+
30
34
  /** @private {number} */
31
35
  this.rate_ = this.harness_.getRate();
32
36
 
@@ -51,15 +55,25 @@ shaka.media.PlayRateController = class {
51
55
  }
52
56
 
53
57
  /**
54
- * Set the playback rate.
58
+ * Sets the buffering flag, which controls the effective playback rate.
59
+ *
60
+ * @param {boolean} isBuffering If true, forces playback rate to 0 internally.
61
+ */
62
+ setBuffering(isBuffering) {
63
+ this.isBuffering_ = isBuffering;
64
+ this.apply_();
65
+ }
66
+
67
+ /**
68
+ * Set the playback rate. This rate will only be used as provided when the
69
+ * player is not buffering. You should never set the rate to 0.
55
70
  *
56
71
  * @param {number} rate
57
72
  */
58
73
  set(rate) {
59
- if (this.rate_ != rate) {
60
- this.rate_ = rate;
61
- this.apply_();
62
- }
74
+ goog.asserts.assert(rate != 0, 'Should never set rate of 0 explicitly!');
75
+ this.rate_ = rate;
76
+ this.apply_();
63
77
  }
64
78
 
65
79
  /**
@@ -93,11 +107,14 @@ shaka.media.PlayRateController = class {
93
107
  // Always stop the timer. We may not start it again.
94
108
  this.timer_.stop();
95
109
 
96
- shaka.log.v1('Changing effective playback rate to', this.rate_);
110
+ /** @type {number} */
111
+ const rate = this.calculateCurrentRate_();
112
+
113
+ shaka.log.v1('Changing effective playback rate to', rate);
97
114
 
98
- if (this.rate_ >= 0) {
115
+ if (rate >= 0) {
99
116
  try {
100
- this.applyRate_(this.rate_);
117
+ this.applyRate_(rate);
101
118
  return;
102
119
  } catch (e) {
103
120
  // Fall through to the next clause.
@@ -117,6 +134,17 @@ shaka.media.PlayRateController = class {
117
134
  this.applyRate_(0);
118
135
  }
119
136
 
137
+ /**
138
+ * Calculate the rate that the controller wants the media element to have
139
+ * based on the current state of the controller.
140
+ *
141
+ * @return {number}
142
+ * @private
143
+ */
144
+ calculateCurrentRate_() {
145
+ return this.isBuffering_ ? 0 : this.rate_;
146
+ }
147
+
120
148
  /**
121
149
  * If the new rate is different than the media element's playback rate, this
122
150
  * will change the playback rate. If the rate does not need to change, it will
@@ -470,7 +470,7 @@ shaka.media.SegmentUtils = class {
470
470
  if (!dolbyVision) {
471
471
  return codecs;
472
472
  }
473
- const type = `video/mp4; codecs="${dolbyVision}"'`;
473
+ const type = `video/mp4; codecs="${dolbyVision}"`;
474
474
  if (shaka.media.Capabilities.isTypeSupported(type)) {
475
475
  return [dolbyVision];
476
476
  }
@@ -174,6 +174,9 @@ shaka.media.StallDetector.MediaElementImplementation = class {
174
174
  if (this.mediaElement_.paused) {
175
175
  return false;
176
176
  }
177
+ if (this.mediaElement_.playbackRate == 0) {
178
+ return false;
179
+ }
177
180
 
178
181
  // If we have don't have enough content, we are not stalled, we are
179
182
  // buffering.
package/lib/player.js CHANGED
@@ -622,6 +622,12 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
622
622
  */
623
623
  this.playRateController_ = null;
624
624
 
625
+ // We use the buffering observer and timer to track when we move from having
626
+ // enough buffered content to not enough. They only exist when content has
627
+ // been loaded and are not re-used between loads.
628
+ /** @private {shaka.util.Timer} */
629
+ this.bufferPoller_ = null;
630
+
625
631
  /** @private {shaka.media.BufferingObserver} */
626
632
  this.bufferObserver_ = null;
627
633
 
@@ -709,6 +715,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
709
715
  this.manifestFilterer_ = new shaka.media.ManifestFilterer(
710
716
  this.config_, this.maxHwRes_, null);
711
717
 
718
+ /** @private {!Array.<shaka.media.PreloadManager>} */
719
+ this.createdPreloadManagers_ = [];
720
+
712
721
  /** @private {shaka.util.Stats} */
713
722
  this.stats_ = null;
714
723
 
@@ -875,6 +884,18 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
875
884
 
876
885
  await detachPromise;
877
886
 
887
+ // A PreloadManager can only be used with the Player instance that created
888
+ // it, so all PreloadManagers this Player has created are now useless.
889
+ // Destroy any remaining managers now, to help prevent memory leaks.
890
+ const preloadManagerDestroys = [];
891
+ for (const preloadManager of this.createdPreloadManagers_) {
892
+ if (!preloadManager.isDestroyed()) {
893
+ preloadManagerDestroys.push(preloadManager.destroy());
894
+ }
895
+ }
896
+ this.createdPreloadManagers_ = [];
897
+ await Promise.all(preloadManagerDestroys);
898
+
878
899
  // Tear-down the event managers to ensure handlers stop firing.
879
900
  if (this.globalEventManager_) {
880
901
  this.globalEventManager_.release();
@@ -1228,6 +1249,11 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1228
1249
  this.playheadObservers_ = null;
1229
1250
  }
1230
1251
 
1252
+ if (this.bufferPoller_) {
1253
+ this.bufferPoller_.stop();
1254
+ this.bufferPoller_ = null;
1255
+ }
1256
+
1231
1257
  // Stop the parser early. Since it is at the start of the pipeline, it
1232
1258
  // should be start early to avoid is pushing new data downstream.
1233
1259
  if (this.parser_) {
@@ -1961,6 +1987,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1961
1987
  };
1962
1988
  preloadManager = new shaka.media.PreloadManager(
1963
1989
  assetUri, mimeType, startTimeOfLoad, startTime, playerInterface);
1990
+ this.createdPreloadManagers_.push(preloadManager);
1964
1991
  return preloadManager;
1965
1992
  }
1966
1993
 
@@ -3101,6 +3128,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
3101
3128
  !this.bufferObserver_,
3102
3129
  'No buffering observer should exist before initialization.');
3103
3130
 
3131
+ goog.asserts.assert(
3132
+ !this.bufferPoller_,
3133
+ 'No buffer timer should exist before initialization.');
3134
+
3104
3135
  // Give dummy values, will be updated below.
3105
3136
  this.bufferObserver_ = new shaka.media.BufferingObserver(1, 2);
3106
3137
 
@@ -3110,6 +3141,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
3110
3141
  this.updateBufferingSettings_(rebufferingGoal);
3111
3142
  this.updateBufferState_();
3112
3143
 
3144
+ this.bufferPoller_ = new shaka.util.Timer(() => {
3145
+ this.pollBufferState_();
3146
+ }).tickEvery(/* seconds= */ 0.25);
3113
3147
  this.loadEventManager_.listen(mediaElement, 'waiting',
3114
3148
  (e) => this.pollBufferState_());
3115
3149
  this.loadEventManager_.listen(mediaElement, 'stalled',
@@ -5833,6 +5867,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5833
5867
  const loaded = this.stats_ && this.bufferObserver_ && this.playhead_;
5834
5868
 
5835
5869
  if (loaded) {
5870
+ this.playRateController_.setBuffering(isBuffering);
5836
5871
  if (this.cmcdManager_) {
5837
5872
  this.cmcdManager_.setBuffering(isBuffering);
5838
5873
  }
@@ -7182,7 +7217,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
7182
7217
  * @export
7183
7218
  */
7184
7219
  // eslint-disable-next-line no-useless-concat
7185
- shaka.Player.version = 'v4.8.2' + '-uncompiled'; // x-release-please-version
7220
+ shaka.Player.version = 'v4.8.4' + '-uncompiled'; // x-release-please-version
7186
7221
 
7187
7222
  // Initialize the deprecation system using the version string we just set
7188
7223
  // on the player.
@@ -88,6 +88,7 @@ shaka.polyfill.MediaCapabilities = class {
88
88
  * @private
89
89
  */
90
90
  static async decodingInfo_(mediaDecodingConfig) {
91
+ /** @type {!MediaCapabilitiesDecodingInfo} */
91
92
  const res = {
92
93
  supported: false,
93
94
  powerEfficient: true,
@@ -96,60 +97,26 @@ shaka.polyfill.MediaCapabilities = class {
96
97
  configuration: mediaDecodingConfig,
97
98
  };
98
99
 
99
- if (!mediaDecodingConfig) {
100
- return res;
101
- }
102
-
103
100
  const videoConfig = mediaDecodingConfig['video'];
104
101
  const audioConfig = mediaDecodingConfig['audio'];
105
102
 
106
- const Capabilities = shaka.media.Capabilities;
107
-
108
103
  if (mediaDecodingConfig.type == 'media-source') {
109
104
  if (!shaka.util.Platform.supportsMediaSource()) {
110
105
  return res;
111
106
  }
112
- // Use 'shaka.media.Capabilities.isTypeSupported'to check if
113
- // the stream is supported.
114
- // Cast platforms will additionally check canDisplayType(), which
115
- // accepts extended MIME type parameters.
116
- // See: https://github.com/shaka-project/shaka-player/issues/4726
107
+
117
108
  if (videoConfig) {
118
- let isSupported;
119
- if (shaka.util.Platform.isChromecast()) {
120
- isSupported =
121
- shaka.polyfill.MediaCapabilities.canCastDisplayType_(videoConfig);
122
- } else if (shaka.util.Platform.isTizen()) {
123
- let extendedType = videoConfig.contentType;
124
- if (videoConfig.width && videoConfig.height) {
125
- extendedType += `; width=${videoConfig.width}`;
126
- extendedType += `; height=${videoConfig.height}`;
127
- }
128
- if (videoConfig.framerate) {
129
- extendedType += `; framerate=${videoConfig.framerate}`;
130
- }
131
- if (videoConfig.bitrate) {
132
- extendedType += `; bitrate=${videoConfig.bitrate}`;
133
- }
134
- isSupported = Capabilities.isTypeSupported(extendedType);
135
- } else {
136
- isSupported = Capabilities.isTypeSupported(videoConfig.contentType);
137
- }
109
+ const isSupported =
110
+ await shaka.polyfill.MediaCapabilities.checkVideoSupport_(
111
+ videoConfig);
138
112
  if (!isSupported) {
139
113
  return res;
140
114
  }
141
115
  }
142
116
 
143
117
  if (audioConfig) {
144
- let isSupported;
145
- if (shaka.util.Platform.isChromecast() &&
146
- audioConfig.spatialRendering) {
147
- const extendedType =
148
- audioConfig.contentType + '; spatialRendering=true';
149
- isSupported = Capabilities.isTypeSupported(extendedType);
150
- } else {
151
- isSupported = Capabilities.isTypeSupported(audioConfig.contentType);
152
- }
118
+ const isSupported =
119
+ shaka.polyfill.MediaCapabilities.checkAudioSupport_(audioConfig);
153
120
  if (!isSupported) {
154
121
  return res;
155
122
  }
@@ -178,122 +145,172 @@ shaka.polyfill.MediaCapabilities = class {
178
145
  if (!mediaDecodingConfig.keySystemConfiguration) {
179
146
  // The variant is supported if it's unencrypted.
180
147
  res.supported = true;
181
- return Promise.resolve(res);
148
+ return res;
182
149
  } else {
183
- // Get the MediaKeySystemAccess for the key system.
184
- // Convert the MediaDecodingConfiguration object to a
185
- // MediaKeySystemConfiguration object.
186
-
187
- /** @type {MediaCapabilitiesKeySystemConfiguration} */
188
- const mediaCapkeySystemConfig =
189
- mediaDecodingConfig.keySystemConfiguration;
190
- const audioCapabilities = [];
191
- const videoCapabilities = [];
192
-
193
- if (mediaCapkeySystemConfig.audio) {
194
- let capability = {
195
- robustness: mediaCapkeySystemConfig.audio.robustness || '',
196
- contentType: mediaDecodingConfig.audio.contentType,
197
- };
198
-
199
- // Some Tizen devices seem to misreport AC-3 support, but correctly
200
- // report EC-3 support. So query EC-3 as a fallback for AC-3.
201
- // See https://github.com/shaka-project/shaka-player/issues/2989 for
202
- // details.
203
- if (shaka.util.Platform.isTizen() &&
204
- mediaDecodingConfig.audio.contentType.includes('codecs="ac-3"')) {
205
- capability = {
206
- robustness: capability.robustness,
207
- contentType: 'audio/mp4; codecs="ec-3"',
208
- };
209
- }
150
+ const mcapKeySystemConfig = mediaDecodingConfig.keySystemConfiguration;
151
+ const keySystemAccess =
152
+ await shaka.polyfill.MediaCapabilities.checkDrmSupport_(
153
+ videoConfig, audioConfig, mcapKeySystemConfig);
154
+ if (keySystemAccess) {
155
+ res.supported = true;
156
+ res.keySystemAccess = keySystemAccess;
157
+ }
158
+ }
210
159
 
211
- if (mediaCapkeySystemConfig.audio.encryptionScheme) {
212
- capability = {
213
- robustness: capability.robustness,
214
- contentType: capability.contentType,
215
- encryptionScheme: mediaCapkeySystemConfig.audio.encryptionScheme,
216
- };
217
- }
160
+ return res;
161
+ }
218
162
 
219
- audioCapabilities.push(capability);
163
+ /**
164
+ * @param {!VideoConfiguration} videoConfig The 'video' field of the
165
+ * MediaDecodingConfiguration.
166
+ * @return {!Promise<boolean>}
167
+ * @private
168
+ */
169
+ static async checkVideoSupport_(videoConfig) {
170
+ // Use 'shaka.media.Capabilities.isTypeSupported' to check if
171
+ // the stream is supported.
172
+ // Cast platforms will additionally check canDisplayType(), which
173
+ // accepts extended MIME type parameters.
174
+ // See: https://github.com/shaka-project/shaka-player/issues/4726
175
+ if (shaka.util.Platform.isChromecast()) {
176
+ const isSupported =
177
+ await shaka.polyfill.MediaCapabilities.canCastDisplayType_(
178
+ videoConfig);
179
+ return isSupported;
180
+ } else if (shaka.util.Platform.isTizen()) {
181
+ let extendedType = videoConfig.contentType;
182
+ if (videoConfig.width && videoConfig.height) {
183
+ extendedType += `; width=${videoConfig.width}`;
184
+ extendedType += `; height=${videoConfig.height}`;
220
185
  }
221
-
222
- if (mediaCapkeySystemConfig.video) {
223
- let capability = {
224
- robustness: mediaCapkeySystemConfig.video.robustness || '',
225
- contentType: mediaDecodingConfig.video.contentType,
226
- };
227
- if (mediaCapkeySystemConfig.video.encryptionScheme) {
228
- capability = {
229
- robustness: capability.robustness,
230
- contentType: capability.contentType,
231
- encryptionScheme: mediaCapkeySystemConfig.video.encryptionScheme,
232
- };
233
- }
234
- videoCapabilities.push(capability);
186
+ if (videoConfig.framerate) {
187
+ extendedType += `; framerate=${videoConfig.framerate}`;
188
+ }
189
+ if (videoConfig.bitrate) {
190
+ extendedType += `; bitrate=${videoConfig.bitrate}`;
235
191
  }
192
+ return shaka.media.Capabilities.isTypeSupported(extendedType);
193
+ }
194
+ return shaka.media.Capabilities.isTypeSupported(videoConfig.contentType);
195
+ }
196
+
197
+ /**
198
+ * @param {!AudioConfiguration} audioConfig The 'audio' field of the
199
+ * MediaDecodingConfiguration.
200
+ * @return {boolean}
201
+ * @private
202
+ */
203
+ static checkAudioSupport_(audioConfig) {
204
+ let extendedType = audioConfig.contentType;
205
+ if (shaka.util.Platform.isChromecast() && audioConfig.spatialRendering) {
206
+ extendedType += '; spatialRendering=true';
207
+ }
208
+ return shaka.media.Capabilities.isTypeSupported(extendedType);
209
+ }
236
210
 
237
- /** @type {MediaKeySystemConfiguration} */
238
- const mediaKeySystemConfig = {
239
- initDataTypes: [mediaCapkeySystemConfig.initDataType],
240
- distinctiveIdentifier: mediaCapkeySystemConfig.distinctiveIdentifier,
241
- persistentState: mediaCapkeySystemConfig.persistentState,
242
- sessionTypes: mediaCapkeySystemConfig.sessionTypes,
211
+ /**
212
+ * @param {VideoConfiguration} videoConfig The 'video' field of the
213
+ * MediaDecodingConfiguration.
214
+ * @param {AudioConfiguration} audioConfig The 'audio' field of the
215
+ * MediaDecodingConfiguration.
216
+ * @param {!MediaCapabilitiesKeySystemConfiguration} mcapKeySystemConfig The
217
+ * 'keySystemConfiguration' field of the MediaDecodingConfiguration.
218
+ * @return {Promise<MediaKeySystemAccess>}
219
+ * @private
220
+ */
221
+ static async checkDrmSupport_(videoConfig, audioConfig, mcapKeySystemConfig) {
222
+ const audioCapabilities = [];
223
+ const videoCapabilities = [];
224
+
225
+ if (mcapKeySystemConfig.audio) {
226
+ const capability = {
227
+ robustness: mcapKeySystemConfig.audio.robustness || '',
228
+ contentType: audioConfig.contentType,
243
229
  };
244
230
 
245
- // Only add the audio video capabilities if they have valid data.
246
- // Otherwise the query will fail.
247
- if (audioCapabilities.length) {
248
- mediaKeySystemConfig.audioCapabilities = audioCapabilities;
231
+ // Some Tizen devices seem to misreport AC-3 support, but correctly
232
+ // report EC-3 support. So query EC-3 as a fallback for AC-3.
233
+ // See https://github.com/shaka-project/shaka-player/issues/2989 for
234
+ // details.
235
+ if (shaka.util.Platform.isTizen() &&
236
+ audioConfig.contentType.includes('codecs="ac-3"')) {
237
+ capability.contentType = 'audio/mp4; codecs="ec-3"';
249
238
  }
250
- if (videoCapabilities.length) {
251
- mediaKeySystemConfig.videoCapabilities = videoCapabilities;
239
+
240
+ if (mcapKeySystemConfig.audio.encryptionScheme) {
241
+ capability.encryptionScheme =
242
+ mcapKeySystemConfig.audio.encryptionScheme;
252
243
  }
253
244
 
254
- const cacheKey = shaka.polyfill.MediaCapabilities
255
- .generateKeySystemCacheKey_(
256
- mediaDecodingConfig.video ?
257
- mediaDecodingConfig.video.contentType : '',
258
- mediaDecodingConfig.audio ?
259
- mediaDecodingConfig.audio.contentType : '',
260
- mediaDecodingConfig.keySystemConfiguration.keySystem);
261
-
262
- let keySystemAccess;
263
- try {
264
- if (cacheKey in shaka.polyfill.MediaCapabilities
265
- .memoizedMediaKeySystemAccessRequests_) {
266
- keySystemAccess = shaka.polyfill.MediaCapabilities
267
- .memoizedMediaKeySystemAccessRequests_[cacheKey];
268
- } else {
269
- keySystemAccess = await navigator.requestMediaKeySystemAccess(
270
- mediaCapkeySystemConfig.keySystem, [mediaKeySystemConfig]);
271
- shaka.polyfill.MediaCapabilities
272
- .memoizedMediaKeySystemAccessRequests_[cacheKey] =
273
- keySystemAccess;
274
- }
275
- } catch (e) {
276
- shaka.log.info('navigator.requestMediaKeySystemAccess failed.');
245
+ audioCapabilities.push(capability);
246
+ }
247
+
248
+ if (mcapKeySystemConfig.video) {
249
+ const capability = {
250
+ robustness: mcapKeySystemConfig.video.robustness || '',
251
+ contentType: videoConfig.contentType,
252
+ };
253
+ if (mcapKeySystemConfig.video.encryptionScheme) {
254
+ capability.encryptionScheme =
255
+ mcapKeySystemConfig.video.encryptionScheme;
277
256
  }
257
+ videoCapabilities.push(capability);
258
+ }
278
259
 
279
- if (keySystemAccess) {
280
- res.supported = true;
281
- res.keySystemAccess = keySystemAccess;
260
+ /** @type {MediaKeySystemConfiguration} */
261
+ const mediaKeySystemConfig = {
262
+ initDataTypes: [mcapKeySystemConfig.initDataType],
263
+ distinctiveIdentifier: mcapKeySystemConfig.distinctiveIdentifier,
264
+ persistentState: mcapKeySystemConfig.persistentState,
265
+ sessionTypes: mcapKeySystemConfig.sessionTypes,
266
+ };
267
+
268
+ // Only add audio / video capabilities if they have valid data.
269
+ // Otherwise the query will fail.
270
+ if (audioCapabilities.length) {
271
+ mediaKeySystemConfig.audioCapabilities = audioCapabilities;
272
+ }
273
+ if (videoCapabilities.length) {
274
+ mediaKeySystemConfig.videoCapabilities = videoCapabilities;
275
+ }
276
+
277
+ const cacheKey = shaka.polyfill.MediaCapabilities
278
+ .generateKeySystemCacheKey_(
279
+ videoConfig ? videoConfig.contentType : '',
280
+ audioConfig ? audioConfig.contentType : '',
281
+ mcapKeySystemConfig.keySystem);
282
+
283
+ /** @type {MediaKeySystemAccess} */
284
+ let keySystemAccess = null;
285
+ try {
286
+ if (cacheKey in shaka.polyfill.MediaCapabilities
287
+ .memoizedMediaKeySystemAccessRequests_) {
288
+ keySystemAccess = shaka.polyfill.MediaCapabilities
289
+ .memoizedMediaKeySystemAccessRequests_[cacheKey];
290
+ } else {
291
+ keySystemAccess = await navigator.requestMediaKeySystemAccess(
292
+ mcapKeySystemConfig.keySystem, [mediaKeySystemConfig]);
293
+ shaka.polyfill.MediaCapabilities
294
+ .memoizedMediaKeySystemAccessRequests_[cacheKey] =
295
+ keySystemAccess;
282
296
  }
297
+ } catch (e) {
298
+ shaka.log.info('navigator.requestMediaKeySystemAccess failed.');
283
299
  }
284
300
 
285
- return res;
301
+ return keySystemAccess;
286
302
  }
287
303
 
288
304
  /**
289
305
  * Checks if the given media parameters of the video or audio streams are
290
306
  * supported by the Cast platform.
291
307
  * @param {!VideoConfiguration} videoConfig The 'video' field of the
292
- * MediaDecodingConfiguration.
293
- * @return {boolean} `true` when the stream can be displayed on a Cast device.
308
+ * MediaDecodingConfiguration.
309
+ * @return {!Promise<boolean>} `true` when the stream can be displayed on a
310
+ * Cast device.
294
311
  * @private
295
312
  */
296
- static canCastDisplayType_(videoConfig) {
313
+ static async canCastDisplayType_(videoConfig) {
297
314
  if (!(window.cast &&
298
315
  cast.__platform__ && cast.__platform__.canDisplayType)) {
299
316
  shaka.log.warning('Expected cast APIs to be available! Falling back to ' +
@@ -321,7 +338,7 @@ shaka.polyfill.MediaCapabilities = class {
321
338
  result = shaka.polyfill.MediaCapabilities
322
339
  .memoizedCanDisplayTypeRequests_[displayType];
323
340
  } else {
324
- result = cast.__platform__.canDisplayType(displayType);
341
+ result = await cast.__platform__.canDisplayType(displayType);
325
342
  shaka.polyfill.MediaCapabilities
326
343
  .memoizedCanDisplayTypeRequests_[displayType] = result;
327
344
  }