shaka-player 4.7.13 → 4.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AUTHORS +1 -0
- package/CONTRIBUTORS +1 -0
- package/LICENSE +54 -0
- package/README.md +22 -1
- package/dist/controls.css +1 -1
- package/dist/controls.css.map +1 -1
- package/dist/deps.js +34 -23
- package/dist/locales.js +44 -34
- package/dist/shaka-player.compiled.d.ts +194 -44
- package/dist/shaka-player.compiled.debug.d.ts +194 -44
- package/dist/shaka-player.compiled.debug.externs.js +474 -337
- package/dist/shaka-player.compiled.debug.js +949 -832
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +474 -337
- package/dist/shaka-player.compiled.js +1237 -1171
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +257 -47
- package/dist/shaka-player.ui.debug.d.ts +257 -47
- package/dist/shaka-player.ui.debug.externs.js +591 -376
- package/dist/shaka-player.ui.debug.js +1165 -965
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +591 -376
- package/dist/shaka-player.ui.js +1521 -1404
- package/dist/shaka-player.ui.map +4 -4
- package/externs/device_sensor_event.js +18 -0
- package/externs/hisense.js +16 -0
- package/externs/playstation.js +31 -0
- package/externs/shaka/abr_manager.js +3 -1
- package/externs/shaka/ads.js +5 -0
- package/externs/shaka/codecs.js +13 -0
- package/externs/shaka/manifest.js +58 -6
- package/externs/shaka/net.js +13 -1
- package/externs/shaka/player.js +184 -13
- package/externs/shaka/text.js +19 -0
- package/externs/tizen.js +41 -0
- package/externs/webos.js +19 -0
- package/externs/xbox.js +43 -0
- package/lib/abr/simple_abr_manager.js +9 -1
- package/lib/ads/ad_manager.js +19 -2
- package/lib/ads/client_side_ad_manager.js +5 -3
- package/lib/ads/media_tailor_ad_manager.js +9 -0
- package/lib/cast/cast_receiver.js +0 -14
- package/lib/cast/cast_utils.js +1 -0
- package/lib/cea/mp4_cea_parser.js +6 -1
- package/lib/dash/content_protection.js +97 -53
- package/lib/dash/dash_parser.js +230 -176
- package/lib/dash/mpd_utils.js +72 -65
- package/lib/dash/segment_base.js +27 -21
- package/lib/dash/segment_list.js +18 -16
- package/lib/dash/segment_template.js +26 -21
- package/lib/hls/hls_parser.js +358 -164
- package/lib/media/buffering_observer.js +25 -1
- package/lib/media/drm_engine.js +91 -71
- package/lib/media/manifest_filterer.js +220 -0
- package/lib/media/media_source_engine.js +34 -5
- package/lib/media/preload_manager.js +827 -0
- package/lib/media/presentation_timeline.js +11 -0
- package/lib/media/segment_index.js +40 -30
- package/lib/media/segment_prefetch.js +140 -43
- package/lib/media/segment_reference.js +31 -8
- package/lib/media/segment_utils.js +208 -25
- package/lib/media/streaming_engine.js +319 -63
- package/lib/mss/content_protection.js +31 -25
- package/lib/mss/mss_parser.js +74 -55
- package/lib/net/networking_engine.js +49 -4
- package/lib/offline/storage.js +3 -3
- package/lib/player.js +882 -673
- package/lib/polyfill/media_capabilities.js +16 -1
- package/lib/text/simple_text_displayer.js +9 -0
- package/lib/text/stub_text_displayer.js +7 -0
- package/lib/text/text_engine.js +16 -3
- package/lib/text/ttml_text_parser.js +116 -114
- package/lib/text/ui_text_displayer.js +28 -11
- package/lib/text/vtt_text_parser.js +54 -115
- package/lib/transmuxer/opus.js +91 -0
- package/lib/transmuxer/ts_transmuxer.js +115 -0
- package/lib/util/cmcd_manager.js +152 -3
- package/lib/util/dom_utils.js +11 -0
- package/lib/util/error.js +5 -0
- package/lib/util/fake_event.js +2 -0
- package/lib/util/manifest_parser_utils.js +97 -3
- package/lib/util/mp4_box_parsers.js +346 -17
- package/lib/util/mp4_generator.js +23 -0
- package/lib/util/periods.js +19 -0
- package/lib/util/platform.js +161 -9
- package/lib/util/player_configuration.js +61 -3
- package/lib/util/stats.js +16 -0
- package/lib/util/stream_utils.js +16 -6
- package/lib/util/string_utils.js +36 -0
- package/lib/util/tXml.js +814 -0
- package/lib/util/ts_parser.js +50 -0
- package/lib/util/xml_utils.js +10 -338
- package/package.json +2 -2
- package/shaka-player.uncompiled.js +8 -0
- package/ui/controls.js +187 -5
- package/ui/enums.js +2 -0
- package/ui/externs/ui.js +30 -3
- package/ui/gl_matrix/matrix_4x4.js +568 -0
- package/ui/gl_matrix/matrix_quaternion.js +45 -0
- package/ui/less/containers.less +11 -0
- package/ui/locales/en-GB.json +2 -0
- package/ui/locales/en.json +2 -0
- package/ui/locales/es-419.json +2 -0
- package/ui/locales/es.json +2 -0
- package/ui/locales/source.json +8 -0
- package/ui/recenter_vr.js +126 -0
- package/ui/resolution_selection.js +25 -5
- package/ui/statistics_button.js +12 -0
- package/ui/text_selection.js +1 -1
- package/ui/toggle_stereoscopic.js +127 -0
- package/ui/ui.js +74 -38
- package/ui/vr_manager.js +557 -0
- package/ui/vr_utils.js +232 -0
- package/ui/vr_webgl.js +697 -0
|
@@ -15,7 +15,7 @@ goog.require('shaka.text.TextEngine');
|
|
|
15
15
|
goog.require('shaka.util.ArrayUtils');
|
|
16
16
|
goog.require('shaka.util.Error');
|
|
17
17
|
goog.require('shaka.util.StringUtils');
|
|
18
|
-
goog.require('shaka.util.
|
|
18
|
+
goog.require('shaka.util.TXml');
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
/**
|
|
@@ -53,7 +53,7 @@ shaka.text.TtmlTextParser = class {
|
|
|
53
53
|
*/
|
|
54
54
|
parseMedia(data, time, uri) {
|
|
55
55
|
const TtmlTextParser = shaka.text.TtmlTextParser;
|
|
56
|
-
const
|
|
56
|
+
const TXml = shaka.util.TXml;
|
|
57
57
|
const ttpNs = TtmlTextParser.parameterNs_;
|
|
58
58
|
const ttsNs = TtmlTextParser.styleNs_;
|
|
59
59
|
const str = shaka.util.StringUtils.fromUTF8(data);
|
|
@@ -65,7 +65,7 @@ shaka.text.TtmlTextParser = class {
|
|
|
65
65
|
return cues;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
const tt =
|
|
68
|
+
const tt = TXml.parseXmlString(str, 'tt');
|
|
69
69
|
if (!tt) {
|
|
70
70
|
throw new shaka.util.Error(
|
|
71
71
|
shaka.util.Error.Severity.CRITICAL,
|
|
@@ -74,23 +74,23 @@ shaka.text.TtmlTextParser = class {
|
|
|
74
74
|
'Failed to parse TTML.');
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
const body =
|
|
77
|
+
const body = TXml.getElementsByTagName(tt, 'body')[0];
|
|
78
78
|
if (!body) {
|
|
79
79
|
return [];
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
// Get the framerate, subFrameRate and frameRateMultiplier if applicable.
|
|
83
|
-
const frameRate =
|
|
84
|
-
const subFrameRate =
|
|
83
|
+
const frameRate = TXml.getAttributeNSList(tt, ttpNs, 'frameRate');
|
|
84
|
+
const subFrameRate = TXml.getAttributeNSList(
|
|
85
85
|
tt, ttpNs, 'subFrameRate');
|
|
86
86
|
const frameRateMultiplier =
|
|
87
|
-
|
|
88
|
-
const tickRate =
|
|
87
|
+
TXml.getAttributeNSList(tt, ttpNs, 'frameRateMultiplier');
|
|
88
|
+
const tickRate = TXml.getAttributeNSList(tt, ttpNs, 'tickRate');
|
|
89
89
|
|
|
90
|
-
const cellResolution =
|
|
90
|
+
const cellResolution = TXml.getAttributeNSList(
|
|
91
91
|
tt, ttpNs, 'cellResolution');
|
|
92
|
-
const spaceStyle = tt.
|
|
93
|
-
const extent =
|
|
92
|
+
const spaceStyle = tt.attributes['xml:space'] || 'default';
|
|
93
|
+
const extent = TXml.getAttributeNSList(tt, ttsNs, 'extent');
|
|
94
94
|
|
|
95
95
|
if (spaceStyle != 'default' && spaceStyle != 'preserve') {
|
|
96
96
|
throw new shaka.util.Error(
|
|
@@ -99,7 +99,7 @@ shaka.text.TtmlTextParser = class {
|
|
|
99
99
|
shaka.util.Error.Code.INVALID_XML,
|
|
100
100
|
'Invalid xml:space value: ' + spaceStyle);
|
|
101
101
|
}
|
|
102
|
-
const
|
|
102
|
+
const collapseMultipleSpaces = spaceStyle == 'default';
|
|
103
103
|
|
|
104
104
|
const rateInfo = new TtmlTextParser.RateInfo_(
|
|
105
105
|
frameRate, subFrameRate, frameRateMultiplier, tickRate);
|
|
@@ -107,10 +107,10 @@ shaka.text.TtmlTextParser = class {
|
|
|
107
107
|
const cellResolutionInfo =
|
|
108
108
|
TtmlTextParser.getCellResolution_(cellResolution);
|
|
109
109
|
|
|
110
|
-
const metadata =
|
|
111
|
-
const metadataElements = metadata ?
|
|
112
|
-
const styles =
|
|
113
|
-
const regionElements =
|
|
110
|
+
const metadata = TXml.getElementsByTagName(tt, 'metadata')[0];
|
|
111
|
+
const metadataElements = metadata ? metadata.children : [];
|
|
112
|
+
const styles = TXml.getElementsByTagName(tt, 'style');
|
|
113
|
+
const regionElements = TXml.getElementsByTagName(tt, 'region');
|
|
114
114
|
|
|
115
115
|
const cueRegions = [];
|
|
116
116
|
for (const region of regionElements) {
|
|
@@ -125,7 +125,7 @@ shaka.text.TtmlTextParser = class {
|
|
|
125
125
|
// elements. We used to allow this, but it is non-compliant, and the
|
|
126
126
|
// loose nature of our previous parser made it difficult to implement TTML
|
|
127
127
|
// nesting more fully.
|
|
128
|
-
if (
|
|
128
|
+
if (TXml.findChildren(body, 'p').length) {
|
|
129
129
|
throw new shaka.util.Error(
|
|
130
130
|
shaka.util.Error.Severity.CRITICAL,
|
|
131
131
|
shaka.util.Error.Category.TEXT,
|
|
@@ -133,9 +133,9 @@ shaka.text.TtmlTextParser = class {
|
|
|
133
133
|
'<p> can only be inside <div> in TTML');
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
-
for (const div of
|
|
136
|
+
for (const div of TXml.findChildren(body, 'div')) {
|
|
137
137
|
// A <div> element should only contain <p>, not <span>.
|
|
138
|
-
if (
|
|
138
|
+
if (TXml.findChildren(div, 'span').length) {
|
|
139
139
|
throw new shaka.util.Error(
|
|
140
140
|
shaka.util.Error.Severity.CRITICAL,
|
|
141
141
|
shaka.util.Error.Category.TEXT,
|
|
@@ -146,7 +146,7 @@ shaka.text.TtmlTextParser = class {
|
|
|
146
146
|
|
|
147
147
|
const cue = TtmlTextParser.parseCue_(
|
|
148
148
|
body, time, rateInfo, metadataElements, styles,
|
|
149
|
-
regionElements, cueRegions,
|
|
149
|
+
regionElements, cueRegions, collapseMultipleSpaces,
|
|
150
150
|
cellResolutionInfo, /* parentCueElement= */ null,
|
|
151
151
|
/* isContent= */ false, uri);
|
|
152
152
|
if (cue) {
|
|
@@ -165,16 +165,16 @@ shaka.text.TtmlTextParser = class {
|
|
|
165
165
|
/**
|
|
166
166
|
* Parses a TTML node into a Cue.
|
|
167
167
|
*
|
|
168
|
-
* @param {!Node} cueNode
|
|
168
|
+
* @param {!shaka.extern.xml.Node} cueNode
|
|
169
169
|
* @param {shaka.extern.TextParser.TimeContext} timeContext
|
|
170
170
|
* @param {!shaka.text.TtmlTextParser.RateInfo_} rateInfo
|
|
171
|
-
* @param {!Array.<!
|
|
172
|
-
* @param {!Array.<!
|
|
173
|
-
* @param {!Array.<!
|
|
171
|
+
* @param {!Array.<!shaka.extern.xml.Node>} metadataElements
|
|
172
|
+
* @param {!Array.<!shaka.extern.xml.Node>} styles
|
|
173
|
+
* @param {!Array.<!shaka.extern.xml.Node>} regionElements
|
|
174
174
|
* @param {!Array.<!shaka.text.CueRegion>} cueRegions
|
|
175
|
-
* @param {boolean}
|
|
175
|
+
* @param {boolean} collapseMultipleSpaces
|
|
176
176
|
* @param {?{columns: number, rows: number}} cellResolution
|
|
177
|
-
* @param {?
|
|
177
|
+
* @param {?shaka.extern.xml.Node} parentCueElement
|
|
178
178
|
* @param {boolean} isContent
|
|
179
179
|
* @param {?(string|undefined)} uri
|
|
180
180
|
* @return {shaka.text.Cue}
|
|
@@ -182,19 +182,16 @@ shaka.text.TtmlTextParser = class {
|
|
|
182
182
|
*/
|
|
183
183
|
static parseCue_(
|
|
184
184
|
cueNode, timeContext, rateInfo, metadataElements, styles, regionElements,
|
|
185
|
-
cueRegions,
|
|
186
|
-
uri) {
|
|
187
|
-
|
|
185
|
+
cueRegions, collapseMultipleSpaces, cellResolution, parentCueElement,
|
|
186
|
+
isContent, uri) {
|
|
187
|
+
const TXml = shaka.util.TXml;
|
|
188
|
+
const StringUtils = shaka.util.StringUtils;
|
|
189
|
+
/** @type {shaka.extern.xml.Node} */
|
|
188
190
|
let cueElement;
|
|
189
|
-
/** @type {
|
|
190
|
-
let parentElement =
|
|
191
|
+
/** @type {?shaka.extern.xml.Node} */
|
|
192
|
+
let parentElement = parentCueElement;
|
|
191
193
|
|
|
192
|
-
if (cueNode
|
|
193
|
-
// The comments do not contain information that interests us here.
|
|
194
|
-
return null;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
if (cueNode.nodeType == Node.TEXT_NODE) {
|
|
194
|
+
if (TXml.isText(cueNode)) {
|
|
198
195
|
if (!isContent) {
|
|
199
196
|
// Ignore text elements outside the content. For example, whitespace
|
|
200
197
|
// on the same lexical level as the <p> elements, in a document with
|
|
@@ -205,13 +202,16 @@ shaka.text.TtmlTextParser = class {
|
|
|
205
202
|
// So pretend the element was a <span>. parentElement was set above, so
|
|
206
203
|
// we should still be able to correctly traverse up for timing
|
|
207
204
|
// information later.
|
|
208
|
-
|
|
209
|
-
span
|
|
205
|
+
/** @type {shaka.extern.xml.Node} */
|
|
206
|
+
const span = {
|
|
207
|
+
tagName: 'span',
|
|
208
|
+
children: [TXml.getTextContents(cueNode)],
|
|
209
|
+
attributes: {},
|
|
210
|
+
parent: null,
|
|
211
|
+
};
|
|
210
212
|
cueElement = span;
|
|
211
213
|
} else {
|
|
212
|
-
|
|
213
|
-
'nodeType should be ELEMENT_NODE!');
|
|
214
|
-
cueElement = /** @type {!Element} */(cueNode);
|
|
214
|
+
cueElement = cueNode;
|
|
215
215
|
}
|
|
216
216
|
goog.asserts.assert(cueElement, 'cueElement should be non-null!');
|
|
217
217
|
|
|
@@ -226,7 +226,7 @@ shaka.text.TtmlTextParser = class {
|
|
|
226
226
|
}
|
|
227
227
|
|
|
228
228
|
let imageUri = null;
|
|
229
|
-
const backgroundImage =
|
|
229
|
+
const backgroundImage = TXml.getAttributeNSList(
|
|
230
230
|
cueElement,
|
|
231
231
|
shaka.text.TtmlTextParser.smpteNsList_,
|
|
232
232
|
'backgroundImage');
|
|
@@ -239,27 +239,24 @@ shaka.text.TtmlTextParser = class {
|
|
|
239
239
|
}
|
|
240
240
|
}
|
|
241
241
|
|
|
242
|
-
if (cueNode.
|
|
242
|
+
if (cueNode.tagName == 'p' || imageElement || imageUri) {
|
|
243
243
|
isContent = true;
|
|
244
244
|
}
|
|
245
245
|
|
|
246
246
|
const parentIsContent = isContent;
|
|
247
247
|
|
|
248
|
-
const spaceStyle = cueElement.
|
|
249
|
-
(
|
|
248
|
+
const spaceStyle = cueElement.attributes['xml:space'] ||
|
|
249
|
+
(collapseMultipleSpaces ? 'default' : 'preserve');
|
|
250
250
|
|
|
251
|
-
const
|
|
251
|
+
const localCollapseMultipleSpaces = spaceStyle == 'default';
|
|
252
252
|
|
|
253
253
|
// Parse any nested cues first.
|
|
254
|
-
const
|
|
255
|
-
return node.nodeType == Node.TEXT_NODE;
|
|
256
|
-
};
|
|
257
|
-
const isLeafNode = Array.from(cueElement.childNodes).every(isTextNode);
|
|
254
|
+
const isLeafNode = cueElement.children.every(TXml.isText);
|
|
258
255
|
const nestedCues = [];
|
|
259
256
|
if (!isLeafNode) {
|
|
260
257
|
// Otherwise, recurse into the children. Text nodes will convert into
|
|
261
258
|
// anonymous spans, which will then be leaf nodes.
|
|
262
|
-
for (const childNode of cueElement.
|
|
259
|
+
for (const childNode of cueElement.children) {
|
|
263
260
|
const nestedCue = shaka.text.TtmlTextParser.parseCue_(
|
|
264
261
|
childNode,
|
|
265
262
|
timeContext,
|
|
@@ -268,7 +265,7 @@ shaka.text.TtmlTextParser = class {
|
|
|
268
265
|
styles,
|
|
269
266
|
regionElements,
|
|
270
267
|
cueRegions,
|
|
271
|
-
|
|
268
|
+
localCollapseMultipleSpaces,
|
|
272
269
|
cellResolution,
|
|
273
270
|
cueElement,
|
|
274
271
|
isContent,
|
|
@@ -284,12 +281,16 @@ shaka.text.TtmlTextParser = class {
|
|
|
284
281
|
|
|
285
282
|
const isNested = /** @type {boolean} */ (parentCueElement != null);
|
|
286
283
|
|
|
284
|
+
const textContent = TXml.getTextContents(cueElement);
|
|
287
285
|
// In this regex, "\S" means "non-whitespace character".
|
|
288
|
-
const hasTextContent =
|
|
286
|
+
const hasTextContent = cueElement.children.length &&
|
|
287
|
+
textContent &&
|
|
288
|
+
/\S/.test(textContent);
|
|
289
|
+
|
|
289
290
|
const hasTimeAttributes =
|
|
290
|
-
cueElement.
|
|
291
|
-
cueElement.
|
|
292
|
-
cueElement.
|
|
291
|
+
cueElement.attributes['begin'] ||
|
|
292
|
+
cueElement.attributes['end'] ||
|
|
293
|
+
cueElement.attributes['dur'];
|
|
293
294
|
|
|
294
295
|
if (!hasTimeAttributes && !hasTextContent && cueElement.tagName != 'br' &&
|
|
295
296
|
nestedCues.length == 0) {
|
|
@@ -299,7 +300,7 @@ shaka.text.TtmlTextParser = class {
|
|
|
299
300
|
// as some information could be held by its attributes.
|
|
300
301
|
// <p /> won't, as it would not be displayed.
|
|
301
302
|
return null;
|
|
302
|
-
} else if (
|
|
303
|
+
} else if (localCollapseMultipleSpaces) {
|
|
303
304
|
// Disregards empty anonymous spans when (local) trim is true.
|
|
304
305
|
return null;
|
|
305
306
|
}
|
|
@@ -310,11 +311,12 @@ shaka.text.TtmlTextParser = class {
|
|
|
310
311
|
cueElement, rateInfo);
|
|
311
312
|
// Resolve local time relative to parent elements. Time elements can appear
|
|
312
313
|
// all the way up to 'body', but not 'tt'.
|
|
313
|
-
while (parentElement && parentElement
|
|
314
|
+
while (parentElement && TXml.isNode(parentElement) &&
|
|
314
315
|
parentElement.tagName != 'tt') {
|
|
315
316
|
({start, end} = shaka.text.TtmlTextParser.resolveTime_(
|
|
316
317
|
parentElement, rateInfo, start, end));
|
|
317
|
-
parentElement =
|
|
318
|
+
parentElement =
|
|
319
|
+
/** @type {shaka.extern.xml.Node} */ (parentElement.parent);
|
|
318
320
|
}
|
|
319
321
|
|
|
320
322
|
if (start == null) {
|
|
@@ -357,10 +359,9 @@ shaka.text.TtmlTextParser = class {
|
|
|
357
359
|
let payload = '';
|
|
358
360
|
if (isLeafNode) {
|
|
359
361
|
// If the childNodes are all text, this is a leaf node. Get the payload.
|
|
360
|
-
payload =
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
payload = payload.trim();
|
|
362
|
+
payload = StringUtils.htmlUnescape(
|
|
363
|
+
shaka.util.TXml.getTextContents(cueElement) || '');
|
|
364
|
+
if (localCollapseMultipleSpaces) {
|
|
364
365
|
// Collapse multiple spaces into one.
|
|
365
366
|
payload = payload.replace(/\s+/g, ' ');
|
|
366
367
|
}
|
|
@@ -386,16 +387,16 @@ shaka.text.TtmlTextParser = class {
|
|
|
386
387
|
// Do not actually apply that region unless it is non-inherited, though.
|
|
387
388
|
// This makes it so that, if a parent element has a region, the children
|
|
388
389
|
// don't also all independently apply the positioning of that region.
|
|
389
|
-
if (cueElement.
|
|
390
|
-
if (regionElement && regionElement.
|
|
391
|
-
const regionId = regionElement.
|
|
390
|
+
if (cueElement.attributes['region']) {
|
|
391
|
+
if (regionElement && regionElement.attributes['xml:id']) {
|
|
392
|
+
const regionId = regionElement.attributes['xml:id'];
|
|
392
393
|
cue.region = cueRegions.filter((region) => region.id == regionId)[0];
|
|
393
394
|
}
|
|
394
395
|
}
|
|
395
396
|
|
|
396
397
|
let regionElementForStyle = regionElement;
|
|
397
|
-
if (parentCueElement && isNested && !cueElement.
|
|
398
|
-
!cueElement.
|
|
398
|
+
if (parentCueElement && isNested && !cueElement.attributes['region'] &&
|
|
399
|
+
!cueElement.attributes['style']) {
|
|
399
400
|
regionElementForStyle =
|
|
400
401
|
shaka.text.TtmlTextParser.getElementsFromCollection_(
|
|
401
402
|
parentCueElement, 'region', regionElements, /* prefix= */ '')[0];
|
|
@@ -405,7 +406,7 @@ shaka.text.TtmlTextParser = class {
|
|
|
405
406
|
cue,
|
|
406
407
|
cueElement,
|
|
407
408
|
regionElementForStyle,
|
|
408
|
-
imageElement,
|
|
409
|
+
/** @type {!shaka.extern.xml.Node} */(imageElement),
|
|
409
410
|
imageUri,
|
|
410
411
|
styles,
|
|
411
412
|
/** isNested= */ parentIsContent, // "nested in a <div>" doesn't count.
|
|
@@ -417,9 +418,9 @@ shaka.text.TtmlTextParser = class {
|
|
|
417
418
|
/**
|
|
418
419
|
* Parses an Element into a TextTrackCue or VTTCue.
|
|
419
420
|
*
|
|
420
|
-
* @param {!
|
|
421
|
-
* @param {!Array.<!
|
|
422
|
-
* used principally for images.
|
|
421
|
+
* @param {!shaka.extern.xml.Node} regionElement
|
|
422
|
+
* @param {!Array.<!shaka.extern.xml.Node>} styles
|
|
423
|
+
* Defined in the top of tt element and used principally for images.
|
|
423
424
|
* @param {?string} globalExtent
|
|
424
425
|
* @return {shaka.text.CueRegion}
|
|
425
426
|
* @private
|
|
@@ -427,7 +428,7 @@ shaka.text.TtmlTextParser = class {
|
|
|
427
428
|
static parseCueRegion_(regionElement, styles, globalExtent) {
|
|
428
429
|
const TtmlTextParser = shaka.text.TtmlTextParser;
|
|
429
430
|
const region = new shaka.text.CueRegion();
|
|
430
|
-
const id = regionElement.
|
|
431
|
+
const id = regionElement.attributes['xml:id'];
|
|
431
432
|
if (!id) {
|
|
432
433
|
shaka.log.warning('TtmlTextParser parser encountered a region with ' +
|
|
433
434
|
'no id. Region will be ignored.');
|
|
@@ -511,11 +512,11 @@ shaka.text.TtmlTextParser = class {
|
|
|
511
512
|
* Adds applicable style properties to a cue.
|
|
512
513
|
*
|
|
513
514
|
* @param {!shaka.text.Cue} cue
|
|
514
|
-
* @param {!
|
|
515
|
-
* @param {
|
|
516
|
-
* @param {
|
|
515
|
+
* @param {!shaka.extern.xml.Node} cueElement
|
|
516
|
+
* @param {shaka.extern.xml.Node} region
|
|
517
|
+
* @param {shaka.extern.xml.Node} imageElement
|
|
517
518
|
* @param {?string} imageUri
|
|
518
|
-
* @param {!Array.<!
|
|
519
|
+
* @param {!Array.<!shaka.extern.xml.Node>} styles
|
|
519
520
|
* @param {boolean} isNested
|
|
520
521
|
* @param {boolean} isLeaf
|
|
521
522
|
* @private
|
|
@@ -524,6 +525,7 @@ shaka.text.TtmlTextParser = class {
|
|
|
524
525
|
cue, cueElement, region, imageElement, imageUri, styles,
|
|
525
526
|
isNested, isLeaf) {
|
|
526
527
|
const TtmlTextParser = shaka.text.TtmlTextParser;
|
|
528
|
+
const TXml = shaka.util.TXml;
|
|
527
529
|
const Cue = shaka.text.Cue;
|
|
528
530
|
|
|
529
531
|
// Styles should be inherited from regions, if a style property is not
|
|
@@ -681,10 +683,10 @@ shaka.text.TtmlTextParser = class {
|
|
|
681
683
|
// in PR #1859, in April 2019, and first released in v2.5.0.
|
|
682
684
|
// Now we check for both, although only imageType (camelCase) is to spec.
|
|
683
685
|
const backgroundImageType =
|
|
684
|
-
imageElement.
|
|
685
|
-
imageElement.
|
|
686
|
-
const backgroundImageEncoding = imageElement.
|
|
687
|
-
const backgroundImageData = imageElement.
|
|
686
|
+
imageElement.attributes['imageType'] ||
|
|
687
|
+
imageElement.attributes['imagetype'];
|
|
688
|
+
const backgroundImageEncoding = imageElement.attributes['encoding'];
|
|
689
|
+
const backgroundImageData = (TXml.getTextContents(imageElement)).trim();
|
|
688
690
|
if (backgroundImageType == 'PNG' &&
|
|
689
691
|
backgroundImageEncoding == 'Base64' &&
|
|
690
692
|
backgroundImageData) {
|
|
@@ -823,9 +825,9 @@ shaka.text.TtmlTextParser = class {
|
|
|
823
825
|
* Finds a specified attribute on either the original cue element or its
|
|
824
826
|
* associated region and returns the value if the attribute was found.
|
|
825
827
|
*
|
|
826
|
-
* @param {!
|
|
827
|
-
* @param {
|
|
828
|
-
* @param {!Array.<!
|
|
828
|
+
* @param {!shaka.extern.xml.Node} cueElement
|
|
829
|
+
* @param {shaka.extern.xml.Node} region
|
|
830
|
+
* @param {!Array.<!shaka.extern.xml.Node>} styles
|
|
829
831
|
* @param {string} attribute
|
|
830
832
|
* @param {boolean=} shouldInheritRegionStyles
|
|
831
833
|
* @return {?string}
|
|
@@ -853,21 +855,21 @@ shaka.text.TtmlTextParser = class {
|
|
|
853
855
|
* Finds a specified attribute on the element's associated region
|
|
854
856
|
* and returns the value if the attribute was found.
|
|
855
857
|
*
|
|
856
|
-
* @param {
|
|
857
|
-
* @param {!Array.<!
|
|
858
|
+
* @param {shaka.extern.xml.Node} region
|
|
859
|
+
* @param {!Array.<!shaka.extern.xml.Node>} styles
|
|
858
860
|
* @param {string} attribute
|
|
859
861
|
* @return {?string}
|
|
860
862
|
* @private
|
|
861
863
|
*/
|
|
862
864
|
static getStyleAttributeFromRegion_(region, styles, attribute) {
|
|
863
|
-
const
|
|
865
|
+
const TXml = shaka.util.TXml;
|
|
864
866
|
const ttsNs = shaka.text.TtmlTextParser.styleNs_;
|
|
865
867
|
|
|
866
868
|
if (!region) {
|
|
867
869
|
return null;
|
|
868
870
|
}
|
|
869
871
|
|
|
870
|
-
const attr =
|
|
872
|
+
const attr = TXml.getAttributeNSList(region, ttsNs, attribute);
|
|
871
873
|
if (attr) {
|
|
872
874
|
return attr;
|
|
873
875
|
}
|
|
@@ -880,19 +882,19 @@ shaka.text.TtmlTextParser = class {
|
|
|
880
882
|
* Finds a specified attribute on the cue element and returns the value
|
|
881
883
|
* if the attribute was found.
|
|
882
884
|
*
|
|
883
|
-
* @param {!
|
|
884
|
-
* @param {!Array.<!
|
|
885
|
+
* @param {!shaka.extern.xml.Node} cueElement
|
|
886
|
+
* @param {!Array.<!shaka.extern.xml.Node>} styles
|
|
885
887
|
* @param {string} attribute
|
|
886
888
|
* @return {?string}
|
|
887
889
|
* @private
|
|
888
890
|
*/
|
|
889
891
|
static getStyleAttributeFromElement_(cueElement, styles, attribute) {
|
|
890
|
-
const
|
|
892
|
+
const TXml = shaka.util.TXml;
|
|
891
893
|
const ttsNs = shaka.text.TtmlTextParser.styleNs_;
|
|
892
894
|
|
|
893
895
|
// Styling on elements should take precedence
|
|
894
896
|
// over the main styling attributes
|
|
895
|
-
const elementAttribute =
|
|
897
|
+
const elementAttribute = TXml.getAttributeNSList(
|
|
896
898
|
cueElement,
|
|
897
899
|
ttsNs,
|
|
898
900
|
attribute);
|
|
@@ -908,14 +910,14 @@ shaka.text.TtmlTextParser = class {
|
|
|
908
910
|
* Finds a specified attribute on an element's styles and the styles those
|
|
909
911
|
* styles inherit from.
|
|
910
912
|
*
|
|
911
|
-
* @param {!
|
|
912
|
-
* @param {!Array.<!
|
|
913
|
+
* @param {!shaka.extern.xml.Node} element
|
|
914
|
+
* @param {!Array.<!shaka.extern.xml.Node>} styles
|
|
913
915
|
* @param {string} attribute
|
|
914
916
|
* @return {?string}
|
|
915
917
|
* @private
|
|
916
918
|
*/
|
|
917
919
|
static getInheritedStyleAttribute_(element, styles, attribute) {
|
|
918
|
-
const
|
|
920
|
+
const TXml = shaka.util.TXml;
|
|
919
921
|
const ttsNs = shaka.text.TtmlTextParser.styleNs_;
|
|
920
922
|
const ebuttsNs = shaka.text.TtmlTextParser.styleEbuttsNs_;
|
|
921
923
|
|
|
@@ -928,14 +930,14 @@ shaka.text.TtmlTextParser = class {
|
|
|
928
930
|
// The last value in our styles stack takes the precedence over the others
|
|
929
931
|
for (let i = 0; i < inheritedStyles.length; i++) {
|
|
930
932
|
// Check ebu namespace first.
|
|
931
|
-
let styleAttributeValue =
|
|
933
|
+
let styleAttributeValue = TXml.getAttributeNS(
|
|
932
934
|
inheritedStyles[i],
|
|
933
935
|
ebuttsNs,
|
|
934
936
|
attribute);
|
|
935
937
|
|
|
936
938
|
if (!styleAttributeValue) {
|
|
937
939
|
// Fall back to tts namespace.
|
|
938
|
-
styleAttributeValue =
|
|
940
|
+
styleAttributeValue = TXml.getAttributeNSList(
|
|
939
941
|
inheritedStyles[i],
|
|
940
942
|
ttsNs,
|
|
941
943
|
attribute);
|
|
@@ -962,12 +964,12 @@ shaka.text.TtmlTextParser = class {
|
|
|
962
964
|
* Selects items from |collection| whose id matches |attributeName|
|
|
963
965
|
* from |element|.
|
|
964
966
|
*
|
|
965
|
-
* @param {
|
|
967
|
+
* @param {shaka.extern.xml.Node} element
|
|
966
968
|
* @param {string} attributeName
|
|
967
|
-
* @param {!Array.<
|
|
969
|
+
* @param {!Array.<shaka.extern.xml.Node>} collection
|
|
968
970
|
* @param {string} prefixName
|
|
969
971
|
* @param {string=} nsName
|
|
970
|
-
* @return {!Array.<!
|
|
972
|
+
* @return {!Array.<!shaka.extern.xml.Node>}
|
|
971
973
|
* @private
|
|
972
974
|
*/
|
|
973
975
|
static getElementsFromCollection_(
|
|
@@ -988,7 +990,7 @@ shaka.text.TtmlTextParser = class {
|
|
|
988
990
|
|
|
989
991
|
for (const name of itemNames) {
|
|
990
992
|
for (const item of collection) {
|
|
991
|
-
if ((prefixName + item.
|
|
993
|
+
if ((prefixName + item.attributes['xml:id']) == name) {
|
|
992
994
|
items.push(item);
|
|
993
995
|
break;
|
|
994
996
|
}
|
|
@@ -1003,7 +1005,7 @@ shaka.text.TtmlTextParser = class {
|
|
|
1003
1005
|
/**
|
|
1004
1006
|
* Traverses upwards from a given node until a given attribute is found.
|
|
1005
1007
|
*
|
|
1006
|
-
* @param {!
|
|
1008
|
+
* @param {!shaka.extern.xml.Node} element
|
|
1007
1009
|
* @param {string} attributeName
|
|
1008
1010
|
* @param {string=} nsName
|
|
1009
1011
|
* @return {?string}
|
|
@@ -1011,19 +1013,19 @@ shaka.text.TtmlTextParser = class {
|
|
|
1011
1013
|
*/
|
|
1012
1014
|
static getInheritedAttribute_(element, attributeName, nsName) {
|
|
1013
1015
|
let ret = null;
|
|
1014
|
-
const
|
|
1015
|
-
while (
|
|
1016
|
+
const TXml = shaka.util.TXml;
|
|
1017
|
+
while (!ret) {
|
|
1016
1018
|
ret = nsName ?
|
|
1017
|
-
|
|
1018
|
-
element.
|
|
1019
|
+
TXml.getAttributeNS(element, nsName, attributeName) :
|
|
1020
|
+
element.attributes[attributeName];
|
|
1019
1021
|
if (ret) {
|
|
1020
1022
|
break;
|
|
1021
1023
|
}
|
|
1022
1024
|
|
|
1023
1025
|
// Element.parentNode can lead to XMLDocument, which is not an Element and
|
|
1024
1026
|
// has no getAttribute().
|
|
1025
|
-
const parentNode = element.
|
|
1026
|
-
if (parentNode
|
|
1027
|
+
const parentNode = element.parent;
|
|
1028
|
+
if (parentNode) {
|
|
1027
1029
|
element = parentNode;
|
|
1028
1030
|
} else {
|
|
1029
1031
|
break;
|
|
@@ -1036,7 +1038,7 @@ shaka.text.TtmlTextParser = class {
|
|
|
1036
1038
|
* Factor parent/ancestor time attributes into the parsed time of a
|
|
1037
1039
|
* child/descendent.
|
|
1038
1040
|
*
|
|
1039
|
-
* @param {!
|
|
1041
|
+
* @param {!shaka.extern.xml.Node} parentElement
|
|
1040
1042
|
* @param {!shaka.text.TtmlTextParser.RateInfo_} rateInfo
|
|
1041
1043
|
* @param {?number} start The child's start time
|
|
1042
1044
|
* @param {?number} end The child's end time
|
|
@@ -1074,18 +1076,18 @@ shaka.text.TtmlTextParser = class {
|
|
|
1074
1076
|
/**
|
|
1075
1077
|
* Parse TTML time attributes from the given element.
|
|
1076
1078
|
*
|
|
1077
|
-
* @param {!
|
|
1079
|
+
* @param {!shaka.extern.xml.Node} element
|
|
1078
1080
|
* @param {!shaka.text.TtmlTextParser.RateInfo_} rateInfo
|
|
1079
1081
|
* @return {{start: ?number, end: ?number}}
|
|
1080
1082
|
* @private
|
|
1081
1083
|
*/
|
|
1082
1084
|
static parseTime_(element, rateInfo) {
|
|
1083
1085
|
const start = shaka.text.TtmlTextParser.parseTimeAttribute_(
|
|
1084
|
-
element.
|
|
1086
|
+
element.attributes['begin'], rateInfo);
|
|
1085
1087
|
let end = shaka.text.TtmlTextParser.parseTimeAttribute_(
|
|
1086
|
-
element.
|
|
1088
|
+
element.attributes['end'], rateInfo);
|
|
1087
1089
|
const duration = shaka.text.TtmlTextParser.parseTimeAttribute_(
|
|
1088
|
-
element.
|
|
1090
|
+
element.attributes['dur'], rateInfo);
|
|
1089
1091
|
if (end == null && duration != null) {
|
|
1090
1092
|
end = start + duration;
|
|
1091
1093
|
}
|
|
@@ -28,8 +28,9 @@ shaka.text.UITextDisplayer = class {
|
|
|
28
28
|
* Constructor.
|
|
29
29
|
* @param {HTMLMediaElement} video
|
|
30
30
|
* @param {HTMLElement} videoContainer
|
|
31
|
+
* @param {shaka.extern.TextDisplayerConfiguration} config
|
|
31
32
|
*/
|
|
32
|
-
constructor(video, videoContainer) {
|
|
33
|
+
constructor(video, videoContainer, config) {
|
|
33
34
|
goog.asserts.assert(videoContainer, 'videoContainer should be valid.');
|
|
34
35
|
|
|
35
36
|
/** @private {boolean} */
|
|
@@ -64,16 +65,12 @@ shaka.text.UITextDisplayer = class {
|
|
|
64
65
|
|
|
65
66
|
this.videoContainer_.appendChild(this.textContainer_);
|
|
66
67
|
|
|
67
|
-
/**
|
|
68
|
-
* The captions' update period in seconds.
|
|
69
|
-
* @private {number}
|
|
70
|
-
*/
|
|
71
|
-
const updatePeriod = 0.25;
|
|
72
|
-
|
|
73
68
|
/** @private {shaka.util.Timer} */
|
|
74
69
|
this.captionsTimer_ = new shaka.util.Timer(() => {
|
|
75
|
-
this.
|
|
76
|
-
|
|
70
|
+
if (!this.video_.paused) {
|
|
71
|
+
this.updateCaptions_();
|
|
72
|
+
}
|
|
73
|
+
}).tickEvery(config.captionsUpdatePeriod);
|
|
77
74
|
|
|
78
75
|
/**
|
|
79
76
|
* Maps cues to cue elements. Specifically points out the wrapper element of
|
|
@@ -93,6 +90,10 @@ shaka.text.UITextDisplayer = class {
|
|
|
93
90
|
this.updateCaptions_(/* forceUpdate= */ true);
|
|
94
91
|
});
|
|
95
92
|
|
|
93
|
+
this.eventManager_.listen(this.video_, 'seeking', () => {
|
|
94
|
+
this.updateCaptions_(/* forceUpdate= */ true);
|
|
95
|
+
});
|
|
96
|
+
|
|
96
97
|
// From: https://html.spec.whatwg.org/multipage/media.html#dom-video-videowidth
|
|
97
98
|
// Whenever the natural width or natural height of the video changes
|
|
98
99
|
// (including, for example, because the selected video track was changed),
|
|
@@ -124,6 +125,17 @@ shaka.text.UITextDisplayer = class {
|
|
|
124
125
|
}
|
|
125
126
|
|
|
126
127
|
|
|
128
|
+
/**
|
|
129
|
+
* @override
|
|
130
|
+
* @export
|
|
131
|
+
*/
|
|
132
|
+
configure(config) {
|
|
133
|
+
if (this.captionsTimer_) {
|
|
134
|
+
this.captionsTimer_.tickEvery(config.captionsUpdatePeriod);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
|
|
127
139
|
/**
|
|
128
140
|
* @override
|
|
129
141
|
* @export
|
|
@@ -339,6 +351,9 @@ shaka.text.UITextDisplayer = class {
|
|
|
339
351
|
const cueRegistry = this.currentCuesMap_.get(cue);
|
|
340
352
|
goog.asserts.assert(cueRegistry, 'cueRegistry should exist.');
|
|
341
353
|
if (cueRegistry.regionElement) {
|
|
354
|
+
if (cueRegistry.regionElement.contains(container)) {
|
|
355
|
+
cueRegistry.regionElement.removeChild(container);
|
|
356
|
+
}
|
|
342
357
|
container.appendChild(cueRegistry.regionElement);
|
|
343
358
|
cueRegistry.regionElement.appendChild(cueRegistry.cueElement);
|
|
344
359
|
} else {
|
|
@@ -373,8 +388,10 @@ shaka.text.UITextDisplayer = class {
|
|
|
373
388
|
if (this.isTextVisible_) {
|
|
374
389
|
// Log currently attached cue elements for verification, later.
|
|
375
390
|
const previousCuesMap = new Map();
|
|
376
|
-
|
|
377
|
-
|
|
391
|
+
if (goog.DEBUG) {
|
|
392
|
+
for (const cue of this.currentCuesMap_.keys()) {
|
|
393
|
+
previousCuesMap.set(cue, this.currentCuesMap_.get(cue));
|
|
394
|
+
}
|
|
378
395
|
}
|
|
379
396
|
|
|
380
397
|
// Update the cues.
|