shaka-player 3.3.4 → 4.0.0

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 (184) hide show
  1. package/.github/workflows/build-and-test.yaml +0 -1
  2. package/.github/workflows/demo-version-index.yaml +8 -0
  3. package/.github/workflows/release-please.yaml +23 -2
  4. package/.github/workflows/selenium-lab-tests.yaml +0 -1
  5. package/.github/workflows/sync-labels.yaml +33 -0
  6. package/.github/workflows/update-issues.yaml +29 -0
  7. package/.github/workflows/validate-pr-title.yaml +35 -0
  8. package/AUTHORS +1 -0
  9. package/CHANGELOG.md +198 -32
  10. package/CONTRIBUTORS +1 -0
  11. package/README.md +10 -7
  12. package/app-engine/demo-version-index/README.md +3 -3
  13. package/app-engine/demo-version-index/app.yaml +27 -1
  14. package/app-engine/demo-version-index/generate.py +164 -0
  15. package/app-engine/demo-version-index/main.py +4 -155
  16. package/app-engine/demo-version-index/requirements.txt +0 -1
  17. package/build/README.md +2 -2
  18. package/build/compiler.py +1 -1
  19. package/build/generateTsDefs.py +0 -2
  20. package/build/shaka-lab.yaml +0 -2
  21. package/build/test.py +7 -1
  22. package/build/types/core +0 -1
  23. package/build/types/polyfill +0 -3
  24. package/demo/common/asset.js +17 -1
  25. package/demo/common/assets.js +29 -0
  26. package/demo/common/message_ids.js +6 -3
  27. package/demo/config.js +10 -11
  28. package/demo/custom.js +39 -0
  29. package/demo/demo_utils.js +0 -1
  30. package/demo/locales/en.json +6 -3
  31. package/demo/locales/source.json +22 -10
  32. package/demo/main.js +9 -6
  33. package/demo/search.js +2 -1
  34. package/dist/controls.css +1 -1
  35. package/dist/demo.compiled.debug.js +71 -64
  36. package/dist/demo.compiled.debug.map +3 -3
  37. package/dist/demo.compiled.js +71 -64
  38. package/dist/demo.compiled.map +3 -3
  39. package/dist/demo.css +1 -1
  40. package/dist/demo.css.map +1 -1
  41. package/dist/deps.js +16 -20
  42. package/dist/locales.js +31 -29
  43. package/dist/receiver.compiled.debug.js +32 -28
  44. package/dist/receiver.compiled.debug.map +3 -3
  45. package/dist/receiver.compiled.js +32 -28
  46. package/dist/receiver.compiled.map +3 -3
  47. package/dist/shaka-player.compiled.d.ts +78 -83
  48. package/dist/shaka-player.compiled.debug.d.ts +78 -83
  49. package/dist/shaka-player.compiled.debug.externs.js +126 -126
  50. package/dist/shaka-player.compiled.debug.js +426 -483
  51. package/dist/shaka-player.compiled.debug.map +4 -4
  52. package/dist/shaka-player.compiled.externs.js +126 -126
  53. package/dist/shaka-player.compiled.js +838 -856
  54. package/dist/shaka-player.compiled.map +4 -4
  55. package/dist/shaka-player.ui.d.ts +80 -83
  56. package/dist/shaka-player.ui.debug.d.ts +80 -83
  57. package/dist/shaka-player.ui.debug.externs.js +134 -126
  58. package/dist/shaka-player.ui.debug.js +488 -543
  59. package/dist/shaka-player.ui.debug.map +4 -4
  60. package/dist/shaka-player.ui.externs.js +134 -126
  61. package/dist/shaka-player.ui.js +980 -994
  62. package/dist/shaka-player.ui.map +4 -4
  63. package/docs/tutorials/config.md +0 -2
  64. package/docs/tutorials/drm-config.md +5 -0
  65. package/docs/tutorials/fairplay.md +7 -22
  66. package/docs/tutorials/faq.md +0 -11
  67. package/docs/tutorials/index.json +2 -8
  68. package/docs/tutorials/plugins.md +1 -3
  69. package/docs/tutorials/upgrade.md +83 -11
  70. package/externs/polyfill.js +15 -0
  71. package/externs/shaka/manifest.js +5 -1
  72. package/externs/shaka/net.js +12 -0
  73. package/externs/shaka/offline.js +5 -1
  74. package/externs/shaka/player.js +39 -32
  75. package/externs/shaka/text.js +12 -12
  76. package/externs/sourcebuffer.js +15 -0
  77. package/lib/ads/client_side_ad.js +13 -2
  78. package/lib/ads/client_side_ad_manager.js +22 -4
  79. package/lib/cast/cast_utils.js +0 -1
  80. package/lib/dash/content_protection.js +85 -5
  81. package/lib/dash/dash_parser.js +1 -1
  82. package/lib/hls/hls_parser.js +378 -743
  83. package/lib/hls/manifest_text_parser.js +2 -3
  84. package/lib/media/drm_engine.js +102 -53
  85. package/lib/media/gap_jumping_controller.js +10 -45
  86. package/lib/media/media_source_engine.js +87 -15
  87. package/lib/media/playhead.js +2 -5
  88. package/lib/media/presentation_timeline.js +1 -2
  89. package/lib/media/segment_index.js +13 -46
  90. package/lib/media/segment_reference.js +47 -1
  91. package/lib/media/streaming_engine.js +29 -11
  92. package/lib/media/time_ranges_utils.js +4 -9
  93. package/lib/net/networking_engine.js +3 -0
  94. package/lib/offline/indexeddb/v1_storage_cell.js +1 -0
  95. package/lib/offline/indexeddb/v2_storage_cell.js +1 -0
  96. package/lib/offline/manifest_converter.js +1 -0
  97. package/lib/offline/storage.js +100 -132
  98. package/lib/player.js +17 -201
  99. package/lib/polyfill/media_capabilities.js +12 -6
  100. package/lib/polyfill/mediasource.js +0 -28
  101. package/lib/polyfill/patchedmediakeys_nop.js +2 -0
  102. package/lib/polyfill/patchedmediakeys_webkit.js +2 -0
  103. package/lib/text/cue.js +0 -6
  104. package/lib/text/lrc_text_parser.js +8 -0
  105. package/lib/text/mp4_ttml_parser.js +8 -0
  106. package/lib/text/mp4_vtt_parser.js +8 -0
  107. package/lib/text/sbv_text_parser.js +8 -0
  108. package/lib/text/simple_text_displayer.js +1 -7
  109. package/lib/text/srt_text_parser.js +8 -0
  110. package/lib/text/ssa_text_parser.js +8 -0
  111. package/lib/text/text_engine.js +19 -3
  112. package/lib/text/ttml_text_parser.js +8 -0
  113. package/lib/text/ui_text_displayer.js +1 -7
  114. package/lib/text/vtt_text_parser.js +24 -2
  115. package/lib/text/web_vtt_generator.js +1 -7
  116. package/lib/util/error.js +9 -13
  117. package/lib/util/fairplay_utils.js +109 -0
  118. package/lib/util/functional.js +0 -42
  119. package/lib/util/mime_utils.js +2 -0
  120. package/lib/util/platform.js +45 -0
  121. package/lib/util/player_configuration.js +12 -6
  122. package/lib/util/pssh.js +23 -4
  123. package/lib/util/stream_utils.js +92 -22
  124. package/lib/util/uint8array_utils.js +0 -16
  125. package/lib/util/xml_utils.js +1 -1
  126. package/package.json +1 -1
  127. package/roadmap.md +19 -19
  128. package/shaka-player.uncompiled.js +0 -3
  129. package/test/cast/cast_utils_unit.js +1 -0
  130. package/test/dash/dash_parser_content_protection_unit.js +44 -0
  131. package/test/dash/dash_parser_manifest_unit.js +0 -24
  132. package/test/dash/dash_parser_segment_template_unit.js +3 -4
  133. package/test/demo/demo_unit.js +2 -1
  134. package/test/hls/hls_live_unit.js +99 -220
  135. package/test/hls/hls_parser_unit.js +515 -432
  136. package/test/media/drm_engine_unit.js +86 -120
  137. package/test/media/media_source_engine_integration.js +26 -3
  138. package/test/media/media_source_engine_unit.js +7 -6
  139. package/test/media/playhead_unit.js +27 -191
  140. package/test/media/segment_index_unit.js +15 -25
  141. package/test/media/streaming_engine_integration.js +4 -39
  142. package/test/media/streaming_engine_unit.js +3 -2
  143. package/test/media/time_ranges_utils_unit.js +0 -5
  144. package/test/offline/manifest_convert_unit.js +34 -4
  145. package/test/offline/offline_manifest_parser_unit.js +1 -0
  146. package/test/offline/storage_integration.js +5 -4
  147. package/test/player_external.js +0 -6
  148. package/test/player_integration.js +16 -0
  149. package/test/player_unit.js +26 -0
  150. package/test/test/util/fake_media_source_engine.js +11 -4
  151. package/test/test/util/manifest_generator.js +3 -1
  152. package/test/test/util/manifest_parser_util.js +4 -1
  153. package/test/test/util/offline_utils.js +1 -0
  154. package/test/test/util/streaming_engine_util.js +1 -0
  155. package/test/test/util/test_scheme.js +24 -1
  156. package/test/test/util/util.js +4 -4
  157. package/test/text/cue_integration.js +2 -2
  158. package/test/text/lrc_text_parser_unit.js +6 -7
  159. package/test/text/mp4_ttml_parser_unit.js +6 -4
  160. package/test/text/mp4_vtt_parser_unit.js +6 -5
  161. package/test/text/sbv_text_parser_unit.js +5 -6
  162. package/test/text/srt_text_parser_unit.js +5 -6
  163. package/test/text/ssa_text_parser_unit.js +17 -29
  164. package/test/text/text_engine_unit.js +45 -5
  165. package/test/text/ttml_text_parser_unit.js +77 -77
  166. package/test/text/vtt_text_parser_unit.js +55 -57
  167. package/test/ui/ui_integration.js +0 -16
  168. package/test/ui/ui_unit.js +27 -0
  169. package/test/util/cmcd_manager_unit.js +3 -0
  170. package/test/util/stream_utils_unit.js +23 -7
  171. package/ui/controls.js +65 -40
  172. package/ui/fullscreen_button.js +26 -6
  173. package/ui/less/containers.less +3 -3
  174. package/ui/locales/en.json +1 -0
  175. package/ui/locales/source.json +5 -0
  176. package/ui/resolution_selection.js +31 -33
  177. package/ui/ui.js +0 -17
  178. package/docs/tutorials/upgrade-v2-5.md +0 -441
  179. package/docs/tutorials/upgrade-v3.md +0 -38
  180. package/lib/polyfill/mathround.js +0 -49
  181. package/lib/polyfill/patchedmediakeys_apple.js +0 -744
  182. package/lib/polyfill/patchedmediakeys_ms.js +0 -698
  183. package/lib/util/mutex.js +0 -52
  184. package/test/util/functional_unit.js +0 -82
@@ -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,19 @@ 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;
71
+ let offset = time.vttOffset;
54
72
 
55
- if (blocks[0].includes('X-TIMESTAMP-MAP')) {
73
+ // Do not honor the 'X-TIMESTAMP-MAP' value when in sequence mode.
74
+ // That is because it is used mainly (solely?) to account for the timestamp
75
+ // offset of the video/audio; when in sequence mode, we normalize that
76
+ // timestamp offset to 0, so we should not account for it.
77
+ if (blocks[0].includes('X-TIMESTAMP-MAP') && !this.sequenceMode_) {
56
78
  // https://bit.ly/2K92l7y
57
79
  // The 'X-TIMESTAMP-MAP' header is used in HLS to align text with
58
80
  // the rest of the media.
@@ -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) {
package/lib/util/error.js CHANGED
@@ -581,13 +581,7 @@ shaka.util.Error.Code = {
581
581
 
582
582
  // RETIRED: 'HLS_COULD_NOT_GUESS_MIME_TYPE': 4021,
583
583
 
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,
584
+ // RETIRED: 'HLS_MASTER_PLAYLIST_NOT_PROVIDED': 4022,
591
585
 
592
586
  /**
593
587
  * One of the required attributes was not provided, so the
@@ -627,12 +621,7 @@ shaka.util.Error.Code = {
627
621
 
628
622
  // RETIRED: 'HLS_LIVE_CONTENT_NOT_SUPPORTED': 4029,
629
623
 
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,
624
+ // RETIRED: 'HLS_COULD_NOT_PARSE_SEGMENT_START_TIME': 4030,
636
625
 
637
626
  // RETIRED: 'HLS_MEDIA_SEQUENCE_REQUIRED_IN_LIVE_STREAMS': 4031,
638
627
 
@@ -686,6 +675,13 @@ shaka.util.Error.Code = {
686
675
  */
687
676
  'HLS_VARIABLE_NOT_FOUND': 4039,
688
677
 
678
+ /**
679
+ * We do not support playing encrypted mp2t with MSE
680
+ */
681
+ 'HLS_MSE_ENCRYPTED_MP2T_NOT_SUPPORTED': 4040,
682
+
683
+ // RETIRED: 'HLS_MSE_ENCRYPTED_LEGACY_APPLE_MEDIA_KEYS_NOT_SUPPORTED': 4041,
684
+
689
685
 
690
686
  // RETIRED: 'INCONSISTENT_BUFFER_STATE': 5000,
691
687
  // 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
  };
@@ -11,6 +11,7 @@ goog.require('shaka.media.Transmuxer');
11
11
 
12
12
  /**
13
13
  * @summary A set of utility functions for dealing with MIME types.
14
+ * @export
14
15
  */
15
16
  shaka.util.MimeUtils = class {
16
17
  /**
@@ -20,6 +21,7 @@ shaka.util.MimeUtils = class {
20
21
  * @param {string} mimeType
21
22
  * @param {string=} codecs
22
23
  * @return {string}
24
+ * @export
23
25
  */
24
26
  static getFullType(mimeType, codecs) {
25
27
  let fullMimeType = mimeType;
@@ -142,6 +142,18 @@ shaka.util.Platform = class {
142
142
  return shaka.util.Platform.userAgentContains_('Web0S');
143
143
  }
144
144
 
145
+ /**
146
+ * Check if the current platform is a WebOS 3.
147
+ *
148
+ * @return {boolean}
149
+ */
150
+ static isWebOS3() {
151
+ // See: http://webostv.developer.lge.com/discover/specifications/web-engine/
152
+ return shaka.util.Platform.userAgentContains_('Web0S') &&
153
+ shaka.util.Platform.userAgentContains_(
154
+ 'Chrome/38.0.2125.122 Safari/537.36');
155
+ }
156
+
145
157
  /**
146
158
  * Check if the current platform is a Google Chromecast.
147
159
  *
@@ -151,6 +163,16 @@ shaka.util.Platform = class {
151
163
  return shaka.util.Platform.userAgentContains_('CrKey');
152
164
  }
153
165
 
166
+ /**
167
+ * Check if the current platform is a Android-based Cast devices.
168
+ *
169
+ * @return {boolean}
170
+ */
171
+ static isAndroidCastDevice() {
172
+ return shaka.util.Platform.isChromecast() &&
173
+ shaka.util.Platform.userAgentContains_('Android');
174
+ }
175
+
154
176
  /**
155
177
  * Check if the current platform is Google Chrome.
156
178
  *
@@ -228,6 +250,16 @@ shaka.util.Platform = class {
228
250
  return null;
229
251
  }
230
252
 
253
+ /**
254
+ * Check if the current platform is Apple Safari
255
+ * or Safari-based iOS browsers.
256
+ *
257
+ * @return {boolean}
258
+ */
259
+ static isSafari() {
260
+ return !!shaka.util.Platform.safariVersion();
261
+ }
262
+
231
263
  /**
232
264
  * Guesses if the platform is a mobile one (iOS or Android).
233
265
  *
@@ -318,6 +350,19 @@ shaka.util.Platform = class {
318
350
  const Platform = shaka.util.Platform;
319
351
  return Platform.isTizen() || Platform.isXboxOne();
320
352
  }
353
+
354
+ /**
355
+ * Returns true if MediaKeys is polyfilled
356
+ *
357
+ * @return {boolean}
358
+ */
359
+ static isMediaKeysPolyfilled() {
360
+ if (window.shakaMediaKeysPolyfill) {
361
+ return true;
362
+ }
363
+
364
+ return false;
365
+ }
321
366
  };
322
367
 
323
368
  /** @private {shaka.util.Timer} */
@@ -9,7 +9,6 @@ goog.provide('shaka.util.PlayerConfiguration');
9
9
  goog.require('goog.asserts');
10
10
  goog.require('shaka.abr.SimpleAbrManager');
11
11
  goog.require('shaka.log');
12
- goog.require('shaka.media.DrmEngine');
13
12
  goog.require('shaka.net.NetworkingEngine');
14
13
  goog.require('shaka.util.ConfigUtils');
15
14
  goog.require('shaka.util.LanguageUtils');
@@ -30,7 +29,10 @@ goog.require('shaka.util.Platform');
30
29
  * @export
31
30
  */
32
31
  shaka.util.PlayerConfiguration = class {
33
- /** @return {shaka.extern.PlayerConfiguration} */
32
+ /**
33
+ * @return {shaka.extern.PlayerConfiguration}
34
+ * @export
35
+ */
34
36
  static createDefault() {
35
37
  // This is a relatively safe default in the absence of clues from the
36
38
  // browser. For slower connections, the default estimate may be too high.
@@ -72,7 +74,6 @@ shaka.util.PlayerConfiguration = class {
72
74
  clearKeys: {}, // key is arbitrary key system ID, value must be string
73
75
  advanced: {}, // key is arbitrary key system ID, value is a record type
74
76
  delayLicenseRequestUntilPlayed: false,
75
- initDataTransform: shaka.media.DrmEngine.defaultInitDataTransform,
76
77
  logLicenseExchange: false,
77
78
  updateExpirationTime: 1,
78
79
  preferredKeySystems: [],
@@ -86,6 +87,7 @@ shaka.util.PlayerConfiguration = class {
86
87
  disableText: false,
87
88
  disableThumbnails: false,
88
89
  defaultPresentationDelay: 0,
90
+ segmentRelativeVttTiming: false,
89
91
  dash: {
90
92
  clockSyncUri: '',
91
93
  ignoreDrmInfo: false,
@@ -100,6 +102,8 @@ shaka.util.PlayerConfiguration = class {
100
102
  keySystemsByURI: {
101
103
  'urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b':
102
104
  'org.w3.clearkey',
105
+ 'urn:uuid:e2719d58-a985-b3c9-781a-b030af78d30e':
106
+ 'org.w3.clearkey',
103
107
  'urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed':
104
108
  'com.widevine.alpha',
105
109
  'urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95':
@@ -119,9 +123,11 @@ shaka.util.PlayerConfiguration = class {
119
123
  hls: {
120
124
  ignoreTextStreamFailures: false,
121
125
  ignoreImageStreamFailures: false,
122
- useFullSegmentsForStartTime: false,
123
126
  defaultAudioCodec: 'mp4a.40.2',
124
127
  defaultVideoCodec: 'avc1.42E01E',
128
+ ignoreManifestProgramDateTime: false,
129
+ mediaPlaylistFullMimeType:
130
+ 'video/mp2t; codecs="avc1.42E01E, mp4a.40.2"',
125
131
  },
126
132
  };
127
133
 
@@ -143,8 +149,6 @@ shaka.util.PlayerConfiguration = class {
143
149
  alwaysStreamText: false,
144
150
  startAtSegmentBoundary: false,
145
151
  gapDetectionThreshold: 0.1,
146
- smallGapLimit: 0.5,
147
- jumpLargeGaps: false,
148
152
  durationBackoff: 1,
149
153
  forceTransmuxTS: false,
150
154
  // Offset by 5 seconds since Chromecast takes a few seconds to start
@@ -218,6 +222,8 @@ shaka.util.PlayerConfiguration = class {
218
222
  // unexpected behaviours when someone tries to plays downloaded content
219
223
  // without a persistent license.
220
224
  usePersistentLicense: true,
225
+
226
+ numberOfParallelDownloads: 5,
221
227
  };
222
228
 
223
229
  const abr = {
package/lib/util/pssh.js CHANGED
@@ -90,16 +90,21 @@ shaka.util.Pssh = class {
90
90
  }
91
91
 
92
92
  /**
93
- * Creates a pssh blob from the given system ID and data.
93
+ * Creates a pssh blob from the given system ID, data, keyIds and version.
94
94
  *
95
95
  * @param {!Uint8Array} data
96
96
  * @param {!Uint8Array} systemId
97
+ * @param {!Set.<string>} keyIds
98
+ * @param {number} version
97
99
  * @return {!Uint8Array}
98
100
  */
99
- static createPssh(data, systemId) {
101
+ static createPssh(data, systemId, keyIds, version) {
100
102
  goog.asserts.assert(systemId.byteLength == 16, 'Invalid system ID length');
101
103
  const dataLength = data.length;
102
- const psshSize = 0x4 + 0x4 + 0x4 + systemId.length + 0x4 + dataLength;
104
+ let psshSize = 0x4 + 0x4 + 0x4 + systemId.length + 0x4 + dataLength;
105
+ if (version > 0) {
106
+ psshSize += 0x4 + (16 * keyIds.size);
107
+ }
103
108
 
104
109
  /** @type {!Uint8Array} */
105
110
  const psshBox = new Uint8Array(psshSize);
@@ -111,10 +116,24 @@ shaka.util.Pssh = class {
111
116
  byteCursor += 0x4;
112
117
  psshData.setUint32(byteCursor, 0x70737368); // 'pssh'
113
118
  byteCursor += 0x4;
114
- psshData.setUint32(byteCursor, 0); // flags
119
+ (version < 1) ? psshData.setUint32(byteCursor, 0) :
120
+ psshData.setUint32(byteCursor, 0x01000000); // version + flags
115
121
  byteCursor += 0x4;
116
122
  psshBox.set(systemId, byteCursor);
117
123
  byteCursor += systemId.length;
124
+
125
+ // if version > 0, add KID count and kid values.
126
+ if (version > 0) {
127
+ psshData.setUint32(byteCursor, keyIds.size); // KID_count
128
+ byteCursor += 0x4;
129
+ const Uint8ArrayUtils = shaka.util.Uint8ArrayUtils;
130
+ for (const keyId of keyIds) {
131
+ const KID = Uint8ArrayUtils.fromHex(keyId);
132
+ psshBox.set(KID, byteCursor);
133
+ byteCursor += KID.length;
134
+ }
135
+ }
136
+
118
137
  psshData.setUint32(byteCursor, dataLength);
119
138
  byteCursor += 0x4;
120
139
  psshBox.set(data, byteCursor);
@@ -406,7 +406,7 @@ shaka.util.StreamUtils = class {
406
406
  shaka.util.StreamUtils.filterManifestByCurrentVariant(
407
407
  currentVariant, manifest);
408
408
  shaka.util.StreamUtils.filterTextStreams_(manifest);
409
- shaka.util.StreamUtils.filterImageStreams_(manifest);
409
+ await shaka.util.StreamUtils.filterImageStreams_(manifest);
410
410
  }
411
411
 
412
412
 
@@ -813,37 +813,57 @@ shaka.util.StreamUtils = class {
813
813
  * @param {shaka.extern.Manifest} manifest
814
814
  * @private
815
815
  */
816
- static filterImageStreams_(manifest) {
817
- // Filter image streams.
818
- manifest.imageStreams = manifest.imageStreams.filter((stream) => {
819
- // TODO: re-examine this and avoid allow-listing the MIME types we can
820
- // accept.
821
- const validMimeTypes = [
822
- 'image/svg+xml',
823
- 'image/png',
824
- 'image/jpeg',
825
- ];
826
- const Platform = shaka.util.Platform;
827
- // Add webp support to popular platforms that support it.
828
- const webpSupport = Platform.isWebOS() ||
829
- Platform.isTizen() ||
830
- Platform.isChromecast();
831
- if (webpSupport) {
832
- validMimeTypes.push('image/webp');
816
+ static async filterImageStreams_(manifest) {
817
+ const imageStreams = [];
818
+ for (const stream of manifest.imageStreams) {
819
+ const mimeType = stream.mimeType;
820
+ if (!shaka.util.StreamUtils.supportedImageMimeTypes_.has(mimeType)) {
821
+ const minImage = shaka.util.StreamUtils.minImage_.get(mimeType);
822
+ if (minImage) {
823
+ // eslint-disable-next-line no-await-in-loop
824
+ const res = await shaka.util.StreamUtils.isImageSupported_(minImage);
825
+ shaka.util.StreamUtils.supportedImageMimeTypes_.set(mimeType, res);
826
+ } else {
827
+ shaka.util.StreamUtils.supportedImageMimeTypes_.set(mimeType, false);
828
+ }
833
829
  }
834
- // TODO: add support to image/webp and image/avif
835
- const keep = validMimeTypes.includes(stream.mimeType);
830
+
831
+ const keep =
832
+ shaka.util.StreamUtils.supportedImageMimeTypes_.get(mimeType);
836
833
 
837
834
  if (!keep) {
838
835
  shaka.log.debug('Dropping image stream. Is not supported by the ' +
839
836
  'platform.', stream);
837
+ } else {
838
+ imageStreams.push(stream);
840
839
  }
840
+ }
841
+ manifest.imageStreams = imageStreams;
842
+ }
841
843
 
842
- return keep;
844
+ /**
845
+ * @param {string} minImage
846
+ * @return {!Promise.<boolean>}
847
+ * @private
848
+ */
849
+ static isImageSupported_(minImage) {
850
+ return new Promise((resolve) => {
851
+ const imageElement = /** @type {HTMLImageElement} */(new Image());
852
+ imageElement.src = minImage;
853
+ if ('decode' in imageElement) {
854
+ imageElement.decode().then(() => {
855
+ resolve(true);
856
+ }).catch(() => {
857
+ resolve(false);
858
+ });
859
+ } else {
860
+ imageElement.onload = imageElement.onerror = () => {
861
+ resolve(imageElement.height === 2);
862
+ };
863
+ }
843
864
  });
844
865
  }
845
866
 
846
-
847
867
  /**
848
868
  * @param {shaka.extern.Stream} s0
849
869
  * @param {shaka.extern.Stream} s1
@@ -878,6 +898,11 @@ shaka.util.StreamUtils = class {
878
898
  /** @type {?shaka.extern.Stream} */
879
899
  const video = variant.video;
880
900
 
901
+ /** @type {?string} */
902
+ const audioMimeType = audio ? audio.mimeType : null;
903
+ /** @type {?string} */
904
+ const videoMimeType = video ? video.mimeType : null;
905
+
881
906
  /** @type {?string} */
882
907
  const audioCodec = audio ? audio.codecs : null;
883
908
  /** @type {?string} */
@@ -942,6 +967,8 @@ shaka.util.StreamUtils = class {
942
967
  pixelAspectRatio: null,
943
968
  hdr: null,
944
969
  mimeType: mimeType,
970
+ audioMimeType: audioMimeType,
971
+ videoMimeType: videoMimeType,
945
972
  codecs: codecs.join(', '),
946
973
  audioCodec: audioCodec,
947
974
  videoCodec: videoCodec,
@@ -1009,6 +1036,8 @@ shaka.util.StreamUtils = class {
1009
1036
  pixelAspectRatio: null,
1010
1037
  hdr: null,
1011
1038
  mimeType: stream.mimeType,
1039
+ audioMimeType: null,
1040
+ videoMimeType: null,
1012
1041
  codecs: stream.codecs || null,
1013
1042
  audioCodec: null,
1014
1043
  videoCodec: null,
@@ -1081,6 +1110,8 @@ shaka.util.StreamUtils = class {
1081
1110
  pixelAspectRatio: null,
1082
1111
  hdr: null,
1083
1112
  mimeType: stream.mimeType,
1113
+ audioMimeType: null,
1114
+ videoMimeType: null,
1084
1115
  codecs: null,
1085
1116
  audioCodec: null,
1086
1117
  videoCodec: null,
@@ -1199,6 +1230,8 @@ shaka.util.StreamUtils = class {
1199
1230
  pixelAspectRatio: null,
1200
1231
  hdr: null,
1201
1232
  mimeType: null,
1233
+ audioMimeType: null,
1234
+ videoMimeType: null,
1202
1235
  codecs: null,
1203
1236
  audioCodec: null,
1204
1237
  videoCodec: null,
@@ -1503,3 +1536,40 @@ shaka.util.StreamUtils.DecodingAttributes = {
1503
1536
  POWER: 'powerEfficient',
1504
1537
  BANDWIDTH: 'bandwidth',
1505
1538
  };
1539
+
1540
+ /**
1541
+ * @private {!Map.<string, boolean>}
1542
+ */
1543
+ shaka.util.StreamUtils.supportedImageMimeTypes_ = new Map()
1544
+ .set('image/svg+xml', true)
1545
+ .set('image/png', true)
1546
+ .set('image/jpeg', true)
1547
+ .set('image/jpg', true);
1548
+
1549
+ /**
1550
+ * @const {string}
1551
+ * @private
1552
+ */
1553
+ shaka.util.StreamUtils.minWebPImage_ = 'data:image/webp;base64,UklGRjoAAABXRU' +
1554
+ 'JQVlA4IC4AAACyAgCdASoCAAIALmk0mk0iIiIiIgBoSygABc6WWgAA/veff/0PP8bA//LwY' +
1555
+ 'AAA';
1556
+
1557
+ /**
1558
+ * @const {string}
1559
+ * @private
1560
+ */
1561
+ shaka.util.StreamUtils.minAvifImage_ = 'data:image/avif;base64,AAAAIGZ0eXBhdm' +
1562
+ 'lmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljd' +
1563
+ 'AAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEA' +
1564
+ 'AAABAAABGgAAAB0AAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAA' +
1565
+ 'AamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAIAAAACAAAAEHBpeGkAAAAAAwgICAAAAA' +
1566
+ 'xhdjFDgQ0MAAAAABNjb2xybmNseAACAAIAAYAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAA' +
1567
+ 'CVtZGF0EgAKCBgANogQEAwgMg8f8D///8WfhwB8+ErK42A=';
1568
+
1569
+ /**
1570
+ * @const {!Map.<string, string>}
1571
+ * @private
1572
+ */
1573
+ shaka.util.StreamUtils.minImage_ = new Map()
1574
+ .set('image/webp', shaka.util.StreamUtils.minWebPImage_)
1575
+ .set('image/avif', shaka.util.StreamUtils.minAvifImage_);