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.
- package/.github/workflows/sync-labels.yaml +1 -1
- package/.github/workflows/update-issues.yaml +1 -1
- package/AUTHORS +5 -0
- package/CHANGELOG.md +85 -39
- package/CONTRIBUTORS +5 -0
- package/README.md +14 -0
- package/demo/common/assets.js +58 -0
- package/demo/common/message_ids.js +22 -1
- package/demo/config.js +89 -5
- package/demo/demo.less +7 -1
- package/demo/index.html +8 -1
- package/demo/locales/en.json +21 -1
- package/demo/locales/source.json +82 -2
- package/demo/main.js +57 -1
- package/demo/search.js +2 -0
- package/demo/visualizer.js +337 -0
- package/demo/visualizer_button.js +103 -0
- package/dist/controls.css +1 -1
- package/dist/controls.css.map +1 -1
- package/dist/demo.compiled.debug.js +83 -58
- package/dist/demo.compiled.debug.map +4 -4
- package/dist/demo.compiled.js +83 -58
- package/dist/demo.compiled.map +4 -4
- package/dist/demo.css +1 -1
- package/dist/demo.css.map +1 -1
- package/dist/deps.js +18 -11
- package/dist/locales.js +32 -32
- package/dist/receiver.compiled.debug.js +23 -19
- package/dist/receiver.compiled.debug.map +3 -3
- package/dist/receiver.compiled.js +23 -19
- package/dist/receiver.compiled.map +3 -3
- package/dist/shaka-player.compiled.d.ts +178 -27
- package/dist/shaka-player.compiled.debug.d.ts +178 -27
- package/dist/shaka-player.compiled.debug.externs.js +163 -32
- package/dist/shaka-player.compiled.debug.js +488 -422
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +163 -32
- package/dist/shaka-player.compiled.js +895 -857
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +178 -27
- package/dist/shaka-player.ui.debug.d.ts +178 -27
- package/dist/shaka-player.ui.debug.externs.js +163 -32
- package/dist/shaka-player.ui.debug.js +519 -450
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +163 -32
- package/dist/shaka-player.ui.js +1053 -1015
- package/dist/shaka-player.ui.map +4 -4
- package/docs/design/lcevc-architecture.png +0 -0
- package/docs/design/lcevc-demo.png +0 -0
- package/docs/design/lcevc-integration.md +72 -0
- package/docs/tutorials/fairplay.md +7 -0
- package/docs/tutorials/faq.md +1 -1
- package/docs/tutorials/ui.md +7 -7
- package/docs/tutorials/upgrade.md +6 -0
- package/externs/ima.js +13 -1
- package/externs/lcevc.js +81 -0
- package/externs/mux.js +4 -42
- package/externs/shaka/ads.js +11 -1
- package/externs/shaka/manifest_parser.js +5 -1
- package/externs/shaka/player.js +178 -15
- package/lib/abr/simple_abr_manager.js +7 -0
- package/lib/ads/ad_manager.js +0 -8
- package/lib/ads/client_side_ad.js +15 -0
- package/lib/ads/client_side_ad_manager.js +21 -8
- package/lib/ads/server_side_ad.js +15 -0
- package/lib/ads/server_side_ad_manager.js +3 -3
- package/lib/cast/cast_proxy.js +2 -1
- package/lib/cast/cast_receiver.js +19 -5
- package/lib/cast/cast_sender.js +2 -1
- package/lib/cast/cast_utils.js +18 -6
- package/lib/cea/dummy_cea_parser.js +28 -0
- package/lib/cea/i_cea_parser.js +14 -2
- package/lib/cea/mp4_cea_parser.js +87 -5
- package/lib/config/auto_show_text.js +33 -0
- package/lib/dash/dash_parser.js +1 -1
- package/lib/hls/hls_parser.js +799 -376
- package/lib/lcevc/lcevc_dil.js +152 -0
- package/lib/media/closed_caption_parser.js +9 -6
- package/lib/media/drm_engine.js +14 -6
- package/lib/media/media_source_engine.js +112 -26
- package/lib/media/playhead.js +23 -1
- package/lib/media/presentation_timeline.js +26 -11
- package/lib/media/segment_reference.js +49 -1
- package/lib/media/streaming_engine.js +145 -24
- package/lib/media/transmuxer.js +79 -18
- package/lib/offline/storage.js +1 -0
- package/lib/player.js +477 -85
- package/lib/polyfill/patchedmediakeys_apple.js +55 -1
- package/lib/text/ui_text_displayer.js +5 -5
- package/lib/text/vtt_text_parser.js +35 -1
- package/lib/util/dom_utils.js +0 -1
- package/lib/util/error.js +17 -0
- package/lib/util/fake_event.js +2 -0
- package/lib/util/id3_utils.js +300 -0
- package/lib/util/manifest_parser_utils.js +4 -0
- package/lib/util/mime_utils.js +3 -6
- package/lib/util/mp4_box_parsers.js +15 -5
- package/lib/util/platform.js +9 -1
- package/lib/util/player_configuration.js +23 -1
- package/lib/util/ts_parser.js +499 -0
- package/maintained-branches.md +2 -2
- package/package.json +4 -1
- package/roadmap.md +27 -7
- package/test/ads/ad_manager_unit.js +180 -29
- package/test/cast/cast_receiver_integration.js +97 -16
- package/test/cast/cast_receiver_unit.js +17 -12
- package/test/cast/cast_utils_unit.js +4 -1
- package/test/dash/dash_parser_content_protection_unit.js +1 -0
- package/test/dash/dash_parser_live_unit.js +1 -0
- package/test/dash/dash_parser_manifest_unit.js +93 -1
- package/test/dash/dash_parser_segment_base_unit.js +1 -0
- package/test/dash/dash_parser_segment_list_unit.js +1 -0
- package/test/dash/dash_parser_segment_template_unit.js +1 -0
- package/test/demo/demo_unit.js +2 -1
- package/test/hls/hls_live_unit.js +253 -19
- package/test/hls/hls_parser_unit.js +500 -12
- package/test/media/closed_caption_parser_unit.js +2 -1
- package/test/media/drm_engine_integration.js +3 -1
- package/test/media/media_source_engine_integration.js +102 -6
- package/test/media/media_source_engine_unit.js +10 -5
- package/test/media/playhead_unit.js +41 -0
- package/test/media/streaming_engine_integration.js +17 -1
- package/test/media/streaming_engine_unit.js +180 -24
- package/test/media/transmuxer_integration.js +21 -10
- package/test/player_integration.js +224 -82
- package/test/player_unit.js +92 -2
- package/test/polyfill/patchedmediakeys_apple_unit.js +124 -0
- package/test/test/assets/id3-metadata.aac +0 -0
- package/test/test/assets/id3-metadata.ts +0 -0
- package/test/test/assets/thumbnails-sprites.vtt +10 -0
- package/test/test/assets/thumbnails.vtt +10 -0
- package/test/test/util/dash_parser_util.js +2 -0
- package/test/test/util/fake_ad.js +21 -0
- package/test/test/util/fake_media_source_engine.js +7 -0
- package/test/test/util/id3_generator.js +100 -0
- package/test/test/util/manifest_generator.js +3 -6
- package/test/test/util/simple_fakes.js +7 -0
- package/test/test/util/stream_generator.js +38 -0
- package/test/test/util/test_scheme.js +43 -14
- package/test/text/vtt_text_parser_unit.js +11 -0
- package/test/ui/text_displayer_layout_unit.js +14 -0
- package/test/util/id3_utils_unit.js +116 -0
- package/test/util/ts_parser_unit.js +77 -0
- package/ui/controls.js +4 -1
- package/ui/less/containers.less +7 -0
- package/ui/settings_menu.js +11 -2
- package/ui/ui.js +43 -4
|
@@ -23,6 +23,8 @@ describe('HlsParser', () => {
|
|
|
23
23
|
let parser;
|
|
24
24
|
/** @type {!jasmine.Spy} */
|
|
25
25
|
let onEventSpy;
|
|
26
|
+
/** @type {!jasmine.Spy} */
|
|
27
|
+
let newDrmInfoSpy;
|
|
26
28
|
/** @type {shaka.extern.ManifestParser.PlayerInterface} */
|
|
27
29
|
let playerInterface;
|
|
28
30
|
/** @type {shaka.extern.ManifestConfiguration} */
|
|
@@ -86,6 +88,7 @@ describe('HlsParser', () => {
|
|
|
86
88
|
|
|
87
89
|
config = shaka.util.PlayerConfiguration.createDefault().manifest;
|
|
88
90
|
onEventSpy = jasmine.createSpy('onEvent');
|
|
91
|
+
newDrmInfoSpy = jasmine.createSpy('newDrmInfo');
|
|
89
92
|
playerInterface = {
|
|
90
93
|
modifyManifestRequest: (request, manifestInfo) => {},
|
|
91
94
|
modifySegmentRequest: (request, segmentInfo) => {},
|
|
@@ -99,6 +102,7 @@ describe('HlsParser', () => {
|
|
|
99
102
|
isAutoLowLatencyMode: () => false,
|
|
100
103
|
enableLowLatencyMode: () => {},
|
|
101
104
|
updateDuration: () => {},
|
|
105
|
+
newDrmInfo: shaka.test.Util.spyFunc(newDrmInfoSpy),
|
|
102
106
|
};
|
|
103
107
|
|
|
104
108
|
parser = new shaka.hls.HlsParser();
|
|
@@ -109,17 +113,18 @@ describe('HlsParser', () => {
|
|
|
109
113
|
* @param {string} master
|
|
110
114
|
* @param {string} media
|
|
111
115
|
* @param {shaka.extern.Manifest} manifest
|
|
116
|
+
* @param {string=} media2
|
|
112
117
|
* @return {!Promise.<shaka.extern.Manifest>}
|
|
113
118
|
*/
|
|
114
|
-
async function testHlsParser(master, media, manifest) {
|
|
119
|
+
async function testHlsParser(master, media, manifest, media2) {
|
|
115
120
|
fakeNetEngine
|
|
116
121
|
.setResponseText('test:/master', master)
|
|
117
122
|
.setResponseText('test:/audio', media)
|
|
118
|
-
.setResponseText('test:/audio2', media)
|
|
123
|
+
.setResponseText('test:/audio2', media2 || media)
|
|
119
124
|
.setResponseText('test:/video', media)
|
|
120
|
-
.setResponseText('test:/video2', media)
|
|
125
|
+
.setResponseText('test:/video2', media2 || media)
|
|
121
126
|
.setResponseText('test:/text', media)
|
|
122
|
-
.setResponseText('test:/text2', media)
|
|
127
|
+
.setResponseText('test:/text2', media2 || media)
|
|
123
128
|
.setResponseText('test:/main.vtt', vttText)
|
|
124
129
|
.setResponseValue('test:/init.mp4', initSegmentData)
|
|
125
130
|
.setResponseValue('test:/init2.mp4', initSegmentData)
|
|
@@ -129,10 +134,30 @@ describe('HlsParser', () => {
|
|
|
129
134
|
.setResponseValue('test:/selfInit.mp4', selfInitializingSegmentData);
|
|
130
135
|
|
|
131
136
|
const actual = await parser.start('test:/master', playerInterface);
|
|
137
|
+
await loadAllStreamsFor(actual);
|
|
132
138
|
expect(actual).toEqual(manifest);
|
|
133
139
|
return actual;
|
|
134
140
|
}
|
|
135
141
|
|
|
142
|
+
/** @param {!shaka.extern.Manifest} manifest */
|
|
143
|
+
async function loadAllStreamsFor(manifest) {
|
|
144
|
+
const promises = [];
|
|
145
|
+
for (const variant of manifest.variants) {
|
|
146
|
+
for (const stream of [variant.video, variant.audio]) {
|
|
147
|
+
if (stream) {
|
|
148
|
+
promises.push(stream.createSegmentIndex());
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
for (const text of manifest.textStreams) {
|
|
153
|
+
promises.push(text.createSegmentIndex());
|
|
154
|
+
}
|
|
155
|
+
for (const image of manifest.imageStreams) {
|
|
156
|
+
promises.push(image.createSegmentIndex());
|
|
157
|
+
}
|
|
158
|
+
await Promise.all(promises);
|
|
159
|
+
}
|
|
160
|
+
|
|
136
161
|
it('parses manifest attributes', async () => {
|
|
137
162
|
const master = [
|
|
138
163
|
'#EXTM3U\n',
|
|
@@ -205,6 +230,7 @@ describe('HlsParser', () => {
|
|
|
205
230
|
.setResponseValue('test:/main.mp4', segmentData);
|
|
206
231
|
|
|
207
232
|
const actual = await parser.start('test:/master', playerInterface);
|
|
233
|
+
await loadAllStreamsFor(actual);
|
|
208
234
|
expect(actual).toEqual(manifest);
|
|
209
235
|
});
|
|
210
236
|
|
|
@@ -280,6 +306,7 @@ describe('HlsParser', () => {
|
|
|
280
306
|
.setResponseValue('test:/main.mp4', segmentData);
|
|
281
307
|
|
|
282
308
|
const actual = await parser.start('test:/master', playerInterface);
|
|
309
|
+
await loadAllStreamsFor(actual);
|
|
283
310
|
expect(actual).toEqual(manifest);
|
|
284
311
|
});
|
|
285
312
|
|
|
@@ -321,6 +348,7 @@ describe('HlsParser', () => {
|
|
|
321
348
|
.setResponseValue('test:/main.mp4', segmentData);
|
|
322
349
|
|
|
323
350
|
const actual = await parser.start('test:/master', playerInterface);
|
|
351
|
+
await loadAllStreamsFor(actual);
|
|
324
352
|
expect(actual).toEqual(manifest);
|
|
325
353
|
});
|
|
326
354
|
|
|
@@ -572,6 +600,33 @@ describe('HlsParser', () => {
|
|
|
572
600
|
await testHlsParser(master, media, manifest);
|
|
573
601
|
});
|
|
574
602
|
|
|
603
|
+
it('accepts mp4a.40.34 codec as audio/mpeg', async () => {
|
|
604
|
+
const master = [
|
|
605
|
+
'#EXTM3U\n',
|
|
606
|
+
'#EXT-X-STREAM-INF:BANDWIDTH=63701,CODECS="mp4a.40.34"\n',
|
|
607
|
+
'audio\n',
|
|
608
|
+
].join('');
|
|
609
|
+
|
|
610
|
+
const media = [
|
|
611
|
+
'#EXTM3U\n',
|
|
612
|
+
'#EXT-X-PLAYLIST-TYPE:VOD\n',
|
|
613
|
+
'#EXTINF:5,\n',
|
|
614
|
+
'main.mp3',
|
|
615
|
+
].join('');
|
|
616
|
+
|
|
617
|
+
const manifest = shaka.test.ManifestGenerator.generate((manifest) => {
|
|
618
|
+
manifest.anyTimeline();
|
|
619
|
+
manifest.addPartialVariant((variant) => {
|
|
620
|
+
variant.addPartialStream(ContentType.AUDIO, (stream) => {
|
|
621
|
+
stream.mime('audio/mpeg', '');
|
|
622
|
+
});
|
|
623
|
+
});
|
|
624
|
+
manifest.sequenceMode = true;
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
await testHlsParser(master, media, manifest);
|
|
628
|
+
});
|
|
629
|
+
|
|
575
630
|
it('parses audio+video variant with closed captions', async () => {
|
|
576
631
|
const master = [
|
|
577
632
|
'#EXTM3U\n',
|
|
@@ -1129,6 +1184,7 @@ describe('HlsParser', () => {
|
|
|
1129
1184
|
.setResponseValue('test:/main.mp4', segmentData);
|
|
1130
1185
|
|
|
1131
1186
|
const actual = await parser.start('test:/master', playerInterface);
|
|
1187
|
+
await loadAllStreamsFor(actual);
|
|
1132
1188
|
expect(actual).toEqual(manifest);
|
|
1133
1189
|
});
|
|
1134
1190
|
|
|
@@ -1200,6 +1256,7 @@ describe('HlsParser', () => {
|
|
|
1200
1256
|
.setResponseValue('test:/main.mp4', segmentData);
|
|
1201
1257
|
|
|
1202
1258
|
const actual = await parser.start('test:/master', playerInterface);
|
|
1259
|
+
await loadAllStreamsFor(actual);
|
|
1203
1260
|
expect(actual).toEqual(manifest);
|
|
1204
1261
|
});
|
|
1205
1262
|
|
|
@@ -1263,6 +1320,7 @@ describe('HlsParser', () => {
|
|
|
1263
1320
|
.setResponseValue('test:/main.mp4', segmentData);
|
|
1264
1321
|
|
|
1265
1322
|
const actual = await parser.start('test:/master', playerInterface);
|
|
1323
|
+
await loadAllStreamsFor(actual);
|
|
1266
1324
|
expect(actual).toEqual(manifest);
|
|
1267
1325
|
});
|
|
1268
1326
|
|
|
@@ -1324,6 +1382,7 @@ describe('HlsParser', () => {
|
|
|
1324
1382
|
.setResponseValue('test:/main.mp4', segmentData);
|
|
1325
1383
|
|
|
1326
1384
|
const actual = await parser.start('test:/master', playerInterface);
|
|
1385
|
+
await loadAllStreamsFor(actual);
|
|
1327
1386
|
expect(actual).toEqual(manifest);
|
|
1328
1387
|
});
|
|
1329
1388
|
|
|
@@ -1387,6 +1446,7 @@ describe('HlsParser', () => {
|
|
|
1387
1446
|
.setResponseValue('test:/main.mp4', segmentData);
|
|
1388
1447
|
|
|
1389
1448
|
const actual = await parser.start('test:/master', playerInterface);
|
|
1449
|
+
await loadAllStreamsFor(actual);
|
|
1390
1450
|
expect(actual).toEqual(manifest);
|
|
1391
1451
|
});
|
|
1392
1452
|
|
|
@@ -1442,6 +1502,7 @@ describe('HlsParser', () => {
|
|
|
1442
1502
|
.setResponseValue('test:/main.mp4', segmentData);
|
|
1443
1503
|
|
|
1444
1504
|
const actual = await parser.start('test:/master', playerInterface);
|
|
1505
|
+
await loadAllStreamsFor(actual);
|
|
1445
1506
|
// Duration should be the minimum of the streams, but ignore the text
|
|
1446
1507
|
// stream.
|
|
1447
1508
|
const timeline = actual.presentationTimeline;
|
|
@@ -1524,6 +1585,7 @@ describe('HlsParser', () => {
|
|
|
1524
1585
|
.setResponseValue('test:/main.mp4', segmentData);
|
|
1525
1586
|
|
|
1526
1587
|
const actual = await parser.start('test:/master', playerInterface);
|
|
1588
|
+
await loadAllStreamsFor(actual);
|
|
1527
1589
|
|
|
1528
1590
|
expect(actual.imageStreams.length).toBe(1);
|
|
1529
1591
|
expect(actual.textStreams.length).toBe(1);
|
|
@@ -1596,6 +1658,7 @@ describe('HlsParser', () => {
|
|
|
1596
1658
|
.setResponseValue('test:/main.mp4', segmentData);
|
|
1597
1659
|
|
|
1598
1660
|
const actual = await parser.start('test:/master', playerInterface);
|
|
1661
|
+
await loadAllStreamsFor(actual);
|
|
1599
1662
|
|
|
1600
1663
|
expect(actual.variants.length).toBe(1);
|
|
1601
1664
|
|
|
@@ -1721,6 +1784,7 @@ describe('HlsParser', () => {
|
|
|
1721
1784
|
parser.configure(config);
|
|
1722
1785
|
|
|
1723
1786
|
const actual = await parser.start('test:/master', playerInterface);
|
|
1787
|
+
await loadAllStreamsFor(actual);
|
|
1724
1788
|
const variant = actual.variants[0];
|
|
1725
1789
|
expect(variant.audio).toBe(null);
|
|
1726
1790
|
expect(variant.video).toBeTruthy();
|
|
@@ -1796,6 +1860,7 @@ describe('HlsParser', () => {
|
|
|
1796
1860
|
parser.configure(config);
|
|
1797
1861
|
|
|
1798
1862
|
const actual = await parser.start('test:/master', playerInterface);
|
|
1863
|
+
await loadAllStreamsFor(actual);
|
|
1799
1864
|
const variant = actual.variants[0];
|
|
1800
1865
|
expect(variant.audio).toBeTruthy();
|
|
1801
1866
|
expect(variant.video).toBe(null);
|
|
@@ -1871,6 +1936,7 @@ describe('HlsParser', () => {
|
|
|
1871
1936
|
parser.configure(config);
|
|
1872
1937
|
|
|
1873
1938
|
const actual = await parser.start('test:/master', playerInterface);
|
|
1939
|
+
await loadAllStreamsFor(actual);
|
|
1874
1940
|
const stream = actual.textStreams[0];
|
|
1875
1941
|
expect(stream).toBeUndefined();
|
|
1876
1942
|
});
|
|
@@ -1945,6 +2011,7 @@ describe('HlsParser', () => {
|
|
|
1945
2011
|
parser.configure(config);
|
|
1946
2012
|
|
|
1947
2013
|
const actual = await parser.start('test:/master', playerInterface);
|
|
2014
|
+
await loadAllStreamsFor(actual);
|
|
1948
2015
|
const stream = actual.imageStreams[0];
|
|
1949
2016
|
expect(stream).toBeUndefined();
|
|
1950
2017
|
});
|
|
@@ -1989,6 +2056,7 @@ describe('HlsParser', () => {
|
|
|
1989
2056
|
.setResponseValue('test:/main.mp4', segmentData);
|
|
1990
2057
|
|
|
1991
2058
|
const actual = await parser.start('test:/master', playerInterface);
|
|
2059
|
+
await loadAllStreamsFor(actual);
|
|
1992
2060
|
expect(actual).toEqual(manifest);
|
|
1993
2061
|
});
|
|
1994
2062
|
|
|
@@ -2040,6 +2108,7 @@ describe('HlsParser', () => {
|
|
|
2040
2108
|
.setResponseValue('test:/main.mp4', segmentData);
|
|
2041
2109
|
|
|
2042
2110
|
const actual = await parser.start('test:/master', playerInterface);
|
|
2111
|
+
await loadAllStreamsFor(actual);
|
|
2043
2112
|
expect(actual).toEqual(manifest);
|
|
2044
2113
|
});
|
|
2045
2114
|
|
|
@@ -2082,6 +2151,7 @@ describe('HlsParser', () => {
|
|
|
2082
2151
|
.setResponseValue('test:/main.mp4', segmentData);
|
|
2083
2152
|
|
|
2084
2153
|
const actual = await parser.start('test:/master', playerInterface);
|
|
2154
|
+
await loadAllStreamsFor(actual);
|
|
2085
2155
|
expect(actual).toEqual(manifest);
|
|
2086
2156
|
});
|
|
2087
2157
|
|
|
@@ -2148,6 +2218,7 @@ describe('HlsParser', () => {
|
|
|
2148
2218
|
.setResponseValue('test:/main.mp4', segmentData);
|
|
2149
2219
|
|
|
2150
2220
|
const actual = await parser.start('test:/master', playerInterface);
|
|
2221
|
+
await loadAllStreamsFor(actual);
|
|
2151
2222
|
expect(actual).toEqual(manifest);
|
|
2152
2223
|
}
|
|
2153
2224
|
|
|
@@ -2293,6 +2364,7 @@ describe('HlsParser', () => {
|
|
|
2293
2364
|
|
|
2294
2365
|
config.hls.ignoreTextStreamFailures = true;
|
|
2295
2366
|
const actual = await parser.start('test:/master', playerInterface);
|
|
2367
|
+
await loadAllStreamsFor(actual);
|
|
2296
2368
|
expect(actual).toEqual(manifest);
|
|
2297
2369
|
});
|
|
2298
2370
|
|
|
@@ -2331,6 +2403,7 @@ describe('HlsParser', () => {
|
|
|
2331
2403
|
|
|
2332
2404
|
config.hls.ignoreImageStreamFailures = true;
|
|
2333
2405
|
const actual = await parser.start('test:/master', playerInterface);
|
|
2406
|
+
await loadAllStreamsFor(actual);
|
|
2334
2407
|
expect(actual).toEqual(manifest);
|
|
2335
2408
|
});
|
|
2336
2409
|
|
|
@@ -2396,6 +2469,7 @@ describe('HlsParser', () => {
|
|
|
2396
2469
|
|
|
2397
2470
|
const actual =
|
|
2398
2471
|
await parser.start('test:/host/master.m3u8', playerInterface);
|
|
2472
|
+
await loadAllStreamsFor(actual);
|
|
2399
2473
|
const video = actual.variants[0].video;
|
|
2400
2474
|
const audio = actual.variants[0].audio;
|
|
2401
2475
|
|
|
@@ -2594,6 +2668,7 @@ describe('HlsParser', () => {
|
|
|
2594
2668
|
.setResponseValue('test:/selfInit.mp4', selfInitializingSegmentData);
|
|
2595
2669
|
|
|
2596
2670
|
const actual = await parser.start('test:/master', playerInterface);
|
|
2671
|
+
await loadAllStreamsFor(actual);
|
|
2597
2672
|
expect(actual).toEqual(manifest);
|
|
2598
2673
|
});
|
|
2599
2674
|
|
|
@@ -2638,7 +2713,8 @@ describe('HlsParser', () => {
|
|
|
2638
2713
|
shaka.util.Error.Severity.CRITICAL,
|
|
2639
2714
|
shaka.util.Error.Category.MANIFEST,
|
|
2640
2715
|
shaka.util.Error.Code.NO_WEB_CRYPTO_API));
|
|
2641
|
-
await
|
|
2716
|
+
const actual = await parser.start('test:/master', playerInterface);
|
|
2717
|
+
await expectAsync(loadAllStreamsFor(actual))
|
|
2642
2718
|
.toBeRejectedWith(expectedError);
|
|
2643
2719
|
} finally {
|
|
2644
2720
|
Object.defineProperty(window, 'crypto', {
|
|
@@ -2648,7 +2724,6 @@ describe('HlsParser', () => {
|
|
|
2648
2724
|
}
|
|
2649
2725
|
});
|
|
2650
2726
|
|
|
2651
|
-
|
|
2652
2727
|
it('constructs DrmInfo for Widevine', async () => {
|
|
2653
2728
|
const master = [
|
|
2654
2729
|
'#EXTM3U\n',
|
|
@@ -2692,6 +2767,7 @@ describe('HlsParser', () => {
|
|
|
2692
2767
|
});
|
|
2693
2768
|
|
|
2694
2769
|
await testHlsParser(master, media, manifest);
|
|
2770
|
+
expect(newDrmInfoSpy).toHaveBeenCalled();
|
|
2695
2771
|
});
|
|
2696
2772
|
|
|
2697
2773
|
it('constructs DrmInfo for PlayReady', async () => {
|
|
@@ -2732,6 +2808,7 @@ describe('HlsParser', () => {
|
|
|
2732
2808
|
});
|
|
2733
2809
|
|
|
2734
2810
|
await testHlsParser(master, media, manifest);
|
|
2811
|
+
expect(newDrmInfoSpy).toHaveBeenCalled();
|
|
2735
2812
|
});
|
|
2736
2813
|
|
|
2737
2814
|
it('constructs DrmInfo for FairPlay', async () => {
|
|
@@ -2769,6 +2846,229 @@ describe('HlsParser', () => {
|
|
|
2769
2846
|
});
|
|
2770
2847
|
|
|
2771
2848
|
await testHlsParser(master, media, manifest);
|
|
2849
|
+
expect(newDrmInfoSpy).toHaveBeenCalled();
|
|
2850
|
+
});
|
|
2851
|
+
|
|
2852
|
+
it('constructs DrmInfo for ClearKey with explicit KEYFORMAT', async () => {
|
|
2853
|
+
const master = [
|
|
2854
|
+
'#EXTM3U\n',
|
|
2855
|
+
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1",',
|
|
2856
|
+
'RESOLUTION=960x540,FRAME-RATE=60\n',
|
|
2857
|
+
'video\n',
|
|
2858
|
+
].join('');
|
|
2859
|
+
|
|
2860
|
+
const media = [
|
|
2861
|
+
'#EXTM3U\n',
|
|
2862
|
+
'#EXT-X-TARGETDURATION:6\n',
|
|
2863
|
+
'#EXT-X-PLAYLIST-TYPE:VOD\n',
|
|
2864
|
+
'#EXT-X-KEY:METHOD=SAMPLE-AES-CTR,',
|
|
2865
|
+
'KEYFORMAT="identity",',
|
|
2866
|
+
'URI="key.bin",\n',
|
|
2867
|
+
'#EXT-X-MAP:URI="init.mp4"\n',
|
|
2868
|
+
'#EXTINF:5,\n',
|
|
2869
|
+
'main.mp4',
|
|
2870
|
+
].join('');
|
|
2871
|
+
|
|
2872
|
+
const manifest = shaka.test.ManifestGenerator.generate((manifest) => {
|
|
2873
|
+
manifest.anyTimeline();
|
|
2874
|
+
manifest.addPartialVariant((variant) => {
|
|
2875
|
+
variant.addPartialStream(ContentType.VIDEO, (stream) => {
|
|
2876
|
+
stream.encrypted = true;
|
|
2877
|
+
stream.addDrmInfo('org.w3.clearkey');
|
|
2878
|
+
});
|
|
2879
|
+
});
|
|
2880
|
+
manifest.sequenceMode = true;
|
|
2881
|
+
});
|
|
2882
|
+
|
|
2883
|
+
await testHlsParser(master, media, manifest);
|
|
2884
|
+
expect(newDrmInfoSpy).toHaveBeenCalled();
|
|
2885
|
+
});
|
|
2886
|
+
|
|
2887
|
+
it('constructs DrmInfo for ClearKey without explicit KEYFORMAT', async () => {
|
|
2888
|
+
const master = [
|
|
2889
|
+
'#EXTM3U\n',
|
|
2890
|
+
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1",',
|
|
2891
|
+
'RESOLUTION=960x540,FRAME-RATE=60\n',
|
|
2892
|
+
'video\n',
|
|
2893
|
+
].join('');
|
|
2894
|
+
|
|
2895
|
+
const media = [
|
|
2896
|
+
'#EXTM3U\n',
|
|
2897
|
+
'#EXT-X-TARGETDURATION:6\n',
|
|
2898
|
+
'#EXT-X-PLAYLIST-TYPE:VOD\n',
|
|
2899
|
+
'#EXT-X-KEY:METHOD=SAMPLE-AES-CTR,',
|
|
2900
|
+
'URI="key.bin",\n',
|
|
2901
|
+
'#EXT-X-MAP:URI="init.mp4"\n',
|
|
2902
|
+
'#EXTINF:5,\n',
|
|
2903
|
+
'main.mp4',
|
|
2904
|
+
].join('');
|
|
2905
|
+
|
|
2906
|
+
const manifest = shaka.test.ManifestGenerator.generate((manifest) => {
|
|
2907
|
+
manifest.anyTimeline();
|
|
2908
|
+
manifest.addPartialVariant((variant) => {
|
|
2909
|
+
variant.addPartialStream(ContentType.VIDEO, (stream) => {
|
|
2910
|
+
stream.encrypted = true;
|
|
2911
|
+
stream.addDrmInfo('org.w3.clearkey');
|
|
2912
|
+
});
|
|
2913
|
+
});
|
|
2914
|
+
manifest.sequenceMode = true;
|
|
2915
|
+
});
|
|
2916
|
+
|
|
2917
|
+
await testHlsParser(master, media, manifest);
|
|
2918
|
+
expect(newDrmInfoSpy).toHaveBeenCalled();
|
|
2919
|
+
});
|
|
2920
|
+
|
|
2921
|
+
describe('constructs DrmInfo with EXT-X-SESSION-KEY', () => {
|
|
2922
|
+
it('for Widevine', async () => {
|
|
2923
|
+
const initDataBase64 =
|
|
2924
|
+
'dGhpcyBpbml0IGRhdGEgY29udGFpbnMgaGlkZGVuIHNlY3JldHMhISE=';
|
|
2925
|
+
|
|
2926
|
+
const keyId = 'abc123';
|
|
2927
|
+
|
|
2928
|
+
const master = [
|
|
2929
|
+
'#EXTM3U\n',
|
|
2930
|
+
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1",',
|
|
2931
|
+
'RESOLUTION=960x540,FRAME-RATE=60\n',
|
|
2932
|
+
'video\n',
|
|
2933
|
+
'#EXT-X-SESSION-KEY:METHOD=SAMPLE-AES-CTR,',
|
|
2934
|
+
'KEYID=0X' + keyId + ',',
|
|
2935
|
+
'KEYFORMAT="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed",',
|
|
2936
|
+
'URI="data:text/plain;base64,',
|
|
2937
|
+
initDataBase64, '",\n',
|
|
2938
|
+
].join('');
|
|
2939
|
+
|
|
2940
|
+
const manifest = shaka.test.ManifestGenerator.generate((manifest) => {
|
|
2941
|
+
manifest.anyTimeline();
|
|
2942
|
+
manifest.addPartialVariant((variant) => {
|
|
2943
|
+
variant.addPartialStream(ContentType.VIDEO, (stream) => {
|
|
2944
|
+
stream.addDrmInfo('com.widevine.alpha', (drmInfo) => {
|
|
2945
|
+
drmInfo.addCencInitData(initDataBase64);
|
|
2946
|
+
drmInfo.keyIds.add(keyId);
|
|
2947
|
+
});
|
|
2948
|
+
});
|
|
2949
|
+
});
|
|
2950
|
+
manifest.sequenceMode = true;
|
|
2951
|
+
});
|
|
2952
|
+
|
|
2953
|
+
fakeNetEngine.setResponseText('test:/master', master);
|
|
2954
|
+
|
|
2955
|
+
const actual = await parser.start('test:/master', playerInterface);
|
|
2956
|
+
expect(actual).toEqual(manifest);
|
|
2957
|
+
});
|
|
2958
|
+
|
|
2959
|
+
it('for PlayReady', async () => {
|
|
2960
|
+
const initDataBase64 =
|
|
2961
|
+
'AAAAKXBzc2gAAAAAmgTweZhAQoarkuZb4IhflQAAAAlQbGF5cmVhZHk=';
|
|
2962
|
+
|
|
2963
|
+
const master = [
|
|
2964
|
+
'#EXTM3U\n',
|
|
2965
|
+
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1",',
|
|
2966
|
+
'RESOLUTION=960x540,FRAME-RATE=60\n',
|
|
2967
|
+
'video\n',
|
|
2968
|
+
'#EXT-X-SESSION-KEY:METHOD=SAMPLE-AES-CTR,',
|
|
2969
|
+
'KEYFORMAT="com.microsoft.playready",',
|
|
2970
|
+
'URI="data:text/plain;base64,UGxheXJlYWR5",\n',
|
|
2971
|
+
].join('');
|
|
2972
|
+
|
|
2973
|
+
const manifest = shaka.test.ManifestGenerator.generate((manifest) => {
|
|
2974
|
+
manifest.anyTimeline();
|
|
2975
|
+
manifest.addPartialVariant((variant) => {
|
|
2976
|
+
variant.addPartialStream(ContentType.VIDEO, (stream) => {
|
|
2977
|
+
stream.addDrmInfo('com.microsoft.playready', (drmInfo) => {
|
|
2978
|
+
drmInfo.addCencInitData(initDataBase64);
|
|
2979
|
+
});
|
|
2980
|
+
});
|
|
2981
|
+
});
|
|
2982
|
+
manifest.sequenceMode = true;
|
|
2983
|
+
});
|
|
2984
|
+
|
|
2985
|
+
fakeNetEngine.setResponseText('test:/master', master);
|
|
2986
|
+
|
|
2987
|
+
const actual = await parser.start('test:/master', playerInterface);
|
|
2988
|
+
expect(actual).toEqual(manifest);
|
|
2989
|
+
});
|
|
2990
|
+
|
|
2991
|
+
it('for FairPlay', async () => {
|
|
2992
|
+
const master = [
|
|
2993
|
+
'#EXTM3U\n',
|
|
2994
|
+
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1",',
|
|
2995
|
+
'RESOLUTION=960x540,FRAME-RATE=60\n',
|
|
2996
|
+
'video\n',
|
|
2997
|
+
'#EXT-X-SESSION-KEY:METHOD=SAMPLE-AES-CTR,',
|
|
2998
|
+
'KEYFORMAT="com.apple.streamingkeydelivery",',
|
|
2999
|
+
'URI="skd://f93d4e700d7ddde90529a27735d9e7cb",\n',
|
|
3000
|
+
].join('');
|
|
3001
|
+
|
|
3002
|
+
const manifest = shaka.test.ManifestGenerator.generate((manifest) => {
|
|
3003
|
+
manifest.anyTimeline();
|
|
3004
|
+
manifest.addPartialVariant((variant) => {
|
|
3005
|
+
variant.addPartialStream(ContentType.VIDEO, (stream) => {
|
|
3006
|
+
stream.addDrmInfo('com.apple.fps', (drmInfo) => {
|
|
3007
|
+
drmInfo.addInitData('sinf', new Uint8Array(0));
|
|
3008
|
+
});
|
|
3009
|
+
});
|
|
3010
|
+
});
|
|
3011
|
+
manifest.sequenceMode = true;
|
|
3012
|
+
});
|
|
3013
|
+
|
|
3014
|
+
fakeNetEngine.setResponseText('test:/master', master);
|
|
3015
|
+
|
|
3016
|
+
const actual = await parser.start('test:/master', playerInterface);
|
|
3017
|
+
expect(actual).toEqual(manifest);
|
|
3018
|
+
});
|
|
3019
|
+
|
|
3020
|
+
it('for ClearKey with explicit KEYFORMAT', async () => {
|
|
3021
|
+
const master = [
|
|
3022
|
+
'#EXTM3U\n',
|
|
3023
|
+
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1",',
|
|
3024
|
+
'RESOLUTION=960x540,FRAME-RATE=60\n',
|
|
3025
|
+
'video\n',
|
|
3026
|
+
'#EXT-X-SESSION-KEY:METHOD=SAMPLE-AES-CTR,',
|
|
3027
|
+
'KEYFORMAT="identity",',
|
|
3028
|
+
'URI="key.bin",\n',
|
|
3029
|
+
].join('');
|
|
3030
|
+
|
|
3031
|
+
const manifest = shaka.test.ManifestGenerator.generate((manifest) => {
|
|
3032
|
+
manifest.anyTimeline();
|
|
3033
|
+
manifest.addPartialVariant((variant) => {
|
|
3034
|
+
variant.addPartialStream(ContentType.VIDEO, (stream) => {
|
|
3035
|
+
stream.addDrmInfo('org.w3.clearkey');
|
|
3036
|
+
});
|
|
3037
|
+
});
|
|
3038
|
+
manifest.sequenceMode = true;
|
|
3039
|
+
});
|
|
3040
|
+
|
|
3041
|
+
fakeNetEngine.setResponseText('test:/master', master);
|
|
3042
|
+
|
|
3043
|
+
const actual = await parser.start('test:/master', playerInterface);
|
|
3044
|
+
expect(actual).toEqual(manifest);
|
|
3045
|
+
});
|
|
3046
|
+
|
|
3047
|
+
it('for ClearKey without explicit KEYFORMAT', async () => {
|
|
3048
|
+
const master = [
|
|
3049
|
+
'#EXTM3U\n',
|
|
3050
|
+
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1",',
|
|
3051
|
+
'RESOLUTION=960x540,FRAME-RATE=60\n',
|
|
3052
|
+
'video\n',
|
|
3053
|
+
'#EXT-X-SESSION-KEY:METHOD=SAMPLE-AES-CTR,',
|
|
3054
|
+
'URI="key.bin",\n',
|
|
3055
|
+
].join('');
|
|
3056
|
+
|
|
3057
|
+
const manifest = shaka.test.ManifestGenerator.generate((manifest) => {
|
|
3058
|
+
manifest.anyTimeline();
|
|
3059
|
+
manifest.addPartialVariant((variant) => {
|
|
3060
|
+
variant.addPartialStream(ContentType.VIDEO, (stream) => {
|
|
3061
|
+
stream.addDrmInfo('org.w3.clearkey');
|
|
3062
|
+
});
|
|
3063
|
+
});
|
|
3064
|
+
manifest.sequenceMode = true;
|
|
3065
|
+
});
|
|
3066
|
+
|
|
3067
|
+
fakeNetEngine.setResponseText('test:/master', master);
|
|
3068
|
+
|
|
3069
|
+
const actual = await parser.start('test:/master', playerInterface);
|
|
3070
|
+
expect(actual).toEqual(manifest);
|
|
3071
|
+
});
|
|
2772
3072
|
});
|
|
2773
3073
|
|
|
2774
3074
|
it('falls back to mp4 if HEAD request fails', async () => {
|
|
@@ -2817,8 +3117,9 @@ describe('HlsParser', () => {
|
|
|
2817
3117
|
* @param {string} master
|
|
2818
3118
|
* @param {string} media
|
|
2819
3119
|
* @param {!shaka.util.Error} error
|
|
3120
|
+
* @param {boolean=} onCreateSegmentIndex
|
|
2820
3121
|
*/
|
|
2821
|
-
async function verifyError(master, media, error) {
|
|
3122
|
+
async function verifyError(master, media, error, onCreateSegmentIndex) {
|
|
2822
3123
|
fakeNetEngine
|
|
2823
3124
|
.setResponseText('test:/master', master)
|
|
2824
3125
|
.setResponseText('test:/audio', media)
|
|
@@ -2829,8 +3130,14 @@ describe('HlsParser', () => {
|
|
|
2829
3130
|
.setResponseValue('data:text/plain;base64,AAECAwQFBgcICQoLDA0ODw==',
|
|
2830
3131
|
aes128Key);
|
|
2831
3132
|
|
|
2832
|
-
|
|
2833
|
-
|
|
3133
|
+
if (onCreateSegmentIndex) {
|
|
3134
|
+
const actual = await parser.start('test:/master', playerInterface);
|
|
3135
|
+
await expectAsync(loadAllStreamsFor(actual))
|
|
3136
|
+
.toBeRejectedWith(Util.jasmineError(error));
|
|
3137
|
+
} else {
|
|
3138
|
+
await expectAsync(parser.start('test:/master', playerInterface))
|
|
3139
|
+
.toBeRejectedWith(Util.jasmineError(error));
|
|
3140
|
+
}
|
|
2834
3141
|
}
|
|
2835
3142
|
|
|
2836
3143
|
it('if unable to guess codecs', async () => {
|
|
@@ -2967,7 +3274,7 @@ describe('HlsParser', () => {
|
|
|
2967
3274
|
shaka.util.Error.Category.MANIFEST,
|
|
2968
3275
|
Code.HLS_MSE_ENCRYPTED_MP2T_NOT_SUPPORTED);
|
|
2969
3276
|
|
|
2970
|
-
await verifyError(master, media, error);
|
|
3277
|
+
await verifyError(master, media, error, /* onCreateSegmentIndex= */ true);
|
|
2971
3278
|
});
|
|
2972
3279
|
|
|
2973
3280
|
describe('if required tags are missing', () => {
|
|
@@ -2983,7 +3290,8 @@ describe('HlsParser', () => {
|
|
|
2983
3290
|
Code.HLS_REQUIRED_TAG_MISSING,
|
|
2984
3291
|
tagName);
|
|
2985
3292
|
|
|
2986
|
-
await verifyError(
|
|
3293
|
+
await verifyError(
|
|
3294
|
+
master, media, error, /* onCreateSegmentIndex= */ true);
|
|
2987
3295
|
}
|
|
2988
3296
|
|
|
2989
3297
|
it('EXTINF', async () => {
|
|
@@ -3270,6 +3578,67 @@ describe('HlsParser', () => {
|
|
|
3270
3578
|
await testHlsParser(master, media, manifest);
|
|
3271
3579
|
});
|
|
3272
3580
|
|
|
3581
|
+
// https://github.com/shaka-project/shaka-player/issues/4308
|
|
3582
|
+
it('handles unaligned streams', async () => {
|
|
3583
|
+
const master = [
|
|
3584
|
+
'#EXTM3U\n',
|
|
3585
|
+
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1",',
|
|
3586
|
+
'RESOLUTION=1280x720,FRAME-RATE=30\n',
|
|
3587
|
+
'video\n',
|
|
3588
|
+
'#EXT-X-STREAM-INF:BANDWIDTH=400,CODECS="avc1",',
|
|
3589
|
+
'RESOLUTION=1920x1080,FRAME-RATE=30\n',
|
|
3590
|
+
'video2\n',
|
|
3591
|
+
].join('');
|
|
3592
|
+
|
|
3593
|
+
const media = [
|
|
3594
|
+
'#EXTM3U\n',
|
|
3595
|
+
'#EXT-X-PLAYLIST-TYPE:VOD\n',
|
|
3596
|
+
'#EXTINF:5\n',
|
|
3597
|
+
'main.mp4\n',
|
|
3598
|
+
'#EXTINF:5\n',
|
|
3599
|
+
'main.mp4',
|
|
3600
|
+
].join('');
|
|
3601
|
+
|
|
3602
|
+
const media2 = [
|
|
3603
|
+
'#EXTM3U\n',
|
|
3604
|
+
'#EXT-X-PLAYLIST-TYPE:VOD\n',
|
|
3605
|
+
'#EXTINF:4\n',
|
|
3606
|
+
'main.mp4\n',
|
|
3607
|
+
'#EXTINF:5\n',
|
|
3608
|
+
'main.mp4\n',
|
|
3609
|
+
'#EXTINF:1\n',
|
|
3610
|
+
'main.mp4',
|
|
3611
|
+
].join('');
|
|
3612
|
+
|
|
3613
|
+
const manifest = shaka.test.ManifestGenerator.generate((manifest) => {
|
|
3614
|
+
manifest.anyTimeline();
|
|
3615
|
+
manifest.addPartialVariant((variant) => {
|
|
3616
|
+
variant.addPartialStream(ContentType.VIDEO, (stream) => {
|
|
3617
|
+
stream.size(1280, 720);
|
|
3618
|
+
});
|
|
3619
|
+
});
|
|
3620
|
+
manifest.addPartialVariant((variant) => {
|
|
3621
|
+
variant.addPartialStream(ContentType.VIDEO, (stream) => {
|
|
3622
|
+
stream.size(1920, 1080);
|
|
3623
|
+
});
|
|
3624
|
+
});
|
|
3625
|
+
manifest.sequenceMode = true;
|
|
3626
|
+
});
|
|
3627
|
+
|
|
3628
|
+
const actual = await testHlsParser(master, media, manifest, media2);
|
|
3629
|
+
|
|
3630
|
+
const videoSegments =
|
|
3631
|
+
Array.from(actual.variants[0].video.segmentIndex || '');
|
|
3632
|
+
expect(videoSegments[0].endTime).toBe(5);
|
|
3633
|
+
expect(videoSegments[1].endTime).toBe(10);
|
|
3634
|
+
|
|
3635
|
+
const videoSegments2 =
|
|
3636
|
+
Array.from(actual.variants[1].video.segmentIndex || '');
|
|
3637
|
+
expect(videoSegments2[0].endTime).toBe(4);
|
|
3638
|
+
expect(videoSegments2[1].endTime).toBe(9);
|
|
3639
|
+
expect(videoSegments2[2].endTime).toBe(10);
|
|
3640
|
+
});
|
|
3641
|
+
|
|
3273
3642
|
// Issue #1875
|
|
3274
3643
|
it('ignores audio groups on audio-only content', async () => {
|
|
3275
3644
|
// NOTE: To reproduce the original issue accurately, the two audio playlist
|
|
@@ -3317,6 +3686,7 @@ describe('HlsParser', () => {
|
|
|
3317
3686
|
.setResponseValue('test:/main.mp4', segmentData);
|
|
3318
3687
|
|
|
3319
3688
|
const actual = await parser.start('test:/master', playerInterface);
|
|
3689
|
+
await loadAllStreamsFor(actual);
|
|
3320
3690
|
expect(actual.variants.length).toBe(1);
|
|
3321
3691
|
expect(actual).toEqual(manifest);
|
|
3322
3692
|
});
|
|
@@ -3349,6 +3719,7 @@ describe('HlsParser', () => {
|
|
|
3349
3719
|
|
|
3350
3720
|
const actual =
|
|
3351
3721
|
await parser.start('test:/host/master.m3u8', playerInterface);
|
|
3722
|
+
await loadAllStreamsFor(actual);
|
|
3352
3723
|
const video = actual.variants[0].video;
|
|
3353
3724
|
const audio = actual.variants[0].audio;
|
|
3354
3725
|
|
|
@@ -3396,6 +3767,7 @@ describe('HlsParser', () => {
|
|
|
3396
3767
|
|
|
3397
3768
|
const actual =
|
|
3398
3769
|
await parser.start('test:/host/master.m3u8', playerInterface);
|
|
3770
|
+
await loadAllStreamsFor(actual);
|
|
3399
3771
|
const video = actual.variants[0].video;
|
|
3400
3772
|
const audio = actual.variants[0].audio;
|
|
3401
3773
|
|
|
@@ -3443,6 +3815,7 @@ describe('HlsParser', () => {
|
|
|
3443
3815
|
|
|
3444
3816
|
const actual =
|
|
3445
3817
|
await parser.start('test:/host/master.m3u8', playerInterface);
|
|
3818
|
+
await loadAllStreamsFor(actual);
|
|
3446
3819
|
const video = actual.variants[0].video;
|
|
3447
3820
|
const audio = actual.variants[0].audio;
|
|
3448
3821
|
|
|
@@ -3657,7 +4030,9 @@ describe('HlsParser', () => {
|
|
|
3657
4030
|
});
|
|
3658
4031
|
});
|
|
3659
4032
|
|
|
3660
|
-
await testHlsParser(media, '', manifest);
|
|
4033
|
+
const actualManifest = await testHlsParser(media, '', manifest);
|
|
4034
|
+
|
|
4035
|
+
expect(actualManifest.presentationTimeline.getDuration()).toBe(5);
|
|
3661
4036
|
});
|
|
3662
4037
|
|
|
3663
4038
|
it('honors hls.mediaPlaylistFullMimeType', async () => {
|
|
@@ -3687,6 +4062,58 @@ describe('HlsParser', () => {
|
|
|
3687
4062
|
await testHlsParser(media, '', manifest);
|
|
3688
4063
|
});
|
|
3689
4064
|
|
|
4065
|
+
it('honors hls.mediaPlaylistFullMimeType but detects AAC', async () => {
|
|
4066
|
+
const media = [
|
|
4067
|
+
'#EXTM3U\n',
|
|
4068
|
+
'#EXT-X-PLAYLIST-TYPE:VOD\n',
|
|
4069
|
+
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
|
|
4070
|
+
'#EXTINF:5,\n',
|
|
4071
|
+
'#EXT-X-BYTERANGE:121090@616\n',
|
|
4072
|
+
'main.aac',
|
|
4073
|
+
].join('');
|
|
4074
|
+
|
|
4075
|
+
const config = shaka.util.PlayerConfiguration.createDefault().manifest;
|
|
4076
|
+
parser.configure(config);
|
|
4077
|
+
|
|
4078
|
+
const manifest = shaka.test.ManifestGenerator.generate((manifest) => {
|
|
4079
|
+
manifest.sequenceMode = true;
|
|
4080
|
+
manifest.anyTimeline();
|
|
4081
|
+
manifest.addPartialVariant((variant) => {
|
|
4082
|
+
variant.addPartialStream(ContentType.AUDIO, (stream) => {
|
|
4083
|
+
stream.mime('audio/aac');
|
|
4084
|
+
});
|
|
4085
|
+
});
|
|
4086
|
+
});
|
|
4087
|
+
|
|
4088
|
+
await testHlsParser(media, '', manifest);
|
|
4089
|
+
});
|
|
4090
|
+
|
|
4091
|
+
it('honors hls.mediaPlaylistFullMimeType but detects MPEG', async () => {
|
|
4092
|
+
const media = [
|
|
4093
|
+
'#EXTM3U\n',
|
|
4094
|
+
'#EXT-X-PLAYLIST-TYPE:VOD\n',
|
|
4095
|
+
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
|
|
4096
|
+
'#EXTINF:5,\n',
|
|
4097
|
+
'#EXT-X-BYTERANGE:121090@616\n',
|
|
4098
|
+
'main.mp3',
|
|
4099
|
+
].join('');
|
|
4100
|
+
|
|
4101
|
+
const config = shaka.util.PlayerConfiguration.createDefault().manifest;
|
|
4102
|
+
parser.configure(config);
|
|
4103
|
+
|
|
4104
|
+
const manifest = shaka.test.ManifestGenerator.generate((manifest) => {
|
|
4105
|
+
manifest.sequenceMode = true;
|
|
4106
|
+
manifest.anyTimeline();
|
|
4107
|
+
manifest.addPartialVariant((variant) => {
|
|
4108
|
+
variant.addPartialStream(ContentType.AUDIO, (stream) => {
|
|
4109
|
+
stream.mime('audio/mpeg');
|
|
4110
|
+
});
|
|
4111
|
+
});
|
|
4112
|
+
});
|
|
4113
|
+
|
|
4114
|
+
await testHlsParser(media, '', manifest);
|
|
4115
|
+
});
|
|
4116
|
+
|
|
3690
4117
|
it('syncs on sequence with ignoreManifestProgramDateTime', async () => {
|
|
3691
4118
|
const config = shaka.util.PlayerConfiguration.createDefault().manifest;
|
|
3692
4119
|
config.hls.ignoreManifestProgramDateTime = true;
|
|
@@ -3774,4 +4201,65 @@ describe('HlsParser', () => {
|
|
|
3774
4201
|
'test:/audio4.mp4',
|
|
3775
4202
|
]);
|
|
3776
4203
|
});
|
|
4204
|
+
|
|
4205
|
+
it('lazy-loads TS content without filtering it out', async () => {
|
|
4206
|
+
const master = [
|
|
4207
|
+
'#EXTM3U\n',
|
|
4208
|
+
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="eng",URI="audio"\n',
|
|
4209
|
+
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1,mp4a",',
|
|
4210
|
+
'RESOLUTION=960x540,FRAME-RATE=60,AUDIO="aud1"\n',
|
|
4211
|
+
'video\n',
|
|
4212
|
+
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1,mp4a",',
|
|
4213
|
+
'RESOLUTION=960x540,FRAME-RATE=120,AUDIO="aud1"\n',
|
|
4214
|
+
'video2\n',
|
|
4215
|
+
].join('');
|
|
4216
|
+
|
|
4217
|
+
const video = [
|
|
4218
|
+
'#EXTM3U\n',
|
|
4219
|
+
'#EXT-X-TARGETDURATION:5\n',
|
|
4220
|
+
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
|
|
4221
|
+
'#EXT-X-MEDIA-SEQUENCE:1\n',
|
|
4222
|
+
'#EXTINF:5,\n',
|
|
4223
|
+
'video1.ts\n',
|
|
4224
|
+
'#EXTINF:5,\n',
|
|
4225
|
+
'video2.ts\n',
|
|
4226
|
+
].join('');
|
|
4227
|
+
|
|
4228
|
+
const audio = [
|
|
4229
|
+
'#EXTM3U\n',
|
|
4230
|
+
'#EXT-X-TARGETDURATION:5\n',
|
|
4231
|
+
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
|
|
4232
|
+
'#EXT-X-MEDIA-SEQUENCE:3\n',
|
|
4233
|
+
'#EXTINF:5,\n',
|
|
4234
|
+
'audio1.ts\n',
|
|
4235
|
+
'#EXTINF:5,\n',
|
|
4236
|
+
'audio2.ts\n',
|
|
4237
|
+
].join('');
|
|
4238
|
+
|
|
4239
|
+
fakeNetEngine
|
|
4240
|
+
.setResponseText('test:/master', master)
|
|
4241
|
+
.setResponseText('test:/audio', audio)
|
|
4242
|
+
.setResponseText('test:/video', video)
|
|
4243
|
+
.setResponseText('test:/video2', video)
|
|
4244
|
+
.setResponseValue('test:/init.mp4', initSegmentData);
|
|
4245
|
+
|
|
4246
|
+
const actualManifest = await parser.start('test:/master', playerInterface);
|
|
4247
|
+
expect(actualManifest.variants.length).toBe(2);
|
|
4248
|
+
|
|
4249
|
+
const actualVideo0 = actualManifest.variants[0].video;
|
|
4250
|
+
const actualVideo1 = actualManifest.variants[1].video;
|
|
4251
|
+
|
|
4252
|
+
// Before loading, all MIME types agree, and are defaulted to video/mp4.
|
|
4253
|
+
expect(actualVideo0.mimeType).toBe('video/mp4');
|
|
4254
|
+
expect(actualVideo1.mimeType).toBe('video/mp4');
|
|
4255
|
+
|
|
4256
|
+
await actualVideo0.createSegmentIndex();
|
|
4257
|
+
|
|
4258
|
+
// After loading just ONE stream, all MIME types agree again, and have been
|
|
4259
|
+
// updated to reflect the TS content found inside the loaded playlist.
|
|
4260
|
+
// This is how we avoid having the unloaded tracks filtered out during
|
|
4261
|
+
// startup.
|
|
4262
|
+
expect(actualVideo0.mimeType).toBe('video/mp2t');
|
|
4263
|
+
expect(actualVideo1.mimeType).toBe('video/mp2t');
|
|
4264
|
+
});
|
|
3777
4265
|
});
|