shaka-player 4.1.2 → 4.1.3
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 +67 -15
- package/.github/workflows/compute-incremental-coverage.py +251 -0
- package/.github/workflows/report-incremental-coverage.yaml +39 -0
- package/.github/workflows/selenium-lab-tests.yaml +44 -1
- package/AUTHORS +1 -0
- package/CHANGELOG.md +16 -0
- package/CONTRIBUTORS +1 -0
- package/demo/index.html +1 -1
- package/dist/controls.css +1 -1
- package/dist/locales.js +30 -30
- package/dist/shaka-player.compiled.d.ts +1 -1
- package/dist/shaka-player.compiled.debug.d.ts +1 -1
- package/dist/shaka-player.compiled.debug.externs.js +1 -1
- package/dist/shaka-player.compiled.debug.js +79 -76
- package/dist/shaka-player.compiled.debug.map +3 -3
- package/dist/shaka-player.compiled.externs.js +1 -1
- package/dist/shaka-player.compiled.js +771 -769
- package/dist/shaka-player.compiled.map +3 -3
- package/dist/shaka-player.ui.d.ts +1 -1
- package/dist/shaka-player.ui.debug.d.ts +1 -1
- package/dist/shaka-player.ui.debug.externs.js +1 -1
- package/dist/shaka-player.ui.debug.js +92 -90
- package/dist/shaka-player.ui.debug.map +3 -3
- package/dist/shaka-player.ui.externs.js +1 -1
- package/dist/shaka-player.ui.js +905 -903
- package/dist/shaka-player.ui.map +3 -3
- package/docs/tutorials/ui.md +6 -6
- package/karma.conf.js +3 -11
- package/lib/ads/client_side_ad_manager.js +1 -1
- package/lib/media/drm_engine.js +1 -1
- package/lib/media/streaming_engine.js +1 -0
- package/lib/player.js +1 -6
- package/lib/polyfill/media_capabilities.js +6 -0
- package/lib/text/cue.js +0 -5
- package/lib/text/simple_text_displayer.js +21 -9
- package/lib/text/ui_text_displayer.js +83 -19
- package/lib/util/buffer_utils.js +3 -0
- package/lib/util/object_utils.js +1 -0
- package/lib/util/stream_utils.js +23 -3
- package/lib/util/string_utils.js +63 -16
- package/package.json +2 -2
- package/test/text/simple_text_displayer_unit.js +27 -0
- package/test/text/ui_text_displayer_unit.js +65 -0
- package/test/util/string_utils_unit.js +46 -8
- package/ui/text_selection.js +2 -2
package/docs/tutorials/ui.md
CHANGED
|
@@ -30,7 +30,7 @@ Set up controls with HTML data attributes:
|
|
|
30
30
|
The data-shaka-player-cast-receiver-id tag allows you to provide a Cast Application ID that
|
|
31
31
|
the cast button will cast to; the value provided here is the sample cast receiver. -->
|
|
32
32
|
<div data-shaka-player-container style="max-width:40em"
|
|
33
|
-
data-shaka-player-cast-receiver-id="
|
|
33
|
+
data-shaka-player-cast-receiver-id="06EDA6EE">
|
|
34
34
|
<!-- The data-shaka-player tag will make the UI library use this video element.
|
|
35
35
|
If no video is provided, the UI will automatically make one inside the container div. -->
|
|
36
36
|
<video autoplay data-shaka-player id="video" style="width:100%;height:100%"></video>
|
|
@@ -111,7 +111,7 @@ set up a listener for the 'caststatuschanged' events.
|
|
|
111
111
|
<!-- Add a data-shaka-player-cast-receiver-id tag to provide a Cast Application ID that
|
|
112
112
|
the cast button will cast to; the value provided here is the sample cast receiver. -->
|
|
113
113
|
<div data-shaka-player-container style="max-width:40em"
|
|
114
|
-
data-shaka-player-cast-receiver-id="
|
|
114
|
+
data-shaka-player-cast-receiver-id="06EDA6EE">
|
|
115
115
|
</div>
|
|
116
116
|
```
|
|
117
117
|
|
|
@@ -154,7 +154,7 @@ or a `<source>` tag inside it to enable auto loading of the specified content.
|
|
|
154
154
|
|
|
155
155
|
```html
|
|
156
156
|
<div data-shaka-player-container style="max-width:40em"
|
|
157
|
-
data-shaka-player-cast-receiver-id="
|
|
157
|
+
data-shaka-player-cast-receiver-id="06EDA6EE">
|
|
158
158
|
<!-- The manifest url in the src attribute will be automatically loaded -->
|
|
159
159
|
<video autoplay data-shaka-player id="video" style="width:100%;height:100%"
|
|
160
160
|
src="https://storage.googleapis.com/shaka-demo-assets/angel-one/dash.mpd"></video>
|
|
@@ -165,7 +165,7 @@ or
|
|
|
165
165
|
|
|
166
166
|
```html
|
|
167
167
|
<div data-shaka-player-container style="max-width:40em"
|
|
168
|
-
data-shaka-player-cast-receiver-id="
|
|
168
|
+
data-shaka-player-cast-receiver-id="06EDA6EE">
|
|
169
169
|
<video autoplay data-shaka-player id="video" style="width:100%;height:100%">
|
|
170
170
|
<!-- The manifest url in the src attribute will be auto loaded -->
|
|
171
171
|
<source src="https://storage.googleapis.com/shaka-demo-assets/angel-one/dash.mpd"/>
|
|
@@ -178,7 +178,7 @@ call to the first one fails.
|
|
|
178
178
|
|
|
179
179
|
```html
|
|
180
180
|
<div data-shaka-player-container style="max-width:40em"
|
|
181
|
-
data-shaka-player-cast-receiver-id="
|
|
181
|
+
data-shaka-player-cast-receiver-id="06EDA6EE">
|
|
182
182
|
<video autoplay data-shaka-player id="video" style="width:100%;height:100%">
|
|
183
183
|
<!-- Try this first -->
|
|
184
184
|
<source src="https://storage.googleapis.com/shaka-demo-assets/angel-one/dash.mpd"/>
|
|
@@ -222,7 +222,7 @@ const video = controls.getVideo();
|
|
|
222
222
|
// Receiver Compatability.
|
|
223
223
|
ui.configure({
|
|
224
224
|
// Set the castReceiverAppId
|
|
225
|
-
'castReceiverAppId': '
|
|
225
|
+
'castReceiverAppId': '06EDA6EE',
|
|
226
226
|
// Enable casting to native Android Apps (e.g. Android TV Apps)
|
|
227
227
|
'castAndroidReceiverCompatible': true,
|
|
228
228
|
});
|
package/karma.conf.js
CHANGED
|
@@ -160,10 +160,6 @@ module.exports = (config) => {
|
|
|
160
160
|
'node_modules/es6-promise-polyfill/promise.js',
|
|
161
161
|
// Babel polyfill, required for async/await
|
|
162
162
|
'node_modules/@babel/polyfill/dist/polyfill.js',
|
|
163
|
-
// TextDecoder polyfill, required for TextDecoder/TextEncoder on IE and
|
|
164
|
-
// legacy Edge
|
|
165
|
-
// eslint-disable-next-line max-len
|
|
166
|
-
'node_modules/fastestsmallesttextencoderdecoder/EncoderDecoderTogether.min.js',
|
|
167
163
|
|
|
168
164
|
// muxjs module next
|
|
169
165
|
'node_modules/mux.js/dist/mux.min.js',
|
|
@@ -306,13 +302,6 @@ module.exports = (config) => {
|
|
|
306
302
|
// Force failure when running empty test-suites.
|
|
307
303
|
failOnEmptyTestSuite: true,
|
|
308
304
|
|
|
309
|
-
coverageReporter: {
|
|
310
|
-
includeAllSources: true,
|
|
311
|
-
reporters: [
|
|
312
|
-
{type: 'text'},
|
|
313
|
-
],
|
|
314
|
-
},
|
|
315
|
-
|
|
316
305
|
specReporter: {
|
|
317
306
|
suppressSkipped: true,
|
|
318
307
|
showBrowser: true,
|
|
@@ -405,9 +394,12 @@ module.exports = (config) => {
|
|
|
405
394
|
|
|
406
395
|
config.set({
|
|
407
396
|
coverageReporter: {
|
|
397
|
+
includeAllSources: true,
|
|
408
398
|
reporters: [
|
|
409
399
|
{type: 'html', dir: 'coverage'},
|
|
410
400
|
{type: 'cobertura', dir: 'coverage', file: 'coverage.xml'},
|
|
401
|
+
{type: 'json-summary', dir: 'coverage', file: 'coverage.json'},
|
|
402
|
+
{type: 'json', dir: 'coverage', file: 'coverage-details.json'},
|
|
411
403
|
],
|
|
412
404
|
},
|
|
413
405
|
});
|
|
@@ -427,7 +427,7 @@ shaka.ads.ClientSideAdManager = class {
|
|
|
427
427
|
onAdComplete_(e) {
|
|
428
428
|
this.onEvent_(new shaka.util.FakeEvent(shaka.ads.AdManager.AD_STOPPED,
|
|
429
429
|
(new Map()).set('originalEvent', e)));
|
|
430
|
-
if (this.ad_.isLinear()) {
|
|
430
|
+
if (this.ad_ && this.ad_.isLinear()) {
|
|
431
431
|
this.adContainer_.removeAttribute('ad-active');
|
|
432
432
|
this.video_.play();
|
|
433
433
|
}
|
package/lib/media/drm_engine.js
CHANGED
|
@@ -1548,7 +1548,7 @@ shaka.media.DrmEngine = class {
|
|
|
1548
1548
|
if (shaka.media.DrmEngine.isPlayReadyKeySystem(
|
|
1549
1549
|
this.currentDrmInfo_.keySystem) &&
|
|
1550
1550
|
keyId.byteLength == 16 &&
|
|
1551
|
-
shaka.util.Platform.isEdge()) {
|
|
1551
|
+
(shaka.util.Platform.isEdge() || shaka.util.Platform.isPS4())) {
|
|
1552
1552
|
// Read out some fields in little-endian:
|
|
1553
1553
|
const dataView = shaka.util.BufferUtils.toDataView(keyId);
|
|
1554
1554
|
const part0 = dataView.getUint32(0, /* LE= */ true);
|
|
@@ -1790,6 +1790,7 @@ shaka.media.StreamingEngine = class {
|
|
|
1790
1790
|
*
|
|
1791
1791
|
* @return {!Promise.<BufferSource>}
|
|
1792
1792
|
* @private
|
|
1793
|
+
* @suppress {strictMissingProperties}
|
|
1793
1794
|
*/
|
|
1794
1795
|
async fetch_(mediaState, reference, streamDataCallback) {
|
|
1795
1796
|
const requestType = shaka.net.NetworkingEngine.RequestType.SEGMENT;
|
package/lib/player.js
CHANGED
|
@@ -857,14 +857,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
857
857
|
if (!window.Promise) {
|
|
858
858
|
shaka.log.alwaysWarn('A Promise implementation or polyfill is required');
|
|
859
859
|
}
|
|
860
|
-
if (!window.TextDecoder || !window.TextEncoder) {
|
|
861
|
-
shaka.log.alwaysWarn(
|
|
862
|
-
'A TextDecoder/TextEncoder implementation or polyfill is required');
|
|
863
|
-
}
|
|
864
860
|
|
|
865
861
|
// Basic features needed for the library to be usable.
|
|
866
862
|
const basicSupport = !!window.Promise && !!window.Uint8Array &&
|
|
867
|
-
!!window.TextDecoder && !!window.TextEncoder &&
|
|
868
863
|
// eslint-disable-next-line no-restricted-syntax
|
|
869
864
|
!!Array.prototype.forEach;
|
|
870
865
|
if (!basicSupport) {
|
|
@@ -6556,7 +6551,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
|
|
|
6556
6551
|
* @define {string} A version number taken from git at compile time.
|
|
6557
6552
|
* @export
|
|
6558
6553
|
*/
|
|
6559
|
-
shaka.Player.version = 'v4.1.
|
|
6554
|
+
shaka.Player.version = 'v4.1.3-uncompiled';
|
|
6560
6555
|
|
|
6561
6556
|
// Initialize the deprecation system using the version string we just set
|
|
6562
6557
|
// on the player.
|
|
@@ -39,10 +39,16 @@ shaka.polyfill.MediaCapabilities = class {
|
|
|
39
39
|
// Browsers.
|
|
40
40
|
// Since MediaCapabilities implementation does not exist in PS4 browsers, we
|
|
41
41
|
// should always install polyfill.
|
|
42
|
+
// Since MediaCapabilities implementation is buggy in Tizen browsers, we
|
|
43
|
+
// should always install polyfill for Tizen browsers.
|
|
44
|
+
// Since MediaCapabilities implementation is buggy in WebOS browsers, we
|
|
45
|
+
// should always install polyfill for WebOS browsers.
|
|
42
46
|
let canUseNativeMCap = true;
|
|
43
47
|
if (shaka.util.Platform.isApple() ||
|
|
44
48
|
shaka.util.Platform.isPS5() ||
|
|
45
49
|
shaka.util.Platform.isPS4() ||
|
|
50
|
+
shaka.util.Platform.isWebOS() ||
|
|
51
|
+
shaka.util.Platform.isTizen() ||
|
|
46
52
|
shaka.util.Platform.isChromecast()) {
|
|
47
53
|
canUseNativeMCap = false;
|
|
48
54
|
}
|
package/lib/text/cue.js
CHANGED
|
@@ -41,14 +41,9 @@ shaka.text.Cue = class {
|
|
|
41
41
|
*/
|
|
42
42
|
this.endTime = endTime;
|
|
43
43
|
|
|
44
|
-
// The explicit type here is to work around an apparent compiler bug where
|
|
45
|
-
// the compiler seems to get confused about the type. Since we are
|
|
46
|
-
// overriding the interface type, it shouldn't be necessary to do this.
|
|
47
|
-
// Compiler version 20200517.
|
|
48
44
|
/**
|
|
49
45
|
* @override
|
|
50
46
|
* @exportInterface
|
|
51
|
-
* @type {string}
|
|
52
47
|
*/
|
|
53
48
|
this.payload = payload;
|
|
54
49
|
|
|
@@ -121,16 +121,28 @@ shaka.text.SimpleTextDisplayer = class {
|
|
|
121
121
|
// We don't want to modify the array or objects passed in, since we don't
|
|
122
122
|
// technically own them. So we build a new array and replace certain items
|
|
123
123
|
// in it if they need to be flattened.
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
124
|
+
// We also don't want to flatten the text payloads starting at a container
|
|
125
|
+
// element; otherwise, for containers encapsulating multiple caption lines,
|
|
126
|
+
// the lines would merge into a single cue. This is undesirable when a
|
|
127
|
+
// subset of the captions are outside of the append time window. To fix
|
|
128
|
+
// this, we only call flattenPayload() starting at elements marked as
|
|
129
|
+
// isContainer = false.
|
|
130
|
+
const getCuesToFlatten = (cues, result) => {
|
|
131
|
+
for (const cue of cues) {
|
|
132
|
+
if (cue.isContainer) {
|
|
133
|
+
// Recurse to find the actual text payload cues.
|
|
134
|
+
getCuesToFlatten(cue.nestedCues, result);
|
|
135
|
+
} else {
|
|
136
|
+
// Flatten the payload.
|
|
137
|
+
const flatCue = cue.clone();
|
|
138
|
+
flatCue.nestedCues = [];
|
|
139
|
+
flatCue.payload = flattenPayload(cue);
|
|
140
|
+
result.push(flatCue);
|
|
141
|
+
}
|
|
132
142
|
}
|
|
133
|
-
|
|
143
|
+
return result;
|
|
144
|
+
};
|
|
145
|
+
const flattenedCues = getCuesToFlatten(cues, []);
|
|
134
146
|
|
|
135
147
|
// Convert cues.
|
|
136
148
|
const textTrackCues = [];
|
|
@@ -77,6 +77,7 @@ shaka.text.UITextDisplayer = class {
|
|
|
77
77
|
* the cue (e.g. the HTML element to put nested cues inside).
|
|
78
78
|
* @private {Map.<!shaka.extern.Cue, !{
|
|
79
79
|
* cueElement: !HTMLElement,
|
|
80
|
+
* regionElement: HTMLElement,
|
|
80
81
|
* wrapper: !HTMLElement
|
|
81
82
|
* }>}
|
|
82
83
|
*/
|
|
@@ -97,6 +98,9 @@ shaka.text.UITextDisplayer = class {
|
|
|
97
98
|
});
|
|
98
99
|
this.resizeObserver_.observe(this.textContainer_);
|
|
99
100
|
}
|
|
101
|
+
|
|
102
|
+
/** @private {Map.<string, !HTMLElement>} */
|
|
103
|
+
this.regionElements_ = new Map();
|
|
100
104
|
}
|
|
101
105
|
|
|
102
106
|
|
|
@@ -234,6 +238,11 @@ shaka.text.UITextDisplayer = class {
|
|
|
234
238
|
// even ones which are going to be planted again.
|
|
235
239
|
toUproot.push(cueRegistry.cueElement);
|
|
236
240
|
|
|
241
|
+
// Also uproot all displayed region elements.
|
|
242
|
+
if (cueRegistry.regionElement) {
|
|
243
|
+
toUproot.push(cueRegistry.regionElement);
|
|
244
|
+
}
|
|
245
|
+
|
|
237
246
|
// If the cue should not be displayed, remove it entirely.
|
|
238
247
|
if (!shouldBeDisplayed) {
|
|
239
248
|
// Since something has to be removed, we will need to update the DOM.
|
|
@@ -268,8 +277,12 @@ shaka.text.UITextDisplayer = class {
|
|
|
268
277
|
goog.asserts.assert(topCue == cue, 'Parent cues should be kept in order');
|
|
269
278
|
}
|
|
270
279
|
if (updateDOM) {
|
|
271
|
-
for (const
|
|
272
|
-
|
|
280
|
+
for (const element of toUproot) {
|
|
281
|
+
// NOTE: Because we uproot shared region elements, too, we might hit an
|
|
282
|
+
// element here that has no parent because we've already processed it.
|
|
283
|
+
if (element.parentElement) {
|
|
284
|
+
element.parentElement.removeChild(element);
|
|
285
|
+
}
|
|
273
286
|
}
|
|
274
287
|
toPlant.sort((a, b) => {
|
|
275
288
|
if (a.startTime != b.startTime) {
|
|
@@ -281,7 +294,12 @@ shaka.text.UITextDisplayer = class {
|
|
|
281
294
|
for (const cue of toPlant) {
|
|
282
295
|
const cueRegistry = this.currentCuesMap_.get(cue);
|
|
283
296
|
goog.asserts.assert(cueRegistry, 'cueRegistry should exist.');
|
|
284
|
-
|
|
297
|
+
if (cueRegistry.regionElement) {
|
|
298
|
+
container.appendChild(cueRegistry.regionElement);
|
|
299
|
+
cueRegistry.regionElement.appendChild(cueRegistry.cueElement);
|
|
300
|
+
} else {
|
|
301
|
+
container.appendChild(cueRegistry.cueElement);
|
|
302
|
+
}
|
|
285
303
|
}
|
|
286
304
|
}
|
|
287
305
|
}
|
|
@@ -298,11 +316,15 @@ shaka.text.UITextDisplayer = class {
|
|
|
298
316
|
|
|
299
317
|
const currentTime = this.video_.currentTime;
|
|
300
318
|
if (!this.isTextVisible_ || forceUpdate) {
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
shaka.util.Dom.removeAllChildren(
|
|
304
|
-
this.currentCuesMap_.clear();
|
|
319
|
+
// Remove child elements from all regions.
|
|
320
|
+
for (const regionElement of this.regionElements_.values()) {
|
|
321
|
+
shaka.util.Dom.removeAllChildren(regionElement);
|
|
305
322
|
}
|
|
323
|
+
// Remove all top-level elements in the text container.
|
|
324
|
+
shaka.util.Dom.removeAllChildren(this.textContainer_);
|
|
325
|
+
// Clear the element maps.
|
|
326
|
+
this.currentCuesMap_.clear();
|
|
327
|
+
this.regionElements_.clear();
|
|
306
328
|
}
|
|
307
329
|
if (this.isTextVisible_) {
|
|
308
330
|
// Log currently attached cue elements for verification, later.
|
|
@@ -332,6 +354,54 @@ shaka.text.UITextDisplayer = class {
|
|
|
332
354
|
}
|
|
333
355
|
}
|
|
334
356
|
|
|
357
|
+
/**
|
|
358
|
+
* Get or create a region element corresponding to the cue region. These are
|
|
359
|
+
* cached by ID.
|
|
360
|
+
*
|
|
361
|
+
* @param {!shaka.extern.Cue} cue
|
|
362
|
+
* @return {!HTMLElement}
|
|
363
|
+
* @private
|
|
364
|
+
*/
|
|
365
|
+
getRegionElement_(cue) {
|
|
366
|
+
const region = cue.region;
|
|
367
|
+
|
|
368
|
+
if (this.regionElements_.has(region.id)) {
|
|
369
|
+
return this.regionElements_.get(region.id);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
const regionElement = shaka.util.Dom.createHTMLElement('span');
|
|
373
|
+
|
|
374
|
+
const percentageUnit = shaka.text.CueRegion.units.PERCENTAGE;
|
|
375
|
+
const heightUnit = region.heightUnits == percentageUnit ? '%' : 'px';
|
|
376
|
+
const widthUnit = region.widthUnits == percentageUnit ? '%' : 'px';
|
|
377
|
+
const viewportAnchorUnit =
|
|
378
|
+
region.viewportAnchorUnits == percentageUnit ? '%' : 'px';
|
|
379
|
+
|
|
380
|
+
regionElement.id = 'shaka-text-region---' + region.id;
|
|
381
|
+
regionElement.classList.add('shaka-text-region');
|
|
382
|
+
|
|
383
|
+
regionElement.style.height = region.height + heightUnit;
|
|
384
|
+
regionElement.style.width = region.width + widthUnit;
|
|
385
|
+
regionElement.style.position = 'absolute';
|
|
386
|
+
regionElement.style.top = region.viewportAnchorY + viewportAnchorUnit;
|
|
387
|
+
regionElement.style.left = region.viewportAnchorX + viewportAnchorUnit;
|
|
388
|
+
|
|
389
|
+
regionElement.style.display = 'flex';
|
|
390
|
+
regionElement.style.flexDirection = 'column';
|
|
391
|
+
regionElement.style.alignItems = 'center';
|
|
392
|
+
|
|
393
|
+
if (cue.displayAlign == shaka.text.Cue.displayAlign.BEFORE) {
|
|
394
|
+
regionElement.style.justifyContent = 'flex-start';
|
|
395
|
+
} else if (cue.displayAlign == shaka.text.Cue.displayAlign.CENTER) {
|
|
396
|
+
regionElement.style.justifyContent = 'center';
|
|
397
|
+
} else {
|
|
398
|
+
regionElement.style.justifyContent = 'flex-end';
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
this.regionElements_.set(region.id, regionElement);
|
|
402
|
+
return regionElement;
|
|
403
|
+
}
|
|
404
|
+
|
|
335
405
|
/**
|
|
336
406
|
* Creates the object for a cue.
|
|
337
407
|
*
|
|
@@ -354,6 +424,11 @@ shaka.text.UITextDisplayer = class {
|
|
|
354
424
|
this.setCaptionStyles_(cueElement, cue, parents, needWrapper);
|
|
355
425
|
}
|
|
356
426
|
|
|
427
|
+
let regionElement = null;
|
|
428
|
+
if (cue.region && cue.region.id) {
|
|
429
|
+
regionElement = this.getRegionElement_(cue);
|
|
430
|
+
}
|
|
431
|
+
|
|
357
432
|
let wrapper = cueElement;
|
|
358
433
|
if (needWrapper) {
|
|
359
434
|
// Create a wrapper element which will serve to contain all children into
|
|
@@ -365,7 +440,7 @@ shaka.text.UITextDisplayer = class {
|
|
|
365
440
|
cueElement.appendChild(wrapper);
|
|
366
441
|
}
|
|
367
442
|
|
|
368
|
-
this.currentCuesMap_.set(cue, {cueElement, wrapper});
|
|
443
|
+
this.currentCuesMap_.set(cue, {cueElement, wrapper, regionElement});
|
|
369
444
|
}
|
|
370
445
|
|
|
371
446
|
/**
|
|
@@ -526,17 +601,6 @@ shaka.text.UITextDisplayer = class {
|
|
|
526
601
|
}
|
|
527
602
|
}
|
|
528
603
|
}
|
|
529
|
-
} else if (cue.region && cue.region.id) {
|
|
530
|
-
const percentageUnit = shaka.text.CueRegion.units.PERCENTAGE;
|
|
531
|
-
const heightUnit = cue.region.heightUnits == percentageUnit ? '%' : 'px';
|
|
532
|
-
const widthUnit = cue.region.widthUnits == percentageUnit ? '%' : 'px';
|
|
533
|
-
const viewportAnchorUnit =
|
|
534
|
-
cue.region.viewportAnchorUnits == percentageUnit ? '%' : 'px';
|
|
535
|
-
style.height = cue.region.height + heightUnit;
|
|
536
|
-
style.width = cue.region.width + widthUnit;
|
|
537
|
-
style.position = 'absolute';
|
|
538
|
-
style.top = cue.region.viewportAnchorY + viewportAnchorUnit;
|
|
539
|
-
style.left = cue.region.viewportAnchorX + viewportAnchorUnit;
|
|
540
604
|
}
|
|
541
605
|
|
|
542
606
|
style.lineHeight = cue.lineHeight;
|
package/lib/util/buffer_utils.js
CHANGED
|
@@ -20,6 +20,7 @@ shaka.util.BufferUtils = class {
|
|
|
20
20
|
* @param {?BufferSource} arr2
|
|
21
21
|
* @return {boolean}
|
|
22
22
|
* @export
|
|
23
|
+
* @suppress {strictMissingProperties}
|
|
23
24
|
*/
|
|
24
25
|
static equal(arr1, arr2) {
|
|
25
26
|
const BufferUtils = shaka.util.BufferUtils;
|
|
@@ -135,8 +136,10 @@ shaka.util.BufferUtils = class {
|
|
|
135
136
|
static view_(data, offset, length, Type) {
|
|
136
137
|
const buffer = shaka.util.BufferUtils.unsafeGetArrayBuffer_(data);
|
|
137
138
|
// Absolute end of the |data| view within |buffer|.
|
|
139
|
+
/** @suppress {strictMissingProperties} */
|
|
138
140
|
const dataEnd = (data.byteOffset || 0) + data.byteLength;
|
|
139
141
|
// Absolute start of the result within |buffer|.
|
|
142
|
+
/** @suppress {strictMissingProperties} */
|
|
140
143
|
const rawStart = (data.byteOffset || 0) + offset;
|
|
141
144
|
const start = Math.max(0, Math.min(rawStart, dataEnd));
|
|
142
145
|
// Absolute end of the result within |buffer|.
|
package/lib/util/object_utils.js
CHANGED
|
@@ -22,6 +22,7 @@ shaka.util.ObjectUtils = class {
|
|
|
22
22
|
const seenObjects = new Set();
|
|
23
23
|
// This recursively clones the value |val|, using the captured variable
|
|
24
24
|
// |seenObjects| to track the objects we have already cloned.
|
|
25
|
+
/** @suppress {strictMissingProperties} */
|
|
25
26
|
const clone = (val) => {
|
|
26
27
|
switch (typeof val) {
|
|
27
28
|
case 'undefined':
|
package/lib/util/stream_utils.js
CHANGED
|
@@ -585,8 +585,16 @@ shaka.util.StreamUtils = class {
|
|
|
585
585
|
// VideoConfiguration
|
|
586
586
|
mediaDecodingConfig.video = {
|
|
587
587
|
contentType: fullType,
|
|
588
|
-
|
|
589
|
-
|
|
588
|
+
|
|
589
|
+
// NOTE: Some decoders strictly check the width and height fields and
|
|
590
|
+
// won't decode smaller than 64x64. So if we don't have this info (as
|
|
591
|
+
// is the case in some of our simpler tests), assume a 64x64 resolution
|
|
592
|
+
// to fill in this required field for MediaCapabilities.
|
|
593
|
+
//
|
|
594
|
+
// This became an issue specifically on Firefox on M1 Macs.
|
|
595
|
+
width: video.width || 64,
|
|
596
|
+
height: video.height || 64,
|
|
597
|
+
|
|
590
598
|
bitrate: video.bandwidth || variant.bandwidth || 1,
|
|
591
599
|
// framerate must be greater than 0, otherwise the config is invalid.
|
|
592
600
|
framerate: video.frameRate || 1,
|
|
@@ -749,7 +757,19 @@ shaka.util.StreamUtils = class {
|
|
|
749
757
|
*/
|
|
750
758
|
static patchVp9(codec) {
|
|
751
759
|
if (codec == 'vp9') {
|
|
752
|
-
|
|
760
|
+
// This means profile 0, level 4.1, 8-bit color. This supports 1080p @
|
|
761
|
+
// 60Hz. See https://en.wikipedia.org/wiki/VP9#Levels
|
|
762
|
+
//
|
|
763
|
+
// If we don't have more detailed codec info, assume this profile and
|
|
764
|
+
// level because it's high enough to likely accommodate the parameters we
|
|
765
|
+
// do have, such as width and height. If an implementation is checking
|
|
766
|
+
// the profile and level very strictly, we want older VP9 content to
|
|
767
|
+
// still work to some degree. But we don't want to set a level so high
|
|
768
|
+
// that it is rejected by a hardware decoder that can't handle the
|
|
769
|
+
// maximum requirements of the level.
|
|
770
|
+
//
|
|
771
|
+
// This became an issue specifically on Firefox on M1 Macs.
|
|
772
|
+
return 'vp09.00.41.08';
|
|
753
773
|
}
|
|
754
774
|
return codec;
|
|
755
775
|
}
|
package/lib/util/string_utils.js
CHANGED
|
@@ -37,6 +37,7 @@ shaka.util.StringUtils = class {
|
|
|
37
37
|
if (uint8[0] == 0xef && uint8[1] == 0xbb && uint8[2] == 0xbf) {
|
|
38
38
|
uint8 = uint8.subarray(3);
|
|
39
39
|
}
|
|
40
|
+
|
|
40
41
|
if (window.TextDecoder && !shaka.util.Platform.isPS4()) {
|
|
41
42
|
// Use the TextDecoder interface to decode the text. This has the
|
|
42
43
|
// advantage compared to the previously-standard decodeUriComponent that
|
|
@@ -51,23 +52,69 @@ shaka.util.StringUtils = class {
|
|
|
51
52
|
}
|
|
52
53
|
return decoded;
|
|
53
54
|
} else {
|
|
54
|
-
//
|
|
55
|
-
|
|
56
|
-
//
|
|
57
|
-
//
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
55
|
+
// Homebrewed UTF-8 decoder based on
|
|
56
|
+
// https://en.wikipedia.org/wiki/UTF-8#Encoding
|
|
57
|
+
// Unlike decodeURIComponent, won't throw on bad encoding.
|
|
58
|
+
// In this way, it is similar to TextDecoder.
|
|
59
|
+
|
|
60
|
+
let decoded = '';
|
|
61
|
+
for (let i = 0; i < uint8.length; ++i) {
|
|
62
|
+
// By default, the "replacement character" codepoint.
|
|
63
|
+
let codePoint = 0xFFFD;
|
|
64
|
+
|
|
65
|
+
// Top bit is 0, 1-byte encoding.
|
|
66
|
+
if ((uint8[i] & 0x80) == 0) {
|
|
67
|
+
codePoint = uint8[i];
|
|
68
|
+
|
|
69
|
+
// Top 3 bits of byte 0 are 110, top 2 bits of byte 1 are 10,
|
|
70
|
+
// 2-byte encoding.
|
|
71
|
+
} else if (uint8.length >= i + 2 &&
|
|
72
|
+
(uint8[i] & 0xe0) == 0xc0 &&
|
|
73
|
+
(uint8[i + 1] & 0xc0) == 0x80) {
|
|
74
|
+
codePoint = ((uint8[i] & 0x1f) << 6) |
|
|
75
|
+
((uint8[i + 1] & 0x3f));
|
|
76
|
+
i += 1; // Consume one extra byte.
|
|
77
|
+
|
|
78
|
+
// Top 4 bits of byte 0 are 1110, top 2 bits of byte 1 and 2 are 10,
|
|
79
|
+
// 3-byte encoding.
|
|
80
|
+
} else if (uint8.length >= i + 3 &&
|
|
81
|
+
(uint8[i] & 0xf0) == 0xe0 &&
|
|
82
|
+
(uint8[i + 1] & 0xc0) == 0x80 &&
|
|
83
|
+
(uint8[i + 2] & 0xc0) == 0x80) {
|
|
84
|
+
codePoint = ((uint8[i] & 0x0f) << 12) |
|
|
85
|
+
((uint8[i + 1] & 0x3f) << 6) |
|
|
86
|
+
((uint8[i + 2] & 0x3f));
|
|
87
|
+
i += 2; // Consume two extra bytes.
|
|
88
|
+
|
|
89
|
+
// Top 5 bits of byte 0 are 11110, top 2 bits of byte 1, 2 and 3 are 10,
|
|
90
|
+
// 4-byte encoding.
|
|
91
|
+
} else if (uint8.length >= i + 4 &&
|
|
92
|
+
(uint8[i] & 0xf1) == 0xf0 &&
|
|
93
|
+
(uint8[i + 1] & 0xc0) == 0x80 &&
|
|
94
|
+
(uint8[i + 2] & 0xc0) == 0x80 &&
|
|
95
|
+
(uint8[i + 3] & 0xc0) == 0x80) {
|
|
96
|
+
codePoint = ((uint8[i] & 0x07) << 18) |
|
|
97
|
+
((uint8[i + 1] & 0x3f) << 12) |
|
|
98
|
+
((uint8[i + 2] & 0x3f) << 6) |
|
|
99
|
+
((uint8[i + 3] & 0x3f));
|
|
100
|
+
i += 3; // Consume three extra bytes.
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// JavaScript strings are a series of UTF-16 characters.
|
|
104
|
+
if (codePoint <= 0xffff) {
|
|
105
|
+
decoded += String.fromCharCode(codePoint);
|
|
106
|
+
} else {
|
|
107
|
+
// UTF-16 surrogate-pair encoding, based on
|
|
108
|
+
// https://en.wikipedia.org/wiki/UTF-16#Description
|
|
109
|
+
const baseCodePoint = codePoint - 0x10000;
|
|
110
|
+
const highPart = baseCodePoint >> 10;
|
|
111
|
+
const lowPart = baseCodePoint & 0x3ff;
|
|
112
|
+
decoded += String.fromCharCode(0xd800 + highPart);
|
|
113
|
+
decoded += String.fromCharCode(0xdc00 + lowPart);
|
|
114
|
+
}
|
|
70
115
|
}
|
|
116
|
+
|
|
117
|
+
return decoded;
|
|
71
118
|
}
|
|
72
119
|
}
|
|
73
120
|
|
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": "4.1.
|
|
4
|
+
"version": "4.1.3",
|
|
5
5
|
"homepage": "https://github.com/shaka-project/shaka-player",
|
|
6
6
|
"author": "Google",
|
|
7
7
|
"maintainers": [
|
|
@@ -94,7 +94,7 @@
|
|
|
94
94
|
"prepublishOnly": "python build/checkversion.py && python build/all.py --force"
|
|
95
95
|
},
|
|
96
96
|
"dependencies": {
|
|
97
|
-
"eme-encryption-scheme-polyfill": "^2.
|
|
97
|
+
"eme-encryption-scheme-polyfill": "^2.1.1"
|
|
98
98
|
},
|
|
99
99
|
"engines": {
|
|
100
100
|
"node": ">=14"
|
|
@@ -127,6 +127,33 @@ describe('SimpleTextDisplayer', () => {
|
|
|
127
127
|
[shakaCue]);
|
|
128
128
|
});
|
|
129
129
|
|
|
130
|
+
it('flattens nested cue payloads correctly', () => {
|
|
131
|
+
const level0ContainerCue = new shaka.text.Cue(10, 30, '');
|
|
132
|
+
level0ContainerCue.isContainer = true;
|
|
133
|
+
|
|
134
|
+
const level1NonContainerCueA = new shaka.text.Cue(10, 20, '');
|
|
135
|
+
const level1NonContainerCueB = new shaka.text.Cue(20, 30, '');
|
|
136
|
+
|
|
137
|
+
// Add a trailing whitespace character to get a space-delimited expected
|
|
138
|
+
// result.
|
|
139
|
+
const cueANestedCue0 = new shaka.text.Cue(10, 20, 'Cue A Test0 ');
|
|
140
|
+
const cueANestedCue1 = new shaka.text.Cue(10, 20, 'Cue A Test1');
|
|
141
|
+
const cueBNestedCue0 = new shaka.text.Cue(20, 30, 'Cue B Test0 ');
|
|
142
|
+
const cueBNestedCue1 = new shaka.text.Cue(20, 30, 'Cue B Test1');
|
|
143
|
+
|
|
144
|
+
level1NonContainerCueA.nestedCues = [cueANestedCue0, cueANestedCue1];
|
|
145
|
+
level1NonContainerCueB.nestedCues = [cueBNestedCue0, cueBNestedCue1];
|
|
146
|
+
level0ContainerCue.nestedCues =
|
|
147
|
+
[level1NonContainerCueA, level1NonContainerCueB];
|
|
148
|
+
|
|
149
|
+
verifyHelper(
|
|
150
|
+
[
|
|
151
|
+
{startTime: 10, endTime: 20, text: 'Cue A Test0 Cue A Test1'},
|
|
152
|
+
{startTime: 20, endTime: 30, text: 'Cue B Test0 Cue B Test1'},
|
|
153
|
+
],
|
|
154
|
+
[level0ContainerCue]);
|
|
155
|
+
});
|
|
156
|
+
|
|
130
157
|
// Regression test for b/159050711
|
|
131
158
|
it('maintains the styles of the parent cue', () => {
|
|
132
159
|
const shakaCue = new shaka.text.Cue(10, 20, '');
|