shaka-player 4.3.12 → 4.3.14
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/ISSUE_TEMPLATE/bug_report.md +1 -1
- package/.github/ISSUE_TEMPLATE/question.md +7 -0
- package/.github/workflows/release-please.yaml +2 -27
- package/AUTHORS +5 -0
- package/CHANGELOG.md +108 -0
- package/CODE_OF_CONDUCT.md +1 -1
- package/CONTRIBUTORS +6 -0
- package/README.md +2 -0
- package/demo/common/asset.js +5 -1
- package/demo/common/assets.js +16 -2
- package/demo/common/message_ids.js +1 -0
- package/demo/custom.js +2 -0
- package/demo/locales/en.json +1 -0
- package/demo/locales/source.json +4 -0
- package/demo/main.js +1 -1
- package/dist/demo.compiled.debug.js +56 -54
- package/dist/demo.compiled.debug.map +3 -3
- package/dist/demo.compiled.js +56 -54
- package/dist/demo.compiled.map +3 -3
- package/dist/deps.js +6 -6
- package/dist/locales.js +31 -31
- package/dist/receiver.compiled.debug.js +24 -22
- package/dist/receiver.compiled.debug.map +3 -3
- package/dist/receiver.compiled.js +24 -22
- package/dist/receiver.compiled.map +3 -3
- package/dist/shaka-player.compiled.d.ts +26 -6
- package/dist/shaka-player.compiled.debug.d.ts +26 -6
- package/dist/shaka-player.compiled.debug.externs.js +79 -49
- package/dist/shaka-player.compiled.debug.js +426 -410
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +79 -49
- package/dist/shaka-player.compiled.js +912 -901
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +38 -7
- package/dist/shaka-player.ui.debug.d.ts +38 -7
- package/dist/shaka-player.ui.debug.externs.js +80 -49
- package/dist/shaka-player.ui.debug.js +460 -442
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +80 -49
- package/dist/shaka-player.ui.js +1060 -1049
- package/dist/shaka-player.ui.map +4 -4
- package/docs/tutorials/ad_monetization.md +54 -0
- package/docs/tutorials/fairplay.md +2 -2
- package/docs/tutorials/index.json +1 -0
- package/docs/tutorials/offline.md +1 -1
- package/docs/tutorials/plugins.md +4 -0
- package/docs/tutorials/text-displayer.md +15 -0
- package/docs/tutorials/ui-customization.md +29 -0
- package/docs/tutorials/ui.md +4 -1
- package/externs/lcevc.js +6 -0
- package/externs/shaka/manifest_parser.js +8 -0
- package/externs/shaka/player.js +1 -1
- package/externs/shaka/text.js +3 -1
- package/lib/abr/simple_abr_manager.js +1 -1
- package/lib/ads/client_side_ad.js +9 -0
- package/lib/ads/client_side_ad_manager.js +7 -2
- package/lib/ads/server_side_ad_manager.js +4 -0
- package/lib/cast/cast_proxy.js +18 -0
- package/lib/cea/cea608_memory.js +1 -1
- package/lib/cea/cea_decoder.js +11 -1
- package/lib/dash/dash_parser.js +79 -42
- package/lib/dash/mpd_utils.js +39 -27
- package/lib/hls/hls_parser.js +148 -49
- package/lib/hls/hls_utils.js +5 -5
- package/lib/media/content_workarounds.js +30 -0
- package/lib/media/drm_engine.js +16 -12
- package/lib/media/media_source_engine.js +4 -2
- package/lib/media/play_rate_controller.js +1 -0
- package/lib/media/presentation_timeline.js +11 -0
- package/lib/media/quality_observer.js +1 -1
- package/lib/media/segment_reference.js +16 -7
- package/lib/media/streaming_engine.js +64 -54
- package/lib/media/transmuxer.js +1 -1
- package/lib/media/video_wrapper.js +4 -3
- package/lib/offline/offline_manifest_parser.js +5 -0
- package/lib/offline/session_deleter.js +0 -8
- package/lib/offline/storage.js +4 -1
- package/lib/player.js +79 -18
- package/lib/polyfill/media_capabilities.js +23 -3
- package/lib/text/mp4_ttml_parser.js +2 -2
- package/lib/text/simple_text_displayer.js +7 -6
- package/lib/text/srt_text_parser.js +2 -2
- package/lib/text/text_engine.js +3 -2
- package/lib/text/ttml_text_parser.js +33 -5
- package/lib/text/ui_text_displayer.js +81 -15
- package/lib/text/vtt_text_parser.js +13 -0
- package/lib/util/buffer_utils.js +26 -4
- package/lib/util/cmcd_manager.js +16 -1
- package/lib/util/fairplay_utils.js +1 -1
- package/lib/util/manifest_parser_utils.js +20 -8
- package/lib/util/object_utils.js +1 -1
- package/lib/util/periods.js +91 -36
- package/lib/util/platform.js +39 -2
- package/lib/util/player_configuration.js +5 -3
- package/lib/util/stream_utils.js +36 -28
- package/lib/util/ts_parser.js +3 -3
- package/lib/util/xml_utils.js +5 -0
- package/package.json +2 -2
- package/test/cea/cea_decoder_unit.js +40 -0
- package/test/dash/dash_parser_live_unit.js +5 -8
- package/test/dash/dash_parser_manifest_unit.js +5 -0
- package/test/dash/dash_parser_segment_base_unit.js +5 -0
- package/test/dash/dash_parser_segment_list_unit.js +21 -17
- package/test/dash/dash_parser_segment_template_unit.js +5 -0
- package/test/hls/hls_live_unit.js +33 -0
- package/test/hls/hls_parser_unit.js +52 -60
- package/test/media/buffering_observer_unit.js +13 -0
- package/test/media/content_workarounds_unit.js +1 -1
- package/test/media/drm_engine_unit.js +30 -3
- package/test/media/media_source_engine_unit.js +1 -1
- package/test/media/transmuxer_integration.js +2 -2
- package/test/offline/storage_integration.js +3 -0
- package/test/player_integration.js +40 -40
- package/test/player_unit.js +31 -0
- package/test/polyfill/media_capabilities_unit.js +12 -6
- package/test/test/util/dash_parser_util.js +16 -7
- package/test/test/util/fake_drm_engine.js +7 -0
- package/test/test/util/layout_tests.js +2 -1
- package/test/test/util/simple_fakes.js +2 -2
- package/test/test/util/test_scheme.js +3 -0
- package/test/test/util/waiter.js +5 -0
- package/test/text/mp4_ttml_parser_unit.js +4 -4
- package/test/text/simple_text_displayer_unit.js +1 -1
- package/test/text/srt_text_parser_unit.js +1 -1
- package/test/text/text_engine_unit.js +6 -0
- package/test/text/ttml_text_parser_unit.js +49 -18
- package/test/text/ui_text_displayer_unit.js +9 -0
- package/test/text/vtt_text_parser_unit.js +2 -17
- package/test/util/buffer_utils_unit.js +1 -1
- package/test/util/periods_unit.js +113 -0
- package/test/util/ts_parser_unit.js +1 -1
- package/third_party/closure-uri/uri.js +1 -1
- package/ui/externs/ui.js +24 -0
- package/ui/language_utils.js +11 -5
- package/ui/play_button.js +20 -0
- package/ui/playback_rate_selection.js +4 -0
- package/ui/presentation_time.js +5 -5
- package/ui/small_play_button.js +2 -2
- package/ui/volume_bar.js +10 -6
- /package/.github/{config.yml → ISSUE_TEMPLATE/config.yml} +0 -0
package/lib/text/text_engine.js
CHANGED
|
@@ -160,9 +160,10 @@ shaka.text.TextEngine = class {
|
|
|
160
160
|
* @param {BufferSource} buffer
|
|
161
161
|
* @param {?number} startTime relative to the start of the presentation
|
|
162
162
|
* @param {?number} endTime relative to the start of the presentation
|
|
163
|
+
* @param {?string=} uri
|
|
163
164
|
* @return {!Promise}
|
|
164
165
|
*/
|
|
165
|
-
async appendBuffer(buffer, startTime, endTime) {
|
|
166
|
+
async appendBuffer(buffer, startTime, endTime, uri) {
|
|
166
167
|
goog.asserts.assert(
|
|
167
168
|
this.parser_, 'The parser should already be initialized');
|
|
168
169
|
|
|
@@ -192,7 +193,7 @@ shaka.text.TextEngine = class {
|
|
|
192
193
|
|
|
193
194
|
// Parse the buffer and add the new cues.
|
|
194
195
|
const allCues = this.parser_.parseMedia(
|
|
195
|
-
shaka.util.BufferUtils.toUint8(buffer), time);
|
|
196
|
+
shaka.util.BufferUtils.toUint8(buffer), time, uri);
|
|
196
197
|
const cuesToAppend = allCues.filter((cue) => {
|
|
197
198
|
return cue.startTime >= this.appendWindowStart_ &&
|
|
198
199
|
cue.startTime < this.appendWindowEnd_;
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
goog.provide('shaka.text.TtmlTextParser');
|
|
8
8
|
|
|
9
9
|
goog.require('goog.asserts');
|
|
10
|
+
goog.require('goog.Uri');
|
|
10
11
|
goog.require('shaka.log');
|
|
11
12
|
goog.require('shaka.text.Cue');
|
|
12
13
|
goog.require('shaka.text.CueRegion');
|
|
@@ -42,7 +43,7 @@ shaka.text.TtmlTextParser = class {
|
|
|
42
43
|
* @override
|
|
43
44
|
* @export
|
|
44
45
|
*/
|
|
45
|
-
parseMedia(data, time) {
|
|
46
|
+
parseMedia(data, time, uri) {
|
|
46
47
|
const TtmlTextParser = shaka.text.TtmlTextParser;
|
|
47
48
|
const XmlUtils = shaka.util.XmlUtils;
|
|
48
49
|
const ttpNs = TtmlTextParser.parameterNs_;
|
|
@@ -139,7 +140,7 @@ shaka.text.TtmlTextParser = class {
|
|
|
139
140
|
body, time, rateInfo, metadataElements, styles,
|
|
140
141
|
regionElements, cueRegions, whitespaceTrim,
|
|
141
142
|
cellResolutionInfo, /* parentCueElement= */ null,
|
|
142
|
-
/* isContent= */ false);
|
|
143
|
+
/* isContent= */ false, uri);
|
|
143
144
|
if (cue) {
|
|
144
145
|
// According to the TTML spec, backgrounds default to transparent.
|
|
145
146
|
// So default the background of the top-level element to transparent.
|
|
@@ -167,12 +168,14 @@ shaka.text.TtmlTextParser = class {
|
|
|
167
168
|
* @param {?{columns: number, rows: number}} cellResolution
|
|
168
169
|
* @param {?Element} parentCueElement
|
|
169
170
|
* @param {boolean} isContent
|
|
171
|
+
* @param {?(string|undefined)} uri
|
|
170
172
|
* @return {shaka.text.Cue}
|
|
171
173
|
* @private
|
|
172
174
|
*/
|
|
173
175
|
static parseCue_(
|
|
174
176
|
cueNode, timeContext, rateInfo, metadataElements, styles, regionElements,
|
|
175
|
-
cueRegions, whitespaceTrim, cellResolution, parentCueElement, isContent
|
|
177
|
+
cueRegions, whitespaceTrim, cellResolution, parentCueElement, isContent,
|
|
178
|
+
uri) {
|
|
176
179
|
/** @type {Element} */
|
|
177
180
|
let cueElement;
|
|
178
181
|
/** @type {Element} */
|
|
@@ -214,7 +217,21 @@ shaka.text.TtmlTextParser = class {
|
|
|
214
217
|
}
|
|
215
218
|
}
|
|
216
219
|
|
|
217
|
-
|
|
220
|
+
let imageUri = null;
|
|
221
|
+
const backgroundImage = shaka.util.XmlUtils.getAttributeNSList(
|
|
222
|
+
cueElement,
|
|
223
|
+
shaka.text.TtmlTextParser.smpteNsList_,
|
|
224
|
+
'backgroundImage');
|
|
225
|
+
if (uri && backgroundImage && !backgroundImage.startsWith('#')) {
|
|
226
|
+
const baseUri = new goog.Uri(uri);
|
|
227
|
+
const relativeUri = new goog.Uri(backgroundImage);
|
|
228
|
+
const newUri = baseUri.resolve(relativeUri).toString();
|
|
229
|
+
if (newUri) {
|
|
230
|
+
imageUri = newUri;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (cueNode.nodeName == 'p' || imageElement || imageUri) {
|
|
218
235
|
isContent = true;
|
|
219
236
|
}
|
|
220
237
|
|
|
@@ -247,6 +264,7 @@ shaka.text.TtmlTextParser = class {
|
|
|
247
264
|
cellResolution,
|
|
248
265
|
cueElement,
|
|
249
266
|
isContent,
|
|
267
|
+
uri,
|
|
250
268
|
);
|
|
251
269
|
|
|
252
270
|
// This node may or may not generate a nested cue.
|
|
@@ -380,6 +398,7 @@ shaka.text.TtmlTextParser = class {
|
|
|
380
398
|
cueElement,
|
|
381
399
|
regionElementForStyle,
|
|
382
400
|
imageElement,
|
|
401
|
+
imageUri,
|
|
383
402
|
styles,
|
|
384
403
|
/** isNested= */ parentIsContent, // "nested in a <div>" doesn't count.
|
|
385
404
|
/** isLeaf= */ (nestedCues.length == 0));
|
|
@@ -464,6 +483,11 @@ shaka.text.TtmlTextParser = class {
|
|
|
464
483
|
region.viewportAnchorX = region.viewportAnchorX * 100 /
|
|
465
484
|
globalWidth;
|
|
466
485
|
}
|
|
486
|
+
} else if (!extent) {
|
|
487
|
+
region.width = 100 - region.viewportAnchorX;
|
|
488
|
+
region.widthUnits = shaka.text.CueRegion.units.PERCENTAGE;
|
|
489
|
+
region.height = 100 - region.viewportAnchorY;
|
|
490
|
+
region.heightUnits = shaka.text.CueRegion.units.PERCENTAGE;
|
|
467
491
|
}
|
|
468
492
|
|
|
469
493
|
region.viewportAnchorUnits = percentage || globalWidth != null ?
|
|
@@ -482,13 +506,15 @@ shaka.text.TtmlTextParser = class {
|
|
|
482
506
|
* @param {!Element} cueElement
|
|
483
507
|
* @param {Element} region
|
|
484
508
|
* @param {Element} imageElement
|
|
509
|
+
* @param {?string} imageUri
|
|
485
510
|
* @param {!Array.<!Element>} styles
|
|
486
511
|
* @param {boolean} isNested
|
|
487
512
|
* @param {boolean} isLeaf
|
|
488
513
|
* @private
|
|
489
514
|
*/
|
|
490
515
|
static addStyle_(
|
|
491
|
-
cue, cueElement, region, imageElement,
|
|
516
|
+
cue, cueElement, region, imageElement, imageUri, styles,
|
|
517
|
+
isNested, isLeaf) {
|
|
492
518
|
const TtmlTextParser = shaka.text.TtmlTextParser;
|
|
493
519
|
const Cue = shaka.text.Cue;
|
|
494
520
|
|
|
@@ -656,6 +682,8 @@ shaka.text.TtmlTextParser = class {
|
|
|
656
682
|
backgroundImageData) {
|
|
657
683
|
cue.backgroundImage = 'data:image/png;base64,' + backgroundImageData;
|
|
658
684
|
}
|
|
685
|
+
} else if (imageUri) {
|
|
686
|
+
cue.backgroundImage = imageUri;
|
|
659
687
|
}
|
|
660
688
|
|
|
661
689
|
const textOutline = TtmlTextParser.getStyleAttribute_(
|
|
@@ -44,6 +44,9 @@ shaka.text.UITextDisplayer = class {
|
|
|
44
44
|
/** @private {HTMLElement} */
|
|
45
45
|
this.videoContainer_ = videoContainer;
|
|
46
46
|
|
|
47
|
+
/** @private {?number} */
|
|
48
|
+
this.aspectRatio_ = null;
|
|
49
|
+
|
|
47
50
|
/** @type {HTMLElement} */
|
|
48
51
|
this.textContainer_ = shaka.util.Dom.createHTMLElement('div');
|
|
49
52
|
this.textContainer_.classList.add('shaka-text-container');
|
|
@@ -90,6 +93,23 @@ shaka.text.UITextDisplayer = class {
|
|
|
90
93
|
this.updateCaptions_(/* forceUpdate= */ true);
|
|
91
94
|
});
|
|
92
95
|
|
|
96
|
+
// From: https://html.spec.whatwg.org/multipage/media.html#dom-video-videowidth
|
|
97
|
+
// Whenever the natural width or natural height of the video changes
|
|
98
|
+
// (including, for example, because the selected video track was changed),
|
|
99
|
+
// if the element's readyState attribute is not HAVE_NOTHING, the user
|
|
100
|
+
// agent must queue a media element task given the media element to fire an
|
|
101
|
+
// event named resize at the media element.
|
|
102
|
+
this.eventManager_.listen(this.video_, 'resize', () => {
|
|
103
|
+
const element = /** @type {!HTMLVideoElement} */ (this.video_);
|
|
104
|
+
const width = element.videoWidth;
|
|
105
|
+
const height = element.videoHeight;
|
|
106
|
+
if (width && height) {
|
|
107
|
+
this.aspectRatio_ = width / height;
|
|
108
|
+
} else {
|
|
109
|
+
this.aspectRatio_ = null;
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
93
113
|
/** @private {ResizeObserver} */
|
|
94
114
|
this.resizeObserver_ = null;
|
|
95
115
|
if ('ResizeObserver' in window) {
|
|
@@ -134,6 +154,11 @@ shaka.text.UITextDisplayer = class {
|
|
|
134
154
|
* @export
|
|
135
155
|
*/
|
|
136
156
|
destroy() {
|
|
157
|
+
// Return resolved promise if destroy() has been called.
|
|
158
|
+
if (!this.textContainer_) {
|
|
159
|
+
return Promise.resolve();
|
|
160
|
+
}
|
|
161
|
+
|
|
137
162
|
// Remove the text container element from the UI.
|
|
138
163
|
this.videoContainer_.removeChild(this.textContainer_);
|
|
139
164
|
this.textContainer_ = null;
|
|
@@ -156,6 +181,8 @@ shaka.text.UITextDisplayer = class {
|
|
|
156
181
|
this.resizeObserver_.disconnect();
|
|
157
182
|
this.resizeObserver_ = null;
|
|
158
183
|
}
|
|
184
|
+
|
|
185
|
+
return Promise.resolve();
|
|
159
186
|
}
|
|
160
187
|
|
|
161
188
|
|
|
@@ -400,6 +427,10 @@ shaka.text.UITextDisplayer = class {
|
|
|
400
427
|
*/
|
|
401
428
|
getRegionElement_(cue) {
|
|
402
429
|
const region = cue.region;
|
|
430
|
+
// from https://dvcs.w3.org/hg/text-tracks/raw-file/default/608toVTT/608toVTT.html#caption-window-size
|
|
431
|
+
// if aspect ratio is 4/3, use that value, otherwise, use the 16:9 value
|
|
432
|
+
const lineWidthMultiple = this.aspectRatio_ === 4/3 ? 2.5 : 1.9;
|
|
433
|
+
const lineHeightMultiple = 5.33;
|
|
403
434
|
|
|
404
435
|
const regionId = this.generateRegionId_(region);
|
|
405
436
|
if (this.regionElements_.has(regionId)) {
|
|
@@ -417,11 +448,33 @@ shaka.text.UITextDisplayer = class {
|
|
|
417
448
|
regionElement.id = 'shaka-text-region---' + regionId;
|
|
418
449
|
regionElement.classList.add('shaka-text-region');
|
|
419
450
|
|
|
420
|
-
regionElement.style.height = region.height + heightUnit;
|
|
421
|
-
regionElement.style.width = region.width + widthUnit;
|
|
422
451
|
regionElement.style.position = 'absolute';
|
|
423
|
-
|
|
424
|
-
|
|
452
|
+
|
|
453
|
+
const linesUnit = shaka.text.CueRegion.units.LINES;
|
|
454
|
+
if (region.heightUnits === linesUnit && region.widthUnits === linesUnit) {
|
|
455
|
+
regionElement.style.height = region.height * lineHeightMultiple + '%';
|
|
456
|
+
regionElement.style.width = region.width * lineWidthMultiple + '%';
|
|
457
|
+
} else {
|
|
458
|
+
regionElement.style.height = region.height + heightUnit;
|
|
459
|
+
regionElement.style.width = region.width + widthUnit;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
if (region.viewportAnchorUnits === linesUnit) {
|
|
463
|
+
// from https://dvcs.w3.org/hg/text-tracks/raw-file/default/608toVTT/608toVTT.html#positioning-in-cea-708
|
|
464
|
+
let top = region.viewportAnchorY / 75 * 100;
|
|
465
|
+
const windowWidth = this.aspectRatio_ === 4/3 ? 160 : 210;
|
|
466
|
+
let left = region.viewportAnchorX / windowWidth * 100;
|
|
467
|
+
// adjust top and left values based on the region anchor and window size
|
|
468
|
+
top -= region.regionAnchorY * region.height * lineHeightMultiple / 100;
|
|
469
|
+
left -= region.regionAnchorX * region.width * lineWidthMultiple / 100;
|
|
470
|
+
regionElement.style.top = top + '%';
|
|
471
|
+
regionElement.style.left = left + '%';
|
|
472
|
+
} else {
|
|
473
|
+
regionElement.style.top = region.viewportAnchorY -
|
|
474
|
+
region.regionAnchorY * region.height / 100 + viewportAnchorUnit;
|
|
475
|
+
regionElement.style.left = region.viewportAnchorX -
|
|
476
|
+
region.regionAnchorX * region.width / 100 + viewportAnchorUnit;
|
|
477
|
+
}
|
|
425
478
|
|
|
426
479
|
regionElement.style.display = 'flex';
|
|
427
480
|
regionElement.style.flexDirection = 'column';
|
|
@@ -600,6 +653,10 @@ shaka.text.UITextDisplayer = class {
|
|
|
600
653
|
// Only do this for the text itself.
|
|
601
654
|
elem.style.backgroundColor = 'rgba(0, 0, 0, 0.8)';
|
|
602
655
|
}
|
|
656
|
+
const fontFamily = inherit((c) => c.fontFamily);
|
|
657
|
+
if (fontFamily) {
|
|
658
|
+
elem.style.fontFamily = fontFamily;
|
|
659
|
+
}
|
|
603
660
|
}
|
|
604
661
|
if (text) {
|
|
605
662
|
elem.textContent = text;
|
|
@@ -653,37 +710,46 @@ shaka.text.UITextDisplayer = class {
|
|
|
653
710
|
// (for vertical growing left) is aligned at the line.
|
|
654
711
|
// TODO: Implement line alignment with line number.
|
|
655
712
|
// TODO: Implement lineAlignment of 'CENTER'.
|
|
656
|
-
|
|
713
|
+
let line = cue.line;
|
|
714
|
+
if (line != null) {
|
|
657
715
|
let lineInterpretation = cue.lineInterpretation;
|
|
658
716
|
// HACK: the current implementation of UITextDisplayer only handled
|
|
659
|
-
// PERCENTAGE,
|
|
660
|
-
|
|
661
|
-
if (cue.line == 0 &&
|
|
662
|
-
lineInterpretation == Cue.lineInterpretation.LINE_NUMBER) {
|
|
717
|
+
// PERCENTAGE, so we need convert LINE_NUMBER to PERCENTAGE
|
|
718
|
+
if (lineInterpretation == Cue.lineInterpretation.LINE_NUMBER) {
|
|
663
719
|
lineInterpretation = Cue.lineInterpretation.PERCENTAGE;
|
|
720
|
+
let maxLines = 16;
|
|
721
|
+
// The maximum number of lines is different if it is a vertical video.
|
|
722
|
+
if (this.aspectRatio_ && this.aspectRatio_ < 1) {
|
|
723
|
+
maxLines = 32;
|
|
724
|
+
}
|
|
725
|
+
if (line < 0) {
|
|
726
|
+
line = 100 + line / maxLines * 100;
|
|
727
|
+
} else {
|
|
728
|
+
line = line / maxLines * 100;
|
|
729
|
+
}
|
|
664
730
|
}
|
|
665
731
|
if (lineInterpretation == Cue.lineInterpretation.PERCENTAGE) {
|
|
666
732
|
style.position = 'absolute';
|
|
667
733
|
if (cue.writingMode == Cue.writingMode.HORIZONTAL_TOP_TO_BOTTOM) {
|
|
668
734
|
style.width = '100%';
|
|
669
735
|
if (cue.lineAlign == Cue.lineAlign.START) {
|
|
670
|
-
style.top =
|
|
736
|
+
style.top = line + '%';
|
|
671
737
|
} else if (cue.lineAlign == Cue.lineAlign.END) {
|
|
672
|
-
style.bottom = (100 -
|
|
738
|
+
style.bottom = (100 - line) + '%';
|
|
673
739
|
}
|
|
674
740
|
} else if (cue.writingMode == Cue.writingMode.VERTICAL_LEFT_TO_RIGHT) {
|
|
675
741
|
style.height = '100%';
|
|
676
742
|
if (cue.lineAlign == Cue.lineAlign.START) {
|
|
677
|
-
style.left =
|
|
743
|
+
style.left = line + '%';
|
|
678
744
|
} else if (cue.lineAlign == Cue.lineAlign.END) {
|
|
679
|
-
style.right = (100 -
|
|
745
|
+
style.right = (100 - line) + '%';
|
|
680
746
|
}
|
|
681
747
|
} else {
|
|
682
748
|
style.height = '100%';
|
|
683
749
|
if (cue.lineAlign == Cue.lineAlign.START) {
|
|
684
|
-
style.right =
|
|
750
|
+
style.right = line + '%';
|
|
685
751
|
} else if (cue.lineAlign == Cue.lineAlign.END) {
|
|
686
|
-
style.left = (100 -
|
|
752
|
+
style.left = (100 - line) + '%';
|
|
687
753
|
}
|
|
688
754
|
}
|
|
689
755
|
}
|
|
@@ -433,6 +433,11 @@ shaka.text.VttTextParser = class {
|
|
|
433
433
|
*/
|
|
434
434
|
static parseCueStyles(payload, rootCue, styles) {
|
|
435
435
|
const VttTextParser = shaka.text.VttTextParser;
|
|
436
|
+
// Optimization for unstyled payloads.
|
|
437
|
+
if (!payload.includes('<')) {
|
|
438
|
+
rootCue.payload = VttTextParser.htmlUnescape_(payload);
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
436
441
|
if (styles.size === 0) {
|
|
437
442
|
VttTextParser.addDefaultTextColor_(styles);
|
|
438
443
|
}
|
|
@@ -688,6 +693,14 @@ shaka.text.VttTextParser = class {
|
|
|
688
693
|
static generateCueFromElement_(element, rootCue, cues, styles) {
|
|
689
694
|
const VttTextParser = shaka.text.VttTextParser;
|
|
690
695
|
const nestedCue = rootCue.clone();
|
|
696
|
+
// We don't want propagate some properties.
|
|
697
|
+
nestedCue.nestedCues = [];
|
|
698
|
+
nestedCue.payload = '';
|
|
699
|
+
// We don't want propagate some position settings
|
|
700
|
+
nestedCue.line = null;
|
|
701
|
+
nestedCue.region = new shaka.text.CueRegion();
|
|
702
|
+
nestedCue.position = null;
|
|
703
|
+
nestedCue.size = 0;
|
|
691
704
|
if (element.nodeType === Node.ELEMENT_NODE && element.nodeName) {
|
|
692
705
|
const bold = shaka.text.Cue.fontWeight.BOLD;
|
|
693
706
|
const italic = shaka.text.Cue.fontStyle.ITALIC;
|
package/lib/util/buffer_utils.js
CHANGED
|
@@ -110,6 +110,23 @@ shaka.util.BufferUtils = class {
|
|
|
110
110
|
return shaka.util.BufferUtils.view_(data, offset, length, Uint8Array);
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
+
/**
|
|
114
|
+
* Creates a new Uint16Array view on the same buffer. This clamps the values
|
|
115
|
+
* to be within the same view (i.e. you can't use this to move past the end
|
|
116
|
+
* of the view, even if the underlying buffer is larger). However, you can
|
|
117
|
+
* pass a negative offset to access the data before the view.
|
|
118
|
+
*
|
|
119
|
+
* @param {BufferSource} data
|
|
120
|
+
* @param {number=} offset The offset from the beginning of this data's view
|
|
121
|
+
* to start the new view at.
|
|
122
|
+
* @param {number=} length The byte length of the new view.
|
|
123
|
+
* @return {!Uint16Array}
|
|
124
|
+
* @export
|
|
125
|
+
*/
|
|
126
|
+
static toUint16(data, offset = 0, length = Infinity) {
|
|
127
|
+
return shaka.util.BufferUtils.view_(data, offset, length, Uint16Array);
|
|
128
|
+
}
|
|
129
|
+
|
|
113
130
|
/**
|
|
114
131
|
* Creates a DataView over the given buffer.
|
|
115
132
|
*
|
|
@@ -135,15 +152,20 @@ shaka.util.BufferUtils = class {
|
|
|
135
152
|
*/
|
|
136
153
|
static view_(data, offset, length, Type) {
|
|
137
154
|
const buffer = shaka.util.BufferUtils.unsafeGetArrayBuffer_(data);
|
|
155
|
+
let bytesPerElement = 1;
|
|
156
|
+
if ('BYTES_PER_ELEMENT' in Type) {
|
|
157
|
+
bytesPerElement = Type.BYTES_PER_ELEMENT;
|
|
158
|
+
}
|
|
138
159
|
// Absolute end of the |data| view within |buffer|.
|
|
139
160
|
/** @suppress {strictMissingProperties} */
|
|
140
|
-
const dataEnd = (data.byteOffset || 0) + data.byteLength
|
|
161
|
+
const dataEnd = ((data.byteOffset || 0) + data.byteLength) /
|
|
162
|
+
bytesPerElement;
|
|
141
163
|
// Absolute start of the result within |buffer|.
|
|
142
164
|
/** @suppress {strictMissingProperties} */
|
|
143
|
-
const rawStart = (data.byteOffset || 0) + offset;
|
|
144
|
-
const start = Math.max(0, Math.min(rawStart, dataEnd));
|
|
165
|
+
const rawStart = ((data.byteOffset || 0) + offset) / bytesPerElement;
|
|
166
|
+
const start = Math.floor(Math.max(0, Math.min(rawStart, dataEnd)));
|
|
145
167
|
// Absolute end of the result within |buffer|.
|
|
146
|
-
const end = Math.min(start + Math.max(length, 0), dataEnd);
|
|
168
|
+
const end = Math.floor(Math.min(start + Math.max(length, 0), dataEnd));
|
|
147
169
|
return new Type(buffer, start, end - start);
|
|
148
170
|
}
|
|
149
171
|
};
|
package/lib/util/cmcd_manager.js
CHANGED
|
@@ -331,12 +331,27 @@ shaka.util.CmcdManager = class {
|
|
|
331
331
|
* @private
|
|
332
332
|
*/
|
|
333
333
|
getObjectTypeFromMimeType_(mimeType) {
|
|
334
|
-
switch (mimeType) {
|
|
334
|
+
switch (mimeType.toLowerCase()) {
|
|
335
|
+
case 'audio/mp4':
|
|
336
|
+
case 'audio/webm':
|
|
337
|
+
case 'audio/ogg':
|
|
338
|
+
case 'audio/mpeg':
|
|
339
|
+
case 'audio/aac':
|
|
340
|
+
case 'audio/flac':
|
|
341
|
+
case 'audio/wav':
|
|
342
|
+
return shaka.util.CmcdManager.ObjectType.AUDIO;
|
|
343
|
+
|
|
335
344
|
case 'video/webm':
|
|
336
345
|
case 'video/mp4':
|
|
346
|
+
case 'video/mpeg':
|
|
347
|
+
case 'video/mp2t':
|
|
337
348
|
return shaka.util.CmcdManager.ObjectType.MUXED;
|
|
338
349
|
|
|
339
350
|
case 'application/x-mpegurl':
|
|
351
|
+
case 'application/vnd.apple.mpegurl':
|
|
352
|
+
case 'application/dash+xml':
|
|
353
|
+
case 'video/vnd.mpeg.dash.mpd':
|
|
354
|
+
case 'application/vnd.ms-sstr+xml':
|
|
340
355
|
return shaka.util.CmcdManager.ObjectType.MANIFEST;
|
|
341
356
|
|
|
342
357
|
default:
|
|
@@ -192,7 +192,7 @@ shaka.util.FairPlayUtils = class {
|
|
|
192
192
|
const stringToArray = (string) => {
|
|
193
193
|
// 2 bytes for each char
|
|
194
194
|
const buffer = new ArrayBuffer(string.length * 2);
|
|
195
|
-
const array =
|
|
195
|
+
const array = shaka.util.BufferUtils.toUint16(buffer);
|
|
196
196
|
for (let i = 0, strLen = string.length; i < strLen; i++) {
|
|
197
197
|
array[i] = string.charCodeAt(i);
|
|
198
198
|
}
|
|
@@ -8,7 +8,6 @@ goog.provide('shaka.util.ManifestParserUtils');
|
|
|
8
8
|
|
|
9
9
|
goog.require('goog.Uri');
|
|
10
10
|
goog.require('shaka.util.Error');
|
|
11
|
-
goog.require('shaka.util.Functional');
|
|
12
11
|
|
|
13
12
|
|
|
14
13
|
/**
|
|
@@ -24,18 +23,29 @@ shaka.util.ManifestParserUtils = class {
|
|
|
24
23
|
* @return {!Array.<string>}
|
|
25
24
|
*/
|
|
26
25
|
static resolveUris(baseUris, relativeUris) {
|
|
27
|
-
const Functional = shaka.util.Functional;
|
|
28
26
|
if (relativeUris.length == 0) {
|
|
29
27
|
return baseUris;
|
|
30
28
|
}
|
|
31
29
|
|
|
30
|
+
if (baseUris.length == 1 && relativeUris.length == 1) {
|
|
31
|
+
const baseUri = new goog.Uri(baseUris[0]);
|
|
32
|
+
const relativeUri = new goog.Uri(relativeUris[0]);
|
|
33
|
+
return [baseUri.resolve(relativeUri).toString()];
|
|
34
|
+
}
|
|
35
|
+
|
|
32
36
|
const relativeAsGoog = relativeUris.map((uri) => new goog.Uri(uri));
|
|
33
|
-
|
|
34
|
-
//
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
|
|
38
|
+
// For each base URI, this code resolves it with every relative URI.
|
|
39
|
+
// The result is a single array containing all the resolved URIs.
|
|
40
|
+
const resolvedUris = [];
|
|
41
|
+
for (const baseStr of baseUris) {
|
|
42
|
+
const base = new goog.Uri(baseStr);
|
|
43
|
+
for (const relative of relativeAsGoog) {
|
|
44
|
+
resolvedUris.push(base.resolve(relative).toString());
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return resolvedUris;
|
|
39
49
|
}
|
|
40
50
|
|
|
41
51
|
|
|
@@ -166,6 +176,8 @@ shaka.util.ManifestParserUtils.VIDEO_CODEC_REGEXPS_ = [
|
|
|
166
176
|
/^avc/,
|
|
167
177
|
/^hev/,
|
|
168
178
|
/^hvc/,
|
|
179
|
+
/^vvc/,
|
|
180
|
+
/^vvi/,
|
|
169
181
|
/^vp0?[89]/,
|
|
170
182
|
/^av01/,
|
|
171
183
|
/^dvh/,
|
package/lib/util/object_utils.js
CHANGED
|
@@ -19,7 +19,7 @@ shaka.util.ObjectUtils = class {
|
|
|
19
19
|
* @return {T}
|
|
20
20
|
*/
|
|
21
21
|
static cloneObject(arg) {
|
|
22
|
-
const seenObjects = new
|
|
22
|
+
const seenObjects = new WeakSet();
|
|
23
23
|
// This recursively clones the value |val|, using the captured variable
|
|
24
24
|
// |seenObjects| to track the objects we have already cloned.
|
|
25
25
|
/** @suppress {strictMissingProperties} */
|
package/lib/util/periods.js
CHANGED
|
@@ -90,6 +90,37 @@ shaka.util.PeriodCombiner = class {
|
|
|
90
90
|
return this.imageStreams_;
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
+
/**
|
|
94
|
+
* Returns an object that contains arrays of streams by type
|
|
95
|
+
* @param {!Array.<shaka.util.PeriodCombiner.Period>} periods
|
|
96
|
+
* @return {{
|
|
97
|
+
* audioStreamsPerPeriod: !Array.<!Array.<shaka.extern.Stream>>,
|
|
98
|
+
* videoStreamsPerPeriod: !Array.<!Array.<shaka.extern.Stream>>,
|
|
99
|
+
* textStreamsPerPeriod: !Array.<!Array.<shaka.extern.Stream>>,
|
|
100
|
+
* imageStreamsPerPeriod: !Array.<!Array.<shaka.extern.Stream>>
|
|
101
|
+
* }}
|
|
102
|
+
* @private
|
|
103
|
+
*/
|
|
104
|
+
getStreamsPerPeriod_(periods) {
|
|
105
|
+
const audioStreamsPerPeriod = [];
|
|
106
|
+
const videoStreamsPerPeriod = [];
|
|
107
|
+
const textStreamsPerPeriod = [];
|
|
108
|
+
const imageStreamsPerPeriod = [];
|
|
109
|
+
|
|
110
|
+
for (const period of periods) {
|
|
111
|
+
audioStreamsPerPeriod.push(period.audioStreams);
|
|
112
|
+
videoStreamsPerPeriod.push(period.videoStreams);
|
|
113
|
+
textStreamsPerPeriod.push(period.textStreams);
|
|
114
|
+
imageStreamsPerPeriod.push(period.imageStreams);
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
audioStreamsPerPeriod,
|
|
118
|
+
videoStreamsPerPeriod,
|
|
119
|
+
textStreamsPerPeriod,
|
|
120
|
+
imageStreamsPerPeriod,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
93
124
|
/**
|
|
94
125
|
* @param {!Array.<shaka.util.PeriodCombiner.Period>} periods
|
|
95
126
|
* @param {boolean} isDynamic
|
|
@@ -136,14 +167,12 @@ shaka.util.PeriodCombiner = class {
|
|
|
136
167
|
return;
|
|
137
168
|
}
|
|
138
169
|
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
const imageStreamsPerPeriod = periods.map(
|
|
146
|
-
(period) => period.imageStreams);
|
|
170
|
+
const {
|
|
171
|
+
audioStreamsPerPeriod,
|
|
172
|
+
videoStreamsPerPeriod,
|
|
173
|
+
textStreamsPerPeriod,
|
|
174
|
+
imageStreamsPerPeriod,
|
|
175
|
+
} = this.getStreamsPerPeriod_(periods);
|
|
147
176
|
|
|
148
177
|
// It's okay to have a period with no text or images, but our algorithm
|
|
149
178
|
// fails on any period without matching streams. So we add dummy streams
|
|
@@ -159,33 +188,32 @@ shaka.util.PeriodCombiner = class {
|
|
|
159
188
|
ContentType.IMAGE));
|
|
160
189
|
}
|
|
161
190
|
|
|
162
|
-
await
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
shaka.util.PeriodCombiner.concatenateStreams_);
|
|
191
|
+
await Promise.all([
|
|
192
|
+
shaka.util.PeriodCombiner.combine_(
|
|
193
|
+
this.audioStreams_,
|
|
194
|
+
audioStreamsPerPeriod,
|
|
195
|
+
firstNewPeriodIndex,
|
|
196
|
+
shaka.util.PeriodCombiner.cloneStream_,
|
|
197
|
+
shaka.util.PeriodCombiner.concatenateStreams_),
|
|
198
|
+
shaka.util.PeriodCombiner.combine_(
|
|
199
|
+
this.videoStreams_,
|
|
200
|
+
videoStreamsPerPeriod,
|
|
201
|
+
firstNewPeriodIndex,
|
|
202
|
+
shaka.util.PeriodCombiner.cloneStream_,
|
|
203
|
+
shaka.util.PeriodCombiner.concatenateStreams_),
|
|
204
|
+
shaka.util.PeriodCombiner.combine_(
|
|
205
|
+
this.textStreams_,
|
|
206
|
+
textStreamsPerPeriod,
|
|
207
|
+
firstNewPeriodIndex,
|
|
208
|
+
shaka.util.PeriodCombiner.cloneStream_,
|
|
209
|
+
shaka.util.PeriodCombiner.concatenateStreams_),
|
|
210
|
+
shaka.util.PeriodCombiner.combine_(
|
|
211
|
+
this.imageStreams_,
|
|
212
|
+
imageStreamsPerPeriod,
|
|
213
|
+
firstNewPeriodIndex,
|
|
214
|
+
shaka.util.PeriodCombiner.cloneStream_,
|
|
215
|
+
shaka.util.PeriodCombiner.concatenateStreams_),
|
|
216
|
+
]);
|
|
189
217
|
}
|
|
190
218
|
|
|
191
219
|
// Create variants for all audio/video combinations.
|
|
@@ -1102,6 +1130,11 @@ shaka.util.PeriodCombiner = class {
|
|
|
1102
1130
|
return true;
|
|
1103
1131
|
}
|
|
1104
1132
|
|
|
1133
|
+
// Forced subtitles should be treated as unique streams
|
|
1134
|
+
if (outputStream.forced !== candidate.forced) {
|
|
1135
|
+
return false;
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1105
1138
|
const languageRelatedness = LanguageUtils.relatedness(
|
|
1106
1139
|
outputStream.language, candidate.language);
|
|
1107
1140
|
|
|
@@ -1180,7 +1213,18 @@ shaka.util.PeriodCombiner = class {
|
|
|
1180
1213
|
return false;
|
|
1181
1214
|
}
|
|
1182
1215
|
|
|
1183
|
-
// If language-based differences haven't decided this, look at
|
|
1216
|
+
// If language-based differences haven't decided this, look at labels.
|
|
1217
|
+
// If available options differ, look does any matches with output stream.
|
|
1218
|
+
if (best.label !== candidate.label) {
|
|
1219
|
+
if (outputStream.label === best.label) {
|
|
1220
|
+
return false;
|
|
1221
|
+
}
|
|
1222
|
+
if (outputStream.label === candidate.label) {
|
|
1223
|
+
return true;
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
// If label-based differences haven't decided this, look at roles. If
|
|
1184
1228
|
// the candidate has more roles in common with the output, upgrade to the
|
|
1185
1229
|
// candidate.
|
|
1186
1230
|
if (outputStream.roles.length) {
|
|
@@ -1383,6 +1427,17 @@ shaka.util.PeriodCombiner = class {
|
|
|
1383
1427
|
return false;
|
|
1384
1428
|
}
|
|
1385
1429
|
|
|
1430
|
+
// If language-based differences haven't decided this, look at labels.
|
|
1431
|
+
// If available options differ, look does any matches with output stream.
|
|
1432
|
+
if (best.label !== candidate.label) {
|
|
1433
|
+
if (outputStream.label === best.label) {
|
|
1434
|
+
return false;
|
|
1435
|
+
}
|
|
1436
|
+
if (outputStream.label === candidate.label) {
|
|
1437
|
+
return true;
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1386
1441
|
// If the candidate has more roles in common with the output, upgrade to the
|
|
1387
1442
|
// candidate.
|
|
1388
1443
|
if (outputStream.roles.length) {
|