shaka-player 3.1.3 → 3.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (182) hide show
  1. package/AUTHORS +3 -0
  2. package/CHANGELOG.md +175 -3
  3. package/CONTRIBUTORS +3 -0
  4. package/README.md +4 -4
  5. package/build/generateExterns.js +1 -1
  6. package/build/types/polyfill +1 -0
  7. package/demo/common/asset.js +9 -7
  8. package/demo/common/assets.js +11 -2
  9. package/demo/common/message_ids.js +2 -1
  10. package/demo/config.js +5 -2
  11. package/demo/index.html +1 -1
  12. package/demo/locales/en.json +2 -1
  13. package/demo/locales/source.json +8 -4
  14. package/demo/main.js +2 -1
  15. package/dist/controls.css +2 -2
  16. package/dist/controls.css.map +1 -1
  17. package/dist/demo.compiled.debug.js +15 -15
  18. package/dist/demo.compiled.debug.map +2 -2
  19. package/dist/demo.compiled.js +15 -15
  20. package/dist/demo.compiled.map +2 -2
  21. package/dist/deps.js +22 -21
  22. package/dist/locales.js +36 -36
  23. package/dist/receiver.compiled.debug.js +9 -9
  24. package/dist/receiver.compiled.debug.map +2 -2
  25. package/dist/receiver.compiled.js +9 -9
  26. package/dist/receiver.compiled.map +2 -2
  27. package/dist/shaka-player.compiled.d.ts +58 -10
  28. package/dist/shaka-player.compiled.debug.d.ts +58 -10
  29. package/dist/shaka-player.compiled.debug.externs.js +777 -717
  30. package/dist/shaka-player.compiled.debug.js +639 -607
  31. package/dist/shaka-player.compiled.debug.map +4 -4
  32. package/dist/shaka-player.compiled.externs.js +777 -717
  33. package/dist/shaka-player.compiled.js +826 -819
  34. package/dist/shaka-player.compiled.map +4 -4
  35. package/dist/shaka-player.ui.d.ts +58 -10
  36. package/dist/shaka-player.ui.debug.d.ts +58 -10
  37. package/dist/shaka-player.ui.debug.externs.js +529 -469
  38. package/dist/shaka-player.ui.debug.js +676 -645
  39. package/dist/shaka-player.ui.debug.map +4 -4
  40. package/dist/shaka-player.ui.externs.js +529 -469
  41. package/dist/shaka-player.ui.js +963 -955
  42. package/dist/shaka-player.ui.map +4 -4
  43. package/docs/tutorials/fairplay.md +1 -1
  44. package/docs/tutorials/ui.md +5 -5
  45. package/externs/aria.js +6 -0
  46. package/externs/shaka/ads.js +24 -1
  47. package/externs/shaka/manifest.js +6 -1
  48. package/externs/shaka/offline.js +5 -0
  49. package/externs/shaka/player.js +53 -9
  50. package/externs/shaka/text.js +10 -10
  51. package/externs/texttrack.js +3 -0
  52. package/karma.conf.js +1 -0
  53. package/lib/ads/ad_manager.js +16 -15
  54. package/lib/ads/client_side_ad_manager.js +52 -88
  55. package/lib/ads/server_side_ad_manager.js +26 -12
  56. package/lib/cast/cast_proxy.js +11 -2
  57. package/lib/cast/cast_receiver.js +2 -1
  58. package/lib/cast/cast_sender.js +2 -2
  59. package/lib/cast/cast_utils.js +6 -1
  60. package/lib/cea/cea608_data_channel.js +56 -56
  61. package/lib/cea/cea608_memory.js +33 -33
  62. package/lib/cea/cea708_service.js +43 -43
  63. package/lib/cea/cea708_window.js +38 -38
  64. package/lib/cea/cea_decoder.js +13 -14
  65. package/lib/cea/cea_utils.js +23 -23
  66. package/lib/cea/dtvcc_packet_builder.js +7 -7
  67. package/lib/cea/i_caption_decoder.js +2 -3
  68. package/lib/cea/i_cea_parser.js +2 -2
  69. package/lib/cea/mp4_cea_parser.js +6 -6
  70. package/lib/dash/dash_parser.js +98 -2
  71. package/lib/dash/segment_list.js +1 -1
  72. package/lib/dash/segment_template.js +1 -1
  73. package/lib/hls/hls_parser.js +159 -29
  74. package/lib/media/closed_caption_parser.js +1 -2
  75. package/lib/media/drm_engine.js +169 -192
  76. package/lib/media/gap_jumping_controller.js +4 -2
  77. package/lib/media/media_source_engine.js +1 -2
  78. package/lib/media/segment_index.js +3 -1
  79. package/lib/media/segment_reference.js +19 -2
  80. package/lib/media/streaming_engine.js +8 -7
  81. package/lib/net/networking_engine.js +3 -1
  82. package/lib/offline/download_manager.js +1 -2
  83. package/lib/offline/indexeddb/v1_storage_cell.js +1 -0
  84. package/lib/offline/indexeddb/v2_storage_cell.js +1 -0
  85. package/lib/offline/manifest_converter.js +3 -1
  86. package/lib/offline/storage.js +12 -14
  87. package/lib/player.js +437 -167
  88. package/lib/polyfill/aria.js +82 -0
  89. package/lib/polyfill/media_capabilities.js +7 -1
  90. package/lib/polyfill/mediasource.js +6 -0
  91. package/lib/polyfill/orientation.js +1 -1
  92. package/lib/polyfill/patchedmediakeys_apple.js +4 -4
  93. package/lib/polyfill/patchedmediakeys_ms.js +4 -4
  94. package/lib/polyfill/patchedmediakeys_webkit.js +4 -4
  95. package/lib/routing/walker.js +2 -3
  96. package/lib/text/mp4_ttml_parser.js +3 -0
  97. package/lib/text/srt_text_parser.js +4 -4
  98. package/lib/text/text_engine.js +16 -4
  99. package/lib/text/ttml_text_parser.js +5 -0
  100. package/lib/text/ui_text_displayer.js +85 -37
  101. package/lib/text/vtt_text_parser.js +2 -2
  102. package/lib/text/web_vtt_generator.js +55 -12
  103. package/lib/util/error.js +6 -0
  104. package/lib/util/fake_event.js +53 -7
  105. package/lib/util/manifest_parser_utils.js +1 -0
  106. package/lib/util/mp4_box_parsers.js +20 -20
  107. package/lib/util/multi_map.js +8 -0
  108. package/lib/util/periods.js +4 -1
  109. package/lib/util/platform.js +13 -1
  110. package/lib/util/player_configuration.js +13 -1
  111. package/lib/util/stream_utils.js +153 -85
  112. package/lib/util/xml_utils.js +31 -12
  113. package/package.json +1 -1
  114. package/roadmap.md +24 -22
  115. package/shaka-player.uncompiled.js +1 -0
  116. package/test/cast/cast_proxy_unit.js +12 -6
  117. package/test/cast/cast_utils_unit.js +2 -1
  118. package/test/cea/cea608_memory_unit.js +1 -1
  119. package/test/cea/cea708_service_unit.js +8 -8
  120. package/test/cea/cea708_window_unit.js +6 -6
  121. package/test/cea/cea_decoder_unit.js +1 -1
  122. package/test/cea/dtvcc_packet_builder_unit.js +2 -2
  123. package/test/dash/dash_parser_manifest_unit.js +230 -2
  124. package/test/hls/hls_parser_unit.js +255 -1
  125. package/test/media/closed_caption_parser_unit.js +6 -1
  126. package/test/media/drm_engine_integration.js +86 -12
  127. package/test/media/drm_engine_unit.js +353 -450
  128. package/test/offline/manifest_convert_unit.js +2 -0
  129. package/test/offline/storage_integration.js +9 -19
  130. package/test/player_integration.js +90 -0
  131. package/test/player_src_equals_integration.js +52 -0
  132. package/test/player_unit.js +11 -33
  133. package/test/test/assets/3675/chunk-stream0-00001.m4s +0 -0
  134. package/test/test/assets/3675/chunk-stream0-00002.m4s +0 -0
  135. package/test/test/assets/3675/chunk-stream0-00003.m4s +0 -0
  136. package/test/test/assets/3675/chunk-stream0-00004.m4s +0 -0
  137. package/test/test/assets/3675/chunk-stream0-00005.m4s +0 -0
  138. package/test/test/assets/3675/chunk-stream0-00006.m4s +0 -0
  139. package/test/test/assets/3675/chunk-stream0-00007.m4s +0 -0
  140. package/test/test/assets/3675/dash_0.mpd +30 -0
  141. package/test/test/assets/3675/dash_1.mpd +30 -0
  142. package/test/test/assets/3675/dash_2.mpd +30 -0
  143. package/test/test/assets/3675/dash_3.mpd +30 -0
  144. package/test/test/assets/3675/dash_4.mpd +30 -0
  145. package/test/test/assets/3675/dash_5.mpd +26 -0
  146. package/test/test/assets/3675/init-stream0.m4s +0 -0
  147. package/test/test/assets/chapters.srt +11 -0
  148. package/test/test/assets/chapters.vtt +10 -0
  149. package/test/test/util/cea_utils.js +12 -12
  150. package/test/test/util/fake_ad_manager.js +8 -1
  151. package/test/test/util/manifest_generator.js +2 -0
  152. package/test/test/util/manifest_parser_util.js +3 -1
  153. package/test/text/ttml_text_parser_unit.js +9 -0
  154. package/test/text/ui_text_displayer_unit.js +29 -17
  155. package/test/text/web_vtt_generator_unit.js +81 -7
  156. package/test/ui/localization_unit.js +15 -14
  157. package/test/ui/text_displayer_layout_unit.js +13 -1
  158. package/test/util/stream_utils_unit.js +153 -10
  159. package/ui/airplay_button.js +1 -1
  160. package/ui/audio_language_selection.js +2 -5
  161. package/ui/cast_button.js +6 -9
  162. package/ui/constants.js +0 -3
  163. package/ui/controls.js +4 -6
  164. package/ui/fast_forward_button.js +2 -3
  165. package/ui/fullscreen_button.js +1 -3
  166. package/ui/language_utils.js +1 -1
  167. package/ui/less/containers.less +3 -7
  168. package/ui/locales/ru.json +6 -6
  169. package/ui/localization.js +28 -51
  170. package/ui/loop_button.js +1 -3
  171. package/ui/mute_button.js +1 -3
  172. package/ui/overflow_menu.js +2 -3
  173. package/ui/pip_button.js +7 -10
  174. package/ui/playback_rate_selection.js +3 -6
  175. package/ui/presentation_time.js +1 -3
  176. package/ui/resolution_selection.js +4 -7
  177. package/ui/rewind_button.js +2 -3
  178. package/ui/seek_bar.js +2 -4
  179. package/ui/spacer.js +1 -1
  180. package/ui/text_selection.js +8 -11
  181. package/ui/ui_utils.js +1 -1
  182. package/ui/volume_bar.js +1 -3
@@ -23,14 +23,8 @@ goog.require('shaka.util.PublicPromise');
23
23
  goog.require('shaka.util.StringUtils');
24
24
  goog.require('shaka.util.Uint8ArrayUtils');
25
25
 
26
- for (const useMediaCapabilities of [true, false]) {
27
- const isEnabled = useMediaCapabilities ? 'enabled' : 'disabled';
28
- describe('DrmEngine with MediaCapabilities ' + isEnabled, () => {
29
- testDrmEngine(useMediaCapabilities);
30
- });
31
- }
32
26
 
33
- function testDrmEngine(useMediaCapabilities) {
27
+ describe('DrmEngine', () => {
34
28
  const Util = shaka.test.Util;
35
29
 
36
30
  const originalRequestMediaKeySystemAccess =
@@ -39,8 +33,6 @@ function testDrmEngine(useMediaCapabilities) {
39
33
  const originalBatchTime = shaka.media.DrmEngine.KEY_STATUS_BATCH_TIME;
40
34
  const originalDecodingInfo = window.shakaMediaCapabilities.decodingInfo;
41
35
 
42
- /** @type {!jasmine.Spy} */
43
- let requestMediaKeySystemAccessSpy;
44
36
  /** @type {!jasmine.Spy} */
45
37
  let decodingInfoSpy;
46
38
  /** @type {!jasmine.Spy} */
@@ -83,10 +75,6 @@ function testDrmEngine(useMediaCapabilities) {
83
75
  });
84
76
 
85
77
  beforeEach(() => {
86
- requestMediaKeySystemAccessSpy =
87
- jasmine.createSpy('requestMediaKeySystemAccess');
88
- navigator.requestMediaKeySystemAccess =
89
- shaka.test.Util.spyFunc(requestMediaKeySystemAccessSpy);
90
78
  decodingInfoSpy = jasmine.createSpy('decodingInfo');
91
79
  window.shakaMediaCapabilities.decodingInfo =
92
80
  shaka.test.Util.spyFunc(decodingInfoSpy);
@@ -121,7 +109,7 @@ function testDrmEngine(useMediaCapabilities) {
121
109
  logErrorSpy.and.callFake(fail);
122
110
 
123
111
  // By default, allow keysystem drm.abc
124
- setRequestMediaKeySystemAccessSpy(['drm.abc']);
112
+ setDecodingInfoSpy(['drm.abc']);
125
113
 
126
114
  mockVideo = new shaka.test.FakeVideo();
127
115
 
@@ -192,74 +180,62 @@ function testDrmEngine(useMediaCapabilities) {
192
180
  describe('init', () => {
193
181
  it('stops on first available key system', async () => {
194
182
  // Accept both drm.abc and drm.def. Only one can be chosen.
195
- setRequestMediaKeySystemAccessSpy(['drm.abc', 'drm.def']);
183
+ setDecodingInfoSpy(['drm.abc', 'drm.def']);
196
184
 
197
185
  const variants = manifest.variants;
198
- await drmEngine.initForPlayback(variants, manifest.offlineSessionIds,
199
- useMediaCapabilities);
186
+ await drmEngine.initForPlayback(variants, manifest.offlineSessionIds);
200
187
  expect(drmEngine.initialized()).toBe(true);
201
188
  expect(shaka.media.DrmEngine.keySystem(drmEngine.getDrmInfo()))
202
189
  .toBe('drm.abc');
203
-
204
- if (!useMediaCapabilities) {
205
- // Only one call, since the first key system worked.
206
- expect(requestMediaKeySystemAccessSpy).toHaveBeenCalledTimes(1);
207
- expect(requestMediaKeySystemAccessSpy)
208
- .toHaveBeenCalledWith('drm.abc', jasmine.any(Object));
209
- }
210
190
  });
211
191
 
212
192
  it('tries to get the key systems in the order they appear in', async () => {
213
193
  // Fail both key systems.
214
- setRequestMediaKeySystemAccessSpy([]);
194
+ setDecodingInfoSpy([]);
215
195
 
216
196
  const variants = manifest.variants;
217
197
  await expectAsync(drmEngine.initForPlayback(variants,
218
- manifest.offlineSessionIds, useMediaCapabilities)).toBeRejected();
198
+ manifest.offlineSessionIds)).toBeRejected();
219
199
 
220
- if (useMediaCapabilities) {
221
- expect(decodingInfoSpy).toHaveBeenCalledTimes(2);
222
- expect(decodingInfoSpy).toHaveBeenCalledWith(containing({
223
- keySystemConfiguration: containing({keySystem: 'drm.abc'}),
224
- }));
200
+ expect(decodingInfoSpy).toHaveBeenCalledTimes(2);
201
+ expect(decodingInfoSpy).toHaveBeenCalledWith(containing({
202
+ keySystemConfiguration: containing({keySystem: 'drm.abc'}),
203
+ }));
225
204
 
226
- expect(decodingInfoSpy).toHaveBeenCalledWith(containing({
227
- keySystemConfiguration: containing({keySystem: 'drm.def'}),
228
- }));
229
- } else {
230
- expect(requestMediaKeySystemAccessSpy).toHaveBeenCalledTimes(2);
231
- // These should be in the same order as the key systems appear in the
232
- // manifest.
233
- const calls = requestMediaKeySystemAccessSpy.calls;
234
- expect(calls.argsFor(0)[0]).toBe('drm.abc');
235
- expect(calls.argsFor(1)[0]).toBe('drm.def');
236
- }
205
+ expect(decodingInfoSpy).toHaveBeenCalledWith(containing({
206
+ keySystemConfiguration: containing({keySystem: 'drm.def'}),
207
+ }));
237
208
  });
238
209
 
239
210
  it('tries the second key system if the first fails', async () => {
240
211
  // Accept drm.def, but not drm.abc.
241
- setRequestMediaKeySystemAccessSpy(['drm.def']);
212
+ setDecodingInfoSpy(['drm.def']);
242
213
 
243
214
  const variants = manifest.variants;
244
- await drmEngine.initForPlayback(variants, manifest.offlineSessionIds,
245
- useMediaCapabilities);
215
+ await drmEngine.initForPlayback(variants, manifest.offlineSessionIds);
246
216
  expect(drmEngine.initialized()).toBe(true);
247
217
  expect(shaka.media.DrmEngine.keySystem(drmEngine.getDrmInfo()))
248
218
  .toBe('drm.def');
219
+ });
249
220
 
250
- if (!useMediaCapabilities) {
251
- // Both key systems were tried, since the first one failed.
252
- expect(requestMediaKeySystemAccessSpy).toHaveBeenCalledTimes(2);
253
- expect(requestMediaKeySystemAccessSpy)
254
- .toHaveBeenCalledWith('drm.abc', jasmine.any(Object));
255
- expect(requestMediaKeySystemAccessSpy)
256
- .toHaveBeenCalledWith('drm.def', jasmine.any(Object));
257
- }
221
+ it('chooses systems by configured preferredKeySystems', async () => {
222
+ // Accept both drm.abc and drm.def. Only one can be chosen.
223
+ setDecodingInfoSpy(['drm.abc', 'drm.def']);
224
+ config.preferredKeySystems = ['drm.def'];
225
+ drmEngine.configure(config);
226
+ logErrorSpy.and.stub();
227
+
228
+ const variants = manifest.variants;
229
+ await drmEngine.initForPlayback(variants, manifest.offlineSessionIds);
230
+
231
+ expect(variants[0].decodingInfos.length).toBe(2);
232
+ expect(shaka.media.DrmEngine.keySystem(drmEngine.getDrmInfo()))
233
+ .toBe('drm.def');
258
234
  });
259
235
 
260
236
  it('chooses systems with configured license servers', async () => {
261
237
  // Accept both drm.abc and drm.def. Only one can be chosen.
262
- setRequestMediaKeySystemAccessSpy(['drm.abc', 'drm.def']);
238
+ setDecodingInfoSpy(['drm.abc', 'drm.def']);
263
239
 
264
240
  // Remove the server URI for drm.abc, which appears first in the manifest.
265
241
  delete config.servers['drm.abc'];
@@ -268,25 +244,16 @@ function testDrmEngine(useMediaCapabilities) {
268
244
  logErrorSpy.and.stub();
269
245
 
270
246
  const variants = manifest.variants;
271
- await drmEngine.initForPlayback(variants, manifest.offlineSessionIds,
272
- useMediaCapabilities);
247
+ await drmEngine.initForPlayback(variants, manifest.offlineSessionIds);
273
248
 
274
- if (useMediaCapabilities) {
275
- expect(variants[0].decodingInfos.length).toBe(2);
276
- } else {
277
- expect(requestMediaKeySystemAccessSpy).toHaveBeenCalledTimes(1);
278
- // Although drm.def appears second in the manifest, it is queried first
279
- // and also selected because it has a server configured.
280
- const calls = requestMediaKeySystemAccessSpy.calls;
281
- expect(calls.argsFor(0)[0]).toBe('drm.def');
282
- }
249
+ expect(variants[0].decodingInfos.length).toBe(2);
283
250
  expect(shaka.media.DrmEngine.keySystem(drmEngine.getDrmInfo()))
284
251
  .toBe('drm.def');
285
252
  });
286
253
 
287
254
  it('overrides manifest with configured license servers', async () => {
288
255
  // Accept both drm.abc and drm.def. Only one can be chosen.
289
- setRequestMediaKeySystemAccessSpy(['drm.abc', 'drm.def']);
256
+ setDecodingInfoSpy(['drm.abc', 'drm.def']);
290
257
 
291
258
  // Add manifest-supplied license servers for both.
292
259
  tweakDrmInfos((drmInfos) => {
@@ -313,18 +280,9 @@ function testDrmEngine(useMediaCapabilities) {
313
280
  logErrorSpy.and.stub();
314
281
 
315
282
  const variants = manifest.variants;
316
- await drmEngine.initForPlayback(variants, manifest.offlineSessionIds,
317
- useMediaCapabilities);
318
-
319
- if (useMediaCapabilities) {
320
- expect(variants[0].decodingInfos.length).toBe(2);
321
- } else {
322
- // Although drm.def appears second in the manifest, it is queried first
323
- // because it has a server configured. The manifest-supplied server for
324
- // drm.abc will not be used.
325
- expect(requestMediaKeySystemAccessSpy).toHaveBeenCalledTimes(1);
326
- }
283
+ await drmEngine.initForPlayback(variants, manifest.offlineSessionIds);
327
284
 
285
+ expect(variants[0].decodingInfos.length).toBe(2);
328
286
  const selectedDrmInfo = drmEngine.getDrmInfo();
329
287
  expect(selectedDrmInfo).not.toBe(null);
330
288
  expect(selectedDrmInfo.keySystem).toBe('drm.def');
@@ -332,11 +290,10 @@ function testDrmEngine(useMediaCapabilities) {
332
290
  });
333
291
 
334
292
  it('detects content type capabilities of key system', async () => {
335
- setRequestMediaKeySystemAccessSpy(['drm.abc']);
293
+ setDecodingInfoSpy(['drm.abc']);
336
294
 
337
295
  const variants = manifest.variants;
338
- await drmEngine.initForPlayback(variants, manifest.offlineSessionIds,
339
- useMediaCapabilities);
296
+ await drmEngine.initForPlayback(variants, manifest.offlineSessionIds);
340
297
  expect(drmEngine.initialized()).toBe(true);
341
298
  expect(drmEngine.willSupport('audio/webm')).toBeTruthy();
342
299
  expect(drmEngine.willSupport('video/mp4; codecs="fake"')).toBeTruthy();
@@ -352,7 +309,7 @@ function testDrmEngine(useMediaCapabilities) {
352
309
 
353
310
  it('fails to initialize if no key systems are available', async () => {
354
311
  // Accept no key systems.
355
- setRequestMediaKeySystemAccessSpy([]);
312
+ setDecodingInfoSpy([]);
356
313
 
357
314
  const expected = Util.jasmineError(new shaka.util.Error(
358
315
  shaka.util.Error.Severity.CRITICAL,
@@ -360,21 +317,12 @@ function testDrmEngine(useMediaCapabilities) {
360
317
  shaka.util.Error.Code.REQUESTED_KEY_SYSTEM_CONFIG_UNAVAILABLE));
361
318
  const variants = manifest.variants;
362
319
  await expectAsync(
363
- drmEngine.initForPlayback(variants, manifest.offlineSessionIds,
364
- useMediaCapabilities)).toBeRejectedWith(expected);
320
+ drmEngine.initForPlayback(variants, manifest.offlineSessionIds))
321
+ .toBeRejectedWith(expected);
365
322
 
366
323
  expect(drmEngine.initialized()).toBe(false);
367
324
 
368
- if (useMediaCapabilities) {
369
- expect(variants[0].decodingInfos.length).toBe(2);
370
- } else {
371
- // Both key systems were tried, since the first one failed.
372
- expect(requestMediaKeySystemAccessSpy).toHaveBeenCalledTimes(2);
373
- expect(requestMediaKeySystemAccessSpy)
374
- .toHaveBeenCalledWith('drm.abc', jasmine.any(Object));
375
- expect(requestMediaKeySystemAccessSpy)
376
- .toHaveBeenCalledWith('drm.def', jasmine.any(Object));
377
- }
325
+ expect(variants[0].decodingInfos.length).toBe(2);
378
326
  });
379
327
 
380
328
  it('does not error for unencrypted assets with no EME', async () => {
@@ -390,13 +338,13 @@ function testDrmEngine(useMediaCapabilities) {
390
338
  });
391
339
 
392
340
  // Accept no key systems, simulating a lack of EME.
393
- setRequestMediaKeySystemAccessSpy([]);
341
+ setDecodingInfoSpy([]);
394
342
 
395
343
  const variants = manifest.variants;
396
344
  // All that matters here is that we don't throw.
397
345
  await expectAsync(
398
- drmEngine.initForPlayback(variants, manifest.offlineSessionIds,
399
- useMediaCapabilities)).not.toBeRejected();
346
+ drmEngine.initForPlayback(variants, manifest.offlineSessionIds))
347
+ .not.toBeRejected();
400
348
  });
401
349
 
402
350
  it('fails to initialize if no key systems are recognized', async () => {
@@ -413,18 +361,13 @@ function testDrmEngine(useMediaCapabilities) {
413
361
  shaka.util.Error.Code.NO_RECOGNIZED_KEY_SYSTEMS));
414
362
  const variants = manifest.variants;
415
363
  await expectAsync(
416
- drmEngine.initForPlayback(variants, manifest.offlineSessionIds,
417
- useMediaCapabilities)).toBeRejectedWith(expected);
364
+ drmEngine.initForPlayback(variants, manifest.offlineSessionIds))
365
+ .toBeRejectedWith(expected);
418
366
 
419
367
  expect(drmEngine.initialized()).toBe(false);
420
368
 
421
- if (useMediaCapabilities) {
422
- expect(variants[0].decodingInfos.length).toBe(1);
423
- expect(variants[0].decodingInfos[0].keySystemAccess).toBeFalsy();
424
- } else {
425
- // No key systems were tried, since the dummy placeholder was detected.
426
- expect(requestMediaKeySystemAccessSpy).not.toHaveBeenCalled();
427
- }
369
+ expect(variants[0].decodingInfos.length).toBe(1);
370
+ expect(variants[0].decodingInfos[0].keySystemAccess).toBeFalsy();
428
371
  });
429
372
 
430
373
  it('fails to initialize if the CDM cannot be created', async () => {
@@ -438,146 +381,95 @@ function testDrmEngine(useMediaCapabilities) {
438
381
  'whoops!'));
439
382
  const variants = manifest.variants;
440
383
  await expectAsync(
441
- drmEngine.initForPlayback(variants, manifest.offlineSessionIds,
442
- useMediaCapabilities)).toBeRejectedWith(expected);
384
+ drmEngine.initForPlayback(variants, manifest.offlineSessionIds))
385
+ .toBeRejectedWith(expected);
443
386
 
444
387
  expect(drmEngine.initialized()).toBe(false);
445
-
446
- if (useMediaCapabilities) {
447
- expect(variants[0].decodingInfos.length).toBe(2);
448
- } else {
449
- expect(requestMediaKeySystemAccessSpy).toHaveBeenCalledTimes(1);
450
- expect(requestMediaKeySystemAccessSpy)
451
- .toHaveBeenCalledWith('drm.abc', jasmine.any(Object));
452
- }
388
+ expect(variants[0].decodingInfos.length).toBe(2);
453
389
  });
454
390
 
455
391
  it('queries audio/video capabilities', async () => {
456
- setRequestMediaKeySystemAccessSpy([]);
392
+ setDecodingInfoSpy([]);
457
393
 
458
394
  const variants = manifest.variants;
459
395
  await expectAsync(
460
- drmEngine.initForPlayback(variants, manifest.offlineSessionIds,
461
- useMediaCapabilities)).toBeRejected();
396
+ drmEngine.initForPlayback(variants, manifest.offlineSessionIds))
397
+ .toBeRejected();
462
398
 
463
399
  expect(drmEngine.initialized()).toBe(false);
464
400
 
465
- if (useMediaCapabilities) {
466
- const decodingConfig1 = containing({
467
- video: containing({
468
- contentType: 'video/foo; codecs="vbar"',
469
- }),
470
- audio: containing({
471
- contentType: 'audio/foo; codecs="abar"',
472
- }),
473
- keySystemConfiguration: containing({
474
- keySystem: 'drm.abc',
475
- persistentState: 'optional',
476
- distinctiveIdentifier: 'optional',
477
- sessionTypes: ['temporary'],
478
- initDataType: 'cenc',
479
- }),
480
- });
401
+ const decodingConfig1 = containing({
402
+ video: containing({
403
+ contentType: 'video/foo; codecs="vbar"',
404
+ }),
405
+ audio: containing({
406
+ contentType: 'audio/foo; codecs="abar"',
407
+ }),
408
+ keySystemConfiguration: containing({
409
+ keySystem: 'drm.abc',
410
+ persistentState: 'optional',
411
+ distinctiveIdentifier: 'optional',
412
+ sessionTypes: ['temporary'],
413
+ initDataType: 'cenc',
414
+ }),
415
+ });
481
416
 
482
- const decodingConfig2 = containing({
483
- video: containing({
484
- contentType: 'video/foo; codecs="vbar"',
485
- }),
486
- audio: containing({
487
- contentType: 'audio/foo; codecs="abar"',
488
- }),
489
- keySystemConfiguration: containing({
490
- keySystem: 'drm.def',
491
- persistentState: 'optional',
492
- distinctiveIdentifier: 'optional',
493
- sessionTypes: ['temporary'],
494
- initDataType: 'cenc',
495
- }),
496
- });
497
- expect(decodingInfoSpy).toHaveBeenCalledTimes(2);
498
- expect(decodingInfoSpy).toHaveBeenCalledWith(decodingConfig1);
499
- expect(decodingInfoSpy).toHaveBeenCalledWith(decodingConfig2);
500
- } else {
501
- expect(requestMediaKeySystemAccessSpy).toHaveBeenCalledTimes(2);
502
- expect(requestMediaKeySystemAccessSpy)
503
- .toHaveBeenCalledWith('drm.abc', [jasmine.objectContaining({
504
- // audioCapabilities not present.
505
- videoCapabilities: [jasmine.objectContaining({
506
- contentType: 'video/foo; codecs="vbar"',
507
- })],
508
- distinctiveIdentifier: 'optional',
509
- persistentState: 'optional',
510
- sessionTypes: ['temporary'],
511
- })]);
512
- expect(requestMediaKeySystemAccessSpy)
513
- .toHaveBeenCalledWith('drm.def', [jasmine.objectContaining({
514
- audioCapabilities: [jasmine.objectContaining({
515
- contentType: 'audio/foo; codecs="abar"',
516
- })],
517
- // videoCapabilities not present.
518
- distinctiveIdentifier: 'optional',
519
- persistentState: 'optional',
520
- sessionTypes: ['temporary'],
521
- })]);
522
- }
417
+ const decodingConfig2 = containing({
418
+ video: containing({
419
+ contentType: 'video/foo; codecs="vbar"',
420
+ }),
421
+ audio: containing({
422
+ contentType: 'audio/foo; codecs="abar"',
423
+ }),
424
+ keySystemConfiguration: containing({
425
+ keySystem: 'drm.def',
426
+ persistentState: 'optional',
427
+ distinctiveIdentifier: 'optional',
428
+ sessionTypes: ['temporary'],
429
+ initDataType: 'cenc',
430
+ }),
431
+ });
432
+ expect(decodingInfoSpy).toHaveBeenCalledTimes(2);
433
+ expect(decodingInfoSpy).toHaveBeenCalledWith(decodingConfig1);
434
+ expect(decodingInfoSpy).toHaveBeenCalledWith(decodingConfig2);
523
435
  });
524
436
 
525
437
  it('asks for persistent state and license for offline', async () => {
526
- setRequestMediaKeySystemAccessSpy([]);
438
+ setDecodingInfoSpy([]);
527
439
 
528
440
  const variants = manifest.variants;
529
441
  await expectAsync(
530
- drmEngine.initForStorage(variants, /* usePersistentLicense= */ true,
531
- useMediaCapabilities)).toBeRejected();
442
+ drmEngine.initForStorage(variants, /* usePersistentLicense= */ true))
443
+ .toBeRejected();
532
444
 
533
445
  expect(drmEngine.initialized()).toBe(false);
534
446
 
535
- if (useMediaCapabilities) {
536
- expect(decodingInfoSpy).toHaveBeenCalledTimes(2);
537
- expect(decodingInfoSpy).toHaveBeenCalledWith(containing({
538
- keySystemConfiguration: containing({
539
- keySystem: 'drm.abc',
540
- distinctiveIdentifier: 'optional',
541
- persistentState: 'required',
542
- sessionTypes: ['persistent-license'],
543
- initDataType: 'cenc',
544
- }),
447
+ expect(decodingInfoSpy).toHaveBeenCalledTimes(2);
448
+ expect(decodingInfoSpy).toHaveBeenCalledWith(containing({
449
+ keySystemConfiguration: containing({
450
+ keySystem: 'drm.abc',
451
+ distinctiveIdentifier: 'optional',
452
+ persistentState: 'required',
453
+ sessionTypes: ['persistent-license'],
454
+ initDataType: 'cenc',
545
455
  }),
546
- );
547
-
548
- expect(decodingInfoSpy).toHaveBeenCalledWith(containing({
549
- keySystemConfiguration: containing({
550
- keySystem: 'drm.def',
551
- distinctiveIdentifier: 'optional',
552
- persistentState: 'required',
553
- sessionTypes: ['persistent-license'],
554
- initDataType: 'cenc',
555
- }),
456
+ }),
457
+ );
458
+
459
+ expect(decodingInfoSpy).toHaveBeenCalledWith(containing({
460
+ keySystemConfiguration: containing({
461
+ keySystem: 'drm.def',
462
+ distinctiveIdentifier: 'optional',
463
+ persistentState: 'required',
464
+ sessionTypes: ['persistent-license'],
465
+ initDataType: 'cenc',
556
466
  }),
557
- );
558
- } else {
559
- expect(requestMediaKeySystemAccessSpy).toHaveBeenCalledTimes(2);
560
- expect(requestMediaKeySystemAccessSpy).toHaveBeenCalledWith('drm.abc', [
561
- jasmine.objectContaining({
562
- distinctiveIdentifier: 'optional',
563
- persistentState: 'required',
564
- sessionTypes: ['persistent-license'],
565
- initDataTypes: ['cenc'],
566
- }),
567
- ]);
568
- expect(requestMediaKeySystemAccessSpy).toHaveBeenCalledWith('drm.def', [
569
- jasmine.objectContaining({
570
- distinctiveIdentifier: 'optional',
571
- persistentState: 'required',
572
- sessionTypes: ['persistent-license'],
573
- initDataTypes: ['cenc'],
574
- }),
575
- ]);
576
- }
467
+ }),
468
+ );
577
469
  });
578
470
 
579
471
  it('honors distinctive identifier and persistent state', async () => {
580
- setRequestMediaKeySystemAccessSpy([]);
472
+ setDecodingInfoSpy([]);
581
473
  tweakDrmInfos((drmInfos) => {
582
474
  drmInfos[0].distinctiveIdentifierRequired = true;
583
475
  drmInfos[1].persistentStateRequired = true;
@@ -585,52 +477,36 @@ function testDrmEngine(useMediaCapabilities) {
585
477
 
586
478
  const variants = manifest.variants;
587
479
  await expectAsync(
588
- drmEngine.initForPlayback(variants, manifest.offlineSessionIds,
589
- useMediaCapabilities)).toBeRejected();
480
+ drmEngine.initForPlayback(variants, manifest.offlineSessionIds))
481
+ .toBeRejected();
590
482
 
591
483
  expect(drmEngine.initialized()).toBe(false);
592
484
 
593
- if (useMediaCapabilities) {
594
- expect(decodingInfoSpy).toHaveBeenCalledTimes(2);
595
- expect(decodingInfoSpy).toHaveBeenCalledWith(containing({
596
- keySystemConfiguration: containing({
597
- keySystem: 'drm.abc',
598
- distinctiveIdentifier: 'required',
599
- persistentState: 'optional',
600
- sessionTypes: ['temporary'],
601
- }),
485
+ expect(decodingInfoSpy).toHaveBeenCalledTimes(2);
486
+ expect(decodingInfoSpy).toHaveBeenCalledWith(containing({
487
+ keySystemConfiguration: containing({
488
+ keySystem: 'drm.abc',
489
+ distinctiveIdentifier: 'required',
490
+ persistentState: 'optional',
491
+ sessionTypes: ['temporary'],
602
492
  }),
603
- );
604
-
605
- expect(decodingInfoSpy).toHaveBeenCalledWith(containing({
606
- keySystemConfiguration: containing({
607
- keySystem: 'drm.def',
608
- distinctiveIdentifier: 'optional',
609
- persistentState: 'required',
610
- sessionTypes: ['temporary'],
611
- }),
493
+ }),
494
+ );
495
+
496
+ expect(decodingInfoSpy).toHaveBeenCalledWith(containing({
497
+ keySystemConfiguration: containing({
498
+ keySystem: 'drm.def',
499
+ distinctiveIdentifier: 'optional',
500
+ persistentState: 'required',
501
+ sessionTypes: ['temporary'],
612
502
  }),
613
- );
614
- } else {
615
- expect(requestMediaKeySystemAccessSpy).toHaveBeenCalledTimes(2);
616
- expect(requestMediaKeySystemAccessSpy)
617
- .toHaveBeenCalledWith('drm.abc', [jasmine.objectContaining({
618
- distinctiveIdentifier: 'required',
619
- persistentState: 'optional',
620
- sessionTypes: ['temporary'],
621
- })]);
622
- expect(requestMediaKeySystemAccessSpy)
623
- .toHaveBeenCalledWith('drm.def', [jasmine.objectContaining({
624
- distinctiveIdentifier: 'optional',
625
- persistentState: 'required',
626
- sessionTypes: ['temporary'],
627
- })]);
628
- }
503
+ }),
504
+ );
629
505
  });
630
506
 
631
507
  it('makes no queries for key systems with clear content if no key config',
632
508
  async () => {
633
- setRequestMediaKeySystemAccessSpy([]);
509
+ setDecodingInfoSpy([]);
634
510
  manifest.variants[0].video.drmInfos = [];
635
511
  manifest.variants[0].audio.drmInfos = [];
636
512
  config.servers = {};
@@ -638,24 +514,18 @@ function testDrmEngine(useMediaCapabilities) {
638
514
 
639
515
  drmEngine.configure(config);
640
516
  const variants = manifest.variants;
641
- await drmEngine.initForPlayback(variants, manifest.offlineSessionIds,
642
- useMediaCapabilities);
643
-
644
- if (useMediaCapabilities) {
645
- // Gets decodingInfo for clear content with no keySystemAccess.
646
- expect(variants[0].decodingInfos.length).toBe(1);
647
- expect(variants[0].decodingInfos[0].keySystemAccess).toBeFalsy();
648
- } else {
649
- // Makes no queries for clear content.
650
- expect(requestMediaKeySystemAccessSpy).not.toHaveBeenCalled();
651
- }
517
+ await drmEngine.initForPlayback(variants, manifest.offlineSessionIds);
518
+
519
+ // Gets decodingInfo for clear content with no keySystemAccess.
520
+ expect(variants[0].decodingInfos.length).toBe(1);
521
+ expect(variants[0].decodingInfos[0].keySystemAccess).toBeFalsy();
652
522
  expect(
653
523
  shaka.media.DrmEngine.keySystem(drmEngine.getDrmInfo())).toBe('');
654
524
  expect(drmEngine.initialized()).toBe(true);
655
525
  });
656
526
 
657
527
  it('makes queries for clear content if key is configured', async () => {
658
- setRequestMediaKeySystemAccessSpy(['drm.abc']);
528
+ setDecodingInfoSpy(['drm.abc']);
659
529
  manifest.variants[0].video.drmInfos = [];
660
530
  manifest.variants[0].audio.drmInfos = [];
661
531
  config.servers = {
@@ -664,16 +534,11 @@ function testDrmEngine(useMediaCapabilities) {
664
534
 
665
535
  drmEngine.configure(config);
666
536
  const variants = manifest.variants;
667
- await drmEngine.initForPlayback(variants, manifest.offlineSessionIds,
668
- useMediaCapabilities);
537
+ await drmEngine.initForPlayback(variants, manifest.offlineSessionIds);
669
538
  expect(drmEngine.initialized()).toBe(true);
670
539
  expect(shaka.media.DrmEngine.keySystem(drmEngine.getDrmInfo()))
671
540
  .toBe('drm.abc');
672
- if (useMediaCapabilities) {
673
- expect(variants[0].decodingInfos.length).toBe(1);
674
- } else {
675
- expect(requestMediaKeySystemAccessSpy).toHaveBeenCalledTimes(1);
676
- }
541
+ expect(variants[0].decodingInfos.length).toBe(1);
677
542
  });
678
543
 
679
544
  it('uses advanced config to fill in DrmInfo', async () => {
@@ -691,12 +556,13 @@ function testDrmEngine(useMediaCapabilities) {
691
556
  });
692
557
  });
693
558
 
694
- setRequestMediaKeySystemAccessSpy([]);
559
+ setDecodingInfoSpy([]);
695
560
 
696
561
  config.advanced['drm.abc'] = {
697
562
  audioRobustness: 'good',
698
563
  videoRobustness: 'really_really_ridiculously_good',
699
564
  serverCertificate: null,
565
+ serverCertificateUri: '',
700
566
  sessionType: 'persistent-license',
701
567
  individualizationServer: '',
702
568
  distinctiveIdentifierRequired: true,
@@ -706,44 +572,26 @@ function testDrmEngine(useMediaCapabilities) {
706
572
 
707
573
  const variants = manifest.variants;
708
574
  await expectAsync(
709
- drmEngine.initForPlayback(variants, manifest.offlineSessionIds,
710
- useMediaCapabilities)).toBeRejected();
575
+ drmEngine.initForPlayback(variants, manifest.offlineSessionIds))
576
+ .toBeRejected();
711
577
 
712
578
  expect(drmEngine.initialized()).toBe(false);
713
-
714
- if (useMediaCapabilities) {
715
- expect(decodingInfoSpy).toHaveBeenCalledTimes(1);
716
- expect(decodingInfoSpy).toHaveBeenCalledWith(containing({
717
- keySystemConfiguration: containing({
718
- keySystem: 'drm.abc',
719
- distinctiveIdentifier: 'required',
720
- persistentState: 'required',
721
- sessionTypes: ['persistent-license'],
722
- initDataType: 'cenc',
723
- audio: containing({
724
- robustness: 'good',
725
- }),
726
- video: containing({
727
- robustness: 'really_really_ridiculously_good',
728
- }),
579
+ expect(decodingInfoSpy).toHaveBeenCalledTimes(1);
580
+ expect(decodingInfoSpy).toHaveBeenCalledWith(containing({
581
+ keySystemConfiguration: containing({
582
+ keySystem: 'drm.abc',
583
+ distinctiveIdentifier: 'required',
584
+ persistentState: 'required',
585
+ sessionTypes: ['persistent-license'],
586
+ initDataType: 'cenc',
587
+ audio: containing({
588
+ robustness: 'good',
729
589
  }),
730
- }));
731
- } else {
732
- expect(requestMediaKeySystemAccessSpy).toHaveBeenCalledTimes(1);
733
- expect(requestMediaKeySystemAccessSpy)
734
- .toHaveBeenCalledWith('drm.abc', [jasmine.objectContaining({
735
- audioCapabilities: [jasmine.objectContaining({
736
- robustness: 'good',
737
- })],
738
- videoCapabilities: [jasmine.objectContaining({
739
- robustness: 'really_really_ridiculously_good',
740
- })],
741
- distinctiveIdentifier: 'required',
742
- persistentState: 'required',
743
- sessionTypes: ['persistent-license'],
744
- initDataTypes: ['cenc'],
745
- })]);
746
- }
590
+ video: containing({
591
+ robustness: 'really_really_ridiculously_good',
592
+ }),
593
+ }),
594
+ }));
747
595
  });
748
596
 
749
597
  it('prefers advanced config from manifest if present', async () => {
@@ -761,7 +609,7 @@ function testDrmEngine(useMediaCapabilities) {
761
609
  });
762
610
  });
763
611
 
764
- setRequestMediaKeySystemAccessSpy([]);
612
+ setDecodingInfoSpy([]);
765
613
 
766
614
  // DrmInfo directly sets advanced settings.
767
615
  tweakDrmInfos((drmInfos) => {
@@ -775,6 +623,7 @@ function testDrmEngine(useMediaCapabilities) {
775
623
  audioRobustness: 'bad',
776
624
  videoRobustness: 'so_bad_it_hurts',
777
625
  serverCertificate: null,
626
+ serverCertificateUri: '',
778
627
  sessionType: '',
779
628
  individualizationServer: '',
780
629
  distinctiveIdentifierRequired: false,
@@ -784,42 +633,26 @@ function testDrmEngine(useMediaCapabilities) {
784
633
 
785
634
  const variants = manifest.variants;
786
635
  await expectAsync(
787
- drmEngine.initForPlayback(variants, manifest.offlineSessionIds,
788
- useMediaCapabilities)).toBeRejected();
636
+ drmEngine.initForPlayback(variants, manifest.offlineSessionIds))
637
+ .toBeRejected();
789
638
 
790
639
  expect(drmEngine.initialized()).toBe(false);
791
640
 
792
- if (useMediaCapabilities) {
793
- expect(decodingInfoSpy).toHaveBeenCalledTimes(1);
794
- expect(decodingInfoSpy).toHaveBeenCalledWith(containing({
795
- keySystemConfiguration: containing({
796
- keySystem: 'drm.abc',
797
- audio: containing({
798
- robustness: 'good',
799
- }),
800
- video: containing({
801
- robustness: 'really_really_ridiculously_good',
802
- }),
803
- distinctiveIdentifier: 'required',
804
- persistentState: 'required',
805
- initDataType: 'cenc',
641
+ expect(decodingInfoSpy).toHaveBeenCalledTimes(1);
642
+ expect(decodingInfoSpy).toHaveBeenCalledWith(containing({
643
+ keySystemConfiguration: containing({
644
+ keySystem: 'drm.abc',
645
+ audio: containing({
646
+ robustness: 'good',
806
647
  }),
807
- }));
808
- } else {
809
- expect(requestMediaKeySystemAccessSpy).toHaveBeenCalledTimes(1);
810
- expect(requestMediaKeySystemAccessSpy)
811
- .toHaveBeenCalledWith('drm.abc', [jasmine.objectContaining({
812
- audioCapabilities: [jasmine.objectContaining({
813
- robustness: 'good',
814
- })],
815
- videoCapabilities: [jasmine.objectContaining({
816
- robustness: 'really_really_ridiculously_good',
817
- })],
818
- distinctiveIdentifier: 'required',
819
- persistentState: 'required',
820
- initDataTypes: ['cenc'],
821
- })]);
822
- }
648
+ video: containing({
649
+ robustness: 'really_really_ridiculously_good',
650
+ }),
651
+ distinctiveIdentifier: 'required',
652
+ persistentState: 'required',
653
+ initDataType: 'cenc',
654
+ }),
655
+ }));
823
656
  });
824
657
 
825
658
  it('sets unique initDataTypes if specified from the initData', async () => {
@@ -834,30 +667,21 @@ function testDrmEngine(useMediaCapabilities) {
834
667
 
835
668
  const variants = manifest.variants;
836
669
 
837
- await drmEngine.initForPlayback(variants, manifest.offlineSessionIds,
838
- useMediaCapabilities);
670
+ await drmEngine.initForPlayback(variants, manifest.offlineSessionIds);
839
671
 
840
672
  expect(drmEngine.initialized()).toBe(true);
841
- if (useMediaCapabilities) {
842
- expect(decodingInfoSpy).toHaveBeenCalledTimes(2);
843
- expect(decodingInfoSpy).toHaveBeenCalledWith(containing({
844
- keySystemConfiguration: containing({
845
- keySystem: 'drm.abc',
846
- initDataType: 'very_nice',
847
- }),
673
+ expect(decodingInfoSpy).toHaveBeenCalledTimes(2);
674
+ expect(decodingInfoSpy).toHaveBeenCalledWith(containing({
675
+ keySystemConfiguration: containing({
676
+ keySystem: 'drm.abc',
677
+ initDataType: 'very_nice',
848
678
  }),
849
- );
850
- } else {
851
- expect(requestMediaKeySystemAccessSpy).toHaveBeenCalledTimes(1);
852
- expect(requestMediaKeySystemAccessSpy)
853
- .toHaveBeenCalledWith('drm.abc', [jasmine.objectContaining({
854
- initDataTypes: ['very_nice'],
855
- })]);
856
- }
679
+ }),
680
+ );
857
681
  });
858
682
 
859
683
  it('fails if license server is not configured', async () => {
860
- setRequestMediaKeySystemAccessSpy(['drm.abc']);
684
+ setDecodingInfoSpy(['drm.abc']);
861
685
 
862
686
  config.servers = {};
863
687
  drmEngine.configure(config);
@@ -869,8 +693,8 @@ function testDrmEngine(useMediaCapabilities) {
869
693
  'drm.abc'));
870
694
  const variants = manifest.variants;
871
695
  await expectAsync(
872
- drmEngine.initForPlayback(variants, manifest.offlineSessionIds,
873
- useMediaCapabilities)).toBeRejectedWith(expected);
696
+ drmEngine.initForPlayback(variants, manifest.offlineSessionIds))
697
+ .toBeRejectedWith(expected);
874
698
  });
875
699
 
876
700
  it('maps TS MIME types through the transmuxer', async () => {
@@ -896,38 +720,26 @@ function testDrmEngine(useMediaCapabilities) {
896
720
  };
897
721
  });
898
722
 
899
- setRequestMediaKeySystemAccessSpy(['drm.abc']);
723
+ setDecodingInfoSpy(['drm.abc']);
900
724
 
901
725
  const variants = manifest.variants;
902
726
  variants[0].video.mimeType = 'video/mp2t';
903
727
  variants[0].audio.mimeType = 'video/mp2t';
904
728
 
905
- await drmEngine.initForPlayback(variants, manifest.offlineSessionIds,
906
- useMediaCapabilities);
729
+ await drmEngine.initForPlayback(variants, manifest.offlineSessionIds);
907
730
  expect(drmEngine.initialized()).toBe(true);
908
731
 
909
- if (useMediaCapabilities) {
910
- const decodingConfig = containing({
911
- video: containing({
912
- contentType: 'video/mp4; codecs="vbar"',
913
- }),
914
- audio: containing({
915
- contentType: 'audio/mp4; codecs="abar"',
916
- }),
917
- });
918
- expect(decodingInfoSpy).toHaveBeenCalledTimes(2);
919
- expect(decodingInfoSpy).toHaveBeenCalledWith(decodingConfig);
920
- } else {
921
- expect(requestMediaKeySystemAccessSpy)
922
- .toHaveBeenCalledWith('drm.abc', [jasmine.objectContaining({
923
- audioCapabilities: [jasmine.objectContaining({
924
- contentType: 'audio/mp4; codecs="abar"',
925
- })],
926
- videoCapabilities: [jasmine.objectContaining({
927
- contentType: 'video/mp4; codecs="vbar"',
928
- })],
929
- })]);
930
- }
732
+ const decodingConfig = containing({
733
+ video: containing({
734
+ contentType: 'video/mp4; codecs="vbar"',
735
+ }),
736
+ audio: containing({
737
+ contentType: 'audio/mp4; codecs="abar"',
738
+ }),
739
+ });
740
+ expect(decodingInfoSpy).toHaveBeenCalledTimes(2);
741
+ expect(decodingInfoSpy).toHaveBeenCalledWith(decodingConfig);
742
+
931
743
  expect(drmEngine.supportsVariant(variants[0])).toBeTruthy();
932
744
  } finally {
933
745
  // Restore the mock.
@@ -954,7 +766,7 @@ function testDrmEngine(useMediaCapabilities) {
954
766
  });
955
767
 
956
768
  it('does nothing for unencrypted content', async () => {
957
- setRequestMediaKeySystemAccessSpy([]);
769
+ setDecodingInfoSpy([]);
958
770
  manifest.variants[0].video.drmInfos = [];
959
771
  manifest.variants[0].audio.drmInfos = [];
960
772
  config.servers = {};
@@ -972,16 +784,75 @@ function testDrmEngine(useMediaCapabilities) {
972
784
  it('sets server certificate if present in config', async () => {
973
785
  const cert = new Uint8Array(1);
974
786
  config.advanced['drm.abc'] = createAdvancedConfig(cert);
787
+ config.advanced['drm.abc'].serverCertificateUri =
788
+ 'https://drm-service.com/certificate';
975
789
  drmEngine.configure(config);
976
790
 
977
791
  const variants = manifest.variants;
978
- await drmEngine.initForPlayback(variants, manifest.offlineSessionIds,
979
- useMediaCapabilities);
792
+ await drmEngine.initForPlayback(variants, manifest.offlineSessionIds);
980
793
 
794
+ expect(fakeNetEngine.request).not.toHaveBeenCalled();
981
795
  // Should be set merely after init, without waiting for attach.
982
796
  expect(mockMediaKeys.setServerCertificate).toHaveBeenCalledWith(cert);
983
797
  });
984
798
 
799
+ it('fetches and sets server certificate from uri', async () => {
800
+ const cert = new Uint8Array(0);
801
+ const serverCertificateUri = 'https://drm-service.com/certificate';
802
+ config.advanced['drm.abc'] = createAdvancedConfig(cert);
803
+ config.advanced['drm.abc'].serverCertificateUri = serverCertificateUri;
804
+
805
+ fakeNetEngine.setResponseValue(
806
+ serverCertificateUri,
807
+ shaka.util.BufferUtils.toArrayBuffer(new Uint8Array(1)));
808
+
809
+ drmEngine.configure(config);
810
+
811
+ const variants = manifest.variants;
812
+ await drmEngine.initForPlayback(variants, manifest.offlineSessionIds);
813
+
814
+ fakeNetEngine.expectRequest(
815
+ serverCertificateUri,
816
+ shaka.net.NetworkingEngine.RequestType.SERVER_CERTIFICATE);
817
+
818
+ // Should be set merely after init, without waiting for attach.
819
+ expect(mockMediaKeys.setServerCertificate).toHaveBeenCalledWith(
820
+ new Uint8Array(1));
821
+ });
822
+
823
+ it('fetches server certificate from uri and triggers error', async () => {
824
+ const cert = new Uint8Array(0);
825
+ const serverCertificateUri = 'https://drm-service.com/certificate';
826
+ config.advanced['drm.abc'] = createAdvancedConfig(cert);
827
+ config.advanced['drm.abc'].serverCertificateUri = serverCertificateUri;
828
+
829
+ // Simulate a permission error from the web server.
830
+ const netError = new shaka.util.Error(
831
+ shaka.util.Error.Severity.CRITICAL,
832
+ shaka.util.Error.Category.NETWORK,
833
+ shaka.util.Error.Code.BAD_HTTP_STATUS,
834
+ serverCertificateUri, 403);
835
+ const operation = shaka.util.AbortableOperation.failed(netError);
836
+ fakeNetEngine.request.and.returnValue(operation);
837
+
838
+ drmEngine.configure(config);
839
+
840
+ const expected = Util.jasmineError(new shaka.util.Error(
841
+ shaka.util.Error.Severity.CRITICAL,
842
+ shaka.util.Error.Category.DRM,
843
+ shaka.util.Error.Code.SERVER_CERTIFICATE_REQUEST_FAILED,
844
+ netError));
845
+
846
+ await expectAsync(initAndAttach()).toBeRejectedWith(expected);
847
+
848
+ fakeNetEngine.expectRequest(
849
+ serverCertificateUri,
850
+ shaka.net.NetworkingEngine.RequestType.SERVER_CERTIFICATE);
851
+
852
+ // Should be set merely after init, without waiting for attach.
853
+ expect(mockMediaKeys.setServerCertificate).not.toHaveBeenCalled();
854
+ });
855
+
985
856
  it('prefers server certificate from DrmInfo', async () => {
986
857
  const cert1 = new Uint8Array(5);
987
858
  const cert2 = new Uint8Array(1);
@@ -1065,7 +936,7 @@ function testDrmEngine(useMediaCapabilities) {
1065
936
  });
1066
937
 
1067
938
  await drmEngine.initForPlayback(
1068
- manifest.variants, manifest.offlineSessionIds, useMediaCapabilities);
939
+ manifest.variants, manifest.offlineSessionIds);
1069
940
  drmEngine.newInitData('cenc', initData);
1070
941
  await drmEngine.attach(mockVideo);
1071
942
 
@@ -1078,7 +949,7 @@ function testDrmEngine(useMediaCapabilities) {
1078
949
  drmInfos[0].keySystem = 'com.fake.NOT.clearkey';
1079
950
  });
1080
951
 
1081
- setRequestMediaKeySystemAccessSpy(['org.w3.clearkey']);
952
+ setDecodingInfoSpy(['org.w3.clearkey']);
1082
953
 
1083
954
  // Configure clear keys (map of hex key IDs to keys)
1084
955
  config.clearKeys = {
@@ -1124,7 +995,7 @@ function testDrmEngine(useMediaCapabilities) {
1124
995
  // Make it so that clear key setup fails by pretending we don't have it.
1125
996
  // In reality, it was failing because of missing codec info, but any
1126
997
  // failure should do for testing purposes.
1127
- setRequestMediaKeySystemAccessSpy([]);
998
+ setDecodingInfoSpy([]);
1128
999
 
1129
1000
  // Configure clear keys (map of hex key IDs to keys)
1130
1001
  config.clearKeys = {
@@ -1139,8 +1010,8 @@ function testDrmEngine(useMediaCapabilities) {
1139
1010
  shaka.util.Error.Severity.CRITICAL,
1140
1011
  shaka.util.Error.Category.DRM,
1141
1012
  shaka.util.Error.Code.REQUESTED_KEY_SYSTEM_CONFIG_UNAVAILABLE));
1142
- await expectAsync(drmEngine.initForPlayback(variants, [],
1143
- useMediaCapabilities)).toBeRejectedWith(expected);
1013
+ await expectAsync(drmEngine.initForPlayback(variants, []))
1014
+ .toBeRejectedWith(expected);
1144
1015
  });
1145
1016
 
1146
1017
  it('fails with an error if setMediaKeys fails', async () => {
@@ -1635,7 +1506,7 @@ function testDrmEngine(useMediaCapabilities) {
1635
1506
  tweakDrmInfos((drmInfos) => {
1636
1507
  drmInfos[0].keySystem = 'com.fake.NOT.clearkey';
1637
1508
  });
1638
- setRequestMediaKeySystemAccessSpy(['org.w3.clearkey']);
1509
+ setDecodingInfoSpy(['org.w3.clearkey']);
1639
1510
 
1640
1511
  // Configure clear keys (map of hex key IDs to keys)
1641
1512
  config.clearKeys = {
@@ -1819,32 +1690,19 @@ function testDrmEngine(useMediaCapabilities) {
1819
1690
  // Hold the MediaKeys query.
1820
1691
  /** @type {!shaka.util.PublicPromise} */
1821
1692
  const p = new shaka.util.PublicPromise();
1822
- requestMediaKeySystemAccessSpy.and.returnValue(p);
1823
1693
  decodingInfoSpy.and.returnValue(p);
1824
1694
 
1825
1695
  const variants = manifest.variants;
1826
1696
  const init = drmEngine.initForPlayback(
1827
- variants, manifest.offlineSessionIds, useMediaCapabilities);
1697
+ variants, manifest.offlineSessionIds);
1828
1698
 
1829
1699
  // This flow should still return "success" when DrmEngine is destroyed.
1830
1700
  await shaka.test.Util.shortDelay();
1831
1701
 
1832
- if (useMediaCapabilities) {
1833
- expect(variants[0].decodingInfos.length).toBe(0);
1834
- await drmEngine.destroy();
1835
- p.reject(new Error('')); // Fail drm.abc.
1836
- await expectAsync(init).toBeRejected();
1837
- } else {
1838
- // The first query has been made, which we are blocking.
1839
- expect(requestMediaKeySystemAccessSpy).toHaveBeenCalledTimes(1);
1840
- expect(requestMediaKeySystemAccessSpy).toHaveBeenCalledWith(
1841
- 'drm.abc', jasmine.any(Array));
1842
- await drmEngine.destroy();
1843
- p.reject(new Error('')); // Fail drm.abc.
1844
- await expectAsync(init).toBeRejected();
1845
- // A second query was not made.
1846
- expect(requestMediaKeySystemAccessSpy).toHaveBeenCalledTimes(1);
1847
- }
1702
+ expect(variants[0].decodingInfos.length).toBe(0);
1703
+ await drmEngine.destroy();
1704
+ p.reject(new Error('')); // Fail drm.abc.
1705
+ await expectAsync(init).toBeRejected();
1848
1706
  expect(drmEngine.initialized()).toBe(false);
1849
1707
  });
1850
1708
 
@@ -1852,23 +1710,14 @@ function testDrmEngine(useMediaCapabilities) {
1852
1710
  // Hold the MediaKeys query:
1853
1711
  /** @type {!shaka.util.PublicPromise} */
1854
1712
  const p = new shaka.util.PublicPromise();
1855
- requestMediaKeySystemAccessSpy.and.returnValue(p);
1856
1713
  decodingInfoSpy.and.returnValue(p);
1857
1714
 
1858
1715
  const variants = manifest.variants;
1859
1716
  const init = drmEngine.initForPlayback(
1860
- variants, manifest.offlineSessionIds, useMediaCapabilities);
1717
+ variants, manifest.offlineSessionIds);
1861
1718
 
1862
1719
  await shaka.test.Util.shortDelay();
1863
-
1864
- if (useMediaCapabilities) {
1865
- expect(variants[0].decodingInfos.length).toBe(0);
1866
- } else {
1867
- // The first query has been made, which we are blocking.
1868
- expect(requestMediaKeySystemAccessSpy).toHaveBeenCalledTimes(1);
1869
- expect(requestMediaKeySystemAccessSpy).toHaveBeenCalledWith(
1870
- 'drm.abc', jasmine.any(Array));
1871
- }
1720
+ expect(variants[0].decodingInfos.length).toBe(0);
1872
1721
  await drmEngine.destroy();
1873
1722
  p.resolve(); // Success for drm.abc.
1874
1723
  await expectAsync(init).toBeRejected();
@@ -1885,7 +1734,7 @@ function testDrmEngine(useMediaCapabilities) {
1885
1734
 
1886
1735
  const variants = manifest.variants;
1887
1736
  const init = drmEngine.initForPlayback(
1888
- variants, manifest.offlineSessionIds, useMediaCapabilities);
1737
+ variants, manifest.offlineSessionIds);
1889
1738
 
1890
1739
  await shaka.test.Util.shortDelay();
1891
1740
  // We are blocked on createMediaKeys:
@@ -2164,6 +2013,26 @@ function testDrmEngine(useMediaCapabilities) {
2164
2013
  });
2165
2014
  });
2166
2015
 
2016
+ describe('isFairPlayKeySystem', () => {
2017
+ it('should return true for FairPlay', () => {
2018
+ expect(shaka.media.DrmEngine.isFairPlayKeySystem(
2019
+ 'com.apple.fps')).toBe(true);
2020
+ expect(shaka.media.DrmEngine.isFairPlayKeySystem(
2021
+ 'com.apple.fps.1_0')).toBe(true);
2022
+ expect(shaka.media.DrmEngine.isFairPlayKeySystem(
2023
+ 'com.apple.fps.2_0')).toBe(true);
2024
+ expect(shaka.media.DrmEngine.isFairPlayKeySystem(
2025
+ 'com.apple.fps.3_0')).toBe(true);
2026
+ });
2027
+
2028
+ it('should return false for non-FairPlay key systems', () => {
2029
+ expect(shaka.media.DrmEngine.isFairPlayKeySystem(
2030
+ 'com.widevine.alpha')).toBe(false);
2031
+ expect(shaka.media.DrmEngine.isFairPlayKeySystem(
2032
+ 'com.abc.playready')).toBe(false);
2033
+ });
2034
+ });
2035
+
2167
2036
  describe('getDrmInfo', () => {
2168
2037
  it('includes correct info', async () => {
2169
2038
  // Leave only one drmInfo
@@ -2179,7 +2048,7 @@ function testDrmEngine(useMediaCapabilities) {
2179
2048
  });
2180
2049
  });
2181
2050
  });
2182
- setRequestMediaKeySystemAccessSpy(['drm.abc']);
2051
+ setDecodingInfoSpy(['drm.abc']);
2183
2052
 
2184
2053
  // Key IDs in manifest
2185
2054
  tweakDrmInfos((drmInfos) => {
@@ -2191,6 +2060,7 @@ function testDrmEngine(useMediaCapabilities) {
2191
2060
  videoRobustness: 'really_really_ridiculously_good',
2192
2061
  distinctiveIdentifierRequired: true,
2193
2062
  serverCertificate: null,
2063
+ serverCertificateUri: '',
2194
2064
  sessionType: '',
2195
2065
  individualizationServer: '',
2196
2066
  persistentStateRequired: true,
@@ -2198,8 +2068,7 @@ function testDrmEngine(useMediaCapabilities) {
2198
2068
  drmEngine.configure(config);
2199
2069
 
2200
2070
  const variants = manifest.variants;
2201
- await drmEngine.initForPlayback(variants, manifest.offlineSessionIds,
2202
- useMediaCapabilities);
2071
+ await drmEngine.initForPlayback(variants, manifest.offlineSessionIds);
2203
2072
  expect(drmEngine.initialized()).toBe(true);
2204
2073
  const drmInfo = drmEngine.getDrmInfo();
2205
2074
  expect(drmInfo).toEqual({
@@ -2210,6 +2079,7 @@ function testDrmEngine(useMediaCapabilities) {
2210
2079
  audioRobustness: 'good',
2211
2080
  videoRobustness: 'really_really_ridiculously_good',
2212
2081
  serverCertificate: undefined,
2082
+ serverCertificateUri: '',
2213
2083
  sessionType: 'temporary',
2214
2084
  initData: [],
2215
2085
  keyIds: new Set(['deadbeefdeadbeefdeadbeefdeadbeef']),
@@ -2227,6 +2097,7 @@ function testDrmEngine(useMediaCapabilities) {
2227
2097
  audioRobustness: 'good',
2228
2098
  videoRobustness: 'really_really_ridiculously_good',
2229
2099
  serverCertificate: undefined,
2100
+ serverCertificateUri: '',
2230
2101
  initData: [],
2231
2102
  keyIds: new Set(['deadbeefdeadbeefdeadbeefdeadbeef']),
2232
2103
  };
@@ -2247,6 +2118,7 @@ function testDrmEngine(useMediaCapabilities) {
2247
2118
  persistentStateRequired: true,
2248
2119
  videoRobustness: 'really_really_ridiculously_good',
2249
2120
  serverCertificate: serverCert,
2121
+ serverCertificateUri: '',
2250
2122
  initData: ['blah'],
2251
2123
  keyIds: new Set(['deadbeefdeadbeefdeadbeefdeadbeef']),
2252
2124
  };
@@ -2257,6 +2129,7 @@ function testDrmEngine(useMediaCapabilities) {
2257
2129
  persistentStateRequired: false,
2258
2130
  audioRobustness: 'good',
2259
2131
  serverCertificate: undefined,
2132
+ serverCertificateUri: '',
2260
2133
  initData: ['init data'],
2261
2134
  keyIds: new Set(['eadbeefdeadbeefdeadbeefdeadbeefd']),
2262
2135
  };
@@ -2268,6 +2141,7 @@ function testDrmEngine(useMediaCapabilities) {
2268
2141
  audioRobustness: 'good',
2269
2142
  videoRobustness: 'really_really_ridiculously_good',
2270
2143
  serverCertificate: serverCert,
2144
+ serverCertificateUri: '',
2271
2145
  initData: ['blah', 'init data'],
2272
2146
  keyIds: new Set([
2273
2147
  'deadbeefdeadbeefdeadbeefdeadbeef',
@@ -2278,6 +2152,50 @@ function testDrmEngine(useMediaCapabilities) {
2278
2152
  [drmInfoAudio]);
2279
2153
  expect(returned).toEqual([drmInfoDesired]);
2280
2154
  });
2155
+
2156
+ it('dedupes the merged init data based on keyId matching', () => {
2157
+ const serverCert = new Uint8Array(0);
2158
+ const drmInfoVideo = {
2159
+ keySystem: 'drm.abc',
2160
+ licenseServerUri: 'http://abc.drm/license',
2161
+ distinctiveIdentifierRequired: false,
2162
+ persistentStateRequired: true,
2163
+ videoRobustness: 'really_really_ridiculously_good',
2164
+ serverCertificate: serverCert,
2165
+ serverCertificateUri: '',
2166
+ initData: [{keyId: 'v-init'}],
2167
+ keyIds: new Set(['deadbeefdeadbeefdeadbeefdeadbeef']),
2168
+ };
2169
+ const drmInfoAudio = {
2170
+ keySystem: 'drm.abc',
2171
+ licenseServerUri: undefined,
2172
+ distinctiveIdentifierRequired: true,
2173
+ persistentStateRequired: false,
2174
+ audioRobustness: 'good',
2175
+ serverCertificate: undefined,
2176
+ serverCertificateUri: '',
2177
+ initData: [{keyId: 'v-init'}, {keyId: 'a-init'}],
2178
+ keyIds: new Set(['eadbeefdeadbeefdeadbeefdeadbeefd']),
2179
+ };
2180
+ const drmInfoDesired = {
2181
+ keySystem: 'drm.abc',
2182
+ licenseServerUri: 'http://abc.drm/license',
2183
+ distinctiveIdentifierRequired: true,
2184
+ persistentStateRequired: true,
2185
+ audioRobustness: 'good',
2186
+ videoRobustness: 'really_really_ridiculously_good',
2187
+ serverCertificate: serverCert,
2188
+ serverCertificateUri: '',
2189
+ initData: [{keyId: 'v-init'}, {keyId: 'a-init'}],
2190
+ keyIds: new Set([
2191
+ 'deadbeefdeadbeefdeadbeefdeadbeef',
2192
+ 'eadbeefdeadbeefdeadbeefdeadbeefd',
2193
+ ]),
2194
+ };
2195
+ const returned = shaka.media.DrmEngine.getCommonDrmInfos([drmInfoVideo],
2196
+ [drmInfoAudio]);
2197
+ expect(returned).toEqual([drmInfoDesired]);
2198
+ });
2281
2199
  }); // describe('getCommonDrmInfos')
2282
2200
 
2283
2201
  describe('configure', () => {
@@ -2371,8 +2289,7 @@ function testDrmEngine(useMediaCapabilities) {
2371
2289
  });
2372
2290
 
2373
2291
  const variants = manifest.variants;
2374
- await drmEngine.initForPlayback(variants, manifest.offlineSessionIds,
2375
- useMediaCapabilities);
2292
+ await drmEngine.initForPlayback(variants, manifest.offlineSessionIds);
2376
2293
  });
2377
2294
 
2378
2295
  it('waits until update() is complete', async () => {
@@ -2478,25 +2395,10 @@ function testDrmEngine(useMediaCapabilities) {
2478
2395
 
2479
2396
  async function initAndAttach() {
2480
2397
  const variants = manifest.variants;
2481
- await drmEngine.initForPlayback(variants, manifest.offlineSessionIds,
2482
- useMediaCapabilities);
2398
+ await drmEngine.initForPlayback(variants, manifest.offlineSessionIds);
2483
2399
  await drmEngine.attach(mockVideo);
2484
2400
  }
2485
2401
 
2486
- function setRequestMediaKeySystemAccessSpy(acceptableKeySystems) {
2487
- // TODO: Setting both the requestMediaKeySystemAccessSpy and decodingInfoSpy
2488
- // as a temporary solution. Only decodingInfoSpy is needed once we use
2489
- // decodingInfo API to get mediaKeySystemAccess.
2490
- setDecodingInfoSpy(acceptableKeySystems);
2491
- requestMediaKeySystemAccessSpy.and.callFake((keySystem) => {
2492
- if (!acceptableKeySystems.includes(keySystem)) {
2493
- return Promise.reject(new Error(''));
2494
- }
2495
- mockMediaKeySystemAccess.keySystem = keySystem;
2496
- return Promise.resolve(mockMediaKeySystemAccess);
2497
- });
2498
- }
2499
-
2500
2402
  function setDecodingInfoSpy(acceptableKeySystems) {
2501
2403
  decodingInfoSpy.and.callFake((config) => {
2502
2404
  const keySystem = config && config.keySystemConfiguration ?
@@ -2588,6 +2490,7 @@ function testDrmEngine(useMediaCapabilities) {
2588
2490
  distinctiveIdentifierRequired: false,
2589
2491
  persistentStateRequired: false,
2590
2492
  serverCertificate: serverCert,
2493
+ serverCertificateUri: '',
2591
2494
  individualizationServer: '',
2592
2495
  sessionType: '',
2593
2496
  videoRobustness: '',
@@ -2613,4 +2516,4 @@ function testDrmEngine(useMediaCapabilities) {
2613
2516
  callback(manifest.variants[0].audio.drmInfos);
2614
2517
  }
2615
2518
  }
2616
- }
2519
+ });