shaka-player 4.1.1 → 4.2.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 (179) hide show
  1. package/.github/workflows/build-and-test.yaml +67 -15
  2. package/.github/workflows/compute-incremental-coverage.py +251 -0
  3. package/.github/workflows/report-incremental-coverage.yaml +39 -0
  4. package/.github/workflows/selenium-lab-tests.yaml +44 -1
  5. package/AUTHORS +1 -0
  6. package/CHANGELOG.md +36 -8
  7. package/CONTRIBUTORS +3 -0
  8. package/README.md +20 -6
  9. package/demo/common/assets.js +25 -0
  10. package/demo/common/message_ids.js +2 -0
  11. package/demo/config.js +5 -1
  12. package/demo/index.html +1 -3
  13. package/demo/locales/en.json +2 -0
  14. package/demo/locales/source.json +8 -0
  15. package/dist/controls.css +1 -1
  16. package/dist/demo.compiled.debug.js +25 -23
  17. package/dist/demo.compiled.debug.map +3 -3
  18. package/dist/demo.compiled.js +25 -23
  19. package/dist/demo.compiled.map +3 -3
  20. package/dist/deps.js +6 -5
  21. package/dist/locales.js +30 -30
  22. package/dist/receiver.compiled.debug.js +24 -22
  23. package/dist/receiver.compiled.debug.map +3 -3
  24. package/dist/receiver.compiled.js +24 -22
  25. package/dist/receiver.compiled.map +3 -3
  26. package/dist/shaka-player.compiled.d.ts +69 -5
  27. package/dist/shaka-player.compiled.debug.d.ts +69 -5
  28. package/dist/shaka-player.compiled.debug.externs.js +111 -42
  29. package/dist/shaka-player.compiled.debug.js +352 -301
  30. package/dist/shaka-player.compiled.debug.map +4 -4
  31. package/dist/shaka-player.compiled.externs.js +111 -42
  32. package/dist/shaka-player.compiled.js +823 -802
  33. package/dist/shaka-player.compiled.map +4 -4
  34. package/dist/shaka-player.ui.d.ts +69 -5
  35. package/dist/shaka-player.ui.debug.d.ts +69 -5
  36. package/dist/shaka-player.ui.debug.externs.js +111 -42
  37. package/dist/shaka-player.ui.debug.js +360 -310
  38. package/dist/shaka-player.ui.debug.map +4 -4
  39. package/dist/shaka-player.ui.externs.js +111 -42
  40. package/dist/shaka-player.ui.js +980 -957
  41. package/dist/shaka-player.ui.map +4 -4
  42. package/docs/tutorials/fairplay.md +149 -0
  43. package/docs/tutorials/ui.md +6 -6
  44. package/docs/tutorials/upgrade.md +1 -0
  45. package/docs/tutorials/welcome.md +6 -3
  46. package/externs/shaka/abr_manager.js +8 -0
  47. package/externs/shaka/manifest.js +43 -0
  48. package/externs/shaka/player.js +13 -2
  49. package/karma.conf.js +3 -11
  50. package/lib/abr/simple_abr_manager.js +72 -4
  51. package/lib/ads/client_side_ad_manager.js +1 -1
  52. package/lib/dash/dash_parser.js +5 -0
  53. package/lib/hls/hls_parser.js +123 -27
  54. package/lib/media/drm_engine.js +1 -3
  55. package/lib/media/media_source_engine.js +57 -24
  56. package/lib/media/presentation_timeline.js +24 -1
  57. package/lib/media/segment_index.js +8 -0
  58. package/lib/media/segment_reference.js +7 -1
  59. package/lib/media/streaming_engine.js +42 -6
  60. package/lib/media/time_ranges_utils.js +46 -0
  61. package/lib/net/networking_engine.js +1 -0
  62. package/lib/player.js +8 -6
  63. package/lib/polyfill/media_capabilities.js +9 -0
  64. package/lib/polyfill/patchedmediakeys_apple.js +729 -0
  65. package/lib/polyfill/videoplaybackquality.js +3 -1
  66. package/lib/text/cue.js +0 -5
  67. package/lib/text/simple_text_displayer.js +21 -9
  68. package/lib/text/ui_text_displayer.js +83 -19
  69. package/lib/text/vtt_text_parser.js +61 -4
  70. package/lib/util/buffer_utils.js +3 -0
  71. package/lib/util/error.js +21 -1
  72. package/lib/util/fairplay_utils.js +100 -2
  73. package/lib/util/object_utils.js +1 -0
  74. package/lib/util/platform.js +30 -1
  75. package/lib/util/player_configuration.js +3 -10
  76. package/lib/util/stream_utils.js +23 -3
  77. package/lib/util/string_utils.js +102 -13
  78. package/maintained-branches.md +15 -0
  79. package/package.json +3 -5
  80. package/shaka-player.uncompiled.js +1 -0
  81. package/test/hls/hls_parser_unit.js +49 -33
  82. package/test/media/drm_engine_integration.js +24 -8
  83. package/test/media/media_source_engine_integration.js +24 -10
  84. package/test/media/media_source_engine_unit.js +53 -38
  85. package/test/media/streaming_engine_unit.js +107 -32
  86. package/test/test/util/fake_media_source_engine.js +10 -9
  87. package/test/test/util/simple_fakes.js +4 -0
  88. package/test/test/util/streaming_engine_util.js +10 -2
  89. package/test/text/simple_text_displayer_unit.js +27 -0
  90. package/test/text/ui_text_displayer_unit.js +65 -0
  91. package/test/text/vtt_text_parser_unit.js +26 -0
  92. package/test/util/string_utils_unit.js +46 -8
  93. package/ui/text_selection.js +2 -2
  94. package/app-engine/README.md +0 -9
  95. package/app-engine/demo-version-index/README.md +0 -14
  96. package/app-engine/demo-version-index/app.yaml +0 -28
  97. package/app-engine/demo-version-index/generate.py +0 -164
  98. package/app-engine/demo-version-index/main.py +0 -11
  99. package/app-engine/demo-version-index/requirements.txt +0 -5
  100. package/app-engine/demo-version-index/templates/index.html +0 -114
  101. package/app-engine/shaka-player-demo/README.md +0 -18
  102. package/app-engine/shaka-player-demo/app.yaml +0 -61
  103. package/app-engine/shaka-player-demo/main.py +0 -95
  104. package/app-engine/shaka-player-demo/posters/10yo.png +0 -0
  105. package/app-engine/shaka-player-demo/posters/10yo.xcf +0 -0
  106. package/app-engine/shaka-player-demo/posters/1yo.jpg +0 -0
  107. package/app-engine/shaka-player-demo/posters/2yo.png +0 -0
  108. package/app-engine/shaka-player-demo/posters/2yo.xcf +0 -0
  109. package/app-engine/shaka-player-demo/posters/3yo.png +0 -0
  110. package/app-engine/shaka-player-demo/posters/3yo.xcf +0 -0
  111. package/app-engine/shaka-player-demo/posters/4yo.png +0 -0
  112. package/app-engine/shaka-player-demo/posters/4yo.xcf +0 -0
  113. package/app-engine/shaka-player-demo/posters/5yo.png +0 -0
  114. package/app-engine/shaka-player-demo/posters/5yo.xcf +0 -0
  115. package/app-engine/shaka-player-demo/posters/6yo.png +0 -0
  116. package/app-engine/shaka-player-demo/posters/6yo.xcf +0 -0
  117. package/app-engine/shaka-player-demo/posters/7yo.png +0 -0
  118. package/app-engine/shaka-player-demo/posters/7yo.xcf +0 -0
  119. package/app-engine/shaka-player-demo/posters/8yo.png +0 -0
  120. package/app-engine/shaka-player-demo/posters/8yo.xcf +0 -0
  121. package/app-engine/shaka-player-demo/posters/9yo.png +0 -0
  122. package/app-engine/shaka-player-demo/posters/9yo.xcf +0 -0
  123. package/app-engine/shaka-player-demo/posters/audioOnly.gif +0 -0
  124. package/app-engine/shaka-player-demo/posters/audioRickRoll.gif +0 -0
  125. package/app-engine/shaka-player-demo/posters/colorbars.gif +0 -0
  126. package/app-engine/shaka-player-demo/posters/fireworks.jpg +0 -0
  127. package/app-engine/shaka-player-demo/posters/party-hat.png +0 -0
  128. package/app-engine/shaka-player-demo/posters/sources.txt +0 -46
  129. package/app-engine/shaka-player-demo/posters/standard.png +0 -0
  130. package/app-engine/shaka-player-demo/posters/trek.jpg +0 -0
  131. package/app-engine/shaka-player-demo/posters/valentines.png +0 -0
  132. package/app-engine/shaka-player-demo/posters/zombie.jpg +0 -0
  133. package/app-engine/shaka-player-demo/posters.py +0 -110
  134. package/app-engine/shaka-player-demo/requirements.txt +0 -5
  135. package/app-engine/shaka-player-demo/time.txt +0 -1
  136. package/build/README.md +0 -127
  137. package/build/__init__.py +0 -0
  138. package/build/all.py +0 -150
  139. package/build/apps.py +0 -186
  140. package/build/build.py +0 -392
  141. package/build/check.py +0 -354
  142. package/build/checkversion.py +0 -84
  143. package/build/compiler.py +0 -513
  144. package/build/conformance.textproto +0 -335
  145. package/build/docker/Dockerfile +0 -20
  146. package/build/docker/docker-compose.yml +0 -10
  147. package/build/docs.py +0 -54
  148. package/build/eslint-plugin-shaka-rules/arg-comment-spacing.js +0 -72
  149. package/build/eslint-plugin-shaka-rules/index.js +0 -24
  150. package/build/eslint-plugin-shaka-rules/package.json +0 -7
  151. package/build/eslint-plugin-shaka-rules/private.js +0 -109
  152. package/build/gendeps.py +0 -71
  153. package/build/generateExterns.js +0 -867
  154. package/build/generateLocalizations.py +0 -262
  155. package/build/generateTsDefs.py +0 -137
  156. package/build/install-linux-prereqs.sh +0 -136
  157. package/build/license-header +0 -5
  158. package/build/misspellings.txt +0 -39
  159. package/build/pixelsChanged.js +0 -35
  160. package/build/shaka-lab.yaml +0 -161
  161. package/build/shakaBuildHelpers.py +0 -356
  162. package/build/stats.py +0 -816
  163. package/build/subprocessWindowsPatch.py +0 -103
  164. package/build/test.py +0 -541
  165. package/build/types/ads +0 -8
  166. package/build/types/cast +0 -6
  167. package/build/types/cea +0 -13
  168. package/build/types/complete +0 -9
  169. package/build/types/core +0 -108
  170. package/build/types/dash +0 -8
  171. package/build/types/hls +0 -9
  172. package/build/types/manifests +0 -5
  173. package/build/types/networking +0 -6
  174. package/build/types/offline +0 -22
  175. package/build/types/polyfill +0 -18
  176. package/build/types/text +0 -11
  177. package/build/types/ui +0 -39
  178. package/build/updateScreenshots.py +0 -85
  179. package/build/wrapper.template.js +0 -52
@@ -11,6 +11,7 @@ goog.require('shaka.log');
11
11
  goog.require('shaka.util.BufferUtils');
12
12
  goog.require('shaka.util.Error');
13
13
  goog.require('shaka.util.Lazy');
14
+ goog.require('shaka.util.Platform');
14
15
 
15
16
 
16
17
  /**
@@ -37,18 +38,84 @@ shaka.util.StringUtils = class {
37
38
  uint8 = uint8.subarray(3);
38
39
  }
39
40
 
40
- // Use the TextDecoder interface to decode the text. This has the advantage
41
- // compared to the previously-standard decodeUriComponent that it will
42
- // continue parsing even if it finds an invalid UTF8 character, rather than
43
- // stop and throw an error.
44
- const utf8decoder = new TextDecoder();
45
- const decoded = utf8decoder.decode(uint8);
46
- if (decoded.includes('\uFFFD')) {
47
- shaka.log.alwaysError('Decoded string contains an "unknown character" ' +
48
- 'codepoint. That probably means the UTF8 ' +
49
- 'encoding was incorrect!');
41
+ if (window.TextDecoder && !shaka.util.Platform.isPS4()) {
42
+ // Use the TextDecoder interface to decode the text. This has the
43
+ // advantage compared to the previously-standard decodeUriComponent that
44
+ // it will continue parsing even if it finds an invalid UTF8 character,
45
+ // rather than stop and throw an error.
46
+ const utf8decoder = new TextDecoder();
47
+ const decoded = utf8decoder.decode(uint8);
48
+ if (decoded.includes('\uFFFD')) {
49
+ shaka.log.alwaysError('Decoded string contains an "unknown character' +
50
+ '" codepoint. That probably means the UTF8 ' +
51
+ 'encoding was incorrect!');
52
+ }
53
+ return decoded;
54
+ } else {
55
+ // Homebrewed UTF-8 decoder based on
56
+ // https://en.wikipedia.org/wiki/UTF-8#Encoding
57
+ // Unlike decodeURIComponent, won't throw on bad encoding.
58
+ // In this way, it is similar to TextDecoder.
59
+
60
+ let decoded = '';
61
+ for (let i = 0; i < uint8.length; ++i) {
62
+ // By default, the "replacement character" codepoint.
63
+ let codePoint = 0xFFFD;
64
+
65
+ // Top bit is 0, 1-byte encoding.
66
+ if ((uint8[i] & 0x80) == 0) {
67
+ codePoint = uint8[i];
68
+
69
+ // Top 3 bits of byte 0 are 110, top 2 bits of byte 1 are 10,
70
+ // 2-byte encoding.
71
+ } else if (uint8.length >= i + 2 &&
72
+ (uint8[i] & 0xe0) == 0xc0 &&
73
+ (uint8[i + 1] & 0xc0) == 0x80) {
74
+ codePoint = ((uint8[i] & 0x1f) << 6) |
75
+ ((uint8[i + 1] & 0x3f));
76
+ i += 1; // Consume one extra byte.
77
+
78
+ // Top 4 bits of byte 0 are 1110, top 2 bits of byte 1 and 2 are 10,
79
+ // 3-byte encoding.
80
+ } else if (uint8.length >= i + 3 &&
81
+ (uint8[i] & 0xf0) == 0xe0 &&
82
+ (uint8[i + 1] & 0xc0) == 0x80 &&
83
+ (uint8[i + 2] & 0xc0) == 0x80) {
84
+ codePoint = ((uint8[i] & 0x0f) << 12) |
85
+ ((uint8[i + 1] & 0x3f) << 6) |
86
+ ((uint8[i + 2] & 0x3f));
87
+ i += 2; // Consume two extra bytes.
88
+
89
+ // Top 5 bits of byte 0 are 11110, top 2 bits of byte 1, 2 and 3 are 10,
90
+ // 4-byte encoding.
91
+ } else if (uint8.length >= i + 4 &&
92
+ (uint8[i] & 0xf1) == 0xf0 &&
93
+ (uint8[i + 1] & 0xc0) == 0x80 &&
94
+ (uint8[i + 2] & 0xc0) == 0x80 &&
95
+ (uint8[i + 3] & 0xc0) == 0x80) {
96
+ codePoint = ((uint8[i] & 0x07) << 18) |
97
+ ((uint8[i + 1] & 0x3f) << 12) |
98
+ ((uint8[i + 2] & 0x3f) << 6) |
99
+ ((uint8[i + 3] & 0x3f));
100
+ i += 3; // Consume three extra bytes.
101
+ }
102
+
103
+ // JavaScript strings are a series of UTF-16 characters.
104
+ if (codePoint <= 0xffff) {
105
+ decoded += String.fromCharCode(codePoint);
106
+ } else {
107
+ // UTF-16 surrogate-pair encoding, based on
108
+ // https://en.wikipedia.org/wiki/UTF-16#Description
109
+ const baseCodePoint = codePoint - 0x10000;
110
+ const highPart = baseCodePoint >> 10;
111
+ const lowPart = baseCodePoint & 0x3ff;
112
+ decoded += String.fromCharCode(0xd800 + highPart);
113
+ decoded += String.fromCharCode(0xdc00 + lowPart);
114
+ }
115
+ }
116
+
117
+ return decoded;
50
118
  }
51
- return decoded;
52
119
  }
53
120
 
54
121
 
@@ -141,8 +208,30 @@ shaka.util.StringUtils = class {
141
208
  * @export
142
209
  */
143
210
  static toUTF8(str) {
144
- const utf8Encoder = new TextEncoder();
145
- return shaka.util.BufferUtils.toArrayBuffer(utf8Encoder.encode(str));
211
+ if (window.TextEncoder && !shaka.util.Platform.isPS4()) {
212
+ const utf8Encoder = new TextEncoder();
213
+ return shaka.util.BufferUtils.toArrayBuffer(utf8Encoder.encode(str));
214
+ } else {
215
+ // http://stackoverflow.com/a/13691499
216
+ // Converts the given string to a URI encoded string. If a character
217
+ // falls in the ASCII range, it is not converted; otherwise it will be
218
+ // converted to a series of URI escape sequences according to UTF-8.
219
+ // Example: 'g#€' -> 'g#%E3%82%AC'
220
+ const encoded = encodeURIComponent(str);
221
+ // Convert each escape sequence individually into a character. Each
222
+ // escape sequence is interpreted as a code-point, so if an escape
223
+ // sequence happens to be part of a multi-byte sequence, each byte will
224
+ // be converted to a single character.
225
+ // Example: 'g#%E3%82%AC' -> '\x67\x35\xe3\x82\xac'
226
+ const utf8 = unescape(encoded);
227
+
228
+ const result = new Uint8Array(utf8.length);
229
+ for (let i = 0; i < utf8.length; i++) {
230
+ const item = utf8[i];
231
+ result[i] = item.charCodeAt(0);
232
+ }
233
+ return shaka.util.BufferUtils.toArrayBuffer(result);
234
+ }
146
235
  }
147
236
 
148
237
 
@@ -0,0 +1,15 @@
1
+ # Maintained Shaka Player Branches
2
+
3
+ We are currently maintaining the following branches of Shaka Player:
4
+
5
+ - v4.1 (latest)
6
+ - v4.0 (previous)
7
+ - v3.3 (LTS until April 30, 2023)
8
+ - v3.2 (in use by the Cast Application Framework)
9
+
10
+ Other branches are no longer receiving bug fixes, and we recommend you upgrade
11
+ to a maintained branch. For details on upgrading, please see our
12
+ [upgrade guide](https://shaka-player-demo.appspot.com/docs/api/tutorial-upgrade.html).
13
+
14
+ For more information on how these branches are chosen, please refer to our
15
+ [Branch Maintenance Policy](https://shaka-project.github.io/maintenance/shaka-player.html#branch-maintenance-policy).
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.1.1",
4
+ "version": "4.2.0",
5
5
  "homepage": "https://github.com/shaka-project/shaka-player",
6
6
  "author": "Google",
7
7
  "maintainers": [
@@ -27,7 +27,6 @@
27
27
  "eslint-config-google": "^0.14.0",
28
28
  "eslint-plugin-shaka-rules": "file:./build/eslint-plugin-shaka-rules",
29
29
  "esprima": "^4.0.1",
30
- "fastestsmallesttextencoderdecoder": "^1.0.22",
31
30
  "fontfaceonload": "^1.0.2",
32
31
  "google-closure-compiler-java": "^20220301.0.0",
33
32
  "google-closure-deps": "https://gitpkg.now.sh/google/closure-library/closure-deps?d7736da6",
@@ -50,7 +49,7 @@
50
49
  "less": "https://gitpkg.now.sh/joeyparrish/less.js/packages/less?28c63a43",
51
50
  "less-plugin-clean-css": "github:austingardner/less-plugin-clean-css#4e9e77bf",
52
51
  "material-design-lite": "^1.3.0",
53
- "mux.js": "^6.1.0",
52
+ "mux.js": "^6.2.0",
54
53
  "open-sans-fonts": "^1.6.2",
55
54
  "postcss-less": "^6.0.0",
56
55
  "pwacompat": "^2.0.17",
@@ -71,7 +70,6 @@
71
70
  "dialog-polyfill/dist/dialog-polyfill.js",
72
71
  "eme-encryption-scheme-polyfill/index.js",
73
72
  "es6-promise-polyfill/promise.min.js",
74
- "fastestsmallesttextencoderdecoder/EncoderDecoderTogether.min.js",
75
73
  "google-closure-library/closure/goog/base.js",
76
74
  "less/dist/less.js",
77
75
  "material-design-lite/dist/material.indigo-blue.min.css",
@@ -96,7 +94,7 @@
96
94
  "prepublishOnly": "python build/checkversion.py && python build/all.py --force"
97
95
  },
98
96
  "dependencies": {
99
- "eme-encryption-scheme-polyfill": "^2.0.5"
97
+ "eme-encryption-scheme-polyfill": "^2.1.1"
100
98
  },
101
99
  "engines": {
102
100
  "node": ">=14"
@@ -38,6 +38,7 @@ goog.require('shaka.polyfill.Fullscreen');
38
38
  goog.require('shaka.polyfill.MediaSource');
39
39
  goog.require('shaka.polyfill.MediaCapabilities');
40
40
  goog.require('shaka.polyfill.Orientation');
41
+ goog.require('shaka.polyfill.PatchedMediaKeysApple');
41
42
  goog.require('shaka.polyfill.PatchedMediaKeysNop');
42
43
  goog.require('shaka.polyfill.PatchedMediaKeysWebkit');
43
44
  goog.require('shaka.polyfill.PiPWebkit');
@@ -33,6 +33,8 @@ describe('HlsParser', () => {
33
33
  let segmentData;
34
34
  /** @type {!Uint8Array} */
35
35
  let selfInitializingSegmentData;
36
+ /** @type {!Uint8Array} */
37
+ let aes128Key;
36
38
 
37
39
  afterEach(() => {
38
40
  shaka.log.alwaysWarn = originalAlwaysWarn;
@@ -75,6 +77,11 @@ describe('HlsParser', () => {
75
77
  selfInitializingSegmentData =
76
78
  shaka.util.Uint8ArrayUtils.concat(initSegmentData, segmentData);
77
79
 
80
+ aes128Key = new Uint8Array([
81
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
82
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
83
+ ]);
84
+
78
85
  fakeNetEngine = new shaka.test.FakeNetworkingEngine();
79
86
 
80
87
  config = shaka.util.PlayerConfiguration.createDefault().manifest;
@@ -2432,7 +2439,7 @@ describe('HlsParser', () => {
2432
2439
  expect(initSegments[1].getUris()[0]).toBe('test:/init2.mp4');
2433
2440
  });
2434
2441
 
2435
- it('drops variants encrypted with AES-128', async () => {
2442
+ it('parses variants encrypted with AES-128', async () => {
2436
2443
  const master = [
2437
2444
  '#EXTM3U\n',
2438
2445
  '#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1,mp4a",',
@@ -2441,10 +2448,15 @@ describe('HlsParser', () => {
2441
2448
  '#EXT-X-STREAM-INF:BANDWIDTH=300,CODECS="avc1,mp4a",',
2442
2449
  'RESOLUTION=960x540,FRAME-RATE=120,AUDIO="aud2"\n',
2443
2450
  'video2\n',
2451
+ '#EXT-X-STREAM-INF:BANDWIDTH=300,CODECS="avc1,mp4a",',
2452
+ 'RESOLUTION=960x540,FRAME-RATE=120,AUDIO="aud3"\n',
2453
+ 'video3\n',
2444
2454
  '#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="eng",',
2445
2455
  'URI="audio"\n',
2446
2456
  '#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud2",LANGUAGE="fr",',
2447
2457
  'URI="audio2"\n',
2458
+ '#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud3",LANGUAGE="de",',
2459
+ 'URI="audio3"\n',
2448
2460
  ].join('');
2449
2461
 
2450
2462
  const media = [
@@ -2456,17 +2468,26 @@ describe('HlsParser', () => {
2456
2468
  'main.mp4',
2457
2469
  ].join('');
2458
2470
 
2459
- const mediaWithAesEncryption = [
2471
+ const mediaWithMp4AesEncryption = [
2460
2472
  '#EXTM3U\n',
2461
2473
  '#EXT-X-PLAYLIST-TYPE:VOD\n',
2462
2474
  '#EXT-X-KEY:METHOD=AES-128,',
2463
- 'URI="800k.key\n',
2475
+ 'URI="800k.key"\n',
2464
2476
  '#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
2465
2477
  '#EXTINF:5,\n',
2466
2478
  '#EXT-X-BYTERANGE:121090@616\n',
2467
2479
  'main.mp4',
2468
2480
  ].join('');
2469
2481
 
2482
+ const mediaWithTSAesEncryption = [
2483
+ '#EXTM3U\n',
2484
+ '#EXT-X-PLAYLIST-TYPE:VOD\n',
2485
+ '#EXT-X-KEY:METHOD=AES-128,',
2486
+ 'URI="800k.key"\n',
2487
+ '#EXTINF:5,\n',
2488
+ 'main.ts',
2489
+ ].join('');
2490
+
2470
2491
  const manifest = shaka.test.ManifestGenerator.generate((manifest) => {
2471
2492
  manifest.anyTimeline();
2472
2493
  manifest.addPartialVariant((variant) => {
@@ -2478,6 +2499,24 @@ describe('HlsParser', () => {
2478
2499
  stream.language = 'en';
2479
2500
  });
2480
2501
  });
2502
+ manifest.addPartialVariant((variant) => {
2503
+ variant.bandwidth = 300;
2504
+ variant.addPartialStream(ContentType.VIDEO, (stream) => {
2505
+ stream.size(960, 540);
2506
+ });
2507
+ variant.addPartialStream(ContentType.AUDIO, (stream) => {
2508
+ stream.language = 'fr';
2509
+ });
2510
+ });
2511
+ manifest.addPartialVariant((variant) => {
2512
+ variant.bandwidth = 300;
2513
+ variant.addPartialStream(ContentType.VIDEO, (stream) => {
2514
+ stream.size(960, 540);
2515
+ });
2516
+ variant.addPartialStream(ContentType.AUDIO, (stream) => {
2517
+ stream.language = 'de';
2518
+ });
2519
+ });
2481
2520
  manifest.sequenceMode = true;
2482
2521
  });
2483
2522
 
@@ -2485,12 +2524,15 @@ describe('HlsParser', () => {
2485
2524
  .setResponseText('test:/master', master)
2486
2525
  .setResponseText('test:/audio', media)
2487
2526
  .setResponseText('test:/audio2', media)
2527
+ .setResponseText('test:/audio3', media)
2488
2528
  .setResponseText('test:/video', media)
2489
- .setResponseText('test:/video2', mediaWithAesEncryption)
2529
+ .setResponseText('test:/video2', mediaWithMp4AesEncryption)
2530
+ .setResponseText('test:/video3', mediaWithTSAesEncryption)
2490
2531
  .setResponseText('test:/main.vtt', vttText)
2491
2532
  .setResponseValue('test:/init.mp4', initSegmentData)
2492
2533
  .setResponseValue('test:/main.mp4', segmentData)
2493
2534
  .setResponseValue('test:/main.test', segmentData)
2535
+ .setResponseValue('test:/800k.key', aes128Key)
2494
2536
  .setResponseValue('test:/selfInit.mp4', selfInitializingSegmentData);
2495
2537
 
2496
2538
  const actual = await parser.start('test:/master', playerInterface);
@@ -2674,7 +2716,9 @@ describe('HlsParser', () => {
2674
2716
  .setResponseText('test:/video', media)
2675
2717
  .setResponseValue('test:/main.exe', segmentData)
2676
2718
  .setResponseValue('test:/init.mp4', initSegmentData)
2677
- .setResponseValue('test:/main.mp4', segmentData);
2719
+ .setResponseValue('test:/main.mp4', segmentData)
2720
+ .setResponseValue('data:text/plain;base64,AAECAwQFBgcICQoLDA0ODw==',
2721
+ aes128Key);
2678
2722
 
2679
2723
  await expectAsync(parser.start('test:/master', playerInterface))
2680
2724
  .toBeRejectedWith(Util.jasmineError(error));
@@ -2708,34 +2752,6 @@ describe('HlsParser', () => {
2708
2752
  await verifyError(master, media, error);
2709
2753
  });
2710
2754
 
2711
- it('if all variants are encrypted with AES-128', async () => {
2712
- const master = [
2713
- '#EXTM3U\n',
2714
- '#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1",',
2715
- 'RESOLUTION=960x540,FRAME-RATE=60\n',
2716
- 'video\n',
2717
- ].join('');
2718
-
2719
- const media = [
2720
- '#EXTM3U\n',
2721
- '#EXT-X-TARGETDURATION:6\n',
2722
- '#EXT-X-PLAYLIST-TYPE:VOD\n',
2723
- '#EXT-X-KEY:METHOD=AES-128,',
2724
- 'URI="data:text/plain;base64\n',
2725
- '#EXT-X-MAP:URI="init.mp4"\n',
2726
- '#EXTINF:5,\n',
2727
- '#EXT-X-BYTERANGE:121090@616\n',
2728
- 'main.mp4',
2729
- ].join('');
2730
-
2731
- const error = new shaka.util.Error(
2732
- shaka.util.Error.Severity.CRITICAL,
2733
- shaka.util.Error.Category.MANIFEST,
2734
- Code.HLS_AES_128_ENCRYPTION_NOT_SUPPORTED);
2735
-
2736
- await verifyError(master, media, error);
2737
- });
2738
-
2739
2755
  describe('if required attributes are missing', () => {
2740
2756
  /**
2741
2757
  * @param {string} master
@@ -209,10 +209,10 @@ describe('DrmEngine', () => {
209
209
  await drmEngine.initForPlayback(variants, manifest.offlineSessionIds);
210
210
  await drmEngine.attach(video);
211
211
  await mediaSourceEngine.appendBuffer(
212
- ContentType.VIDEO, videoInitSegment, null, null,
212
+ ContentType.VIDEO, videoInitSegment, null,
213
213
  /* hasClosedCaptions= */ false);
214
214
  await mediaSourceEngine.appendBuffer(
215
- ContentType.AUDIO, audioInitSegment, null, null,
215
+ ContentType.AUDIO, audioInitSegment, null,
216
216
  /* hasClosedCaptions= */ false);
217
217
  await encryptedEventSeen;
218
218
  // With PlayReady, a persistent license policy can cause a different
@@ -245,11 +245,13 @@ describe('DrmEngine', () => {
245
245
  }
246
246
  }
247
247
 
248
+ const reference = dummyReference(0, 10);
249
+
248
250
  await mediaSourceEngine.appendBuffer(
249
- ContentType.VIDEO, videoSegment, 0, 10,
251
+ ContentType.VIDEO, videoSegment, reference,
250
252
  /* hasClosedCaptions= */ false);
251
253
  await mediaSourceEngine.appendBuffer(
252
- ContentType.AUDIO, audioSegment, 0, 10,
254
+ ContentType.AUDIO, audioSegment, reference,
253
255
  /* hasClosedCaptions= */ false);
254
256
 
255
257
  expect(video.buffered.end(0)).toBeGreaterThan(0);
@@ -305,10 +307,10 @@ describe('DrmEngine', () => {
305
307
  await drmEngine.initForPlayback(variants, manifest.offlineSessionIds);
306
308
  await drmEngine.attach(video);
307
309
  await mediaSourceEngine.appendBuffer(
308
- ContentType.VIDEO, videoInitSegment, null, null,
310
+ ContentType.VIDEO, videoInitSegment, null,
309
311
  /* hasClosedCaptions= */ false);
310
312
  await mediaSourceEngine.appendBuffer(
311
- ContentType.AUDIO, audioInitSegment, null, null,
313
+ ContentType.AUDIO, audioInitSegment, null,
312
314
  /* hasClosedCaptions= */ false);
313
315
  await encryptedEventSeen;
314
316
 
@@ -326,11 +328,13 @@ describe('DrmEngine', () => {
326
328
  }
327
329
  }
328
330
 
331
+ const reference = dummyReference(0, 10);
332
+
329
333
  await mediaSourceEngine.appendBuffer(
330
- ContentType.VIDEO, videoSegment, 0, 10,
334
+ ContentType.VIDEO, videoSegment, reference,
331
335
  /* hasClosedCaptions= */ false);
332
336
  await mediaSourceEngine.appendBuffer(
333
- ContentType.AUDIO, audioSegment, 0, 10,
337
+ ContentType.AUDIO, audioSegment, reference,
334
338
  /* hasClosedCaptions= */ false);
335
339
 
336
340
  expect(video.buffered.end(0)).toBeGreaterThan(0);
@@ -345,4 +349,16 @@ describe('DrmEngine', () => {
345
349
  expect(video.currentTime).toBeGreaterThan(0);
346
350
  });
347
351
  }); // describe('ClearKey')
352
+
353
+ function dummyReference(startTime, endTime) {
354
+ return new shaka.media.SegmentReference(
355
+ startTime, endTime,
356
+ /* uris= */ () => ['foo://bar'],
357
+ /* startByte= */ 0,
358
+ /* endByte= */ null,
359
+ /* initSegmentReference= */ null,
360
+ /* timestampOffset= */ 0,
361
+ /* appendWindowStart= */ 0,
362
+ /* appendWindowEnd= */ Infinity);
363
+ }
348
364
  });
@@ -57,32 +57,32 @@ describe('MediaSourceEngine', () => {
57
57
 
58
58
  function appendInit(type) {
59
59
  const segment = generators[type].getInitSegment(Date.now() / 1000);
60
+ const reference = null;
60
61
  return mediaSourceEngine.appendBuffer(
61
- type, segment, null, null, /* hasClosedCaptions= */ false);
62
+ type, segment, reference, /* hasClosedCaptions= */ false);
62
63
  }
63
64
 
64
65
  function append(type, segmentNumber) {
65
66
  const segment = generators[type]
66
67
  .getSegment(segmentNumber, Date.now() / 1000);
68
+ const reference = dummyReference(type, segmentNumber);
67
69
  return mediaSourceEngine.appendBuffer(
68
- type, segment, null, null, /* hasClosedCaptions= */ false);
70
+ type, segment, reference, /* hasClosedCaptions= */ false);
69
71
  }
70
72
 
71
- // The start time and end time should be null for init segment with closed
72
- // captions.
73
73
  function appendInitWithClosedCaptions(type) {
74
74
  const segment = generators[type].getInitSegment(Date.now() / 1000);
75
- return mediaSourceEngine.appendBuffer(type, segment, /* startTime= */ null,
76
- /* endTime= */ null, /* hasClosedCaptions= */ true);
75
+ const reference = null;
76
+ return mediaSourceEngine.appendBuffer(
77
+ type, segment, reference, /* hasClosedCaptions= */ true);
77
78
  }
78
79
 
79
- // The start time and end time should be valid for the segments with closed
80
- // captions.
81
80
  function appendWithClosedCaptions(type, segmentNumber) {
82
81
  const segment = generators[type]
83
82
  .getSegment(segmentNumber, Date.now() / 1000);
84
- return mediaSourceEngine.appendBuffer(type, segment, /* startTime= */ 0,
85
- /* endTime= */ 2, /* hasClosedCaptions= */ true);
83
+ const reference = dummyReference(type, segmentNumber);
84
+ return mediaSourceEngine.appendBuffer(
85
+ type, segment, reference, /* hasClosedCaptions= */ true);
86
86
  }
87
87
 
88
88
  function buffered(type, time) {
@@ -93,6 +93,20 @@ describe('MediaSourceEngine', () => {
93
93
  return mediaSourceEngine.bufferStart(type);
94
94
  }
95
95
 
96
+ function dummyReference(type, segmentNumber) {
97
+ const start = segmentNumber * metadata[type].segmentDuration;
98
+ const end = (segmentNumber + 1) * metadata[type].segmentDuration;
99
+ return new shaka.media.SegmentReference(
100
+ start, end,
101
+ /* uris= */ () => ['foo://bar'],
102
+ /* startByte= */ 0,
103
+ /* endByte= */ null,
104
+ /* initSegmentReference= */ null,
105
+ /* timestampOffset= */ 0,
106
+ /* appendWindowStart= */ 0,
107
+ /* appendWindowEnd= */ Infinity);
108
+ }
109
+
96
110
  function remove(type, segmentNumber) {
97
111
  const start = segmentNumber * metadata[type].segmentDuration;
98
112
  const end = (segmentNumber + 1) * metadata[type].segmentDuration;