shaka-player 4.4.2 → 4.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/release-please.yaml +2 -27
- package/CHANGELOG.md +67 -25
- package/README.md +16 -10
- package/demo/asset_card.js +18 -21
- package/demo/cast_receiver/index.html +0 -1
- package/demo/close_button.js +0 -3
- package/demo/common/asset.js +27 -5
- package/demo/common/assets.js +150 -52
- package/demo/config.js +192 -253
- package/demo/custom.js +39 -98
- package/demo/front.js +7 -10
- package/demo/index.html +19 -22
- package/demo/input.js +6 -11
- package/demo/input_container.js +8 -9
- package/demo/main.js +52 -124
- package/demo/search.js +23 -35
- package/demo/service_worker.js +0 -3
- package/demo/tooltip.js +3 -5
- package/demo/visualizer.js +3 -5
- package/demo/visualizer_button.js +1 -20
- package/dist/demo.compiled.debug.js +187 -211
- package/dist/demo.compiled.debug.map +4 -4
- package/dist/demo.compiled.js +187 -211
- package/dist/demo.compiled.map +4 -4
- package/dist/deps.js +27 -24
- package/dist/locales.js +31 -31
- package/dist/receiver.compiled.debug.js +42 -61
- package/dist/receiver.compiled.debug.map +4 -4
- package/dist/receiver.compiled.js +42 -61
- package/dist/receiver.compiled.map +4 -4
- package/dist/shaka-player.compiled.d.ts +728 -365
- package/dist/shaka-player.compiled.debug.d.ts +728 -365
- package/dist/shaka-player.compiled.debug.externs.js +475 -51
- package/dist/shaka-player.compiled.debug.js +576 -480
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +475 -51
- package/dist/shaka-player.compiled.js +1117 -1056
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +783 -410
- package/dist/shaka-player.ui.debug.d.ts +783 -410
- package/dist/shaka-player.ui.debug.externs.js +496 -51
- package/dist/shaka-player.ui.debug.js +647 -542
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +496 -51
- package/dist/shaka-player.ui.js +1260 -1194
- package/dist/shaka-player.ui.map +4 -4
- package/docs/design/lcevc-integration.md +14 -14
- package/docs/tutorials/faq.md +27 -55
- package/docs/tutorials/index.json +2 -1
- package/docs/tutorials/lcevc.md +118 -0
- package/docs/tutorials/offline.md +1 -1
- package/docs/tutorials/ui-customization.md +4 -0
- package/externs/css_style_declaration.js +19 -0
- package/externs/lcevc.js +37 -13
- package/externs/media_remote.js +68 -0
- package/externs/mediatailor.js +193 -0
- package/externs/pictureinpicture.js +11 -0
- package/externs/shaka/ads.js +27 -0
- package/externs/shaka/manifest.js +10 -5
- package/externs/shaka/manifest_parser.js +7 -1
- package/externs/shaka/offline.js +4 -0
- package/externs/shaka/player.js +12 -1
- package/karma.conf.js +7 -3
- package/lib/abr/simple_abr_manager.js +43 -3
- package/lib/ads/ad_manager.js +104 -50
- package/lib/ads/media_tailor_ad.js +297 -0
- package/lib/ads/media_tailor_ad_manager.js +828 -0
- package/lib/cea/ts_cea_parser.js +15 -2
- package/lib/config/codec_switching_strategy.js +24 -0
- package/lib/dash/content_protection.js +120 -2
- package/lib/dash/dash_parser.js +79 -13
- package/lib/dash/mpd_utils.js +10 -6
- package/lib/dash/segment_base.js +13 -5
- package/lib/dash/segment_list.js +13 -5
- package/lib/dash/segment_template.js +43 -85
- package/lib/dependencies/all.js +8 -7
- package/lib/hls/hls_parser.js +101 -37
- package/lib/lcevc/lcevc_dec.js +220 -0
- package/lib/media/adaptation_set.js +22 -11
- package/lib/media/adaptation_set_criteria.js +25 -7
- package/lib/media/media_source_engine.js +215 -27
- package/lib/media/segment_index.js +5 -5
- package/lib/media/segment_reference.js +10 -5
- package/lib/media/streaming_engine.js +44 -39
- package/lib/mss/mss_parser.js +1 -0
- package/lib/net/networking_engine.js +1 -0
- package/lib/offline/indexeddb/v1_storage_cell.js +1 -0
- package/lib/offline/indexeddb/v2_storage_cell.js +1 -0
- package/lib/offline/manifest_converter.js +1 -0
- package/lib/offline/storage.js +3 -0
- package/lib/player.js +142 -72
- package/lib/routing/payload.js +5 -1
- package/lib/text/cue.js +62 -47
- package/lib/text/ttml_text_parser.js +17 -0
- package/lib/text/ui_text_displayer.js +4 -0
- package/lib/text/vtt_text_parser.js +35 -22
- package/lib/transmuxer/h265.js +862 -0
- package/lib/transmuxer/mpeg_ts_transmuxer.js +3 -2
- package/lib/transmuxer/ts_transmuxer.js +160 -15
- package/lib/util/cmcd_manager.js +6 -1
- package/lib/util/error.js +28 -9
- package/lib/util/exp_golomb.js +30 -2
- package/lib/util/fake_event.js +3 -0
- package/lib/util/manifest_parser_utils.js +10 -4
- package/lib/util/mp4_generator.js +4 -4
- package/lib/util/periods.js +2 -0
- package/lib/util/platform.js +54 -0
- package/lib/util/player_configuration.js +8 -0
- package/lib/util/state_history.js +6 -2
- package/lib/util/stream_utils.js +133 -217
- package/lib/util/ts_parser.js +229 -43
- package/maintained-branches.md +3 -2
- package/package.json +4 -6
- package/roadmap.md +15 -5
- package/shaka-player.uncompiled.js +2 -1
- package/support.html +0 -1
- package/test/cast/cast_receiver_integration.js +47 -0
- package/test/codec_switching/codec_switching_integration.js +181 -0
- package/test/dash/dash_parser_content_protection_unit.js +2 -0
- package/test/dash/dash_parser_integration.js +73 -0
- package/test/dash/dash_parser_live_unit.js +2 -0
- package/test/dash/dash_parser_manifest_unit.js +47 -0
- package/test/dash/dash_parser_segment_base_unit.js +7 -0
- package/test/dash/dash_parser_segment_list_unit.js +23 -17
- package/test/dash/dash_parser_segment_template_unit.js +49 -0
- package/test/demo/demo_unit.js +0 -16
- package/test/hls/hls_live_unit.js +35 -0
- package/test/hls/hls_parser_unit.js +8 -61
- package/test/media/adaptation_set_criteria_unit.js +118 -0
- package/test/media/adaptation_set_unit.js +1 -0
- package/test/media/media_source_engine_unit.js +165 -1
- package/test/media/segment_index_unit.js +4 -4
- package/test/media/segment_prefetch_unit.js +1 -1
- package/test/media/streaming_engine_unit.js +17 -17
- package/test/mss/mss_parser_unit.js +2 -0
- package/test/offline/manifest_convert_unit.js +5 -0
- package/test/offline/storage_integration.js +1 -4
- package/test/player_unit.js +102 -131
- package/test/routing/walker_unit.js +1 -0
- package/test/test/assets/dash-aes-128/Fragments(video=0,format=mpd-time-csf) +0 -0
- package/test/test/assets/dash-aes-128/Fragments(video=20000000,format=mpd-time-csf) +0 -0
- package/test/test/assets/dash-aes-128/Fragments(video=40000000,format=mpd-time-csf) +0 -0
- package/test/test/assets/dash-aes-128/Fragments(video=60000000,format=mpd-time-csf) +0 -0
- package/test/test/assets/dash-aes-128/Fragments(video=i,format=mpd-time-csf) +0 -0
- package/test/test/assets/dash-aes-128/dash.mpd +18 -0
- package/test/test/assets/dash-aes-128/license +1 -0
- package/test/test/assets/dash-multi-codec/audio_en_2c_128k_aac.mp4 +0 -0
- package/test/test/assets/dash-multi-codec/audio_es_2c_64k_opus.webm +0 -0
- package/test/test/assets/dash-multi-codec/dash.mpd +32 -0
- package/test/test/assets/dash-multi-codec/video_144p_108k_h264.mp4 +0 -0
- package/test/test/assets/hls-ts-h265/0.ts +0 -0
- package/test/test/assets/hls-ts-h265/hevc.m3u8 +6 -0
- package/test/test/assets/hls-ts-h265/index.m3u8 +7 -0
- package/test/test/assets/hls-ts-muxed-aac-h265/0.ts +0 -0
- package/test/test/assets/hls-ts-muxed-aac-h265/1.ts +0 -0
- package/test/test/assets/hls-ts-muxed-aac-h265/media.m3u8 +9 -0
- package/test/test/assets/hls-ts-muxed-ac3-h264/media.m3u8 +24 -0
- package/test/test/assets/hls-ts-muxed-ac3-h264/s_00000.ts +0 -0
- package/test/test/assets/hls-ts-muxed-ac3-h264/s_00001.ts +0 -0
- package/test/test/assets/hls-ts-muxed-ac3-h264/s_00002.ts +0 -0
- package/test/test/assets/hls-ts-muxed-ac3-h264/s_00003.ts +0 -0
- package/test/test/assets/hls-ts-muxed-ac3-h264/s_00004.ts +0 -0
- package/test/test/assets/hls-ts-muxed-ac3-h264/s_00005.ts +0 -0
- package/test/test/assets/hls-ts-muxed-ec3-h264/ChID_voices_1280x720p_25fps_h264_6ch_256kbps_ddp_sub_0000.ts +0 -0
- package/test/test/assets/hls-ts-muxed-ec3-h264/ChID_voices_1280x720p_25fps_h264_6ch_256kbps_ddp_sub_0001.ts +0 -0
- package/test/test/assets/hls-ts-muxed-ec3-h264/ChID_voices_1280x720p_25fps_h264_6ch_256kbps_ddp_sub_0002.ts +0 -0
- package/test/test/assets/hls-ts-muxed-ec3-h264/ChID_voices_1280x720p_25fps_h264_6ch_256kbps_ddp_sub_0003.ts +0 -0
- package/test/test/assets/hls-ts-muxed-ec3-h264/prog_index.m3u8 +14 -0
- package/test/test/assets/hls-ts-muxed-mp3-h264/index.m3u8 +23 -0
- package/test/test/assets/hls-ts-muxed-mp3-h264/seg-1-v1-a1.ts +0 -0
- package/test/test/assets/hls-ts-muxed-mp3-h264/seg-2-v1-a1.ts +0 -0
- package/test/test/assets/hls-ts-muxed-mp3-h264/seg-3-v1-a1.ts +0 -0
- package/test/test/assets/hls-ts-muxed-mp3-h264/seg-4-v1-a1.ts +0 -0
- package/test/test/assets/hls-ts-muxed-mp3-h264/seg-5-v1-a1.ts +0 -0
- package/test/test/assets/hls-ts-muxed-mp3-h264/seg-6-v1-a1.ts +0 -0
- package/test/test/assets/hls-ts-muxed-mp3-h264/seg-7-v1-a1.ts +0 -0
- package/test/test/assets/hls-ts-muxed-mp3-h264/seg-8-v1-a1.ts +0 -0
- package/test/test/util/dash_parser_util.js +20 -7
- package/test/test/util/fake_ad_manager.js +20 -0
- package/test/test/util/fake_media_source_engine.js +8 -2
- package/test/test/util/manifest_generator.js +7 -3
- package/test/test/util/mss_parser_util.js +4 -0
- package/test/test/util/offline_utils.js +1 -0
- package/test/test/util/streaming_engine_util.js +7 -4
- package/test/text/ttml_text_parser_unit.js +52 -0
- package/test/text/vtt_text_parser_unit.js +105 -48
- package/test/transmuxer/transmuxer_integration.js +121 -4
- package/test/ui/ui_unit.js +3 -10
- package/test/util/stream_utils_unit.js +208 -74
- package/test/util/ts_parser_unit.js +1 -1
- package/ui/controls.js +125 -0
- package/ui/externs/ui.js +8 -1
- package/ui/pip_button.js +22 -120
- package/ui/remote_button.js +201 -0
- package/ui/ui.js +5 -2
- package/demo/common/message_ids.js +0 -309
- package/demo/locales/en.json +0 -287
- package/demo/locales/source.json +0 -1142
- package/externs/mux.js +0 -69
- package/lib/lcevc/lcevc_dil.js +0 -152
- package/lib/transmuxer/muxjs_transmuxer.js +0 -242
- package/test/transmuxer/muxjs_transmuxer_integration.js +0 -177
package/lib/cea/ts_cea_parser.js
CHANGED
|
@@ -49,10 +49,23 @@ shaka.cea.TsCeaParser = class {
|
|
|
49
49
|
return captionPackets;
|
|
50
50
|
}
|
|
51
51
|
const tsParser = new shaka.util.TsParser().parse(uint8ArrayData);
|
|
52
|
+
const codecs = tsParser.getCodecs();
|
|
52
53
|
const videoNalus = tsParser.getVideoNalus();
|
|
54
|
+
const validNaluTypes = [];
|
|
55
|
+
switch (codecs.video) {
|
|
56
|
+
case 'avc':
|
|
57
|
+
validNaluTypes.push(CeaUtils.H264_NALU_TYPE_SEI);
|
|
58
|
+
break;
|
|
59
|
+
case 'hvc':
|
|
60
|
+
validNaluTypes.push(CeaUtils.H265_PREFIX_NALU_TYPE_SEI);
|
|
61
|
+
validNaluTypes.push(CeaUtils.H265_SUFFIX_NALU_TYPE_SEI);
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
if (!validNaluTypes.length) {
|
|
65
|
+
return captionPackets;
|
|
66
|
+
}
|
|
53
67
|
for (const nalu of videoNalus) {
|
|
54
|
-
if (nalu.type
|
|
55
|
-
nalu.time != null) {
|
|
68
|
+
if (validNaluTypes.includes(nalu.type) && nalu.time != null) {
|
|
56
69
|
for (const packet of this.seiProcessor_.process(nalu.data)) {
|
|
57
70
|
captionPackets.push({
|
|
58
71
|
packet: packet,
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/*! @license
|
|
2
|
+
* Shaka Player
|
|
3
|
+
* Copyright 2016 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
goog.provide('shaka.config.CodecSwitchingStrategy');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @enum {string}
|
|
11
|
+
* @export
|
|
12
|
+
*/
|
|
13
|
+
shaka.config.CodecSwitchingStrategy = {
|
|
14
|
+
// Allow codec switching which will always involve reloading the
|
|
15
|
+
// `MediaSource`.
|
|
16
|
+
RELOAD: 'reload',
|
|
17
|
+
// Allow codec switching; determine if `SourceBuffer.changeType` is available
|
|
18
|
+
// and attempt to use this first, but fall back to reloading `MediaSource` if
|
|
19
|
+
// not available.
|
|
20
|
+
//
|
|
21
|
+
// Note: Some devices that support `SourceBuffer.changeType` can become stuck
|
|
22
|
+
// in a pause state.
|
|
23
|
+
SMOOTH: 'smooth',
|
|
24
|
+
};
|
|
@@ -39,6 +39,8 @@ shaka.dash.ContentProtection = class {
|
|
|
39
39
|
/** @type {!Array.<shaka.extern.DrmInfo>} */
|
|
40
40
|
let drmInfos = [];
|
|
41
41
|
let parsedNonCenc = [];
|
|
42
|
+
/** @type {?shaka.dash.ContentProtection.Aes128Info} */
|
|
43
|
+
let aes128Info = null;
|
|
42
44
|
|
|
43
45
|
// Get the default key ID; if there are multiple, they must all match.
|
|
44
46
|
const keyIds = new Set(parsed.map((element) => element.keyId));
|
|
@@ -53,6 +55,21 @@ shaka.dash.ContentProtection = class {
|
|
|
53
55
|
}
|
|
54
56
|
|
|
55
57
|
if (!ignoreDrmInfo) {
|
|
58
|
+
const aes128Elements = parsed.filter((elem) => {
|
|
59
|
+
return elem.schemeUri == ContentProtection.Aes128Protection_;
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
if (aes128Elements.length > 1) {
|
|
63
|
+
throw new shaka.util.Error(
|
|
64
|
+
shaka.util.Error.Severity.CRITICAL,
|
|
65
|
+
shaka.util.Error.Category.MANIFEST,
|
|
66
|
+
shaka.util.Error.Code.DASH_CONFLICTING_AES_128);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (aes128Elements.length) {
|
|
70
|
+
aes128Info = ContentProtection.parseAes128_(aes128Elements[0]);
|
|
71
|
+
}
|
|
72
|
+
|
|
56
73
|
// Find the default key ID and init data. Create a new array of all the
|
|
57
74
|
// non-CENC elements.
|
|
58
75
|
parsedNonCenc = parsed.filter((elem) => {
|
|
@@ -62,7 +79,7 @@ shaka.dash.ContentProtection = class {
|
|
|
62
79
|
defaultInit = elem.init || defaultInit;
|
|
63
80
|
return false;
|
|
64
81
|
} else {
|
|
65
|
-
return
|
|
82
|
+
return elem.schemeUri != ContentProtection.Aes128Protection_;
|
|
66
83
|
}
|
|
67
84
|
});
|
|
68
85
|
|
|
@@ -80,7 +97,8 @@ shaka.dash.ContentProtection = class {
|
|
|
80
97
|
|
|
81
98
|
// If there are only CENC element(s) or ignoreDrmInfo flag is set, assume
|
|
82
99
|
// all key-systems are supported.
|
|
83
|
-
if (parsed.length &&
|
|
100
|
+
if (parsed.length && !aes128Info &&
|
|
101
|
+
(ignoreDrmInfo || !parsedNonCenc.length)) {
|
|
84
102
|
drmInfos = [];
|
|
85
103
|
|
|
86
104
|
for (const keySystem of Object.values(keySystemsByURI)) {
|
|
@@ -110,6 +128,7 @@ shaka.dash.ContentProtection = class {
|
|
|
110
128
|
defaultKeyId: defaultKeyId,
|
|
111
129
|
defaultInit: defaultInit,
|
|
112
130
|
drmInfos: drmInfos,
|
|
131
|
+
aes128Info: aes128Info,
|
|
113
132
|
firstRepresentation: true,
|
|
114
133
|
};
|
|
115
134
|
}
|
|
@@ -551,6 +570,79 @@ shaka.dash.ContentProtection = class {
|
|
|
551
570
|
init: (init.length > 0 ? init : null),
|
|
552
571
|
};
|
|
553
572
|
}
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* Parses the given AES-128 ContentProtection element.
|
|
576
|
+
*
|
|
577
|
+
* @param {shaka.dash.ContentProtection.Element} element
|
|
578
|
+
* @return {?shaka.dash.ContentProtection.Aes128Info}
|
|
579
|
+
* @private
|
|
580
|
+
*/
|
|
581
|
+
static parseAes128_(element) {
|
|
582
|
+
// Check if the Web Crypto API is available.
|
|
583
|
+
if (!window.crypto || !window.crypto.subtle) {
|
|
584
|
+
shaka.log.alwaysWarn('Web Crypto API is not available to decrypt ' +
|
|
585
|
+
'AES-128. (Web Crypto only exists in secure origins like https)');
|
|
586
|
+
throw new shaka.util.Error(
|
|
587
|
+
shaka.util.Error.Severity.CRITICAL,
|
|
588
|
+
shaka.util.Error.Category.MANIFEST,
|
|
589
|
+
shaka.util.Error.Code.NO_WEB_CRYPTO_API);
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
const namespace = 'urn:mpeg:dash:schema:sea:2012';
|
|
593
|
+
const segmentEncryption = shaka.util.XmlUtils.findChildNS(
|
|
594
|
+
element.node, namespace, 'SegmentEncryption');
|
|
595
|
+
|
|
596
|
+
if (!segmentEncryption) {
|
|
597
|
+
throw new shaka.util.Error(
|
|
598
|
+
shaka.util.Error.Severity.CRITICAL,
|
|
599
|
+
shaka.util.Error.Category.MANIFEST,
|
|
600
|
+
shaka.util.Error.Code.DASH_UNSUPPORTED_AES_128);
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
const aesSchemeIdUri = 'urn:mpeg:dash:sea:aes128-cbc:2013';
|
|
604
|
+
const segmentEncryptionSchemeIdUri =
|
|
605
|
+
segmentEncryption.getAttribute('schemeIdUri');
|
|
606
|
+
if (segmentEncryptionSchemeIdUri != aesSchemeIdUri) {
|
|
607
|
+
throw new shaka.util.Error(
|
|
608
|
+
shaka.util.Error.Severity.CRITICAL,
|
|
609
|
+
shaka.util.Error.Category.MANIFEST,
|
|
610
|
+
shaka.util.Error.Code.DASH_UNSUPPORTED_AES_128);
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
const cryptoPeriod = shaka.util.XmlUtils.findChildNS(
|
|
614
|
+
element.node, namespace, 'CryptoPeriod');
|
|
615
|
+
|
|
616
|
+
if (!cryptoPeriod) {
|
|
617
|
+
throw new shaka.util.Error(
|
|
618
|
+
shaka.util.Error.Severity.CRITICAL,
|
|
619
|
+
shaka.util.Error.Category.MANIFEST,
|
|
620
|
+
shaka.util.Error.Code.DASH_UNSUPPORTED_AES_128);
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
const ivHex = cryptoPeriod.getAttribute('IV');
|
|
624
|
+
const keyUri = cryptoPeriod.getAttribute('keyUriTemplate');
|
|
625
|
+
if (!ivHex || !keyUri) {
|
|
626
|
+
throw new shaka.util.Error(
|
|
627
|
+
shaka.util.Error.Severity.CRITICAL,
|
|
628
|
+
shaka.util.Error.Category.MANIFEST,
|
|
629
|
+
shaka.util.Error.Code.DASH_UNSUPPORTED_AES_128);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
// Exclude 0x at the start of string.
|
|
633
|
+
const iv = shaka.util.Uint8ArrayUtils.fromHex(ivHex.substr(2));
|
|
634
|
+
if (iv.byteLength != 16) {
|
|
635
|
+
throw new shaka.util.Error(
|
|
636
|
+
shaka.util.Error.Severity.CRITICAL,
|
|
637
|
+
shaka.util.Error.Category.MANIFEST,
|
|
638
|
+
shaka.util.Error.Code.AES_128_INVALID_IV_LENGTH);
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
return {
|
|
642
|
+
keyUri,
|
|
643
|
+
iv,
|
|
644
|
+
};
|
|
645
|
+
}
|
|
554
646
|
};
|
|
555
647
|
|
|
556
648
|
/**
|
|
@@ -584,6 +676,7 @@ shaka.dash.ContentProtection.PLAYREADY_RECORD_TYPES = {
|
|
|
584
676
|
* defaultKeyId: ?string,
|
|
585
677
|
* defaultInit: Array.<shaka.extern.InitDataOverride>,
|
|
586
678
|
* drmInfos: !Array.<shaka.extern.DrmInfo>,
|
|
679
|
+
* aes128Info: ?shaka.dash.ContentProtection.Aes128Info,
|
|
587
680
|
* firstRepresentation: boolean
|
|
588
681
|
* }}
|
|
589
682
|
*
|
|
@@ -599,6 +692,8 @@ shaka.dash.ContentProtection.PLAYREADY_RECORD_TYPES = {
|
|
|
599
692
|
* is no default.
|
|
600
693
|
* @property {!Array.<shaka.extern.DrmInfo>} drmInfos
|
|
601
694
|
* The DrmInfo objects.
|
|
695
|
+
* @property {?shaka.dash.ContentProtection.Aes128Info} aes128Info
|
|
696
|
+
* The AES-128 key info.
|
|
602
697
|
* @property {boolean} firstRepresentation
|
|
603
698
|
* True when first parsed; changed to false after the first call to
|
|
604
699
|
* parseKeyIds. This is used to determine if a dummy key-system should be
|
|
@@ -607,6 +702,22 @@ shaka.dash.ContentProtection.PLAYREADY_RECORD_TYPES = {
|
|
|
607
702
|
*/
|
|
608
703
|
shaka.dash.ContentProtection.Context;
|
|
609
704
|
|
|
705
|
+
/**
|
|
706
|
+
* @typedef {{
|
|
707
|
+
* keyUri: string,
|
|
708
|
+
* iv: !Uint8Array
|
|
709
|
+
* }}
|
|
710
|
+
*
|
|
711
|
+
* @description
|
|
712
|
+
* Contains information about the AES-128 keyUri and IV found at the
|
|
713
|
+
* AdaptationSet level.
|
|
714
|
+
*
|
|
715
|
+
* @property {string} method
|
|
716
|
+
* The keyUri in the manifest.
|
|
717
|
+
* @property {!Uint8Array} iv
|
|
718
|
+
* The IV in the manifest.
|
|
719
|
+
*/
|
|
720
|
+
shaka.dash.ContentProtection.Aes128Info;
|
|
610
721
|
|
|
611
722
|
/**
|
|
612
723
|
* @typedef {{
|
|
@@ -658,6 +769,13 @@ shaka.dash.ContentProtection.licenseUrlParsers_ = new Map()
|
|
|
658
769
|
shaka.dash.ContentProtection.MP4Protection_ =
|
|
659
770
|
'urn:mpeg:dash:mp4protection:2011';
|
|
660
771
|
|
|
772
|
+
/**
|
|
773
|
+
* @const {string}
|
|
774
|
+
* @private
|
|
775
|
+
*/
|
|
776
|
+
shaka.dash.ContentProtection.Aes128Protection_ =
|
|
777
|
+
'urn:mpeg:dash:sea:2012';
|
|
778
|
+
|
|
661
779
|
|
|
662
780
|
/**
|
|
663
781
|
* @const {string}
|
package/lib/dash/dash_parser.js
CHANGED
|
@@ -224,12 +224,10 @@ shaka.dash.DashParser = class {
|
|
|
224
224
|
const type = shaka.net.NetworkingEngine.AdvancedRequestType.MPD;
|
|
225
225
|
const request = shaka.net.NetworkingEngine.makeRequest(
|
|
226
226
|
this.manifestUris_, this.config_.retryParameters);
|
|
227
|
-
const networkingEngine = this.playerInterface_.networkingEngine;
|
|
228
227
|
const startTime = Date.now();
|
|
229
|
-
const operation = networkingEngine.request(requestType, request, {type});
|
|
230
|
-
this.operationManager_.manage(operation);
|
|
231
228
|
|
|
232
|
-
const response = await
|
|
229
|
+
const response = await this.makeNetworkRequest_(
|
|
230
|
+
request, requestType, {type});
|
|
233
231
|
|
|
234
232
|
// Detect calls to stop().
|
|
235
233
|
if (!this.playerInterface_) {
|
|
@@ -533,7 +531,10 @@ shaka.dash.DashParser = class {
|
|
|
533
531
|
// Just update the variants and text streams, which may change as periods
|
|
534
532
|
// are added or removed.
|
|
535
533
|
this.manifest_.variants = this.periodCombiner_.getVariants();
|
|
536
|
-
|
|
534
|
+
const textStreams = this.periodCombiner_.getTextStreams();
|
|
535
|
+
if (textStreams.length > 0) {
|
|
536
|
+
this.manifest_.textStreams = textStreams;
|
|
537
|
+
}
|
|
537
538
|
this.manifest_.imageStreams = this.periodCombiner_.getImageStreams();
|
|
538
539
|
|
|
539
540
|
// Re-filter the manifest. This will check any configured restrictions on
|
|
@@ -1166,6 +1167,9 @@ shaka.dash.DashParser = class {
|
|
|
1166
1167
|
}
|
|
1167
1168
|
}
|
|
1168
1169
|
|
|
1170
|
+
const adaptationId = context.adaptationSet.id ||
|
|
1171
|
+
('__fake__' + this.globalId_++);
|
|
1172
|
+
|
|
1169
1173
|
for (const stream of streams) {
|
|
1170
1174
|
// Some DRM license providers require that we have a default
|
|
1171
1175
|
// key ID from the manifest in the wrapped license request.
|
|
@@ -1175,6 +1179,9 @@ shaka.dash.DashParser = class {
|
|
|
1175
1179
|
new Set([...drmInfo.keyIds, ...stream.keyIds]) :
|
|
1176
1180
|
drmInfo.keyIds || stream.keyIds;
|
|
1177
1181
|
}
|
|
1182
|
+
if (this.config_.dash.enableAudioGroups) {
|
|
1183
|
+
stream.groupId = adaptationId;
|
|
1184
|
+
}
|
|
1178
1185
|
}
|
|
1179
1186
|
|
|
1180
1187
|
const repIds = representations
|
|
@@ -1182,7 +1189,7 @@ shaka.dash.DashParser = class {
|
|
|
1182
1189
|
.filter(shaka.util.Functional.isNotNull);
|
|
1183
1190
|
|
|
1184
1191
|
return {
|
|
1185
|
-
id:
|
|
1192
|
+
id: adaptationId,
|
|
1186
1193
|
contentType: context.adaptationSet.contentType,
|
|
1187
1194
|
language: language,
|
|
1188
1195
|
main: main,
|
|
@@ -1247,21 +1254,61 @@ shaka.dash.DashParser = class {
|
|
|
1247
1254
|
const isImage = contentType == ContentType.IMAGE;
|
|
1248
1255
|
|
|
1249
1256
|
try {
|
|
1257
|
+
/** @type {shaka.extern.aes128Key|undefined} */
|
|
1258
|
+
let aes128Key = undefined;
|
|
1259
|
+
if (contentProtection.aes128Info) {
|
|
1260
|
+
const baseUris = context.representation.baseUris;
|
|
1261
|
+
const uris = shaka.util.ManifestParserUtils.resolveUris(
|
|
1262
|
+
baseUris, [contentProtection.aes128Info.keyUri]);
|
|
1263
|
+
const requestType = shaka.net.NetworkingEngine.RequestType.KEY;
|
|
1264
|
+
const request = shaka.net.NetworkingEngine.makeRequest(
|
|
1265
|
+
uris, this.config_.retryParameters);
|
|
1266
|
+
|
|
1267
|
+
aes128Key = {
|
|
1268
|
+
method: 'AES-128',
|
|
1269
|
+
iv: contentProtection.aes128Info.iv,
|
|
1270
|
+
firstMediaSequenceNumber: 0,
|
|
1271
|
+
};
|
|
1272
|
+
|
|
1273
|
+
// Don't download the key object until the segment is parsed, to
|
|
1274
|
+
// avoid a startup delay for long manifests with lots of keys.
|
|
1275
|
+
aes128Key.fetchKey = async () => {
|
|
1276
|
+
const keyResponse =
|
|
1277
|
+
await this.makeNetworkRequest_(request, requestType);
|
|
1278
|
+
|
|
1279
|
+
// keyResponse.status is undefined when URI is
|
|
1280
|
+
// "data:text/plain;base64,"
|
|
1281
|
+
if (!keyResponse.data || keyResponse.data.byteLength != 16) {
|
|
1282
|
+
throw new shaka.util.Error(
|
|
1283
|
+
shaka.util.Error.Severity.CRITICAL,
|
|
1284
|
+
shaka.util.Error.Category.MANIFEST,
|
|
1285
|
+
shaka.util.Error.Code.AES_128_INVALID_KEY_LENGTH);
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
const algorithm = {
|
|
1289
|
+
name: 'AES-CBC',
|
|
1290
|
+
};
|
|
1291
|
+
aes128Key.cryptoKey = await window.crypto.subtle.importKey(
|
|
1292
|
+
'raw', keyResponse.data, algorithm, true, ['decrypt']);
|
|
1293
|
+
aes128Key.fetchKey = undefined; // No longer needed.
|
|
1294
|
+
};
|
|
1295
|
+
}
|
|
1250
1296
|
const requestSegment = (uris, startByte, endByte, isInit) => {
|
|
1251
1297
|
return this.requestSegment_(uris, startByte, endByte, isInit);
|
|
1252
1298
|
};
|
|
1253
1299
|
if (context.representation.segmentBase) {
|
|
1254
1300
|
streamInfo = shaka.dash.SegmentBase.createStreamInfo(
|
|
1255
|
-
context, requestSegment);
|
|
1301
|
+
context, requestSegment, aes128Key);
|
|
1256
1302
|
} else if (context.representation.segmentList) {
|
|
1257
1303
|
streamInfo = shaka.dash.SegmentList.createStreamInfo(
|
|
1258
|
-
context, this.streamMap_);
|
|
1304
|
+
context, this.streamMap_, aes128Key);
|
|
1259
1305
|
} else if (context.representation.segmentTemplate) {
|
|
1260
1306
|
const hasManifest = !!this.manifest_;
|
|
1261
1307
|
|
|
1262
1308
|
streamInfo = shaka.dash.SegmentTemplate.createStreamInfo(
|
|
1263
1309
|
context, requestSegment, this.streamMap_, hasManifest,
|
|
1264
|
-
this.config_.dash.initialSegmentLimit, this.periodDurations_
|
|
1310
|
+
this.config_.dash.initialSegmentLimit, this.periodDurations_,
|
|
1311
|
+
aes128Key);
|
|
1265
1312
|
} else {
|
|
1266
1313
|
goog.asserts.assert(isText,
|
|
1267
1314
|
'Must have Segment* with non-text streams.');
|
|
@@ -1360,6 +1407,7 @@ shaka.dash.DashParser = class {
|
|
|
1360
1407
|
stream = {
|
|
1361
1408
|
id: this.globalId_++,
|
|
1362
1409
|
originalId: context.representation.id,
|
|
1410
|
+
groupId: null,
|
|
1363
1411
|
createSegmentIndex: () => Promise.resolve(),
|
|
1364
1412
|
closeSegmentIndex: () => {
|
|
1365
1413
|
if (stream.segmentIndex) {
|
|
@@ -1453,6 +1501,8 @@ shaka.dash.DashParser = class {
|
|
|
1453
1501
|
return;
|
|
1454
1502
|
}
|
|
1455
1503
|
|
|
1504
|
+
this.playerInterface_.onManifestUpdated();
|
|
1505
|
+
|
|
1456
1506
|
this.setUpdateTimer_(updateDelay);
|
|
1457
1507
|
}
|
|
1458
1508
|
|
|
@@ -1926,10 +1976,8 @@ shaka.dash.DashParser = class {
|
|
|
1926
1976
|
endByte,
|
|
1927
1977
|
this.config_.retryParameters);
|
|
1928
1978
|
|
|
1929
|
-
const
|
|
1930
|
-
|
|
1931
|
-
this.operationManager_.manage(operation);
|
|
1932
|
-
const response = await operation.promise;
|
|
1979
|
+
const response = await this.makeNetworkRequest_(
|
|
1980
|
+
request, requestType, {type});
|
|
1933
1981
|
return response.data;
|
|
1934
1982
|
}
|
|
1935
1983
|
|
|
@@ -1955,6 +2003,24 @@ shaka.dash.DashParser = class {
|
|
|
1955
2003
|
// types well.
|
|
1956
2004
|
return mimeType.split('/')[0];
|
|
1957
2005
|
}
|
|
2006
|
+
|
|
2007
|
+
|
|
2008
|
+
/**
|
|
2009
|
+
* Create a networking request. This will manage the request using the
|
|
2010
|
+
* parser's operation manager.
|
|
2011
|
+
*
|
|
2012
|
+
* @param {shaka.extern.Request} request
|
|
2013
|
+
* @param {shaka.net.NetworkingEngine.RequestType} type
|
|
2014
|
+
* @param {shaka.extern.RequestContext=} context
|
|
2015
|
+
* @return {!Promise.<shaka.extern.Response>}
|
|
2016
|
+
* @private
|
|
2017
|
+
*/
|
|
2018
|
+
makeNetworkRequest_(request, type, context) {
|
|
2019
|
+
const op = this.playerInterface_.networkingEngine.request(
|
|
2020
|
+
type, request, context);
|
|
2021
|
+
this.operationManager_.manage(op);
|
|
2022
|
+
return op.promise;
|
|
2023
|
+
}
|
|
1958
2024
|
};
|
|
1959
2025
|
|
|
1960
2026
|
|
package/lib/dash/mpd_utils.js
CHANGED
|
@@ -30,7 +30,7 @@ shaka.dash.MpdUtils = class {
|
|
|
30
30
|
* @param {?string} representationId
|
|
31
31
|
* @param {?number} number
|
|
32
32
|
* @param {?number} bandwidth
|
|
33
|
-
* @param {?number} time
|
|
33
|
+
* @param {?(number|bigint)} time
|
|
34
34
|
* @return {string} A URI string.
|
|
35
35
|
* @see ISO/IEC 23009-1:2014 section 5.3.9.4.4
|
|
36
36
|
*/
|
|
@@ -71,11 +71,15 @@ shaka.dash.MpdUtils = class {
|
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
if (name == 'Time') {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
74
|
+
if (typeof value != 'bigint') {
|
|
75
|
+
goog.asserts.assert(typeof value == 'number',
|
|
76
|
+
'Time value should be a number or bigint!');
|
|
77
|
+
if (Math.abs(value - Math.round(value)) >= 0.2) {
|
|
78
|
+
shaka.log.alwaysWarn(
|
|
79
|
+
'Calculated $Time$ values must be close to integers');
|
|
80
|
+
}
|
|
81
|
+
value = Math.round(value);
|
|
82
|
+
}
|
|
79
83
|
}
|
|
80
84
|
|
|
81
85
|
/** @type {string} */
|
package/lib/dash/segment_base.js
CHANGED
|
@@ -31,9 +31,10 @@ shaka.dash.SegmentBase = class {
|
|
|
31
31
|
*
|
|
32
32
|
* @param {shaka.dash.DashParser.Context} context
|
|
33
33
|
* @param {function(?shaka.dash.DashParser.InheritanceFrame):Element} callback
|
|
34
|
+
* @param {shaka.extern.aes128Key|undefined} aes128Key
|
|
34
35
|
* @return {shaka.media.InitSegmentReference}
|
|
35
36
|
*/
|
|
36
|
-
static createInitSegment(context, callback) {
|
|
37
|
+
static createInitSegment(context, callback, aes128Key) {
|
|
37
38
|
const MpdUtils = shaka.dash.MpdUtils;
|
|
38
39
|
const XmlUtils = shaka.util.XmlUtils;
|
|
39
40
|
const ManifestParserUtils = shaka.util.ManifestParserUtils;
|
|
@@ -63,7 +64,13 @@ shaka.dash.SegmentBase = class {
|
|
|
63
64
|
const getUris = () => resolvedUris;
|
|
64
65
|
const qualityInfo = shaka.dash.SegmentBase.createQualityInfo(context);
|
|
65
66
|
return new shaka.media.InitSegmentReference(
|
|
66
|
-
getUris,
|
|
67
|
+
getUris,
|
|
68
|
+
startByte,
|
|
69
|
+
endByte,
|
|
70
|
+
qualityInfo,
|
|
71
|
+
/* timescale= */ null,
|
|
72
|
+
/* segmentData= */ null,
|
|
73
|
+
aes128Key);
|
|
67
74
|
}
|
|
68
75
|
|
|
69
76
|
/**
|
|
@@ -71,9 +78,10 @@ shaka.dash.SegmentBase = class {
|
|
|
71
78
|
*
|
|
72
79
|
* @param {shaka.dash.DashParser.Context} context
|
|
73
80
|
* @param {shaka.dash.DashParser.RequestSegmentCallback} requestSegment
|
|
81
|
+
* @param {shaka.extern.aes128Key|undefined} aes128Key
|
|
74
82
|
* @return {shaka.dash.DashParser.StreamInfo}
|
|
75
83
|
*/
|
|
76
|
-
static createStreamInfo(context, requestSegment) {
|
|
84
|
+
static createStreamInfo(context, requestSegment, aes128Key) {
|
|
77
85
|
goog.asserts.assert(context.representation.segmentBase,
|
|
78
86
|
'Should only be called with SegmentBase');
|
|
79
87
|
// Since SegmentBase does not need updates, simply treat any call as
|
|
@@ -95,8 +103,8 @@ shaka.dash.SegmentBase = class {
|
|
|
95
103
|
const scaledPresentationTimeOffset =
|
|
96
104
|
(unscaledPresentationTimeOffset / timescale) || 0;
|
|
97
105
|
|
|
98
|
-
const initSegmentReference =
|
|
99
|
-
|
|
106
|
+
const initSegmentReference = SegmentBase.createInitSegment(
|
|
107
|
+
context, SegmentBase.fromInheritance_, aes128Key);
|
|
100
108
|
|
|
101
109
|
// Throws an immediate error if the format is unsupported.
|
|
102
110
|
SegmentBase.checkSegmentIndexRangeSupport_(context, initSegmentReference);
|
package/lib/dash/segment_list.js
CHANGED
|
@@ -31,15 +31,16 @@ shaka.dash.SegmentList = class {
|
|
|
31
31
|
*
|
|
32
32
|
* @param {shaka.dash.DashParser.Context} context
|
|
33
33
|
* @param {!Object.<string, !shaka.extern.Stream>} streamMap
|
|
34
|
+
* @param {shaka.extern.aes128Key|undefined} aes128Key
|
|
34
35
|
* @return {shaka.dash.DashParser.StreamInfo}
|
|
35
36
|
*/
|
|
36
|
-
static createStreamInfo(context, streamMap) {
|
|
37
|
+
static createStreamInfo(context, streamMap, aes128Key) {
|
|
37
38
|
goog.asserts.assert(context.representation.segmentList,
|
|
38
39
|
'Should only be called with SegmentList');
|
|
39
40
|
const SegmentList = shaka.dash.SegmentList;
|
|
40
41
|
|
|
41
42
|
const initSegmentReference = shaka.dash.SegmentBase.createInitSegment(
|
|
42
|
-
context, SegmentList.fromInheritance_);
|
|
43
|
+
context, SegmentList.fromInheritance_, aes128Key);
|
|
43
44
|
const info = SegmentList.parseSegmentListInfo_(context);
|
|
44
45
|
|
|
45
46
|
SegmentList.checkSegmentListInfo_(context, info);
|
|
@@ -59,7 +60,7 @@ shaka.dash.SegmentList = class {
|
|
|
59
60
|
const references = SegmentList.createSegmentReferences_(
|
|
60
61
|
context.periodInfo.start, context.periodInfo.duration,
|
|
61
62
|
info.startNumber, context.representation.baseUris, info,
|
|
62
|
-
initSegmentReference);
|
|
63
|
+
initSegmentReference, aes128Key);
|
|
63
64
|
|
|
64
65
|
const isNew = !segmentIndex;
|
|
65
66
|
if (segmentIndex) {
|
|
@@ -197,12 +198,13 @@ shaka.dash.SegmentList = class {
|
|
|
197
198
|
* @param {!Array.<string>} baseUris
|
|
198
199
|
* @param {shaka.dash.SegmentList.SegmentListInfo} info
|
|
199
200
|
* @param {shaka.media.InitSegmentReference} initSegmentReference
|
|
201
|
+
* @param {shaka.extern.aes128Key|undefined} aes128Key
|
|
200
202
|
* @return {!Array.<!shaka.media.SegmentReference>}
|
|
201
203
|
* @private
|
|
202
204
|
*/
|
|
203
205
|
static createSegmentReferences_(
|
|
204
206
|
periodStart, periodDuration, startNumber, baseUris, info,
|
|
205
|
-
initSegmentReference) {
|
|
207
|
+
initSegmentReference, aes128Key) {
|
|
206
208
|
const ManifestParserUtils = shaka.util.ManifestParserUtils;
|
|
207
209
|
|
|
208
210
|
let max = info.mediaSegments.length;
|
|
@@ -257,7 +259,13 @@ shaka.dash.SegmentList = class {
|
|
|
257
259
|
segment.end,
|
|
258
260
|
initSegmentReference,
|
|
259
261
|
timestampOffset,
|
|
260
|
-
appendWindowStart, appendWindowEnd
|
|
262
|
+
appendWindowStart, appendWindowEnd,
|
|
263
|
+
/* partialReferences= */ [],
|
|
264
|
+
/* tilesLayout= */ '',
|
|
265
|
+
/* tileDuration= */ null,
|
|
266
|
+
/* syncTime= */ null,
|
|
267
|
+
shaka.media.SegmentReference.Status.AVAILABLE,
|
|
268
|
+
aes128Key));
|
|
261
269
|
prevEndTime = endTime;
|
|
262
270
|
}
|
|
263
271
|
|