shaka-player 3.1.5 → 3.2.3
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.
- package/CHANGELOG.md +129 -6
- package/README.md +4 -4
- package/build/types/polyfill +1 -0
- package/demo/common/asset.js +9 -7
- package/demo/common/assets.js +10 -1
- package/demo/common/message_ids.js +2 -1
- package/demo/config.js +5 -2
- package/demo/index.html +1 -1
- package/demo/locales/en.json +2 -1
- package/demo/locales/source.json +8 -4
- package/demo/main.js +2 -1
- package/dist/demo.compiled.debug.js +14 -14
- package/dist/demo.compiled.debug.map +2 -2
- package/dist/demo.compiled.js +14 -14
- package/dist/demo.compiled.map +2 -2
- package/dist/deps.js +21 -20
- package/dist/locales.js +30 -30
- package/dist/receiver.compiled.debug.js +8 -8
- package/dist/receiver.compiled.debug.map +2 -2
- package/dist/receiver.compiled.js +8 -8
- package/dist/receiver.compiled.map +2 -2
- package/dist/shaka-player.compiled.d.ts +63 -10
- package/dist/shaka-player.compiled.debug.d.ts +63 -10
- package/dist/shaka-player.compiled.debug.externs.js +786 -716
- package/dist/shaka-player.compiled.debug.js +563 -534
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +786 -716
- package/dist/shaka-player.compiled.js +820 -809
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +63 -10
- package/dist/shaka-player.ui.debug.d.ts +63 -10
- package/dist/shaka-player.ui.debug.externs.js +542 -472
- package/dist/shaka-player.ui.debug.js +594 -566
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +542 -472
- package/dist/shaka-player.ui.js +938 -927
- package/dist/shaka-player.ui.map +4 -4
- package/docs/tutorials/ui.md +5 -5
- package/externs/aria.js +6 -0
- package/externs/shaka/ads.js +24 -1
- package/externs/shaka/manifest.js +6 -1
- package/externs/shaka/offline.js +5 -0
- package/externs/shaka/player.js +61 -9
- package/externs/shaka/text.js +10 -10
- package/externs/texttrack.js +3 -0
- package/lib/ads/ad_manager.js +16 -15
- package/lib/ads/client_side_ad_manager.js +6 -2
- package/lib/ads/server_side_ad_manager.js +19 -2
- package/lib/cast/cast_proxy.js +8 -0
- package/lib/cast/cast_utils.js +6 -1
- package/lib/cea/cea608_data_channel.js +56 -56
- package/lib/cea/cea608_memory.js +33 -33
- package/lib/cea/cea708_service.js +43 -43
- package/lib/cea/cea708_window.js +38 -38
- package/lib/cea/cea_decoder.js +13 -14
- package/lib/cea/cea_utils.js +23 -23
- package/lib/cea/dtvcc_packet_builder.js +7 -7
- package/lib/cea/i_caption_decoder.js +2 -3
- package/lib/cea/i_cea_parser.js +2 -2
- package/lib/cea/mp4_cea_parser.js +6 -6
- package/lib/dash/dash_parser.js +5 -0
- package/lib/hls/hls_parser.js +159 -12
- package/lib/media/closed_caption_parser.js +1 -2
- package/lib/media/drm_engine.js +162 -192
- package/lib/media/segment_index.js +4 -1
- package/lib/media/segment_reference.js +37 -2
- package/lib/media/streaming_engine.js +6 -6
- package/lib/net/networking_engine.js +1 -0
- package/lib/offline/download_manager.js +1 -2
- package/lib/offline/indexeddb/v1_storage_cell.js +1 -0
- package/lib/offline/indexeddb/v2_storage_cell.js +1 -0
- package/lib/offline/manifest_converter.js +3 -1
- package/lib/offline/storage.js +12 -14
- package/lib/player.js +426 -149
- package/lib/polyfill/aria.js +82 -0
- package/lib/routing/walker.js +2 -3
- package/lib/text/srt_text_parser.js +4 -4
- package/lib/text/text_engine.js +2 -2
- package/lib/text/vtt_text_parser.js +2 -2
- package/lib/text/web_vtt_generator.js +55 -12
- package/lib/util/error.js +6 -0
- package/lib/util/manifest_parser_utils.js +1 -0
- package/lib/util/mp4_box_parsers.js +20 -20
- package/lib/util/multi_map.js +8 -0
- package/lib/util/player_configuration.js +13 -1
- package/lib/util/stream_utils.js +168 -88
- package/lib/util/xml_utils.js +31 -12
- package/package.json +1 -1
- package/roadmap.md +24 -22
- package/shaka-player.uncompiled.js +1 -0
- package/test/cast/cast_utils_unit.js +1 -0
- package/test/cea/cea608_memory_unit.js +1 -1
- package/test/cea/cea708_service_unit.js +8 -8
- package/test/cea/cea708_window_unit.js +6 -6
- package/test/cea/cea_decoder_unit.js +1 -1
- package/test/cea/dtvcc_packet_builder_unit.js +2 -2
- package/test/dash/dash_parser_manifest_unit.js +43 -1
- package/test/hls/hls_parser_unit.js +250 -0
- package/test/media/drm_engine_integration.js +86 -12
- package/test/media/drm_engine_unit.js +310 -450
- package/test/offline/manifest_convert_unit.js +2 -0
- package/test/offline/storage_integration.js +9 -19
- package/test/player_integration.js +85 -0
- package/test/player_src_equals_integration.js +85 -0
- package/test/player_unit.js +25 -37
- package/test/test/assets/chapters.srt +11 -0
- package/test/test/assets/chapters.vtt +10 -0
- package/test/test/assets/chapters2.vtt +10 -0
- package/test/test/util/cea_utils.js +12 -12
- package/test/test/util/fake_ad_manager.js +7 -0
- package/test/test/util/manifest_generator.js +2 -0
- package/test/test/util/manifest_parser_util.js +3 -1
- package/test/text/web_vtt_generator_unit.js +81 -7
- package/test/util/stream_utils_unit.js +118 -10
- package/ui/airplay_button.js +1 -1
- package/ui/audio_language_selection.js +2 -5
- package/ui/cast_button.js +4 -6
- package/ui/constants.js +0 -3
- package/ui/fast_forward_button.js +2 -3
- package/ui/fullscreen_button.js +1 -3
- package/ui/language_utils.js +1 -1
- package/ui/loop_button.js +1 -3
- package/ui/mute_button.js +1 -3
- package/ui/overflow_menu.js +2 -3
- package/ui/pip_button.js +5 -7
- package/ui/playback_rate_selection.js +3 -6
- package/ui/presentation_time.js +1 -3
- package/ui/resolution_selection.js +4 -7
- package/ui/rewind_button.js +2 -3
- package/ui/seek_bar.js +2 -4
- package/ui/spacer.js +1 -1
- package/ui/text_selection.js +8 -11
- package/ui/ui_utils.js +1 -1
- package/ui/volume_bar.js +1 -3
|
@@ -36,7 +36,7 @@ describe('DashParser Manifest', () => {
|
|
|
36
36
|
/** @type {!ArrayBuffer} */
|
|
37
37
|
let mp4Index;
|
|
38
38
|
|
|
39
|
-
/** @type {
|
|
39
|
+
/** @type {string} */
|
|
40
40
|
const thumbnailScheme = 'http://dashif.org/guidelines/thumbnail_tile';
|
|
41
41
|
/**
|
|
42
42
|
* CICP scheme. parameter must be one of the following: "ColourPrimaries",
|
|
@@ -1798,6 +1798,48 @@ describe('DashParser Manifest', () => {
|
|
|
1798
1798
|
expect(manifest.presentationTimeline).toBeTruthy();
|
|
1799
1799
|
});
|
|
1800
1800
|
|
|
1801
|
+
it('Invokes manifestPreprocessor in config', async () => {
|
|
1802
|
+
const manifestText = [
|
|
1803
|
+
'<MPD minBufferTime="PT75S">',
|
|
1804
|
+
' <Period id="1" duration="PT30S">',
|
|
1805
|
+
' <AdaptationSet id="2" mimeType="video/mp4">',
|
|
1806
|
+
' <Representation id="video-sd" width="640" height="480">',
|
|
1807
|
+
' <BaseURL>v-sd.mp4</BaseURL>',
|
|
1808
|
+
' <SegmentBase indexRange="100-200" />',
|
|
1809
|
+
' </Representation>',
|
|
1810
|
+
' </AdaptationSet>',
|
|
1811
|
+
' <AdaptationSet id="3" mimeType="audio/mp4">',
|
|
1812
|
+
' <Representation id="audio-en">',
|
|
1813
|
+
' <BaseURL>a-en.mp4</BaseURL>',
|
|
1814
|
+
' <SegmentBase indexRange="100-200" />',
|
|
1815
|
+
' </Representation>',
|
|
1816
|
+
' </AdaptationSet>',
|
|
1817
|
+
' <AdaptationSet mimeType="text/vtt" lang="de">',
|
|
1818
|
+
' <Representation>',
|
|
1819
|
+
' <BaseURL>http://example.com/de.vtt</BaseURL>',
|
|
1820
|
+
' </Representation>',
|
|
1821
|
+
' </AdaptationSet>',
|
|
1822
|
+
' </Period>',
|
|
1823
|
+
'</MPD>',
|
|
1824
|
+
].join('\n');
|
|
1825
|
+
|
|
1826
|
+
fakeNetEngine.setResponseText('dummy://foo', manifestText);
|
|
1827
|
+
const config = shaka.util.PlayerConfiguration.createDefault().manifest;
|
|
1828
|
+
config.dash.manifestPreprocessor = (mpd) => {
|
|
1829
|
+
const selector = 'AdaptationSet[mimeType="text/vtt"';
|
|
1830
|
+
const vttElements = mpd.querySelectorAll(selector);
|
|
1831
|
+
for (const element of vttElements) {
|
|
1832
|
+
element.parentNode.removeChild(element);
|
|
1833
|
+
}
|
|
1834
|
+
};
|
|
1835
|
+
parser.configure(config);
|
|
1836
|
+
|
|
1837
|
+
/** @type {shaka.extern.Manifest} */
|
|
1838
|
+
const manifest = await parser.start('dummy://foo', playerInterface);
|
|
1839
|
+
const stream = manifest.textStreams[0];
|
|
1840
|
+
expect(stream).toBeUndefined();
|
|
1841
|
+
});
|
|
1842
|
+
|
|
1801
1843
|
it('converts Accessibility element to "kind"', async () => {
|
|
1802
1844
|
const manifestText = [
|
|
1803
1845
|
'<MPD minBufferTime="PT75S">',
|
|
@@ -1318,6 +1318,103 @@ describe('HlsParser', () => {
|
|
|
1318
1318
|
expect(actual.variants[0].video).toBeTruthy();
|
|
1319
1319
|
});
|
|
1320
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
|
+
|
|
1321
1418
|
it('Disable audio does not create audio streams', async () => {
|
|
1322
1419
|
const master = [
|
|
1323
1420
|
'#EXTM3U\n',
|
|
@@ -1328,6 +1425,8 @@ describe('HlsParser', () => {
|
|
|
1328
1425
|
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1,mp4a",',
|
|
1329
1426
|
'RESOLUTION=960x540,FRAME-RATE=60,AUDIO="aud1",SUBTITLES="sub1"\n',
|
|
1330
1427
|
'video\n',
|
|
1428
|
+
'#EXT-X-IMAGE-STREAM-INF:RESOLUTION=240×135,CODECS="jpeg",',
|
|
1429
|
+
'URI="image"\n',
|
|
1331
1430
|
].join('');
|
|
1332
1431
|
|
|
1333
1432
|
const video = [
|
|
@@ -1360,11 +1459,23 @@ describe('HlsParser', () => {
|
|
|
1360
1459
|
'main.vtt',
|
|
1361
1460
|
].join('');
|
|
1362
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
|
+
|
|
1363
1473
|
fakeNetEngine
|
|
1364
1474
|
.setResponseText('test:/master', master)
|
|
1365
1475
|
.setResponseText('test:/audio', audio)
|
|
1366
1476
|
.setResponseText('test:/video', video)
|
|
1367
1477
|
.setResponseText('test:/text', text)
|
|
1478
|
+
.setResponseText('test:/image', image)
|
|
1368
1479
|
.setResponseText('test:/main.vtt', vttText)
|
|
1369
1480
|
.setResponseValue('test:/init.mp4', initSegmentData)
|
|
1370
1481
|
.setResponseValue('test:/main.mp4', segmentData);
|
|
@@ -1389,6 +1500,8 @@ describe('HlsParser', () => {
|
|
|
1389
1500
|
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1,mp4a",',
|
|
1390
1501
|
'RESOLUTION=960x540,FRAME-RATE=60,AUDIO="aud1",SUBTITLES="sub1"\n',
|
|
1391
1502
|
'video\n',
|
|
1503
|
+
'#EXT-X-IMAGE-STREAM-INF:RESOLUTION=240×135,CODECS="jpeg",',
|
|
1504
|
+
'URI="image"\n',
|
|
1392
1505
|
].join('');
|
|
1393
1506
|
|
|
1394
1507
|
const video = [
|
|
@@ -1421,11 +1534,23 @@ describe('HlsParser', () => {
|
|
|
1421
1534
|
'main.vtt',
|
|
1422
1535
|
].join('');
|
|
1423
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
|
+
|
|
1424
1548
|
fakeNetEngine
|
|
1425
1549
|
.setResponseText('test:/master', master)
|
|
1426
1550
|
.setResponseText('test:/audio', audio)
|
|
1427
1551
|
.setResponseText('test:/video', video)
|
|
1428
1552
|
.setResponseText('test:/text', text)
|
|
1553
|
+
.setResponseText('test:/image', image)
|
|
1429
1554
|
.setResponseText('test:/main.vtt', vttText)
|
|
1430
1555
|
.setResponseValue('test:/init.mp4', initSegmentData)
|
|
1431
1556
|
.setResponseValue('test:/main.mp4', segmentData);
|
|
@@ -1450,6 +1575,8 @@ describe('HlsParser', () => {
|
|
|
1450
1575
|
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1,mp4a",',
|
|
1451
1576
|
'RESOLUTION=960x540,FRAME-RATE=60,AUDIO="aud1",SUBTITLES="sub1"\n',
|
|
1452
1577
|
'video\n',
|
|
1578
|
+
'#EXT-X-IMAGE-STREAM-INF:RESOLUTION=240×135,CODECS="jpeg",',
|
|
1579
|
+
'URI="image"\n',
|
|
1453
1580
|
].join('');
|
|
1454
1581
|
|
|
1455
1582
|
const video = [
|
|
@@ -1482,11 +1609,23 @@ describe('HlsParser', () => {
|
|
|
1482
1609
|
'main.vtt',
|
|
1483
1610
|
].join('');
|
|
1484
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
|
+
|
|
1485
1623
|
fakeNetEngine
|
|
1486
1624
|
.setResponseText('test:/master', master)
|
|
1487
1625
|
.setResponseText('test:/audio', audio)
|
|
1488
1626
|
.setResponseText('test:/video', video)
|
|
1489
1627
|
.setResponseText('test:/text', text)
|
|
1628
|
+
.setResponseText('test:/image', image)
|
|
1490
1629
|
.setResponseText('test:/main.vtt', vttText)
|
|
1491
1630
|
.setResponseValue('test:/init.mp4', initSegmentData)
|
|
1492
1631
|
.setResponseValue('test:/main.mp4', segmentData);
|
|
@@ -1500,6 +1639,80 @@ describe('HlsParser', () => {
|
|
|
1500
1639
|
expect(stream).toBeUndefined();
|
|
1501
1640
|
});
|
|
1502
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
|
+
|
|
1503
1716
|
it('parses manifest with MP4+TTML streams', async () => {
|
|
1504
1717
|
const master = [
|
|
1505
1718
|
'#EXTM3U\n',
|
|
@@ -1670,6 +1883,43 @@ describe('HlsParser', () => {
|
|
|
1670
1883
|
expect(actual).toEqual(manifest);
|
|
1671
1884
|
});
|
|
1672
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
|
+
|
|
1673
1923
|
it('parses video described by a media tag', async () => {
|
|
1674
1924
|
const master = [
|
|
1675
1925
|
'#EXTM3U\n',
|
|
@@ -162,7 +162,7 @@ describe('DrmEngine', () => {
|
|
|
162
162
|
document.body.removeChild(video);
|
|
163
163
|
});
|
|
164
164
|
|
|
165
|
-
function
|
|
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
|
-
|
|
178
|
-
|
|
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
|
});
|