shaka-player 3.2.0 → 3.2.4
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 +182 -1
- package/CONTRIBUTORS +3 -0
- package/README.md +4 -4
- 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/demo/index.html +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 +8 -8
- package/dist/locales.js +37 -37
- package/dist/receiver.compiled.debug.js +1 -1
- package/dist/receiver.compiled.js +1 -1
- package/dist/shaka-player.compiled.d.ts +195 -5
- package/dist/shaka-player.compiled.debug.d.ts +195 -5
- package/dist/shaka-player.compiled.debug.externs.js +226 -14
- package/dist/shaka-player.compiled.debug.js +424 -399
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +226 -14
- package/dist/shaka-player.compiled.js +826 -812
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +197 -16
- package/dist/shaka-player.ui.debug.d.ts +197 -16
- package/dist/shaka-player.ui.debug.externs.js +230 -27
- package/dist/shaka-player.ui.debug.js +466 -442
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +230 -27
- package/dist/shaka-player.ui.js +946 -932
- package/dist/shaka-player.ui.map +4 -4
- package/docs/tutorials/fairplay.md +1 -1
- package/docs/tutorials/faq.md +14 -0
- package/docs/tutorials/ui.md +5 -5
- package/externs/ima.js +4 -0
- package/externs/shaka/ads.js +25 -0
- package/externs/shaka/player.js +8 -0
- package/externs/shaka/text.js +10 -0
- package/karma.conf.js +20 -15
- package/lib/ads/ad_manager.js +46 -26
- package/lib/ads/client_side_ad_manager.js +82 -88
- package/lib/ads/server_side_ad_manager.js +45 -13
- 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 +74 -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/segment_index.js +2 -1
- package/lib/media/segment_reference.js +19 -1
- package/lib/media/streaming_engine.js +8 -4
- package/lib/net/networking_engine.js +6 -1
- package/lib/offline/download_manager.js +1 -2
- package/lib/offline/storage.js +8 -9
- package/lib/player.js +129 -59
- package/lib/polyfill/aria.js +12 -2
- 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/text/web_vtt_generator.js +21 -12
- 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 +105 -13
- package/lib/util/xml_utils.js +50 -12
- package/package.json +3 -6
- package/roadmap.md +24 -22
- 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_integration.js +86 -2
- package/test/media/drm_engine_unit.js +57 -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 +79 -8
- package/test/player_src_equals_integration.js +41 -8
- package/test/player_unit.js +14 -4
- 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/chapters.vtt +2 -2
- package/test/test/assets/chapters2.vtt +10 -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 +11 -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/text/web_vtt_generator_unit.js +58 -3
- 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 +60 -3
- 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/seek_bar.js +1 -2
- package/ui/text_selection.js +1 -0
package/lib/util/platform.js
CHANGED
|
@@ -163,7 +163,6 @@ shaka.util.Platform = class {
|
|
|
163
163
|
!shaka.util.Platform.isEdge();
|
|
164
164
|
}
|
|
165
165
|
|
|
166
|
-
|
|
167
166
|
/**
|
|
168
167
|
* Check if the current platform is from Apple.
|
|
169
168
|
*
|
|
@@ -179,6 +178,19 @@ shaka.util.Platform = class {
|
|
|
179
178
|
&& !shaka.util.Platform.isTizen();
|
|
180
179
|
}
|
|
181
180
|
|
|
181
|
+
/**
|
|
182
|
+
* Check if the current platform is Playstation 5.
|
|
183
|
+
*
|
|
184
|
+
* Returns true on Playstation 5 browsers.
|
|
185
|
+
*
|
|
186
|
+
* Returns false for Playstation 5 browsers
|
|
187
|
+
*
|
|
188
|
+
* @return {boolean}
|
|
189
|
+
*/
|
|
190
|
+
static isPS5() {
|
|
191
|
+
return shaka.util.Platform.userAgentContains_('PlayStation 5');
|
|
192
|
+
}
|
|
193
|
+
|
|
182
194
|
/**
|
|
183
195
|
* Returns a major version number for Safari, or Safari-based iOS browsers.
|
|
184
196
|
*
|
package/lib/util/stream_utils.js
CHANGED
|
@@ -423,9 +423,44 @@ shaka.util.StreamUtils = class {
|
|
|
423
423
|
'MediaCapabilities should be valid.');
|
|
424
424
|
|
|
425
425
|
await shaka.util.StreamUtils.getDecodingInfosForVariants(
|
|
426
|
-
manifest.variants, usePersistentLicenses);
|
|
426
|
+
manifest.variants, usePersistentLicenses, /* srcEquals= */ false);
|
|
427
427
|
manifest.variants = manifest.variants.filter((variant) => {
|
|
428
|
+
// See: https://github.com/google/shaka-player/issues/3860
|
|
428
429
|
const video = variant.video;
|
|
430
|
+
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
431
|
+
if (video) {
|
|
432
|
+
let videoCodecs = shaka.util.StreamUtils.patchVp9(video.codecs);
|
|
433
|
+
// For multiplexed streams. Here we must check the audio of the
|
|
434
|
+
// stream to see if it is compatible.
|
|
435
|
+
if (video.codecs.includes(',')) {
|
|
436
|
+
const allCodecs = video.codecs.split(',');
|
|
437
|
+
videoCodecs = shaka.util.ManifestParserUtils.guessCodecs(
|
|
438
|
+
ContentType.VIDEO, allCodecs);
|
|
439
|
+
const audioCodecs = shaka.util.ManifestParserUtils.guessCodecs(
|
|
440
|
+
ContentType.AUDIO, allCodecs);
|
|
441
|
+
const audioFullType = shaka.util.MimeUtils.getFullOrConvertedType(
|
|
442
|
+
video.mimeType, audioCodecs, ContentType.AUDIO);
|
|
443
|
+
if (!MediaSource.isTypeSupported(audioFullType)) {
|
|
444
|
+
return false;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
const fullType = shaka.util.MimeUtils.getFullOrConvertedType(
|
|
448
|
+
video.mimeType, videoCodecs, ContentType.VIDEO);
|
|
449
|
+
if (!MediaSource.isTypeSupported(fullType)) {
|
|
450
|
+
return false;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
const audio = variant.audio;
|
|
454
|
+
if (audio) {
|
|
455
|
+
const codecs =
|
|
456
|
+
shaka.util.StreamUtils.getCorrectAudioCodecs_(audio.codecs);
|
|
457
|
+
const fullType = shaka.util.MimeUtils.getFullOrConvertedType(
|
|
458
|
+
audio.mimeType, codecs, ContentType.AUDIO);
|
|
459
|
+
if (!MediaSource.isTypeSupported(fullType)) {
|
|
460
|
+
return false;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
429
464
|
// See: https://github.com/google/shaka-player/issues/3380
|
|
430
465
|
if (shaka.util.Platform.isXboxOne() && video &&
|
|
431
466
|
((video.width && video.width > 1920) ||
|
|
@@ -456,9 +491,11 @@ shaka.util.StreamUtils = class {
|
|
|
456
491
|
*
|
|
457
492
|
* @param {!Array.<shaka.extern.Variant>} variants
|
|
458
493
|
* @param {boolean} usePersistentLicenses
|
|
494
|
+
* @param {boolean} srcEquals
|
|
459
495
|
* @exportDoc
|
|
460
496
|
*/
|
|
461
|
-
static async getDecodingInfosForVariants(variants, usePersistentLicenses
|
|
497
|
+
static async getDecodingInfosForVariants(variants, usePersistentLicenses,
|
|
498
|
+
srcEquals) {
|
|
462
499
|
const gotDecodingInfo = variants.some((variant) =>
|
|
463
500
|
variant.decodingInfos.length);
|
|
464
501
|
if (gotDecodingInfo) {
|
|
@@ -482,7 +519,7 @@ shaka.util.StreamUtils = class {
|
|
|
482
519
|
for (const variant of variants) {
|
|
483
520
|
/** @type {!Array.<!MediaDecodingConfiguration>} */
|
|
484
521
|
const decodingConfigs = shaka.util.StreamUtils.getDecodingConfigs_(
|
|
485
|
-
variant, usePersistentLicenses);
|
|
522
|
+
variant, usePersistentLicenses, srcEquals);
|
|
486
523
|
|
|
487
524
|
for (const config of decodingConfigs) {
|
|
488
525
|
operations.push(getVariantDecodingInfos(variant, config));
|
|
@@ -497,17 +534,18 @@ shaka.util.StreamUtils = class {
|
|
|
497
534
|
* results for each variant.
|
|
498
535
|
* @param {!shaka.extern.Variant} variant
|
|
499
536
|
* @param {boolean} usePersistentLicenses
|
|
537
|
+
* @param {boolean} srcEquals
|
|
500
538
|
* @return {!Array.<!MediaDecodingConfiguration>}
|
|
501
539
|
* @private
|
|
502
540
|
*/
|
|
503
|
-
static getDecodingConfigs_(variant, usePersistentLicenses) {
|
|
541
|
+
static getDecodingConfigs_(variant, usePersistentLicenses, srcEquals) {
|
|
504
542
|
const audio = variant.audio;
|
|
505
543
|
const video = variant.video;
|
|
506
544
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
507
545
|
|
|
508
546
|
/** @type {!MediaDecodingConfiguration} */
|
|
509
547
|
const mediaDecodingConfig = {
|
|
510
|
-
type: 'media-source',
|
|
548
|
+
type: srcEquals ? 'file' : 'media-source',
|
|
511
549
|
};
|
|
512
550
|
|
|
513
551
|
if (video) {
|
|
@@ -544,14 +582,23 @@ shaka.util.StreamUtils = class {
|
|
|
544
582
|
// framerate must be greater than 0, otherwise the config is invalid.
|
|
545
583
|
framerate: video.frameRate || 1,
|
|
546
584
|
};
|
|
585
|
+
if (video.hdr) {
|
|
586
|
+
switch (video.hdr) {
|
|
587
|
+
case 'SDR':
|
|
588
|
+
mediaDecodingConfig.video.transferFunction = 'srgb';
|
|
589
|
+
break;
|
|
590
|
+
case 'PQ':
|
|
591
|
+
mediaDecodingConfig.video.transferFunction = 'pq';
|
|
592
|
+
break;
|
|
593
|
+
case 'HLG':
|
|
594
|
+
mediaDecodingConfig.video.transferFunction = 'hlg';
|
|
595
|
+
break;
|
|
596
|
+
}
|
|
597
|
+
}
|
|
547
598
|
}
|
|
548
599
|
if (audio) {
|
|
549
|
-
// Some Tizen devices seem to misreport AC-3 support, but correctly
|
|
550
|
-
// report EC-3 support. So query EC-3 as a fallback for AC-3.
|
|
551
|
-
// See https://github.com/google/shaka-player/issues/2989 for details.
|
|
552
600
|
const codecs =
|
|
553
|
-
(audio.codecs
|
|
554
|
-
shaka.util.Platform.isTizen()) ? 'ec-3' : audio.codecs;
|
|
601
|
+
shaka.util.StreamUtils.getCorrectAudioCodecs_(audio.codecs);
|
|
555
602
|
const fullType = shaka.util.MimeUtils.getFullOrConvertedType(
|
|
556
603
|
audio.mimeType, codecs, ContentType.AUDIO);
|
|
557
604
|
|
|
@@ -662,6 +709,26 @@ shaka.util.StreamUtils = class {
|
|
|
662
709
|
return configs;
|
|
663
710
|
}
|
|
664
711
|
|
|
712
|
+
|
|
713
|
+
/**
|
|
714
|
+
* Generates the correct audio codec for MediaDecodingConfiguration and
|
|
715
|
+
* for MediaSource.isTypeSupported.
|
|
716
|
+
* @param {string} codecs
|
|
717
|
+
* @return {string}
|
|
718
|
+
* @private
|
|
719
|
+
*/
|
|
720
|
+
static getCorrectAudioCodecs_(codecs) {
|
|
721
|
+
// Some Tizen devices seem to misreport AC-3 support, but correctly
|
|
722
|
+
// report EC-3 support. So query EC-3 as a fallback for AC-3.
|
|
723
|
+
// See https://github.com/google/shaka-player/issues/2989 for details.
|
|
724
|
+
if (shaka.util.Platform.isTizen()) {
|
|
725
|
+
return codecs.toLowerCase() == 'ac-3' ? 'ec-3' : codecs;
|
|
726
|
+
} else {
|
|
727
|
+
return codecs;
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
|
|
665
732
|
/**
|
|
666
733
|
* MediaCapabilities supports 'vp09...' codecs, but not 'vp9'. Translate vp9
|
|
667
734
|
* codec strings into 'vp09...', to allow such content to play with
|
|
@@ -973,6 +1040,31 @@ shaka.util.StreamUtils = class {
|
|
|
973
1040
|
static imageStreamToTrack(stream) {
|
|
974
1041
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
975
1042
|
|
|
1043
|
+
let width = stream.width || null;
|
|
1044
|
+
let height = stream.height || null;
|
|
1045
|
+
|
|
1046
|
+
// The stream width and height represent the size of the entire thumbnail
|
|
1047
|
+
// sheet, so divide by the layout.
|
|
1048
|
+
let reference = null;
|
|
1049
|
+
// Note: segmentIndex is built by default for HLS, but not for DASH, but
|
|
1050
|
+
// in DASH this information comes at the stream level and not at the
|
|
1051
|
+
// segment level.
|
|
1052
|
+
if (stream.segmentIndex) {
|
|
1053
|
+
reference = stream.segmentIndex.get(0);
|
|
1054
|
+
}
|
|
1055
|
+
let layout = stream.tilesLayout;
|
|
1056
|
+
if (reference) {
|
|
1057
|
+
layout = reference.getTilesLayout() || layout;
|
|
1058
|
+
}
|
|
1059
|
+
if (layout && width != null) {
|
|
1060
|
+
width /= Number(layout.split('x')[0]);
|
|
1061
|
+
}
|
|
1062
|
+
if (layout && height != null) {
|
|
1063
|
+
height /= Number(layout.split('x')[1]);
|
|
1064
|
+
}
|
|
1065
|
+
// TODO: What happens if there are multiple grids, with different
|
|
1066
|
+
// layout sizes, inside this image stream?
|
|
1067
|
+
|
|
976
1068
|
/** @type {shaka.extern.Track} */
|
|
977
1069
|
const track = {
|
|
978
1070
|
id: stream.id,
|
|
@@ -982,8 +1074,8 @@ shaka.util.StreamUtils = class {
|
|
|
982
1074
|
language: '',
|
|
983
1075
|
label: null,
|
|
984
1076
|
kind: null,
|
|
985
|
-
width
|
|
986
|
-
height
|
|
1077
|
+
width,
|
|
1078
|
+
height,
|
|
987
1079
|
frameRate: null,
|
|
988
1080
|
pixelAspectRatio: null,
|
|
989
1081
|
hdr: null,
|
|
@@ -1000,7 +1092,7 @@ shaka.util.StreamUtils = class {
|
|
|
1000
1092
|
channelsCount: null,
|
|
1001
1093
|
audioSamplingRate: null,
|
|
1002
1094
|
spatialAudio: false,
|
|
1003
|
-
tilesLayout:
|
|
1095
|
+
tilesLayout: layout || null,
|
|
1004
1096
|
audioBandwidth: null,
|
|
1005
1097
|
videoBandwidth: null,
|
|
1006
1098
|
originalVideoId: null,
|
package/lib/util/xml_utils.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
goog.provide('shaka.util.XmlUtils');
|
|
8
8
|
|
|
9
|
+
goog.require('goog.asserts');
|
|
9
10
|
goog.require('shaka.log');
|
|
10
11
|
goog.require('shaka.util.StringUtils');
|
|
11
12
|
|
|
@@ -102,6 +103,25 @@ shaka.util.XmlUtils = class {
|
|
|
102
103
|
}
|
|
103
104
|
|
|
104
105
|
|
|
106
|
+
/**
|
|
107
|
+
* Gets a namespace-qualified attribute.
|
|
108
|
+
* @param {!Element} elem The element to get from.
|
|
109
|
+
* @param {!Array.<string>} nsList The lis of namespace URIs.
|
|
110
|
+
* @param {string} name The local name of the attribute.
|
|
111
|
+
* @return {?string} The attribute's value, or null if not present.
|
|
112
|
+
*/
|
|
113
|
+
static getAttributeNSList(elem, nsList, name) {
|
|
114
|
+
// Some browsers return the empty string when the attribute is missing,
|
|
115
|
+
// so check if it exists first. See: https://mzl.la/2L7F0UK
|
|
116
|
+
for (const ns of nsList) {
|
|
117
|
+
if (elem.hasAttributeNS(ns, name)) {
|
|
118
|
+
return elem.getAttributeNS(ns, name);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
|
|
105
125
|
/**
|
|
106
126
|
* Gets the text contents of a node.
|
|
107
127
|
* @param {!Node} elem The XML element.
|
|
@@ -312,29 +332,46 @@ shaka.util.XmlUtils = class {
|
|
|
312
332
|
|
|
313
333
|
|
|
314
334
|
/**
|
|
315
|
-
* Parse a string and return the resulting root element if
|
|
316
|
-
*
|
|
335
|
+
* Parse a string and return the resulting root element if it was valid XML.
|
|
336
|
+
*
|
|
317
337
|
* @param {string} xmlString
|
|
318
338
|
* @param {string} expectedRootElemName
|
|
319
339
|
* @return {Element}
|
|
320
340
|
*/
|
|
321
341
|
static parseXmlString(xmlString, expectedRootElemName) {
|
|
322
342
|
const parser = new DOMParser();
|
|
323
|
-
let rootElem = null;
|
|
324
343
|
let xml = null;
|
|
325
344
|
try {
|
|
326
345
|
xml = parser.parseFromString(xmlString, 'text/xml');
|
|
327
|
-
} catch (exception) {
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
// same type as the element linking.
|
|
331
|
-
if (xml.documentElement.tagName == expectedRootElemName) {
|
|
332
|
-
rootElem = xml.documentElement;
|
|
333
|
-
}
|
|
346
|
+
} catch (exception) {
|
|
347
|
+
shaka.log.error('XML parsing exception:', exception);
|
|
348
|
+
return null;
|
|
334
349
|
}
|
|
335
|
-
|
|
350
|
+
|
|
351
|
+
// According to MDN, parseFromString never returns null.
|
|
352
|
+
goog.asserts.assert(xml, 'Parsed XML document cannot be null!');
|
|
353
|
+
|
|
354
|
+
// Check for empty documents.
|
|
355
|
+
const rootElem = xml.documentElement;
|
|
356
|
+
if (!rootElem) {
|
|
357
|
+
shaka.log.error('XML document was empty!');
|
|
336
358
|
return null;
|
|
337
|
-
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Check for parser errors.
|
|
362
|
+
const parserErrorElements = rootElem.getElementsByTagName('parsererror');
|
|
363
|
+
if (parserErrorElements.length) {
|
|
364
|
+
shaka.log.error('XML parser error found:', parserErrorElements[0]);
|
|
365
|
+
return null;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// The top-level element in the loaded XML should have the name we expect.
|
|
369
|
+
if (xml.documentElement.tagName != expectedRootElemName) {
|
|
370
|
+
shaka.log.error(
|
|
371
|
+
`XML tag name does not match expected "${expectedRootElemName}":`,
|
|
372
|
+
xml.documentElement.tagName);
|
|
373
|
+
return null;
|
|
374
|
+
}
|
|
338
375
|
|
|
339
376
|
return rootElem;
|
|
340
377
|
}
|
|
@@ -352,6 +389,7 @@ shaka.util.XmlUtils = class {
|
|
|
352
389
|
const string = shaka.util.StringUtils.fromUTF8(data);
|
|
353
390
|
return shaka.util.XmlUtils.parseXmlString(string, expectedRootElemName);
|
|
354
391
|
} catch (exception) {
|
|
392
|
+
shaka.log.error('parseXmlString threw!', exception);
|
|
355
393
|
return null;
|
|
356
394
|
}
|
|
357
395
|
}
|
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.2.
|
|
4
|
+
"version": "3.2.4",
|
|
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",
|
package/roadmap.md
CHANGED
|
@@ -8,49 +8,51 @@ CHANGELOG.md.
|
|
|
8
8
|
The goals of future milestones are fluid until we begin that development cycle,
|
|
9
9
|
so the exact milestone for future features is not pre-determined.
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
- HLS performance improvements
|
|
13
|
-
https://github.com/google/shaka-player/issues/3363
|
|
14
|
-
- HLS interstitial support
|
|
15
|
-
https://github.com/google/shaka-player/issues/3364
|
|
16
|
-
- Integration with Google Video ML/Cloud AI
|
|
11
|
+
Priority features up next:
|
|
17
12
|
- Support containerless formats
|
|
18
13
|
https://github.com/google/shaka-player/issues/2337
|
|
14
|
+
- Preload API
|
|
15
|
+
https://github.com/google/shaka-player/issues/880
|
|
16
|
+
- Codec-switching and order preference
|
|
17
|
+
https://github.com/google/shaka-player/issues/1528
|
|
18
|
+
https://github.com/google/shaka-player/issues/2179
|
|
19
|
+
|
|
20
|
+
Candidate features for future release cycles:
|
|
21
|
+
- HLS interstitial support
|
|
22
|
+
https://github.com/google/shaka-player/issues/3364
|
|
19
23
|
- Support ID3 tags in mp4
|
|
20
24
|
https://github.com/google/shaka-player/issues/3351
|
|
21
25
|
- MSE support for FairPlay
|
|
22
26
|
https://github.com/google/shaka-player/issues/3346
|
|
23
|
-
- Codec-switching and order preference
|
|
24
|
-
https://github.com/google/shaka-player/issues/1528
|
|
25
|
-
https://github.com/google/shaka-player/issues/2179
|
|
26
|
-
- Configurable key-system priority
|
|
27
|
-
https://github.com/google/shaka-player/issues/3002
|
|
28
|
-
- Preload API
|
|
29
|
-
https://github.com/google/shaka-player/issues/880
|
|
30
|
-
- Thumbnail tracks
|
|
31
|
-
DASH: https://github.com/google/shaka-player/issues/559
|
|
32
|
-
HLS: https://github.com/google/shaka-player/issues/2429
|
|
33
27
|
- Smaller, more modular binary
|
|
34
28
|
- Official TypeScript defs, generated from source
|
|
35
29
|
https://github.com/google/shaka-player/issues/1030
|
|
30
|
+
- Shaka Analytics Component and an integration with BigQuery Analytics
|
|
31
|
+
https://github.com/google/shaka-player/issues/3359
|
|
32
|
+
- Integration with Google Video ML/Cloud AI
|
|
36
33
|
|
|
37
|
-
v3.3 - 2021 Q3
|
|
34
|
+
v3.3 - 2021 Q3 or Q4
|
|
38
35
|
- Background fetch for offline storage
|
|
39
36
|
https://github.com/google/shaka-player/issues/879
|
|
40
|
-
-
|
|
41
|
-
https://github.com/google/shaka-player/issues/
|
|
37
|
+
- HLS performance improvements
|
|
38
|
+
https://github.com/google/shaka-player/issues/3363
|
|
42
39
|
|
|
43
|
-
|
|
40
|
+
=====
|
|
41
|
+
|
|
42
|
+
v3.2 - 2021 Q3
|
|
44
43
|
- MediaCapabilities to make performance-based decisions
|
|
45
44
|
https://github.com/google/shaka-player/issues/1391
|
|
45
|
+
- Configurable key-system priority
|
|
46
|
+
https://github.com/google/shaka-player/issues/3002
|
|
46
47
|
|
|
47
48
|
v3.1 - 2021 Q2
|
|
48
49
|
- Low-latency live (LL-HLS, LL-DASH)
|
|
49
50
|
https://github.com/google/shaka-player/issues/1525
|
|
50
51
|
- Own CEA parser, use mux.js only for TS
|
|
51
52
|
https://github.com/google/shaka-player/issues/2648
|
|
52
|
-
|
|
53
|
-
|
|
53
|
+
- Thumbnail tracks
|
|
54
|
+
DASH: https://github.com/google/shaka-player/issues/559
|
|
55
|
+
HLS: https://github.com/google/shaka-player/issues/2429
|
|
54
56
|
|
|
55
57
|
v3.0 - 2020 Q2
|
|
56
58
|
- Code health improvements
|
|
@@ -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 () => {
|
|
@@ -123,7 +123,7 @@ describe('CastUtils', () => {
|
|
|
123
123
|
expect(typeof deserialized).toBe('object');
|
|
124
124
|
|
|
125
125
|
// The object can be used to construct a FakeEvent.
|
|
126
|
-
const fakeEvent =
|
|
126
|
+
const fakeEvent = FakeEvent.fromRealEvent(deserialized);
|
|
127
127
|
|
|
128
128
|
// The fake event has the same type and properties as the original.
|
|
129
129
|
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 () => {
|