shaka-player 4.3.3 → 4.3.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/.github/workflows/build-and-test.yaml +4 -4
- package/.github/workflows/demo-version-index.yaml +6 -0
- package/.github/workflows/selenium-lab-tests.yaml +72 -11
- package/.github/workflows/update-screenshots.yaml +142 -0
- package/CHANGELOG.md +41 -0
- package/demo/common/asset.js +15 -0
- package/demo/common/assets.js +4 -2
- package/demo/common/message_ids.js +1 -0
- package/demo/custom.js +19 -0
- package/demo/locales/en.json +1 -0
- package/demo/locales/source.json +4 -0
- package/demo/main.js +14 -0
- package/dist/demo.compiled.debug.js +30 -28
- package/dist/demo.compiled.debug.map +3 -3
- package/dist/demo.compiled.js +30 -28
- package/dist/demo.compiled.map +3 -3
- package/dist/deps.js +4 -3
- package/dist/locales.js +30 -30
- package/dist/receiver.compiled.debug.js +22 -22
- package/dist/receiver.compiled.debug.map +2 -2
- package/dist/receiver.compiled.js +22 -22
- package/dist/receiver.compiled.map +2 -2
- package/dist/shaka-player.compiled.d.ts +7 -0
- package/dist/shaka-player.compiled.debug.d.ts +7 -0
- package/dist/shaka-player.compiled.debug.js +116 -109
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.js +825 -821
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +7 -0
- package/dist/shaka-player.ui.debug.d.ts +7 -0
- package/dist/shaka-player.ui.debug.js +128 -121
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.js +937 -932
- package/dist/shaka-player.ui.map +4 -4
- package/docs/tutorials/errors.md +94 -0
- package/docs/tutorials/fairplay.md +2 -2
- package/docs/tutorials/index.json +2 -0
- package/docs/tutorials/widevine-service-certs.md +45 -0
- package/externs/ima.js +9 -0
- package/lib/ads/client_side_ad_manager.js +6 -3
- package/lib/dash/segment_template.js +17 -5
- package/lib/hls/hls_parser.js +12 -2
- package/lib/media/drm_engine.js +4 -2
- package/lib/media/transmuxer.js +13 -7
- package/lib/media/video_wrapper.js +3 -3
- package/lib/offline/indexeddb/storage_mechanism.js +1 -1
- package/lib/player.js +3 -1
- package/lib/polyfill/media_capabilities.js +6 -9
- package/lib/polyfill/mediasource.js +24 -2
- package/lib/text/simple_text_displayer.js +2 -64
- package/lib/text/text_utils.js +88 -0
- package/lib/text/ui_text_displayer.js +1 -5
- package/lib/text/vtt_text_parser.js +228 -102
- package/lib/text/web_vtt_generator.js +2 -64
- package/lib/util/mime_utils.js +1 -1
- package/lib/util/platform.js +8 -0
- package/lib/util/stream_utils.js +26 -14
- package/package.json +1 -1
- package/support.html +1 -0
- package/test/dash/dash_parser_manifest_unit.js +0 -36
- package/test/polyfill/media_capabilities_unit.js +21 -22
- package/test/test/util/layout_tests.js +433 -0
- package/test/test/util/util.js +0 -120
- package/test/{ui → text}/text_displayer_layout_unit.js +59 -235
- package/test/text/ui_text_displayer_unit.js +2 -4
- package/test/text/vtt_text_parser_unit.js +138 -8
- package/test/text/web_vtt_layout_integration.js +525 -0
|
@@ -474,6 +474,7 @@ shaka.text.UITextDisplayer = class {
|
|
|
474
474
|
wrapper = shaka.util.Dom.createHTMLElement('span');
|
|
475
475
|
wrapper.classList.add('shaka-text-wrapper');
|
|
476
476
|
wrapper.style.backgroundColor = cue.backgroundColor;
|
|
477
|
+
wrapper.style.lineHeight = 'normal';
|
|
477
478
|
cueElement.appendChild(wrapper);
|
|
478
479
|
}
|
|
479
480
|
|
|
@@ -617,12 +618,7 @@ shaka.text.UITextDisplayer = class {
|
|
|
617
618
|
// TODO: Implement lineAlignment of 'CENTER'.
|
|
618
619
|
if (cue.line != null) {
|
|
619
620
|
if (cue.lineInterpretation == Cue.lineInterpretation.PERCENTAGE) {
|
|
620
|
-
// When setting absolute positioning, you need to set x/y/width/height
|
|
621
|
-
// so the element is positioned correctly. Set these as default and
|
|
622
|
-
// other settings will override them.
|
|
623
621
|
style.position = 'absolute';
|
|
624
|
-
style.left = '0';
|
|
625
|
-
style.top = '0';
|
|
626
622
|
if (cue.writingMode == Cue.writingMode.HORIZONTAL_TOP_TO_BOTTOM) {
|
|
627
623
|
style.width = '100%';
|
|
628
624
|
if (cue.lineAlign == Cue.lineAlign.START) {
|
|
@@ -154,14 +154,14 @@ shaka.text.VttTextParser = class {
|
|
|
154
154
|
for (const [key, value] of Object.entries(textColor)) {
|
|
155
155
|
const cue = new shaka.text.Cue(0, 0, '');
|
|
156
156
|
cue.color = value;
|
|
157
|
-
styles.set(key, cue);
|
|
157
|
+
styles.set('.' + key, cue);
|
|
158
158
|
}
|
|
159
159
|
|
|
160
160
|
const bgColor = shaka.text.Cue.defaultTextBackgroundColor;
|
|
161
161
|
for (const [key, value] of Object.entries(bgColor)) {
|
|
162
162
|
const cue = new shaka.text.Cue(0, 0, '');
|
|
163
163
|
cue.backgroundColor = value;
|
|
164
|
-
styles.set(key, cue);
|
|
164
|
+
styles.set('.' + key, cue);
|
|
165
165
|
}
|
|
166
166
|
}
|
|
167
167
|
|
|
@@ -221,96 +221,121 @@ shaka.text.VttTextParser = class {
|
|
|
221
221
|
return;
|
|
222
222
|
}
|
|
223
223
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
let
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
224
|
+
/** @type {!Array.<!Array.<string>>} */
|
|
225
|
+
const styleBlocks = [];
|
|
226
|
+
let lastBlockIndex = -1;
|
|
227
|
+
for (let i = 1; i < text.length; i++) {
|
|
228
|
+
if (text[i].includes('::cue')) {
|
|
229
|
+
styleBlocks.push([]);
|
|
230
|
+
lastBlockIndex = styleBlocks.length - 1;
|
|
231
|
+
}
|
|
232
|
+
if (lastBlockIndex == -1) {
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
styleBlocks[lastBlockIndex].push(text[i]);
|
|
236
|
+
if (text[i].includes('}')) {
|
|
237
|
+
lastBlockIndex = -1;
|
|
238
|
+
}
|
|
233
239
|
}
|
|
234
240
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
+
for (const styleBlock of styleBlocks) {
|
|
242
|
+
let styleSelector = 'global';
|
|
243
|
+
// Look for what is within parentheses. For example:
|
|
244
|
+
// <code>:: cue (b) {</code>, what we are looking for is <code>b</code>
|
|
245
|
+
const selector = styleBlock[0].match(/\((.*)\)/);
|
|
246
|
+
if (selector) {
|
|
247
|
+
styleSelector = selector.pop();
|
|
241
248
|
}
|
|
242
|
-
}
|
|
243
249
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
250
|
+
// We start at 1 to avoid '::cue' and end earlier to avoid '}'
|
|
251
|
+
let propertyLines = styleBlock.slice(1, -1);
|
|
252
|
+
if (styleBlock[0].includes('}')) {
|
|
253
|
+
const payload = /\{(.*?)\}/.exec(styleBlock[0]);
|
|
254
|
+
if (payload) {
|
|
255
|
+
propertyLines = payload[1].split(';');
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Continue styles over multiple selectors if necessary.
|
|
260
|
+
// For example,
|
|
261
|
+
// ::cue(b) { background: white; } ::cue(b) { color: blue; }
|
|
262
|
+
// should set both the background and foreground of bold tags.
|
|
263
|
+
let cue = styles.get(styleSelector);
|
|
264
|
+
if (!cue) {
|
|
265
|
+
cue = new shaka.text.Cue(0, 0, '');
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
let validStyle = false;
|
|
269
|
+
for (let i = 0; i < propertyLines.length; i++) {
|
|
270
|
+
// We look for CSS properties. As a general rule they are separated by
|
|
271
|
+
// <code>:</code>. Eg: <code>color: red;</code>
|
|
272
|
+
const lineParts = /^\s*([^:]+):\s*(.*)/.exec(propertyLines[i]);
|
|
273
|
+
if (lineParts) {
|
|
274
|
+
const name = lineParts[1].trim();
|
|
275
|
+
const value = lineParts[2].trim().replace(';', '');
|
|
276
|
+
switch (name) {
|
|
277
|
+
case 'background-color':
|
|
278
|
+
case 'background':
|
|
272
279
|
validStyle = true;
|
|
273
|
-
cue.
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
280
|
+
cue.backgroundColor = value;
|
|
281
|
+
break;
|
|
282
|
+
case 'color':
|
|
283
|
+
validStyle = true;
|
|
284
|
+
cue.color = value;
|
|
285
|
+
break;
|
|
286
|
+
case 'font-family':
|
|
287
|
+
validStyle = true;
|
|
288
|
+
cue.fontFamily = value;
|
|
289
|
+
break;
|
|
290
|
+
case 'font-size':
|
|
291
|
+
validStyle = true;
|
|
292
|
+
cue.fontSize = value;
|
|
293
|
+
break;
|
|
294
|
+
case 'font-weight':
|
|
295
|
+
if (parseInt(value, 10) >= 700 || value == 'bold') {
|
|
287
296
|
validStyle = true;
|
|
288
|
-
cue.
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
297
|
+
cue.fontWeight = shaka.text.Cue.fontWeight.BOLD;
|
|
298
|
+
}
|
|
299
|
+
break;
|
|
300
|
+
case 'font-style':
|
|
301
|
+
switch (value) {
|
|
302
|
+
case 'normal':
|
|
303
|
+
validStyle = true;
|
|
304
|
+
cue.fontStyle = shaka.text.Cue.fontStyle.NORMAL;
|
|
305
|
+
break;
|
|
306
|
+
case 'italic':
|
|
307
|
+
validStyle = true;
|
|
308
|
+
cue.fontStyle = shaka.text.Cue.fontStyle.ITALIC;
|
|
309
|
+
break;
|
|
310
|
+
case 'oblique':
|
|
311
|
+
validStyle = true;
|
|
312
|
+
cue.fontStyle = shaka.text.Cue.fontStyle.OBLIQUE;
|
|
313
|
+
break;
|
|
314
|
+
}
|
|
315
|
+
break;
|
|
316
|
+
case 'opacity':
|
|
317
|
+
validStyle = true;
|
|
318
|
+
cue.opacity = parseFloat(value);
|
|
319
|
+
break;
|
|
320
|
+
case 'text-shadow':
|
|
321
|
+
validStyle = true;
|
|
322
|
+
cue.textShadow = value;
|
|
323
|
+
break;
|
|
324
|
+
case 'white-space':
|
|
325
|
+
validStyle = true;
|
|
326
|
+
cue.wrapLine = value != 'noWrap';
|
|
327
|
+
break;
|
|
328
|
+
default:
|
|
329
|
+
shaka.log.warning('VTT parser encountered an unsupported style: ',
|
|
330
|
+
lineParts);
|
|
331
|
+
break;
|
|
332
|
+
}
|
|
308
333
|
}
|
|
309
334
|
}
|
|
310
|
-
}
|
|
311
335
|
|
|
312
|
-
|
|
313
|
-
|
|
336
|
+
if (validStyle) {
|
|
337
|
+
styles.set(styleSelector, cue);
|
|
338
|
+
}
|
|
314
339
|
}
|
|
315
340
|
}
|
|
316
341
|
|
|
@@ -337,8 +362,8 @@ shaka.text.VttTextParser = class {
|
|
|
337
362
|
return null;
|
|
338
363
|
}
|
|
339
364
|
|
|
340
|
-
// Skip style blocks.
|
|
341
|
-
if (text[0] == 'STYLE') {
|
|
365
|
+
// Skip style and region blocks.
|
|
366
|
+
if (text[0] == 'STYLE' || text[0] == 'REGION') {
|
|
342
367
|
return null;
|
|
343
368
|
}
|
|
344
369
|
|
|
@@ -366,8 +391,7 @@ shaka.text.VttTextParser = class {
|
|
|
366
391
|
end += timeOffset;
|
|
367
392
|
|
|
368
393
|
// Get the payload.
|
|
369
|
-
const payload =
|
|
370
|
-
text.slice(1).join('\n').trim());
|
|
394
|
+
const payload = text.slice(1).join('\n').trim();
|
|
371
395
|
|
|
372
396
|
let cue = null;
|
|
373
397
|
if (styles.has('global')) {
|
|
@@ -414,6 +438,7 @@ shaka.text.VttTextParser = class {
|
|
|
414
438
|
}
|
|
415
439
|
payload = VttTextParser.replaceColorPayload_(payload);
|
|
416
440
|
payload = VttTextParser.replaceKaraokeStylePayload_(payload);
|
|
441
|
+
payload = VttTextParser.replaceVoiceStylePayload_(payload);
|
|
417
442
|
const xmlPayload = '<span>' + payload + '</span>';
|
|
418
443
|
const element = shaka.util.XmlUtils.parseXmlString(xmlPayload, 'span');
|
|
419
444
|
if (element) {
|
|
@@ -424,7 +449,7 @@ shaka.text.VttTextParser = class {
|
|
|
424
449
|
const childNode = childNodes[0];
|
|
425
450
|
if (childNode.nodeType == Node.TEXT_NODE ||
|
|
426
451
|
childNode.nodeType == Node.CDATA_SECTION_NODE) {
|
|
427
|
-
rootCue.payload = payload;
|
|
452
|
+
rootCue.payload = VttTextParser.htmlUnescape_(payload);
|
|
428
453
|
return;
|
|
429
454
|
}
|
|
430
455
|
}
|
|
@@ -435,8 +460,81 @@ shaka.text.VttTextParser = class {
|
|
|
435
460
|
rootCue.nestedCues = cues;
|
|
436
461
|
} else {
|
|
437
462
|
shaka.log.warning('The cue\'s markup could not be parsed: ', payload);
|
|
438
|
-
rootCue.payload = payload;
|
|
463
|
+
rootCue.payload = VttTextParser.htmlUnescape_(payload);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* Converts voice style tag to be valid for xml parsing
|
|
469
|
+
* For example,
|
|
470
|
+
* input: <v Shaka>Test
|
|
471
|
+
* output: <v.voice-Shaka>Test</v.voice-Shaka>
|
|
472
|
+
*
|
|
473
|
+
* @param {string} payload
|
|
474
|
+
* @return {string} processed payload
|
|
475
|
+
* @private
|
|
476
|
+
*/
|
|
477
|
+
static replaceVoiceStylePayload_(payload) {
|
|
478
|
+
const voiceTag = 'v';
|
|
479
|
+
const names = [];
|
|
480
|
+
let nameStart = -1;
|
|
481
|
+
let newPayload = '';
|
|
482
|
+
let hasVoiceEndTag = false;
|
|
483
|
+
for (let i = 0; i < payload.length; i++) {
|
|
484
|
+
// This condition is used to manage tags that have end tags.
|
|
485
|
+
if (payload[i] === '/') {
|
|
486
|
+
const end = payload.indexOf('>', i);
|
|
487
|
+
if (end === -1) {
|
|
488
|
+
return payload;
|
|
489
|
+
}
|
|
490
|
+
const tagEnd = payload.substring(i + 1, end);
|
|
491
|
+
if (!tagEnd || tagEnd != voiceTag) {
|
|
492
|
+
newPayload += payload[i];
|
|
493
|
+
continue;
|
|
494
|
+
}
|
|
495
|
+
hasVoiceEndTag = true;
|
|
496
|
+
let tagStart = null;
|
|
497
|
+
if (names.length) {
|
|
498
|
+
tagStart = names[names.length -1];
|
|
499
|
+
}
|
|
500
|
+
if (!tagStart) {
|
|
501
|
+
newPayload += payload[i];
|
|
502
|
+
} else if (tagStart === tagEnd) {
|
|
503
|
+
newPayload += '/' + tagEnd + '>';
|
|
504
|
+
i += tagEnd.length + 1;
|
|
505
|
+
} else {
|
|
506
|
+
if (!tagStart.startsWith(voiceTag)) {
|
|
507
|
+
newPayload += payload[i];
|
|
508
|
+
continue;
|
|
509
|
+
}
|
|
510
|
+
newPayload += '/' + tagStart + '>';
|
|
511
|
+
i += tagEnd.length + 1;
|
|
512
|
+
}
|
|
513
|
+
} else {
|
|
514
|
+
// Here we only want the tag name, not any other payload.
|
|
515
|
+
if (payload[i] === '<') {
|
|
516
|
+
nameStart = i + 1;
|
|
517
|
+
if (payload[nameStart] != voiceTag) {
|
|
518
|
+
nameStart = -1;
|
|
519
|
+
}
|
|
520
|
+
} else if (payload[i] === '>') {
|
|
521
|
+
if (nameStart > 0) {
|
|
522
|
+
names.push(payload.substr(nameStart, i - nameStart));
|
|
523
|
+
nameStart = -1;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
newPayload += payload[i];
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
for (const name of names) {
|
|
530
|
+
const newName = name.replace(' ', '.voice-');
|
|
531
|
+
newPayload = newPayload.replace(`<${name}>`, `<${newName}>`);
|
|
532
|
+
newPayload = newPayload.replace(`</${name}>`, `</${newName}>`);
|
|
533
|
+
if (!hasVoiceEndTag) {
|
|
534
|
+
newPayload += `</${newName}>`;
|
|
535
|
+
}
|
|
439
536
|
}
|
|
537
|
+
return newPayload;
|
|
440
538
|
}
|
|
441
539
|
|
|
442
540
|
/**
|
|
@@ -495,28 +593,36 @@ shaka.text.VttTextParser = class {
|
|
|
495
593
|
let nameStart = -1;
|
|
496
594
|
let newPayload = '';
|
|
497
595
|
for (let i = 0; i < payload.length; i++) {
|
|
498
|
-
if (payload[i] === '/') {
|
|
596
|
+
if (payload[i] === '/' && i > 0 && payload[i - 1] === '<') {
|
|
499
597
|
const end = payload.indexOf('>', i);
|
|
500
598
|
if (end <= i) {
|
|
501
599
|
return payload;
|
|
502
600
|
}
|
|
503
601
|
const tagEnd = payload.substring(i + 1, end);
|
|
602
|
+
if (!tagEnd || tagEnd !== 'c') {
|
|
603
|
+
newPayload += payload[i];
|
|
604
|
+
continue;
|
|
605
|
+
}
|
|
504
606
|
const tagStart = names.pop();
|
|
505
|
-
if (!
|
|
506
|
-
|
|
607
|
+
if (!tagStart) {
|
|
608
|
+
newPayload += payload[i];
|
|
507
609
|
} else if (tagStart === tagEnd) {
|
|
508
610
|
newPayload += '/' + tagEnd + '>';
|
|
509
611
|
i += tagEnd.length + 1;
|
|
510
612
|
} else {
|
|
511
|
-
if (!tagStart.startsWith('c.')
|
|
512
|
-
|
|
613
|
+
if (!tagStart.startsWith('c.')) {
|
|
614
|
+
newPayload += payload[i];
|
|
615
|
+
continue;
|
|
513
616
|
}
|
|
514
|
-
newPayload += '/' + tagStart + '>';
|
|
515
617
|
i += tagEnd.length + 1;
|
|
618
|
+
newPayload += '/' + tagStart + '>';
|
|
516
619
|
}
|
|
517
620
|
} else {
|
|
518
621
|
if (payload[i] === '<') {
|
|
519
622
|
nameStart = i + 1;
|
|
623
|
+
if (payload[nameStart] != 'c') {
|
|
624
|
+
nameStart = -1;
|
|
625
|
+
}
|
|
520
626
|
} else if (payload[i] === '>') {
|
|
521
627
|
if (nameStart > 0) {
|
|
522
628
|
names.push(payload.substr(nameStart, i - nameStart));
|
|
@@ -585,12 +691,29 @@ shaka.text.VttTextParser = class {
|
|
|
585
691
|
const bold = shaka.text.Cue.fontWeight.BOLD;
|
|
586
692
|
const italic = shaka.text.Cue.fontStyle.ITALIC;
|
|
587
693
|
const underline = shaka.text.Cue.textDecoration.UNDERLINE;
|
|
588
|
-
const tags = element.nodeName.split(/[ .]+/);
|
|
694
|
+
const tags = element.nodeName.split(/(?=[ .])+/g);
|
|
589
695
|
for (const tag of tags) {
|
|
590
|
-
|
|
591
|
-
|
|
696
|
+
let styleTag = tag;
|
|
697
|
+
// White blanks at start indicate that the style is a voice
|
|
698
|
+
if (styleTag.startsWith('.voice-')) {
|
|
699
|
+
const voice = styleTag.split('-').pop();
|
|
700
|
+
styleTag = `v[voice="${voice}"]`;
|
|
701
|
+
// The specification allows to have quotes and not, so we check to
|
|
702
|
+
// see which one is being used.
|
|
703
|
+
if (!styles.has(styleTag)) {
|
|
704
|
+
styleTag = `v[voice=${voice}]`;
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
if (styles.has(styleTag)) {
|
|
708
|
+
VttTextParser.mergeStyle_(nestedCue, styles.get(styleTag));
|
|
592
709
|
}
|
|
593
710
|
switch (tag) {
|
|
711
|
+
case 'br': {
|
|
712
|
+
const lineBreakCue = rootCue.clone();
|
|
713
|
+
lineBreakCue.lineBreak = true;
|
|
714
|
+
cues.push(lineBreakCue);
|
|
715
|
+
break;
|
|
716
|
+
}
|
|
594
717
|
case 'b':
|
|
595
718
|
nestedCue.fontWeight = bold;
|
|
596
719
|
break;
|
|
@@ -631,7 +754,7 @@ shaka.text.VttTextParser = class {
|
|
|
631
754
|
}
|
|
632
755
|
if (text.length > 0) {
|
|
633
756
|
const textCue = nestedCue.clone();
|
|
634
|
-
textCue.payload = text;
|
|
757
|
+
textCue.payload = VttTextParser.htmlUnescape_(text);
|
|
635
758
|
cues.push(textCue);
|
|
636
759
|
}
|
|
637
760
|
isFirst = false;
|
|
@@ -846,8 +969,8 @@ shaka.text.VttTextParser = class {
|
|
|
846
969
|
}
|
|
847
970
|
|
|
848
971
|
/**
|
|
849
|
-
* This method converts the HTML entities &, <, >, ",
|
|
850
|
-
*
|
|
972
|
+
* This method converts the HTML entities &, <, >, ", ',
|
|
973
|
+
* , ‎ and ‏ in string to their corresponding characters.
|
|
851
974
|
*
|
|
852
975
|
* @param {!string} input
|
|
853
976
|
* @return {string}
|
|
@@ -861,10 +984,13 @@ shaka.text.VttTextParser = class {
|
|
|
861
984
|
'>': '>',
|
|
862
985
|
'"': '"',
|
|
863
986
|
''': '\'',
|
|
987
|
+
' ': '\u{a0}',
|
|
988
|
+
'‎': '\u{200e}',
|
|
989
|
+
'‏': '\u{200f}',
|
|
864
990
|
};
|
|
865
991
|
|
|
866
992
|
// Used to match HTML entities and HTML characters.
|
|
867
|
-
const reEscapedHtml = /&(?:amp|lt|gt|quot|#(0+)?39);/g;
|
|
993
|
+
const reEscapedHtml = /&(?:amp|lt|gt|quot|#(0+)?39|nbsp|lrm|rlm);/g;
|
|
868
994
|
const reHasEscapedHtml = RegExp(reEscapedHtml.source);
|
|
869
995
|
// This check is an optimization, since replace always makes a copy
|
|
870
996
|
if (input && reHasEscapedHtml.test(input)) {
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
goog.provide('shaka.text.WebVttGenerator');
|
|
8
8
|
|
|
9
9
|
goog.require('shaka.text.Cue');
|
|
10
|
+
goog.require('shaka.text.Utils');
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
/**
|
|
@@ -20,45 +21,6 @@ shaka.text.WebVttGenerator = class {
|
|
|
20
21
|
* @return {string}
|
|
21
22
|
*/
|
|
22
23
|
static convert(cues, adCuePoints) {
|
|
23
|
-
// Flatten nested cue payloads recursively. If a cue has nested cues,
|
|
24
|
-
// their contents should be combined and replace the payload of the parent.
|
|
25
|
-
const flattenPayload = (cue) => {
|
|
26
|
-
// Handle styles (currently bold/italics/underline).
|
|
27
|
-
// TODO: add support for color rendering.
|
|
28
|
-
const openStyleTags = [];
|
|
29
|
-
const bold = cue.fontWeight >= shaka.text.Cue.fontWeight.BOLD;
|
|
30
|
-
const italics = cue.fontStyle == shaka.text.Cue.fontStyle.ITALIC;
|
|
31
|
-
const underline = cue.textDecoration.includes(
|
|
32
|
-
shaka.text.Cue.textDecoration.UNDERLINE);
|
|
33
|
-
if (bold) {
|
|
34
|
-
openStyleTags.push('b');
|
|
35
|
-
}
|
|
36
|
-
if (italics) {
|
|
37
|
-
openStyleTags.push('i');
|
|
38
|
-
}
|
|
39
|
-
if (underline) {
|
|
40
|
-
openStyleTags.push('u');
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Prefix opens tags, suffix closes tags in reverse order of opening.
|
|
44
|
-
const prefixStyleTags = openStyleTags.reduce((acc, tag) => {
|
|
45
|
-
return `${acc}<${tag}>`;
|
|
46
|
-
}, '');
|
|
47
|
-
const suffixStyleTags = openStyleTags.reduceRight((acc, tag) => {
|
|
48
|
-
return `${acc}</${tag}>`;
|
|
49
|
-
}, '');
|
|
50
|
-
|
|
51
|
-
if (cue.lineBreak) {
|
|
52
|
-
// This is a vertical lineBreak, so insert a newline.
|
|
53
|
-
return '\n';
|
|
54
|
-
} else if (cue.nestedCues.length) {
|
|
55
|
-
return cue.nestedCues.map(flattenPayload).join('');
|
|
56
|
-
} else {
|
|
57
|
-
// This is a real cue.
|
|
58
|
-
return prefixStyleTags + cue.payload + suffixStyleTags;
|
|
59
|
-
}
|
|
60
|
-
};
|
|
61
|
-
|
|
62
24
|
const webvttTimeString = (time) => {
|
|
63
25
|
let newTime = time;
|
|
64
26
|
for (const adCuePoint of adCuePoints) {
|
|
@@ -78,31 +40,7 @@ shaka.text.WebVttGenerator = class {
|
|
|
78
40
|
milliseconds;
|
|
79
41
|
};
|
|
80
42
|
|
|
81
|
-
|
|
82
|
-
// technically own them. So we build a new array and replace certain items
|
|
83
|
-
// in it if they need to be flattened.
|
|
84
|
-
// We also don't want to flatten the text payloads starting at a container
|
|
85
|
-
// element; otherwise, for containers encapsulating multiple caption lines,
|
|
86
|
-
// the lines would merge into a single cue. This is undesirable when a
|
|
87
|
-
// subset of the captions are outside of the append time window. To fix
|
|
88
|
-
// this, we only call flattenPayload() starting at elements marked as
|
|
89
|
-
// isContainer = false.
|
|
90
|
-
const getCuesToFlatten = (cues, result) => {
|
|
91
|
-
for (const cue of cues) {
|
|
92
|
-
if (cue.isContainer) {
|
|
93
|
-
// Recurse to find the actual text payload cues.
|
|
94
|
-
getCuesToFlatten(cue.nestedCues, result);
|
|
95
|
-
} else {
|
|
96
|
-
// Flatten the payload.
|
|
97
|
-
const flatCue = cue.clone();
|
|
98
|
-
flatCue.nestedCues = [];
|
|
99
|
-
flatCue.payload = flattenPayload(cue);
|
|
100
|
-
result.push(flatCue);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
return result;
|
|
104
|
-
};
|
|
105
|
-
const flattenedCues = getCuesToFlatten(cues, []);
|
|
43
|
+
const flattenedCues = shaka.text.Utils.getCuesToFlatten(cues, []);
|
|
106
44
|
|
|
107
45
|
let webvttString = 'WEBVTT\n\n';
|
|
108
46
|
for (const cue of flattenedCues) {
|
package/lib/util/mime_utils.js
CHANGED
|
@@ -49,7 +49,7 @@ shaka.util.MimeUtils = class {
|
|
|
49
49
|
|
|
50
50
|
if (Transmuxer.isSupported(fullMimeType, contentType)) {
|
|
51
51
|
return shaka.media.Transmuxer.convertCodecs(contentType, fullMimeType);
|
|
52
|
-
} else if (contentType == ContentType.AUDIO) {
|
|
52
|
+
} else if (mimeType != 'video/mp2t' && contentType == ContentType.AUDIO) {
|
|
53
53
|
// video/mp2t is the correct mime type for TS audio, so only replace the
|
|
54
54
|
// word "video" with "audio" for non-TS audio content.
|
|
55
55
|
return fullMimeType.replace('video', 'audio');
|
package/lib/util/platform.js
CHANGED
|
@@ -238,6 +238,14 @@ shaka.util.Platform = class {
|
|
|
238
238
|
return shaka.util.Platform.userAgentContains_('PlayStation 4');
|
|
239
239
|
}
|
|
240
240
|
|
|
241
|
+
/**
|
|
242
|
+
* Check if the current platform is Hisense.
|
|
243
|
+
*/
|
|
244
|
+
static isHisense() {
|
|
245
|
+
return shaka.util.Platform.userAgentContains_('Hisense') ||
|
|
246
|
+
shaka.util.Platform.userAgentContains_('VIDAA');
|
|
247
|
+
}
|
|
248
|
+
|
|
241
249
|
/**
|
|
242
250
|
* Check if the current platform is Virgin Media device.
|
|
243
251
|
*/
|
package/lib/util/stream_utils.js
CHANGED
|
@@ -712,26 +712,38 @@ shaka.util.StreamUtils = class {
|
|
|
712
712
|
}
|
|
713
713
|
|
|
714
714
|
if (audio) {
|
|
715
|
-
|
|
715
|
+
// See: https://github.com/shaka-project/shaka-player/issues/4659
|
|
716
|
+
if (info.audioRobustness != '') {
|
|
717
|
+
if (!keySystemConfig.audio) {
|
|
718
|
+
// KeySystemTrackConfiguration
|
|
719
|
+
keySystemConfig.audio = {
|
|
720
|
+
robustness: info.audioRobustness,
|
|
721
|
+
};
|
|
722
|
+
} else {
|
|
723
|
+
keySystemConfig.audio.robustness =
|
|
724
|
+
keySystemConfig.audio.robustness || info.audioRobustness;
|
|
725
|
+
}
|
|
726
|
+
} else if (!keySystemConfig.audio) {
|
|
716
727
|
// KeySystemTrackConfiguration
|
|
717
|
-
keySystemConfig.audio = {
|
|
718
|
-
robustness: info.audioRobustness,
|
|
719
|
-
};
|
|
720
|
-
} else {
|
|
721
|
-
keySystemConfig.audio.robustness =
|
|
722
|
-
keySystemConfig.audio.robustness || info.audioRobustness;
|
|
728
|
+
keySystemConfig.audio = {};
|
|
723
729
|
}
|
|
724
730
|
}
|
|
725
731
|
|
|
726
732
|
if (video) {
|
|
727
|
-
|
|
733
|
+
// See: https://github.com/shaka-project/shaka-player/issues/4659
|
|
734
|
+
if (info.videoRobustness != '') {
|
|
735
|
+
if (!keySystemConfig.video) {
|
|
736
|
+
// KeySystemTrackConfiguration
|
|
737
|
+
keySystemConfig.video = {
|
|
738
|
+
robustness: info.videoRobustness,
|
|
739
|
+
};
|
|
740
|
+
} else {
|
|
741
|
+
keySystemConfig.video.robustness =
|
|
742
|
+
keySystemConfig.video.robustness || info.videoRobustness;
|
|
743
|
+
}
|
|
744
|
+
} else if (!keySystemConfig.video) {
|
|
728
745
|
// KeySystemTrackConfiguration
|
|
729
|
-
keySystemConfig.video = {
|
|
730
|
-
robustness: info.videoRobustness,
|
|
731
|
-
};
|
|
732
|
-
} else {
|
|
733
|
-
keySystemConfig.video.robustness =
|
|
734
|
-
keySystemConfig.video.robustness || info.videoRobustness;
|
|
746
|
+
keySystemConfig.video = {};
|
|
735
747
|
}
|
|
736
748
|
}
|
|
737
749
|
}
|
package/package.json
CHANGED