shaka-player 4.8.3 → 4.8.5

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.
package/externs/ima.js CHANGED
@@ -204,6 +204,8 @@ google.ima.AdDisplayContainer = class {
204
204
  constructor(adContainer, video) {}
205
205
 
206
206
  initialize() {}
207
+
208
+ destroy() {}
207
209
  };
208
210
 
209
211
 
@@ -63,16 +63,17 @@ shaka.ads.ClientSideAdManager = class {
63
63
 
64
64
  google.ima.settings.setDisableCustomPlaybackForIOS10Plus(true);
65
65
 
66
- const adDisplayContainer = new google.ima.AdDisplayContainer(
66
+ /** @private {!google.ima.AdDisplayContainer} */
67
+ this.adDisplayContainer_ = new google.ima.AdDisplayContainer(
67
68
  this.adContainer_,
68
69
  this.video_);
69
70
 
70
71
  // TODO: IMA: Must be done as the result of a user action on mobile
71
- adDisplayContainer.initialize();
72
+ this.adDisplayContainer_.initialize();
72
73
 
73
74
  // IMA: This instance should be re-used for the entire lifecycle of
74
75
  // the page.
75
- this.adsLoader_ = new google.ima.AdsLoader(adDisplayContainer);
76
+ this.adsLoader_ = new google.ima.AdsLoader(this.adDisplayContainer_);
76
77
 
77
78
  this.adsLoader_.getSettings().setPlayerType('shaka-player');
78
79
  this.adsLoader_.getSettings().setPlayerVersion(shaka.Player.version);
@@ -176,6 +177,7 @@ shaka.ads.ClientSideAdManager = class {
176
177
  this.imaAdsManager_.destroy();
177
178
  }
178
179
  this.adsLoader_.destroy();
180
+ this.adDisplayContainer_.destroy();
179
181
  }
180
182
 
181
183
  /**
@@ -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
  }
@@ -928,13 +928,12 @@ shaka.hls.HlsParser = class {
928
928
  if (!playlist.segments.length) {
929
929
  return defaultBasicInfo;
930
930
  }
931
- const middleSegmentIdx = Math.trunc((playlist.segments.length - 1) / 2);
932
- const middleSegment = playlist.segments[middleSegmentIdx];
933
- const middleSegmentUris = shaka.hls.Utils.constructSegmentUris(
931
+ const firstSegment = playlist.segments[0];
932
+ const firstSegmentUris = shaka.hls.Utils.constructSegmentUris(
934
933
  getUris(),
935
- playlist.segments[0].verbatimSegmentUri);
936
- const middleSegmentUri = middleSegmentUris[0];
937
- const parsedUri = new goog.Uri(middleSegmentUri);
934
+ firstSegment.verbatimSegmentUri);
935
+ const firstSegmentUri = firstSegmentUris[0];
936
+ const parsedUri = new goog.Uri(firstSegmentUri);
938
937
  const extension = parsedUri.getPath().split('.').pop();
939
938
  const rawMimeType = HlsParser.RAW_FORMATS_TO_MIME_TYPES_[extension];
940
939
  if (rawMimeType) {
@@ -946,7 +945,7 @@ shaka.hls.HlsParser = class {
946
945
 
947
946
  let initData = null;
948
947
  const initSegmentRef = this.getInitSegmentReference_(
949
- playlist, middleSegment.tags, getUris);
948
+ playlist, firstSegment.tags, getUris);
950
949
  this.mapTagToInitSegmentRefMap_.clear();
951
950
  if (initSegmentRef) {
952
951
  const initSegmentRequest = shaka.util.Networking.createSegmentRequest(
@@ -961,8 +960,17 @@ shaka.hls.HlsParser = class {
961
960
  initData = initResponse.data;
962
961
  }
963
962
 
964
- const segmentRequest = shaka.net.NetworkingEngine.makeRequest(
965
- middleSegmentUris, this.config_.retryParameters);
963
+ let startByte = 0;
964
+ let endByte = null;
965
+ const byterangeTag = shaka.hls.Utils.getFirstTagWithName(
966
+ firstSegment.tags, 'EXT-X-BYTERANGE');
967
+ if (byterangeTag) {
968
+ [startByte, endByte] = this.parseByteRange_(
969
+ /* previousReference= */ null, byterangeTag.value);
970
+ }
971
+
972
+ const segmentRequest = shaka.util.Networking.createSegmentRequest(
973
+ firstSegmentUris, startByte, endByte, this.config_.retryParameters);
966
974
  const type = shaka.net.NetworkingEngine.AdvancedRequestType.MEDIA_SEGMENT;
967
975
  const response = await this.makeNetworkRequest_(
968
976
  segmentRequest, requestType, {type});
@@ -980,7 +988,8 @@ shaka.hls.HlsParser = class {
980
988
  return basicInfo;
981
989
  }
982
990
  } else if (extension == 'mp4' || extension == 'cmfv' ||
983
- extension == 'm4s' || contentMimeType == 'video/mp4' ||
991
+ extension == 'm4s' || extension == 'fmp4' ||
992
+ contentMimeType == 'video/mp4' ||
984
993
  contentMimeType == 'audio/mp4' ||
985
994
  contentMimeType == 'video/iso.segment') {
986
995
  const basicInfo = shaka.media.SegmentUtils.getBasicInfoFromMp4(
@@ -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
 
@@ -280,7 +280,7 @@ shaka.media.MediaSourceEngine = class {
280
280
  if (contentType == ContentType.VIDEO) {
281
281
  codecs = StreamUtils.getCorrectVideoCodecs(codecs);
282
282
  } else if (contentType == ContentType.AUDIO) {
283
- codecs = StreamUtils.getCorrectAudioCodecs(codecs);
283
+ codecs = StreamUtils.getCorrectAudioCodecs(codecs, mimeType);
284
284
  }
285
285
  const extendedMimeType = MimeUtils.getExtendedType(
286
286
  stream, mimeType, codecs);
@@ -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
  }
@@ -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
  }
package/lib/player.js CHANGED
@@ -715,6 +715,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
715
715
  this.manifestFilterer_ = new shaka.media.ManifestFilterer(
716
716
  this.config_, this.maxHwRes_, null);
717
717
 
718
+ /** @private {!Array.<shaka.media.PreloadManager>} */
719
+ this.createdPreloadManagers_ = [];
720
+
718
721
  /** @private {shaka.util.Stats} */
719
722
  this.stats_ = null;
720
723
 
@@ -881,6 +884,18 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
881
884
 
882
885
  await detachPromise;
883
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
+
884
899
  // Tear-down the event managers to ensure handlers stop firing.
885
900
  if (this.globalEventManager_) {
886
901
  this.globalEventManager_.release();
@@ -1972,6 +1987,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1972
1987
  };
1973
1988
  preloadManager = new shaka.media.PreloadManager(
1974
1989
  assetUri, mimeType, startTimeOfLoad, startTime, playerInterface);
1990
+ this.createdPreloadManagers_.push(preloadManager);
1975
1991
  return preloadManager;
1976
1992
  }
1977
1993
 
@@ -7201,7 +7217,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
7201
7217
  * @export
7202
7218
  */
7203
7219
  // eslint-disable-next-line no-useless-concat
7204
- shaka.Player.version = 'v4.8.3' + '-uncompiled'; // x-release-please-version
7220
+ shaka.Player.version = 'v4.8.5' + '-uncompiled'; // x-release-please-version
7205
7221
 
7206
7222
  // Initialize the deprecation system using the version string we just set
7207
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
  }
@@ -22,6 +22,7 @@ goog.require('shaka.util.Id3Utils');
22
22
  goog.require('shaka.util.ManifestParserUtils');
23
23
  goog.require('shaka.util.MimeUtils');
24
24
  goog.require('shaka.util.Mp4Generator');
25
+ goog.require('shaka.util.StreamUtils');
25
26
  goog.require('shaka.util.TsParser');
26
27
  goog.require('shaka.util.Uint8ArrayUtils');
27
28
 
@@ -143,13 +144,17 @@ shaka.transmuxer.TsTransmuxer = class {
143
144
  convertCodecs(contentType, mimeType) {
144
145
  if (this.isTsContainer_(mimeType)) {
145
146
  const ContentType = shaka.util.ManifestParserUtils.ContentType;
147
+ const StreamUtils = shaka.util.StreamUtils;
146
148
  // The replace it's necessary because Firefox(the only browser that
147
149
  // supports MP3 in MP4) only support the MP3 codec with the mp3 string.
148
150
  // MediaSource.isTypeSupported('audio/mp4; codecs="mp4a.40.34"') -> false
149
151
  // MediaSource.isTypeSupported('audio/mp4; codecs="mp3"') -> true
150
152
  const codecs = shaka.util.MimeUtils.getCodecs(mimeType)
151
153
  .replace('mp4a.40.34', 'mp3').split(',')
152
- .map(this.getCorrectVideoCodecs_).join(',');
154
+ .map((codecs) => {
155
+ return StreamUtils.getCorrectAudioCodecs(codecs, 'audio/mp4');
156
+ })
157
+ .map(StreamUtils.getCorrectVideoCodecs).join(',');
153
158
  if (contentType == ContentType.AUDIO) {
154
159
  return `audio/mp4; codecs="${codecs}"`;
155
160
  }
@@ -159,29 +164,6 @@ shaka.transmuxer.TsTransmuxer = class {
159
164
  }
160
165
 
161
166
 
162
- /**
163
- * @param {string} codec
164
- * @return {string}
165
- * @private
166
- */
167
- getCorrectVideoCodecs_(codec) {
168
- if (codec.includes('avc1')) {
169
- // Convert avc1 codec string from RFC-4281 to RFC-6381 for
170
- // MediaSource.isTypeSupported
171
- // Example, convert avc1.66.30 to avc1.42001e (0x42 == 66 and 0x1e == 30)
172
- const avcdata = codec.split('.');
173
- if (avcdata.length == 3) {
174
- let result = avcdata.shift() + '.';
175
- result += parseInt(avcdata.shift(), 10).toString(16);
176
- result +=
177
- ('000' + parseInt(avcdata.shift(), 10).toString(16)).slice(-4);
178
- return result;
179
- }
180
- }
181
- return codec;
182
- }
183
-
184
-
185
167
  /**
186
168
  * @override
187
169
  * @export