shaka-player 4.2.3 → 4.3.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 (147) hide show
  1. package/.github/workflows/sync-labels.yaml +1 -1
  2. package/.github/workflows/update-issues.yaml +1 -1
  3. package/AUTHORS +5 -0
  4. package/CHANGELOG.md +85 -39
  5. package/CONTRIBUTORS +5 -0
  6. package/README.md +14 -0
  7. package/demo/common/assets.js +58 -0
  8. package/demo/common/message_ids.js +22 -1
  9. package/demo/config.js +89 -5
  10. package/demo/demo.less +7 -1
  11. package/demo/index.html +8 -1
  12. package/demo/locales/en.json +21 -1
  13. package/demo/locales/source.json +82 -2
  14. package/demo/main.js +57 -1
  15. package/demo/search.js +2 -0
  16. package/demo/visualizer.js +337 -0
  17. package/demo/visualizer_button.js +103 -0
  18. package/dist/controls.css +1 -1
  19. package/dist/controls.css.map +1 -1
  20. package/dist/demo.compiled.debug.js +83 -58
  21. package/dist/demo.compiled.debug.map +4 -4
  22. package/dist/demo.compiled.js +83 -58
  23. package/dist/demo.compiled.map +4 -4
  24. package/dist/demo.css +1 -1
  25. package/dist/demo.css.map +1 -1
  26. package/dist/deps.js +18 -11
  27. package/dist/locales.js +32 -32
  28. package/dist/receiver.compiled.debug.js +23 -19
  29. package/dist/receiver.compiled.debug.map +3 -3
  30. package/dist/receiver.compiled.js +23 -19
  31. package/dist/receiver.compiled.map +3 -3
  32. package/dist/shaka-player.compiled.d.ts +178 -27
  33. package/dist/shaka-player.compiled.debug.d.ts +178 -27
  34. package/dist/shaka-player.compiled.debug.externs.js +163 -32
  35. package/dist/shaka-player.compiled.debug.js +488 -422
  36. package/dist/shaka-player.compiled.debug.map +4 -4
  37. package/dist/shaka-player.compiled.externs.js +163 -32
  38. package/dist/shaka-player.compiled.js +895 -857
  39. package/dist/shaka-player.compiled.map +4 -4
  40. package/dist/shaka-player.ui.d.ts +178 -27
  41. package/dist/shaka-player.ui.debug.d.ts +178 -27
  42. package/dist/shaka-player.ui.debug.externs.js +163 -32
  43. package/dist/shaka-player.ui.debug.js +519 -450
  44. package/dist/shaka-player.ui.debug.map +4 -4
  45. package/dist/shaka-player.ui.externs.js +163 -32
  46. package/dist/shaka-player.ui.js +1053 -1015
  47. package/dist/shaka-player.ui.map +4 -4
  48. package/docs/design/lcevc-architecture.png +0 -0
  49. package/docs/design/lcevc-demo.png +0 -0
  50. package/docs/design/lcevc-integration.md +72 -0
  51. package/docs/tutorials/fairplay.md +7 -0
  52. package/docs/tutorials/faq.md +1 -1
  53. package/docs/tutorials/ui.md +7 -7
  54. package/docs/tutorials/upgrade.md +6 -0
  55. package/externs/ima.js +13 -1
  56. package/externs/lcevc.js +81 -0
  57. package/externs/mux.js +4 -42
  58. package/externs/shaka/ads.js +11 -1
  59. package/externs/shaka/manifest_parser.js +5 -1
  60. package/externs/shaka/player.js +178 -15
  61. package/lib/abr/simple_abr_manager.js +7 -0
  62. package/lib/ads/ad_manager.js +0 -8
  63. package/lib/ads/client_side_ad.js +15 -0
  64. package/lib/ads/client_side_ad_manager.js +21 -8
  65. package/lib/ads/server_side_ad.js +15 -0
  66. package/lib/ads/server_side_ad_manager.js +3 -3
  67. package/lib/cast/cast_proxy.js +2 -1
  68. package/lib/cast/cast_receiver.js +19 -5
  69. package/lib/cast/cast_sender.js +2 -1
  70. package/lib/cast/cast_utils.js +18 -6
  71. package/lib/cea/dummy_cea_parser.js +28 -0
  72. package/lib/cea/i_cea_parser.js +14 -2
  73. package/lib/cea/mp4_cea_parser.js +87 -5
  74. package/lib/config/auto_show_text.js +33 -0
  75. package/lib/dash/dash_parser.js +1 -1
  76. package/lib/hls/hls_parser.js +799 -376
  77. package/lib/lcevc/lcevc_dil.js +152 -0
  78. package/lib/media/closed_caption_parser.js +9 -6
  79. package/lib/media/drm_engine.js +14 -6
  80. package/lib/media/media_source_engine.js +112 -26
  81. package/lib/media/playhead.js +23 -1
  82. package/lib/media/presentation_timeline.js +26 -11
  83. package/lib/media/segment_reference.js +49 -1
  84. package/lib/media/streaming_engine.js +145 -24
  85. package/lib/media/transmuxer.js +79 -18
  86. package/lib/offline/storage.js +1 -0
  87. package/lib/player.js +477 -85
  88. package/lib/polyfill/patchedmediakeys_apple.js +55 -1
  89. package/lib/text/ui_text_displayer.js +5 -5
  90. package/lib/text/vtt_text_parser.js +35 -1
  91. package/lib/util/dom_utils.js +0 -1
  92. package/lib/util/error.js +17 -0
  93. package/lib/util/fake_event.js +2 -0
  94. package/lib/util/id3_utils.js +300 -0
  95. package/lib/util/manifest_parser_utils.js +4 -0
  96. package/lib/util/mime_utils.js +3 -6
  97. package/lib/util/mp4_box_parsers.js +15 -5
  98. package/lib/util/platform.js +9 -1
  99. package/lib/util/player_configuration.js +23 -1
  100. package/lib/util/ts_parser.js +499 -0
  101. package/maintained-branches.md +2 -2
  102. package/package.json +4 -1
  103. package/roadmap.md +27 -7
  104. package/test/ads/ad_manager_unit.js +180 -29
  105. package/test/cast/cast_receiver_integration.js +97 -16
  106. package/test/cast/cast_receiver_unit.js +17 -12
  107. package/test/cast/cast_utils_unit.js +4 -1
  108. package/test/dash/dash_parser_content_protection_unit.js +1 -0
  109. package/test/dash/dash_parser_live_unit.js +1 -0
  110. package/test/dash/dash_parser_manifest_unit.js +93 -1
  111. package/test/dash/dash_parser_segment_base_unit.js +1 -0
  112. package/test/dash/dash_parser_segment_list_unit.js +1 -0
  113. package/test/dash/dash_parser_segment_template_unit.js +1 -0
  114. package/test/demo/demo_unit.js +2 -1
  115. package/test/hls/hls_live_unit.js +253 -19
  116. package/test/hls/hls_parser_unit.js +500 -12
  117. package/test/media/closed_caption_parser_unit.js +2 -1
  118. package/test/media/drm_engine_integration.js +3 -1
  119. package/test/media/media_source_engine_integration.js +102 -6
  120. package/test/media/media_source_engine_unit.js +10 -5
  121. package/test/media/playhead_unit.js +41 -0
  122. package/test/media/streaming_engine_integration.js +17 -1
  123. package/test/media/streaming_engine_unit.js +180 -24
  124. package/test/media/transmuxer_integration.js +21 -10
  125. package/test/player_integration.js +224 -82
  126. package/test/player_unit.js +92 -2
  127. package/test/polyfill/patchedmediakeys_apple_unit.js +124 -0
  128. package/test/test/assets/id3-metadata.aac +0 -0
  129. package/test/test/assets/id3-metadata.ts +0 -0
  130. package/test/test/assets/thumbnails-sprites.vtt +10 -0
  131. package/test/test/assets/thumbnails.vtt +10 -0
  132. package/test/test/util/dash_parser_util.js +2 -0
  133. package/test/test/util/fake_ad.js +21 -0
  134. package/test/test/util/fake_media_source_engine.js +7 -0
  135. package/test/test/util/id3_generator.js +100 -0
  136. package/test/test/util/manifest_generator.js +3 -6
  137. package/test/test/util/simple_fakes.js +7 -0
  138. package/test/test/util/stream_generator.js +38 -0
  139. package/test/test/util/test_scheme.js +43 -14
  140. package/test/text/vtt_text_parser_unit.js +11 -0
  141. package/test/ui/text_displayer_layout_unit.js +14 -0
  142. package/test/util/id3_utils_unit.js +116 -0
  143. package/test/util/ts_parser_unit.js +77 -0
  144. package/ui/controls.js +4 -1
  145. package/ui/less/containers.less +7 -0
  146. package/ui/settings_menu.js +11 -2
  147. package/ui/ui.js +43 -4
@@ -205,88 +205,6 @@ describe('Player', () => {
205
205
  }
206
206
  });
207
207
 
208
- it('is called automatically if language prefs match', async () => {
209
- // If the text is a match for the user's preferences, and audio differs
210
- // from text, we enable text display automatically.
211
-
212
- // NOTE: This is also a regression test for #1696, in which a change
213
- // to this feature broke StreamingEngine initialization.
214
-
215
- const preferredTextLanguage = 'fa'; // The same as in the content itself
216
- player.configure({preferredTextLanguage: preferredTextLanguage});
217
-
218
- // Now load a version of Sintel with delayed setup of video & audio
219
- // streams and wait for completion.
220
- await player.load('test:sintel_realistic_compiled');
221
- // By this point, a MediaSource error would be thrown in a repro of bug
222
- // #1696.
223
-
224
- // Make sure the automatic setting took effect.
225
- expect(player.isTextTrackVisible()).toBe(true);
226
-
227
- // Make sure the content we tested with has text tracks, that the config
228
- // we used matches the text language, and that the audio language differs.
229
- // These will catch any changes to the underlying content that would
230
- // invalidate the test setup.
231
- expect(player.getTextTracks().length).not.toBe(0);
232
- const textTrack = player.getTextTracks()[0];
233
- expect(textTrack.language).toBe(preferredTextLanguage);
234
-
235
- const variantTrack = player.getVariantTracks()[0];
236
- expect(variantTrack.language).not.toBe(textTrack.language);
237
- });
238
-
239
- it('is not called automatically without language pref match', async () => {
240
- // If the text preference doesn't match the content, we do not enable text
241
- // display automatically.
242
-
243
- const preferredTextLanguage = 'xx'; // Differs from the content itself
244
- player.configure({preferredTextLanguage: preferredTextLanguage});
245
-
246
- // Now load the content and wait for completion.
247
- await player.load('test:sintel_realistic_compiled');
248
-
249
- // Make sure the automatic setting did not happen.
250
- expect(player.isTextTrackVisible()).toBe(false);
251
-
252
- // Make sure the content we tested with has text tracks, that the config
253
- // we used does not match the text language, and that the text and audio
254
- // languages do not match each other (to keep this distinct from the next
255
- // test case). This will catch any changes to the underlying content that
256
- // would invalidate the test setup.
257
- expect(player.getTextTracks().length).not.toBe(0);
258
- const textTrack = player.getTextTracks()[0];
259
- expect(textTrack.language).not.toBe(preferredTextLanguage);
260
-
261
- const variantTrack = player.getVariantTracks()[0];
262
- expect(variantTrack.language).not.toBe(textTrack.language);
263
- });
264
-
265
- it('is not called automatically with audio and text match', async () => {
266
- // If the audio and text tracks use the same language, we do not enable
267
- // text display automatically, no matter the text preference.
268
-
269
- const preferredTextLanguage = 'und'; // The same as in the content itself
270
- player.configure({preferredTextLanguage: preferredTextLanguage});
271
-
272
- // Now load the content and wait for completion.
273
- await player.load('test:sintel_compiled');
274
-
275
- // Make sure the automatic setting did not happen.
276
- expect(player.isTextTrackVisible()).toBe(false);
277
-
278
- // Make sure the content we tested with has text tracks, that the
279
- // config we used matches the content, and that the text and audio
280
- // languages match each other. This will catch any changes to the
281
- // underlying content that would invalidate the test setup.
282
- expect(player.getTextTracks().length).not.toBe(0);
283
- const textTrack = player.getTextTracks()[0];
284
- expect(textTrack.language).toBe(preferredTextLanguage);
285
-
286
- const variantTrack = player.getVariantTracks()[0];
287
- expect(variantTrack.language).toBe(textTrack.language);
288
- });
289
-
290
208
  // Repro for https://github.com/shaka-project/shaka-player/issues/1879.
291
209
  it('appends cues when enabled initially', async () => {
292
210
  let cues = [];
@@ -376,6 +294,160 @@ describe('Player', () => {
376
294
  });
377
295
  }); // describe('setTextTrackVisibility')
378
296
 
297
+ describe('autoShowText', () => {
298
+ async function textMatchesAudioDoesNot() {
299
+ const preferredTextLanguage = 'fa'; // The same as in the content
300
+ player.configure({preferredTextLanguage: preferredTextLanguage});
301
+
302
+ // NOTE: This is also a regression test for #1696, in which a change to
303
+ // this feature broke StreamingEngine initialization.
304
+
305
+ // Now load a version of Sintel with delayed setup of video & audio
306
+ // streams and wait for completion.
307
+ await player.load('test:sintel_realistic_compiled');
308
+ // By this point, a MediaSource error would be thrown in a repro of bug
309
+ // #1696.
310
+
311
+ // Make sure the content we tested with has text tracks, that the config
312
+ // we used matches the text language, and that the audio language differs.
313
+ // These will catch any changes to the underlying content that would
314
+ // invalidate the test setup.
315
+ expect(player.getTextTracks().length).not.toBe(0);
316
+ const textTrack = player.getTextTracks()[0];
317
+ expect(textTrack.language).toBe(preferredTextLanguage);
318
+
319
+ const variantTrack = player.getVariantTracks()[0];
320
+ expect(variantTrack.language).not.toBe(textTrack.language);
321
+ }
322
+
323
+ async function textDoesNotMatch() {
324
+ const preferredTextLanguage = 'xx'; // Differs from the content
325
+ player.configure({preferredTextLanguage: preferredTextLanguage});
326
+
327
+ // Now load the content and wait for completion.
328
+ await player.load('test:sintel_realistic_compiled');
329
+
330
+ // Make sure the content we tested with has text tracks, that the config
331
+ // we used does not match the text language, and that the text and audio
332
+ // languages do not match each other (to keep this distinct from the next
333
+ // test case). This will catch any changes to the underlying content that
334
+ // would invalidate the test setup.
335
+ expect(player.getTextTracks().length).not.toBe(0);
336
+ const textTrack = player.getTextTracks()[0];
337
+ expect(textTrack.language).not.toBe(preferredTextLanguage);
338
+
339
+ const variantTrack = player.getVariantTracks()[0];
340
+ expect(variantTrack.language).not.toBe(textTrack.language);
341
+ }
342
+
343
+ async function textAndAudioMatch() {
344
+ const preferredTextLanguage = 'und'; // The same as in the content
345
+ player.configure({preferredTextLanguage: preferredTextLanguage});
346
+
347
+ // Now load the content and wait for completion.
348
+ await player.load('test:sintel_compiled');
349
+
350
+ // Make sure the content we tested with has text tracks, that the config
351
+ // we used matches the content, and that the text and audio languages
352
+ // match each other. This will catch any changes to the underlying
353
+ // content that would invalidate the test setup.
354
+ expect(player.getTextTracks().length).not.toBe(0);
355
+ const textTrack = player.getTextTracks()[0];
356
+ expect(textTrack.language).toBe(preferredTextLanguage);
357
+
358
+ const variantTrack = player.getVariantTracks()[0];
359
+ expect(variantTrack.language).toBe(textTrack.language);
360
+ }
361
+
362
+ describe('IF_SUBTITLES_MAY_BE_NEEDED', () => {
363
+ beforeEach(() => {
364
+ player.configure(
365
+ 'autoShowText',
366
+ shaka.config.AutoShowText.IF_SUBTITLES_MAY_BE_NEEDED);
367
+ });
368
+
369
+ it('enables text if text matches and audio does not', async () => {
370
+ await textMatchesAudioDoesNot();
371
+ expect(player.isTextTrackVisible()).toBe(true);
372
+ });
373
+
374
+ it('disables text if text does not match', async () => {
375
+ await textDoesNotMatch();
376
+ expect(player.isTextTrackVisible()).toBe(false);
377
+ });
378
+
379
+ it('disables text if both text and audio match', async () => {
380
+ await textAndAudioMatch();
381
+ expect(player.isTextTrackVisible()).toBe(false);
382
+ });
383
+ }); // IF_SUBTITLES_MAY_BE_NEEDED
384
+
385
+ describe('IF_PREFERRED_TEXT_LANGUAGE', () => {
386
+ beforeEach(() => {
387
+ player.configure(
388
+ 'autoShowText',
389
+ shaka.config.AutoShowText.IF_PREFERRED_TEXT_LANGUAGE);
390
+ });
391
+
392
+ it('enables text if text matches and audio does not', async () => {
393
+ await textMatchesAudioDoesNot();
394
+ expect(player.isTextTrackVisible()).toBe(true);
395
+ });
396
+
397
+ it('disables text if text does not match', async () => {
398
+ await textDoesNotMatch();
399
+ expect(player.isTextTrackVisible()).toBe(false);
400
+ });
401
+
402
+ it('enables text if both text and audio match', async () => {
403
+ await textAndAudioMatch();
404
+ expect(player.isTextTrackVisible()).toBe(true);
405
+ });
406
+ }); // IF_PREFERRED_TEXT_LANGUAGE
407
+
408
+ describe('ALWAYS', () => {
409
+ beforeEach(() => {
410
+ player.configure('autoShowText', shaka.config.AutoShowText.ALWAYS);
411
+ });
412
+
413
+ it('enables text if text matches and audio does not', async () => {
414
+ await textMatchesAudioDoesNot();
415
+ expect(player.isTextTrackVisible()).toBe(true);
416
+ });
417
+
418
+ it('enables text if text does not match', async () => {
419
+ await textDoesNotMatch();
420
+ expect(player.isTextTrackVisible()).toBe(true);
421
+ });
422
+
423
+ it('enables text if both text and audio match', async () => {
424
+ await textAndAudioMatch();
425
+ expect(player.isTextTrackVisible()).toBe(true);
426
+ });
427
+ }); // ALWAYS
428
+
429
+ describe('NEVER', () => {
430
+ beforeEach(() => {
431
+ player.configure('autoShowText', shaka.config.AutoShowText.NEVER);
432
+ });
433
+
434
+ it('disables text if text matches and audio does not', async () => {
435
+ await textMatchesAudioDoesNot();
436
+ expect(player.isTextTrackVisible()).toBe(false);
437
+ });
438
+
439
+ it('disables text if text does not match', async () => {
440
+ await textDoesNotMatch();
441
+ expect(player.isTextTrackVisible()).toBe(false);
442
+ });
443
+
444
+ it('disables text if both text and audio match', async () => {
445
+ await textAndAudioMatch();
446
+ expect(player.isTextTrackVisible()).toBe(false);
447
+ });
448
+ }); // NEVER
449
+ }); // AutoShowText
450
+
379
451
  describe('plays', () => {
380
452
  it('with external text tracks', async () => {
381
453
  await player.load('test:sintel_no_text_compiled');
@@ -1152,4 +1224,74 @@ describe('Player', () => {
1152
1224
  expect(chapter3.endTime).toBe(61.349);
1153
1225
  });
1154
1226
  }); // describe('addChaptersTrack')
1227
+
1228
+ it('requires a license server for HLS ClearKey content', async () => {
1229
+ const expectedError = Util.jasmineError(new shaka.util.Error(
1230
+ shaka.util.Error.Severity.CRITICAL,
1231
+ shaka.util.Error.Category.DRM,
1232
+ shaka.util.Error.Code.NO_LICENSE_SERVER_GIVEN,
1233
+ 'org.w3.clearkey'));
1234
+ await expectAsync(player.load('test:sintel-hls-clearkey'))
1235
+ .toBeRejectedWith(expectedError);
1236
+ });
1237
+
1238
+ describe('addThumbnailsTrack', () => {
1239
+ it('appends thumbnails for external thumbnails with sprites',
1240
+ async () => {
1241
+ await player.load('test:sintel_no_text_compiled');
1242
+ const locationUri = new goog.Uri(location.href);
1243
+ const partialUri =
1244
+ new goog.Uri('/base/test/test/assets/thumbnails-sprites.vtt');
1245
+ const absoluteUri = locationUri.resolve(partialUri);
1246
+ const newTrack =
1247
+ await player.addThumbnailsTrack(absoluteUri.toString());
1248
+
1249
+ expect(player.getImageTracks()).toEqual([newTrack]);
1250
+
1251
+ const thumbnail1 = await player.getThumbnails(newTrack.id, 0);
1252
+ expect(thumbnail1.startTime).toBe(0);
1253
+ expect(thumbnail1.duration).toBe(5);
1254
+ expect(thumbnail1.height).toBe(90);
1255
+ expect(thumbnail1.positionX).toBe(0);
1256
+ expect(thumbnail1.positionY).toBe(0);
1257
+ expect(thumbnail1.width).toBe(160);
1258
+ const thumbnail2 = await player.getThumbnails(newTrack.id, 10);
1259
+ expect(thumbnail2.startTime).toBe(5);
1260
+ expect(thumbnail2.duration).toBe(25);
1261
+ expect(thumbnail2.height).toBe(90);
1262
+ expect(thumbnail2.positionX).toBe(160);
1263
+ expect(thumbnail2.positionY).toBe(0);
1264
+ expect(thumbnail2.width).toBe(160);
1265
+ const thumbnail3 = await player.getThumbnails(newTrack.id, 40);
1266
+ expect(thumbnail3.startTime).toBe(30);
1267
+ expect(thumbnail3.duration).toBe(30);
1268
+ expect(thumbnail3.height).toBe(90);
1269
+ expect(thumbnail3.positionX).toBe(160);
1270
+ expect(thumbnail3.positionY).toBe(90);
1271
+ expect(thumbnail3.width).toBe(160);
1272
+ });
1273
+
1274
+ it('appends thumbnails for external thumbnails without sprites',
1275
+ async () => {
1276
+ await player.load('test:sintel_no_text_compiled');
1277
+ const locationUri = new goog.Uri(location.href);
1278
+ const partialUri =
1279
+ new goog.Uri('/base/test/test/assets/thumbnails.vtt');
1280
+ const absoluteUri = locationUri.resolve(partialUri);
1281
+ const newTrack =
1282
+ await player.addThumbnailsTrack(absoluteUri.toString());
1283
+
1284
+ expect(player.getImageTracks()).toEqual([newTrack]);
1285
+
1286
+ const thumbnail1 = await player.getThumbnails(newTrack.id, 0);
1287
+ expect(thumbnail1.startTime).toBe(0);
1288
+ expect(thumbnail1.duration).toBe(5);
1289
+ const thumbnail2 = await player.getThumbnails(newTrack.id, 10);
1290
+ expect(thumbnail2.startTime).toBe(5);
1291
+ expect(thumbnail2.duration).toBe(25);
1292
+ const thumbnail3 = await player.getThumbnails(newTrack.id, 40);
1293
+ expect(thumbnail3.startTime).toBe(30);
1294
+ expect(thumbnail3.duration).toBe(30);
1295
+ });
1296
+ }); // describe('addThumbnailsTrack')
1155
1297
  });
@@ -123,6 +123,7 @@ describe('Player', () => {
123
123
  streamingEngine = new shaka.test.FakeStreamingEngine();
124
124
  mediaSourceEngine = {
125
125
  init: jasmine.createSpy('init').and.returnValue(Promise.resolve()),
126
+ configure: jasmine.createSpy('configure'),
126
127
  open: jasmine.createSpy('open').and.returnValue(Promise.resolve()),
127
128
  destroy:
128
129
  jasmine.createSpy('destroy').and.returnValue(Promise.resolve()),
@@ -130,6 +131,8 @@ describe('Player', () => {
130
131
  getUseEmbeddedText: jasmine.createSpy('getUseEmbeddedText'),
131
132
  setSegmentRelativeVttTiming:
132
133
  jasmine.createSpy('setSegmentRelativeVttTiming'),
134
+ updateLcevcDil:
135
+ jasmine.createSpy('updateLcevcDil'),
133
136
  getTextDisplayer: () => textDisplayer,
134
137
  getBufferedInfo: () => bufferedInfo,
135
138
  ended: jasmine.createSpy('ended').and.returnValue(false),
@@ -626,13 +629,13 @@ describe('Player', () => {
626
629
  // Try a bogus streaming config (number instead of Object)
627
630
  logErrorSpy.calls.reset();
628
631
  player.configure({
629
- streaming: 5,
632
+ drm: 5,
630
633
  });
631
634
 
632
635
  newConfig = player.getConfiguration();
633
636
  expect(newConfig).toEqual(defaultConfig);
634
637
  expect(logErrorSpy).toHaveBeenCalledWith(
635
- stringContaining('.streaming'));
638
+ stringContaining('.drm'));
636
639
  });
637
640
 
638
641
  it('accepts synchronous function values for async function fields', () => {
@@ -3940,6 +3943,93 @@ describe('Player', () => {
3940
3943
  });
3941
3944
  });
3942
3945
 
3946
+ describe('config streaming.failureCallback default', () => {
3947
+ /** @type {jasmine.Spy} */
3948
+ let retryStreaming;
3949
+ /** @type {jasmine.Spy} */
3950
+ let isLive;
3951
+
3952
+ /**
3953
+ * @suppress {accessControls}
3954
+ * @param {!shaka.util.Error} error
3955
+ */
3956
+ function defaultStreamingFailureCallback(error) {
3957
+ player.defaultStreamingFailureCallback_(error);
3958
+ }
3959
+
3960
+ beforeEach(() => {
3961
+ retryStreaming = jasmine.createSpy('retryStreaming');
3962
+ isLive = jasmine.createSpy('isLive');
3963
+
3964
+ player.retryStreaming = Util.spyFunc(retryStreaming);
3965
+ player.isLive = Util.spyFunc(isLive);
3966
+ });
3967
+
3968
+ it('ignores VOD failures', () => {
3969
+ isLive.and.returnValue(false);
3970
+
3971
+ const error = new shaka.util.Error(
3972
+ shaka.util.Error.Severity.CRITICAL,
3973
+ shaka.util.Error.Category.NETWORK,
3974
+ shaka.util.Error.Code.BAD_HTTP_STATUS,
3975
+ /* url= */ '',
3976
+ /* http_status= */ 404);
3977
+
3978
+ defaultStreamingFailureCallback(error);
3979
+ expect(retryStreaming).not.toHaveBeenCalled();
3980
+ });
3981
+
3982
+ it('retries live on HTTP 404', () => {
3983
+ isLive.and.returnValue(true);
3984
+
3985
+ const error = new shaka.util.Error(
3986
+ shaka.util.Error.Severity.CRITICAL,
3987
+ shaka.util.Error.Category.NETWORK,
3988
+ shaka.util.Error.Code.BAD_HTTP_STATUS,
3989
+ /* url= */ '',
3990
+ /* http_status= */ 404);
3991
+
3992
+ defaultStreamingFailureCallback(error);
3993
+ expect(retryStreaming).toHaveBeenCalled();
3994
+ });
3995
+
3996
+ it('retries live on generic HTTP error', () => {
3997
+ isLive.and.returnValue(true);
3998
+
3999
+ const error = new shaka.util.Error(
4000
+ shaka.util.Error.Severity.CRITICAL,
4001
+ shaka.util.Error.Category.NETWORK,
4002
+ shaka.util.Error.Code.HTTP_ERROR);
4003
+
4004
+ defaultStreamingFailureCallback(error);
4005
+ expect(retryStreaming).toHaveBeenCalled();
4006
+ });
4007
+
4008
+ it('retries live on HTTP timeout', () => {
4009
+ isLive.and.returnValue(true);
4010
+
4011
+ const error = new shaka.util.Error(
4012
+ shaka.util.Error.Severity.CRITICAL,
4013
+ shaka.util.Error.Category.NETWORK,
4014
+ shaka.util.Error.Code.TIMEOUT);
4015
+
4016
+ defaultStreamingFailureCallback(error);
4017
+ expect(retryStreaming).toHaveBeenCalled();
4018
+ });
4019
+
4020
+ it('ignores other live failures', () => {
4021
+ isLive.and.returnValue(true);
4022
+
4023
+ const error = new shaka.util.Error(
4024
+ shaka.util.Error.Severity.CRITICAL,
4025
+ shaka.util.Error.Category.MEDIA,
4026
+ shaka.util.Error.Code.VIDEO_ERROR);
4027
+
4028
+ defaultStreamingFailureCallback(error);
4029
+ expect(retryStreaming).not.toHaveBeenCalled();
4030
+ });
4031
+ });
4032
+
3943
4033
  /**
3944
4034
  * Gets the currently active variant track.
3945
4035
  * @return {shaka.extern.Track}
@@ -0,0 +1,124 @@
1
+ describe('PatchedMediaKeys_Apple', () => {
2
+ const PatchedMediaKeysApple = shaka.polyfill.PatchedMediaKeysApple;
3
+ let originalMediaKeys;
4
+ let originalHTMLMediaElementPrototypeSetMediaKeys;
5
+ let originalWindowMediaKeys;
6
+ let originalWindowMediaKeySystemAccess;
7
+ let originalNavigatorRequestMediaKeySystemAccess;
8
+ let originalHTMLSVideoElement;
9
+ let originalWebKitMediaKeys;
10
+
11
+ beforeEach(() => {
12
+ originalHTMLSVideoElement = window.HTMLVideoElement;
13
+ originalWebKitMediaKeys = window.WebKitMediaKeys;
14
+
15
+ Object.defineProperty(window,
16
+ 'HTMLVideoElement', {value: {}, configurable: true, writable: true});
17
+ Object.defineProperty(window,
18
+ 'WebKitMediaKeys', {value: {}, configurable: true, writable: true});
19
+
20
+ originalMediaKeys = /** @type {!Object} */ (
21
+ Object.getOwnPropertyDescriptor(
22
+ // eslint-disable-next-line no-restricted-syntax
23
+ HTMLMediaElement.prototype, 'mediaKeys',
24
+ )
25
+ );
26
+
27
+ // eslint-disable-next-line no-restricted-syntax
28
+ originalHTMLMediaElementPrototypeSetMediaKeys = HTMLMediaElement
29
+ .prototype.setMediaKeys;
30
+ originalWindowMediaKeys = window.MediaKeys;
31
+ originalWindowMediaKeySystemAccess = window.MediaKeySystemAccess;
32
+ originalNavigatorRequestMediaKeySystemAccess = navigator
33
+ .requestMediaKeySystemAccess;
34
+
35
+ delete window.shakaMediaKeysPolyfill;
36
+ });
37
+
38
+ afterEach(() => {
39
+ window.HTMLVideoElement = originalHTMLSVideoElement;
40
+ window.WebKitMediaKeys = originalWebKitMediaKeys;
41
+
42
+ Object.defineProperty(
43
+ // eslint-disable-next-line no-restricted-syntax
44
+ HTMLMediaElement.prototype,
45
+ 'mediaKeys',
46
+ originalMediaKeys,
47
+ );
48
+
49
+ // eslint-disable-next-line no-restricted-syntax
50
+ HTMLMediaElement
51
+ .prototype.setMediaKeys =
52
+ originalHTMLMediaElementPrototypeSetMediaKeys;
53
+ window.MediaKeys = originalWindowMediaKeys;
54
+ window.MediaKeySystemAccess = originalWindowMediaKeySystemAccess;
55
+ navigator
56
+ .requestMediaKeySystemAccess =
57
+ originalNavigatorRequestMediaKeySystemAccess;
58
+
59
+ delete window.shakaMediaKeysPolyfill;
60
+ });
61
+
62
+ describe('install', () => {
63
+ it('should override browser globals', () => {
64
+ shaka.polyfill.PatchedMediaKeysApple.install();
65
+
66
+ expect(shaka.polyfill.PatchedMediaKeysApple.enableUninstall)
67
+ .toBe(undefined);
68
+
69
+ expect(
70
+ originalHTMLMediaElementPrototypeSetMediaKeys,
71
+ ).not.toEqual(
72
+ // eslint-disable-next-line no-restricted-syntax
73
+ HTMLMediaElement.prototype.setMediaKeys,
74
+ );
75
+
76
+ expect(
77
+ Object.getOwnPropertyDescriptor(
78
+ // eslint-disable-next-line no-restricted-syntax
79
+ HTMLMediaElement.prototype, 'mediaKeys',
80
+ ).value,
81
+ ).toBeNull();
82
+
83
+ // eslint-disable-next-line no-restricted-syntax
84
+ expect(HTMLMediaElement.prototype.setMediaKeys)
85
+ .toEqual(PatchedMediaKeysApple.setMediaKeys);
86
+
87
+ expect(window.MediaKeys).toEqual(PatchedMediaKeysApple.MediaKeys);
88
+ expect(window.MediaKeySystemAccess)
89
+ .toEqual(PatchedMediaKeysApple.MediaKeySystemAccess);
90
+ expect(navigator.requestMediaKeySystemAccess)
91
+ .toEqual(PatchedMediaKeysApple.requestMediaKeySystemAccess);
92
+ expect(window.shakaMediaKeysPolyfill).toBe(true);
93
+ });
94
+ });
95
+
96
+ describe('uninstall', () => {
97
+ it('should restore browser globals', () => {
98
+ shaka.polyfill.PatchedMediaKeysApple.install(true);
99
+
100
+ expect(shaka.polyfill.PatchedMediaKeysApple.enableUninstall)
101
+ .toBe(true);
102
+
103
+ shaka.polyfill.PatchedMediaKeysApple.uninstall();
104
+
105
+ expect(
106
+ Object.getOwnPropertyDescriptor(
107
+ // eslint-disable-next-line no-restricted-syntax
108
+ HTMLMediaElement.prototype, 'mediaKeys',
109
+ ).value,
110
+ ).not.toBeNull();
111
+
112
+ // eslint-disable-next-line no-restricted-syntax
113
+ expect(HTMLMediaElement.prototype.setMediaKeys)
114
+ .toEqual(originalHTMLMediaElementPrototypeSetMediaKeys);
115
+
116
+ expect(window.MediaKeys).toEqual(originalWindowMediaKeys);
117
+ expect(window.MediaKeySystemAccess)
118
+ .toEqual(originalWindowMediaKeySystemAccess);
119
+ expect(navigator.requestMediaKeySystemAccess)
120
+ .toEqual(originalNavigatorRequestMediaKeySystemAccess);
121
+ expect(window.shakaMediaKeysPolyfill).toBe(false);
122
+ });
123
+ });
124
+ });
Binary file
@@ -0,0 +1,10 @@
1
+ WEBVTT
2
+
3
+ 00:00.000 --> 00:05.000
4
+ image1.jpg#xywh=0,0,160,90
5
+
6
+ 00:05.000 --> 00:30.000
7
+ image2.jpg#xywh=160,0,160,90
8
+
9
+ 00:30.000 --> 01:00.000
10
+ image3.jpg#xywh=160,90,160,90
@@ -0,0 +1,10 @@
1
+ WEBVTT
2
+
3
+ 00:00.000 --> 00:05.000
4
+ image1.jpg
5
+
6
+ 00:05.000 --> 00:30.000
7
+ image2.jpg
8
+
9
+ 00:30.000 --> 01:00.000
10
+ image3.jpg
@@ -44,6 +44,7 @@ shaka.test.Dash = class {
44
44
  isAutoLowLatencyMode: () => false,
45
45
  enableLowLatencyMode: () => {},
46
46
  updateDuration: () => {},
47
+ newDrmInfo: (stream) => {},
47
48
  };
48
49
  const manifest = await dashParser.start('dummy://foo', playerInterface);
49
50
  const stream = manifest.variants[0].video;
@@ -79,6 +80,7 @@ shaka.test.Dash = class {
79
80
  isAutoLowLatencyMode: () => false,
80
81
  enableLowLatencyMode: () => {},
81
82
  updateDuration: () => {},
83
+ newDrmInfo: (stream) => {},
82
84
  };
83
85
  const p = dashParser.start('dummy://foo', playerInterface);
84
86
  await expectAsync(p).toBeRejectedWith(
@@ -34,6 +34,12 @@ shaka.test.FakeAd = class {
34
34
 
35
35
  /** @private {number} */
36
36
  this.volume_ = 1;
37
+
38
+ /** @private {string} */
39
+ this.title_ = 'Test Title';
40
+
41
+ /** @private {string} */
42
+ this.description_ = 'Test Description';
37
43
  }
38
44
 
39
45
  /**
@@ -190,6 +196,21 @@ shaka.test.FakeAd = class {
190
196
  return this.position_;
191
197
  }
192
198
 
199
+ /**
200
+ * @override
201
+ * @export
202
+ */
203
+ getTitle() {
204
+ return this.title_;
205
+ }
206
+
207
+ /**
208
+ * @override
209
+ * @export
210
+ */
211
+ getDescription() {
212
+ return this.description_;
213
+ }
193
214
 
194
215
  /**
195
216
  * @override
@@ -54,6 +54,9 @@ shaka.test.FakeMediaSourceEngine = class {
54
54
  /** @type {!jasmine.Spy} */
55
55
  this.open = jasmine.createSpy('open').and.returnValue(Promise.resolve());
56
56
 
57
+ /** @type {!jasmine.Spy} */
58
+ this.configure = jasmine.createSpy('configure').and.stub();
59
+
57
60
  /** @type {!jasmine.Spy} */
58
61
  this.reinitText = jasmine.createSpy('reinitText').and.stub();
59
62
 
@@ -129,6 +132,10 @@ shaka.test.FakeMediaSourceEngine = class {
129
132
  /** @type {!jasmine.Spy} */
130
133
  this.setSegmentRelativeVttTiming =
131
134
  jasmine.createSpy('setSegmentRelativeVttTiming').and.stub();
135
+
136
+ /** @type {!jasmine.Spy} */
137
+ this.updateLcevcDil =
138
+ jasmine.createSpy('updateLcevcDil').and.stub();
132
139
  }
133
140
 
134
141
  /** @override */