shaka-player 4.2.1 → 4.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/.github/workflows/build-and-test.yaml +42 -22
- package/.github/workflows/compute-incremental-coverage.py +14 -3
- package/.github/workflows/custom-actions/set-commit-status/action.yaml +1 -5
- package/.github/workflows/deflake.yaml +28 -0
- package/.github/workflows/demo-version-index.yaml +1 -1
- package/.github/workflows/nightly-demo.yaml +1 -1
- package/.github/workflows/release-please.yaml +5 -5
- package/.github/workflows/report-incremental-coverage.yaml +1 -1
- package/.github/workflows/selenium-lab-tests.yaml +21 -10
- package/AUTHORS +4 -0
- package/CHANGELOG.md +36 -0
- package/CONTRIBUTORS +4 -0
- package/demo/common/message_ids.js +1 -0
- package/demo/config.js +3 -1
- package/demo/locales/en.json +1 -0
- package/demo/locales/source.json +4 -0
- package/dist/demo.compiled.debug.js +7 -6
- package/dist/demo.compiled.debug.map +3 -3
- package/dist/demo.compiled.js +7 -6
- package/dist/demo.compiled.map +3 -3
- package/dist/deps.js +4 -4
- package/dist/locales.js +32 -32
- package/dist/receiver.compiled.debug.js +4 -3
- package/dist/receiver.compiled.debug.map +3 -3
- package/dist/receiver.compiled.js +4 -3
- package/dist/receiver.compiled.map +3 -3
- package/dist/shaka-player.compiled.d.ts +3 -2
- package/dist/shaka-player.compiled.debug.d.ts +3 -2
- package/dist/shaka-player.compiled.debug.externs.js +25 -24
- package/dist/shaka-player.compiled.debug.js +268 -259
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +25 -24
- package/dist/shaka-player.compiled.js +749 -746
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +3 -2
- package/dist/shaka-player.ui.debug.d.ts +3 -2
- package/dist/shaka-player.ui.debug.externs.js +25 -24
- package/dist/shaka-player.ui.debug.js +243 -234
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +25 -24
- package/dist/shaka-player.ui.js +908 -904
- package/dist/shaka-player.ui.map +4 -4
- package/docs/jsdoc-template/publish.js +33 -0
- package/docs/jsdoc-template/tmpl/container.tmpl +4 -1
- package/docs/jsdoc.conf.json +1 -0
- package/docs/tutorials/welcome.md +2 -2
- package/externs/shaka/manifest_parser.js +4 -1
- package/externs/shaka/player.js +7 -1
- package/lib/ads/client_side_ad_manager.js +12 -2
- package/lib/cast/cast_sender.js +39 -14
- package/lib/cea/cea608_memory.js +4 -0
- package/lib/hls/hls_parser.js +48 -15
- package/lib/media/drm_engine.js +59 -7
- package/lib/media/media_source_engine.js +43 -15
- package/lib/media/playhead.js +1 -1
- package/lib/media/segment_index.js +4 -1
- package/lib/media/streaming_engine.js +79 -14
- package/lib/offline/indexeddb/storage_mechanism.js +24 -0
- package/lib/offline/storage.js +1 -0
- package/lib/player.js +17 -5
- package/lib/polyfill/media_capabilities.js +7 -6
- package/lib/text/ttml_text_parser.js +69 -87
- package/lib/text/vtt_text_parser.js +1 -1
- package/lib/util/error.js +7 -0
- package/lib/util/periods.js +9 -9
- package/lib/util/platform.js +19 -5
- package/lib/util/player_configuration.js +4 -0
- package/lib/util/pssh.js +2 -1
- package/lib/util/stream_utils.js +2 -1
- package/lib/util/xml_utils.js +45 -6
- package/package.json +1 -1
- package/test/cea/cea608_memory_unit.js +88 -0
- 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 +1 -0
- 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/hls/hls_live_unit.js +169 -247
- package/test/hls/hls_parser_unit.js +60 -2
- package/test/media/drm_engine_unit.js +61 -0
- package/test/media/media_source_engine_integration.js +36 -0
- package/test/media/media_source_engine_unit.js +118 -1
- package/test/media/segment_index_unit.js +28 -2
- package/test/media/streaming_engine_integration.js +9 -7
- package/test/media/streaming_engine_unit.js +29 -0
- package/test/offline/offline_integration.js +3 -3
- package/test/offline/storage_integration.js +30 -0
- package/test/player_integration.js +2 -2
- package/test/test/util/dash_parser_util.js +2 -0
- package/test/test/util/ttml_utils.js +0 -2
- package/test/text/ttml_text_parser_unit.js +46 -0
- package/test/text/vtt_text_parser_unit.js +23 -1
- package/test/ui/text_displayer_layout_unit.js +17 -2
- package/test/util/periods_unit.js +149 -11
- package/test/util/xml_utils_unit.js +64 -0
- package/ui/controls.js +0 -7
|
@@ -77,6 +77,7 @@ describe('HlsParser live', () => {
|
|
|
77
77
|
isLowLatencyMode: () => false,
|
|
78
78
|
isAutoLowLatencyMode: () => false,
|
|
79
79
|
enableLowLatencyMode: () => {},
|
|
80
|
+
updateDuration: () => {},
|
|
80
81
|
};
|
|
81
82
|
|
|
82
83
|
parser = new shaka.hls.HlsParser();
|
|
@@ -100,13 +101,11 @@ describe('HlsParser live', () => {
|
|
|
100
101
|
/**
|
|
101
102
|
* @param {string} master
|
|
102
103
|
* @param {string} initialMedia
|
|
103
|
-
* @param {
|
|
104
|
-
* @
|
|
105
|
-
* @param {!Array} updatedReferences
|
|
104
|
+
* @param {Array=} initialReferences
|
|
105
|
+
* @return {!Promise.<shaka.extern.Manifest>}
|
|
106
106
|
*/
|
|
107
|
-
async function
|
|
108
|
-
master, initialMedia, initialReferences
|
|
109
|
-
updatedReferences) {
|
|
107
|
+
async function testInitialManifest(
|
|
108
|
+
master, initialMedia, initialReferences=null) {
|
|
110
109
|
fakeNetEngine
|
|
111
110
|
.setResponseText('test:/master', master)
|
|
112
111
|
.setResponseText('test:/video', initialMedia)
|
|
@@ -118,32 +117,52 @@ describe('HlsParser live', () => {
|
|
|
118
117
|
.setResponseValue('test:/main2.mp4', segmentData)
|
|
119
118
|
.setResponseValue('test:/main3.mp4', segmentData)
|
|
120
119
|
.setResponseValue('test:/main4.mp4', segmentData)
|
|
120
|
+
.setResponseValue('test:/partial.mp4', segmentData)
|
|
121
|
+
.setResponseValue('test:/partial2.mp4', segmentData)
|
|
121
122
|
.setResponseValue('test:/selfInit.mp4', selfInitializingSegmentData);
|
|
122
123
|
|
|
123
124
|
const manifest = await parser.start('test:/master', playerInterface);
|
|
124
125
|
|
|
125
|
-
|
|
126
|
-
await
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
126
|
+
if (initialReferences) {
|
|
127
|
+
await Promise.all(manifest.variants.map(async (variant) => {
|
|
128
|
+
await variant.video.createSegmentIndex();
|
|
129
|
+
|
|
130
|
+
// The compiler doesn't count null checks done outside this callback,
|
|
131
|
+
// so we need an assertion here.
|
|
132
|
+
goog.asserts.assert(initialReferences != null, 'references non-null');
|
|
133
|
+
ManifestParser.verifySegmentIndex(variant.video, initialReferences);
|
|
134
|
+
|
|
135
|
+
if (variant.audio) {
|
|
136
|
+
await variant.audio.createSegmentIndex();
|
|
137
|
+
ManifestParser.verifySegmentIndex(variant.audio, initialReferences);
|
|
138
|
+
}
|
|
139
|
+
}));
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return manifest;
|
|
143
|
+
}
|
|
133
144
|
|
|
145
|
+
/**
|
|
146
|
+
* @param {shaka.extern.Manifest} manifest
|
|
147
|
+
* @param {string} updatedMedia
|
|
148
|
+
* @param {Array=} updatedReferences
|
|
149
|
+
*/
|
|
150
|
+
async function testUpdate(manifest, updatedMedia, updatedReferences=null) {
|
|
134
151
|
// Replace the entries with the updated values.
|
|
135
152
|
fakeNetEngine
|
|
136
153
|
.setResponseText('test:/video', updatedMedia)
|
|
137
154
|
.setResponseText('test:/redirected/video', updatedMedia)
|
|
138
155
|
.setResponseText('test:/video2', updatedMedia)
|
|
139
|
-
.setResponseText('test:/audio', updatedMedia)
|
|
140
|
-
.setResponseText('test:/video?_HLS_skip=YES', updatedMedia);
|
|
156
|
+
.setResponseText('test:/audio', updatedMedia);
|
|
141
157
|
|
|
142
158
|
await delayForUpdatePeriod();
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
ManifestParser.verifySegmentIndex(variant.
|
|
159
|
+
|
|
160
|
+
if (updatedReferences) {
|
|
161
|
+
for (const variant of manifest.variants) {
|
|
162
|
+
ManifestParser.verifySegmentIndex(variant.video, updatedReferences);
|
|
163
|
+
if (variant.audio) {
|
|
164
|
+
ManifestParser.verifySegmentIndex(variant.audio, updatedReferences);
|
|
165
|
+
}
|
|
147
166
|
}
|
|
148
167
|
}
|
|
149
168
|
}
|
|
@@ -170,13 +189,8 @@ describe('HlsParser live', () => {
|
|
|
170
189
|
].join('');
|
|
171
190
|
|
|
172
191
|
it('treats already ended presentation like VOD', async () => {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
.setResponseText('test:/video', media + '#EXT-X-ENDLIST')
|
|
176
|
-
.setResponseValue('test:/init.mp4', initSegmentData)
|
|
177
|
-
.setResponseValue('test:/main.mp4', segmentData);
|
|
178
|
-
|
|
179
|
-
const manifest = await parser.start('test:/master', playerInterface);
|
|
192
|
+
const manifest = await testInitialManifest(
|
|
193
|
+
master, media + '#EXT-X-ENDLIST');
|
|
180
194
|
expect(manifest.presentationTimeline.isLive()).toBe(false);
|
|
181
195
|
expect(manifest.presentationTimeline.isInProgress()).toBe(false);
|
|
182
196
|
});
|
|
@@ -188,8 +202,8 @@ describe('HlsParser live', () => {
|
|
|
188
202
|
const ref2 = makeReference(
|
|
189
203
|
'test:/main2.mp4', 2, 4, /* syncTime= */ null);
|
|
190
204
|
|
|
191
|
-
await
|
|
192
|
-
|
|
205
|
+
const manifest = await testInitialManifest(master, media, [ref1]);
|
|
206
|
+
await testUpdate(manifest, mediaWithAdditionalSegment, [ref1, ref2]);
|
|
193
207
|
});
|
|
194
208
|
|
|
195
209
|
it('updates all variants', async () => {
|
|
@@ -205,9 +219,9 @@ describe('HlsParser live', () => {
|
|
|
205
219
|
const ref2 = makeReference(
|
|
206
220
|
'test:/main2.mp4', 2, 4, /* syncTime= */ null);
|
|
207
221
|
|
|
208
|
-
await
|
|
209
|
-
masterWithTwoVariants, media, [ref1]
|
|
210
|
-
|
|
222
|
+
const manifest = await testInitialManifest(
|
|
223
|
+
masterWithTwoVariants, media, [ref1]);
|
|
224
|
+
await testUpdate(manifest, mediaWithAdditionalSegment, [ref1, ref2]);
|
|
211
225
|
});
|
|
212
226
|
|
|
213
227
|
it('updates all streams', async () => {
|
|
@@ -228,9 +242,9 @@ describe('HlsParser live', () => {
|
|
|
228
242
|
const ref2 = makeReference(
|
|
229
243
|
'test:/main2.mp4', 2, 4, /* syncTime= */ null);
|
|
230
244
|
|
|
231
|
-
await
|
|
232
|
-
masterWithAudio, media, [ref1]
|
|
233
|
-
|
|
245
|
+
const manifest = await testInitialManifest(
|
|
246
|
+
masterWithAudio, media, [ref1]);
|
|
247
|
+
await testUpdate(manifest, mediaWithAdditionalSegment, [ref1, ref2]);
|
|
234
248
|
});
|
|
235
249
|
|
|
236
250
|
it('handles multiple updates', async () => {
|
|
@@ -253,72 +267,28 @@ describe('HlsParser live', () => {
|
|
|
253
267
|
const ref3 = makeReference(
|
|
254
268
|
'test:/main3.mp4', 4, 6, /* syncTime= */ null);
|
|
255
269
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
.setResponseValue('test:/init.mp4', initSegmentData)
|
|
260
|
-
.setResponseValue('test:/main.mp4', segmentData);
|
|
261
|
-
|
|
262
|
-
const manifest = await parser.start('test:/master', playerInterface);
|
|
263
|
-
|
|
264
|
-
const video = manifest.variants[0].video;
|
|
265
|
-
await video.createSegmentIndex();
|
|
266
|
-
ManifestParser.verifySegmentIndex(video, [ref1]);
|
|
267
|
-
|
|
268
|
-
fakeNetEngine
|
|
269
|
-
.setResponseText('test:/master', master)
|
|
270
|
-
.setResponseText('test:/video', updatedMedia1);
|
|
271
|
-
|
|
272
|
-
await delayForUpdatePeriod();
|
|
273
|
-
ManifestParser.verifySegmentIndex(video, [ref1, ref2]);
|
|
274
|
-
|
|
275
|
-
fakeNetEngine
|
|
276
|
-
.setResponseText('test:/master', master)
|
|
277
|
-
.setResponseText('test:/video', updatedMedia2);
|
|
278
|
-
|
|
279
|
-
await delayForUpdatePeriod();
|
|
280
|
-
ManifestParser.verifySegmentIndex(video, [ref1, ref2, ref3]);
|
|
270
|
+
const manifest = await testInitialManifest(master, media, [ref1]);
|
|
271
|
+
await testUpdate(manifest, updatedMedia1, [ref1, ref2]);
|
|
272
|
+
await testUpdate(manifest, updatedMedia2, [ref1, ref2, ref3]);
|
|
281
273
|
});
|
|
282
274
|
|
|
283
275
|
it('converts presentation to VOD when it is finished', async () => {
|
|
284
|
-
|
|
285
|
-
.setResponseText('test:/master', master)
|
|
286
|
-
.setResponseText('test:/video', media)
|
|
287
|
-
.setResponseValue('test:/init.mp4', initSegmentData)
|
|
288
|
-
.setResponseValue('test:/main.mp4', segmentData);
|
|
289
|
-
|
|
290
|
-
const manifest = await parser.start('test:/master', playerInterface);
|
|
291
|
-
|
|
276
|
+
const manifest = await testInitialManifest(master, media);
|
|
292
277
|
expect(manifest.presentationTimeline.isLive()).toBe(true);
|
|
293
|
-
fakeNetEngine
|
|
294
|
-
.setResponseText('test:/master', master)
|
|
295
|
-
.setResponseText('test:/video',
|
|
296
|
-
mediaWithAdditionalSegment + '#EXT-X-ENDLIST\n');
|
|
297
278
|
|
|
298
|
-
await
|
|
279
|
+
await testUpdate(
|
|
280
|
+
manifest, mediaWithAdditionalSegment + '#EXT-X-ENDLIST\n');
|
|
299
281
|
expect(manifest.presentationTimeline.isLive()).toBe(false);
|
|
300
282
|
});
|
|
301
283
|
|
|
302
284
|
it('starts presentation as VOD when ENDLIST is present', async () => {
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
.setResponseText('test:/video', media + '#EXT-X-ENDLIST')
|
|
306
|
-
.setResponseValue('test:/init.mp4', initSegmentData)
|
|
307
|
-
.setResponseValue('test:/main.mp4', segmentData);
|
|
308
|
-
|
|
309
|
-
const manifest = await parser.start('test:/master', playerInterface);
|
|
285
|
+
const manifest = await testInitialManifest(
|
|
286
|
+
master, media + '#EXT-X-ENDLIST');
|
|
310
287
|
expect(manifest.presentationTimeline.isLive()).toBe(false);
|
|
311
288
|
});
|
|
312
289
|
|
|
313
290
|
it('does not throw when interrupted by stop', async () => {
|
|
314
|
-
|
|
315
|
-
.setResponseText('test:/master', master)
|
|
316
|
-
.setResponseText('test:/video', media)
|
|
317
|
-
.setResponseValue('test:/init.mp4', initSegmentData)
|
|
318
|
-
.setResponseValue('test:/main.mp4', segmentData);
|
|
319
|
-
|
|
320
|
-
const manifest = await parser.start('test:/master', playerInterface);
|
|
321
|
-
|
|
291
|
+
const manifest = await testInitialManifest(master, media);
|
|
322
292
|
expect(manifest.presentationTimeline.isLive()).toBe(true);
|
|
323
293
|
|
|
324
294
|
// Block the next request so that update() is still happening when we
|
|
@@ -336,6 +306,74 @@ describe('HlsParser live', () => {
|
|
|
336
306
|
// Wait for stop to complete.
|
|
337
307
|
await stopPromise;
|
|
338
308
|
});
|
|
309
|
+
|
|
310
|
+
it('calls notifySegments on each update', async () => {
|
|
311
|
+
const manifest = await testInitialManifest(master, media);
|
|
312
|
+
const notifySegmentsSpy = spyOn(
|
|
313
|
+
manifest.presentationTimeline, 'notifySegments').and.callThrough();
|
|
314
|
+
|
|
315
|
+
// Trigger an update.
|
|
316
|
+
await delayForUpdatePeriod();
|
|
317
|
+
|
|
318
|
+
expect(notifySegmentsSpy).toHaveBeenCalled();
|
|
319
|
+
notifySegmentsSpy.calls.reset();
|
|
320
|
+
|
|
321
|
+
// Trigger another update.
|
|
322
|
+
await delayForUpdatePeriod();
|
|
323
|
+
|
|
324
|
+
expect(notifySegmentsSpy).toHaveBeenCalled();
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
it('converts to VOD only after all playlists end', async () => {
|
|
328
|
+
const master = [
|
|
329
|
+
'#EXTM3U\n',
|
|
330
|
+
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="eng",',
|
|
331
|
+
'URI="audio"\n',
|
|
332
|
+
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1",AUDIO="aud1",',
|
|
333
|
+
'RESOLUTION=960x540,FRAME-RATE=60\n',
|
|
334
|
+
'video\n',
|
|
335
|
+
].join('');
|
|
336
|
+
|
|
337
|
+
const mediaWithEndList = media + '#EXT-X-ENDLIST';
|
|
338
|
+
|
|
339
|
+
const manifest = await testInitialManifest(master, media);
|
|
340
|
+
expect(manifest.presentationTimeline.isLive()).toBe(true);
|
|
341
|
+
|
|
342
|
+
// Update video only.
|
|
343
|
+
fakeNetEngine.setResponseText('test:/video', mediaWithEndList);
|
|
344
|
+
await delayForUpdatePeriod();
|
|
345
|
+
|
|
346
|
+
// Audio hasn't "ended" yet, so we're still live.
|
|
347
|
+
expect(manifest.presentationTimeline.isLive()).toBe(true);
|
|
348
|
+
|
|
349
|
+
// Update audio.
|
|
350
|
+
fakeNetEngine.setResponseText('test:/audio', mediaWithEndList);
|
|
351
|
+
await delayForUpdatePeriod();
|
|
352
|
+
|
|
353
|
+
// Now both have "ended", so we're no longer live.
|
|
354
|
+
expect(manifest.presentationTimeline.isLive()).toBe(false);
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
it('stops updating after all playlists end', async () => {
|
|
358
|
+
const manifest = await testInitialManifest(master, media);
|
|
359
|
+
expect(manifest.presentationTimeline.isLive()).toBe(true);
|
|
360
|
+
|
|
361
|
+
fakeNetEngine.request.calls.reset();
|
|
362
|
+
await testUpdate(
|
|
363
|
+
manifest, mediaWithAdditionalSegment + '#EXT-X-ENDLIST\n');
|
|
364
|
+
|
|
365
|
+
// We saw one request for the video playlist, which signalled "ENDLIST".
|
|
366
|
+
fakeNetEngine.expectRequest(
|
|
367
|
+
'test:/video',
|
|
368
|
+
shaka.net.NetworkingEngine.RequestType.MANIFEST);
|
|
369
|
+
expect(manifest.presentationTimeline.isLive()).toBe(false);
|
|
370
|
+
|
|
371
|
+
fakeNetEngine.request.calls.reset();
|
|
372
|
+
await delayForUpdatePeriod();
|
|
373
|
+
|
|
374
|
+
// No new updates were requested.
|
|
375
|
+
expect(fakeNetEngine.request).not.toHaveBeenCalled();
|
|
376
|
+
});
|
|
339
377
|
}); // describe('update')
|
|
340
378
|
}); // describe('playlist type EVENT')
|
|
341
379
|
|
|
@@ -413,47 +451,27 @@ describe('HlsParser live', () => {
|
|
|
413
451
|
].join('');
|
|
414
452
|
|
|
415
453
|
it('starts presentation as VOD when ENDLIST is present', async () => {
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
.setResponseText('test:/video', media + '#EXT-X-ENDLIST')
|
|
419
|
-
.setResponseValue('test:/init.mp4', initSegmentData)
|
|
420
|
-
.setResponseValue('test:/main.mp4', segmentData);
|
|
421
|
-
|
|
422
|
-
const manifest = await parser.start('test:/master', playerInterface);
|
|
454
|
+
const manifest = await testInitialManifest(
|
|
455
|
+
master, media + '#EXT-X-ENDLIST');
|
|
423
456
|
expect(manifest.presentationTimeline.isLive()).toBe(false);
|
|
424
457
|
});
|
|
425
458
|
|
|
426
459
|
it('does not fail on a missing sequence number', async () => {
|
|
427
|
-
|
|
428
|
-
.setResponseText('test:/master', master)
|
|
429
|
-
.setResponseText('test:/video', mediaWithoutSequenceNumber)
|
|
430
|
-
.setResponseValue('test:/init.mp4', initSegmentData)
|
|
431
|
-
.setResponseValue('test:/main.mp4', segmentData);
|
|
432
|
-
|
|
433
|
-
await parser.start('test:/master', playerInterface);
|
|
460
|
+
await testInitialManifest(master, mediaWithoutSequenceNumber);
|
|
434
461
|
});
|
|
435
462
|
|
|
436
463
|
it('sets presentation delay as configured', async () => {
|
|
437
|
-
fakeNetEngine
|
|
438
|
-
.setResponseText('test:/master', master)
|
|
439
|
-
.setResponseText('test:/video', media)
|
|
440
|
-
.setResponseValue('test:/init.mp4', initSegmentData)
|
|
441
|
-
.setResponseValue('test:/main.mp4', segmentData);
|
|
442
464
|
config.defaultPresentationDelay = 10;
|
|
443
465
|
parser.configure(config);
|
|
444
|
-
|
|
466
|
+
|
|
467
|
+
const manifest = await testInitialManifest(master, media);
|
|
445
468
|
expect(manifest.presentationTimeline.getDelay()).toBe(
|
|
446
469
|
config.defaultPresentationDelay);
|
|
447
470
|
});
|
|
448
471
|
|
|
449
472
|
it('sets 3 times target duration as presentation delay if not configured',
|
|
450
473
|
async () => {
|
|
451
|
-
|
|
452
|
-
.setResponseText('test:/master', master)
|
|
453
|
-
.setResponseText('test:/video', media)
|
|
454
|
-
.setResponseValue('test:/init.mp4', initSegmentData)
|
|
455
|
-
.setResponseValue('test:/main.mp4', segmentData);
|
|
456
|
-
const manifest = await parser.start('test:/master', playerInterface);
|
|
474
|
+
const manifest = await testInitialManifest(master, media);
|
|
457
475
|
expect(manifest.presentationTimeline.getDelay()).toBe(15);
|
|
458
476
|
});
|
|
459
477
|
|
|
@@ -469,15 +487,9 @@ describe('HlsParser live', () => {
|
|
|
469
487
|
'main.mp4\n',
|
|
470
488
|
].join('');
|
|
471
489
|
|
|
472
|
-
fakeNetEngine
|
|
473
|
-
.setResponseText('test:/master', master)
|
|
474
|
-
.setResponseText('test:/video', mediaWithLowLatency)
|
|
475
|
-
.setResponseValue('test:/init.mp4', initSegmentData)
|
|
476
|
-
.setResponseValue('test:/main.mp4', segmentData);
|
|
477
|
-
|
|
478
490
|
playerInterface.isLowLatencyMode = () => true;
|
|
479
491
|
|
|
480
|
-
const manifest = await
|
|
492
|
+
const manifest = await testInitialManifest(master, mediaWithLowLatency);
|
|
481
493
|
// Presentation delay should be the value of 'PART-HOLD-BACK' if not
|
|
482
494
|
// configured.
|
|
483
495
|
expect(manifest.presentationTimeline.getDelay()).toBe(1.8);
|
|
@@ -485,13 +497,8 @@ describe('HlsParser live', () => {
|
|
|
485
497
|
|
|
486
498
|
describe('availabilityWindowOverride', () => {
|
|
487
499
|
async function testWindowOverride(expectedWindow) {
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
.setResponseText('test:/video', mediaWithManySegments)
|
|
491
|
-
.setResponseValue('test:/init.mp4', initSegmentData)
|
|
492
|
-
.setResponseValue('test:/main.mp4', segmentData);
|
|
493
|
-
|
|
494
|
-
const manifest = await parser.start('test:/master', playerInterface);
|
|
500
|
+
const manifest = await testInitialManifest(
|
|
501
|
+
master, mediaWithManySegments);
|
|
495
502
|
expect(manifest).toBeTruthy();
|
|
496
503
|
const timeline = manifest.presentationTimeline;
|
|
497
504
|
expect(timeline).toBeTruthy();
|
|
@@ -514,13 +521,6 @@ describe('HlsParser live', () => {
|
|
|
514
521
|
});
|
|
515
522
|
|
|
516
523
|
it('sets timestamp offset for segments with discontinuity', async () => {
|
|
517
|
-
fakeNetEngine
|
|
518
|
-
.setResponseText('test:/master', master)
|
|
519
|
-
.setResponseText('test:/video', mediaWithDiscontinuity)
|
|
520
|
-
.setResponseValue('test:/init.mp4', initSegmentData)
|
|
521
|
-
.setResponseValue('test:/main.mp4', segmentData)
|
|
522
|
-
.setResponseValue('test:/main2.mp4', segmentData);
|
|
523
|
-
|
|
524
524
|
const ref1 = makeReference(
|
|
525
525
|
'test:/main.mp4', 0, 2, /* syncTime= */ null,
|
|
526
526
|
/* baseUri= */ '', /* startByte= */ 0, /* endByte= */ null,
|
|
@@ -533,10 +533,7 @@ describe('HlsParser live', () => {
|
|
|
533
533
|
/* baseUri= */ '', /* startByte= */ 0, /* endByte= */ null,
|
|
534
534
|
/* timestampOffset= */ 0);
|
|
535
535
|
|
|
536
|
-
|
|
537
|
-
const video = manifest.variants[0].video;
|
|
538
|
-
await video.createSegmentIndex();
|
|
539
|
-
ManifestParser.verifySegmentIndex(video, [ref1, ref2]);
|
|
536
|
+
await testInitialManifest(master, mediaWithDiscontinuity, [ref1, ref2]);
|
|
540
537
|
});
|
|
541
538
|
|
|
542
539
|
// Test for https://github.com/shaka-project/shaka-player/issues/4223
|
|
@@ -562,14 +559,6 @@ describe('HlsParser live', () => {
|
|
|
562
559
|
'#EXT-X-PRELOAD-HINT:TYPE=PART,URI="partial.mp4",BYTERANGE-START=210\n',
|
|
563
560
|
].join('');
|
|
564
561
|
|
|
565
|
-
fakeNetEngine
|
|
566
|
-
.setResponseText('test:/master', master)
|
|
567
|
-
.setResponseText('test:/video', mediaWithPartialSegments)
|
|
568
|
-
.setResponseValue('test:/init.mp4', initSegmentData)
|
|
569
|
-
.setResponseValue('test:/main.mp4', segmentData)
|
|
570
|
-
.setResponseValue('test:/partial.mp4', segmentData)
|
|
571
|
-
.setResponseValue('test:/partial2.mp4', segmentData);
|
|
572
|
-
|
|
573
562
|
const partialRef = makeReference(
|
|
574
563
|
'test:/partial.mp4', 0, 2, /* syncTime= */ null,
|
|
575
564
|
/* baseUri= */ '', /* startByte= */ 0, /* endByte= */ 199);
|
|
@@ -597,10 +586,7 @@ describe('HlsParser live', () => {
|
|
|
597
586
|
/* baseUri= */ '', /* startByte= */ 0, /* endByte= */ null,
|
|
598
587
|
/* timestampOffset= */ 0, [partialRef3, preloadRef]);
|
|
599
588
|
|
|
600
|
-
|
|
601
|
-
const video = manifest.variants[0].video;
|
|
602
|
-
await video.createSegmentIndex();
|
|
603
|
-
ManifestParser.verifySegmentIndex(video, [ref, ref2]);
|
|
589
|
+
await testInitialManifest(master, mediaWithPartialSegments, [ref, ref2]);
|
|
604
590
|
});
|
|
605
591
|
|
|
606
592
|
// Test for https://github.com/shaka-project/shaka-player/issues/4223
|
|
@@ -623,14 +609,6 @@ describe('HlsParser live', () => {
|
|
|
623
609
|
'#EXT-X-PRELOAD-HINT:TYPE=PART,URI="partial.mp4",BYTERANGE-START=210\n',
|
|
624
610
|
].join('');
|
|
625
611
|
|
|
626
|
-
fakeNetEngine
|
|
627
|
-
.setResponseText('test:/master', master)
|
|
628
|
-
.setResponseText('test:/video', mediaWithPartialSegments)
|
|
629
|
-
.setResponseValue('test:/init.mp4', initSegmentData)
|
|
630
|
-
.setResponseValue('test:/main.mp4', segmentData)
|
|
631
|
-
.setResponseValue('test:/partial.mp4', segmentData)
|
|
632
|
-
.setResponseValue('test:/partial2.mp4', segmentData);
|
|
633
|
-
|
|
634
612
|
const ref = makeReference(
|
|
635
613
|
'test:/main.mp4', 0, 4, /* syncTime= */ null,
|
|
636
614
|
/* baseUri= */ '', /* startByte= */ 0, /* endByte= */ null,
|
|
@@ -646,10 +624,7 @@ describe('HlsParser live', () => {
|
|
|
646
624
|
/* baseUri= */ '', /* startByte= */ 0, /* endByte= */ 209,
|
|
647
625
|
/* timestampOffset= */ 0, [partialRef]);
|
|
648
626
|
|
|
649
|
-
|
|
650
|
-
const video = manifest.variants[0].video;
|
|
651
|
-
await video.createSegmentIndex();
|
|
652
|
-
ManifestParser.verifySegmentIndex(video, [ref, ref2]);
|
|
627
|
+
await testInitialManifest(master, mediaWithPartialSegments, [ref, ref2]);
|
|
653
628
|
});
|
|
654
629
|
|
|
655
630
|
// Test for https://github.com/shaka-project/shaka-player/issues/4185
|
|
@@ -667,12 +642,8 @@ describe('HlsParser live', () => {
|
|
|
667
642
|
'#EXT-X-PRELOAD-HINT:TYPE=PART,URI="partial.mp4",BYTERANGE-START=210\n',
|
|
668
643
|
].join('');
|
|
669
644
|
|
|
670
|
-
fakeNetEngine
|
|
671
|
-
.setResponseText('test:/master', master)
|
|
672
|
-
.setResponseText('test:/video', mediaWithPartialSegments);
|
|
673
|
-
|
|
674
645
|
// If this throws, the test fails. Otherwise, it passes.
|
|
675
|
-
await
|
|
646
|
+
await testInitialManifest(master, mediaWithPartialSegments);
|
|
676
647
|
});
|
|
677
648
|
|
|
678
649
|
describe('update', () => {
|
|
@@ -682,8 +653,8 @@ describe('HlsParser live', () => {
|
|
|
682
653
|
const ref2 = makeReference(
|
|
683
654
|
'test:/main2.mp4', 2, 4, /* syncTime= */ null);
|
|
684
655
|
|
|
685
|
-
await
|
|
686
|
-
|
|
656
|
+
const manifest = await testInitialManifest(master, media, [ref1]);
|
|
657
|
+
await testUpdate(manifest, mediaWithAdditionalSegment, [ref1, ref2]);
|
|
687
658
|
});
|
|
688
659
|
|
|
689
660
|
it('evicts removed segments', async () => {
|
|
@@ -692,9 +663,9 @@ describe('HlsParser live', () => {
|
|
|
692
663
|
const ref2 = makeReference(
|
|
693
664
|
'test:/main2.mp4', 2, 4, /* syncTime= */ null);
|
|
694
665
|
|
|
695
|
-
await
|
|
696
|
-
master, mediaWithAdditionalSegment, [ref1, ref2]
|
|
697
|
-
|
|
666
|
+
const manifest = await testInitialManifest(
|
|
667
|
+
master, mediaWithAdditionalSegment, [ref1, ref2]);
|
|
668
|
+
await testUpdate(manifest, mediaWithRemovedSegment, [ref2]);
|
|
698
669
|
});
|
|
699
670
|
|
|
700
671
|
it('handles updates with redirects', async () => {
|
|
@@ -719,58 +690,32 @@ describe('HlsParser live', () => {
|
|
|
719
690
|
}
|
|
720
691
|
});
|
|
721
692
|
|
|
693
|
+
const manifest = await testInitialManifest(master, media, [oldRef1]);
|
|
722
694
|
await testUpdate(
|
|
723
|
-
|
|
724
|
-
[newRef1, newRef2]);
|
|
695
|
+
manifest, mediaWithAdditionalSegment, [newRef1, newRef2]);
|
|
725
696
|
});
|
|
726
697
|
|
|
727
698
|
it('parses start time from mp4 segments', async () => {
|
|
728
|
-
|
|
729
|
-
.setResponseText('test:/master', master)
|
|
730
|
-
.setResponseText('test:/video', media)
|
|
731
|
-
.setResponseValue('test:/init.mp4', initSegmentData)
|
|
732
|
-
.setResponseValue('test:/main.mp4', segmentData);
|
|
733
|
-
|
|
734
|
-
const expectedRef = makeReference(
|
|
699
|
+
const ref = makeReference(
|
|
735
700
|
'test:/main.mp4', 0, 2, /* syncTime= */ null);
|
|
736
701
|
// In live content, we do not set timestampOffset.
|
|
737
|
-
|
|
702
|
+
ref.timestampOffset = 0;
|
|
738
703
|
|
|
739
|
-
|
|
740
|
-
const video = manifest.variants[0].video;
|
|
741
|
-
await video.createSegmentIndex();
|
|
742
|
-
ManifestParser.verifySegmentIndex(video, [expectedRef]);
|
|
704
|
+
await testInitialManifest(master, media, [ref]);
|
|
743
705
|
});
|
|
744
706
|
|
|
745
707
|
it('gets start time on update without segment request', async () => {
|
|
746
|
-
fakeNetEngine
|
|
747
|
-
.setResponseText('test:/master', master)
|
|
748
|
-
.setResponseText('test:/video', mediaWithAdditionalSegment)
|
|
749
|
-
.setResponseValue('test:/init.mp4', initSegmentData)
|
|
750
|
-
.setResponseValue('test:/main.mp4', segmentData);
|
|
751
|
-
|
|
752
708
|
const ref1 = makeReference(
|
|
753
709
|
'test:/main.mp4', 0, 2, /* syncTime= */ null);
|
|
754
710
|
|
|
755
711
|
const ref2 = makeReference(
|
|
756
712
|
'test:/main2.mp4', 2, 4, /* syncTime= */ null);
|
|
757
713
|
|
|
758
|
-
const manifest = await
|
|
759
|
-
|
|
760
|
-
await video.createSegmentIndex();
|
|
761
|
-
ManifestParser.verifySegmentIndex(video, [ref1, ref2]);
|
|
762
|
-
|
|
763
|
-
fakeNetEngine
|
|
764
|
-
.setResponseText('test:/master', master)
|
|
765
|
-
.setResponseText('test:/video', mediaWithRemovedSegment)
|
|
766
|
-
.setResponseValue('test:/init.mp4', initSegmentData)
|
|
767
|
-
.setResponseValue('test:/main.mp4', segmentData)
|
|
768
|
-
.setResponseValue('test:/main2.mp4', segmentData);
|
|
714
|
+
const manifest = await testInitialManifest(
|
|
715
|
+
master, mediaWithAdditionalSegment, [ref1, ref2]);
|
|
769
716
|
|
|
770
717
|
fakeNetEngine.request.calls.reset();
|
|
771
|
-
await
|
|
772
|
-
|
|
773
|
-
ManifestParser.verifySegmentIndex(video, [ref2]);
|
|
718
|
+
await testUpdate(manifest, mediaWithRemovedSegment, [ref2]);
|
|
774
719
|
|
|
775
720
|
// Only one request was made, and it was for the playlist.
|
|
776
721
|
// No segment requests were needed to get the start time.
|
|
@@ -782,37 +727,17 @@ describe('HlsParser live', () => {
|
|
|
782
727
|
|
|
783
728
|
it('reuses cached timestamp offset for segments with discontinuity',
|
|
784
729
|
async () => {
|
|
785
|
-
fakeNetEngine
|
|
786
|
-
.setResponseText('test:/master', master)
|
|
787
|
-
.setResponseText('test:/video', mediaWithDiscontinuity)
|
|
788
|
-
.setResponseValue('test:/init.mp4', initSegmentData)
|
|
789
|
-
.setResponseValue('test:/main.mp4', segmentData)
|
|
790
|
-
.setResponseValue('test:/main2.mp4', segmentData);
|
|
791
|
-
|
|
792
730
|
const ref1 = makeReference(
|
|
793
731
|
'test:/main.mp4', 0, 2, /* syncTime= */ null);
|
|
794
|
-
|
|
795
732
|
const ref2 = makeReference(
|
|
796
733
|
'test:/main2.mp4', 2, 4, /* syncTime= */ null);
|
|
797
734
|
|
|
798
|
-
const manifest =
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
const video = manifest.variants[0].video;
|
|
802
|
-
await video.createSegmentIndex();
|
|
803
|
-
ManifestParser.verifySegmentIndex(video, [ref1, ref2]);
|
|
804
|
-
|
|
805
|
-
fakeNetEngine
|
|
806
|
-
.setResponseText('test:/master', master)
|
|
807
|
-
.setResponseText('test:/video',
|
|
808
|
-
mediaWithUpdatedDiscontinuitySegment)
|
|
809
|
-
.setResponseValue('test:/init.mp4', initSegmentData)
|
|
810
|
-
.setResponseValue('test:/main2.mp4', segmentData);
|
|
735
|
+
const manifest = await testInitialManifest(
|
|
736
|
+
master, mediaWithDiscontinuity, [ref1, ref2]);
|
|
811
737
|
|
|
812
738
|
fakeNetEngine.request.calls.reset();
|
|
813
|
-
await
|
|
814
|
-
|
|
815
|
-
ManifestParser.verifySegmentIndex(video, [ref2]);
|
|
739
|
+
await testUpdate(
|
|
740
|
+
manifest, mediaWithUpdatedDiscontinuitySegment, [ref2]);
|
|
816
741
|
|
|
817
742
|
// Only one request should be made, and it's for the playlist.
|
|
818
743
|
// Expect to use the cached timestamp offset for the main2.mp4
|
|
@@ -853,19 +778,12 @@ describe('HlsParser live', () => {
|
|
|
853
778
|
'main3.mp4\n',
|
|
854
779
|
].join('');
|
|
855
780
|
|
|
856
|
-
fakeNetEngine
|
|
857
|
-
|
|
858
|
-
.setResponseText('test:/video', mediaWithDeltaUpdates)
|
|
859
|
-
.setResponseText('test:/video?_HLS_skip=YES',
|
|
860
|
-
mediaWithSkippedSegments)
|
|
861
|
-
.setResponseValue('test:/init.mp4', initSegmentData)
|
|
862
|
-
.setResponseValue('test:/main.mp4', segmentData)
|
|
863
|
-
.setResponseValue('test:/main2.mp4', segmentData)
|
|
864
|
-
.setResponseValue('test:/main3.mp4', segmentData);
|
|
781
|
+
fakeNetEngine.setResponseText(
|
|
782
|
+
'test:/video?_HLS_skip=YES', mediaWithSkippedSegments);
|
|
865
783
|
|
|
866
784
|
playerInterface.isLowLatencyMode = () => true;
|
|
867
|
-
|
|
868
|
-
|
|
785
|
+
|
|
786
|
+
await testInitialManifest(master, mediaWithDeltaUpdates);
|
|
869
787
|
|
|
870
788
|
fakeNetEngine.request.calls.reset();
|
|
871
789
|
await delayForUpdatePeriod();
|
|
@@ -875,7 +793,6 @@ describe('HlsParser live', () => {
|
|
|
875
793
|
shaka.net.NetworkingEngine.RequestType.MANIFEST);
|
|
876
794
|
});
|
|
877
795
|
|
|
878
|
-
|
|
879
796
|
it('skips older segments', async () => {
|
|
880
797
|
const mediaWithSkippedSegments = [
|
|
881
798
|
'#EXTM3U\n',
|
|
@@ -896,12 +813,15 @@ describe('HlsParser live', () => {
|
|
|
896
813
|
'test:/main2.mp4', 2, 4, /* syncTime= */ null);
|
|
897
814
|
const ref3 = makeReference(
|
|
898
815
|
'test:/main3.mp4', 4, 6, /* syncTime= */ null);
|
|
816
|
+
|
|
817
|
+
const manifest = await testInitialManifest(
|
|
818
|
+
master, mediaWithAdditionalSegment, [ref1, ref2]);
|
|
819
|
+
|
|
899
820
|
// With 'SKIPPED-SEGMENTS', ref1 is skipped from the playlist,
|
|
900
821
|
// and ref1 should be in the SegmentReferences list.
|
|
901
822
|
// ref3 should be appended to the SegmentReferences list.
|
|
902
823
|
await testUpdate(
|
|
903
|
-
|
|
904
|
-
mediaWithSkippedSegments, [ref1, ref2, ref3]);
|
|
824
|
+
manifest, mediaWithSkippedSegments, [ref1, ref2, ref3]);
|
|
905
825
|
});
|
|
906
826
|
|
|
907
827
|
it('skips older segments with discontinuity', async () => {
|
|
@@ -959,12 +879,14 @@ describe('HlsParser live', () => {
|
|
|
959
879
|
/* baseUri= */ '', /* startByte= */ 0, /* endByte= */ null,
|
|
960
880
|
/* timestampOffset= */ 0);
|
|
961
881
|
|
|
882
|
+
const manifest = await testInitialManifest(
|
|
883
|
+
master, mediaWithDiscontinuity2, [ref1, ref2, ref3]);
|
|
884
|
+
|
|
962
885
|
// With 'SKIPPED-SEGMENTS', ref1, ref2 are skipped from the playlist,
|
|
963
886
|
// and ref1,ref2 should be in the SegmentReferences list.
|
|
964
887
|
// ref3,ref4 should be appended to the SegmentReferences list.
|
|
965
888
|
await testUpdate(
|
|
966
|
-
|
|
967
|
-
mediaWithSkippedSegments2, [ref1, ref2, ref3, ref4]);
|
|
889
|
+
manifest, mediaWithSkippedSegments2, [ref1, ref2, ref3, ref4]);
|
|
968
890
|
});
|
|
969
891
|
}); // describe('update')
|
|
970
892
|
}); // describe('playlist type LIVE')
|