shaka-player 3.1.1 → 3.1.5
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/AUTHORS +7 -2
- package/CHANGELOG.md +199 -0
- package/CONTRIBUTORS +9 -3
- package/build/generateExterns.js +1 -1
- package/build/misspellings.txt +1 -0
- package/build/shakaBuildHelpers.py +6 -5
- package/build/subprocessWindowsPatch.py +103 -0
- package/demo/common/assets.js +1 -1
- package/dist/controls.css +2 -2
- package/dist/controls.css.map +1 -1
- package/dist/demo.compiled.debug.js +1 -1
- package/dist/demo.compiled.js +1 -1
- package/dist/deps.js +7 -7
- package/dist/locales.js +35 -33
- package/dist/receiver.compiled.debug.js +1 -1
- package/dist/receiver.compiled.js +1 -1
- package/dist/shaka-player.compiled.d.ts +171 -3
- package/dist/shaka-player.compiled.debug.d.ts +171 -3
- package/dist/shaka-player.compiled.debug.externs.js +179 -1
- package/dist/shaka-player.compiled.debug.js +365 -344
- package/dist/shaka-player.compiled.debug.map +3 -3
- package/dist/shaka-player.compiled.externs.js +179 -1
- package/dist/shaka-player.compiled.js +806 -798
- package/dist/shaka-player.compiled.map +3 -3
- package/dist/shaka-player.ui.d.ts +171 -3
- package/dist/shaka-player.ui.debug.d.ts +171 -3
- package/dist/shaka-player.ui.debug.externs.js +181 -3
- package/dist/shaka-player.ui.debug.js +458 -435
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +181 -3
- package/dist/shaka-player.ui.js +927 -918
- package/dist/shaka-player.ui.map +4 -4
- package/docs/tutorials/fairplay.md +1 -1
- package/docs/tutorials/faq.md +14 -0
- package/docs/tutorials/index.json +2 -2
- package/docs/tutorials/upgrade-v3.md +38 -0
- package/docs/tutorials/upgrade.md +3 -3
- package/externs/ima.js +4 -0
- package/externs/shaka/ads.js +2 -0
- package/externs/shaka/player.js +6 -0
- package/externs/shaka/text.js +10 -0
- package/karma.conf.js +1 -0
- package/lib/ads/ad_manager.js +29 -11
- package/lib/ads/client_side_ad_manager.js +76 -86
- package/lib/ads/server_side_ad_manager.js +26 -11
- package/lib/cast/cast_proxy.js +3 -2
- package/lib/cast/cast_receiver.js +2 -1
- package/lib/cast/cast_sender.js +2 -2
- package/lib/cea/sei_processor.js +24 -17
- package/lib/dash/dash_parser.js +100 -5
- package/lib/dash/segment_list.js +1 -1
- package/lib/dash/segment_template.js +7 -4
- package/lib/hls/hls_parser.js +44 -17
- package/lib/media/closed_caption_parser.js +3 -1
- package/lib/media/content_workarounds.js +12 -0
- package/lib/media/drm_engine.js +20 -1
- package/lib/media/gap_jumping_controller.js +8 -4
- package/lib/media/media_source_engine.js +1 -2
- package/lib/media/playhead.js +1 -1
- package/lib/media/presentation_timeline.js +3 -1
- package/lib/media/segment_reference.js +8 -0
- package/lib/media/streaming_engine.js +28 -19
- package/lib/net/networking_engine.js +2 -1
- package/lib/player.js +108 -54
- package/lib/polyfill/encryption_scheme.js +2 -0
- package/lib/polyfill/fullscreen.js +2 -0
- package/lib/polyfill/mathround.js +2 -0
- package/lib/polyfill/media_capabilities.js +76 -22
- package/lib/polyfill/mediasource.js +8 -0
- package/lib/polyfill/orientation.js +3 -1
- package/lib/polyfill/patchedmediakeys_apple.js +6 -4
- package/lib/polyfill/patchedmediakeys_ms.js +6 -4
- package/lib/polyfill/patchedmediakeys_nop.js +2 -0
- package/lib/polyfill/patchedmediakeys_webkit.js +6 -4
- package/lib/polyfill/pip_webkit.js +2 -0
- package/lib/polyfill/storage_estimate.js +2 -0
- package/lib/polyfill/video_play_promise.js +2 -0
- package/lib/polyfill/videoplaybackquality.js +2 -0
- package/lib/polyfill/vttcue.js +2 -0
- package/lib/text/cue.js +17 -0
- package/lib/text/mp4_ttml_parser.js +3 -0
- package/lib/text/text_engine.js +14 -2
- package/lib/text/ttml_text_parser.js +135 -87
- package/lib/text/ui_text_displayer.js +233 -83
- package/lib/text/vtt_text_parser.js +4 -2
- package/lib/util/fake_event.js +53 -7
- package/lib/util/mp4_box_parsers.js +1 -1
- package/lib/util/periods.js +111 -35
- package/lib/util/platform.js +13 -1
- package/lib/util/stream_utils.js +79 -10
- package/lib/util/xml_utils.js +19 -0
- package/package.json +2 -2
- package/test/cast/cast_proxy_unit.js +12 -6
- package/test/cast/cast_receiver_integration.js +1 -1
- package/test/cast/cast_utils_unit.js +1 -1
- package/test/cea/mp4_cea_parser_unit.js +26 -0
- package/test/dash/dash_parser_live_unit.js +1 -0
- package/test/dash/dash_parser_manifest_unit.js +220 -2
- package/test/dash/dash_parser_segment_template_unit.js +3 -3
- package/test/hls/hls_parser_unit.js +5 -1
- package/test/media/closed_caption_parser_unit.js +20 -0
- package/test/media/drm_engine_unit.js +56 -0
- package/test/media/playhead_unit.js +27 -1
- package/test/media/presentation_timeline_unit.js +19 -0
- package/test/media/streaming_engine_integration.js +2 -1
- package/test/media/streaming_engine_unit.js +148 -3
- package/test/player_integration.js +38 -0
- package/test/player_unit.js +101 -0
- package/test/test/assets/3675/chunk-stream0-00001.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00002.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00003.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00004.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00005.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00006.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00007.m4s +0 -0
- package/test/test/assets/3675/dash_0.mpd +30 -0
- package/test/test/assets/3675/dash_1.mpd +30 -0
- package/test/test/assets/3675/dash_2.mpd +30 -0
- package/test/test/assets/3675/dash_3.mpd +30 -0
- package/test/test/assets/3675/dash_4.mpd +30 -0
- package/test/test/assets/3675/dash_5.mpd +26 -0
- package/test/test/assets/3675/init-stream0.m4s +0 -0
- package/test/test/assets/empty_caption_video_init.mp4 +0 -0
- package/test/test/assets/empty_caption_video_segment.mp4 +0 -0
- package/test/test/util/fake_ad_manager.js +4 -1
- package/test/test/util/simple_fakes.js +5 -3
- package/test/test/util/ttml_utils.js +81 -0
- package/test/text/mp4_ttml_parser_unit.js +11 -18
- package/test/text/ttml_text_parser_unit.js +542 -220
- package/test/text/ui_text_displayer_unit.js +95 -18
- package/test/text/vtt_text_parser_unit.js +17 -1
- package/test/ui/localization_unit.js +15 -14
- package/test/ui/text_displayer_layout_unit.js +52 -1
- package/test/util/mp4_box_parsers_unit.js +28 -0
- package/test/util/periods_unit.js +190 -2
- package/test/util/stream_utils_unit.js +59 -2
- package/ui/airplay_button.js +5 -0
- package/ui/big_play_button.js +10 -0
- package/ui/cast_button.js +2 -3
- package/ui/controls.js +8 -6
- package/ui/less/buttons.less +0 -4
- package/ui/less/containers.less +10 -5
- package/ui/locales/en.json +1 -0
- package/ui/locales/ru.json +6 -6
- package/ui/locales/source.json +4 -0
- package/ui/localization.js +28 -51
- package/ui/pip_button.js +2 -3
- package/ui/play_button.js +10 -10
- package/ui/small_play_button.js +12 -0
- package/ui/text_selection.js +1 -0
- package/docs/tutorials/upgrade-v2-4.md +0 -481
|
@@ -73,7 +73,14 @@ shaka.text.UITextDisplayer = class {
|
|
|
73
73
|
this.updateCaptions_();
|
|
74
74
|
}).tickEvery(updatePeriod);
|
|
75
75
|
|
|
76
|
-
/**
|
|
76
|
+
/**
|
|
77
|
+
* Maps cues to cue elements. Specifically points out the wrapper element of
|
|
78
|
+
* the cue (e.g. the HTML element to put nested cues inside).
|
|
79
|
+
* @private {Map.<!shaka.extern.Cue, !{
|
|
80
|
+
* cueElement: !HTMLElement,
|
|
81
|
+
* wrapper: !HTMLElement
|
|
82
|
+
* }>}
|
|
83
|
+
*/
|
|
77
84
|
this.currentCuesMap_ = new Map();
|
|
78
85
|
|
|
79
86
|
/** @private {shaka.util.EventManager} */
|
|
@@ -160,9 +167,14 @@ shaka.text.UITextDisplayer = class {
|
|
|
160
167
|
}
|
|
161
168
|
|
|
162
169
|
// Remove the cues out of the time range.
|
|
170
|
+
const oldNumCues = this.cues_.length;
|
|
163
171
|
this.cues_ = this.cues_.filter(
|
|
164
172
|
(cue) => cue.startTime < start || cue.endTime >= end);
|
|
165
|
-
this
|
|
173
|
+
// If anything was actually removed in this process, force the captions to
|
|
174
|
+
// update. This makes sure that the currently-displayed cues will stop
|
|
175
|
+
// displaying if removed (say, due to the user changing languages).
|
|
176
|
+
const forceUpdate = oldNumCues > this.cues_.length;
|
|
177
|
+
this.updateCaptions_(forceUpdate);
|
|
166
178
|
|
|
167
179
|
return true;
|
|
168
180
|
}
|
|
@@ -185,68 +197,151 @@ shaka.text.UITextDisplayer = class {
|
|
|
185
197
|
}
|
|
186
198
|
|
|
187
199
|
/**
|
|
188
|
-
*
|
|
189
|
-
* @param {
|
|
200
|
+
* @param {!Array.<!shaka.extern.Cue>} cues
|
|
201
|
+
* @param {!HTMLElement} container
|
|
202
|
+
* @param {number} currentTime
|
|
203
|
+
* @param {!Array.<!shaka.extern.Cue>} parents
|
|
190
204
|
* @private
|
|
191
205
|
*/
|
|
192
|
-
|
|
193
|
-
|
|
206
|
+
updateCuesRecursive_(cues, container, currentTime, parents) {
|
|
207
|
+
// Set to true if the cues have changed in some way, which will require
|
|
208
|
+
// DOM changes. E.g. if a cue was added or removed.
|
|
209
|
+
let updateDOM = false;
|
|
210
|
+
/**
|
|
211
|
+
* The elements to remove from the DOM.
|
|
212
|
+
* Some of these elements may be added back again, if their corresponding
|
|
213
|
+
* cue is in toPlant.
|
|
214
|
+
* These elements are only removed if updateDOM is true.
|
|
215
|
+
* @type {!Array.<!HTMLElement>}
|
|
216
|
+
*/
|
|
217
|
+
const toUproot = [];
|
|
218
|
+
/**
|
|
219
|
+
* The cues whose corresponding elements should be in the DOM.
|
|
220
|
+
* Some of these might be new, some might have been displayed beforehand.
|
|
221
|
+
* These will only be added if updateDOM is true.
|
|
222
|
+
* @type {!Array.<!shaka.extern.Cue>}
|
|
223
|
+
*/
|
|
224
|
+
const toPlant = [];
|
|
225
|
+
for (const cue of cues) {
|
|
226
|
+
parents.push(cue);
|
|
227
|
+
|
|
228
|
+
let cueRegistry = this.currentCuesMap_.get(cue);
|
|
229
|
+
const shouldBeDisplayed =
|
|
230
|
+
cue.startTime <= currentTime && cue.endTime > currentTime;
|
|
231
|
+
let wrapper = cueRegistry ? cueRegistry.wrapper : null;
|
|
232
|
+
|
|
233
|
+
if (cueRegistry) {
|
|
234
|
+
// If the cues are replanted, all existing cues should be uprooted,
|
|
235
|
+
// even ones which are going to be planted again.
|
|
236
|
+
toUproot.push(cueRegistry.cueElement);
|
|
237
|
+
|
|
238
|
+
// If the cue should not be displayed, remove it entirely.
|
|
239
|
+
if (!shouldBeDisplayed) {
|
|
240
|
+
// Since something has to be removed, we will need to update the DOM.
|
|
241
|
+
updateDOM = true;
|
|
242
|
+
this.currentCuesMap_.delete(cue);
|
|
243
|
+
cueRegistry = null;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
194
246
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
247
|
+
if (shouldBeDisplayed) {
|
|
248
|
+
toPlant.push(cue);
|
|
249
|
+
if (!cueRegistry) {
|
|
250
|
+
// The cue has to be made!
|
|
251
|
+
this.createCue_(cue, parents);
|
|
252
|
+
cueRegistry = this.currentCuesMap_.get(cue);
|
|
253
|
+
wrapper = cueRegistry.wrapper;
|
|
254
|
+
updateDOM = true;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
200
257
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
this.
|
|
258
|
+
// Recursively check the nested cues, to see if they need to be added or
|
|
259
|
+
// removed.
|
|
260
|
+
// If wrapper is null, that means that the cue is not only not being
|
|
261
|
+
// displayed currently, it also was not removed this tick. So it's
|
|
262
|
+
// guaranteed that the children will neither need to be added nor removed.
|
|
263
|
+
if (cue.nestedCues.length > 0 && wrapper) {
|
|
264
|
+
this.updateCuesRecursive_(
|
|
265
|
+
cue.nestedCues, wrapper, currentTime, parents);
|
|
208
266
|
}
|
|
209
|
-
}
|
|
210
267
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
268
|
+
const topCue = parents.pop();
|
|
269
|
+
goog.asserts.assert(topCue == cue, 'Parent cues should be kept in order');
|
|
270
|
+
}
|
|
271
|
+
if (updateDOM) {
|
|
272
|
+
for (const cueElement of toUproot) {
|
|
273
|
+
container.removeChild(cueElement);
|
|
274
|
+
}
|
|
275
|
+
toPlant.sort((a, b) => {
|
|
276
|
+
if (a.startTime != b.startTime) {
|
|
277
|
+
return a.startTime - b.startTime;
|
|
278
|
+
} else {
|
|
279
|
+
return a.endTime - b.endTime;
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
for (const cue of toPlant) {
|
|
283
|
+
const cueRegistry = this.currentCuesMap_.get(cue);
|
|
284
|
+
goog.asserts.assert(cueRegistry, 'cueRegistry should exist.');
|
|
285
|
+
container.appendChild(cueRegistry.cueElement);
|
|
217
286
|
}
|
|
218
287
|
}
|
|
288
|
+
}
|
|
219
289
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
290
|
+
/**
|
|
291
|
+
* Display the current captions.
|
|
292
|
+
* @param {boolean=} forceUpdate
|
|
293
|
+
* @private
|
|
294
|
+
*/
|
|
295
|
+
updateCaptions_(forceUpdate = false) {
|
|
296
|
+
if (!this.textContainer_) {
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const currentTime = this.video_.currentTime;
|
|
301
|
+
if (!this.isTextVisible_ || forceUpdate) {
|
|
302
|
+
if (this.currentCuesMap_.size > 0) {
|
|
303
|
+
// Clear away any existing cues.
|
|
304
|
+
shaka.util.Dom.removeAllChildren(this.textContainer_);
|
|
305
|
+
this.currentCuesMap_.clear();
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
if (this.isTextVisible_) {
|
|
309
|
+
// Log currently attached cue elements for verification, later.
|
|
310
|
+
const previousCuesMap = new Map();
|
|
311
|
+
for (const cue of this.currentCuesMap_.keys()) {
|
|
312
|
+
previousCuesMap.set(cue, this.currentCuesMap_.get(cue));
|
|
230
313
|
}
|
|
231
|
-
});
|
|
232
314
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
this.textContainer_,
|
|
236
|
-
|
|
315
|
+
// Update the cues.
|
|
316
|
+
this.updateCuesRecursive_(
|
|
317
|
+
this.cues_, this.textContainer_, currentTime, /* parents= */ []);
|
|
318
|
+
|
|
319
|
+
if (goog.DEBUG) {
|
|
320
|
+
// Previously, we had an issue (#2076) where cues sometimes were not
|
|
321
|
+
// properly removed from the DOM. It is not clear if this issue still
|
|
322
|
+
// happens, so the previous fix for it has been changed to an assert.
|
|
323
|
+
for (const cue of previousCuesMap.keys()) {
|
|
324
|
+
if (!this.currentCuesMap_.has(cue)) {
|
|
325
|
+
// TODO: If the problem does not appear again, then we should remove
|
|
326
|
+
// this assert (and the previousCuesMap code) in Shaka v4.
|
|
327
|
+
const cueElement = previousCuesMap.get(cue).cueElement;
|
|
328
|
+
goog.asserts.assert(
|
|
329
|
+
!cueElement.parentNode, 'Cue was not properly removed!');
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
237
333
|
}
|
|
238
334
|
}
|
|
239
335
|
|
|
240
336
|
/**
|
|
241
|
-
*
|
|
337
|
+
* Creates the object for a cue.
|
|
242
338
|
*
|
|
243
|
-
* @param {Element} container
|
|
244
339
|
* @param {!shaka.extern.Cue} cue
|
|
245
|
-
* @param {
|
|
246
|
-
* @return {!Element} the created captions element
|
|
340
|
+
* @param {!Array.<!shaka.extern.Cue>} parents
|
|
247
341
|
* @private
|
|
248
342
|
*/
|
|
249
|
-
|
|
343
|
+
createCue_(cue, parents) {
|
|
344
|
+
const isNested = parents.length > 1;
|
|
250
345
|
let type = isNested ? 'span' : 'div';
|
|
251
346
|
if (cue.lineBreak || cue.spacer) {
|
|
252
347
|
if (cue.spacer) {
|
|
@@ -257,31 +352,42 @@ shaka.text.UITextDisplayer = class {
|
|
|
257
352
|
type = 'br';
|
|
258
353
|
}
|
|
259
354
|
|
|
355
|
+
const needWrapper = !isNested && cue.nestedCues.length > 0;
|
|
356
|
+
|
|
260
357
|
// Nested cues are inline elements. Top-level cues are block elements.
|
|
261
358
|
const cueElement = shaka.util.Dom.createHTMLElement(type);
|
|
262
|
-
|
|
263
359
|
if (type != 'br') {
|
|
264
|
-
this.setCaptionStyles_(cueElement, cue,
|
|
360
|
+
this.setCaptionStyles_(cueElement, cue, parents, needWrapper);
|
|
361
|
+
}
|
|
265
362
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
363
|
+
let wrapper = cueElement;
|
|
364
|
+
if (needWrapper) {
|
|
365
|
+
// Create a wrapper element which will serve to contain all children into
|
|
366
|
+
// a single item. This ensures that nested span elements appear
|
|
367
|
+
// horizontally and br elements occupy no vertical space.
|
|
368
|
+
wrapper = shaka.util.Dom.createHTMLElement('span');
|
|
369
|
+
wrapper.classList.add('shaka-text-wrapper');
|
|
370
|
+
wrapper.style.backgroundColor = cue.backgroundColor;
|
|
371
|
+
cueElement.appendChild(wrapper);
|
|
269
372
|
}
|
|
270
373
|
|
|
271
|
-
|
|
272
|
-
return cueElement;
|
|
374
|
+
this.currentCuesMap_.set(cue, {cueElement, wrapper});
|
|
273
375
|
}
|
|
274
376
|
|
|
275
377
|
/**
|
|
276
378
|
* @param {!HTMLElement} cueElement
|
|
277
379
|
* @param {!shaka.extern.Cue} cue
|
|
278
|
-
* @param {
|
|
380
|
+
* @param {!Array.<!shaka.extern.Cue>} parents
|
|
381
|
+
* @param {boolean} hasWrapper
|
|
279
382
|
* @private
|
|
280
383
|
*/
|
|
281
|
-
setCaptionStyles_(cueElement, cue,
|
|
384
|
+
setCaptionStyles_(cueElement, cue, parents, hasWrapper) {
|
|
282
385
|
const Cue = shaka.text.Cue;
|
|
283
|
-
|
|
386
|
+
const inherit =
|
|
387
|
+
(cb) => shaka.text.UITextDisplayer.inheritProperty_(parents, cb);
|
|
388
|
+
const style = cueElement.style;
|
|
284
389
|
const isLeaf = cue.nestedCues.length == 0;
|
|
390
|
+
const isNested = parents.length > 1;
|
|
285
391
|
|
|
286
392
|
// TODO: wrapLine is not yet supported. Lines always wrap.
|
|
287
393
|
|
|
@@ -300,16 +406,6 @@ shaka.text.UITextDisplayer = class {
|
|
|
300
406
|
const nonBreakingSpace = '\xa0';
|
|
301
407
|
return nonBreakingSpace.repeat(match.length);
|
|
302
408
|
});
|
|
303
|
-
if (isNested) {
|
|
304
|
-
cueElement.textContent = text;
|
|
305
|
-
} else if (text.length) {
|
|
306
|
-
// If a top-level cue has text, move to a <span> so the background is
|
|
307
|
-
// styled correctly.
|
|
308
|
-
const span = shaka.util.Dom.createHTMLElement('span');
|
|
309
|
-
span.textContent = text;
|
|
310
|
-
cueElement.appendChild(span);
|
|
311
|
-
style = span.style;
|
|
312
|
-
}
|
|
313
409
|
|
|
314
410
|
style.backgroundColor = cue.backgroundColor;
|
|
315
411
|
style.border = cue.border;
|
|
@@ -318,32 +414,62 @@ shaka.text.UITextDisplayer = class {
|
|
|
318
414
|
style.opacity = cue.opacity;
|
|
319
415
|
style.paddingLeft = shaka.text.UITextDisplayer.convertLengthValue_(
|
|
320
416
|
cue.linePadding, cue, this.videoContainer_);
|
|
321
|
-
style.paddingRight =
|
|
322
|
-
|
|
417
|
+
style.paddingRight =
|
|
418
|
+
shaka.text.UITextDisplayer.convertLengthValue_(
|
|
419
|
+
cue.linePadding, cue, this.videoContainer_);
|
|
323
420
|
|
|
324
421
|
if (cue.backgroundImage) {
|
|
325
422
|
style.backgroundImage = 'url(\'' + cue.backgroundImage + '\')';
|
|
326
423
|
style.backgroundRepeat = 'no-repeat';
|
|
327
424
|
style.backgroundSize = 'contain';
|
|
328
425
|
style.backgroundPosition = 'center';
|
|
426
|
+
} else {
|
|
427
|
+
// If we have both text and nested cues, then style everything; otherwise
|
|
428
|
+
// place the text in its own <span> so the background doesn't fill the
|
|
429
|
+
// whole region.
|
|
430
|
+
let elem;
|
|
431
|
+
if (cue.nestedCues.length) {
|
|
432
|
+
elem = cueElement;
|
|
433
|
+
} else {
|
|
434
|
+
elem = shaka.util.Dom.createHTMLElement('span');
|
|
435
|
+
cueElement.appendChild(elem);
|
|
436
|
+
}
|
|
329
437
|
|
|
330
|
-
if (cue.
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
438
|
+
if (cue.border) {
|
|
439
|
+
elem.style.border = cue.border;
|
|
440
|
+
}
|
|
441
|
+
if (!hasWrapper) {
|
|
442
|
+
const bgColor = inherit((c) => c.backgroundColor);
|
|
443
|
+
if (bgColor) {
|
|
444
|
+
elem.style.backgroundColor = bgColor;
|
|
445
|
+
} else if (text) {
|
|
446
|
+
// If there is no background, default to a semi-transparent black.
|
|
447
|
+
// Only do this for the text itself.
|
|
448
|
+
elem.style.backgroundColor = 'rgba(0, 0, 0, 0.8)';
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
if (text) {
|
|
452
|
+
elem.textContent = text;
|
|
335
453
|
}
|
|
336
454
|
}
|
|
337
455
|
|
|
338
456
|
// The displayAlign attribute specifies the vertical alignment of the
|
|
339
457
|
// captions inside the text container. Before means at the top of the
|
|
340
458
|
// text container, and after means at the bottom.
|
|
341
|
-
if (
|
|
342
|
-
style.
|
|
343
|
-
} else if (cue.displayAlign == Cue.displayAlign.CENTER) {
|
|
344
|
-
style.verticalAlign = 'middle';
|
|
459
|
+
if (isNested && !parents[parents.length - 1].isContainer) {
|
|
460
|
+
style.display = 'inline';
|
|
345
461
|
} else {
|
|
346
|
-
style.
|
|
462
|
+
style.display = 'flex';
|
|
463
|
+
style.flexDirection = 'column';
|
|
464
|
+
style.alignItems = 'center';
|
|
465
|
+
|
|
466
|
+
if (cue.displayAlign == Cue.displayAlign.BEFORE) {
|
|
467
|
+
style.justifyContent = 'flex-start';
|
|
468
|
+
} else if (cue.displayAlign == Cue.displayAlign.CENTER) {
|
|
469
|
+
style.justifyContent = 'center';
|
|
470
|
+
} else {
|
|
471
|
+
style.justifyContent = 'flex-end';
|
|
472
|
+
}
|
|
347
473
|
}
|
|
348
474
|
|
|
349
475
|
if (!isLeaf) {
|
|
@@ -376,20 +502,28 @@ shaka.text.UITextDisplayer = class {
|
|
|
376
502
|
// TODO: Implement lineAlignment of 'CENTER'.
|
|
377
503
|
if (cue.line) {
|
|
378
504
|
if (cue.lineInterpretation == Cue.lineInterpretation.PERCENTAGE) {
|
|
505
|
+
// When setting absolute positioning, you need to set x/y/width/height
|
|
506
|
+
// so the element is positioned correctly. Set these as default and
|
|
507
|
+
// other settings will override them.
|
|
379
508
|
style.position = 'absolute';
|
|
509
|
+
style.left = '0';
|
|
510
|
+
style.top = '0';
|
|
380
511
|
if (cue.writingMode == Cue.writingMode.HORIZONTAL_TOP_TO_BOTTOM) {
|
|
512
|
+
style.width = '100%';
|
|
381
513
|
if (cue.lineAlign == Cue.lineAlign.START) {
|
|
382
514
|
style.top = cue.line + '%';
|
|
383
515
|
} else if (cue.lineAlign == Cue.lineAlign.END) {
|
|
384
516
|
style.bottom = cue.line + '%';
|
|
385
517
|
}
|
|
386
518
|
} else if (cue.writingMode == Cue.writingMode.VERTICAL_LEFT_TO_RIGHT) {
|
|
519
|
+
style.height = '100%';
|
|
387
520
|
if (cue.lineAlign == Cue.lineAlign.START) {
|
|
388
521
|
style.left = cue.line + '%';
|
|
389
522
|
} else if (cue.lineAlign == Cue.lineAlign.END) {
|
|
390
523
|
style.right = cue.line + '%';
|
|
391
524
|
}
|
|
392
525
|
} else {
|
|
526
|
+
style.height = '100%';
|
|
393
527
|
if (cue.lineAlign == Cue.lineAlign.START) {
|
|
394
528
|
style.right = cue.line + '%';
|
|
395
529
|
} else if (cue.lineAlign == Cue.lineAlign.END) {
|
|
@@ -397,12 +531,7 @@ shaka.text.UITextDisplayer = class {
|
|
|
397
531
|
}
|
|
398
532
|
}
|
|
399
533
|
}
|
|
400
|
-
} else if (cue.region && cue.region.id
|
|
401
|
-
((!isNested && !isLeaf) || (cue.backgroundImage))) {
|
|
402
|
-
// In text-base cues, regions are only applied to block container
|
|
403
|
-
// (!isNested && !isLeaf).
|
|
404
|
-
// In bitmap-based cues, region settings are used to specify the size and
|
|
405
|
-
// position of the backgroundImage.
|
|
534
|
+
} else if (cue.region && cue.region.id) {
|
|
406
535
|
const percentageUnit = shaka.text.CueRegion.units.PERCENTAGE;
|
|
407
536
|
const heightUnit = cue.region.heightUnits == percentageUnit ? '%' : 'px';
|
|
408
537
|
const widthUnit = cue.region.widthUnits == percentageUnit ? '%' : 'px';
|
|
@@ -535,4 +664,25 @@ shaka.text.UITextDisplayer = class {
|
|
|
535
664
|
|
|
536
665
|
return (containerHeight * value / cue.cellResolution.rows) + 'px';
|
|
537
666
|
}
|
|
667
|
+
|
|
668
|
+
/**
|
|
669
|
+
* Inherits a property from the parent Cue elements. If the value is falsy,
|
|
670
|
+
* it is assumed to be inherited from the parent. This returns null if the
|
|
671
|
+
* value isn't found.
|
|
672
|
+
*
|
|
673
|
+
* @param {!Array.<!shaka.extern.Cue>} parents
|
|
674
|
+
* @param {function(!shaka.extern.Cue):?T} cb
|
|
675
|
+
* @return {?T}
|
|
676
|
+
* @template T
|
|
677
|
+
* @private
|
|
678
|
+
*/
|
|
679
|
+
static inheritProperty_(parents, cb) {
|
|
680
|
+
for (let i = parents.length - 1; i >= 0; i--) {
|
|
681
|
+
const val = cb(parents[i]);
|
|
682
|
+
if (val || val === 0) {
|
|
683
|
+
return val;
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
return null;
|
|
687
|
+
}
|
|
538
688
|
};
|
|
@@ -122,7 +122,7 @@ shaka.text.VttTextParser = class {
|
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
/**
|
|
125
|
-
* Add
|
|
125
|
+
* Add default color
|
|
126
126
|
*
|
|
127
127
|
* @param {!Map.<string, shaka.text.Cue>} styles
|
|
128
128
|
* @private
|
|
@@ -436,7 +436,9 @@ shaka.text.VttTextParser = class {
|
|
|
436
436
|
}
|
|
437
437
|
const tagEnd = payload.substring(i + 1, end);
|
|
438
438
|
const tagStart = names.pop();
|
|
439
|
-
if (
|
|
439
|
+
if (!tagEnd || !tagStart) {
|
|
440
|
+
return payload;
|
|
441
|
+
} else if (tagStart === tagEnd) {
|
|
440
442
|
newPayload += '/' + tagEnd + '>';
|
|
441
443
|
i += tagEnd.length + 1;
|
|
442
444
|
} else {
|
package/lib/util/fake_event.js
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
goog.provide('shaka.util.FakeEvent');
|
|
8
8
|
|
|
9
|
+
goog.require('goog.asserts');
|
|
10
|
+
|
|
9
11
|
|
|
10
12
|
/**
|
|
11
13
|
* @summary Create an Event work-alike object based on the provided dictionary.
|
|
@@ -16,18 +18,62 @@ goog.provide('shaka.util.FakeEvent');
|
|
|
16
18
|
*/
|
|
17
19
|
shaka.util.FakeEvent = class {
|
|
18
20
|
/**
|
|
19
|
-
* @param {
|
|
20
|
-
* @
|
|
21
|
+
* @param {!Event} event
|
|
22
|
+
* @return {!shaka.util.FakeEvent}
|
|
21
23
|
*/
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
for (const key in
|
|
25
|
-
Object.defineProperty(
|
|
26
|
-
value:
|
|
24
|
+
static fromRealEvent(event) {
|
|
25
|
+
const fakeEvent = new shaka.util.FakeEvent(event.type);
|
|
26
|
+
for (const key in event) {
|
|
27
|
+
Object.defineProperty(fakeEvent, key, {
|
|
28
|
+
value: event[key],
|
|
27
29
|
writable: true,
|
|
28
30
|
enumerable: true,
|
|
29
31
|
});
|
|
30
32
|
}
|
|
33
|
+
return fakeEvent;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Allows us to tell the compiler that the dictionary "map" is actually a
|
|
38
|
+
* generic object, for backwards compatibility.
|
|
39
|
+
* @param {!Map.<string, Object>} dict
|
|
40
|
+
* @return {!Object}
|
|
41
|
+
* @suppress {invalidCasts}
|
|
42
|
+
* @private
|
|
43
|
+
*/
|
|
44
|
+
static recastDictAsObject_(dict) {
|
|
45
|
+
goog.asserts.assert(!(dict instanceof Map), 'dict should not be a map');
|
|
46
|
+
return /** @type {!Object} */ (dict);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @param {string} type
|
|
51
|
+
* @param {Map.<string, Object>=} dict
|
|
52
|
+
*/
|
|
53
|
+
constructor(type, dict) {
|
|
54
|
+
if (dict) {
|
|
55
|
+
if (dict instanceof Map) {
|
|
56
|
+
// Take properties from dict if present.
|
|
57
|
+
for (const key of dict.keys()) {
|
|
58
|
+
Object.defineProperty(this, key, {
|
|
59
|
+
value: dict.get(key),
|
|
60
|
+
writable: true,
|
|
61
|
+
enumerable: true,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
} else {
|
|
65
|
+
// For backwards compatibility with external apps that may make use of
|
|
66
|
+
// this public constructor, this should still accept generic objects.
|
|
67
|
+
const obj = shaka.util.FakeEvent.recastDictAsObject_(dict);
|
|
68
|
+
for (const key in obj) {
|
|
69
|
+
Object.defineProperty(this, key, {
|
|
70
|
+
value: obj[key],
|
|
71
|
+
writable: true,
|
|
72
|
+
enumerable: true,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
31
77
|
|
|
32
78
|
// The properties below cannot be set by the dict. They are all provided
|
|
33
79
|
// for compatibility with native events.
|
|
@@ -176,7 +176,7 @@ shaka.util.Mp4BoxParsers = class {
|
|
|
176
176
|
if (version == 1) {
|
|
177
177
|
reader.skip(8); // Skip "creation_time"
|
|
178
178
|
reader.skip(8); // Skip "modification_time"
|
|
179
|
-
trackId = reader.
|
|
179
|
+
trackId = reader.readUint32();
|
|
180
180
|
} else {
|
|
181
181
|
reader.skip(4); // Skip "creation_time"
|
|
182
182
|
reader.skip(4); // Skip "modification_time"
|