shaka-player 3.1.0 → 3.1.4
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 +8 -2
- package/CHANGELOG.md +209 -0
- package/CONTRIBUTORS +10 -3
- package/README.md +3 -0
- package/build/apps.py +2 -2
- package/build/check.py +2 -2
- package/build/conformance.textproto +0 -9
- 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/asset.js +11 -0
- package/demo/common/assets.js +1 -1
- package/demo/common/message_ids.js +5 -0
- package/demo/config.js +2 -1
- package/demo/custom.js +84 -3
- package/demo/demo.less +13 -1
- package/demo/demo_utils.js +1 -0
- package/demo/input.js +31 -12
- package/demo/input_container.js +24 -1
- package/demo/locales/en.json +6 -1
- package/demo/locales/source.json +20 -0
- package/demo/main.js +5 -1
- package/dist/controls.css +2 -2
- package/dist/controls.css.map +1 -1
- package/dist/demo.compiled.debug.js +99 -91
- package/dist/demo.compiled.debug.map +3 -3
- package/dist/demo.compiled.js +99 -91
- package/dist/demo.compiled.map +3 -3
- package/dist/demo.css +1 -1
- package/dist/demo.css.map +1 -1
- package/dist/deps.js +9 -9
- package/dist/locales.js +36 -34
- package/dist/receiver.compiled.debug.js +74 -70
- package/dist/receiver.compiled.debug.map +3 -3
- package/dist/receiver.compiled.js +74 -70
- package/dist/receiver.compiled.map +3 -3
- package/dist/shaka-player.compiled.d.ts +165 -9
- package/dist/shaka-player.compiled.debug.d.ts +165 -9
- package/dist/shaka-player.compiled.debug.externs.js +179 -11
- package/dist/shaka-player.compiled.debug.js +367 -339
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +179 -11
- package/dist/shaka-player.compiled.js +818 -807
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +165 -9
- package/dist/shaka-player.ui.debug.d.ts +165 -9
- package/dist/shaka-player.ui.debug.externs.js +181 -13
- package/dist/shaka-player.ui.debug.js +459 -429
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +181 -13
- package/dist/shaka-player.ui.js +973 -961
- package/dist/shaka-player.ui.map +4 -4
- package/docs/tutorials/fairplay.md +1 -1
- package/docs/tutorials/faq.md +25 -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/media_capabilities.js +16 -0
- package/externs/shaka/ads.js +2 -0
- package/externs/shaka/net.js +2 -2
- package/externs/shaka/player.js +6 -0
- package/karma.conf.js +1 -0
- package/lib/abr/simple_abr_manager.js +1 -1
- 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 +107 -4
- package/lib/dash/segment_list.js +1 -1
- package/lib/dash/segment_template.js +45 -15
- package/lib/dependencies/all.js +15 -15
- package/lib/hls/hls_parser.js +46 -18
- package/lib/media/closed_caption_parser.js +3 -1
- package/lib/media/content_workarounds.js +18 -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_index.js +38 -12
- package/lib/media/segment_reference.js +8 -0
- package/lib/media/streaming_engine.js +30 -21
- package/lib/net/http_fetch_plugin.js +2 -2
- package/lib/net/networking_engine.js +3 -2
- package/lib/offline/storage.js +4 -0
- package/lib/player.js +113 -56
- 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 +62 -23
- 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 +11 -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 +52 -30
- package/lib/text/ui_text_displayer.js +255 -82
- package/lib/text/vtt_text_parser.js +4 -2
- package/lib/util/error.js +1 -4
- package/lib/util/fake_event.js +53 -7
- package/lib/util/mime_utils.js +33 -0
- package/lib/util/mp4_box_parsers.js +1 -1
- package/lib/util/mp4_parser.js +14 -0
- package/lib/util/networking.js +1 -1
- package/lib/util/periods.js +132 -37
- package/lib/util/platform.js +13 -1
- package/lib/util/stream_utils.js +46 -7
- package/package.json +3 -3
- 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 +187 -1
- package/test/dash/dash_parser_segment_template_unit.js +3 -3
- package/test/demo/demo_unit.js +0 -1
- package/test/hls/hls_parser_unit.js +45 -1
- package/test/media/closed_caption_parser_unit.js +20 -0
- package/test/media/drm_engine_unit.js +59 -3
- package/test/media/playhead_unit.js +27 -1
- package/test/media/presentation_timeline_unit.js +19 -0
- package/test/media/segment_index_unit.js +76 -3
- package/test/media/streaming_engine_unit.js +3 -3
- package/test/player_integration.js +38 -0
- package/test/player_unit.js +125 -6
- 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/waiter.js +36 -2
- package/test/text/ttml_text_parser_unit.js +55 -0
- 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/ui/ui_unit.js +4 -0
- 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 +56 -5
- 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 +9 -7
- 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/ui.js +1 -1
- package/docs/tutorials/upgrade-v2-4.md +0 -481
|
@@ -12,6 +12,7 @@ goog.require('shaka.Deprecate');
|
|
|
12
12
|
goog.require('shaka.text.Cue');
|
|
13
13
|
goog.require('shaka.text.CueRegion');
|
|
14
14
|
goog.require('shaka.util.Dom');
|
|
15
|
+
goog.require('shaka.util.EventManager');
|
|
15
16
|
goog.require('shaka.util.Timer');
|
|
16
17
|
|
|
17
18
|
|
|
@@ -72,8 +73,31 @@ shaka.text.UITextDisplayer = class {
|
|
|
72
73
|
this.updateCaptions_();
|
|
73
74
|
}).tickEvery(updatePeriod);
|
|
74
75
|
|
|
75
|
-
/**
|
|
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
|
+
*/
|
|
76
84
|
this.currentCuesMap_ = new Map();
|
|
85
|
+
|
|
86
|
+
/** @private {shaka.util.EventManager} */
|
|
87
|
+
this.eventManager_ = new shaka.util.EventManager();
|
|
88
|
+
|
|
89
|
+
this.eventManager_.listen(document, 'fullscreenchange', () => {
|
|
90
|
+
this.updateCaptions_(/* forceUpdate= */ true);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
/** @private {ResizeObserver} */
|
|
94
|
+
this.resizeObserver_ = null;
|
|
95
|
+
if ('ResizeObserver' in window) {
|
|
96
|
+
this.resizeObserver_ = new ResizeObserver(() => {
|
|
97
|
+
this.updateCaptions_(/* forceUpdate= */ true);
|
|
98
|
+
});
|
|
99
|
+
this.resizeObserver_.observe(this.textContainer_);
|
|
100
|
+
}
|
|
77
101
|
}
|
|
78
102
|
|
|
79
103
|
|
|
@@ -118,6 +142,17 @@ shaka.text.UITextDisplayer = class {
|
|
|
118
142
|
}
|
|
119
143
|
|
|
120
144
|
this.currentCuesMap_.clear();
|
|
145
|
+
|
|
146
|
+
// Tear-down the event manager to ensure messages stop moving around.
|
|
147
|
+
if (this.eventManager_) {
|
|
148
|
+
this.eventManager_.release();
|
|
149
|
+
this.eventManager_ = null;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (this.resizeObserver_) {
|
|
153
|
+
this.resizeObserver_.disconnect();
|
|
154
|
+
this.resizeObserver_ = null;
|
|
155
|
+
}
|
|
121
156
|
}
|
|
122
157
|
|
|
123
158
|
|
|
@@ -132,9 +167,14 @@ shaka.text.UITextDisplayer = class {
|
|
|
132
167
|
}
|
|
133
168
|
|
|
134
169
|
// Remove the cues out of the time range.
|
|
170
|
+
const oldNumCues = this.cues_.length;
|
|
135
171
|
this.cues_ = this.cues_.filter(
|
|
136
172
|
(cue) => cue.startTime < start || cue.endTime >= end);
|
|
137
|
-
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);
|
|
138
178
|
|
|
139
179
|
return true;
|
|
140
180
|
}
|
|
@@ -157,67 +197,151 @@ shaka.text.UITextDisplayer = class {
|
|
|
157
197
|
}
|
|
158
198
|
|
|
159
199
|
/**
|
|
160
|
-
*
|
|
200
|
+
* @param {!Array.<!shaka.extern.Cue>} cues
|
|
201
|
+
* @param {!HTMLElement} container
|
|
202
|
+
* @param {number} currentTime
|
|
203
|
+
* @param {!Array.<!shaka.extern.Cue>} parents
|
|
161
204
|
* @private
|
|
162
205
|
*/
|
|
163
|
-
|
|
164
|
-
|
|
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
|
+
}
|
|
165
246
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
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
|
+
}
|
|
171
257
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
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);
|
|
179
266
|
}
|
|
180
|
-
}
|
|
181
267
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
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);
|
|
188
286
|
}
|
|
189
287
|
}
|
|
288
|
+
}
|
|
190
289
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
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));
|
|
201
313
|
}
|
|
202
|
-
});
|
|
203
314
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
this.textContainer_,
|
|
207
|
-
|
|
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
|
+
}
|
|
208
333
|
}
|
|
209
334
|
}
|
|
210
335
|
|
|
211
336
|
/**
|
|
212
|
-
*
|
|
337
|
+
* Creates the object for a cue.
|
|
213
338
|
*
|
|
214
|
-
* @param {Element} container
|
|
215
339
|
* @param {!shaka.extern.Cue} cue
|
|
216
|
-
* @param {
|
|
217
|
-
* @return {!Element} the created captions element
|
|
340
|
+
* @param {!Array.<!shaka.extern.Cue>} parents
|
|
218
341
|
* @private
|
|
219
342
|
*/
|
|
220
|
-
|
|
343
|
+
createCue_(cue, parents) {
|
|
344
|
+
const isNested = parents.length > 1;
|
|
221
345
|
let type = isNested ? 'span' : 'div';
|
|
222
346
|
if (cue.lineBreak || cue.spacer) {
|
|
223
347
|
if (cue.spacer) {
|
|
@@ -230,29 +354,36 @@ shaka.text.UITextDisplayer = class {
|
|
|
230
354
|
|
|
231
355
|
// Nested cues are inline elements. Top-level cues are block elements.
|
|
232
356
|
const cueElement = shaka.util.Dom.createHTMLElement(type);
|
|
233
|
-
|
|
234
357
|
if (type != 'br') {
|
|
235
|
-
this.setCaptionStyles_(cueElement, cue,
|
|
358
|
+
this.setCaptionStyles_(cueElement, cue, parents);
|
|
359
|
+
}
|
|
236
360
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
361
|
+
let wrapper = cueElement;
|
|
362
|
+
if (!isNested && cue.nestedCues.length) {
|
|
363
|
+
// Create a wrapper element which will serve to contain all children into
|
|
364
|
+
// a single item. This ensures that nested span elements appear
|
|
365
|
+
// horizontally and br elements occupy no vertical space.
|
|
366
|
+
wrapper = shaka.util.Dom.createHTMLElement('span');
|
|
367
|
+
wrapper.classList.add('shaka-text-wrapper');
|
|
368
|
+
cueElement.appendChild(wrapper);
|
|
240
369
|
}
|
|
241
370
|
|
|
242
|
-
|
|
243
|
-
return cueElement;
|
|
371
|
+
this.currentCuesMap_.set(cue, {cueElement, wrapper});
|
|
244
372
|
}
|
|
245
373
|
|
|
246
374
|
/**
|
|
247
375
|
* @param {!HTMLElement} cueElement
|
|
248
376
|
* @param {!shaka.extern.Cue} cue
|
|
249
|
-
* @param {
|
|
377
|
+
* @param {!Array.<!shaka.extern.Cue>} parents
|
|
250
378
|
* @private
|
|
251
379
|
*/
|
|
252
|
-
setCaptionStyles_(cueElement, cue,
|
|
380
|
+
setCaptionStyles_(cueElement, cue, parents) {
|
|
253
381
|
const Cue = shaka.text.Cue;
|
|
254
|
-
|
|
382
|
+
const inherit =
|
|
383
|
+
(cb) => shaka.text.UITextDisplayer.inheritProperty_(parents, cb);
|
|
384
|
+
const style = cueElement.style;
|
|
255
385
|
const isLeaf = cue.nestedCues.length == 0;
|
|
386
|
+
const isNested = parents.length > 1;
|
|
256
387
|
|
|
257
388
|
// TODO: wrapLine is not yet supported. Lines always wrap.
|
|
258
389
|
|
|
@@ -271,16 +402,6 @@ shaka.text.UITextDisplayer = class {
|
|
|
271
402
|
const nonBreakingSpace = '\xa0';
|
|
272
403
|
return nonBreakingSpace.repeat(match.length);
|
|
273
404
|
});
|
|
274
|
-
if (isNested) {
|
|
275
|
-
cueElement.textContent = text;
|
|
276
|
-
} else if (text.length) {
|
|
277
|
-
// If a top-level cue has text, move to a <span> so the background is
|
|
278
|
-
// styled correctly.
|
|
279
|
-
const span = shaka.util.Dom.createHTMLElement('span');
|
|
280
|
-
span.textContent = text;
|
|
281
|
-
cueElement.appendChild(span);
|
|
282
|
-
style = span.style;
|
|
283
|
-
}
|
|
284
405
|
|
|
285
406
|
style.backgroundColor = cue.backgroundColor;
|
|
286
407
|
style.border = cue.border;
|
|
@@ -289,32 +410,60 @@ shaka.text.UITextDisplayer = class {
|
|
|
289
410
|
style.opacity = cue.opacity;
|
|
290
411
|
style.paddingLeft = shaka.text.UITextDisplayer.convertLengthValue_(
|
|
291
412
|
cue.linePadding, cue, this.videoContainer_);
|
|
292
|
-
style.paddingRight =
|
|
293
|
-
|
|
413
|
+
style.paddingRight =
|
|
414
|
+
shaka.text.UITextDisplayer.convertLengthValue_(
|
|
415
|
+
cue.linePadding, cue, this.videoContainer_);
|
|
294
416
|
|
|
295
417
|
if (cue.backgroundImage) {
|
|
296
418
|
style.backgroundImage = 'url(\'' + cue.backgroundImage + '\')';
|
|
297
419
|
style.backgroundRepeat = 'no-repeat';
|
|
298
420
|
style.backgroundSize = 'contain';
|
|
299
421
|
style.backgroundPosition = 'center';
|
|
422
|
+
} else {
|
|
423
|
+
// If we have both text and nested cues, then style everything; otherwise
|
|
424
|
+
// place the text in its own <span> so the background doesn't fill the
|
|
425
|
+
// whole region.
|
|
426
|
+
let elem;
|
|
427
|
+
if (cue.nestedCues.length) {
|
|
428
|
+
elem = cueElement;
|
|
429
|
+
} else {
|
|
430
|
+
elem = shaka.util.Dom.createHTMLElement('span');
|
|
431
|
+
cueElement.appendChild(elem);
|
|
432
|
+
}
|
|
300
433
|
|
|
301
|
-
if (cue.
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
434
|
+
if (cue.border) {
|
|
435
|
+
elem.style.border = cue.border;
|
|
436
|
+
}
|
|
437
|
+
const bgColor = inherit((c) => c.backgroundColor);
|
|
438
|
+
if (bgColor) {
|
|
439
|
+
elem.style.backgroundColor = bgColor;
|
|
440
|
+
} else if (text) {
|
|
441
|
+
// If there is no background, default to a semi-transparent black.
|
|
442
|
+
// Only do this for the text itself.
|
|
443
|
+
elem.style.backgroundColor = 'rgba(0, 0, 0, 0.8)';
|
|
444
|
+
}
|
|
445
|
+
if (text) {
|
|
446
|
+
elem.textContent = text;
|
|
306
447
|
}
|
|
307
448
|
}
|
|
308
449
|
|
|
309
450
|
// The displayAlign attribute specifies the vertical alignment of the
|
|
310
451
|
// captions inside the text container. Before means at the top of the
|
|
311
452
|
// text container, and after means at the bottom.
|
|
312
|
-
if (
|
|
313
|
-
style.
|
|
314
|
-
} else if (cue.displayAlign == Cue.displayAlign.CENTER) {
|
|
315
|
-
style.verticalAlign = 'middle';
|
|
453
|
+
if (isNested) {
|
|
454
|
+
style.display = 'inline';
|
|
316
455
|
} else {
|
|
317
|
-
style.
|
|
456
|
+
style.display = 'flex';
|
|
457
|
+
style.flexDirection = 'column';
|
|
458
|
+
style.alignItems = 'center';
|
|
459
|
+
|
|
460
|
+
if (cue.displayAlign == Cue.displayAlign.BEFORE) {
|
|
461
|
+
style.justifyContent = 'flex-start';
|
|
462
|
+
} else if (cue.displayAlign == Cue.displayAlign.CENTER) {
|
|
463
|
+
style.justifyContent = 'center';
|
|
464
|
+
} else {
|
|
465
|
+
style.justifyContent = 'flex-end';
|
|
466
|
+
}
|
|
318
467
|
}
|
|
319
468
|
|
|
320
469
|
if (!isLeaf) {
|
|
@@ -347,20 +496,28 @@ shaka.text.UITextDisplayer = class {
|
|
|
347
496
|
// TODO: Implement lineAlignment of 'CENTER'.
|
|
348
497
|
if (cue.line) {
|
|
349
498
|
if (cue.lineInterpretation == Cue.lineInterpretation.PERCENTAGE) {
|
|
499
|
+
// When setting absolute positioning, you need to set x/y/width/height
|
|
500
|
+
// so the element is positioned correctly. Set these as default and
|
|
501
|
+
// other settings will override them.
|
|
350
502
|
style.position = 'absolute';
|
|
503
|
+
style.left = '0';
|
|
504
|
+
style.top = '0';
|
|
351
505
|
if (cue.writingMode == Cue.writingMode.HORIZONTAL_TOP_TO_BOTTOM) {
|
|
506
|
+
style.width = '100%';
|
|
352
507
|
if (cue.lineAlign == Cue.lineAlign.START) {
|
|
353
508
|
style.top = cue.line + '%';
|
|
354
509
|
} else if (cue.lineAlign == Cue.lineAlign.END) {
|
|
355
510
|
style.bottom = cue.line + '%';
|
|
356
511
|
}
|
|
357
512
|
} else if (cue.writingMode == Cue.writingMode.VERTICAL_LEFT_TO_RIGHT) {
|
|
513
|
+
style.height = '100%';
|
|
358
514
|
if (cue.lineAlign == Cue.lineAlign.START) {
|
|
359
515
|
style.left = cue.line + '%';
|
|
360
516
|
} else if (cue.lineAlign == Cue.lineAlign.END) {
|
|
361
517
|
style.right = cue.line + '%';
|
|
362
518
|
}
|
|
363
519
|
} else {
|
|
520
|
+
style.height = '100%';
|
|
364
521
|
if (cue.lineAlign == Cue.lineAlign.START) {
|
|
365
522
|
style.right = cue.line + '%';
|
|
366
523
|
} else if (cue.lineAlign == Cue.lineAlign.END) {
|
|
@@ -368,12 +525,7 @@ shaka.text.UITextDisplayer = class {
|
|
|
368
525
|
}
|
|
369
526
|
}
|
|
370
527
|
}
|
|
371
|
-
} else if (cue.region && cue.region.id
|
|
372
|
-
((!isNested && !isLeaf) || (cue.backgroundImage))) {
|
|
373
|
-
// In text-base cues, regions are only applied to block container
|
|
374
|
-
// (!isNested && !isLeaf).
|
|
375
|
-
// In bitmap-based cues, region settings are used to specify the size and
|
|
376
|
-
// position of the backgroundImage.
|
|
528
|
+
} else if (cue.region && cue.region.id) {
|
|
377
529
|
const percentageUnit = shaka.text.CueRegion.units.PERCENTAGE;
|
|
378
530
|
const heightUnit = cue.region.heightUnits == percentageUnit ? '%' : 'px';
|
|
379
531
|
const widthUnit = cue.region.widthUnits == percentageUnit ? '%' : 'px';
|
|
@@ -506,4 +658,25 @@ shaka.text.UITextDisplayer = class {
|
|
|
506
658
|
|
|
507
659
|
return (containerHeight * value / cue.cellResolution.rows) + 'px';
|
|
508
660
|
}
|
|
661
|
+
|
|
662
|
+
/**
|
|
663
|
+
* Inherits a property from the parent Cue elements. If the value is falsy,
|
|
664
|
+
* it is assumed to be inherited from the parent. This returns null if the
|
|
665
|
+
* value isn't found.
|
|
666
|
+
*
|
|
667
|
+
* @param {!Array.<!shaka.extern.Cue>} parents
|
|
668
|
+
* @param {function(!shaka.extern.Cue):?T} cb
|
|
669
|
+
* @return {?T}
|
|
670
|
+
* @template T
|
|
671
|
+
* @private
|
|
672
|
+
*/
|
|
673
|
+
static inheritProperty_(parents, cb) {
|
|
674
|
+
for (let i = parents.length - 1; i >= 0; i--) {
|
|
675
|
+
const val = cb(parents[i]);
|
|
676
|
+
if (val || val === 0) {
|
|
677
|
+
return val;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
return null;
|
|
681
|
+
}
|
|
509
682
|
};
|
|
@@ -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/error.js
CHANGED
|
@@ -22,10 +22,7 @@ goog.provide('shaka.util.Error');
|
|
|
22
22
|
* <ul>
|
|
23
23
|
* <li><a href="https://developer.mozilla.org/en-US/docs/Web/API/MediaError">MediaError</a>
|
|
24
24
|
* <li><a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status">HTTP Codes</a>
|
|
25
|
-
* <li>
|
|
26
|
-
* <a href="https://docs.microsoft.com/en-us/windows/win32/wmdm/error-codes">PlayReady errors</a>
|
|
27
|
-
* or
|
|
28
|
-
* <a href="https://github.com/tpn/winsdk-10/blob/master/Include/10.0.16299.0/winrt/Windows.Media.Protection.PlayReadyErrors.h">more PlayReady errors</a>
|
|
25
|
+
* <li><a href="https://hresult.info">Edge/PlayReady errors</a>
|
|
29
26
|
* </ul>
|
|
30
27
|
*
|
|
31
28
|
* @export
|
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.
|
package/lib/util/mime_utils.js
CHANGED
|
@@ -105,6 +105,39 @@ shaka.util.MimeUtils = class {
|
|
|
105
105
|
return parts[0];
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
+
/**
|
|
109
|
+
* Takes a full MIME type (with codecs) or basic MIME type (without codecs)
|
|
110
|
+
* and returns a basic MIME type (without codecs or other parameters).
|
|
111
|
+
*
|
|
112
|
+
* @param {string} mimeType
|
|
113
|
+
* @return {string}
|
|
114
|
+
*/
|
|
115
|
+
static getBasicType(mimeType) {
|
|
116
|
+
return mimeType.split(';')[0];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Takes a MIME type and returns the codecs parameter, or an empty string if
|
|
121
|
+
* there is no codecs parameter.
|
|
122
|
+
*
|
|
123
|
+
* @param {string} mimeType
|
|
124
|
+
* @return {string}
|
|
125
|
+
*/
|
|
126
|
+
static getCodecs(mimeType) {
|
|
127
|
+
// Parse the basic MIME type from its parameters.
|
|
128
|
+
const pieces = mimeType.split(/ *; */);
|
|
129
|
+
pieces.shift(); // Remove basic MIME type from pieces.
|
|
130
|
+
|
|
131
|
+
const codecs = pieces.find((piece) => piece.startsWith('codecs='));
|
|
132
|
+
if (!codecs) {
|
|
133
|
+
return '';
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// The value may be quoted, so remove quotes at the beginning or end.
|
|
137
|
+
const value = codecs.split('=')[1].replace(/^"|"$/g, '');
|
|
138
|
+
return value;
|
|
139
|
+
}
|
|
140
|
+
|
|
108
141
|
/**
|
|
109
142
|
* Get the base and profile of a codec string. Where [0] will be the codec
|
|
110
143
|
* base and [1] will be the profile.
|
|
@@ -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"
|
package/lib/util/mp4_parser.js
CHANGED
|
@@ -111,6 +111,12 @@ shaka.util.Mp4Parser = class {
|
|
|
111
111
|
parseNext(absStart, reader, partialOkay, stopOnPartial) {
|
|
112
112
|
const start = reader.getPosition();
|
|
113
113
|
|
|
114
|
+
// size(4 bytes) + type(4 bytes) = 8 bytes
|
|
115
|
+
if (stopOnPartial && start + 8 > reader.getLength()) {
|
|
116
|
+
this.done_ = true;
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
114
120
|
let size = reader.readUint32();
|
|
115
121
|
const type = reader.readUint32();
|
|
116
122
|
const name = shaka.util.Mp4Parser.typeToString(type);
|
|
@@ -122,6 +128,10 @@ shaka.util.Mp4Parser = class {
|
|
|
122
128
|
size = reader.getLength() - start;
|
|
123
129
|
break;
|
|
124
130
|
case 1:
|
|
131
|
+
if (stopOnPartial && reader.getPosition() + 8 > reader.getLength()) {
|
|
132
|
+
this.done_ = true;
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
125
135
|
size = reader.readUint64();
|
|
126
136
|
has64BitSize = true;
|
|
127
137
|
break;
|
|
@@ -134,6 +144,10 @@ shaka.util.Mp4Parser = class {
|
|
|
134
144
|
let flags = null;
|
|
135
145
|
|
|
136
146
|
if (this.headers_[type] == shaka.util.Mp4Parser.BoxType_.FULL_BOX) {
|
|
147
|
+
if (stopOnPartial && reader.getPosition() + 4 > reader.getLength()) {
|
|
148
|
+
this.done_ = true;
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
137
151
|
const versionAndFlags = reader.readUint32();
|
|
138
152
|
version = versionAndFlags >>> 24;
|
|
139
153
|
flags = versionAndFlags & 0xFFFFFF;
|
package/lib/util/networking.js
CHANGED
|
@@ -26,7 +26,7 @@ shaka.util.Networking = class {
|
|
|
26
26
|
* @param {?number} start
|
|
27
27
|
* @param {?number} end
|
|
28
28
|
* @param {shaka.extern.RetryParameters} retryParameters
|
|
29
|
-
* @param {?function(BufferSource)=} streamDataCallback
|
|
29
|
+
* @param {?function(BufferSource):!Promise=} streamDataCallback
|
|
30
30
|
* @return {shaka.extern.Request}
|
|
31
31
|
*/
|
|
32
32
|
static createSegmentRequest(uris, start, end, retryParameters,
|