shaka-player 4.3.0 → 4.3.2

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 (78) hide show
  1. package/.github/workflows/build-and-test.yaml +20 -0
  2. package/AUTHORS +2 -0
  3. package/CHANGELOG.md +44 -0
  4. package/CONTRIBUTORS +2 -0
  5. package/README.md +1 -1
  6. package/conditional/dummy_cast_proxy.js +120 -0
  7. package/dist/demo.compiled.debug.js +104 -110
  8. package/dist/demo.compiled.debug.map +3 -3
  9. package/dist/demo.compiled.js +104 -110
  10. package/dist/demo.compiled.map +3 -3
  11. package/dist/deps.js +8 -6
  12. package/dist/locales.js +32 -32
  13. package/dist/receiver.compiled.debug.js +54 -60
  14. package/dist/receiver.compiled.debug.map +3 -3
  15. package/dist/receiver.compiled.js +54 -60
  16. package/dist/receiver.compiled.map +3 -3
  17. package/dist/shaka-player.compiled.d.ts +19 -2
  18. package/dist/shaka-player.compiled.debug.d.ts +19 -2
  19. package/dist/shaka-player.compiled.debug.externs.js +102 -80
  20. package/dist/shaka-player.compiled.debug.js +657 -652
  21. package/dist/shaka-player.compiled.debug.map +4 -4
  22. package/dist/shaka-player.compiled.externs.js +102 -80
  23. package/dist/shaka-player.compiled.js +904 -901
  24. package/dist/shaka-player.compiled.map +4 -4
  25. package/dist/shaka-player.ui.d.ts +19 -2
  26. package/dist/shaka-player.ui.debug.d.ts +19 -2
  27. package/dist/shaka-player.ui.debug.externs.js +102 -80
  28. package/dist/shaka-player.ui.debug.js +703 -698
  29. package/dist/shaka-player.ui.debug.map +4 -4
  30. package/dist/shaka-player.ui.externs.js +102 -80
  31. package/dist/shaka-player.ui.js +1029 -1026
  32. package/dist/shaka-player.ui.map +4 -4
  33. package/docs/tutorials/welcome.md +2 -2
  34. package/externs/shaka/mp4_parser.js +3 -0
  35. package/externs/shaka/player.js +2 -1
  36. package/lib/ads/ad_manager.js +2 -2
  37. package/lib/cea/cea708_window.js +2 -2
  38. package/lib/cea/mp4_cea_parser.js +37 -16
  39. package/lib/dash/segment_template.js +1 -1
  40. package/lib/hls/hls_parser.js +49 -23
  41. package/lib/media/content_workarounds.js +12 -0
  42. package/lib/media/media_source_capabilities.js +36 -0
  43. package/lib/media/media_source_engine.js +28 -7
  44. package/lib/media/segment_reference.js +3 -0
  45. package/lib/media/streaming_engine.js +33 -10
  46. package/lib/media/transmuxer.js +6 -4
  47. package/lib/player.js +10 -6
  48. package/lib/polyfill/abort_controller.js +143 -0
  49. package/lib/polyfill/media_capabilities.js +65 -11
  50. package/lib/polyfill/patchedmediakeys_apple.js +33 -28
  51. package/lib/text/ttml_text_parser.js +32 -7
  52. package/lib/text/ui_text_displayer.js +17 -0
  53. package/lib/util/mp4_box_parsers.js +24 -0
  54. package/lib/util/mp4_parser.js +35 -0
  55. package/lib/util/player_configuration.js +10 -3
  56. package/lib/util/stream_utils.js +35 -13
  57. package/package.json +4 -4
  58. package/shaka-player.uncompiled.js +1 -0
  59. package/test/ads/ad_manager_unit.js +3 -1
  60. package/test/dash/dash_parser_manifest_unit.js +1 -0
  61. package/test/hls/hls_parser_unit.js +132 -0
  62. package/test/media/drm_engine_unit.js +11 -1
  63. package/test/media/media_source_engine_integration.js +78 -2
  64. package/test/player_integration.js +10 -3
  65. package/test/polyfill/media_capabilities_unit.js +259 -0
  66. package/test/polyfill/patchedmediakeys_apple_unit.js +6 -0
  67. package/test/test/boot.js +29 -11
  68. package/test/test/util/manifest_parser_util.js +4 -3
  69. package/test/test/util/stream_generator.js +212 -8
  70. package/test/test/util/test_scheme.js +6 -5
  71. package/test/test/util/util.js +3 -2
  72. package/test/text/mp4_ttml_parser_unit.js +6 -4
  73. package/test/text/ttml_text_parser_unit.js +119 -86
  74. package/test/text/ui_text_displayer_unit.js +76 -0
  75. package/test/util/stream_utils_unit.js +18 -0
  76. package/test/util/xml_utils_unit.js +6 -1
  77. package/third_party/closure-uri/uri.js +1 -1
  78. package/ui/controls.js +49 -36
@@ -0,0 +1,143 @@
1
+ /*! @license
2
+ * Shaka Player
3
+ * Copyright 2016 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ goog.provide('shaka.polyfill.AbortController');
8
+
9
+ goog.require('shaka.polyfill');
10
+ goog.require('shaka.util.FakeEvent');
11
+ goog.require('shaka.util.FakeEventTarget');
12
+
13
+ /**
14
+ * @summary A polyfill for systems that do not implement AbortController.
15
+ * This is used both with the fetch API for HTTP requests and inside the HLS
16
+ * parser.
17
+ * @export
18
+ * @extends AbortController
19
+ */
20
+ shaka.polyfill.AbortController = class {
21
+ /**
22
+ * Install the polyfill if needed.
23
+ * @export
24
+ */
25
+ static install() {
26
+ if (window.AbortController) {
27
+ // Not needed.
28
+ return;
29
+ }
30
+
31
+ window.AbortController = shaka.polyfill.AbortController;
32
+ window.AbortSignal = shaka.polyfill.AbortController.AbortSignal;
33
+ }
34
+
35
+ /** */
36
+ constructor() {
37
+ /** @private {!shaka.polyfill.AbortController.AbortSignal} */
38
+ this.signal_ = new shaka.polyfill.AbortController.AbortSignal();
39
+ }
40
+
41
+ /**
42
+ * @override
43
+ * @suppress {const|duplicate} Since the original is defined as "const", we
44
+ * need this suppression to override it.
45
+ */
46
+ get signal() {
47
+ return this.signal_;
48
+ }
49
+
50
+ /**
51
+ * @param {*=} reason
52
+ * @override
53
+ */
54
+ abort(reason) {
55
+ this.signal_.doAbort_(reason);
56
+ }
57
+ };
58
+
59
+ /**
60
+ * @summary A polyfill for AbortSignal, part of the AbortController API.
61
+ * @implements {AbortSignal}
62
+ */
63
+ shaka.polyfill.AbortController.AbortSignal =
64
+ class extends shaka.util.FakeEventTarget {
65
+ /** */
66
+ constructor() {
67
+ super();
68
+
69
+ /** @private {boolean} */
70
+ this.aborted_ = false;
71
+
72
+ /** @private {*} */
73
+ this.reason_ = undefined;
74
+
75
+ /** @type {?function(!Event)} */
76
+ this.onabort = null;
77
+ }
78
+
79
+ /** @override */
80
+ get aborted() {
81
+ return this.aborted_;
82
+ }
83
+
84
+ /** @return {*} */
85
+ get reason() {
86
+ return this.reason_;
87
+ }
88
+
89
+ /**
90
+ * @param {*} reason
91
+ * @private
92
+ */
93
+ doAbort_(reason) {
94
+ if (this.aborted_) {
95
+ return;
96
+ }
97
+
98
+ this.aborted_ = true;
99
+ this.reason_ = reason;
100
+ if (this.reason_ === undefined) {
101
+ // This is equivalent to a native implementation.
102
+ this.reason_ = new DOMException(
103
+ 'signal is aborted without reason', 'AbortError');
104
+ }
105
+
106
+ // According to MDN:
107
+ // "Event type - A generic Event with no added properties."
108
+ const event = new shaka.util.FakeEvent('abort');
109
+ if (this.onabort) {
110
+ this.onabort(event);
111
+ }
112
+ this.dispatchEvent(event);
113
+ }
114
+
115
+
116
+ /**
117
+ * @param {*=} reason
118
+ * @return {!AbortSignal}
119
+ */
120
+ static abort(reason) {
121
+ const signal = new shaka.polyfill.AbortController.AbortSignal();
122
+ signal.doAbort_(reason);
123
+ return signal;
124
+ }
125
+
126
+ /**
127
+ * @param {number} timeMs
128
+ * @return {!AbortSignal}
129
+ */
130
+ static timeout(timeMs) {
131
+ const signal = new shaka.polyfill.AbortController.AbortSignal();
132
+
133
+ window.setTimeout(() => {
134
+ // This is equivalent to a native implementation.
135
+ signal.doAbort_(new DOMException('signal timed out', 'TimeoutError'));
136
+ }, timeMs);
137
+
138
+ return signal;
139
+ }
140
+ };
141
+
142
+
143
+ shaka.polyfill.register(shaka.polyfill.AbortController.install);
@@ -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,