shaka-player 3.1.2 → 3.1.6
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 +73 -0
- package/.github/workflows/release.yaml +53 -0
- package/AUTHORS +3 -0
- package/CHANGELOG.md +170 -1
- package/CONTRIBUTORS +3 -0
- package/build/checkversion.py +1 -1
- package/build/generateExterns.js +1 -1
- package/build/misspellings.txt +1 -0
- package/build/shakaBuildHelpers.py +6 -5
- package/build/subprocessWindowsPatch.py +103 -0
- package/build/test.py +6 -0
- package/demo/common/assets.js +1 -1
- package/dist/controls.css +2 -2
- package/dist/controls.css.map +1 -1
- package/dist/demo.compiled.debug.js +1 -1
- package/dist/demo.compiled.js +1 -1
- package/dist/deps.js +7 -7
- package/dist/locales.js +35 -35
- package/dist/receiver.compiled.debug.js +1 -1
- package/dist/receiver.compiled.js +1 -1
- package/dist/shaka-player.compiled.d.ts +172 -3
- package/dist/shaka-player.compiled.debug.d.ts +172 -3
- package/dist/shaka-player.compiled.debug.externs.js +184 -1
- package/dist/shaka-player.compiled.debug.js +390 -370
- package/dist/shaka-player.compiled.debug.map +3 -3
- package/dist/shaka-player.compiled.externs.js +184 -1
- package/dist/shaka-player.compiled.js +809 -805
- package/dist/shaka-player.compiled.map +3 -3
- package/dist/shaka-player.ui.d.ts +174 -14
- package/dist/shaka-player.ui.debug.d.ts +174 -14
- package/dist/shaka-player.ui.debug.externs.js +200 -26
- package/dist/shaka-player.ui.debug.js +431 -412
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +200 -26
- package/dist/shaka-player.ui.js +934 -929
- package/dist/shaka-player.ui.map +4 -4
- package/docs/tutorials/fairplay.md +1 -1
- package/docs/tutorials/faq.md +14 -0
- package/externs/ima.js +4 -0
- package/externs/shaka/ads.js +2 -0
- package/externs/shaka/text.js +10 -0
- package/karma.conf.js +20 -15
- package/lib/ads/ad_manager.js +30 -11
- package/lib/ads/client_side_ad_manager.js +76 -86
- package/lib/ads/server_side_ad_manager.js +26 -11
- package/lib/cast/cast_proxy.js +6 -2
- package/lib/cast/cast_receiver.js +5 -1
- package/lib/cast/cast_sender.js +2 -2
- package/lib/cea/sei_processor.js +24 -17
- package/lib/dash/dash_parser.js +131 -24
- package/lib/dash/segment_list.js +1 -1
- package/lib/dash/segment_template.js +1 -1
- package/lib/hls/hls_parser.js +44 -17
- package/lib/hls/manifest_text_parser.js +1 -0
- package/lib/media/closed_caption_parser.js +3 -1
- package/lib/media/content_workarounds.js +12 -0
- package/lib/media/drm_engine.js +20 -1
- package/lib/media/gap_jumping_controller.js +8 -4
- package/lib/media/media_source_engine.js +1 -2
- package/lib/media/playhead.js +1 -1
- package/lib/media/region_observer.js +39 -33
- package/lib/media/region_timeline.js +17 -19
- package/lib/media/streaming_engine.js +8 -4
- package/lib/net/networking_engine.js +6 -1
- package/lib/offline/storage.js +10 -11
- package/lib/player.js +66 -30
- package/lib/polyfill/encryption_scheme.js +2 -0
- package/lib/polyfill/fullscreen.js +2 -0
- package/lib/polyfill/mathround.js +2 -0
- package/lib/polyfill/media_capabilities.js +73 -21
- package/lib/polyfill/mediasource.js +8 -0
- package/lib/polyfill/orientation.js +3 -1
- package/lib/polyfill/patchedmediakeys_apple.js +6 -4
- package/lib/polyfill/patchedmediakeys_ms.js +6 -4
- package/lib/polyfill/patchedmediakeys_nop.js +2 -0
- package/lib/polyfill/patchedmediakeys_webkit.js +6 -4
- package/lib/polyfill/pip_webkit.js +2 -0
- package/lib/polyfill/storage_estimate.js +2 -0
- package/lib/polyfill/video_play_promise.js +2 -0
- package/lib/polyfill/videoplaybackquality.js +2 -0
- package/lib/polyfill/vttcue.js +2 -0
- package/lib/text/cue.js +6 -0
- package/lib/text/mp4_ttml_parser.js +3 -0
- package/lib/text/text_engine.js +14 -2
- package/lib/text/ttml_text_parser.js +135 -87
- package/lib/text/ui_text_displayer.js +213 -80
- package/lib/text/vtt_text_parser.js +4 -2
- package/lib/util/fake_event.js +53 -7
- package/lib/util/fake_event_target.js +21 -1
- package/lib/util/mp4_box_parsers.js +1 -1
- package/lib/util/periods.js +4 -1
- package/lib/util/platform.js +13 -1
- package/lib/util/stream_utils.js +77 -10
- package/lib/util/xml_utils.js +19 -0
- package/package.json +3 -6
- package/test/cast/cast_proxy_unit.js +12 -6
- package/test/cast/cast_receiver_integration.js +1 -1
- package/test/cast/cast_utils_unit.js +1 -1
- package/test/cea/mp4_cea_parser_unit.js +26 -0
- package/test/dash/dash_parser_live_unit.js +43 -0
- package/test/dash/dash_parser_manifest_unit.js +222 -3
- package/test/hls/hls_parser_unit.js +79 -1
- package/test/media/closed_caption_parser_unit.js +20 -0
- package/test/media/drm_engine_unit.js +56 -0
- package/test/media/playhead_unit.js +27 -1
- package/test/media/region_observer_unit.js +40 -5
- package/test/media/region_timeline_unit.js +40 -22
- package/test/media/streaming_engine_integration.js +2 -1
- package/test/media/streaming_engine_unit.js +145 -0
- package/test/player_integration.js +38 -0
- package/test/test/assets/3675/chunk-stream0-00001.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00002.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00003.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00004.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00005.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00006.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00007.m4s +0 -0
- package/test/test/assets/3675/dash_0.mpd +30 -0
- package/test/test/assets/3675/dash_1.mpd +30 -0
- package/test/test/assets/3675/dash_2.mpd +30 -0
- package/test/test/assets/3675/dash_3.mpd +30 -0
- package/test/test/assets/3675/dash_4.mpd +30 -0
- package/test/test/assets/3675/dash_5.mpd +26 -0
- package/test/test/assets/3675/init-stream0.m4s +0 -0
- package/test/test/assets/empty_caption_video_init.mp4 +0 -0
- package/test/test/assets/empty_caption_video_segment.mp4 +0 -0
- package/test/test/util/fake_ad_manager.js +4 -1
- package/test/test/util/ttml_utils.js +81 -0
- package/test/test/util/util.js +10 -7
- package/test/text/mp4_ttml_parser_unit.js +11 -18
- package/test/text/ttml_text_parser_unit.js +542 -220
- package/test/text/ui_text_displayer_unit.js +93 -20
- package/test/text/vtt_text_parser_unit.js +17 -1
- package/test/ui/localization_unit.js +15 -14
- package/test/ui/text_displayer_layout_unit.js +21 -8
- package/test/util/mp4_box_parsers_unit.js +28 -0
- package/test/util/stream_utils_unit.js +59 -2
- package/ui/cast_button.js +2 -3
- package/ui/controls.js +11 -6
- package/ui/less/buttons.less +0 -4
- package/ui/less/containers.less +6 -7
- package/ui/locales/ru.json +6 -6
- package/ui/localization.js +40 -87
- package/ui/pip_button.js +2 -3
- package/ui/text_selection.js +1 -0
package/lib/util/stream_utils.js
CHANGED
|
@@ -337,9 +337,44 @@ shaka.util.StreamUtils = class {
|
|
|
337
337
|
'MediaCapabilities should be valid.');
|
|
338
338
|
|
|
339
339
|
await shaka.util.StreamUtils.getDecodingInfosForVariants(
|
|
340
|
-
manifest.variants, usePersistentLicenses);
|
|
340
|
+
manifest.variants, usePersistentLicenses, /* srcEquals= */ false);
|
|
341
341
|
manifest.variants = manifest.variants.filter((variant) => {
|
|
342
|
+
// See: https://github.com/google/shaka-player/issues/3860
|
|
342
343
|
const video = variant.video;
|
|
344
|
+
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
345
|
+
if (video) {
|
|
346
|
+
let videoCodecs = shaka.util.StreamUtils.patchVp9(video.codecs);
|
|
347
|
+
// For multiplexed streams. Here we must check the audio of the
|
|
348
|
+
// stream to see if it is compatible.
|
|
349
|
+
if (video.codecs.includes(',')) {
|
|
350
|
+
const allCodecs = video.codecs.split(',');
|
|
351
|
+
videoCodecs = shaka.util.ManifestParserUtils.guessCodecs(
|
|
352
|
+
ContentType.VIDEO, allCodecs);
|
|
353
|
+
const audioCodecs = shaka.util.ManifestParserUtils.guessCodecs(
|
|
354
|
+
ContentType.AUDIO, allCodecs);
|
|
355
|
+
const audioFullType = shaka.util.MimeUtils.getFullOrConvertedType(
|
|
356
|
+
video.mimeType, audioCodecs, ContentType.AUDIO);
|
|
357
|
+
if (!MediaSource.isTypeSupported(audioFullType)) {
|
|
358
|
+
return false;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
const fullType = shaka.util.MimeUtils.getFullOrConvertedType(
|
|
362
|
+
video.mimeType, videoCodecs, ContentType.VIDEO);
|
|
363
|
+
if (!MediaSource.isTypeSupported(fullType)) {
|
|
364
|
+
return false;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
const audio = variant.audio;
|
|
368
|
+
if (audio) {
|
|
369
|
+
const codecs =
|
|
370
|
+
shaka.util.StreamUtils.getCorrectAudioCodecs_(audio.codecs);
|
|
371
|
+
const fullType = shaka.util.MimeUtils.getFullOrConvertedType(
|
|
372
|
+
audio.mimeType, codecs, ContentType.AUDIO);
|
|
373
|
+
if (!MediaSource.isTypeSupported(fullType)) {
|
|
374
|
+
return false;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
343
378
|
// See: https://github.com/google/shaka-player/issues/3380
|
|
344
379
|
if (shaka.util.Platform.isXboxOne() && video &&
|
|
345
380
|
((video.width && video.width > 1920) ||
|
|
@@ -410,9 +445,11 @@ shaka.util.StreamUtils = class {
|
|
|
410
445
|
*
|
|
411
446
|
* @param {!Array.<shaka.extern.Variant>} variants
|
|
412
447
|
* @param {boolean} usePersistentLicenses
|
|
448
|
+
* @param {boolean} srcEquals
|
|
413
449
|
* @exportDoc
|
|
414
450
|
*/
|
|
415
|
-
static async getDecodingInfosForVariants(variants, usePersistentLicenses
|
|
451
|
+
static async getDecodingInfosForVariants(variants, usePersistentLicenses,
|
|
452
|
+
srcEquals) {
|
|
416
453
|
const gotDecodingInfo = variants.some((variant) =>
|
|
417
454
|
variant.decodingInfos.length);
|
|
418
455
|
if (gotDecodingInfo) {
|
|
@@ -436,7 +473,7 @@ shaka.util.StreamUtils = class {
|
|
|
436
473
|
for (const variant of variants) {
|
|
437
474
|
/** @type {!Array.<!MediaDecodingConfiguration>} */
|
|
438
475
|
const decodingConfigs = shaka.util.StreamUtils.getDecodingConfigs_(
|
|
439
|
-
variant, usePersistentLicenses);
|
|
476
|
+
variant, usePersistentLicenses, srcEquals);
|
|
440
477
|
|
|
441
478
|
for (const config of decodingConfigs) {
|
|
442
479
|
operations.push(getVariantDecodingInfos(variant, config));
|
|
@@ -451,17 +488,18 @@ shaka.util.StreamUtils = class {
|
|
|
451
488
|
* results for each variant.
|
|
452
489
|
* @param {!shaka.extern.Variant} variant
|
|
453
490
|
* @param {boolean} usePersistentLicenses
|
|
491
|
+
* @param {boolean} srcEquals
|
|
454
492
|
* @return {!Array.<!MediaDecodingConfiguration>}
|
|
455
493
|
* @private
|
|
456
494
|
*/
|
|
457
|
-
static getDecodingConfigs_(variant, usePersistentLicenses) {
|
|
495
|
+
static getDecodingConfigs_(variant, usePersistentLicenses, srcEquals) {
|
|
458
496
|
const audio = variant.audio;
|
|
459
497
|
const video = variant.video;
|
|
460
498
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
461
499
|
|
|
462
500
|
/** @type {!MediaDecodingConfiguration} */
|
|
463
501
|
const mediaDecodingConfig = {
|
|
464
|
-
type: 'media-source',
|
|
502
|
+
type: srcEquals ? 'file' : 'media-source',
|
|
465
503
|
};
|
|
466
504
|
|
|
467
505
|
if (video) {
|
|
@@ -498,14 +536,23 @@ shaka.util.StreamUtils = class {
|
|
|
498
536
|
// framerate must be greater than 0, otherwise the config is invalid.
|
|
499
537
|
framerate: video.frameRate || 1,
|
|
500
538
|
};
|
|
539
|
+
if (video.hdr) {
|
|
540
|
+
switch (video.hdr) {
|
|
541
|
+
case 'SDR':
|
|
542
|
+
mediaDecodingConfig.video.transferFunction = 'srgb';
|
|
543
|
+
break;
|
|
544
|
+
case 'PQ':
|
|
545
|
+
mediaDecodingConfig.video.transferFunction = 'pq';
|
|
546
|
+
break;
|
|
547
|
+
case 'HLG':
|
|
548
|
+
mediaDecodingConfig.video.transferFunction = 'hlg';
|
|
549
|
+
break;
|
|
550
|
+
}
|
|
551
|
+
}
|
|
501
552
|
}
|
|
502
553
|
if (audio) {
|
|
503
|
-
// Some Tizen devices seem to misreport AC-3 support, but correctly
|
|
504
|
-
// report EC-3 support. So query EC-3 as a fallback for AC-3.
|
|
505
|
-
// See https://github.com/google/shaka-player/issues/2989 for details.
|
|
506
554
|
const codecs =
|
|
507
|
-
(audio.codecs
|
|
508
|
-
shaka.util.Platform.isTizen()) ? 'ec-3' : audio.codecs;
|
|
555
|
+
shaka.util.StreamUtils.getCorrectAudioCodecs_(audio.codecs);
|
|
509
556
|
const fullType = shaka.util.MimeUtils.getFullOrConvertedType(
|
|
510
557
|
audio.mimeType, codecs, ContentType.AUDIO);
|
|
511
558
|
|
|
@@ -616,6 +663,26 @@ shaka.util.StreamUtils = class {
|
|
|
616
663
|
return configs;
|
|
617
664
|
}
|
|
618
665
|
|
|
666
|
+
|
|
667
|
+
/**
|
|
668
|
+
* Generates the correct audio codec for MediaDecodingConfiguration and
|
|
669
|
+
* for MediaSource.isTypeSupported.
|
|
670
|
+
* @param {string} codecs
|
|
671
|
+
* @return {string}
|
|
672
|
+
* @private
|
|
673
|
+
*/
|
|
674
|
+
static getCorrectAudioCodecs_(codecs) {
|
|
675
|
+
// Some Tizen devices seem to misreport AC-3 support, but correctly
|
|
676
|
+
// report EC-3 support. So query EC-3 as a fallback for AC-3.
|
|
677
|
+
// See https://github.com/google/shaka-player/issues/2989 for details.
|
|
678
|
+
if (shaka.util.Platform.isTizen()) {
|
|
679
|
+
return codecs.toLowerCase() == 'ac-3' ? 'ec-3' : codecs;
|
|
680
|
+
} else {
|
|
681
|
+
return codecs;
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
|
|
619
686
|
/**
|
|
620
687
|
* MediaCapabilities supports 'vp09...' codecs, but not 'vp9'. Translate vp9
|
|
621
688
|
* codec strings into 'vp09...', to allow such content to play with
|
package/lib/util/xml_utils.js
CHANGED
|
@@ -102,6 +102,25 @@ shaka.util.XmlUtils = class {
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
|
|
105
|
+
/**
|
|
106
|
+
* Gets a namespace-qualified attribute.
|
|
107
|
+
* @param {!Element} elem The element to get from.
|
|
108
|
+
* @param {!Array.<string>} nsList The lis of namespace URIs.
|
|
109
|
+
* @param {string} name The local name of the attribute.
|
|
110
|
+
* @return {?string} The attribute's value, or null if not present.
|
|
111
|
+
*/
|
|
112
|
+
static getAttributeNSList(elem, nsList, name) {
|
|
113
|
+
// Some browsers return the empty string when the attribute is missing,
|
|
114
|
+
// so check if it exists first. See: https://mzl.la/2L7F0UK
|
|
115
|
+
for (const ns of nsList) {
|
|
116
|
+
if (elem.hasAttributeNS(ns, name)) {
|
|
117
|
+
return elem.getAttributeNS(ns, name);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
|
|
105
124
|
/**
|
|
106
125
|
* Gets the text contents of a node.
|
|
107
126
|
* @param {!Node} elem The XML element.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "shaka-player",
|
|
3
3
|
"description": "DASH/EME video player library",
|
|
4
|
-
"version": "3.1.
|
|
4
|
+
"version": "3.1.6",
|
|
5
5
|
"homepage": "https://github.com/google/shaka-player",
|
|
6
6
|
"author": "Google",
|
|
7
7
|
"maintainers": [
|
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
}
|
|
12
12
|
],
|
|
13
13
|
"devDependencies": {
|
|
14
|
-
"@chiragrupani/karma-chromium-edge-launcher": "^2.1.0",
|
|
15
14
|
"@teppeis/clutz": "^1.0.29-4c95e12.v20190929",
|
|
16
15
|
"awesomplete": "^1.1.4",
|
|
17
16
|
"babel-core": "^6.26.3",
|
|
@@ -36,15 +35,12 @@
|
|
|
36
35
|
"jsdoc": "github:joeyparrish/jsdoc#45c271fa",
|
|
37
36
|
"karma": "^6.2.0",
|
|
38
37
|
"karma-babel-preprocessor": "^7.0.0",
|
|
39
|
-
"karma-chrome-launcher": "^2.2.0",
|
|
40
38
|
"karma-coverage": "^1.1.2",
|
|
41
|
-
"karma-edge-launcher": "^0.4.2",
|
|
42
|
-
"karma-firefox-launcher": "^1.1.0",
|
|
43
39
|
"karma-ie-launcher": "^1.0.0",
|
|
44
40
|
"karma-jasmine": "^3.3.1",
|
|
45
41
|
"karma-jasmine-ajax": "^0.1.13",
|
|
42
|
+
"karma-local-wd-launcher": "^1.4.1",
|
|
46
43
|
"karma-opera-launcher": "^1.0.0",
|
|
47
|
-
"karma-safari-launcher": "^1.0.0",
|
|
48
44
|
"karma-sourcemap-loader": "^0.3.7",
|
|
49
45
|
"karma-spec-reporter": "0.0.32",
|
|
50
46
|
"karma-webdriver-launcher": "^1.0.8",
|
|
@@ -55,6 +51,7 @@
|
|
|
55
51
|
"pwacompat": "^2.0.10",
|
|
56
52
|
"rimraf": "^2.6.3",
|
|
57
53
|
"sprintf-js": "^1.1.2",
|
|
54
|
+
"ssim.js": "^3.5.0",
|
|
58
55
|
"stylelint": "^13.8.0",
|
|
59
56
|
"stylelint-config-standard": "^20.0.0",
|
|
60
57
|
"tippy.js": "^4.3.1",
|
|
@@ -297,7 +297,8 @@ describe('CastProxy', () => {
|
|
|
297
297
|
'timeupdate', Util.spyFunc(proxyListener));
|
|
298
298
|
|
|
299
299
|
expect(proxyListener).not.toHaveBeenCalled();
|
|
300
|
-
const fakeEvent = new FakeEvent(
|
|
300
|
+
const fakeEvent = new FakeEvent(
|
|
301
|
+
'timeupdate', (new Map()).set('detail', 8675309));
|
|
301
302
|
mockVideo.on['timeupdate'](fakeEvent);
|
|
302
303
|
expect(proxyListener).toHaveBeenCalledWith(jasmine.objectContaining({
|
|
303
304
|
type: 'timeupdate',
|
|
@@ -315,7 +316,8 @@ describe('CastProxy', () => {
|
|
|
315
316
|
mockSender.hasRemoteProperties.and.returnValue(true);
|
|
316
317
|
|
|
317
318
|
expect(proxyListener).not.toHaveBeenCalled();
|
|
318
|
-
const fakeEvent = new FakeEvent(
|
|
319
|
+
const fakeEvent = new FakeEvent(
|
|
320
|
+
'timeupdate', (new Map()).set('detail', 8675309));
|
|
319
321
|
mockVideo.on['timeupdate'](fakeEvent);
|
|
320
322
|
expect(proxyListener).not.toHaveBeenCalled();
|
|
321
323
|
});
|
|
@@ -332,7 +334,8 @@ describe('CastProxy', () => {
|
|
|
332
334
|
mockSender.hasRemoteProperties.and.returnValue(true);
|
|
333
335
|
|
|
334
336
|
expect(proxyListener).not.toHaveBeenCalled();
|
|
335
|
-
const fakeEvent = new FakeEvent(
|
|
337
|
+
const fakeEvent = new FakeEvent(
|
|
338
|
+
'timeupdate', (new Map()).set('detail', 8675309));
|
|
336
339
|
mockSender.onRemoteEvent('video', fakeEvent);
|
|
337
340
|
expect(proxyListener).toHaveBeenCalledWith(jasmine.objectContaining({
|
|
338
341
|
type: 'timeupdate',
|
|
@@ -446,7 +449,8 @@ describe('CastProxy', () => {
|
|
|
446
449
|
'buffering', Util.spyFunc(proxyListener));
|
|
447
450
|
|
|
448
451
|
expect(proxyListener).not.toHaveBeenCalled();
|
|
449
|
-
const fakeEvent = new FakeEvent(
|
|
452
|
+
const fakeEvent = new FakeEvent(
|
|
453
|
+
'buffering', (new Map()).set('detail', 8675309));
|
|
450
454
|
mockPlayer.listeners['buffering'](fakeEvent);
|
|
451
455
|
expect(proxyListener).toHaveBeenCalledWith(jasmine.objectContaining({
|
|
452
456
|
type: 'buffering',
|
|
@@ -464,7 +468,8 @@ describe('CastProxy', () => {
|
|
|
464
468
|
mockSender.hasRemoteProperties.and.returnValue(true);
|
|
465
469
|
|
|
466
470
|
expect(proxyListener).not.toHaveBeenCalled();
|
|
467
|
-
const fakeEvent = new FakeEvent(
|
|
471
|
+
const fakeEvent = new FakeEvent(
|
|
472
|
+
'buffering', (new Map()).set('detail', 8675309));
|
|
468
473
|
mockPlayer.listeners['buffering'](fakeEvent);
|
|
469
474
|
expect(proxyListener).not.toHaveBeenCalled();
|
|
470
475
|
});
|
|
@@ -481,7 +486,8 @@ describe('CastProxy', () => {
|
|
|
481
486
|
mockSender.hasRemoteProperties.and.returnValue(true);
|
|
482
487
|
|
|
483
488
|
expect(proxyListener).not.toHaveBeenCalled();
|
|
484
|
-
const fakeEvent = new FakeEvent(
|
|
489
|
+
const fakeEvent = new FakeEvent(
|
|
490
|
+
'buffering', (new Map()).set('detail', 8675309));
|
|
485
491
|
mockSender.onRemoteEvent('player', fakeEvent);
|
|
486
492
|
expect(proxyListener).toHaveBeenCalledWith(jasmine.objectContaining({
|
|
487
493
|
type: 'buffering',
|
|
@@ -170,7 +170,7 @@ filterDescribe('CastReceiver', castReceiverIntegrationSupport, () => {
|
|
|
170
170
|
// Check that the update message is of a reasonable size. From previous
|
|
171
171
|
// testing we found that the socket would silently reject data that got
|
|
172
172
|
// too big. 6KB is safely below the limit.
|
|
173
|
-
expect(message.length).toBeLessThan(
|
|
173
|
+
expect(message.length).toBeLessThan(7 * 1024);
|
|
174
174
|
});
|
|
175
175
|
|
|
176
176
|
drmIt('has reasonable average message size', async () => {
|
|
@@ -122,7 +122,7 @@ describe('CastUtils', () => {
|
|
|
122
122
|
expect(typeof deserialized).toBe('object');
|
|
123
123
|
|
|
124
124
|
// The object can be used to construct a FakeEvent.
|
|
125
|
-
const fakeEvent =
|
|
125
|
+
const fakeEvent = FakeEvent.fromRealEvent(deserialized);
|
|
126
126
|
|
|
127
127
|
// The fake event has the same type and properties as the original.
|
|
128
128
|
const asObj = /** @type {!Object} */ (fakeEvent);
|
|
@@ -27,6 +27,32 @@ describe('Mp4CeaParser', () => {
|
|
|
27
27
|
ceaSegment = responses[1];
|
|
28
28
|
});
|
|
29
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Test only the functionality of removing EPB
|
|
32
|
+
* Expect removeEmu() to return the NALU with correct length
|
|
33
|
+
*
|
|
34
|
+
* Chromium VDA has a strict standard on NALU length
|
|
35
|
+
* It will complain about conformance if the array is malformed
|
|
36
|
+
*
|
|
37
|
+
* If EPB is removed by shifting bytes, it will return the original NALU
|
|
38
|
+
* length, which will fail this test
|
|
39
|
+
*
|
|
40
|
+
* Note that the CEA-608 packet in this test is incomplete
|
|
41
|
+
*/
|
|
42
|
+
it('parses CEA-608 SEI data from MP4 H.264 stream', () => {
|
|
43
|
+
const seiProcessor = new shaka.cea.SeiProcessor();
|
|
44
|
+
|
|
45
|
+
const cea608Packet = new Uint8Array([
|
|
46
|
+
0x00, 0x00, 0x03, // Emulation prevention byte
|
|
47
|
+
]);
|
|
48
|
+
|
|
49
|
+
const naluData = seiProcessor.removeEmu(cea608Packet);
|
|
50
|
+
expect(naluData).toBeDefined();
|
|
51
|
+
|
|
52
|
+
// EPB should be removed by returning new array, not by shifting bytes
|
|
53
|
+
expect(naluData.length).toBe(2);
|
|
54
|
+
});
|
|
55
|
+
|
|
30
56
|
it('parses cea data from mp4 stream', () => {
|
|
31
57
|
const cea708Parser = new shaka.cea.Mp4CeaParser();
|
|
32
58
|
|
|
@@ -1289,6 +1289,49 @@ describe('DashParser Live', () => {
|
|
|
1289
1289
|
.toHaveBeenCalledWith(
|
|
1290
1290
|
jasmine.objectContaining({id: '2', startTime: 10, endTime: 25}));
|
|
1291
1291
|
});
|
|
1292
|
+
|
|
1293
|
+
it('will not add timeline regions outside the DVR window', async () => {
|
|
1294
|
+
const manifest = [
|
|
1295
|
+
'<MPD type="dynamic" minimumUpdatePeriod="PT' + updateTime + 'S"',
|
|
1296
|
+
' availabilityStartTime="1970-01-01T00:00:00Z"',
|
|
1297
|
+
' suggestedPresentationDelay="PT0S"',
|
|
1298
|
+
' timeShiftBufferDepth="PT30S">',
|
|
1299
|
+
' <Period id="1">',
|
|
1300
|
+
' <EventStream schemeIdUri="http://example.com" timescale="1">',
|
|
1301
|
+
' <Event id="0" presentationTime="0" duration="10"/>',
|
|
1302
|
+
' <Event id="1" presentationTime="10" duration="10"/>',
|
|
1303
|
+
' <Event id="2" presentationTime="20" duration="10"/>',
|
|
1304
|
+
' <Event id="3" presentationTime="30" duration="10"/>',
|
|
1305
|
+
' <Event id="4" presentationTime="40" duration="10"/>',
|
|
1306
|
+
' </EventStream>',
|
|
1307
|
+
' <AdaptationSet mimeType="video/mp4">',
|
|
1308
|
+
' <Representation bandwidth="1">',
|
|
1309
|
+
' <SegmentTemplate startNumber="1" media="s$Number$.mp4"',
|
|
1310
|
+
' duration="2" />',
|
|
1311
|
+
' </Representation>',
|
|
1312
|
+
' </AdaptationSet>',
|
|
1313
|
+
' </Period>',
|
|
1314
|
+
'</MPD>',
|
|
1315
|
+
].join('\n');
|
|
1316
|
+
|
|
1317
|
+
// 45 seconds after the epoch, which is also 45 seconds after the
|
|
1318
|
+
// presentation started. Only the last 30 seconds are available, from
|
|
1319
|
+
// time 15 to 45.
|
|
1320
|
+
Date.now = () => 45 * 1000;
|
|
1321
|
+
|
|
1322
|
+
fakeNetEngine.setResponseText('dummy://foo', manifest);
|
|
1323
|
+
await parser.start('dummy://foo', playerInterface);
|
|
1324
|
+
|
|
1325
|
+
expect(onTimelineRegionAddedSpy).toHaveBeenCalledTimes(4);
|
|
1326
|
+
expect(onTimelineRegionAddedSpy).toHaveBeenCalledWith(
|
|
1327
|
+
jasmine.objectContaining({id: '1'}));
|
|
1328
|
+
expect(onTimelineRegionAddedSpy).toHaveBeenCalledWith(
|
|
1329
|
+
jasmine.objectContaining({id: '2'}));
|
|
1330
|
+
expect(onTimelineRegionAddedSpy).toHaveBeenCalledWith(
|
|
1331
|
+
jasmine.objectContaining({id: '3'}));
|
|
1332
|
+
expect(onTimelineRegionAddedSpy).toHaveBeenCalledWith(
|
|
1333
|
+
jasmine.objectContaining({id: '4'}));
|
|
1334
|
+
});
|
|
1292
1335
|
});
|
|
1293
1336
|
|
|
1294
1337
|
it('honors clockSyncUri for in-progress recordings', async () => {
|
|
@@ -38,6 +38,14 @@ describe('DashParser Manifest', () => {
|
|
|
38
38
|
|
|
39
39
|
/** @type {!string} */
|
|
40
40
|
const thumbnailScheme = 'http://dashif.org/guidelines/thumbnail_tile';
|
|
41
|
+
/**
|
|
42
|
+
* CICP scheme. parameter must be one of the following: "ColourPrimaries",
|
|
43
|
+
* "TransferCharacteristics", or "MatrixCoefficients".
|
|
44
|
+
*
|
|
45
|
+
* @param {string} parameter
|
|
46
|
+
* @return {string}
|
|
47
|
+
*/
|
|
48
|
+
const cicpScheme = (parameter) => `urn:mpeg:mpegB:cicp:${parameter}`;
|
|
41
49
|
|
|
42
50
|
beforeAll(async () => {
|
|
43
51
|
mp4Index = await shaka.test.Util.fetch(mp4IndexSegmentUri);
|
|
@@ -975,7 +983,8 @@ describe('DashParser Manifest', () => {
|
|
|
975
983
|
|
|
976
984
|
it('duplicate Representation ids with live', async () => {
|
|
977
985
|
const source = [
|
|
978
|
-
'<MPD minBufferTime="PT75S" type="dynamic"
|
|
986
|
+
'<MPD minBufferTime="PT75S" type="dynamic"',
|
|
987
|
+
' availabilityStartTime="1970-01-01T00:00:00Z">',
|
|
979
988
|
' <Period id="1" duration="PT30S">',
|
|
980
989
|
' <AdaptationSet mimeType="video/mp4">',
|
|
981
990
|
' <Representation id="1" bandwidth="1">',
|
|
@@ -1822,7 +1831,7 @@ describe('DashParser Manifest', () => {
|
|
|
1822
1831
|
expect(textStream.kind).toBe('caption');
|
|
1823
1832
|
});
|
|
1824
1833
|
|
|
1825
|
-
it('converts Roles element to "forced"', async () => {
|
|
1834
|
+
it('converts Roles element to "forced" (old role)', async () => {
|
|
1826
1835
|
const manifestText = [
|
|
1827
1836
|
'<MPD minBufferTime="PT75S">',
|
|
1828
1837
|
' <Period id="1" duration="PT30S">',
|
|
@@ -1854,7 +1863,40 @@ describe('DashParser Manifest', () => {
|
|
|
1854
1863
|
expect(textStream.forced).toBe(true);
|
|
1855
1864
|
});
|
|
1856
1865
|
|
|
1857
|
-
it('
|
|
1866
|
+
it('converts Roles element to "forced"', async () => {
|
|
1867
|
+
const manifestText = [
|
|
1868
|
+
'<MPD minBufferTime="PT75S">',
|
|
1869
|
+
' <Period id="1" duration="PT30S">',
|
|
1870
|
+
' <AdaptationSet id="1" contentType="text">',
|
|
1871
|
+
' <Accessibility schemeIdUri="urn:mpeg:dash:role:2011" ',
|
|
1872
|
+
' value="captions" />',
|
|
1873
|
+
' <Accessibility schemeIdUri="urn:mpeg:dash:role:2011" ',
|
|
1874
|
+
' value="forced-subtitle" />',
|
|
1875
|
+
' <Accessibility schemeIdUri="foobar" value="bar" />',
|
|
1876
|
+
' <Representation id="text-en" mimeType="text/webvtt">',
|
|
1877
|
+
' <BaseURL>t-en.vtt</BaseURL>',
|
|
1878
|
+
' </Representation>',
|
|
1879
|
+
' </AdaptationSet>',
|
|
1880
|
+
' <AdaptationSet id="1" mimeType="video/mp4">',
|
|
1881
|
+
' <Representation id="video-sd" width="640" height="480">',
|
|
1882
|
+
' <BaseURL>v-sd.mp4</BaseURL>',
|
|
1883
|
+
' <SegmentBase indexRange="100-200" />',
|
|
1884
|
+
' </Representation>',
|
|
1885
|
+
' </AdaptationSet>',
|
|
1886
|
+
' </Period>',
|
|
1887
|
+
'</MPD>',
|
|
1888
|
+
].join('\n');
|
|
1889
|
+
|
|
1890
|
+
fakeNetEngine.setResponseText('dummy://foo', manifestText);
|
|
1891
|
+
/** @type {shaka.extern.Manifest} */
|
|
1892
|
+
const manifest = await parser.start('dummy://foo', playerInterface);
|
|
1893
|
+
const textStream = manifest.textStreams[0];
|
|
1894
|
+
expect(textStream.roles).toEqual(['captions', 'forced-subtitle']);
|
|
1895
|
+
expect(textStream.forced).toBe(true);
|
|
1896
|
+
});
|
|
1897
|
+
|
|
1898
|
+
it('supports HDR signaling via profiles', async () => {
|
|
1899
|
+
// (DASH-IF IOP v4.3 10.3.3.)
|
|
1858
1900
|
const hdrProfile =
|
|
1859
1901
|
'http://dashif.org/guidelines/dash-if-uhd#hevc-hdr-pq10';
|
|
1860
1902
|
const manifestText = [
|
|
@@ -1885,6 +1927,110 @@ describe('DashParser Manifest', () => {
|
|
|
1885
1927
|
expect(stream.hdr).toBe('PQ');
|
|
1886
1928
|
});
|
|
1887
1929
|
|
|
1930
|
+
it('supports HDR signaling via SupplementalProperty', async () => {
|
|
1931
|
+
// (DASH-IF IOP v4.3 6.2.5.1.)
|
|
1932
|
+
const hdrScheme = cicpScheme('TransferCharacteristics');
|
|
1933
|
+
const pq = 16;
|
|
1934
|
+
const manifestText = [
|
|
1935
|
+
'<MPD minBufferTime="PT75S">',
|
|
1936
|
+
' <Period id="1" duration="PT30S">',
|
|
1937
|
+
' <AdaptationSet id="2" mimeType="video/mp4">',
|
|
1938
|
+
` <SupplementalProperty schemeIdUri="${hdrScheme}" value="${pq}" />`,
|
|
1939
|
+
' <Representation codecs="hvc1.2.4.L150.90">',
|
|
1940
|
+
' <BaseURL>v-sd.mp4</BaseURL>',
|
|
1941
|
+
' <SegmentBase indexRange="100-200" />',
|
|
1942
|
+
' </Representation>',
|
|
1943
|
+
' </AdaptationSet>',
|
|
1944
|
+
' <AdaptationSet id="3" mimeType="audio/mp4">',
|
|
1945
|
+
' <Representation id="audio-en">',
|
|
1946
|
+
' <BaseURL>a-en.mp4</BaseURL>',
|
|
1947
|
+
' <SegmentBase indexRange="100-200" />',
|
|
1948
|
+
' </Representation>',
|
|
1949
|
+
' </AdaptationSet>',
|
|
1950
|
+
' </Period>',
|
|
1951
|
+
'</MPD>',
|
|
1952
|
+
].join('\n');
|
|
1953
|
+
|
|
1954
|
+
fakeNetEngine.setResponseText('dummy://foo', manifestText);
|
|
1955
|
+
|
|
1956
|
+
/** @type {shaka.extern.Manifest} */
|
|
1957
|
+
const manifest = await parser.start('dummy://foo', playerInterface);
|
|
1958
|
+
expect(manifest.variants.length).toBe(1);
|
|
1959
|
+
const stream = manifest.variants[0].video;
|
|
1960
|
+
expect(stream.hdr).toBe('PQ');
|
|
1961
|
+
});
|
|
1962
|
+
|
|
1963
|
+
it('supports HDR signaling via EssentialProperty', async () => {
|
|
1964
|
+
// (DASH-IF IOP v4.3 6.2.5.1.)
|
|
1965
|
+
const hdrScheme = cicpScheme('TransferCharacteristics');
|
|
1966
|
+
const hlg = 18;
|
|
1967
|
+
const manifestText = [
|
|
1968
|
+
'<MPD minBufferTime="PT75S">',
|
|
1969
|
+
' <Period id="1" duration="PT30S">',
|
|
1970
|
+
' <AdaptationSet id="2" mimeType="video/mp4">',
|
|
1971
|
+
` <EssentialProperty schemeIdUri="${hdrScheme}" value="${hlg}" />`,
|
|
1972
|
+
' <Representation codecs="hvc1.2.4.L153.B0">',
|
|
1973
|
+
' <BaseURL>v-sd.mp4</BaseURL>',
|
|
1974
|
+
' <SegmentBase indexRange="100-200" />',
|
|
1975
|
+
' </Representation>',
|
|
1976
|
+
' </AdaptationSet>',
|
|
1977
|
+
' <AdaptationSet id="3" mimeType="audio/mp4">',
|
|
1978
|
+
' <Representation id="audio-en">',
|
|
1979
|
+
' <BaseURL>a-en.mp4</BaseURL>',
|
|
1980
|
+
' <SegmentBase indexRange="100-200" />',
|
|
1981
|
+
' </Representation>',
|
|
1982
|
+
' </AdaptationSet>',
|
|
1983
|
+
' </Period>',
|
|
1984
|
+
'</MPD>',
|
|
1985
|
+
].join('\n');
|
|
1986
|
+
|
|
1987
|
+
fakeNetEngine.setResponseText('dummy://foo', manifestText);
|
|
1988
|
+
|
|
1989
|
+
/** @type {shaka.extern.Manifest} */
|
|
1990
|
+
const manifest = await parser.start('dummy://foo', playerInterface);
|
|
1991
|
+
expect(manifest.variants.length).toBe(1);
|
|
1992
|
+
const stream = manifest.variants[0].video;
|
|
1993
|
+
expect(stream.hdr).toBe('HLG');
|
|
1994
|
+
});
|
|
1995
|
+
|
|
1996
|
+
it('supports SDR signalling via EssentialProperty', async () => {
|
|
1997
|
+
// (DASH-IF IOP v4.3 6.2.5.1.)
|
|
1998
|
+
const scheme = cicpScheme('TransferCharacteristics');
|
|
1999
|
+
const sdrValues = [1, 6, 13, 14, 15];
|
|
2000
|
+
const manifestPromises = [];
|
|
2001
|
+
for (const value of sdrValues) {
|
|
2002
|
+
const manifestText = [
|
|
2003
|
+
'<MPD minBufferTime="PT75S">',
|
|
2004
|
+
' <Period id="1" duration="PT30S">',
|
|
2005
|
+
' <AdaptationSet id="2" mimeType="video/mp4">',
|
|
2006
|
+
` <EssentialProperty schemeIdUri="${scheme}" value="${value}" />`,
|
|
2007
|
+
' <Representation codecs="avc1.640028">',
|
|
2008
|
+
' <BaseURL>v-sd.mp4</BaseURL>',
|
|
2009
|
+
' <SegmentBase indexRange="100-200" />',
|
|
2010
|
+
' </Representation>',
|
|
2011
|
+
' </AdaptationSet>',
|
|
2012
|
+
' <AdaptationSet id="3" mimeType="audio/mp4">',
|
|
2013
|
+
' <Representation id="audio-en">',
|
|
2014
|
+
' <BaseURL>a-en.mp4</BaseURL>',
|
|
2015
|
+
' <SegmentBase indexRange="100-200" />',
|
|
2016
|
+
' </Representation>',
|
|
2017
|
+
' </AdaptationSet>',
|
|
2018
|
+
' </Period>',
|
|
2019
|
+
'</MPD>',
|
|
2020
|
+
].join('\n');
|
|
2021
|
+
|
|
2022
|
+
fakeNetEngine.setResponseText('dummy://foo', manifestText);
|
|
2023
|
+
|
|
2024
|
+
manifestPromises.push(parser.start('dummy://foo', playerInterface));
|
|
2025
|
+
}
|
|
2026
|
+
const manifests = await Promise.all(manifestPromises);
|
|
2027
|
+
for (const manifest of manifests) {
|
|
2028
|
+
expect(manifest.variants.length).toBe(1);
|
|
2029
|
+
const stream = manifest.variants[0].video;
|
|
2030
|
+
expect(stream.hdr).toBe('SDR');
|
|
2031
|
+
}
|
|
2032
|
+
});
|
|
2033
|
+
|
|
1888
2034
|
it('Does not error when image adaptation sets are present', async () => {
|
|
1889
2035
|
const manifestText = [
|
|
1890
2036
|
'<MPD minBufferTime="PT75S">',
|
|
@@ -2108,4 +2254,77 @@ describe('DashParser Manifest', () => {
|
|
|
2108
2254
|
expect(uri).not.toContain('/p2/');
|
|
2109
2255
|
}
|
|
2110
2256
|
});
|
|
2257
|
+
|
|
2258
|
+
/**
|
|
2259
|
+
* @param {!Array.<number>} periods Start time of multiple periods
|
|
2260
|
+
* @return {string}
|
|
2261
|
+
*/
|
|
2262
|
+
function buildManifestWithPeriodStartTime(periods) {
|
|
2263
|
+
const mpdTemplate = [
|
|
2264
|
+
`<MPD type="dynamic"`,
|
|
2265
|
+
'availabilityStartTime="1970-01-01T00:00:00Z"',
|
|
2266
|
+
'timeShiftBufferDepth="PT10H">',
|
|
2267
|
+
' %(periods)s',
|
|
2268
|
+
'</MPD>',
|
|
2269
|
+
].join('\n');
|
|
2270
|
+
const periodTemplate = (id, period, duration) => {
|
|
2271
|
+
return [
|
|
2272
|
+
` <Period id="${id}" start="PT${period}S">`,
|
|
2273
|
+
' <AdaptationSet mimeType="video/mp4" lang="en" group="1">',
|
|
2274
|
+
' <SegmentTemplate startNumber="1" media="l-$Number$.mp4">',
|
|
2275
|
+
' <SegmentTimeline>',
|
|
2276
|
+
` <S t="0" d="${duration}" />`,
|
|
2277
|
+
' </SegmentTimeline>',
|
|
2278
|
+
' </SegmentTemplate>',
|
|
2279
|
+
' <Representation id="1"/>',
|
|
2280
|
+
' </AdaptationSet>',
|
|
2281
|
+
' </Period>',
|
|
2282
|
+
].join('\n');
|
|
2283
|
+
};
|
|
2284
|
+
const periodXmls = periods.map((period, i) => {
|
|
2285
|
+
const duration = i+1 === periods.length ? 10 : periods[i+1] - period;
|
|
2286
|
+
// Period start time as ID here. If we use index then there will be
|
|
2287
|
+
// periods with same period ID and different start time which are invalid.
|
|
2288
|
+
return periodTemplate(period, period, duration);
|
|
2289
|
+
});
|
|
2290
|
+
return sprintf(mpdTemplate, {
|
|
2291
|
+
periods: periodXmls.join('\n'),
|
|
2292
|
+
});
|
|
2293
|
+
}
|
|
2294
|
+
|
|
2295
|
+
// Bug description: Inconsistent period start time in the manifests due
|
|
2296
|
+
// to failover triggered in backend servers
|
|
2297
|
+
|
|
2298
|
+
// When one of the servers is down, the manifest will be served by other
|
|
2299
|
+
// redundant servers. The period start time might become out of sync
|
|
2300
|
+
// during the switch-over/recovery.
|
|
2301
|
+
|
|
2302
|
+
it('skip periods that are earlier than max period start time', async () => {
|
|
2303
|
+
const sources = [
|
|
2304
|
+
buildManifestWithPeriodStartTime([5, 15]),
|
|
2305
|
+
buildManifestWithPeriodStartTime([6, 15]), // simulate out-of-sync of +1s
|
|
2306
|
+
buildManifestWithPeriodStartTime([4, 15]), // simulate out-of-sync of -1s
|
|
2307
|
+
];
|
|
2308
|
+
const segments = [];
|
|
2309
|
+
|
|
2310
|
+
for (const source of sources) {
|
|
2311
|
+
fakeNetEngine.setResponseText('dummy://foo', source);
|
|
2312
|
+
/** @type {shaka.extern.Manifest} */
|
|
2313
|
+
// eslint-disable-next-line no-await-in-loop
|
|
2314
|
+
const manifest = await parser.start('dummy://foo', playerInterface);
|
|
2315
|
+
const video = manifest.variants[0].video;
|
|
2316
|
+
// eslint-disable-next-line no-await-in-loop
|
|
2317
|
+
await video.createSegmentIndex();
|
|
2318
|
+
goog.asserts.assert(video.segmentIndex, 'Null segmentIndex!');
|
|
2319
|
+
segments.push(Array.from(video.segmentIndex));
|
|
2320
|
+
}
|
|
2321
|
+
|
|
2322
|
+
// Expect identical segments
|
|
2323
|
+
expect(segments[0].length).toBe(2);
|
|
2324
|
+
expect(segments[1].length).toBe(2);
|
|
2325
|
+
expect(segments[0][0].startTime).toBe(5);
|
|
2326
|
+
expect(segments[1][0].startTime).toBe(5);
|
|
2327
|
+
expect(segments[0][1].startTime).toBe(15);
|
|
2328
|
+
expect(segments[1][1].startTime).toBe(15);
|
|
2329
|
+
});
|
|
2111
2330
|
});
|