shaka-player 4.2.5 → 4.3.1
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 +7 -0
- package/.github/workflows/sync-labels.yaml +1 -1
- package/.github/workflows/update-issues.yaml +1 -1
- package/AUTHORS +4 -0
- package/CHANGELOG.md +114 -74
- package/CONTRIBUTORS +4 -0
- package/README.md +14 -0
- package/conditional/dummy_cast_proxy.js +120 -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 +107 -88
- package/dist/demo.compiled.debug.map +4 -4
- package/dist/demo.compiled.js +107 -88
- package/dist/demo.compiled.map +4 -4
- package/dist/demo.css +1 -1
- package/dist/demo.css.map +1 -1
- package/dist/deps.js +21 -13
- package/dist/locales.js +31 -31
- package/dist/receiver.compiled.debug.js +75 -77
- package/dist/receiver.compiled.debug.map +3 -3
- package/dist/receiver.compiled.js +75 -77
- package/dist/receiver.compiled.map +3 -3
- package/dist/shaka-player.compiled.d.ts +181 -27
- package/dist/shaka-player.compiled.debug.d.ts +181 -27
- package/dist/shaka-player.compiled.debug.externs.js +169 -31
- package/dist/shaka-player.compiled.debug.js +510 -450
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +169 -31
- package/dist/shaka-player.compiled.js +912 -874
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +181 -27
- package/dist/shaka-player.ui.debug.d.ts +181 -27
- package/dist/shaka-player.ui.debug.externs.js +169 -31
- package/dist/shaka-player.ui.debug.js +533 -471
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +169 -31
- package/dist/shaka-player.ui.js +1073 -1036
- 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 +1 -42
- package/externs/shaka/ads.js +11 -1
- package/externs/shaka/manifest_parser.js +5 -1
- package/externs/shaka/mp4_parser.js +3 -0
- 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_utils.js +2 -0
- 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 +108 -5
- package/lib/config/auto_show_text.js +33 -0
- package/lib/dash/dash_parser.js +1 -1
- package/lib/hls/hls_parser.js +808 -384
- 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_capabilities.js +36 -0
- package/lib/media/media_source_engine.js +113 -28
- package/lib/media/presentation_timeline.js +26 -11
- package/lib/media/segment_reference.js +49 -1
- package/lib/media/streaming_engine.js +142 -12
- package/lib/media/transmuxer.js +78 -21
- package/lib/offline/storage.js +1 -0
- package/lib/player.js +456 -77
- package/lib/polyfill/media_capabilities.js +9 -5
- package/lib/polyfill/patchedmediakeys_apple.js +63 -4
- 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/mime_utils.js +3 -6
- package/lib/util/mp4_box_parsers.js +39 -5
- package/lib/util/mp4_parser.js +35 -0
- package/lib/util/player_configuration.js +23 -1
- package/lib/util/stream_utils.js +5 -3
- package/lib/util/ts_parser.js +499 -0
- package/maintained-branches.md +2 -2
- package/package.json +7 -4
- package/roadmap.md +5 -3
- package/test/ads/ad_manager_unit.js +182 -29
- package/test/cast/cast_utils_unit.js +3 -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 +631 -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 +71 -6
- package/test/media/media_source_engine_unit.js +10 -5
- package/test/media/streaming_engine_integration.js +4 -1
- package/test/media/streaming_engine_unit.js +144 -1
- package/test/media/transmuxer_integration.js +21 -10
- package/test/player_integration.js +224 -82
- package/test/player_unit.js +5 -2
- package/test/polyfill/media_capabilities_unit.js +21 -23
- package/test/polyfill/patchedmediakeys_apple_unit.js +130 -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 +4 -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/util/id3_utils_unit.js +116 -0
- package/test/util/ts_parser_unit.js +77 -0
- package/third_party/closure-uri/uri.js +1 -1
- package/ui/less/containers.less +7 -0
- package/ui/ui.js +43 -4
|
@@ -205,88 +205,6 @@ describe('Player', () => {
|
|
|
205
205
|
}
|
|
206
206
|
});
|
|
207
207
|
|
|
208
|
-
it('is called automatically if language prefs match', async () => {
|
|
209
|
-
// If the text is a match for the user's preferences, and audio differs
|
|
210
|
-
// from text, we enable text display automatically.
|
|
211
|
-
|
|
212
|
-
// NOTE: This is also a regression test for #1696, in which a change
|
|
213
|
-
// to this feature broke StreamingEngine initialization.
|
|
214
|
-
|
|
215
|
-
const preferredTextLanguage = 'fa'; // The same as in the content itself
|
|
216
|
-
player.configure({preferredTextLanguage: preferredTextLanguage});
|
|
217
|
-
|
|
218
|
-
// Now load a version of Sintel with delayed setup of video & audio
|
|
219
|
-
// streams and wait for completion.
|
|
220
|
-
await player.load('test:sintel_realistic_compiled');
|
|
221
|
-
// By this point, a MediaSource error would be thrown in a repro of bug
|
|
222
|
-
// #1696.
|
|
223
|
-
|
|
224
|
-
// Make sure the automatic setting took effect.
|
|
225
|
-
expect(player.isTextTrackVisible()).toBe(true);
|
|
226
|
-
|
|
227
|
-
// Make sure the content we tested with has text tracks, that the config
|
|
228
|
-
// we used matches the text language, and that the audio language differs.
|
|
229
|
-
// These will catch any changes to the underlying content that would
|
|
230
|
-
// invalidate the test setup.
|
|
231
|
-
expect(player.getTextTracks().length).not.toBe(0);
|
|
232
|
-
const textTrack = player.getTextTracks()[0];
|
|
233
|
-
expect(textTrack.language).toBe(preferredTextLanguage);
|
|
234
|
-
|
|
235
|
-
const variantTrack = player.getVariantTracks()[0];
|
|
236
|
-
expect(variantTrack.language).not.toBe(textTrack.language);
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
it('is not called automatically without language pref match', async () => {
|
|
240
|
-
// If the text preference doesn't match the content, we do not enable text
|
|
241
|
-
// display automatically.
|
|
242
|
-
|
|
243
|
-
const preferredTextLanguage = 'xx'; // Differs from the content itself
|
|
244
|
-
player.configure({preferredTextLanguage: preferredTextLanguage});
|
|
245
|
-
|
|
246
|
-
// Now load the content and wait for completion.
|
|
247
|
-
await player.load('test:sintel_realistic_compiled');
|
|
248
|
-
|
|
249
|
-
// Make sure the automatic setting did not happen.
|
|
250
|
-
expect(player.isTextTrackVisible()).toBe(false);
|
|
251
|
-
|
|
252
|
-
// Make sure the content we tested with has text tracks, that the config
|
|
253
|
-
// we used does not match the text language, and that the text and audio
|
|
254
|
-
// languages do not match each other (to keep this distinct from the next
|
|
255
|
-
// test case). This will catch any changes to the underlying content that
|
|
256
|
-
// would invalidate the test setup.
|
|
257
|
-
expect(player.getTextTracks().length).not.toBe(0);
|
|
258
|
-
const textTrack = player.getTextTracks()[0];
|
|
259
|
-
expect(textTrack.language).not.toBe(preferredTextLanguage);
|
|
260
|
-
|
|
261
|
-
const variantTrack = player.getVariantTracks()[0];
|
|
262
|
-
expect(variantTrack.language).not.toBe(textTrack.language);
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
it('is not called automatically with audio and text match', async () => {
|
|
266
|
-
// If the audio and text tracks use the same language, we do not enable
|
|
267
|
-
// text display automatically, no matter the text preference.
|
|
268
|
-
|
|
269
|
-
const preferredTextLanguage = 'und'; // The same as in the content itself
|
|
270
|
-
player.configure({preferredTextLanguage: preferredTextLanguage});
|
|
271
|
-
|
|
272
|
-
// Now load the content and wait for completion.
|
|
273
|
-
await player.load('test:sintel_compiled');
|
|
274
|
-
|
|
275
|
-
// Make sure the automatic setting did not happen.
|
|
276
|
-
expect(player.isTextTrackVisible()).toBe(false);
|
|
277
|
-
|
|
278
|
-
// Make sure the content we tested with has text tracks, that the
|
|
279
|
-
// config we used matches the content, and that the text and audio
|
|
280
|
-
// languages match each other. This will catch any changes to the
|
|
281
|
-
// underlying content that would invalidate the test setup.
|
|
282
|
-
expect(player.getTextTracks().length).not.toBe(0);
|
|
283
|
-
const textTrack = player.getTextTracks()[0];
|
|
284
|
-
expect(textTrack.language).toBe(preferredTextLanguage);
|
|
285
|
-
|
|
286
|
-
const variantTrack = player.getVariantTracks()[0];
|
|
287
|
-
expect(variantTrack.language).toBe(textTrack.language);
|
|
288
|
-
});
|
|
289
|
-
|
|
290
208
|
// Repro for https://github.com/shaka-project/shaka-player/issues/1879.
|
|
291
209
|
it('appends cues when enabled initially', async () => {
|
|
292
210
|
let cues = [];
|
|
@@ -376,6 +294,160 @@ describe('Player', () => {
|
|
|
376
294
|
});
|
|
377
295
|
}); // describe('setTextTrackVisibility')
|
|
378
296
|
|
|
297
|
+
describe('autoShowText', () => {
|
|
298
|
+
async function textMatchesAudioDoesNot() {
|
|
299
|
+
const preferredTextLanguage = 'fa'; // The same as in the content
|
|
300
|
+
player.configure({preferredTextLanguage: preferredTextLanguage});
|
|
301
|
+
|
|
302
|
+
// NOTE: This is also a regression test for #1696, in which a change to
|
|
303
|
+
// this feature broke StreamingEngine initialization.
|
|
304
|
+
|
|
305
|
+
// Now load a version of Sintel with delayed setup of video & audio
|
|
306
|
+
// streams and wait for completion.
|
|
307
|
+
await player.load('test:sintel_realistic_compiled');
|
|
308
|
+
// By this point, a MediaSource error would be thrown in a repro of bug
|
|
309
|
+
// #1696.
|
|
310
|
+
|
|
311
|
+
// Make sure the content we tested with has text tracks, that the config
|
|
312
|
+
// we used matches the text language, and that the audio language differs.
|
|
313
|
+
// These will catch any changes to the underlying content that would
|
|
314
|
+
// invalidate the test setup.
|
|
315
|
+
expect(player.getTextTracks().length).not.toBe(0);
|
|
316
|
+
const textTrack = player.getTextTracks()[0];
|
|
317
|
+
expect(textTrack.language).toBe(preferredTextLanguage);
|
|
318
|
+
|
|
319
|
+
const variantTrack = player.getVariantTracks()[0];
|
|
320
|
+
expect(variantTrack.language).not.toBe(textTrack.language);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
async function textDoesNotMatch() {
|
|
324
|
+
const preferredTextLanguage = 'xx'; // Differs from the content
|
|
325
|
+
player.configure({preferredTextLanguage: preferredTextLanguage});
|
|
326
|
+
|
|
327
|
+
// Now load the content and wait for completion.
|
|
328
|
+
await player.load('test:sintel_realistic_compiled');
|
|
329
|
+
|
|
330
|
+
// Make sure the content we tested with has text tracks, that the config
|
|
331
|
+
// we used does not match the text language, and that the text and audio
|
|
332
|
+
// languages do not match each other (to keep this distinct from the next
|
|
333
|
+
// test case). This will catch any changes to the underlying content that
|
|
334
|
+
// would invalidate the test setup.
|
|
335
|
+
expect(player.getTextTracks().length).not.toBe(0);
|
|
336
|
+
const textTrack = player.getTextTracks()[0];
|
|
337
|
+
expect(textTrack.language).not.toBe(preferredTextLanguage);
|
|
338
|
+
|
|
339
|
+
const variantTrack = player.getVariantTracks()[0];
|
|
340
|
+
expect(variantTrack.language).not.toBe(textTrack.language);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
async function textAndAudioMatch() {
|
|
344
|
+
const preferredTextLanguage = 'und'; // The same as in the content
|
|
345
|
+
player.configure({preferredTextLanguage: preferredTextLanguage});
|
|
346
|
+
|
|
347
|
+
// Now load the content and wait for completion.
|
|
348
|
+
await player.load('test:sintel_compiled');
|
|
349
|
+
|
|
350
|
+
// Make sure the content we tested with has text tracks, that the config
|
|
351
|
+
// we used matches the content, and that the text and audio languages
|
|
352
|
+
// match each other. This will catch any changes to the underlying
|
|
353
|
+
// content that would invalidate the test setup.
|
|
354
|
+
expect(player.getTextTracks().length).not.toBe(0);
|
|
355
|
+
const textTrack = player.getTextTracks()[0];
|
|
356
|
+
expect(textTrack.language).toBe(preferredTextLanguage);
|
|
357
|
+
|
|
358
|
+
const variantTrack = player.getVariantTracks()[0];
|
|
359
|
+
expect(variantTrack.language).toBe(textTrack.language);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
describe('IF_SUBTITLES_MAY_BE_NEEDED', () => {
|
|
363
|
+
beforeEach(() => {
|
|
364
|
+
player.configure(
|
|
365
|
+
'autoShowText',
|
|
366
|
+
shaka.config.AutoShowText.IF_SUBTITLES_MAY_BE_NEEDED);
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
it('enables text if text matches and audio does not', async () => {
|
|
370
|
+
await textMatchesAudioDoesNot();
|
|
371
|
+
expect(player.isTextTrackVisible()).toBe(true);
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
it('disables text if text does not match', async () => {
|
|
375
|
+
await textDoesNotMatch();
|
|
376
|
+
expect(player.isTextTrackVisible()).toBe(false);
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
it('disables text if both text and audio match', async () => {
|
|
380
|
+
await textAndAudioMatch();
|
|
381
|
+
expect(player.isTextTrackVisible()).toBe(false);
|
|
382
|
+
});
|
|
383
|
+
}); // IF_SUBTITLES_MAY_BE_NEEDED
|
|
384
|
+
|
|
385
|
+
describe('IF_PREFERRED_TEXT_LANGUAGE', () => {
|
|
386
|
+
beforeEach(() => {
|
|
387
|
+
player.configure(
|
|
388
|
+
'autoShowText',
|
|
389
|
+
shaka.config.AutoShowText.IF_PREFERRED_TEXT_LANGUAGE);
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
it('enables text if text matches and audio does not', async () => {
|
|
393
|
+
await textMatchesAudioDoesNot();
|
|
394
|
+
expect(player.isTextTrackVisible()).toBe(true);
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
it('disables text if text does not match', async () => {
|
|
398
|
+
await textDoesNotMatch();
|
|
399
|
+
expect(player.isTextTrackVisible()).toBe(false);
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
it('enables text if both text and audio match', async () => {
|
|
403
|
+
await textAndAudioMatch();
|
|
404
|
+
expect(player.isTextTrackVisible()).toBe(true);
|
|
405
|
+
});
|
|
406
|
+
}); // IF_PREFERRED_TEXT_LANGUAGE
|
|
407
|
+
|
|
408
|
+
describe('ALWAYS', () => {
|
|
409
|
+
beforeEach(() => {
|
|
410
|
+
player.configure('autoShowText', shaka.config.AutoShowText.ALWAYS);
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
it('enables text if text matches and audio does not', async () => {
|
|
414
|
+
await textMatchesAudioDoesNot();
|
|
415
|
+
expect(player.isTextTrackVisible()).toBe(true);
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
it('enables text if text does not match', async () => {
|
|
419
|
+
await textDoesNotMatch();
|
|
420
|
+
expect(player.isTextTrackVisible()).toBe(true);
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
it('enables text if both text and audio match', async () => {
|
|
424
|
+
await textAndAudioMatch();
|
|
425
|
+
expect(player.isTextTrackVisible()).toBe(true);
|
|
426
|
+
});
|
|
427
|
+
}); // ALWAYS
|
|
428
|
+
|
|
429
|
+
describe('NEVER', () => {
|
|
430
|
+
beforeEach(() => {
|
|
431
|
+
player.configure('autoShowText', shaka.config.AutoShowText.NEVER);
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
it('disables text if text matches and audio does not', async () => {
|
|
435
|
+
await textMatchesAudioDoesNot();
|
|
436
|
+
expect(player.isTextTrackVisible()).toBe(false);
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
it('disables text if text does not match', async () => {
|
|
440
|
+
await textDoesNotMatch();
|
|
441
|
+
expect(player.isTextTrackVisible()).toBe(false);
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
it('disables text if both text and audio match', async () => {
|
|
445
|
+
await textAndAudioMatch();
|
|
446
|
+
expect(player.isTextTrackVisible()).toBe(false);
|
|
447
|
+
});
|
|
448
|
+
}); // NEVER
|
|
449
|
+
}); // AutoShowText
|
|
450
|
+
|
|
379
451
|
describe('plays', () => {
|
|
380
452
|
it('with external text tracks', async () => {
|
|
381
453
|
await player.load('test:sintel_no_text_compiled');
|
|
@@ -1152,4 +1224,74 @@ describe('Player', () => {
|
|
|
1152
1224
|
expect(chapter3.endTime).toBe(61.349);
|
|
1153
1225
|
});
|
|
1154
1226
|
}); // describe('addChaptersTrack')
|
|
1227
|
+
|
|
1228
|
+
it('requires a license server for HLS ClearKey content', async () => {
|
|
1229
|
+
const expectedError = Util.jasmineError(new shaka.util.Error(
|
|
1230
|
+
shaka.util.Error.Severity.CRITICAL,
|
|
1231
|
+
shaka.util.Error.Category.DRM,
|
|
1232
|
+
shaka.util.Error.Code.NO_LICENSE_SERVER_GIVEN,
|
|
1233
|
+
'org.w3.clearkey'));
|
|
1234
|
+
await expectAsync(player.load('test:sintel-hls-clearkey'))
|
|
1235
|
+
.toBeRejectedWith(expectedError);
|
|
1236
|
+
});
|
|
1237
|
+
|
|
1238
|
+
describe('addThumbnailsTrack', () => {
|
|
1239
|
+
it('appends thumbnails for external thumbnails with sprites',
|
|
1240
|
+
async () => {
|
|
1241
|
+
await player.load('test:sintel_no_text_compiled');
|
|
1242
|
+
const locationUri = new goog.Uri(location.href);
|
|
1243
|
+
const partialUri =
|
|
1244
|
+
new goog.Uri('/base/test/test/assets/thumbnails-sprites.vtt');
|
|
1245
|
+
const absoluteUri = locationUri.resolve(partialUri);
|
|
1246
|
+
const newTrack =
|
|
1247
|
+
await player.addThumbnailsTrack(absoluteUri.toString());
|
|
1248
|
+
|
|
1249
|
+
expect(player.getImageTracks()).toEqual([newTrack]);
|
|
1250
|
+
|
|
1251
|
+
const thumbnail1 = await player.getThumbnails(newTrack.id, 0);
|
|
1252
|
+
expect(thumbnail1.startTime).toBe(0);
|
|
1253
|
+
expect(thumbnail1.duration).toBe(5);
|
|
1254
|
+
expect(thumbnail1.height).toBe(90);
|
|
1255
|
+
expect(thumbnail1.positionX).toBe(0);
|
|
1256
|
+
expect(thumbnail1.positionY).toBe(0);
|
|
1257
|
+
expect(thumbnail1.width).toBe(160);
|
|
1258
|
+
const thumbnail2 = await player.getThumbnails(newTrack.id, 10);
|
|
1259
|
+
expect(thumbnail2.startTime).toBe(5);
|
|
1260
|
+
expect(thumbnail2.duration).toBe(25);
|
|
1261
|
+
expect(thumbnail2.height).toBe(90);
|
|
1262
|
+
expect(thumbnail2.positionX).toBe(160);
|
|
1263
|
+
expect(thumbnail2.positionY).toBe(0);
|
|
1264
|
+
expect(thumbnail2.width).toBe(160);
|
|
1265
|
+
const thumbnail3 = await player.getThumbnails(newTrack.id, 40);
|
|
1266
|
+
expect(thumbnail3.startTime).toBe(30);
|
|
1267
|
+
expect(thumbnail3.duration).toBe(30);
|
|
1268
|
+
expect(thumbnail3.height).toBe(90);
|
|
1269
|
+
expect(thumbnail3.positionX).toBe(160);
|
|
1270
|
+
expect(thumbnail3.positionY).toBe(90);
|
|
1271
|
+
expect(thumbnail3.width).toBe(160);
|
|
1272
|
+
});
|
|
1273
|
+
|
|
1274
|
+
it('appends thumbnails for external thumbnails without sprites',
|
|
1275
|
+
async () => {
|
|
1276
|
+
await player.load('test:sintel_no_text_compiled');
|
|
1277
|
+
const locationUri = new goog.Uri(location.href);
|
|
1278
|
+
const partialUri =
|
|
1279
|
+
new goog.Uri('/base/test/test/assets/thumbnails.vtt');
|
|
1280
|
+
const absoluteUri = locationUri.resolve(partialUri);
|
|
1281
|
+
const newTrack =
|
|
1282
|
+
await player.addThumbnailsTrack(absoluteUri.toString());
|
|
1283
|
+
|
|
1284
|
+
expect(player.getImageTracks()).toEqual([newTrack]);
|
|
1285
|
+
|
|
1286
|
+
const thumbnail1 = await player.getThumbnails(newTrack.id, 0);
|
|
1287
|
+
expect(thumbnail1.startTime).toBe(0);
|
|
1288
|
+
expect(thumbnail1.duration).toBe(5);
|
|
1289
|
+
const thumbnail2 = await player.getThumbnails(newTrack.id, 10);
|
|
1290
|
+
expect(thumbnail2.startTime).toBe(5);
|
|
1291
|
+
expect(thumbnail2.duration).toBe(25);
|
|
1292
|
+
const thumbnail3 = await player.getThumbnails(newTrack.id, 40);
|
|
1293
|
+
expect(thumbnail3.startTime).toBe(30);
|
|
1294
|
+
expect(thumbnail3.duration).toBe(30);
|
|
1295
|
+
});
|
|
1296
|
+
}); // describe('addThumbnailsTrack')
|
|
1155
1297
|
});
|
package/test/player_unit.js
CHANGED
|
@@ -123,6 +123,7 @@ describe('Player', () => {
|
|
|
123
123
|
streamingEngine = new shaka.test.FakeStreamingEngine();
|
|
124
124
|
mediaSourceEngine = {
|
|
125
125
|
init: jasmine.createSpy('init').and.returnValue(Promise.resolve()),
|
|
126
|
+
configure: jasmine.createSpy('configure'),
|
|
126
127
|
open: jasmine.createSpy('open').and.returnValue(Promise.resolve()),
|
|
127
128
|
destroy:
|
|
128
129
|
jasmine.createSpy('destroy').and.returnValue(Promise.resolve()),
|
|
@@ -130,6 +131,8 @@ describe('Player', () => {
|
|
|
130
131
|
getUseEmbeddedText: jasmine.createSpy('getUseEmbeddedText'),
|
|
131
132
|
setSegmentRelativeVttTiming:
|
|
132
133
|
jasmine.createSpy('setSegmentRelativeVttTiming'),
|
|
134
|
+
updateLcevcDil:
|
|
135
|
+
jasmine.createSpy('updateLcevcDil'),
|
|
133
136
|
getTextDisplayer: () => textDisplayer,
|
|
134
137
|
getBufferedInfo: () => bufferedInfo,
|
|
135
138
|
ended: jasmine.createSpy('ended').and.returnValue(false),
|
|
@@ -626,13 +629,13 @@ describe('Player', () => {
|
|
|
626
629
|
// Try a bogus streaming config (number instead of Object)
|
|
627
630
|
logErrorSpy.calls.reset();
|
|
628
631
|
player.configure({
|
|
629
|
-
|
|
632
|
+
drm: 5,
|
|
630
633
|
});
|
|
631
634
|
|
|
632
635
|
newConfig = player.getConfiguration();
|
|
633
636
|
expect(newConfig).toEqual(defaultConfig);
|
|
634
637
|
expect(logErrorSpy).toHaveBeenCalledWith(
|
|
635
|
-
stringContaining('.
|
|
638
|
+
stringContaining('.drm'));
|
|
636
639
|
});
|
|
637
640
|
|
|
638
641
|
it('accepts synchronous function values for async function fields', () => {
|
|
@@ -12,6 +12,7 @@ describe('MediaCapabilities', () => {
|
|
|
12
12
|
const originalRequestMediaKeySystemAccess =
|
|
13
13
|
navigator.requestMediaKeySystemAccess;
|
|
14
14
|
const originalMediaCapabilities = navigator.mediaCapabilities;
|
|
15
|
+
const supportMap = shaka.media.Capabilities.MediaSourceTypeSupportMap;
|
|
15
16
|
|
|
16
17
|
/** @type {MediaDecodingConfiguration} */
|
|
17
18
|
let mockDecodingConfig;
|
|
@@ -102,18 +103,12 @@ describe('MediaCapabilities', () => {
|
|
|
102
103
|
describe('decodingInfo', () => {
|
|
103
104
|
it('should check codec support when MediaDecodingConfiguration.type ' +
|
|
104
105
|
'is "media-source"', () => {
|
|
105
|
-
|
|
106
|
-
spyOn(window['MediaSource'], 'isTypeSupported').and.returnValue(true);
|
|
106
|
+
expect(window['MediaSource']['isTypeSupported']).toBeDefined();
|
|
107
107
|
shaka.polyfill.MediaCapabilities.install();
|
|
108
108
|
navigator.mediaCapabilities.decodingInfo(mockDecodingConfig);
|
|
109
109
|
|
|
110
|
-
expect(
|
|
111
|
-
expect(
|
|
112
|
-
mockDecodingConfig.video.contentType,
|
|
113
|
-
);
|
|
114
|
-
expect(isTypeSupportedSpy).toHaveBeenCalledWith(
|
|
115
|
-
mockDecodingConfig.audio.contentType,
|
|
116
|
-
);
|
|
110
|
+
expect(supportMap.has(mockDecodingConfig.video.contentType)).toBe(true);
|
|
111
|
+
expect(supportMap.has(mockDecodingConfig.audio.contentType)).toBe(true);
|
|
117
112
|
});
|
|
118
113
|
|
|
119
114
|
it('should check codec support when MediaDecodingConfiguration.type ' +
|
|
@@ -200,9 +195,7 @@ describe('MediaCapabilities', () => {
|
|
|
200
195
|
const isChromecastSpy =
|
|
201
196
|
spyOn(shaka['util']['Platform'],
|
|
202
197
|
'isChromecast').and.returnValue(true);
|
|
203
|
-
|
|
204
|
-
spyOn(window['MediaSource'], 'isTypeSupported')
|
|
205
|
-
.and.returnValue(true);
|
|
198
|
+
expect(window['MediaSource']['isTypeSupported']).toBeDefined();
|
|
206
199
|
|
|
207
200
|
shaka.polyfill.MediaCapabilities.install();
|
|
208
201
|
await navigator.mediaCapabilities.decodingInfo(mockDecodingConfig);
|
|
@@ -212,7 +205,10 @@ describe('MediaCapabilities', () => {
|
|
|
212
205
|
expect(isChromecastSpy).toHaveBeenCalledTimes(2);
|
|
213
206
|
// 1 (fallback in canCastDisplayType()) +
|
|
214
207
|
// 1 (mockDecodingConfig.audio).
|
|
215
|
-
expect(
|
|
208
|
+
expect(supportMap.has(mockDecodingConfig.video.contentType))
|
|
209
|
+
.toBe(true);
|
|
210
|
+
expect(supportMap.has(mockDecodingConfig.audio.contentType))
|
|
211
|
+
.toBe(true);
|
|
216
212
|
});
|
|
217
213
|
|
|
218
214
|
it('should use cast.__platform__.canDisplayType for "supported" field ' +
|
|
@@ -226,8 +222,7 @@ describe('MediaCapabilities', () => {
|
|
|
226
222
|
const isChromecastSpy =
|
|
227
223
|
spyOn(shaka['util']['Platform'],
|
|
228
224
|
'isChromecast').and.returnValue(true);
|
|
229
|
-
|
|
230
|
-
spyOn(window['MediaSource'], 'isTypeSupported').and.returnValue(true);
|
|
225
|
+
expect(window['MediaSource']['isTypeSupported']).toBeDefined();
|
|
231
226
|
|
|
232
227
|
// Tests an HDR stream's extended MIME type is correctly provided.
|
|
233
228
|
mockDecodingConfig.video.transferFunction = 'pq';
|
|
@@ -236,14 +231,17 @@ describe('MediaCapabilities', () => {
|
|
|
236
231
|
// Round to a whole number since we can't rely on number => string
|
|
237
232
|
// conversion precision on all devices.
|
|
238
233
|
mockDecodingConfig.video.framerate = 24;
|
|
234
|
+
|
|
235
|
+
const chromecastType =
|
|
236
|
+
'video/mp4; ' +
|
|
237
|
+
'codecs="hev1.2.4.L153.B0"; ' +
|
|
238
|
+
'width=512; ' +
|
|
239
|
+
'height=288; ' +
|
|
240
|
+
'framerate=24; ' +
|
|
241
|
+
'eotf=smpte2084';
|
|
239
242
|
mockCanDisplayType.and.callFake((type) => {
|
|
240
|
-
expect(type).toBe(
|
|
241
|
-
|
|
242
|
-
'codecs="hev1.2.4.L153.B0"; ' +
|
|
243
|
-
'width=512; ' +
|
|
244
|
-
'height=288; ' +
|
|
245
|
-
'framerate=24; ' +
|
|
246
|
-
'eotf=smpte2084');
|
|
243
|
+
expect(type).toBe(chromecastType);
|
|
244
|
+
supportMap.set(type, true);
|
|
247
245
|
return true;
|
|
248
246
|
});
|
|
249
247
|
|
|
@@ -253,7 +251,7 @@ describe('MediaCapabilities', () => {
|
|
|
253
251
|
// 1 (during install()) + 1 (for video config check).
|
|
254
252
|
expect(isChromecastSpy).toHaveBeenCalledTimes(2);
|
|
255
253
|
// 1 (mockDecodingConfig.audio).
|
|
256
|
-
expect(
|
|
254
|
+
expect(supportMap.has(chromecastType)).toBe(true);
|
|
257
255
|
// Called once in canCastDisplayType.
|
|
258
256
|
expect(mockCanDisplayType).toHaveBeenCalledTimes(1);
|
|
259
257
|
});
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/*! @license
|
|
2
|
+
* Shaka Player
|
|
3
|
+
* Copyright 2016 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
describe('PatchedMediaKeys_Apple', () => {
|
|
8
|
+
const PatchedMediaKeysApple = shaka.polyfill.PatchedMediaKeysApple;
|
|
9
|
+
let originalMediaKeys;
|
|
10
|
+
let originalHTMLMediaElementPrototypeSetMediaKeys;
|
|
11
|
+
let originalWindowMediaKeys;
|
|
12
|
+
let originalWindowMediaKeySystemAccess;
|
|
13
|
+
let originalNavigatorRequestMediaKeySystemAccess;
|
|
14
|
+
let originalHTMLSVideoElement;
|
|
15
|
+
let originalWebKitMediaKeys;
|
|
16
|
+
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
originalHTMLSVideoElement = window.HTMLVideoElement;
|
|
19
|
+
originalWebKitMediaKeys = window.WebKitMediaKeys;
|
|
20
|
+
|
|
21
|
+
Object.defineProperty(window,
|
|
22
|
+
'HTMLVideoElement', {value: {}, configurable: true, writable: true});
|
|
23
|
+
Object.defineProperty(window,
|
|
24
|
+
'WebKitMediaKeys', {value: {}, configurable: true, writable: true});
|
|
25
|
+
|
|
26
|
+
originalMediaKeys = /** @type {!Object} */ (
|
|
27
|
+
Object.getOwnPropertyDescriptor(
|
|
28
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
29
|
+
HTMLMediaElement.prototype, 'mediaKeys',
|
|
30
|
+
)
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
34
|
+
originalHTMLMediaElementPrototypeSetMediaKeys = HTMLMediaElement
|
|
35
|
+
.prototype.setMediaKeys;
|
|
36
|
+
originalWindowMediaKeys = window.MediaKeys;
|
|
37
|
+
originalWindowMediaKeySystemAccess = window.MediaKeySystemAccess;
|
|
38
|
+
originalNavigatorRequestMediaKeySystemAccess = navigator
|
|
39
|
+
.requestMediaKeySystemAccess;
|
|
40
|
+
|
|
41
|
+
delete window.shakaMediaKeysPolyfill;
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
afterEach(() => {
|
|
45
|
+
window.HTMLVideoElement = originalHTMLSVideoElement;
|
|
46
|
+
window.WebKitMediaKeys = originalWebKitMediaKeys;
|
|
47
|
+
|
|
48
|
+
Object.defineProperty(
|
|
49
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
50
|
+
HTMLMediaElement.prototype,
|
|
51
|
+
'mediaKeys',
|
|
52
|
+
originalMediaKeys,
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
56
|
+
HTMLMediaElement
|
|
57
|
+
.prototype.setMediaKeys =
|
|
58
|
+
originalHTMLMediaElementPrototypeSetMediaKeys;
|
|
59
|
+
window.MediaKeys = originalWindowMediaKeys;
|
|
60
|
+
window.MediaKeySystemAccess = originalWindowMediaKeySystemAccess;
|
|
61
|
+
navigator
|
|
62
|
+
.requestMediaKeySystemAccess =
|
|
63
|
+
originalNavigatorRequestMediaKeySystemAccess;
|
|
64
|
+
|
|
65
|
+
delete window.shakaMediaKeysPolyfill;
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
describe('install', () => {
|
|
69
|
+
it('should override browser globals', () => {
|
|
70
|
+
shaka.polyfill.PatchedMediaKeysApple.install();
|
|
71
|
+
|
|
72
|
+
expect(shaka.polyfill.PatchedMediaKeysApple.enableUninstall)
|
|
73
|
+
.toBe(undefined);
|
|
74
|
+
|
|
75
|
+
expect(
|
|
76
|
+
originalHTMLMediaElementPrototypeSetMediaKeys,
|
|
77
|
+
).not.toEqual(
|
|
78
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
79
|
+
HTMLMediaElement.prototype.setMediaKeys,
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
expect(
|
|
83
|
+
Object.getOwnPropertyDescriptor(
|
|
84
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
85
|
+
HTMLMediaElement.prototype, 'mediaKeys',
|
|
86
|
+
).value,
|
|
87
|
+
).toBeNull();
|
|
88
|
+
|
|
89
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
90
|
+
expect(HTMLMediaElement.prototype.setMediaKeys)
|
|
91
|
+
.toEqual(PatchedMediaKeysApple.setMediaKeys);
|
|
92
|
+
|
|
93
|
+
expect(window.MediaKeys).toEqual(PatchedMediaKeysApple.MediaKeys);
|
|
94
|
+
expect(window.MediaKeySystemAccess)
|
|
95
|
+
.toEqual(PatchedMediaKeysApple.MediaKeySystemAccess);
|
|
96
|
+
expect(navigator.requestMediaKeySystemAccess)
|
|
97
|
+
.toEqual(PatchedMediaKeysApple.requestMediaKeySystemAccess);
|
|
98
|
+
expect(window.shakaMediaKeysPolyfill).toBe(true);
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
describe('uninstall', () => {
|
|
103
|
+
it('should restore browser globals', () => {
|
|
104
|
+
shaka.polyfill.PatchedMediaKeysApple.install(true);
|
|
105
|
+
|
|
106
|
+
expect(shaka.polyfill.PatchedMediaKeysApple.enableUninstall)
|
|
107
|
+
.toBe(true);
|
|
108
|
+
|
|
109
|
+
shaka.polyfill.PatchedMediaKeysApple.uninstall();
|
|
110
|
+
|
|
111
|
+
expect(
|
|
112
|
+
Object.getOwnPropertyDescriptor(
|
|
113
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
114
|
+
HTMLMediaElement.prototype, 'mediaKeys',
|
|
115
|
+
).value,
|
|
116
|
+
).not.toBeNull();
|
|
117
|
+
|
|
118
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
119
|
+
expect(HTMLMediaElement.prototype.setMediaKeys)
|
|
120
|
+
.toEqual(originalHTMLMediaElementPrototypeSetMediaKeys);
|
|
121
|
+
|
|
122
|
+
expect(window.MediaKeys).toEqual(originalWindowMediaKeys);
|
|
123
|
+
expect(window.MediaKeySystemAccess)
|
|
124
|
+
.toEqual(originalWindowMediaKeySystemAccess);
|
|
125
|
+
expect(navigator.requestMediaKeySystemAccess)
|
|
126
|
+
.toEqual(originalNavigatorRequestMediaKeySystemAccess);
|
|
127
|
+
expect(window.shakaMediaKeysPolyfill).toBe(false);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
});
|
|
Binary file
|
|
Binary file
|
|
@@ -44,6 +44,7 @@ shaka.test.Dash = class {
|
|
|
44
44
|
isAutoLowLatencyMode: () => false,
|
|
45
45
|
enableLowLatencyMode: () => {},
|
|
46
46
|
updateDuration: () => {},
|
|
47
|
+
newDrmInfo: (stream) => {},
|
|
47
48
|
};
|
|
48
49
|
const manifest = await dashParser.start('dummy://foo', playerInterface);
|
|
49
50
|
const stream = manifest.variants[0].video;
|
|
@@ -79,6 +80,7 @@ shaka.test.Dash = class {
|
|
|
79
80
|
isAutoLowLatencyMode: () => false,
|
|
80
81
|
enableLowLatencyMode: () => {},
|
|
81
82
|
updateDuration: () => {},
|
|
83
|
+
newDrmInfo: (stream) => {},
|
|
82
84
|
};
|
|
83
85
|
const p = dashParser.start('dummy://foo', playerInterface);
|
|
84
86
|
await expectAsync(p).toBeRejectedWith(
|
|
@@ -34,6 +34,12 @@ shaka.test.FakeAd = class {
|
|
|
34
34
|
|
|
35
35
|
/** @private {number} */
|
|
36
36
|
this.volume_ = 1;
|
|
37
|
+
|
|
38
|
+
/** @private {string} */
|
|
39
|
+
this.title_ = 'Test Title';
|
|
40
|
+
|
|
41
|
+
/** @private {string} */
|
|
42
|
+
this.description_ = 'Test Description';
|
|
37
43
|
}
|
|
38
44
|
|
|
39
45
|
/**
|
|
@@ -190,6 +196,21 @@ shaka.test.FakeAd = class {
|
|
|
190
196
|
return this.position_;
|
|
191
197
|
}
|
|
192
198
|
|
|
199
|
+
/**
|
|
200
|
+
* @override
|
|
201
|
+
* @export
|
|
202
|
+
*/
|
|
203
|
+
getTitle() {
|
|
204
|
+
return this.title_;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* @override
|
|
209
|
+
* @export
|
|
210
|
+
*/
|
|
211
|
+
getDescription() {
|
|
212
|
+
return this.description_;
|
|
213
|
+
}
|
|
193
214
|
|
|
194
215
|
/**
|
|
195
216
|
* @override
|
|
@@ -54,6 +54,9 @@ shaka.test.FakeMediaSourceEngine = class {
|
|
|
54
54
|
/** @type {!jasmine.Spy} */
|
|
55
55
|
this.open = jasmine.createSpy('open').and.returnValue(Promise.resolve());
|
|
56
56
|
|
|
57
|
+
/** @type {!jasmine.Spy} */
|
|
58
|
+
this.configure = jasmine.createSpy('configure').and.stub();
|
|
59
|
+
|
|
57
60
|
/** @type {!jasmine.Spy} */
|
|
58
61
|
this.reinitText = jasmine.createSpy('reinitText').and.stub();
|
|
59
62
|
|
|
@@ -129,6 +132,10 @@ shaka.test.FakeMediaSourceEngine = class {
|
|
|
129
132
|
/** @type {!jasmine.Spy} */
|
|
130
133
|
this.setSegmentRelativeVttTiming =
|
|
131
134
|
jasmine.createSpy('setSegmentRelativeVttTiming').and.stub();
|
|
135
|
+
|
|
136
|
+
/** @type {!jasmine.Spy} */
|
|
137
|
+
this.updateLcevcDil =
|
|
138
|
+
jasmine.createSpy('updateLcevcDil').and.stub();
|
|
132
139
|
}
|
|
133
140
|
|
|
134
141
|
/** @override */
|