shaka-player 3.3.4 → 4.0.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/build-and-test.yaml +0 -1
- package/.github/workflows/demo-version-index.yaml +8 -0
- package/.github/workflows/release-please.yaml +23 -2
- package/.github/workflows/selenium-lab-tests.yaml +0 -1
- package/.github/workflows/sync-labels.yaml +33 -0
- package/.github/workflows/update-issues.yaml +29 -0
- package/.github/workflows/validate-pr-title.yaml +35 -0
- package/AUTHORS +1 -0
- package/CHANGELOG.md +198 -32
- package/CONTRIBUTORS +1 -0
- package/README.md +10 -7
- package/app-engine/demo-version-index/README.md +3 -3
- package/app-engine/demo-version-index/app.yaml +27 -1
- package/app-engine/demo-version-index/generate.py +164 -0
- package/app-engine/demo-version-index/main.py +4 -155
- package/app-engine/demo-version-index/requirements.txt +0 -1
- package/build/README.md +2 -2
- package/build/compiler.py +1 -1
- package/build/generateTsDefs.py +0 -2
- package/build/shaka-lab.yaml +0 -2
- package/build/test.py +7 -1
- package/build/types/core +0 -1
- package/build/types/polyfill +0 -3
- package/demo/common/asset.js +17 -1
- package/demo/common/assets.js +29 -0
- package/demo/common/message_ids.js +6 -3
- package/demo/config.js +10 -11
- package/demo/custom.js +39 -0
- package/demo/demo_utils.js +0 -1
- package/demo/locales/en.json +6 -3
- package/demo/locales/source.json +22 -10
- package/demo/main.js +9 -6
- package/demo/search.js +2 -1
- package/dist/controls.css +1 -1
- package/dist/demo.compiled.debug.js +71 -64
- package/dist/demo.compiled.debug.map +3 -3
- package/dist/demo.compiled.js +71 -64
- package/dist/demo.compiled.map +3 -3
- package/dist/demo.css +1 -1
- package/dist/demo.css.map +1 -1
- package/dist/deps.js +16 -20
- package/dist/locales.js +31 -29
- package/dist/receiver.compiled.debug.js +32 -28
- package/dist/receiver.compiled.debug.map +3 -3
- package/dist/receiver.compiled.js +32 -28
- package/dist/receiver.compiled.map +3 -3
- package/dist/shaka-player.compiled.d.ts +78 -83
- package/dist/shaka-player.compiled.debug.d.ts +78 -83
- package/dist/shaka-player.compiled.debug.externs.js +126 -126
- package/dist/shaka-player.compiled.debug.js +426 -483
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +126 -126
- package/dist/shaka-player.compiled.js +838 -856
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +80 -83
- package/dist/shaka-player.ui.debug.d.ts +80 -83
- package/dist/shaka-player.ui.debug.externs.js +134 -126
- package/dist/shaka-player.ui.debug.js +488 -543
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +134 -126
- package/dist/shaka-player.ui.js +980 -994
- package/dist/shaka-player.ui.map +4 -4
- package/docs/tutorials/config.md +0 -2
- package/docs/tutorials/drm-config.md +5 -0
- package/docs/tutorials/fairplay.md +7 -22
- package/docs/tutorials/faq.md +0 -11
- package/docs/tutorials/index.json +2 -8
- package/docs/tutorials/plugins.md +1 -3
- package/docs/tutorials/upgrade.md +83 -11
- package/externs/polyfill.js +15 -0
- package/externs/shaka/manifest.js +5 -1
- package/externs/shaka/net.js +12 -0
- package/externs/shaka/offline.js +5 -1
- package/externs/shaka/player.js +39 -32
- package/externs/shaka/text.js +12 -12
- package/externs/sourcebuffer.js +15 -0
- package/lib/ads/client_side_ad.js +13 -2
- package/lib/ads/client_side_ad_manager.js +22 -4
- package/lib/cast/cast_utils.js +0 -1
- package/lib/dash/content_protection.js +85 -5
- package/lib/dash/dash_parser.js +1 -1
- package/lib/hls/hls_parser.js +378 -743
- package/lib/hls/manifest_text_parser.js +2 -3
- package/lib/media/drm_engine.js +102 -53
- package/lib/media/gap_jumping_controller.js +10 -45
- package/lib/media/media_source_engine.js +87 -15
- package/lib/media/playhead.js +2 -5
- package/lib/media/presentation_timeline.js +1 -2
- package/lib/media/segment_index.js +13 -46
- package/lib/media/segment_reference.js +47 -1
- package/lib/media/streaming_engine.js +29 -11
- package/lib/media/time_ranges_utils.js +4 -9
- package/lib/net/networking_engine.js +3 -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 +100 -132
- package/lib/player.js +17 -201
- package/lib/polyfill/media_capabilities.js +12 -6
- package/lib/polyfill/mediasource.js +0 -28
- package/lib/polyfill/patchedmediakeys_nop.js +2 -0
- package/lib/polyfill/patchedmediakeys_webkit.js +2 -0
- package/lib/text/cue.js +0 -6
- package/lib/text/lrc_text_parser.js +8 -0
- package/lib/text/mp4_ttml_parser.js +8 -0
- package/lib/text/mp4_vtt_parser.js +8 -0
- package/lib/text/sbv_text_parser.js +8 -0
- package/lib/text/simple_text_displayer.js +1 -7
- package/lib/text/srt_text_parser.js +8 -0
- package/lib/text/ssa_text_parser.js +8 -0
- package/lib/text/text_engine.js +19 -3
- package/lib/text/ttml_text_parser.js +8 -0
- package/lib/text/ui_text_displayer.js +1 -7
- package/lib/text/vtt_text_parser.js +24 -2
- package/lib/text/web_vtt_generator.js +1 -7
- package/lib/util/error.js +9 -13
- package/lib/util/fairplay_utils.js +109 -0
- package/lib/util/functional.js +0 -42
- package/lib/util/mime_utils.js +2 -0
- package/lib/util/platform.js +45 -0
- package/lib/util/player_configuration.js +12 -6
- package/lib/util/pssh.js +23 -4
- package/lib/util/stream_utils.js +92 -22
- package/lib/util/uint8array_utils.js +0 -16
- package/lib/util/xml_utils.js +1 -1
- package/package.json +1 -1
- package/roadmap.md +19 -19
- package/shaka-player.uncompiled.js +0 -3
- package/test/cast/cast_utils_unit.js +1 -0
- package/test/dash/dash_parser_content_protection_unit.js +44 -0
- package/test/dash/dash_parser_manifest_unit.js +0 -24
- package/test/dash/dash_parser_segment_template_unit.js +3 -4
- package/test/demo/demo_unit.js +2 -1
- package/test/hls/hls_live_unit.js +99 -220
- package/test/hls/hls_parser_unit.js +515 -432
- package/test/media/drm_engine_unit.js +86 -120
- package/test/media/media_source_engine_integration.js +26 -3
- package/test/media/media_source_engine_unit.js +7 -6
- package/test/media/playhead_unit.js +27 -191
- package/test/media/segment_index_unit.js +15 -25
- package/test/media/streaming_engine_integration.js +4 -39
- package/test/media/streaming_engine_unit.js +3 -2
- package/test/media/time_ranges_utils_unit.js +0 -5
- package/test/offline/manifest_convert_unit.js +34 -4
- package/test/offline/offline_manifest_parser_unit.js +1 -0
- package/test/offline/storage_integration.js +5 -4
- package/test/player_external.js +0 -6
- package/test/player_integration.js +16 -0
- package/test/player_unit.js +26 -0
- package/test/test/util/fake_media_source_engine.js +11 -4
- package/test/test/util/manifest_generator.js +3 -1
- package/test/test/util/manifest_parser_util.js +4 -1
- package/test/test/util/offline_utils.js +1 -0
- package/test/test/util/streaming_engine_util.js +1 -0
- package/test/test/util/test_scheme.js +24 -1
- package/test/test/util/util.js +4 -4
- package/test/text/cue_integration.js +2 -2
- package/test/text/lrc_text_parser_unit.js +6 -7
- package/test/text/mp4_ttml_parser_unit.js +6 -4
- package/test/text/mp4_vtt_parser_unit.js +6 -5
- package/test/text/sbv_text_parser_unit.js +5 -6
- package/test/text/srt_text_parser_unit.js +5 -6
- package/test/text/ssa_text_parser_unit.js +17 -29
- package/test/text/text_engine_unit.js +45 -5
- package/test/text/ttml_text_parser_unit.js +77 -77
- package/test/text/vtt_text_parser_unit.js +55 -57
- package/test/ui/ui_integration.js +0 -16
- package/test/ui/ui_unit.js +27 -0
- package/test/util/cmcd_manager_unit.js +3 -0
- package/test/util/stream_utils_unit.js +23 -7
- package/ui/controls.js +65 -40
- package/ui/fullscreen_button.js +26 -6
- package/ui/less/containers.less +3 -3
- package/ui/locales/en.json +1 -0
- package/ui/locales/source.json +5 -0
- package/ui/resolution_selection.js +31 -33
- package/ui/ui.js +0 -17
- package/docs/tutorials/upgrade-v2-5.md +0 -441
- package/docs/tutorials/upgrade-v3.md +0 -38
- package/lib/polyfill/mathround.js +0 -49
- package/lib/polyfill/patchedmediakeys_apple.js +0 -744
- package/lib/polyfill/patchedmediakeys_ms.js +0 -698
- package/lib/util/mutex.js +0 -52
- package/test/util/functional_unit.js +0 -82
|
@@ -22,6 +22,12 @@ goog.require('shaka.util.XmlUtils');
|
|
|
22
22
|
* @export
|
|
23
23
|
*/
|
|
24
24
|
shaka.text.VttTextParser = class {
|
|
25
|
+
/** Constructs a VTT parser. */
|
|
26
|
+
constructor() {
|
|
27
|
+
/** @private {boolean} */
|
|
28
|
+
this.sequenceMode_ = false;
|
|
29
|
+
}
|
|
30
|
+
|
|
25
31
|
/**
|
|
26
32
|
* @override
|
|
27
33
|
* @export
|
|
@@ -30,6 +36,14 @@ shaka.text.VttTextParser = class {
|
|
|
30
36
|
goog.asserts.assert(false, 'VTT does not have init segments');
|
|
31
37
|
}
|
|
32
38
|
|
|
39
|
+
/**
|
|
40
|
+
* @override
|
|
41
|
+
* @export
|
|
42
|
+
*/
|
|
43
|
+
setSequenceMode(sequenceMode) {
|
|
44
|
+
this.sequenceMode_ = sequenceMode;
|
|
45
|
+
}
|
|
46
|
+
|
|
33
47
|
/**
|
|
34
48
|
* @override
|
|
35
49
|
* @export
|
|
@@ -48,11 +62,19 @@ shaka.text.VttTextParser = class {
|
|
|
48
62
|
shaka.util.Error.Code.INVALID_TEXT_HEADER);
|
|
49
63
|
}
|
|
50
64
|
|
|
65
|
+
// Depending on "segmentRelativeVttTiming" configuration,
|
|
66
|
+
// "vttOffset" will correspond to either "periodStart" (default)
|
|
67
|
+
// or "segmentStart", for segmented VTT where timings are relative
|
|
68
|
+
// to the beginning of each segment.
|
|
51
69
|
// NOTE: "periodStart" is the timestamp offset applied via TextEngine.
|
|
52
70
|
// It is no longer closely tied to periods, but the name stuck around.
|
|
53
|
-
let offset = time.
|
|
71
|
+
let offset = time.vttOffset;
|
|
54
72
|
|
|
55
|
-
|
|
73
|
+
// Do not honor the 'X-TIMESTAMP-MAP' value when in sequence mode.
|
|
74
|
+
// That is because it is used mainly (solely?) to account for the timestamp
|
|
75
|
+
// offset of the video/audio; when in sequence mode, we normalize that
|
|
76
|
+
// timestamp offset to 0, so we should not account for it.
|
|
77
|
+
if (blocks[0].includes('X-TIMESTAMP-MAP') && !this.sequenceMode_) {
|
|
56
78
|
// https://bit.ly/2K92l7y
|
|
57
79
|
// The 'X-TIMESTAMP-MAP' header is used in HLS to align text with
|
|
58
80
|
// the rest of the media.
|
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
|
|
7
7
|
goog.provide('shaka.text.WebVttGenerator');
|
|
8
8
|
|
|
9
|
-
goog.require('shaka.Deprecate');
|
|
10
9
|
goog.require('shaka.text.Cue');
|
|
11
10
|
|
|
12
11
|
|
|
@@ -49,12 +48,7 @@ shaka.text.WebVttGenerator = class {
|
|
|
49
48
|
return `${acc}</${tag}>`;
|
|
50
49
|
}, '');
|
|
51
50
|
|
|
52
|
-
if (cue.lineBreak
|
|
53
|
-
if (cue.spacer) {
|
|
54
|
-
shaka.Deprecate.deprecateFeature(4,
|
|
55
|
-
'shaka.text.Cue',
|
|
56
|
-
'Please use lineBreak instead of spacer.');
|
|
57
|
-
}
|
|
51
|
+
if (cue.lineBreak) {
|
|
58
52
|
// This is a vertical lineBreak, so insert a newline.
|
|
59
53
|
return '\n';
|
|
60
54
|
} else if (cue.nestedCues.length) {
|
package/lib/util/error.js
CHANGED
|
@@ -581,13 +581,7 @@ shaka.util.Error.Code = {
|
|
|
581
581
|
|
|
582
582
|
// RETIRED: 'HLS_COULD_NOT_GUESS_MIME_TYPE': 4021,
|
|
583
583
|
|
|
584
|
-
|
|
585
|
-
* No Master Playlist has been provided. Master playlist provides
|
|
586
|
-
* vital information about the streams (like codecs) that is
|
|
587
|
-
* required for MediaSource. We don't support directly providing
|
|
588
|
-
* a Media Playlist.
|
|
589
|
-
*/
|
|
590
|
-
'HLS_MASTER_PLAYLIST_NOT_PROVIDED': 4022,
|
|
584
|
+
// RETIRED: 'HLS_MASTER_PLAYLIST_NOT_PROVIDED': 4022,
|
|
591
585
|
|
|
592
586
|
/**
|
|
593
587
|
* One of the required attributes was not provided, so the
|
|
@@ -627,12 +621,7 @@ shaka.util.Error.Code = {
|
|
|
627
621
|
|
|
628
622
|
// RETIRED: 'HLS_LIVE_CONTENT_NOT_SUPPORTED': 4029,
|
|
629
623
|
|
|
630
|
-
|
|
631
|
-
* The HLS parser was unable to parse segment start time from the media.
|
|
632
|
-
* <br> error.data[0] is the failed media playlist URI.
|
|
633
|
-
* <br> error.data[1] is the failed media segment URI (if any).
|
|
634
|
-
*/
|
|
635
|
-
'HLS_COULD_NOT_PARSE_SEGMENT_START_TIME': 4030,
|
|
624
|
+
// RETIRED: 'HLS_COULD_NOT_PARSE_SEGMENT_START_TIME': 4030,
|
|
636
625
|
|
|
637
626
|
// RETIRED: 'HLS_MEDIA_SEQUENCE_REQUIRED_IN_LIVE_STREAMS': 4031,
|
|
638
627
|
|
|
@@ -686,6 +675,13 @@ shaka.util.Error.Code = {
|
|
|
686
675
|
*/
|
|
687
676
|
'HLS_VARIABLE_NOT_FOUND': 4039,
|
|
688
677
|
|
|
678
|
+
/**
|
|
679
|
+
* We do not support playing encrypted mp2t with MSE
|
|
680
|
+
*/
|
|
681
|
+
'HLS_MSE_ENCRYPTED_MP2T_NOT_SUPPORTED': 4040,
|
|
682
|
+
|
|
683
|
+
// RETIRED: 'HLS_MSE_ENCRYPTED_LEGACY_APPLE_MEDIA_KEYS_NOT_SUPPORTED': 4041,
|
|
684
|
+
|
|
689
685
|
|
|
690
686
|
// RETIRED: 'INCONSISTENT_BUFFER_STATE': 5000,
|
|
691
687
|
// RETIRED: 'INVALID_SEGMENT_INDEX': 5001,
|
|
@@ -8,9 +8,11 @@ goog.provide('shaka.util.FairPlayUtils');
|
|
|
8
8
|
|
|
9
9
|
goog.require('goog.Uri');
|
|
10
10
|
goog.require('goog.asserts');
|
|
11
|
+
goog.require('shaka.net.NetworkingEngine');
|
|
11
12
|
goog.require('shaka.util.BufferUtils');
|
|
12
13
|
goog.require('shaka.util.Error');
|
|
13
14
|
goog.require('shaka.util.StringUtils');
|
|
15
|
+
goog.require('shaka.util.Uint8ArrayUtils');
|
|
14
16
|
|
|
15
17
|
|
|
16
18
|
/**
|
|
@@ -18,6 +20,29 @@ goog.require('shaka.util.StringUtils');
|
|
|
18
20
|
* @export
|
|
19
21
|
*/
|
|
20
22
|
shaka.util.FairPlayUtils = class {
|
|
23
|
+
/**
|
|
24
|
+
* Check if FairPlay is supported.
|
|
25
|
+
*
|
|
26
|
+
* @return {!Promise.<boolean>}
|
|
27
|
+
* @export
|
|
28
|
+
*/
|
|
29
|
+
static async isFairPlaySupported() {
|
|
30
|
+
const config = {
|
|
31
|
+
initDataTypes: ['cenc', 'sinf', 'skd'],
|
|
32
|
+
videoCapabilities: [
|
|
33
|
+
{
|
|
34
|
+
contentType: 'video/mp4; codecs="avc1.42E01E"',
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
};
|
|
38
|
+
try {
|
|
39
|
+
await navigator.requestMediaKeySystemAccess('com.apple.fps', [config]);
|
|
40
|
+
return true;
|
|
41
|
+
} catch (err) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
21
46
|
/**
|
|
22
47
|
* Using the default method, extract a content ID from the init data. This is
|
|
23
48
|
* based on the FairPlay example documentation.
|
|
@@ -106,4 +131,88 @@ shaka.util.FairPlayUtils = class {
|
|
|
106
131
|
offset == rebuiltInitData.length, 'Inconsistent init data length');
|
|
107
132
|
return rebuiltInitData;
|
|
108
133
|
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* SPC FairPlay request.
|
|
137
|
+
*
|
|
138
|
+
* @param {shaka.net.NetworkingEngine.RequestType} type
|
|
139
|
+
* @param {shaka.extern.Request} request
|
|
140
|
+
* @export
|
|
141
|
+
*/
|
|
142
|
+
static spcFairPlayRequest(type, request) {
|
|
143
|
+
if (type !== shaka.net.NetworkingEngine.RequestType.LICENSE) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const body = /** @type {!(ArrayBuffer|ArrayBufferView)} */(request.body);
|
|
147
|
+
const originalPayload = shaka.util.BufferUtils.toUint8(body);
|
|
148
|
+
const base64Payload = shaka.util.Uint8ArrayUtils.toBase64(originalPayload);
|
|
149
|
+
request.headers['Content-Type'] = 'application/x-www-form-urlencoded';
|
|
150
|
+
request.body = shaka.util.StringUtils.toUTF8('spc=' + base64Payload);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Common FairPlay response transform for some DRMs providers.
|
|
155
|
+
*
|
|
156
|
+
* @param {shaka.net.NetworkingEngine.RequestType} type
|
|
157
|
+
* @param {shaka.extern.Response} response
|
|
158
|
+
* @export
|
|
159
|
+
*/
|
|
160
|
+
static commonFairPlayResponse(type, response) {
|
|
161
|
+
if (type !== shaka.net.NetworkingEngine.RequestType.LICENSE) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// In Apple's docs, responses can be of the form:
|
|
166
|
+
// '\n<ckc>base64encoded</ckc>\n' or 'base64encoded'
|
|
167
|
+
// We have also seen responses in JSON format from some of our partners.
|
|
168
|
+
// In all of these text-based formats, the CKC data is base64-encoded.
|
|
169
|
+
|
|
170
|
+
let responseText;
|
|
171
|
+
try {
|
|
172
|
+
// Convert it to text for further processing.
|
|
173
|
+
responseText = shaka.util.StringUtils.fromUTF8(response.data);
|
|
174
|
+
} catch (error) {
|
|
175
|
+
// Assume it's not a text format of any kind and leave it alone.
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
let licenseProcessing = false;
|
|
180
|
+
|
|
181
|
+
// Trim whitespace.
|
|
182
|
+
responseText = responseText.trim();
|
|
183
|
+
|
|
184
|
+
// Look for <ckc> wrapper and remove it.
|
|
185
|
+
if (responseText.substr(0, 5) === '<ckc>' &&
|
|
186
|
+
responseText.substr(-6) === '</ckc>') {
|
|
187
|
+
responseText = responseText.slice(5, -6);
|
|
188
|
+
licenseProcessing = true;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Look for a JSON wrapper and remove it.
|
|
192
|
+
try {
|
|
193
|
+
const responseObject = /** @type {!Object} */(JSON.parse(responseText));
|
|
194
|
+
if (responseObject['ckc']) {
|
|
195
|
+
responseText = responseObject['ckc'];
|
|
196
|
+
licenseProcessing = true;
|
|
197
|
+
}
|
|
198
|
+
if (responseObject['CkcMessage']) {
|
|
199
|
+
responseText = responseObject['CkcMessage'];
|
|
200
|
+
licenseProcessing = true;
|
|
201
|
+
}
|
|
202
|
+
if (responseObject['License']) {
|
|
203
|
+
responseText = responseObject['License'];
|
|
204
|
+
licenseProcessing = true;
|
|
205
|
+
}
|
|
206
|
+
} catch (err) {
|
|
207
|
+
// It wasn't JSON. Fall through with other transformations.
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (licenseProcessing) {
|
|
211
|
+
// Decode the base64-encoded data into the format the browser expects.
|
|
212
|
+
// It's not clear why FairPlay license servers don't just serve this
|
|
213
|
+
// directly.
|
|
214
|
+
response.data = shaka.util.BufferUtils.toArrayBuffer(
|
|
215
|
+
shaka.util.Uint8ArrayUtils.fromBase64(responseText));
|
|
216
|
+
}
|
|
217
|
+
}
|
|
109
218
|
};
|
package/lib/util/functional.js
CHANGED
|
@@ -6,9 +6,6 @@
|
|
|
6
6
|
|
|
7
7
|
goog.provide('shaka.util.Functional');
|
|
8
8
|
|
|
9
|
-
goog.require('shaka.Deprecate');
|
|
10
|
-
|
|
11
|
-
|
|
12
9
|
/**
|
|
13
10
|
* @summary A set of functional utility functions.
|
|
14
11
|
*/
|
|
@@ -70,43 +67,4 @@ shaka.util.Functional = class {
|
|
|
70
67
|
static isNotNull(value) {
|
|
71
68
|
return value != null;
|
|
72
69
|
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Calls a factory function while allowing it to be a constructor for
|
|
76
|
-
* reverse-compatibility.
|
|
77
|
-
*
|
|
78
|
-
* @param {function():!T} factory
|
|
79
|
-
* @return {!T}
|
|
80
|
-
* @template T
|
|
81
|
-
*/
|
|
82
|
-
static callFactory(factory) {
|
|
83
|
-
// See https://stackoverflow.com/q/10428603/1208502
|
|
84
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
85
|
-
const obj = Object.create(factory.prototype || Object.prototype);
|
|
86
|
-
// If this is a constructor, call it with our newly created object to
|
|
87
|
-
// initialize it; if this isn't a constructor, the "this" shouldn't be used
|
|
88
|
-
// since it should be "undefined".
|
|
89
|
-
let ret;
|
|
90
|
-
try {
|
|
91
|
-
ret = factory.call(obj); // eslint-disable-line no-restricted-syntax
|
|
92
|
-
|
|
93
|
-
// If it didn't return anything, assume it is a constructor and return our
|
|
94
|
-
// "this" value instead.
|
|
95
|
-
if (!ret) {
|
|
96
|
-
shaka.Deprecate.deprecateFeature(4,
|
|
97
|
-
'Factories requiring new',
|
|
98
|
-
'Factories should be plain functions');
|
|
99
|
-
ret = obj;
|
|
100
|
-
}
|
|
101
|
-
} catch (e) {
|
|
102
|
-
// This was an ES6 class, so it threw a TypeError because we didn't use
|
|
103
|
-
// "new". Fall back to actually using "new".
|
|
104
|
-
shaka.Deprecate.deprecateFeature(4,
|
|
105
|
-
'Factories requiring new',
|
|
106
|
-
'Factories should be plain functions');
|
|
107
|
-
const FactoryAsClass = /** @type {function(new: T)} */(factory);
|
|
108
|
-
ret = new FactoryAsClass();
|
|
109
|
-
}
|
|
110
|
-
return ret;
|
|
111
|
-
}
|
|
112
70
|
};
|
package/lib/util/mime_utils.js
CHANGED
|
@@ -11,6 +11,7 @@ goog.require('shaka.media.Transmuxer');
|
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* @summary A set of utility functions for dealing with MIME types.
|
|
14
|
+
* @export
|
|
14
15
|
*/
|
|
15
16
|
shaka.util.MimeUtils = class {
|
|
16
17
|
/**
|
|
@@ -20,6 +21,7 @@ shaka.util.MimeUtils = class {
|
|
|
20
21
|
* @param {string} mimeType
|
|
21
22
|
* @param {string=} codecs
|
|
22
23
|
* @return {string}
|
|
24
|
+
* @export
|
|
23
25
|
*/
|
|
24
26
|
static getFullType(mimeType, codecs) {
|
|
25
27
|
let fullMimeType = mimeType;
|
package/lib/util/platform.js
CHANGED
|
@@ -142,6 +142,18 @@ shaka.util.Platform = class {
|
|
|
142
142
|
return shaka.util.Platform.userAgentContains_('Web0S');
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
+
/**
|
|
146
|
+
* Check if the current platform is a WebOS 3.
|
|
147
|
+
*
|
|
148
|
+
* @return {boolean}
|
|
149
|
+
*/
|
|
150
|
+
static isWebOS3() {
|
|
151
|
+
// See: http://webostv.developer.lge.com/discover/specifications/web-engine/
|
|
152
|
+
return shaka.util.Platform.userAgentContains_('Web0S') &&
|
|
153
|
+
shaka.util.Platform.userAgentContains_(
|
|
154
|
+
'Chrome/38.0.2125.122 Safari/537.36');
|
|
155
|
+
}
|
|
156
|
+
|
|
145
157
|
/**
|
|
146
158
|
* Check if the current platform is a Google Chromecast.
|
|
147
159
|
*
|
|
@@ -151,6 +163,16 @@ shaka.util.Platform = class {
|
|
|
151
163
|
return shaka.util.Platform.userAgentContains_('CrKey');
|
|
152
164
|
}
|
|
153
165
|
|
|
166
|
+
/**
|
|
167
|
+
* Check if the current platform is a Android-based Cast devices.
|
|
168
|
+
*
|
|
169
|
+
* @return {boolean}
|
|
170
|
+
*/
|
|
171
|
+
static isAndroidCastDevice() {
|
|
172
|
+
return shaka.util.Platform.isChromecast() &&
|
|
173
|
+
shaka.util.Platform.userAgentContains_('Android');
|
|
174
|
+
}
|
|
175
|
+
|
|
154
176
|
/**
|
|
155
177
|
* Check if the current platform is Google Chrome.
|
|
156
178
|
*
|
|
@@ -228,6 +250,16 @@ shaka.util.Platform = class {
|
|
|
228
250
|
return null;
|
|
229
251
|
}
|
|
230
252
|
|
|
253
|
+
/**
|
|
254
|
+
* Check if the current platform is Apple Safari
|
|
255
|
+
* or Safari-based iOS browsers.
|
|
256
|
+
*
|
|
257
|
+
* @return {boolean}
|
|
258
|
+
*/
|
|
259
|
+
static isSafari() {
|
|
260
|
+
return !!shaka.util.Platform.safariVersion();
|
|
261
|
+
}
|
|
262
|
+
|
|
231
263
|
/**
|
|
232
264
|
* Guesses if the platform is a mobile one (iOS or Android).
|
|
233
265
|
*
|
|
@@ -318,6 +350,19 @@ shaka.util.Platform = class {
|
|
|
318
350
|
const Platform = shaka.util.Platform;
|
|
319
351
|
return Platform.isTizen() || Platform.isXboxOne();
|
|
320
352
|
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Returns true if MediaKeys is polyfilled
|
|
356
|
+
*
|
|
357
|
+
* @return {boolean}
|
|
358
|
+
*/
|
|
359
|
+
static isMediaKeysPolyfilled() {
|
|
360
|
+
if (window.shakaMediaKeysPolyfill) {
|
|
361
|
+
return true;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
return false;
|
|
365
|
+
}
|
|
321
366
|
};
|
|
322
367
|
|
|
323
368
|
/** @private {shaka.util.Timer} */
|
|
@@ -9,7 +9,6 @@ goog.provide('shaka.util.PlayerConfiguration');
|
|
|
9
9
|
goog.require('goog.asserts');
|
|
10
10
|
goog.require('shaka.abr.SimpleAbrManager');
|
|
11
11
|
goog.require('shaka.log');
|
|
12
|
-
goog.require('shaka.media.DrmEngine');
|
|
13
12
|
goog.require('shaka.net.NetworkingEngine');
|
|
14
13
|
goog.require('shaka.util.ConfigUtils');
|
|
15
14
|
goog.require('shaka.util.LanguageUtils');
|
|
@@ -30,7 +29,10 @@ goog.require('shaka.util.Platform');
|
|
|
30
29
|
* @export
|
|
31
30
|
*/
|
|
32
31
|
shaka.util.PlayerConfiguration = class {
|
|
33
|
-
/**
|
|
32
|
+
/**
|
|
33
|
+
* @return {shaka.extern.PlayerConfiguration}
|
|
34
|
+
* @export
|
|
35
|
+
*/
|
|
34
36
|
static createDefault() {
|
|
35
37
|
// This is a relatively safe default in the absence of clues from the
|
|
36
38
|
// browser. For slower connections, the default estimate may be too high.
|
|
@@ -72,7 +74,6 @@ shaka.util.PlayerConfiguration = class {
|
|
|
72
74
|
clearKeys: {}, // key is arbitrary key system ID, value must be string
|
|
73
75
|
advanced: {}, // key is arbitrary key system ID, value is a record type
|
|
74
76
|
delayLicenseRequestUntilPlayed: false,
|
|
75
|
-
initDataTransform: shaka.media.DrmEngine.defaultInitDataTransform,
|
|
76
77
|
logLicenseExchange: false,
|
|
77
78
|
updateExpirationTime: 1,
|
|
78
79
|
preferredKeySystems: [],
|
|
@@ -86,6 +87,7 @@ shaka.util.PlayerConfiguration = class {
|
|
|
86
87
|
disableText: false,
|
|
87
88
|
disableThumbnails: false,
|
|
88
89
|
defaultPresentationDelay: 0,
|
|
90
|
+
segmentRelativeVttTiming: false,
|
|
89
91
|
dash: {
|
|
90
92
|
clockSyncUri: '',
|
|
91
93
|
ignoreDrmInfo: false,
|
|
@@ -100,6 +102,8 @@ shaka.util.PlayerConfiguration = class {
|
|
|
100
102
|
keySystemsByURI: {
|
|
101
103
|
'urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b':
|
|
102
104
|
'org.w3.clearkey',
|
|
105
|
+
'urn:uuid:e2719d58-a985-b3c9-781a-b030af78d30e':
|
|
106
|
+
'org.w3.clearkey',
|
|
103
107
|
'urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed':
|
|
104
108
|
'com.widevine.alpha',
|
|
105
109
|
'urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95':
|
|
@@ -119,9 +123,11 @@ shaka.util.PlayerConfiguration = class {
|
|
|
119
123
|
hls: {
|
|
120
124
|
ignoreTextStreamFailures: false,
|
|
121
125
|
ignoreImageStreamFailures: false,
|
|
122
|
-
useFullSegmentsForStartTime: false,
|
|
123
126
|
defaultAudioCodec: 'mp4a.40.2',
|
|
124
127
|
defaultVideoCodec: 'avc1.42E01E',
|
|
128
|
+
ignoreManifestProgramDateTime: false,
|
|
129
|
+
mediaPlaylistFullMimeType:
|
|
130
|
+
'video/mp2t; codecs="avc1.42E01E, mp4a.40.2"',
|
|
125
131
|
},
|
|
126
132
|
};
|
|
127
133
|
|
|
@@ -143,8 +149,6 @@ shaka.util.PlayerConfiguration = class {
|
|
|
143
149
|
alwaysStreamText: false,
|
|
144
150
|
startAtSegmentBoundary: false,
|
|
145
151
|
gapDetectionThreshold: 0.1,
|
|
146
|
-
smallGapLimit: 0.5,
|
|
147
|
-
jumpLargeGaps: false,
|
|
148
152
|
durationBackoff: 1,
|
|
149
153
|
forceTransmuxTS: false,
|
|
150
154
|
// Offset by 5 seconds since Chromecast takes a few seconds to start
|
|
@@ -218,6 +222,8 @@ shaka.util.PlayerConfiguration = class {
|
|
|
218
222
|
// unexpected behaviours when someone tries to plays downloaded content
|
|
219
223
|
// without a persistent license.
|
|
220
224
|
usePersistentLicense: true,
|
|
225
|
+
|
|
226
|
+
numberOfParallelDownloads: 5,
|
|
221
227
|
};
|
|
222
228
|
|
|
223
229
|
const abr = {
|
package/lib/util/pssh.js
CHANGED
|
@@ -90,16 +90,21 @@ shaka.util.Pssh = class {
|
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
/**
|
|
93
|
-
* Creates a pssh blob from the given system ID and
|
|
93
|
+
* Creates a pssh blob from the given system ID, data, keyIds and version.
|
|
94
94
|
*
|
|
95
95
|
* @param {!Uint8Array} data
|
|
96
96
|
* @param {!Uint8Array} systemId
|
|
97
|
+
* @param {!Set.<string>} keyIds
|
|
98
|
+
* @param {number} version
|
|
97
99
|
* @return {!Uint8Array}
|
|
98
100
|
*/
|
|
99
|
-
static createPssh(data, systemId) {
|
|
101
|
+
static createPssh(data, systemId, keyIds, version) {
|
|
100
102
|
goog.asserts.assert(systemId.byteLength == 16, 'Invalid system ID length');
|
|
101
103
|
const dataLength = data.length;
|
|
102
|
-
|
|
104
|
+
let psshSize = 0x4 + 0x4 + 0x4 + systemId.length + 0x4 + dataLength;
|
|
105
|
+
if (version > 0) {
|
|
106
|
+
psshSize += 0x4 + (16 * keyIds.size);
|
|
107
|
+
}
|
|
103
108
|
|
|
104
109
|
/** @type {!Uint8Array} */
|
|
105
110
|
const psshBox = new Uint8Array(psshSize);
|
|
@@ -111,10 +116,24 @@ shaka.util.Pssh = class {
|
|
|
111
116
|
byteCursor += 0x4;
|
|
112
117
|
psshData.setUint32(byteCursor, 0x70737368); // 'pssh'
|
|
113
118
|
byteCursor += 0x4;
|
|
114
|
-
psshData.setUint32(byteCursor, 0)
|
|
119
|
+
(version < 1) ? psshData.setUint32(byteCursor, 0) :
|
|
120
|
+
psshData.setUint32(byteCursor, 0x01000000); // version + flags
|
|
115
121
|
byteCursor += 0x4;
|
|
116
122
|
psshBox.set(systemId, byteCursor);
|
|
117
123
|
byteCursor += systemId.length;
|
|
124
|
+
|
|
125
|
+
// if version > 0, add KID count and kid values.
|
|
126
|
+
if (version > 0) {
|
|
127
|
+
psshData.setUint32(byteCursor, keyIds.size); // KID_count
|
|
128
|
+
byteCursor += 0x4;
|
|
129
|
+
const Uint8ArrayUtils = shaka.util.Uint8ArrayUtils;
|
|
130
|
+
for (const keyId of keyIds) {
|
|
131
|
+
const KID = Uint8ArrayUtils.fromHex(keyId);
|
|
132
|
+
psshBox.set(KID, byteCursor);
|
|
133
|
+
byteCursor += KID.length;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
118
137
|
psshData.setUint32(byteCursor, dataLength);
|
|
119
138
|
byteCursor += 0x4;
|
|
120
139
|
psshBox.set(data, byteCursor);
|
package/lib/util/stream_utils.js
CHANGED
|
@@ -406,7 +406,7 @@ shaka.util.StreamUtils = class {
|
|
|
406
406
|
shaka.util.StreamUtils.filterManifestByCurrentVariant(
|
|
407
407
|
currentVariant, manifest);
|
|
408
408
|
shaka.util.StreamUtils.filterTextStreams_(manifest);
|
|
409
|
-
shaka.util.StreamUtils.filterImageStreams_(manifest);
|
|
409
|
+
await shaka.util.StreamUtils.filterImageStreams_(manifest);
|
|
410
410
|
}
|
|
411
411
|
|
|
412
412
|
|
|
@@ -813,37 +813,57 @@ shaka.util.StreamUtils = class {
|
|
|
813
813
|
* @param {shaka.extern.Manifest} manifest
|
|
814
814
|
* @private
|
|
815
815
|
*/
|
|
816
|
-
static filterImageStreams_(manifest) {
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
Platform.isTizen() ||
|
|
830
|
-
Platform.isChromecast();
|
|
831
|
-
if (webpSupport) {
|
|
832
|
-
validMimeTypes.push('image/webp');
|
|
816
|
+
static async filterImageStreams_(manifest) {
|
|
817
|
+
const imageStreams = [];
|
|
818
|
+
for (const stream of manifest.imageStreams) {
|
|
819
|
+
const mimeType = stream.mimeType;
|
|
820
|
+
if (!shaka.util.StreamUtils.supportedImageMimeTypes_.has(mimeType)) {
|
|
821
|
+
const minImage = shaka.util.StreamUtils.minImage_.get(mimeType);
|
|
822
|
+
if (minImage) {
|
|
823
|
+
// eslint-disable-next-line no-await-in-loop
|
|
824
|
+
const res = await shaka.util.StreamUtils.isImageSupported_(minImage);
|
|
825
|
+
shaka.util.StreamUtils.supportedImageMimeTypes_.set(mimeType, res);
|
|
826
|
+
} else {
|
|
827
|
+
shaka.util.StreamUtils.supportedImageMimeTypes_.set(mimeType, false);
|
|
828
|
+
}
|
|
833
829
|
}
|
|
834
|
-
|
|
835
|
-
const keep =
|
|
830
|
+
|
|
831
|
+
const keep =
|
|
832
|
+
shaka.util.StreamUtils.supportedImageMimeTypes_.get(mimeType);
|
|
836
833
|
|
|
837
834
|
if (!keep) {
|
|
838
835
|
shaka.log.debug('Dropping image stream. Is not supported by the ' +
|
|
839
836
|
'platform.', stream);
|
|
837
|
+
} else {
|
|
838
|
+
imageStreams.push(stream);
|
|
840
839
|
}
|
|
840
|
+
}
|
|
841
|
+
manifest.imageStreams = imageStreams;
|
|
842
|
+
}
|
|
841
843
|
|
|
842
|
-
|
|
844
|
+
/**
|
|
845
|
+
* @param {string} minImage
|
|
846
|
+
* @return {!Promise.<boolean>}
|
|
847
|
+
* @private
|
|
848
|
+
*/
|
|
849
|
+
static isImageSupported_(minImage) {
|
|
850
|
+
return new Promise((resolve) => {
|
|
851
|
+
const imageElement = /** @type {HTMLImageElement} */(new Image());
|
|
852
|
+
imageElement.src = minImage;
|
|
853
|
+
if ('decode' in imageElement) {
|
|
854
|
+
imageElement.decode().then(() => {
|
|
855
|
+
resolve(true);
|
|
856
|
+
}).catch(() => {
|
|
857
|
+
resolve(false);
|
|
858
|
+
});
|
|
859
|
+
} else {
|
|
860
|
+
imageElement.onload = imageElement.onerror = () => {
|
|
861
|
+
resolve(imageElement.height === 2);
|
|
862
|
+
};
|
|
863
|
+
}
|
|
843
864
|
});
|
|
844
865
|
}
|
|
845
866
|
|
|
846
|
-
|
|
847
867
|
/**
|
|
848
868
|
* @param {shaka.extern.Stream} s0
|
|
849
869
|
* @param {shaka.extern.Stream} s1
|
|
@@ -878,6 +898,11 @@ shaka.util.StreamUtils = class {
|
|
|
878
898
|
/** @type {?shaka.extern.Stream} */
|
|
879
899
|
const video = variant.video;
|
|
880
900
|
|
|
901
|
+
/** @type {?string} */
|
|
902
|
+
const audioMimeType = audio ? audio.mimeType : null;
|
|
903
|
+
/** @type {?string} */
|
|
904
|
+
const videoMimeType = video ? video.mimeType : null;
|
|
905
|
+
|
|
881
906
|
/** @type {?string} */
|
|
882
907
|
const audioCodec = audio ? audio.codecs : null;
|
|
883
908
|
/** @type {?string} */
|
|
@@ -942,6 +967,8 @@ shaka.util.StreamUtils = class {
|
|
|
942
967
|
pixelAspectRatio: null,
|
|
943
968
|
hdr: null,
|
|
944
969
|
mimeType: mimeType,
|
|
970
|
+
audioMimeType: audioMimeType,
|
|
971
|
+
videoMimeType: videoMimeType,
|
|
945
972
|
codecs: codecs.join(', '),
|
|
946
973
|
audioCodec: audioCodec,
|
|
947
974
|
videoCodec: videoCodec,
|
|
@@ -1009,6 +1036,8 @@ shaka.util.StreamUtils = class {
|
|
|
1009
1036
|
pixelAspectRatio: null,
|
|
1010
1037
|
hdr: null,
|
|
1011
1038
|
mimeType: stream.mimeType,
|
|
1039
|
+
audioMimeType: null,
|
|
1040
|
+
videoMimeType: null,
|
|
1012
1041
|
codecs: stream.codecs || null,
|
|
1013
1042
|
audioCodec: null,
|
|
1014
1043
|
videoCodec: null,
|
|
@@ -1081,6 +1110,8 @@ shaka.util.StreamUtils = class {
|
|
|
1081
1110
|
pixelAspectRatio: null,
|
|
1082
1111
|
hdr: null,
|
|
1083
1112
|
mimeType: stream.mimeType,
|
|
1113
|
+
audioMimeType: null,
|
|
1114
|
+
videoMimeType: null,
|
|
1084
1115
|
codecs: null,
|
|
1085
1116
|
audioCodec: null,
|
|
1086
1117
|
videoCodec: null,
|
|
@@ -1199,6 +1230,8 @@ shaka.util.StreamUtils = class {
|
|
|
1199
1230
|
pixelAspectRatio: null,
|
|
1200
1231
|
hdr: null,
|
|
1201
1232
|
mimeType: null,
|
|
1233
|
+
audioMimeType: null,
|
|
1234
|
+
videoMimeType: null,
|
|
1202
1235
|
codecs: null,
|
|
1203
1236
|
audioCodec: null,
|
|
1204
1237
|
videoCodec: null,
|
|
@@ -1503,3 +1536,40 @@ shaka.util.StreamUtils.DecodingAttributes = {
|
|
|
1503
1536
|
POWER: 'powerEfficient',
|
|
1504
1537
|
BANDWIDTH: 'bandwidth',
|
|
1505
1538
|
};
|
|
1539
|
+
|
|
1540
|
+
/**
|
|
1541
|
+
* @private {!Map.<string, boolean>}
|
|
1542
|
+
*/
|
|
1543
|
+
shaka.util.StreamUtils.supportedImageMimeTypes_ = new Map()
|
|
1544
|
+
.set('image/svg+xml', true)
|
|
1545
|
+
.set('image/png', true)
|
|
1546
|
+
.set('image/jpeg', true)
|
|
1547
|
+
.set('image/jpg', true);
|
|
1548
|
+
|
|
1549
|
+
/**
|
|
1550
|
+
* @const {string}
|
|
1551
|
+
* @private
|
|
1552
|
+
*/
|
|
1553
|
+
shaka.util.StreamUtils.minWebPImage_ = 'data:image/webp;base64,UklGRjoAAABXRU' +
|
|
1554
|
+
'JQVlA4IC4AAACyAgCdASoCAAIALmk0mk0iIiIiIgBoSygABc6WWgAA/veff/0PP8bA//LwY' +
|
|
1555
|
+
'AAA';
|
|
1556
|
+
|
|
1557
|
+
/**
|
|
1558
|
+
* @const {string}
|
|
1559
|
+
* @private
|
|
1560
|
+
*/
|
|
1561
|
+
shaka.util.StreamUtils.minAvifImage_ = 'data:image/avif;base64,AAAAIGZ0eXBhdm' +
|
|
1562
|
+
'lmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljd' +
|
|
1563
|
+
'AAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEA' +
|
|
1564
|
+
'AAABAAABGgAAAB0AAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAA' +
|
|
1565
|
+
'AamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAIAAAACAAAAEHBpeGkAAAAAAwgICAAAAA' +
|
|
1566
|
+
'xhdjFDgQ0MAAAAABNjb2xybmNseAACAAIAAYAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAA' +
|
|
1567
|
+
'CVtZGF0EgAKCBgANogQEAwgMg8f8D///8WfhwB8+ErK42A=';
|
|
1568
|
+
|
|
1569
|
+
/**
|
|
1570
|
+
* @const {!Map.<string, string>}
|
|
1571
|
+
* @private
|
|
1572
|
+
*/
|
|
1573
|
+
shaka.util.StreamUtils.minImage_ = new Map()
|
|
1574
|
+
.set('image/webp', shaka.util.StreamUtils.minWebPImage_)
|
|
1575
|
+
.set('image/avif', shaka.util.StreamUtils.minAvifImage_);
|