shaka-player 3.3.4 → 4.0.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 (201) hide show
  1. package/.github/config.yml +1 -0
  2. package/.github/workflows/build-and-test.yaml +0 -1
  3. package/.github/workflows/demo-version-index.yaml +8 -0
  4. package/.github/workflows/release-please.yaml +19 -3
  5. package/.github/workflows/selenium-lab-tests.yaml +0 -1
  6. package/.github/workflows/sync-labels.yaml +33 -0
  7. package/.github/workflows/update-issues.yaml +29 -0
  8. package/.github/workflows/validate-pr-title.yaml +35 -0
  9. package/AUTHORS +1 -0
  10. package/CHANGELOG.md +209 -28
  11. package/CONTRIBUTORS +1 -0
  12. package/README.md +10 -7
  13. package/app-engine/demo-version-index/README.md +3 -3
  14. package/app-engine/demo-version-index/app.yaml +27 -1
  15. package/app-engine/demo-version-index/generate.py +164 -0
  16. package/app-engine/demo-version-index/main.py +4 -155
  17. package/app-engine/demo-version-index/requirements.txt +0 -1
  18. package/build/README.md +2 -2
  19. package/build/compiler.py +1 -1
  20. package/build/generateTsDefs.py +0 -2
  21. package/build/misspellings.txt +1 -0
  22. package/build/shaka-lab.yaml +0 -2
  23. package/build/test.py +7 -1
  24. package/build/types/core +0 -1
  25. package/build/types/polyfill +0 -3
  26. package/demo/common/asset.js +17 -1
  27. package/demo/common/assets.js +29 -0
  28. package/demo/common/message_ids.js +6 -3
  29. package/demo/config.js +10 -11
  30. package/demo/custom.js +39 -0
  31. package/demo/demo_utils.js +0 -1
  32. package/demo/locales/en.json +6 -3
  33. package/demo/locales/source.json +22 -10
  34. package/demo/main.js +9 -6
  35. package/demo/search.js +2 -1
  36. package/dist/controls.css +1 -1
  37. package/dist/demo.compiled.debug.js +71 -64
  38. package/dist/demo.compiled.debug.map +3 -3
  39. package/dist/demo.compiled.js +71 -64
  40. package/dist/demo.compiled.map +3 -3
  41. package/dist/demo.css +1 -1
  42. package/dist/demo.css.map +1 -1
  43. package/dist/deps.js +16 -20
  44. package/dist/locales.js +33 -31
  45. package/dist/receiver.compiled.debug.js +32 -28
  46. package/dist/receiver.compiled.debug.map +3 -3
  47. package/dist/receiver.compiled.js +32 -28
  48. package/dist/receiver.compiled.map +3 -3
  49. package/dist/shaka-player.compiled.d.ts +78 -82
  50. package/dist/shaka-player.compiled.debug.d.ts +78 -82
  51. package/dist/shaka-player.compiled.debug.externs.js +127 -126
  52. package/dist/shaka-player.compiled.debug.js +471 -523
  53. package/dist/shaka-player.compiled.debug.map +4 -4
  54. package/dist/shaka-player.compiled.externs.js +127 -126
  55. package/dist/shaka-player.compiled.js +844 -858
  56. package/dist/shaka-player.compiled.map +4 -4
  57. package/dist/shaka-player.ui.d.ts +80 -82
  58. package/dist/shaka-player.ui.debug.d.ts +80 -82
  59. package/dist/shaka-player.ui.debug.externs.js +135 -126
  60. package/dist/shaka-player.ui.debug.js +531 -581
  61. package/dist/shaka-player.ui.debug.map +4 -4
  62. package/dist/shaka-player.ui.externs.js +135 -126
  63. package/dist/shaka-player.ui.js +991 -1001
  64. package/dist/shaka-player.ui.map +4 -4
  65. package/docs/tutorials/config.md +0 -2
  66. package/docs/tutorials/drm-config.md +5 -0
  67. package/docs/tutorials/fairplay.md +7 -22
  68. package/docs/tutorials/faq.md +0 -11
  69. package/docs/tutorials/index.json +2 -8
  70. package/docs/tutorials/plugins.md +1 -3
  71. package/docs/tutorials/upgrade.md +85 -11
  72. package/externs/polyfill.js +15 -0
  73. package/externs/shaka/manifest.js +5 -1
  74. package/externs/shaka/net.js +12 -0
  75. package/externs/shaka/offline.js +5 -1
  76. package/externs/shaka/player.js +39 -21
  77. package/externs/shaka/text.js +12 -12
  78. package/externs/sourcebuffer.js +15 -0
  79. package/karma.conf.js +4 -0
  80. package/lib/ads/client_side_ad.js +13 -2
  81. package/lib/ads/client_side_ad_manager.js +22 -4
  82. package/lib/cast/cast_utils.js +0 -1
  83. package/lib/dash/content_protection.js +85 -5
  84. package/lib/dash/dash_parser.js +3 -3
  85. package/lib/hls/hls_parser.js +391 -747
  86. package/lib/hls/manifest_text_parser.js +2 -3
  87. package/lib/media/drm_engine.js +102 -37
  88. package/lib/media/gap_jumping_controller.js +10 -45
  89. package/lib/media/media_source_engine.js +157 -31
  90. package/lib/media/playhead.js +2 -5
  91. package/lib/media/presentation_timeline.js +1 -2
  92. package/lib/media/segment_index.js +13 -46
  93. package/lib/media/segment_reference.js +47 -1
  94. package/lib/media/streaming_engine.js +28 -11
  95. package/lib/media/time_ranges_utils.js +4 -9
  96. package/lib/net/networking_engine.js +3 -0
  97. package/lib/offline/indexeddb/storage_mechanism.js +4 -2
  98. package/lib/offline/indexeddb/v1_storage_cell.js +1 -0
  99. package/lib/offline/indexeddb/v2_storage_cell.js +1 -0
  100. package/lib/offline/manifest_converter.js +1 -0
  101. package/lib/offline/storage.js +100 -132
  102. package/lib/player.js +181 -307
  103. package/lib/polyfill/media_capabilities.js +12 -6
  104. package/lib/polyfill/mediasource.js +0 -28
  105. package/lib/polyfill/patchedmediakeys_nop.js +2 -0
  106. package/lib/polyfill/patchedmediakeys_webkit.js +2 -0
  107. package/lib/text/cue.js +0 -6
  108. package/lib/text/lrc_text_parser.js +8 -0
  109. package/lib/text/mp4_ttml_parser.js +8 -0
  110. package/lib/text/mp4_vtt_parser.js +8 -0
  111. package/lib/text/sbv_text_parser.js +8 -0
  112. package/lib/text/simple_text_displayer.js +1 -7
  113. package/lib/text/srt_text_parser.js +8 -0
  114. package/lib/text/ssa_text_parser.js +8 -0
  115. package/lib/text/text_engine.js +19 -3
  116. package/lib/text/ttml_text_parser.js +8 -0
  117. package/lib/text/ui_text_displayer.js +1 -7
  118. package/lib/text/vtt_text_parser.js +27 -5
  119. package/lib/text/web_vtt_generator.js +1 -7
  120. package/lib/util/config_utils.js +25 -0
  121. package/lib/util/error.js +16 -13
  122. package/lib/util/fairplay_utils.js +109 -0
  123. package/lib/util/functional.js +0 -42
  124. package/lib/util/manifest_parser_utils.js +1 -1
  125. package/lib/util/mime_utils.js +50 -1
  126. package/lib/util/periods.js +5 -4
  127. package/lib/util/platform.js +45 -0
  128. package/lib/util/player_configuration.js +26 -20
  129. package/lib/util/pssh.js +23 -4
  130. package/lib/util/stream_utils.js +96 -24
  131. package/lib/util/uint8array_utils.js +0 -16
  132. package/lib/util/xml_utils.js +1 -1
  133. package/package.json +4 -1
  134. package/roadmap.md +19 -19
  135. package/shaka-player.uncompiled.js +0 -3
  136. package/test/cast/cast_utils_unit.js +1 -0
  137. package/test/dash/dash_parser_content_protection_unit.js +44 -0
  138. package/test/dash/dash_parser_manifest_unit.js +0 -24
  139. package/test/dash/dash_parser_segment_template_unit.js +3 -4
  140. package/test/demo/demo_unit.js +2 -1
  141. package/test/hls/hls_live_unit.js +121 -220
  142. package/test/hls/hls_parser_unit.js +515 -432
  143. package/test/hls/manifest_text_parser_unit.js +19 -0
  144. package/test/media/drm_engine_unit.js +86 -120
  145. package/test/media/media_source_engine_integration.js +26 -3
  146. package/test/media/media_source_engine_unit.js +7 -6
  147. package/test/media/playhead_unit.js +27 -191
  148. package/test/media/segment_index_unit.js +15 -25
  149. package/test/media/streaming_engine_integration.js +4 -39
  150. package/test/media/streaming_engine_unit.js +3 -2
  151. package/test/media/time_ranges_utils_unit.js +0 -5
  152. package/test/offline/indexeddb_storage_unit.js +1 -1
  153. package/test/offline/manifest_convert_unit.js +34 -4
  154. package/test/offline/offline_manifest_parser_unit.js +1 -0
  155. package/test/offline/storage_compatibility_unit.js +1 -1
  156. package/test/offline/storage_integration.js +5 -4
  157. package/test/player_external.js +7 -7
  158. package/test/player_integration.js +22 -10
  159. package/test/player_src_equals_integration.js +81 -83
  160. package/test/player_unit.js +124 -1
  161. package/test/test/boot.js +9 -0
  162. package/test/test/externs/filters.js +4 -0
  163. package/test/test/util/fake_media_source_engine.js +11 -4
  164. package/test/test/util/manifest_generator.js +3 -1
  165. package/test/test/util/manifest_parser_util.js +4 -1
  166. package/test/test/util/offline_utils.js +1 -0
  167. package/test/test/util/simple_fakes.js +31 -2
  168. package/test/test/util/streaming_engine_util.js +1 -0
  169. package/test/test/util/test_scheme.js +24 -1
  170. package/test/test/util/util.js +4 -4
  171. package/test/text/cue_integration.js +2 -2
  172. package/test/text/lrc_text_parser_unit.js +6 -7
  173. package/test/text/mp4_ttml_parser_unit.js +6 -4
  174. package/test/text/mp4_vtt_parser_unit.js +6 -5
  175. package/test/text/sbv_text_parser_unit.js +5 -6
  176. package/test/text/srt_text_parser_unit.js +5 -6
  177. package/test/text/ssa_text_parser_unit.js +17 -29
  178. package/test/text/text_engine_unit.js +45 -5
  179. package/test/text/ttml_text_parser_unit.js +77 -77
  180. package/test/text/vtt_text_parser_unit.js +116 -59
  181. package/test/ui/ui_integration.js +0 -16
  182. package/test/ui/ui_unit.js +27 -0
  183. package/test/util/cmcd_manager_unit.js +3 -0
  184. package/test/util/mime_utils_unit.js +54 -0
  185. package/test/util/periods_unit.js +79 -0
  186. package/test/util/stream_utils_unit.js +23 -7
  187. package/ui/controls.js +65 -40
  188. package/ui/fullscreen_button.js +26 -6
  189. package/ui/less/containers.less +3 -3
  190. package/ui/locales/en.json +1 -0
  191. package/ui/locales/source.json +5 -0
  192. package/ui/resolution_selection.js +31 -33
  193. package/ui/ui.js +0 -17
  194. package/docs/tutorials/gsoc-ideas-list.md +0 -3
  195. package/docs/tutorials/upgrade-v2-5.md +0 -441
  196. package/docs/tutorials/upgrade-v3.md +0 -38
  197. package/lib/polyfill/mathround.js +0 -49
  198. package/lib/polyfill/patchedmediakeys_apple.js +0 -744
  199. package/lib/polyfill/patchedmediakeys_ms.js +0 -698
  200. package/lib/util/mutex.js +0 -52
  201. package/test/util/functional_unit.js +0 -82
@@ -24,8 +24,8 @@ shaka.polyfill.MediaCapabilities = class {
24
24
  * @export
25
25
  */
26
26
  static install() {
27
- // Since MediaCapabilities is not fully supported on Chromecast yet, we
28
- // should always install polyfill for Chromecast.
27
+ // Since MediaCapabilities is not fully supported on some Chromecast yet,
28
+ // we should always install polyfill for all Chromecast not Android-based.
29
29
  // TODO: re-evaluate MediaCapabilities in the future versions of Chromecast.
30
30
  // Since MediaCapabilities implementation is buggy in Apple browsers, we
31
31
  // should always install polyfill for Apple browsers.
@@ -37,10 +37,16 @@ shaka.polyfill.MediaCapabilities = class {
37
37
  // See: https://github.com/shaka-project/shaka-player/issues/3582
38
38
  // TODO: re-evaluate MediaCapabilities in the future versions of PS5
39
39
  // Browsers.
40
- if (!shaka.util.Platform.isChromecast() &&
41
- !shaka.util.Platform.isApple() &&
42
- !shaka.util.Platform.isPS5() &&
43
- navigator.mediaCapabilities) {
40
+ let canUseNativeMCap = true;
41
+ if (shaka.util.Platform.isApple() ||
42
+ shaka.util.Platform.isPS5() ||
43
+ shaka.util.Platform.isChromecast()) {
44
+ canUseNativeMCap = false;
45
+ }
46
+ if (shaka.util.Platform.isAndroidCastDevice()) {
47
+ canUseNativeMCap = true;
48
+ }
49
+ if (canUseNativeMCap && navigator.mediaCapabilities) {
44
50
  shaka.log.info(
45
51
  'MediaCapabilities: Native mediaCapabilities support found.');
46
52
  return;
@@ -38,11 +38,6 @@ shaka.polyfill.MediaSource = class {
38
38
  // Chromecast cannot make accurate determinations via isTypeSupported.
39
39
  shaka.polyfill.MediaSource.patchCastIsTypeSupported_();
40
40
  } else if (safariVersion) {
41
- // TS content is broken on Safari in general.
42
- // See https://github.com/shaka-project/shaka-player/issues/743
43
- // and https://bugs.webkit.org/show_bug.cgi?id=165342
44
- shaka.polyfill.MediaSource.rejectTsContent_();
45
-
46
41
  // NOTE: shaka.Player.isBrowserSupported() has its own restrictions on
47
42
  // Safari version.
48
43
  if (safariVersion <= 12) {
@@ -127,29 +122,6 @@ shaka.polyfill.MediaSource = class {
127
122
  };
128
123
  }
129
124
 
130
- /**
131
- * Patch isTypeSupported() to reject TS content. Used to avoid TS-related MSE
132
- * bugs on Safari.
133
- *
134
- * @private
135
- */
136
- static rejectTsContent_() {
137
- const originalIsTypeSupported = MediaSource.isTypeSupported;
138
-
139
- MediaSource.isTypeSupported = (mimeType) => {
140
- // Parse the basic MIME type from its parameters.
141
- const pieces = mimeType.split(/ *; */);
142
- const basicMimeType = pieces[0];
143
- const container = basicMimeType.split('/')[1];
144
-
145
- if (container.toLowerCase() == 'mp2t') {
146
- return false;
147
- }
148
-
149
- return originalIsTypeSupported(mimeType);
150
- };
151
- }
152
-
153
125
  /**
154
126
  * Patch |MediaSource.isTypeSupported| to always reject |codec|. This is used
155
127
  * when we know that we are on a platform that does not work well with a given
@@ -49,6 +49,8 @@ shaka.polyfill.PatchedMediaKeysNop = class {
49
49
  // These are not usable, but allow Player.isBrowserSupported to pass.
50
50
  window.MediaKeys = PatchedMediaKeysNop.MediaKeys;
51
51
  window.MediaKeySystemAccess = PatchedMediaKeysNop.MediaKeySystemAccess;
52
+
53
+ window.shakaMediaKeysPolyfill = true;
52
54
  }
53
55
 
54
56
  /**
@@ -72,6 +72,8 @@ shaka.polyfill.PatchedMediaKeysWebkit = class {
72
72
  PatchedMediaKeysWebkit.setMediaKeys;
73
73
  window.MediaKeys = PatchedMediaKeysWebkit.MediaKeys;
74
74
  window.MediaKeySystemAccess = PatchedMediaKeysWebkit.MediaKeySystemAccess;
75
+
76
+ window.shakaMediaKeysPolyfill = true;
75
77
  }
76
78
 
77
79
  /**
package/lib/text/cue.js CHANGED
@@ -234,12 +234,6 @@ shaka.text.Cue = class {
234
234
  */
235
235
  this.lineBreak = false;
236
236
 
237
- /**
238
- * @override
239
- * @exportInterface
240
- */
241
- this.spacer = false;
242
-
243
237
  /**
244
238
  * @override
245
239
  * @exportInterface
@@ -28,6 +28,14 @@ shaka.text.LrcTextParser = class {
28
28
  goog.asserts.assert(false, 'LRC does not have init segments');
29
29
  }
30
30
 
31
+ /**
32
+ * @override
33
+ * @export
34
+ */
35
+ setSequenceMode(sequenceMode) {
36
+ // Unused.
37
+ }
38
+
31
39
  /**
32
40
  * @override
33
41
  * @export
@@ -55,6 +55,14 @@ shaka.text.Mp4TtmlParser = class {
55
55
  }
56
56
  }
57
57
 
58
+ /**
59
+ * @override
60
+ * @export
61
+ */
62
+ setSequenceMode(sequenceMode) {
63
+ // Unused.
64
+ }
65
+
58
66
  /**
59
67
  * @override
60
68
  * @export
@@ -84,6 +84,14 @@ shaka.text.Mp4VttParser = class {
84
84
  }
85
85
  }
86
86
 
87
+ /**
88
+ * @override
89
+ * @export
90
+ */
91
+ setSequenceMode(sequenceMode) {
92
+ // Unused.
93
+ }
94
+
87
95
  /**
88
96
  * @override
89
97
  * @export
@@ -27,6 +27,14 @@ shaka.text.SbvTextParser = class {
27
27
  goog.asserts.assert(false, 'SubViewer does not have init segments');
28
28
  }
29
29
 
30
+ /**
31
+ * @override
32
+ * @export
33
+ */
34
+ setSequenceMode(sequenceMode) {
35
+ // Unused.
36
+ }
37
+
30
38
  /**
31
39
  * @override
32
40
  * @export
@@ -12,7 +12,6 @@
12
12
  goog.provide('shaka.text.SimpleTextDisplayer');
13
13
 
14
14
  goog.require('goog.asserts');
15
- goog.require('shaka.Deprecate');
16
15
  goog.require('shaka.log');
17
16
  goog.require('shaka.text.Cue');
18
17
 
@@ -108,12 +107,7 @@ shaka.text.SimpleTextDisplayer = class {
108
107
  return `${acc}</${tag}>`;
109
108
  }, '');
110
109
 
111
- if (cue.lineBreak || cue.spacer) {
112
- if (cue.spacer) {
113
- shaka.Deprecate.deprecateFeature(4,
114
- 'shaka.extern.Cue',
115
- 'Please use lineBreak instead of spacer.');
116
- }
110
+ if (cue.lineBreak) {
117
111
  // This is a vertical lineBreak, so insert a newline.
118
112
  return '\n';
119
113
  } else if (cue.nestedCues.length) {
@@ -35,6 +35,14 @@ shaka.text.SrtTextParser = class {
35
35
  goog.asserts.assert(false, 'SRT does not have init segments');
36
36
  }
37
37
 
38
+ /**
39
+ * @override
40
+ * @export
41
+ */
42
+ setSequenceMode(sequenceMode) {
43
+ // Unused.
44
+ }
45
+
38
46
  /**
39
47
  * @override
40
48
  * @export
@@ -28,6 +28,14 @@ shaka.text.SsaTextParser = class {
28
28
  goog.asserts.assert(false, 'SSA does not have init segments');
29
29
  }
30
30
 
31
+ /**
32
+ * @override
33
+ * @export
34
+ */
35
+ setSequenceMode(sequenceMode) {
36
+ // Unused.
37
+ }
38
+
31
39
  /**
32
40
  * @override
33
41
  * @export
@@ -7,9 +7,9 @@
7
7
  goog.provide('shaka.text.TextEngine');
8
8
 
9
9
  goog.require('goog.asserts');
10
+ goog.require('shaka.log');
10
11
  goog.require('shaka.text.Cue');
11
12
  goog.require('shaka.util.BufferUtils');
12
- goog.require('shaka.util.Functional');
13
13
  goog.require('shaka.util.IDestroyable');
14
14
  goog.require('shaka.util.MimeUtils');
15
15
  goog.requireType('shaka.cea.ICaptionDecoder');
@@ -30,6 +30,9 @@ shaka.text.TextEngine = class {
30
30
  /** @private {shaka.extern.TextDisplayer} */
31
31
  this.displayer_ = displayer;
32
32
 
33
+ /** @private {boolean} */
34
+ this.segmentRelativeVttTiming_ = false;
35
+
33
36
  /** @private {number} */
34
37
  this.timestampOffset_ = 0;
35
38
 
@@ -128,8 +131,10 @@ shaka.text.TextEngine = class {
128
131
  * called at least once before appendBuffer.
129
132
  *
130
133
  * @param {string} mimeType
134
+ * @param {boolean} sequenceMode
135
+ * @param {boolean} segmentRelativeVttTiming
131
136
  */
132
- initParser(mimeType) {
137
+ initParser(mimeType, sequenceMode, segmentRelativeVttTiming) {
133
138
  // No parser for CEA, which is extracted from video and side-loaded
134
139
  // into TextEngine and TextDisplayer.
135
140
  if (mimeType == shaka.util.MimeUtils.CEA608_CLOSED_CAPTION_MIMETYPE ||
@@ -140,7 +145,14 @@ shaka.text.TextEngine = class {
140
145
  const factory = shaka.text.TextEngine.parserMap_[mimeType];
141
146
  goog.asserts.assert(
142
147
  factory, 'Text type negotiation should have happened already');
143
- this.parser_ = shaka.util.Functional.callFactory(factory);
148
+ this.parser_ = factory();
149
+ if (this.parser_.setSequenceMode) {
150
+ this.parser_.setSequenceMode(sequenceMode);
151
+ } else {
152
+ shaka.log.alwaysWarn(
153
+ 'Text parsers should have a "setSequenceMode" method!');
154
+ }
155
+ this.segmentRelativeVttTiming_ = segmentRelativeVttTiming;
144
156
  }
145
157
 
146
158
  /**
@@ -166,11 +178,15 @@ shaka.text.TextEngine = class {
166
178
  return;
167
179
  }
168
180
 
181
+ const vttOffset = this.segmentRelativeVttTiming_ ?
182
+ startTime : this.timestampOffset_;
183
+
169
184
  /** @type {shaka.extern.TextParser.TimeContext} **/
170
185
  const time = {
171
186
  periodStart: this.timestampOffset_,
172
187
  segmentStart: startTime,
173
188
  segmentEnd: endTime,
189
+ vttOffset: vttOffset,
174
190
  };
175
191
 
176
192
  // Parse the buffer and add the new cues.
@@ -30,6 +30,14 @@ shaka.text.TtmlTextParser = class {
30
30
  goog.asserts.assert(false, 'TTML does not have init segments');
31
31
  }
32
32
 
33
+ /**
34
+ * @override
35
+ * @export
36
+ */
37
+ setSequenceMode(sequenceMode) {
38
+ // Unused.
39
+ }
40
+
33
41
  /**
34
42
  * @override
35
43
  * @export
@@ -8,7 +8,6 @@
8
8
  goog.provide('shaka.text.UITextDisplayer');
9
9
 
10
10
  goog.require('goog.asserts');
11
- goog.require('shaka.Deprecate');
12
11
  goog.require('shaka.text.Cue');
13
12
  goog.require('shaka.text.CueRegion');
14
13
  goog.require('shaka.util.Dom');
@@ -343,12 +342,7 @@ shaka.text.UITextDisplayer = class {
343
342
  createCue_(cue, parents) {
344
343
  const isNested = parents.length > 1;
345
344
  let type = isNested ? 'span' : 'div';
346
- if (cue.lineBreak || cue.spacer) {
347
- if (cue.spacer) {
348
- shaka.Deprecate.deprecateFeature(4,
349
- 'shaka.extern.Cue',
350
- 'Please use lineBreak instead of spacer.');
351
- }
345
+ if (cue.lineBreak) {
352
346
  type = 'br';
353
347
  }
354
348
 
@@ -22,6 +22,12 @@ goog.require('shaka.util.XmlUtils');
22
22
  * @export
23
23
  */
24
24
  shaka.text.VttTextParser = class {
25
+ /** Constructs a VTT parser. */
26
+ constructor() {
27
+ /** @private {boolean} */
28
+ this.sequenceMode_ = false;
29
+ }
30
+
25
31
  /**
26
32
  * @override
27
33
  * @export
@@ -30,6 +36,14 @@ shaka.text.VttTextParser = class {
30
36
  goog.asserts.assert(false, 'VTT does not have init segments');
31
37
  }
32
38
 
39
+ /**
40
+ * @override
41
+ * @export
42
+ */
43
+ setSequenceMode(sequenceMode) {
44
+ this.sequenceMode_ = sequenceMode;
45
+ }
46
+
33
47
  /**
34
48
  * @override
35
49
  * @export
@@ -48,11 +62,21 @@ shaka.text.VttTextParser = class {
48
62
  shaka.util.Error.Code.INVALID_TEXT_HEADER);
49
63
  }
50
64
 
65
+ // Depending on "segmentRelativeVttTiming" configuration,
66
+ // "vttOffset" will correspond to either "periodStart" (default)
67
+ // or "segmentStart", for segmented VTT where timings are relative
68
+ // to the beginning of each segment.
51
69
  // NOTE: "periodStart" is the timestamp offset applied via TextEngine.
52
70
  // It is no longer closely tied to periods, but the name stuck around.
53
- let offset = time.periodStart;
54
-
55
- if (blocks[0].includes('X-TIMESTAMP-MAP')) {
71
+ // NOTE: This offset and the flag choosing its meaning have no effect on
72
+ // HLS content, which should use X-TIMESTAMP-MAP and periodStart instead.
73
+ let offset = time.vttOffset;
74
+
75
+ // Only use 'X-TIMESTAMP-MAP' in sequence mode, as that is currently
76
+ // shorthand for HLS. Note that an offset based on the first video
77
+ // timestamp has already been extracted, and appears in periodStart.
78
+ // The relative offset from X-TIMESTAMP-MAP will be added to that for HLS.
79
+ if (blocks[0].includes('X-TIMESTAMP-MAP') && this.sequenceMode_) {
56
80
  // https://bit.ly/2K92l7y
57
81
  // The 'X-TIMESTAMP-MAP' header is used in HLS to align text with
58
82
  // the rest of the media.
@@ -87,8 +111,6 @@ shaka.text.VttTextParser = class {
87
111
  mpegTime += shaka.text.VttTextParser.TS_ROLLOVER_;
88
112
  }
89
113
 
90
- // Apple-encoded HLS content uses absolute timestamps, so assume the
91
- // presence of the map tag means the content uses absolute timestamps.
92
114
  offset = time.periodStart + mpegTime / mpegTimescale - cueTime;
93
115
  }
94
116
  }
@@ -6,7 +6,6 @@
6
6
 
7
7
  goog.provide('shaka.text.WebVttGenerator');
8
8
 
9
- goog.require('shaka.Deprecate');
10
9
  goog.require('shaka.text.Cue');
11
10
 
12
11
 
@@ -49,12 +48,7 @@ shaka.text.WebVttGenerator = class {
49
48
  return `${acc}</${tag}>`;
50
49
  }, '');
51
50
 
52
- if (cue.lineBreak || cue.spacer) {
53
- if (cue.spacer) {
54
- shaka.Deprecate.deprecateFeature(4,
55
- 'shaka.text.Cue',
56
- 'Please use lineBreak instead of spacer.');
57
- }
51
+ if (cue.lineBreak) {
58
52
  // This is a vertical lineBreak, so insert a newline.
59
53
  return '\n';
60
54
  } else if (cue.nestedCues.length) {
@@ -127,4 +127,29 @@ shaka.util.ConfigUtils = class {
127
127
  last[fieldName.substring(nameStart).replace(/\\\./g, '.')] = value;
128
128
  return configObject;
129
129
  }
130
+
131
+ /**
132
+ * Reference the input parameters so the compiler doesn't remove them from
133
+ * the calling function. Return whatever value is specified.
134
+ *
135
+ * This allows an empty or default implementation of a config callback that
136
+ * still bears the complete function signature even in compiled mode.
137
+ *
138
+ * The caller should look something like this:
139
+ *
140
+ * const callback = (a, b, c, d) => {
141
+ * return referenceParametersAndReturn(
142
+ [a, b, c, d],
143
+ a); // Can be anything, doesn't need to be one of the parameters
144
+ * };
145
+ *
146
+ * @param {!Array.<?>} parameters
147
+ * @param {T} returnValue
148
+ * @return {T}
149
+ * @template T
150
+ * @noinline
151
+ */
152
+ static referenceParametersAndReturn(parameters, returnValue) {
153
+ return parameters && returnValue;
154
+ }
130
155
  };
package/lib/util/error.js CHANGED
@@ -345,6 +345,13 @@ shaka.util.Error.Code = {
345
345
  */
346
346
  'MISSING_TEXT_PLUGIN': 2014,
347
347
 
348
+ /**
349
+ * The chapters track failed to load. The browser does not provide any
350
+ * information in this case to identify why it failed, but there may be
351
+ * details in the JavaScript console.
352
+ */
353
+ 'CHAPTERS_TRACK_FAILED': 2015,
354
+
348
355
  /**
349
356
  * Some component tried to read past the end of a buffer. The segment index,
350
357
  * init segment, or PSSH may be malformed.
@@ -581,13 +588,7 @@ shaka.util.Error.Code = {
581
588
 
582
589
  // RETIRED: 'HLS_COULD_NOT_GUESS_MIME_TYPE': 4021,
583
590
 
584
- /**
585
- * No Master Playlist has been provided. Master playlist provides
586
- * vital information about the streams (like codecs) that is
587
- * required for MediaSource. We don't support directly providing
588
- * a Media Playlist.
589
- */
590
- 'HLS_MASTER_PLAYLIST_NOT_PROVIDED': 4022,
591
+ // RETIRED: 'HLS_MASTER_PLAYLIST_NOT_PROVIDED': 4022,
591
592
 
592
593
  /**
593
594
  * One of the required attributes was not provided, so the
@@ -627,12 +628,7 @@ shaka.util.Error.Code = {
627
628
 
628
629
  // RETIRED: 'HLS_LIVE_CONTENT_NOT_SUPPORTED': 4029,
629
630
 
630
- /**
631
- * The HLS parser was unable to parse segment start time from the media.
632
- * <br> error.data[0] is the failed media playlist URI.
633
- * <br> error.data[1] is the failed media segment URI (if any).
634
- */
635
- 'HLS_COULD_NOT_PARSE_SEGMENT_START_TIME': 4030,
631
+ // RETIRED: 'HLS_COULD_NOT_PARSE_SEGMENT_START_TIME': 4030,
636
632
 
637
633
  // RETIRED: 'HLS_MEDIA_SEQUENCE_REQUIRED_IN_LIVE_STREAMS': 4031,
638
634
 
@@ -686,6 +682,13 @@ shaka.util.Error.Code = {
686
682
  */
687
683
  'HLS_VARIABLE_NOT_FOUND': 4039,
688
684
 
685
+ /**
686
+ * We do not support playing encrypted mp2t with MSE
687
+ */
688
+ 'HLS_MSE_ENCRYPTED_MP2T_NOT_SUPPORTED': 4040,
689
+
690
+ // RETIRED: 'HLS_MSE_ENCRYPTED_LEGACY_APPLE_MEDIA_KEYS_NOT_SUPPORTED': 4041,
691
+
689
692
 
690
693
  // RETIRED: 'INCONSISTENT_BUFFER_STATE': 5000,
691
694
  // RETIRED: 'INVALID_SEGMENT_INDEX': 5001,
@@ -8,9 +8,11 @@ goog.provide('shaka.util.FairPlayUtils');
8
8
 
9
9
  goog.require('goog.Uri');
10
10
  goog.require('goog.asserts');
11
+ goog.require('shaka.net.NetworkingEngine');
11
12
  goog.require('shaka.util.BufferUtils');
12
13
  goog.require('shaka.util.Error');
13
14
  goog.require('shaka.util.StringUtils');
15
+ goog.require('shaka.util.Uint8ArrayUtils');
14
16
 
15
17
 
16
18
  /**
@@ -18,6 +20,29 @@ goog.require('shaka.util.StringUtils');
18
20
  * @export
19
21
  */
20
22
  shaka.util.FairPlayUtils = class {
23
+ /**
24
+ * Check if FairPlay is supported.
25
+ *
26
+ * @return {!Promise.<boolean>}
27
+ * @export
28
+ */
29
+ static async isFairPlaySupported() {
30
+ const config = {
31
+ initDataTypes: ['cenc', 'sinf', 'skd'],
32
+ videoCapabilities: [
33
+ {
34
+ contentType: 'video/mp4; codecs="avc1.42E01E"',
35
+ },
36
+ ],
37
+ };
38
+ try {
39
+ await navigator.requestMediaKeySystemAccess('com.apple.fps', [config]);
40
+ return true;
41
+ } catch (err) {
42
+ return false;
43
+ }
44
+ }
45
+
21
46
  /**
22
47
  * Using the default method, extract a content ID from the init data. This is
23
48
  * based on the FairPlay example documentation.
@@ -106,4 +131,88 @@ shaka.util.FairPlayUtils = class {
106
131
  offset == rebuiltInitData.length, 'Inconsistent init data length');
107
132
  return rebuiltInitData;
108
133
  }
134
+
135
+ /**
136
+ * SPC FairPlay request.
137
+ *
138
+ * @param {shaka.net.NetworkingEngine.RequestType} type
139
+ * @param {shaka.extern.Request} request
140
+ * @export
141
+ */
142
+ static spcFairPlayRequest(type, request) {
143
+ if (type !== shaka.net.NetworkingEngine.RequestType.LICENSE) {
144
+ return;
145
+ }
146
+ const body = /** @type {!(ArrayBuffer|ArrayBufferView)} */(request.body);
147
+ const originalPayload = shaka.util.BufferUtils.toUint8(body);
148
+ const base64Payload = shaka.util.Uint8ArrayUtils.toBase64(originalPayload);
149
+ request.headers['Content-Type'] = 'application/x-www-form-urlencoded';
150
+ request.body = shaka.util.StringUtils.toUTF8('spc=' + base64Payload);
151
+ }
152
+
153
+ /**
154
+ * Common FairPlay response transform for some DRMs providers.
155
+ *
156
+ * @param {shaka.net.NetworkingEngine.RequestType} type
157
+ * @param {shaka.extern.Response} response
158
+ * @export
159
+ */
160
+ static commonFairPlayResponse(type, response) {
161
+ if (type !== shaka.net.NetworkingEngine.RequestType.LICENSE) {
162
+ return;
163
+ }
164
+
165
+ // In Apple's docs, responses can be of the form:
166
+ // '\n<ckc>base64encoded</ckc>\n' or 'base64encoded'
167
+ // We have also seen responses in JSON format from some of our partners.
168
+ // In all of these text-based formats, the CKC data is base64-encoded.
169
+
170
+ let responseText;
171
+ try {
172
+ // Convert it to text for further processing.
173
+ responseText = shaka.util.StringUtils.fromUTF8(response.data);
174
+ } catch (error) {
175
+ // Assume it's not a text format of any kind and leave it alone.
176
+ return;
177
+ }
178
+
179
+ let licenseProcessing = false;
180
+
181
+ // Trim whitespace.
182
+ responseText = responseText.trim();
183
+
184
+ // Look for <ckc> wrapper and remove it.
185
+ if (responseText.substr(0, 5) === '<ckc>' &&
186
+ responseText.substr(-6) === '</ckc>') {
187
+ responseText = responseText.slice(5, -6);
188
+ licenseProcessing = true;
189
+ }
190
+
191
+ // Look for a JSON wrapper and remove it.
192
+ try {
193
+ const responseObject = /** @type {!Object} */(JSON.parse(responseText));
194
+ if (responseObject['ckc']) {
195
+ responseText = responseObject['ckc'];
196
+ licenseProcessing = true;
197
+ }
198
+ if (responseObject['CkcMessage']) {
199
+ responseText = responseObject['CkcMessage'];
200
+ licenseProcessing = true;
201
+ }
202
+ if (responseObject['License']) {
203
+ responseText = responseObject['License'];
204
+ licenseProcessing = true;
205
+ }
206
+ } catch (err) {
207
+ // It wasn't JSON. Fall through with other transformations.
208
+ }
209
+
210
+ if (licenseProcessing) {
211
+ // Decode the base64-encoded data into the format the browser expects.
212
+ // It's not clear why FairPlay license servers don't just serve this
213
+ // directly.
214
+ response.data = shaka.util.BufferUtils.toArrayBuffer(
215
+ shaka.util.Uint8ArrayUtils.fromBase64(responseText));
216
+ }
217
+ }
109
218
  };
@@ -6,9 +6,6 @@
6
6
 
7
7
  goog.provide('shaka.util.Functional');
8
8
 
9
- goog.require('shaka.Deprecate');
10
-
11
-
12
9
  /**
13
10
  * @summary A set of functional utility functions.
14
11
  */
@@ -70,43 +67,4 @@ shaka.util.Functional = class {
70
67
  static isNotNull(value) {
71
68
  return value != null;
72
69
  }
73
-
74
- /**
75
- * Calls a factory function while allowing it to be a constructor for
76
- * reverse-compatibility.
77
- *
78
- * @param {function():!T} factory
79
- * @return {!T}
80
- * @template T
81
- */
82
- static callFactory(factory) {
83
- // See https://stackoverflow.com/q/10428603/1208502
84
- // eslint-disable-next-line no-restricted-syntax
85
- const obj = Object.create(factory.prototype || Object.prototype);
86
- // If this is a constructor, call it with our newly created object to
87
- // initialize it; if this isn't a constructor, the "this" shouldn't be used
88
- // since it should be "undefined".
89
- let ret;
90
- try {
91
- ret = factory.call(obj); // eslint-disable-line no-restricted-syntax
92
-
93
- // If it didn't return anything, assume it is a constructor and return our
94
- // "this" value instead.
95
- if (!ret) {
96
- shaka.Deprecate.deprecateFeature(4,
97
- 'Factories requiring new',
98
- 'Factories should be plain functions');
99
- ret = obj;
100
- }
101
- } catch (e) {
102
- // This was an ES6 class, so it threw a TypeError because we didn't use
103
- // "new". Fall back to actually using "new".
104
- shaka.Deprecate.deprecateFeature(4,
105
- 'Factories requiring new',
106
- 'Factories should be plain functions');
107
- const FactoryAsClass = /** @type {function(new: T)} */(factory);
108
- ret = new FactoryAsClass();
109
- }
110
- return ret;
111
- }
112
70
  };