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
@@ -783,7 +783,7 @@ describe('HlsParser', () => {
783
783
  'RESOLUTION=960x540,FRAME-RATE=120,AUDIO="aud2"\n',
784
784
  'video2\n',
785
785
  '#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="eng",',
786
- 'URI="audio"\n',
786
+ 'DEFAULT=YES,URI="audio"\n',
787
787
  '#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud2",LANGUAGE="fr",',
788
788
  'URI="audio2"\n',
789
789
  ].join('');
@@ -801,6 +801,7 @@ describe('HlsParser', () => {
801
801
  manifest.anyTimeline();
802
802
  manifest.addPartialVariant((variant) => {
803
803
  variant.bandwidth = 200;
804
+ variant.primary = true;
804
805
  variant.addPartialStream(ContentType.VIDEO, (stream) => {
805
806
  stream.size(960, 540);
806
807
  });
@@ -810,6 +811,7 @@ describe('HlsParser', () => {
810
811
  });
811
812
  manifest.addPartialVariant((variant) => {
812
813
  variant.bandwidth = 300;
814
+ variant.primary = false;
813
815
  variant.addPartialStream(ContentType.VIDEO, (stream) => {
814
816
  stream.size(960, 540);
815
817
  });
@@ -1307,6 +1309,8 @@ describe('HlsParser', () => {
1307
1309
  // stream.
1308
1310
  const timeline = actual.presentationTimeline;
1309
1311
  expect(timeline.getDuration()).toBe(10);
1312
+ expect(timeline.getSeekRangeStart()).toBe(0);
1313
+ expect(timeline.getSeekRangeEnd()).toBe(10);
1310
1314
 
1311
1315
  expect(actual.textStreams.length).toBe(1);
1312
1316
  expect(actual.variants.length).toBe(1);
@@ -1314,6 +1318,103 @@ describe('HlsParser', () => {
1314
1318
  expect(actual.variants[0].video).toBeTruthy();
1315
1319
  });
1316
1320
 
1321
+ it('parse image streams', async () => {
1322
+ const master = [
1323
+ '#EXTM3U\n',
1324
+ '#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="sub1",LANGUAGE="eng",',
1325
+ 'URI="text"\n',
1326
+ '#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="eng",',
1327
+ 'CHANNELS="2",URI="audio"\n',
1328
+ '#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1,mp4a",',
1329
+ 'RESOLUTION=960x540,FRAME-RATE=60,AUDIO="aud1",SUBTITLES="sub1"\n',
1330
+ 'video\n',
1331
+ '#EXT-X-IMAGE-STREAM-INF:RESOLUTION=240×135,CODECS="jpeg",',
1332
+ 'URI="image"\n',
1333
+ ].join('');
1334
+
1335
+ const video = [
1336
+ '#EXTM3U\n',
1337
+ '#EXT-X-PLAYLIST-TYPE:VOD\n',
1338
+ '#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
1339
+ '#EXTINF:5,\n',
1340
+ 'main.mp4\n',
1341
+ '#EXTINF:5,\n',
1342
+ 'main.mp4\n',
1343
+ '#EXTINF:5,\n',
1344
+ 'main.mp4\n',
1345
+ ].join('');
1346
+
1347
+ const audio = [
1348
+ '#EXTM3U\n',
1349
+ '#EXT-X-PLAYLIST-TYPE:VOD\n',
1350
+ '#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
1351
+ '#EXTINF:5,\n',
1352
+ 'main.mp4\n',
1353
+ '#EXTINF:5,\n',
1354
+ 'main.mp4\n',
1355
+ '#EXTINF:5,\n',
1356
+ 'main.mp4\n',
1357
+ ].join('');
1358
+
1359
+ const text = [
1360
+ '#EXTM3U\n',
1361
+ '#EXT-X-PLAYLIST-TYPE:VOD\n',
1362
+ '#EXTINF:5,\n',
1363
+ '#EXT-X-BYTERANGE:121090@616\n',
1364
+ 'main.vtt',
1365
+ ].join('');
1366
+
1367
+ const image = [
1368
+ '#EXTM3U\n',
1369
+ '#EXT-X-PLAYLIST-TYPE:VOD\n',
1370
+ '#EXTINF:5,\n',
1371
+ 'image.jpg\n',
1372
+ '#EXTINF:5,\n',
1373
+ '#EXT-X-TILES:RESOLUTION=640x360,LAYOUT=5x2,DURATION=6.006\n',
1374
+ 'image.jpg\n',
1375
+ '#EXTINF:5,\n',
1376
+ 'image.jpg\n',
1377
+ ].join('');
1378
+
1379
+ fakeNetEngine
1380
+ .setResponseText('test:/master', master)
1381
+ .setResponseText('test:/audio', audio)
1382
+ .setResponseText('test:/video', video)
1383
+ .setResponseText('test:/text', text)
1384
+ .setResponseText('test:/image', image)
1385
+ .setResponseText('test:/main.vtt', vttText)
1386
+ .setResponseValue('test:/init.mp4', initSegmentData)
1387
+ .setResponseValue('test:/main.mp4', segmentData);
1388
+
1389
+ const actual = await parser.start('test:/master', playerInterface);
1390
+
1391
+ expect(actual.imageStreams.length).toBe(1);
1392
+ expect(actual.textStreams.length).toBe(1);
1393
+ expect(actual.variants.length).toBe(1);
1394
+
1395
+ const thumbnails = actual.imageStreams[0];
1396
+
1397
+ await thumbnails.createSegmentIndex();
1398
+ goog.asserts.assert(thumbnails.segmentIndex != null, 'Null segmentIndex!');
1399
+
1400
+ const firstThumbnailReference = thumbnails.segmentIndex.get(0);
1401
+ const secondThumbnailReference = thumbnails.segmentIndex.get(1);
1402
+ const thirdThumbnailReference = thumbnails.segmentIndex.get(2);
1403
+
1404
+ expect(firstThumbnailReference).not.toBe(null);
1405
+ expect(secondThumbnailReference).not.toBe(null);
1406
+ expect(thirdThumbnailReference).not.toBe(null);
1407
+ if (firstThumbnailReference) {
1408
+ expect(firstThumbnailReference.getTilesLayout()).toBe('1x1');
1409
+ }
1410
+ if (secondThumbnailReference) {
1411
+ expect(secondThumbnailReference.getTilesLayout()).toBe('5x2');
1412
+ }
1413
+ if (thirdThumbnailReference) {
1414
+ expect(thirdThumbnailReference.getTilesLayout()).toBe('1x1');
1415
+ }
1416
+ });
1417
+
1317
1418
  it('Disable audio does not create audio streams', async () => {
1318
1419
  const master = [
1319
1420
  '#EXTM3U\n',
@@ -1324,6 +1425,8 @@ describe('HlsParser', () => {
1324
1425
  '#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1,mp4a",',
1325
1426
  'RESOLUTION=960x540,FRAME-RATE=60,AUDIO="aud1",SUBTITLES="sub1"\n',
1326
1427
  'video\n',
1428
+ '#EXT-X-IMAGE-STREAM-INF:RESOLUTION=240×135,CODECS="jpeg",',
1429
+ 'URI="image"\n',
1327
1430
  ].join('');
1328
1431
 
1329
1432
  const video = [
@@ -1356,11 +1459,23 @@ describe('HlsParser', () => {
1356
1459
  'main.vtt',
1357
1460
  ].join('');
1358
1461
 
1462
+ const image = [
1463
+ '#EXTM3U\n',
1464
+ '#EXT-X-PLAYLIST-TYPE:VOD\n',
1465
+ '#EXTINF:5,\n',
1466
+ 'image.jpg\n',
1467
+ '#EXTINF:5,\n',
1468
+ 'image.jpg\n',
1469
+ '#EXTINF:5,\n',
1470
+ 'image.jpg\n',
1471
+ ].join('');
1472
+
1359
1473
  fakeNetEngine
1360
1474
  .setResponseText('test:/master', master)
1361
1475
  .setResponseText('test:/audio', audio)
1362
1476
  .setResponseText('test:/video', video)
1363
1477
  .setResponseText('test:/text', text)
1478
+ .setResponseText('test:/image', image)
1364
1479
  .setResponseText('test:/main.vtt', vttText)
1365
1480
  .setResponseValue('test:/init.mp4', initSegmentData)
1366
1481
  .setResponseValue('test:/main.mp4', segmentData);
@@ -1385,6 +1500,8 @@ describe('HlsParser', () => {
1385
1500
  '#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1,mp4a",',
1386
1501
  'RESOLUTION=960x540,FRAME-RATE=60,AUDIO="aud1",SUBTITLES="sub1"\n',
1387
1502
  'video\n',
1503
+ '#EXT-X-IMAGE-STREAM-INF:RESOLUTION=240×135,CODECS="jpeg",',
1504
+ 'URI="image"\n',
1388
1505
  ].join('');
1389
1506
 
1390
1507
  const video = [
@@ -1417,11 +1534,23 @@ describe('HlsParser', () => {
1417
1534
  'main.vtt',
1418
1535
  ].join('');
1419
1536
 
1537
+ const image = [
1538
+ '#EXTM3U\n',
1539
+ '#EXT-X-PLAYLIST-TYPE:VOD\n',
1540
+ '#EXTINF:5,\n',
1541
+ 'image.jpg\n',
1542
+ '#EXTINF:5,\n',
1543
+ 'image.jpg\n',
1544
+ '#EXTINF:5,\n',
1545
+ 'image.jpg\n',
1546
+ ].join('');
1547
+
1420
1548
  fakeNetEngine
1421
1549
  .setResponseText('test:/master', master)
1422
1550
  .setResponseText('test:/audio', audio)
1423
1551
  .setResponseText('test:/video', video)
1424
1552
  .setResponseText('test:/text', text)
1553
+ .setResponseText('test:/image', image)
1425
1554
  .setResponseText('test:/main.vtt', vttText)
1426
1555
  .setResponseValue('test:/init.mp4', initSegmentData)
1427
1556
  .setResponseValue('test:/main.mp4', segmentData);
@@ -1446,6 +1575,8 @@ describe('HlsParser', () => {
1446
1575
  '#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1,mp4a",',
1447
1576
  'RESOLUTION=960x540,FRAME-RATE=60,AUDIO="aud1",SUBTITLES="sub1"\n',
1448
1577
  'video\n',
1578
+ '#EXT-X-IMAGE-STREAM-INF:RESOLUTION=240×135,CODECS="jpeg",',
1579
+ 'URI="image"\n',
1449
1580
  ].join('');
1450
1581
 
1451
1582
  const video = [
@@ -1478,11 +1609,23 @@ describe('HlsParser', () => {
1478
1609
  'main.vtt',
1479
1610
  ].join('');
1480
1611
 
1612
+ const image = [
1613
+ '#EXTM3U\n',
1614
+ '#EXT-X-PLAYLIST-TYPE:VOD\n',
1615
+ '#EXTINF:5,\n',
1616
+ 'image.jpg\n',
1617
+ '#EXTINF:5,\n',
1618
+ 'image.jpg\n',
1619
+ '#EXTINF:5,\n',
1620
+ 'image.jpg\n',
1621
+ ].join('');
1622
+
1481
1623
  fakeNetEngine
1482
1624
  .setResponseText('test:/master', master)
1483
1625
  .setResponseText('test:/audio', audio)
1484
1626
  .setResponseText('test:/video', video)
1485
1627
  .setResponseText('test:/text', text)
1628
+ .setResponseText('test:/image', image)
1486
1629
  .setResponseText('test:/main.vtt', vttText)
1487
1630
  .setResponseValue('test:/init.mp4', initSegmentData)
1488
1631
  .setResponseValue('test:/main.mp4', segmentData);
@@ -1496,6 +1639,80 @@ describe('HlsParser', () => {
1496
1639
  expect(stream).toBeUndefined();
1497
1640
  });
1498
1641
 
1642
+ it('Disable thumbnails does not create image streams', async () => {
1643
+ const master = [
1644
+ '#EXTM3U\n',
1645
+ '#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="sub1",LANGUAGE="eng",',
1646
+ 'URI="text"\n',
1647
+ '#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="eng",',
1648
+ 'CHANNELS="2",URI="audio"\n',
1649
+ '#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1,mp4a",',
1650
+ 'RESOLUTION=960x540,FRAME-RATE=60,AUDIO="aud1",SUBTITLES="sub1"\n',
1651
+ 'video\n',
1652
+ '#EXT-X-IMAGE-STREAM-INF:RESOLUTION=240×135,CODECS="jpeg",',
1653
+ 'URI="image"\n',
1654
+ ].join('');
1655
+
1656
+ const video = [
1657
+ '#EXTM3U\n',
1658
+ '#EXT-X-PLAYLIST-TYPE:VOD\n',
1659
+ '#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
1660
+ '#EXTINF:5,\n',
1661
+ 'main.mp4\n',
1662
+ '#EXTINF:5,\n',
1663
+ 'main.mp4\n',
1664
+ '#EXTINF:5,\n',
1665
+ 'main.mp4\n',
1666
+ ].join('');
1667
+
1668
+ const audio = [
1669
+ '#EXTM3U\n',
1670
+ '#EXT-X-PLAYLIST-TYPE:VOD\n',
1671
+ '#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
1672
+ '#EXTINF:5,\n',
1673
+ 'main.mp4\n',
1674
+ '#EXTINF:5,\n',
1675
+ 'main.mp4\n',
1676
+ ].join('');
1677
+
1678
+ const text = [
1679
+ '#EXTM3U\n',
1680
+ '#EXT-X-PLAYLIST-TYPE:VOD\n',
1681
+ '#EXTINF:5,\n',
1682
+ '#EXT-X-BYTERANGE:121090@616\n',
1683
+ 'main.vtt',
1684
+ ].join('');
1685
+
1686
+ const image = [
1687
+ '#EXTM3U\n',
1688
+ '#EXT-X-PLAYLIST-TYPE:VOD\n',
1689
+ '#EXTINF:5,\n',
1690
+ 'image.jpg\n',
1691
+ '#EXTINF:5,\n',
1692
+ 'image.jpg\n',
1693
+ '#EXTINF:5,\n',
1694
+ 'image.jpg\n',
1695
+ ].join('');
1696
+
1697
+ fakeNetEngine
1698
+ .setResponseText('test:/master', master)
1699
+ .setResponseText('test:/audio', audio)
1700
+ .setResponseText('test:/video', video)
1701
+ .setResponseText('test:/text', text)
1702
+ .setResponseText('test:/image', image)
1703
+ .setResponseText('test:/main.vtt', vttText)
1704
+ .setResponseValue('test:/init.mp4', initSegmentData)
1705
+ .setResponseValue('test:/main.mp4', segmentData);
1706
+
1707
+ const config = shaka.util.PlayerConfiguration.createDefault().manifest;
1708
+ config.disableThumbnails = true;
1709
+ parser.configure(config);
1710
+
1711
+ const actual = await parser.start('test:/master', playerInterface);
1712
+ const stream = actual.imageStreams[0];
1713
+ expect(stream).toBeUndefined();
1714
+ });
1715
+
1499
1716
  it('parses manifest with MP4+TTML streams', async () => {
1500
1717
  const master = [
1501
1718
  '#EXTM3U\n',
@@ -1666,6 +1883,43 @@ describe('HlsParser', () => {
1666
1883
  expect(actual).toEqual(manifest);
1667
1884
  });
1668
1885
 
1886
+ it('drops failed image streams when configured to', async () => {
1887
+ const master = [
1888
+ '#EXTM3U\n',
1889
+ '#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1",',
1890
+ 'RESOLUTION=960x540,FRAME-RATE=60\n',
1891
+ 'video\n',
1892
+ '#EXT-X-IMAGE-STREAM-INF:RESOLUTION=240×135,CODECS="jpeg"\n',
1893
+ ].join('');
1894
+
1895
+ const media = [
1896
+ '#EXTM3U\n',
1897
+ '#EXT-X-PLAYLIST-TYPE:VOD\n',
1898
+ '#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
1899
+ '#EXTINF:5,\n',
1900
+ '#EXT-X-BYTERANGE:121090@616\n',
1901
+ 'main.mp4',
1902
+ ].join('');
1903
+
1904
+ const manifest = shaka.test.ManifestGenerator.generate((manifest) => {
1905
+ manifest.anyTimeline();
1906
+ manifest.addPartialVariant((variant) => {
1907
+ variant.addPartialStream(ContentType.VIDEO);
1908
+ });
1909
+ });
1910
+
1911
+ fakeNetEngine
1912
+ .setResponseText('test:/master', master)
1913
+ .setResponseText('test:/audio', media)
1914
+ .setResponseText('test:/video', media)
1915
+ .setResponseValue('test:/init.mp4', initSegmentData)
1916
+ .setResponseValue('test:/main.mp4', segmentData);
1917
+
1918
+ config.hls.ignoreImageStreamFailures = true;
1919
+ const actual = await parser.start('test:/master', playerInterface);
1920
+ expect(actual).toEqual(manifest);
1921
+ });
1922
+
1669
1923
  it('parses video described by a media tag', async () => {
1670
1924
  const master = [
1671
1925
  '#EXTM3U\n',
@@ -1,3 +1,9 @@
1
+ /*! @license
2
+ * Shaka Player
3
+ * Copyright 2016 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
1
7
  goog.require('shaka.media.ClosedCaptionParser');
2
8
  goog.require('shaka.test.Util');
3
9
 
@@ -12,4 +18,3 @@ describe('ClosedCaptionParser', () => {
12
18
  parser.parseFrom(videoSegment);
13
19
  });
14
20
  });
15
-
@@ -162,7 +162,7 @@ describe('DrmEngine', () => {
162
162
  document.body.removeChild(video);
163
163
  });
164
164
 
165
- function checkSupport() {
165
+ function checkTrueDrmSupport() {
166
166
  if (shaka.util.Platform.isXboxOne()) {
167
167
  // Axinom won't issue a license for an Xbox One. The error message from
168
168
  // the license server says "Your DRM client's security level is 150, but
@@ -174,16 +174,11 @@ describe('DrmEngine', () => {
174
174
  return support['com.widevine.alpha'] || support['com.microsoft.playready'];
175
175
  }
176
176
 
177
- for (const useMediaCapabilities of [true, false]) {
178
- const isEnabled = useMediaCapabilities ? 'enabled' : 'disabled';
179
- filterDescribe('basic flow with MediaCapabilities ' + isEnabled,
180
- checkSupport, () => {
181
- testBasicFlow(useMediaCapabilities);
182
- });
177
+ function checkClearKeySupport() {
178
+ return support['org.w3.clearkey'];
183
179
  }
184
180
 
185
-
186
- function testBasicFlow(useMediaCapabilities) {
181
+ filterDescribe('basic flow', checkTrueDrmSupport, () => {
187
182
  drmIt('gets a license and can play encrypted segments', async () => {
188
183
  // The error callback should not be invoked.
189
184
  onErrorSpy.and.callFake(fail);
@@ -226,8 +221,7 @@ describe('DrmEngine', () => {
226
221
 
227
222
  const variants = manifest.variants;
228
223
 
229
- await drmEngine.initForPlayback(variants, manifest.offlineSessionIds,
230
- useMediaCapabilities);
224
+ await drmEngine.initForPlayback(variants, manifest.offlineSessionIds);
231
225
  await drmEngine.attach(video);
232
226
  await mediaSourceEngine.appendBuffer(
233
227
  ContentType.VIDEO, videoInitSegment, null, null,
@@ -283,5 +277,85 @@ describe('DrmEngine', () => {
283
277
  expect(video.readyState).toBeGreaterThan(1);
284
278
  expect(video.currentTime).toBeGreaterThan(0);
285
279
  });
286
- } // describe('basic flow')
280
+ }); // describe('basic flow')
281
+
282
+ filterDescribe('ClearKey', checkClearKeySupport, () => {
283
+ drmIt('plays encrypted content with the ClearKey CDM', async () => {
284
+ // Configure DrmEngine for ClearKey playback.
285
+ const config = shaka.util.PlayerConfiguration.createDefault().drm;
286
+ config.clearKeys = {
287
+ // From https://github.com/Axinom/public-test-vectors/tree/conservative#v61-multidrm
288
+ '6e5a1d26275747d78046eaa5d1d34b5a': '197f26f572c864d2338b3ae5d114ea9c',
289
+ };
290
+ drmEngine.configure(config);
291
+
292
+ // The error callback should not be invoked.
293
+ onErrorSpy.and.callFake(fail);
294
+
295
+ /** @type {!shaka.util.PublicPromise} */
296
+ const encryptedEventSeen = new shaka.util.PublicPromise();
297
+ eventManager.listen(video, 'encrypted', () => {
298
+ encryptedEventSeen.resolve();
299
+ });
300
+ eventManager.listen(video, 'error', () => {
301
+ fail('MediaError code ' + video.error.code);
302
+ let extended = video.error.msExtendedCode;
303
+ if (extended) {
304
+ if (extended < 0) {
305
+ extended += Math.pow(2, 32);
306
+ }
307
+ fail('MediaError msExtendedCode ' + extended.toString(16));
308
+ }
309
+ });
310
+
311
+ /** @type {!shaka.util.PublicPromise} */
312
+ const keyStatusEventSeen = new shaka.util.PublicPromise();
313
+ onKeyStatusSpy.and.callFake(() => {
314
+ keyStatusEventSeen.resolve();
315
+ });
316
+
317
+ const variants = manifest.variants;
318
+
319
+ await drmEngine.initForPlayback(variants, manifest.offlineSessionIds);
320
+ await drmEngine.attach(video);
321
+ await mediaSourceEngine.appendBuffer(
322
+ ContentType.VIDEO, videoInitSegment, null, null,
323
+ /* hasClosedCaptions= */ false);
324
+ await mediaSourceEngine.appendBuffer(
325
+ ContentType.AUDIO, audioInitSegment, null, null,
326
+ /* hasClosedCaptions= */ false);
327
+ await encryptedEventSeen;
328
+
329
+ // Some platforms (notably 2017 Tizen TVs) do not fire key status
330
+ // events.
331
+ const keyStatusTimeout = shaka.test.Util.delay(5);
332
+ await Promise.race([keyStatusTimeout, keyStatusEventSeen]);
333
+
334
+ const call = onKeyStatusSpy.calls.mostRecent();
335
+ if (call) {
336
+ const map = /** @type {!Object} */ (call.args[0]);
337
+ expect(Object.keys(map).length).not.toBe(0);
338
+ for (const k in map) {
339
+ expect(map[k]).toBe('usable');
340
+ }
341
+ }
342
+
343
+ await mediaSourceEngine.appendBuffer(
344
+ ContentType.VIDEO, videoSegment, 0, 10,
345
+ /* hasClosedCaptions= */ false);
346
+ await mediaSourceEngine.appendBuffer(
347
+ ContentType.AUDIO, audioSegment, 0, 10,
348
+ /* hasClosedCaptions= */ false);
349
+
350
+ expect(video.buffered.end(0)).toBeGreaterThan(0);
351
+ video.play();
352
+
353
+ const waiter = new shaka.test.Waiter(eventManager).timeoutAfter(15);
354
+ await waiter.waitForMovement(video);
355
+
356
+ // Something should have played by now.
357
+ expect(video.readyState).toBeGreaterThan(1);
358
+ expect(video.currentTime).toBeGreaterThan(0);
359
+ });
360
+ }); // describe('ClearKey')
287
361
  });