shaka-player 4.3.0 → 4.3.1

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 (71) hide show
  1. package/.github/workflows/build-and-test.yaml +7 -0
  2. package/AUTHORS +1 -0
  3. package/CHANGELOG.md +33 -0
  4. package/CONTRIBUTORS +1 -0
  5. package/conditional/dummy_cast_proxy.js +120 -0
  6. package/dist/demo.compiled.debug.js +104 -110
  7. package/dist/demo.compiled.debug.map +3 -3
  8. package/dist/demo.compiled.js +104 -110
  9. package/dist/demo.compiled.map +3 -3
  10. package/dist/deps.js +8 -6
  11. package/dist/locales.js +28 -28
  12. package/dist/receiver.compiled.debug.js +54 -60
  13. package/dist/receiver.compiled.debug.map +3 -3
  14. package/dist/receiver.compiled.js +54 -60
  15. package/dist/receiver.compiled.map +3 -3
  16. package/dist/shaka-player.compiled.d.ts +19 -2
  17. package/dist/shaka-player.compiled.debug.d.ts +19 -2
  18. package/dist/shaka-player.compiled.debug.externs.js +102 -80
  19. package/dist/shaka-player.compiled.debug.js +632 -628
  20. package/dist/shaka-player.compiled.debug.map +4 -4
  21. package/dist/shaka-player.compiled.externs.js +102 -80
  22. package/dist/shaka-player.compiled.js +903 -900
  23. package/dist/shaka-player.compiled.map +4 -4
  24. package/dist/shaka-player.ui.d.ts +19 -2
  25. package/dist/shaka-player.ui.debug.d.ts +19 -2
  26. package/dist/shaka-player.ui.debug.externs.js +102 -80
  27. package/dist/shaka-player.ui.debug.js +681 -678
  28. package/dist/shaka-player.ui.debug.map +4 -4
  29. package/dist/shaka-player.ui.externs.js +102 -80
  30. package/dist/shaka-player.ui.js +1033 -1031
  31. package/dist/shaka-player.ui.map +4 -4
  32. package/docs/tutorials/welcome.md +2 -2
  33. package/externs/shaka/mp4_parser.js +3 -0
  34. package/externs/shaka/player.js +2 -1
  35. package/lib/cea/cea708_window.js +2 -2
  36. package/lib/cea/mp4_cea_parser.js +37 -16
  37. package/lib/dash/segment_template.js +1 -1
  38. package/lib/hls/hls_parser.js +37 -19
  39. package/lib/media/content_workarounds.js +12 -0
  40. package/lib/media/media_source_capabilities.js +36 -0
  41. package/lib/media/media_source_engine.js +12 -7
  42. package/lib/media/transmuxer.js +6 -4
  43. package/lib/player.js +5 -5
  44. package/lib/polyfill/abort_controller.js +143 -0
  45. package/lib/polyfill/media_capabilities.js +65 -11
  46. package/lib/polyfill/patchedmediakeys_apple.js +33 -28
  47. package/lib/text/ttml_text_parser.js +32 -7
  48. package/lib/text/ui_text_displayer.js +17 -0
  49. package/lib/util/mp4_box_parsers.js +24 -0
  50. package/lib/util/mp4_parser.js +35 -0
  51. package/lib/util/player_configuration.js +10 -3
  52. package/lib/util/stream_utils.js +35 -13
  53. package/package.json +4 -4
  54. package/shaka-player.uncompiled.js +1 -0
  55. package/test/ads/ad_manager_unit.js +3 -1
  56. package/test/dash/dash_parser_manifest_unit.js +1 -0
  57. package/test/hls/hls_parser_unit.js +132 -0
  58. package/test/media/drm_engine_unit.js +11 -1
  59. package/test/media/media_source_engine_integration.js +78 -2
  60. package/test/polyfill/media_capabilities_unit.js +259 -0
  61. package/test/polyfill/patchedmediakeys_apple_unit.js +6 -0
  62. package/test/test/util/stream_generator.js +212 -8
  63. package/test/test/util/test_scheme.js +4 -3
  64. package/test/test/util/util.js +3 -2
  65. package/test/text/mp4_ttml_parser_unit.js +6 -4
  66. package/test/text/ttml_text_parser_unit.js +119 -86
  67. package/test/text/ui_text_displayer_unit.js +76 -0
  68. package/test/util/stream_utils_unit.js +18 -0
  69. package/test/util/xml_utils_unit.js +6 -1
  70. package/third_party/closure-uri/uri.js +1 -1
  71. package/ui/controls.js +45 -36
@@ -7,7 +7,9 @@
7
7
  goog.provide('shaka.polyfill.MediaCapabilities');
8
8
 
9
9
  goog.require('shaka.log');
10
+ goog.require('shaka.media.Capabilities');
10
11
  goog.require('shaka.polyfill');
12
+ goog.require('shaka.util.Error');
11
13
  goog.require('shaka.util.Platform');
12
14
 
13
15
 
@@ -96,37 +98,51 @@ shaka.polyfill.MediaCapabilities = class {
96
98
  return res;
97
99
  }
98
100
 
101
+ const videoConfig = mediaDecodingConfig['video'];
102
+ const audioConfig = mediaDecodingConfig['audio'];
103
+
104
+ const Capabilities = shaka.media.Capabilities;
105
+
99
106
  if (mediaDecodingConfig.type == 'media-source') {
100
107
  if (!shaka.util.Platform.supportsMediaSource()) {
101
108
  return res;
102
109
  }
103
- // Use 'MediaSource.isTypeSupported' to check if the stream is supported.
104
- if (mediaDecodingConfig['video']) {
105
- const contentType = mediaDecodingConfig['video'].contentType;
106
- const isSupported = MediaSource.isTypeSupported(contentType);
110
+ // Use 'shaka.media.Capabilities.isTypeSupported'to check if
111
+ // the stream is supported.
112
+ // Cast platforms will additionally check canDisplayType(), which
113
+ // accepts extended MIME type parameters.
114
+ // See: https://github.com/shaka-project/shaka-player/issues/4726
115
+ if (videoConfig) {
116
+ let isSupported;
117
+ if (shaka.util.Platform.isChromecast()) {
118
+ isSupported =
119
+ shaka.polyfill.MediaCapabilities.canCastDisplayType_(videoConfig);
120
+ } else {
121
+ isSupported = Capabilities.isTypeSupported(videoConfig.contentType);
122
+ }
107
123
  if (!isSupported) {
108
124
  return res;
109
125
  }
110
126
  }
111
127
 
112
- if (mediaDecodingConfig['audio']) {
113
- const contentType = mediaDecodingConfig['audio'].contentType;
114
- const isSupported = MediaSource.isTypeSupported(contentType);
128
+ if (audioConfig) {
129
+ const contentType = audioConfig.contentType;
130
+ const isSupported = Capabilities.isTypeSupported(contentType);
115
131
  if (!isSupported) {
116
132
  return res;
117
133
  }
118
134
  }
119
135
  } else if (mediaDecodingConfig.type == 'file') {
120
- if (mediaDecodingConfig['video']) {
121
- const contentType = mediaDecodingConfig['video'].contentType;
136
+ if (videoConfig) {
137
+ const contentType = videoConfig.contentType;
122
138
  const isSupported = shaka.util.Platform.supportsMediaType(contentType);
123
139
  if (!isSupported) {
124
140
  return res;
125
141
  }
126
142
  }
127
143
 
128
- if (mediaDecodingConfig['audio']) {
129
- const contentType = mediaDecodingConfig['audio'].contentType;
144
+ if (audioConfig) {
145
+ const contentType = audioConfig.contentType;
130
146
  const isSupported = shaka.util.Platform.supportsMediaType(contentType);
131
147
  if (!isSupported) {
132
148
  return res;
@@ -201,6 +217,44 @@ shaka.polyfill.MediaCapabilities = class {
201
217
 
202
218
  return res;
203
219
  }
220
+
221
+ /**
222
+ * Checks if the given media parameters of the video or audio streams are
223
+ * supported by the Cast platform.
224
+ * @param {!VideoConfiguration} videoConfig The 'video' field of the
225
+ * MediaDecodingConfiguration.
226
+ * @return {boolean} `true` when the stream can be displayed on a Cast device.
227
+ * @private
228
+ */
229
+ static canCastDisplayType_(videoConfig) {
230
+ if (!(window.cast)) {
231
+ shaka.log.error('Expected cast namespace to be available!');
232
+ throw new shaka.util.Error(
233
+ shaka.util.Error.Severity.CRITICAL,
234
+ shaka.util.Error.Category.CAST,
235
+ shaka.util.Error.Code.CAST_API_UNAVAILABLE);
236
+ } else if (!(cast.__platform__ && cast.__platform__.canDisplayType)) {
237
+ shaka.log.warning('Expected cast APIs to be available! Falling back to ' +
238
+ 'shaka.media.Capabilities.isTypeSupported() for type support.');
239
+ return shaka.media.Capabilities.isTypeSupported(videoConfig.contentType);
240
+ }
241
+
242
+ let displayType = videoConfig.contentType;
243
+ if (videoConfig.width && videoConfig.height) {
244
+ displayType +=
245
+ `; width=${videoConfig.width}; height=${videoConfig.height}`;
246
+ }
247
+ if (videoConfig.framerate) {
248
+ displayType += `; framerate=${videoConfig.framerate}`;
249
+ }
250
+ if (videoConfig.transferFunction === 'pq') {
251
+ // A "PQ" transfer function indicates this is an HDR-capable stream;
252
+ // "smpte2084" is the published standard. We need to inform the platform
253
+ // this query is specifically for HDR.
254
+ displayType += '; eotf=smpte2084';
255
+ }
256
+ return cast.__platform__.canDisplayType(displayType);
257
+ }
204
258
  };
205
259
 
206
260
  /**
@@ -27,38 +27,39 @@ goog.require('shaka.util.StringUtils');
27
27
  shaka.polyfill.PatchedMediaKeysApple = class {
28
28
  /**
29
29
  * Installs the polyfill if needed.
30
- * @param {boolean} enableUninstall enables uninstalling the polyfill
30
+ * @param {boolean=} enableUninstall enables uninstalling the polyfill
31
31
  * @export
32
32
  */
33
33
  static install(enableUninstall = false) {
34
+ // Alias
35
+ const PatchedMediaKeysApple = shaka.polyfill.PatchedMediaKeysApple;
36
+
34
37
  if (!window.HTMLVideoElement || !window.WebKitMediaKeys) {
35
38
  // No HTML5 video or no prefixed EME.
36
39
  return;
37
40
  }
38
41
 
39
42
  if (enableUninstall) {
40
- this.enableUninstall = true;
41
- this.originalHTMLMediaElementPrototypeMediaKeys =
43
+ PatchedMediaKeysApple.enableUninstall = true;
44
+ PatchedMediaKeysApple.originalHTMLMediaElementPrototypeMediaKeys =
42
45
  /** @type {!Object} */ (
43
46
  Object.getOwnPropertyDescriptor(
44
47
  // eslint-disable-next-line no-restricted-syntax
45
48
  HTMLMediaElement.prototype, 'mediaKeys',
46
49
  )
47
50
  );
48
- // eslint-disable-next-line no-restricted-syntax
49
- this.originalHTMLMediaElementPrototypeSetMediaKeys = HTMLMediaElement
50
- .prototype.setMediaKeys;
51
- this.originalWindowMediaKeys = window.MediaKeys;
52
- this.originalWindowMediaKeySystemAccess = window.MediaKeySystemAccess;
53
- this.originalNavigatorRequestMediaKeySystemAccess = navigator
54
- .requestMediaKeySystemAccess;
51
+ PatchedMediaKeysApple.originalHTMLMediaElementPrototypeSetMediaKeys =
52
+ // eslint-disable-next-line no-restricted-syntax
53
+ HTMLMediaElement.prototype.setMediaKeys;
54
+ PatchedMediaKeysApple.originalWindowMediaKeys = window.MediaKeys;
55
+ PatchedMediaKeysApple.originalWindowMediaKeySystemAccess =
56
+ window.MediaKeySystemAccess;
57
+ PatchedMediaKeysApple.originalNavigatorRequestMediaKeySystemAccess =
58
+ navigator.requestMediaKeySystemAccess;
55
59
  }
56
60
 
57
61
  shaka.log.info('Using Apple-prefixed EME');
58
62
 
59
- // Alias
60
- const PatchedMediaKeysApple = shaka.polyfill.PatchedMediaKeysApple;
61
-
62
63
  // Delete mediaKeys to work around strict mode compatibility issues.
63
64
  // eslint-disable-next-line no-restricted-syntax
64
65
  delete HTMLMediaElement.prototype['mediaKeys'];
@@ -83,32 +84,36 @@ shaka.polyfill.PatchedMediaKeysApple = class {
83
84
  * @export
84
85
  */
85
86
  static uninstall() {
86
- if (!this.enableUninstall) {
87
+ // Alias
88
+ const PatchedMediaKeysApple = shaka.polyfill.PatchedMediaKeysApple;
89
+
90
+ if (!PatchedMediaKeysApple.enableUninstall) {
87
91
  return;
88
92
  }
89
93
 
90
94
  shaka.log.info('Un-installing Apple-prefixed EME');
91
95
 
92
- this.enableUninstall = false;
96
+ PatchedMediaKeysApple.enableUninstall = false;
93
97
  Object.defineProperty(
94
98
  // eslint-disable-next-line no-restricted-syntax
95
99
  HTMLMediaElement.prototype,
96
100
  'mediaKeys',
97
- this.originalHTMLMediaElementPrototypeMediaKeys,
101
+ PatchedMediaKeysApple.originalHTMLMediaElementPrototypeMediaKeys,
98
102
  );
99
103
  // eslint-disable-next-line no-restricted-syntax
100
- HTMLMediaElement.prototype.setMediaKeys = this
101
- .originalHTMLMediaElementPrototypeSetMediaKeys;
102
- window.MediaKeys = this.originalWindowMediaKeys;
103
- window.MediaKeySystemAccess = this.originalWindowMediaKeySystemAccess;
104
- navigator.requestMediaKeySystemAccess = this
105
- .originalNavigatorRequestMediaKeySystemAccess;
106
-
107
- this.originalWindowMediaKeys = null;
108
- this.originalWindowMediaKeySystemAccess = null;
109
- this.originalHTMLMediaElementPrototypeSetMediaKeys = null;
110
- this.originalNavigatorRequestMediaKeySystemAccess = null;
111
- this.originalHTMLMediaElementPrototypeMediaKeys = null;
104
+ HTMLMediaElement.prototype.setMediaKeys =
105
+ PatchedMediaKeysApple.originalHTMLMediaElementPrototypeSetMediaKeys;
106
+ window.MediaKeys = PatchedMediaKeysApple.originalWindowMediaKeys;
107
+ window.MediaKeySystemAccess =
108
+ PatchedMediaKeysApple.originalWindowMediaKeySystemAccess;
109
+ navigator.requestMediaKeySystemAccess =
110
+ PatchedMediaKeysApple.originalNavigatorRequestMediaKeySystemAccess;
111
+
112
+ PatchedMediaKeysApple.originalWindowMediaKeys = null;
113
+ PatchedMediaKeysApple.originalWindowMediaKeySystemAccess = null;
114
+ PatchedMediaKeysApple.originalHTMLMediaElementPrototypeSetMediaKeys = null;
115
+ PatchedMediaKeysApple.originalNavigatorRequestMediaKeySystemAccess = null;
116
+ PatchedMediaKeysApple.originalHTMLMediaElementPrototypeMediaKeys = null;
112
117
 
113
118
  window.shakaMediaKeysPolyfill = false;
114
119
  }
@@ -136,7 +136,7 @@ shaka.text.TtmlTextParser = class {
136
136
  }
137
137
 
138
138
  const cue = TtmlTextParser.parseCue_(
139
- body, time.periodStart, rateInfo, metadataElements, styles,
139
+ body, time, rateInfo, metadataElements, styles,
140
140
  regionElements, cueRegions, whitespaceTrim,
141
141
  cellResolutionInfo, /* parentCueElement= */ null,
142
142
  /* isContent= */ false);
@@ -157,7 +157,7 @@ shaka.text.TtmlTextParser = class {
157
157
  * Parses a TTML node into a Cue.
158
158
  *
159
159
  * @param {!Node} cueNode
160
- * @param {number} offset
160
+ * @param {shaka.extern.TextParser.TimeContext} timeContext
161
161
  * @param {!shaka.text.TtmlTextParser.RateInfo_} rateInfo
162
162
  * @param {!Array.<!Element>} metadataElements
163
163
  * @param {!Array.<!Element>} styles
@@ -171,7 +171,7 @@ shaka.text.TtmlTextParser = class {
171
171
  * @private
172
172
  */
173
173
  static parseCue_(
174
- cueNode, offset, rateInfo, metadataElements, styles, regionElements,
174
+ cueNode, timeContext, rateInfo, metadataElements, styles, regionElements,
175
175
  cueRegions, whitespaceTrim, cellResolution, parentCueElement, isContent) {
176
176
  /** @type {Element} */
177
177
  let cueElement;
@@ -236,7 +236,7 @@ shaka.text.TtmlTextParser = class {
236
236
  for (const childNode of cueElement.childNodes) {
237
237
  const nestedCue = shaka.text.TtmlTextParser.parseCue_(
238
238
  childNode,
239
- offset,
239
+ timeContext,
240
240
  rateInfo,
241
241
  metadataElements,
242
242
  styles,
@@ -293,15 +293,20 @@ shaka.text.TtmlTextParser = class {
293
293
  if (start == null) {
294
294
  start = 0;
295
295
  }
296
- start += offset;
296
+ start += timeContext.periodStart;
297
297
 
298
298
  // If end is null, that means the duration is effectively infinite.
299
299
  if (end == null) {
300
300
  end = Infinity;
301
301
  } else {
302
- end += offset;
302
+ end += timeContext.periodStart;
303
303
  }
304
304
 
305
+ // Clip times to segment boundaries.
306
+ // https://github.com/shaka-project/shaka-player/issues/4631
307
+ start = Math.max(start, timeContext.segmentStart);
308
+ end = Math.min(end, timeContext.segmentEnd);
309
+
305
310
  if (!hasTimeAttributes && nestedCues.length > 0) {
306
311
  // If no time is defined for this cue, base the timing information on
307
312
  // the time of the nested cues. In the case of multiple nested cues with
@@ -569,8 +574,28 @@ shaka.text.TtmlTextParser = class {
569
574
 
570
575
  const fontFamily = TtmlTextParser.getStyleAttribute_(
571
576
  cueElement, region, styles, 'fontFamily', shouldInheritRegionStyles);
577
+ // See https://github.com/sandflow/imscJS/blob/1.1.3/src/main/js/html.js#L1384
572
578
  if (fontFamily) {
573
- cue.fontFamily = fontFamily;
579
+ switch (fontFamily) {
580
+ case 'monospaceSerif':
581
+ cue.fontFamily = 'Courier New,Liberation Mono,Courier,monospace';
582
+ break;
583
+ case 'proportionalSansSerif':
584
+ cue.fontFamily = 'Arial,Helvetica,Liberation Sans,sans-serif';
585
+ break;
586
+ case 'sansSerif':
587
+ cue.fontFamily = 'sans-serif';
588
+ break;
589
+ case 'monospaceSansSerif':
590
+ cue.fontFamily = 'Consolas,monospace';
591
+ break;
592
+ case 'proportionalSerif':
593
+ cue.fontFamily = 'serif';
594
+ break;
595
+ default:
596
+ cue.fontFamily = fontFamily;
597
+ break;
598
+ }
574
599
  }
575
600
 
576
601
  const fontWeight = TtmlTextParser.getStyleAttribute_(
@@ -199,6 +199,19 @@ shaka.text.UITextDisplayer = class {
199
199
  this.isTextVisible_ = on;
200
200
  }
201
201
 
202
+ /**
203
+ * @private
204
+ */
205
+ isElementUnderTextContainer_(elemToCheck) {
206
+ while (elemToCheck != null) {
207
+ if (elemToCheck == this.textContainer_) {
208
+ return true;
209
+ }
210
+ elemToCheck = elemToCheck.parentElement;
211
+ }
212
+ return false;
213
+ }
214
+
202
215
  /**
203
216
  * @param {!Array.<!shaka.extern.Cue>} cues
204
217
  * @param {!HTMLElement} container
@@ -260,6 +273,9 @@ shaka.text.UITextDisplayer = class {
260
273
  cueRegistry = this.currentCuesMap_.get(cue);
261
274
  wrapper = cueRegistry.wrapper;
262
275
  updateDOM = true;
276
+ } else if (!this.isElementUnderTextContainer_(wrapper)) {
277
+ // We found that the wrapper needs to be in the DOM
278
+ updateDOM = true;
263
279
  }
264
280
  }
265
281
 
@@ -276,6 +292,7 @@ shaka.text.UITextDisplayer = class {
276
292
  const topCue = parents.pop();
277
293
  goog.asserts.assert(topCue == cue, 'Parent cues should be kept in order');
278
294
  }
295
+
279
296
  if (updateDOM) {
280
297
  for (const element of toUproot) {
281
298
  // NOTE: Because we uproot shared region elements, too, we might hit an
@@ -7,6 +7,7 @@
7
7
  goog.provide('shaka.util.Mp4BoxParsers');
8
8
 
9
9
  goog.require('shaka.util.DataViewReader');
10
+ goog.require('shaka.util.Mp4Parser');
10
11
 
11
12
  shaka.util.Mp4BoxParsers = class {
12
13
  /**
@@ -188,6 +189,17 @@ shaka.util.Mp4BoxParsers = class {
188
189
  trackId,
189
190
  };
190
191
  }
192
+
193
+ /**
194
+ * Parses a FRMA box.
195
+ * @param {!shaka.util.DataViewReader} reader
196
+ * @return {!shaka.util.ParsedFRMABox}
197
+ */
198
+ static parseFRMA(reader) {
199
+ const fourcc = reader.readUint32();
200
+ const codec = shaka.util.Mp4Parser.typeToString(fourcc);
201
+ return {codec};
202
+ }
191
203
  };
192
204
 
193
205
 
@@ -306,3 +318,15 @@ shaka.util.ParsedTRUNSample;
306
318
  * @exportDoc
307
319
  */
308
320
  shaka.util.ParsedTKHDBox;
321
+
322
+ /**
323
+ * @typedef {{
324
+ * codec: string
325
+ * }}
326
+ *
327
+ * @property {string} codec
328
+ * A fourcc for a codec
329
+ *
330
+ * @exportDoc
331
+ */
332
+ shaka.util.ParsedFRMABox;
@@ -172,6 +172,7 @@ shaka.util.Mp4Parser = class {
172
172
 
173
173
  /** @type {shaka.extern.ParsedBox} */
174
174
  const box = {
175
+ name,
175
176
  parser: this,
176
177
  partialOkay: partialOkay || false,
177
178
  version,
@@ -236,6 +237,40 @@ shaka.util.Mp4Parser = class {
236
237
  }
237
238
 
238
239
 
240
+ /**
241
+ * A callback that tells the Mp4 parser to treat the body of a box as a visual
242
+ * sample entry. A visual sample entry has some fixed-sized fields
243
+ * describing the video codec parameters, followed by an arbitrary number of
244
+ * appended children. Each child is a box.
245
+ *
246
+ * @param {!shaka.extern.ParsedBox} box
247
+ * @export
248
+ */
249
+ static visualSampleEntry(box) {
250
+ // The "reader" starts at the payload, so we need to add the header to the
251
+ // start position. The header size varies.
252
+ const headerSize = shaka.util.Mp4Parser.headerSize(box);
253
+
254
+ // Skip 6 reserved bytes.
255
+ // Skip 2-byte data reference index.
256
+ // Skip 16 more reserved bytes.
257
+ // Skip 4 bytes for width/height.
258
+ // Skip 8 bytes for horizontal/vertical resolution.
259
+ // Skip 4 more reserved bytes (0)
260
+ // Skip 2-byte frame count.
261
+ // Skip 32-byte compressor name (length byte, then name, then 0-padding).
262
+ // Skip 2-byte depth.
263
+ // Skip 2 more reserved bytes (0xff)
264
+ // 78 bytes total.
265
+ // See also https://github.com/shaka-project/shaka-packager/blob/d5ca6e84/packager/media/formats/mp4/box_definitions.cc#L1544
266
+ box.reader.skip(78);
267
+
268
+ while (box.reader.hasMoreData() && !box.parser.done_) {
269
+ box.parser.parseNext(box.start + headerSize, box.reader, box.partialOkay);
270
+ }
271
+ }
272
+
273
+
239
274
  /**
240
275
  * Create a callback that tells the Mp4 parser to treat the body of a box as a
241
276
  * binary blob and to parse the body's contents using the provided callback.
@@ -12,6 +12,7 @@ goog.require('shaka.config.AutoShowText');
12
12
  goog.require('shaka.log');
13
13
  goog.require('shaka.net.NetworkingEngine');
14
14
  goog.require('shaka.util.ConfigUtils');
15
+ goog.require('shaka.util.FairPlayUtils');
15
16
  goog.require('shaka.util.LanguageUtils');
16
17
  goog.require('shaka.util.ManifestParserUtils');
17
18
  goog.require('shaka.util.Platform');
@@ -68,9 +69,15 @@ shaka.util.PlayerConfiguration = class {
68
69
  advanced: {}, // key is arbitrary key system ID, value is a record type
69
70
  delayLicenseRequestUntilPlayed: false,
70
71
  initDataTransform: (initData, initDataType, drmInfo) => {
71
- return shaka.util.ConfigUtils.referenceParametersAndReturn(
72
- [initData, initDataType, drmInfo],
73
- initData);
72
+ const keySystem = drmInfo.keySystem;
73
+ if (keySystem == 'com.apple.fps.1_0' && initDataType == 'skd') {
74
+ const cert = drmInfo.serverCertificate;
75
+ const contentId =
76
+ shaka.util.FairPlayUtils.defaultGetContentId(initData);
77
+ initData = shaka.util.FairPlayUtils.initDataTransform(
78
+ initData, contentId, cert);
79
+ }
80
+ return initData;
74
81
  },
75
82
  logLicenseExchange: false,
76
83
  updateExpirationTime: 1,
@@ -8,6 +8,7 @@ goog.provide('shaka.util.StreamUtils');
8
8
 
9
9
  goog.require('goog.asserts');
10
10
  goog.require('shaka.log');
11
+ goog.require('shaka.media.Capabilities');
11
12
  goog.require('shaka.text.TextEngine');
12
13
  goog.require('shaka.util.Functional');
13
14
  goog.require('shaka.util.LanguageUtils');
@@ -437,25 +438,31 @@ shaka.util.StreamUtils = class {
437
438
  // See: https://github.com/shaka-project/shaka-player/issues/3860
438
439
  const video = variant.video;
439
440
  const ContentType = shaka.util.ManifestParserUtils.ContentType;
441
+ const Capabilities = shaka.media.Capabilities;
440
442
  if (video) {
441
- let videoCodecs = shaka.util.StreamUtils.patchVp9(video.codecs);
443
+ let videoCodecs =
444
+ shaka.util.StreamUtils.getCorrectVideoCodecs_(video.codecs);
442
445
  // For multiplexed streams. Here we must check the audio of the
443
446
  // stream to see if it is compatible.
444
447
  if (video.codecs.includes(',')) {
445
448
  const allCodecs = video.codecs.split(',');
446
449
  videoCodecs = shaka.util.ManifestParserUtils.guessCodecs(
447
450
  ContentType.VIDEO, allCodecs);
448
- const audioCodecs = shaka.util.ManifestParserUtils.guessCodecs(
451
+ videoCodecs =
452
+ shaka.util.StreamUtils.getCorrectVideoCodecs_(videoCodecs);
453
+ let audioCodecs = shaka.util.ManifestParserUtils.guessCodecs(
449
454
  ContentType.AUDIO, allCodecs);
455
+ audioCodecs =
456
+ shaka.util.StreamUtils.getCorrectAudioCodecs_(audioCodecs);
450
457
  const audioFullType = shaka.util.MimeUtils.getFullOrConvertedType(
451
458
  video.mimeType, audioCodecs, ContentType.AUDIO);
452
- if (!MediaSource.isTypeSupported(audioFullType)) {
459
+ if (!Capabilities.isTypeSupported(audioFullType)) {
453
460
  return false;
454
461
  }
455
462
  }
456
463
  const fullType = shaka.util.MimeUtils.getFullOrConvertedType(
457
464
  video.mimeType, videoCodecs, ContentType.VIDEO);
458
- if (!MediaSource.isTypeSupported(fullType)) {
465
+ if (!Capabilities.isTypeSupported(fullType)) {
459
466
  return false;
460
467
  }
461
468
  }
@@ -465,7 +472,7 @@ shaka.util.StreamUtils = class {
465
472
  shaka.util.StreamUtils.getCorrectAudioCodecs_(audio.codecs);
466
473
  const fullType = shaka.util.MimeUtils.getFullOrConvertedType(
467
474
  audio.mimeType, codecs, ContentType.AUDIO);
468
- if (!MediaSource.isTypeSupported(fullType)) {
475
+ if (!Capabilities.isTypeSupported(fullType)) {
469
476
  return false;
470
477
  }
471
478
  }
@@ -566,7 +573,8 @@ shaka.util.StreamUtils = class {
566
573
  const allCodecs = video.codecs.split(',');
567
574
  videoCodecs = shaka.util.ManifestParserUtils.guessCodecs(
568
575
  ContentType.VIDEO, allCodecs);
569
- videoCodecs = shaka.util.StreamUtils.patchVp9(videoCodecs);
576
+ videoCodecs =
577
+ shaka.util.StreamUtils.getCorrectVideoCodecs_(videoCodecs);
570
578
  const audioCodecs = shaka.util.ManifestParserUtils.guessCodecs(
571
579
  ContentType.AUDIO, allCodecs);
572
580
 
@@ -580,7 +588,7 @@ shaka.util.StreamUtils = class {
580
588
  spatialRendering: false,
581
589
  };
582
590
  }
583
- videoCodecs = shaka.util.StreamUtils.patchVp9(videoCodecs);
591
+ videoCodecs = shaka.util.StreamUtils.getCorrectVideoCodecs_(videoCodecs);
584
592
  const fullType = shaka.util.MimeUtils.getFullOrConvertedType(
585
593
  video.mimeType, videoCodecs, ContentType.VIDEO);
586
594
  // VideoConfiguration
@@ -749,15 +757,29 @@ shaka.util.StreamUtils = class {
749
757
 
750
758
 
751
759
  /**
752
- * MediaCapabilities supports 'vp09...' codecs, but not 'vp9'. Translate vp9
753
- * codec strings into 'vp09...', to allow such content to play with
754
- * mediaCapabilities enabled.
755
- *
760
+ * Generates the correct video codec for MediaDecodingConfiguration and
761
+ * for MediaSource.isTypeSupported.
756
762
  * @param {string} codec
757
763
  * @return {string}
764
+ * @private
758
765
  */
759
- static patchVp9(codec) {
760
- if (codec == 'vp9') {
766
+ static getCorrectVideoCodecs_(codec) {
767
+ if (codec.includes('avc1')) {
768
+ // Convert avc1 codec string from RFC-4281 to RFC-6381 for
769
+ // MediaSource.isTypeSupported
770
+ // Example, convert avc1.66.30 to avc1.42001e (0x42 == 66 and 0x1e == 30)
771
+ const avcdata = codec.split('.');
772
+ if (avcdata.length == 3) {
773
+ let result = avcdata.shift() + '.';
774
+ result += parseInt(avcdata.shift(), 10).toString(16);
775
+ result +=
776
+ ('000' + parseInt(avcdata.shift(), 10).toString(16)).slice(-4);
777
+ return result;
778
+ }
779
+ } else if (codec == 'vp9') {
780
+ // MediaCapabilities supports 'vp09...' codecs, but not 'vp9'. Translate
781
+ // vp9 codec strings into 'vp09...', to allow such content to play with
782
+ // mediaCapabilities enabled.
761
783
  // This means profile 0, level 4.1, 8-bit color. This supports 1080p @
762
784
  // 60Hz. See https://en.wikipedia.org/wiki/VP9#Levels
763
785
  //
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "shaka-player",
3
3
  "description": "DASH/EME video player library",
4
- "version": "4.3.0",
4
+ "version": "4.3.1",
5
5
  "homepage": "https://github.com/shaka-project/shaka-player",
6
6
  "author": "Google",
7
7
  "maintainers": [
@@ -28,9 +28,9 @@
28
28
  "eslint-plugin-shaka-rules": "file:./build/eslint-plugin-shaka-rules",
29
29
  "esprima": "^4.0.1",
30
30
  "fontfaceonload": "^1.0.2",
31
- "google-closure-compiler-java": "^20220301.0.0",
32
- "google-closure-deps": "https://gitpkg.now.sh/google/closure-library/closure-deps?d7736da6",
33
- "google-closure-library": "^20220301.0.0",
31
+ "google-closure-compiler-java": "^20221102.0.0",
32
+ "google-closure-deps": "^20221102.0.0",
33
+ "google-closure-library": "^20221102.0.0",
34
34
  "htmlhint": "^1.1.3",
35
35
  "jasmine-ajax": "^4.0.0",
36
36
  "jimp": "^0.16.1",
@@ -32,6 +32,7 @@ goog.require('shaka.offline.OfflineManifestParser');
32
32
  goog.require('shaka.offline.OfflineScheme');
33
33
  goog.require('shaka.offline.Storage');
34
34
  goog.require('shaka.offline.indexeddb.StorageMechanism');
35
+ goog.require('shaka.polyfill.AbortController');
35
36
  goog.require('shaka.polyfill.Aria');
36
37
  goog.require('shaka.polyfill.EncryptionScheme');
37
38
  goog.require('shaka.polyfill.Fullscreen');
@@ -115,7 +115,9 @@ describe('Ad manager', () => {
115
115
  startedEventB = /** @type {!google.ima.AdEvent} */ (
116
116
  new shaka.util.FakeEvent(google.ima.AdEvent.Type.STARTED));
117
117
  startedEventB.getAd = () => {
118
- return {};
118
+ return {
119
+ isLinear: () => true,
120
+ };
119
121
  };
120
122
  }
121
123
  makeMocks();
@@ -2198,6 +2198,7 @@ describe('DashParser Manifest', () => {
2198
2198
  /** @type {shaka.extern.Manifest} */
2199
2199
  const manifest = await parser.start('dummy://foo', playerInterface);
2200
2200
  expect(manifest.imageStreams.length).toBe(1);
2201
+ expect(manifest.presentationTimeline.getMaxSegmentDuration()).toBe(1);
2201
2202
  const imageStream = manifest.imageStreams[0];
2202
2203
  expect(imageStream.width).toBe(1024);
2203
2204
  expect(imageStream.height).toBe(1152);