shaka-player 3.3.16 → 3.3.18

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.
Files changed (68) hide show
  1. package/.github/workflows/build-and-test.yaml +4 -4
  2. package/.github/workflows/demo-version-index.yaml +6 -0
  3. package/.github/workflows/selenium-lab-tests.yaml +72 -11
  4. package/.github/workflows/update-screenshots.yaml +142 -0
  5. package/CHANGELOG.md +38 -0
  6. package/demo/common/asset.js +15 -0
  7. package/demo/common/assets.js +4 -2
  8. package/demo/common/message_ids.js +1 -0
  9. package/demo/custom.js +19 -0
  10. package/demo/locales/en.json +1 -0
  11. package/demo/locales/source.json +4 -0
  12. package/demo/main.js +14 -0
  13. package/dist/demo.compiled.debug.js +146 -144
  14. package/dist/demo.compiled.debug.map +3 -3
  15. package/dist/demo.compiled.js +146 -144
  16. package/dist/demo.compiled.map +3 -3
  17. package/dist/deps.js +1 -1
  18. package/dist/locales.js +28 -28
  19. package/dist/receiver.compiled.debug.js +15 -15
  20. package/dist/receiver.compiled.debug.map +2 -2
  21. package/dist/receiver.compiled.js +15 -15
  22. package/dist/receiver.compiled.map +2 -2
  23. package/dist/shaka-player.compiled.d.ts +12 -0
  24. package/dist/shaka-player.compiled.debug.d.ts +12 -0
  25. package/dist/shaka-player.compiled.debug.externs.js +4 -0
  26. package/dist/shaka-player.compiled.debug.js +105 -100
  27. package/dist/shaka-player.compiled.debug.map +3 -3
  28. package/dist/shaka-player.compiled.externs.js +4 -0
  29. package/dist/shaka-player.compiled.js +296 -292
  30. package/dist/shaka-player.compiled.map +3 -3
  31. package/dist/shaka-player.ui.d.ts +12 -0
  32. package/dist/shaka-player.ui.debug.d.ts +12 -0
  33. package/dist/shaka-player.ui.debug.externs.js +4 -0
  34. package/dist/shaka-player.ui.debug.js +119 -114
  35. package/dist/shaka-player.ui.debug.map +3 -3
  36. package/dist/shaka-player.ui.externs.js +4 -0
  37. package/dist/shaka-player.ui.js +311 -307
  38. package/dist/shaka-player.ui.map +3 -3
  39. package/docs/tutorials/errors.md +94 -0
  40. package/docs/tutorials/index.json +2 -0
  41. package/docs/tutorials/widevine-service-certs.md +45 -0
  42. package/externs/ima.js +9 -0
  43. package/externs/shaka/text.js +7 -0
  44. package/lib/ads/client_side_ad_manager.js +6 -3
  45. package/lib/dash/segment_template.js +17 -5
  46. package/lib/hls/hls_parser.js +3 -1
  47. package/lib/media/transmuxer.js +13 -7
  48. package/lib/media/video_wrapper.js +3 -3
  49. package/lib/offline/indexeddb/storage_mechanism.js +1 -1
  50. package/lib/player.js +3 -1
  51. package/lib/polyfill/media_capabilities.js +3 -8
  52. package/lib/polyfill/mediasource.js +7 -2
  53. package/lib/text/cue.js +6 -0
  54. package/lib/text/ui_text_displayer.js +2 -5
  55. package/lib/text/vtt_text_parser.js +257 -93
  56. package/lib/util/mime_utils.js +1 -1
  57. package/lib/util/platform.js +8 -0
  58. package/lib/util/stream_utils.js +26 -14
  59. package/package.json +1 -1
  60. package/support.html +1 -0
  61. package/test/dash/dash_parser_manifest_unit.js +0 -36
  62. package/test/polyfill/media_capabilities_unit.js +20 -22
  63. package/test/test/util/layout_tests.js +433 -0
  64. package/test/test/util/util.js +0 -120
  65. package/test/{ui → text}/text_displayer_layout_unit.js +59 -235
  66. package/test/text/ui_text_displayer_unit.js +2 -4
  67. package/test/text/vtt_text_parser_unit.js +151 -6
  68. package/test/text/web_vtt_layout_integration.js +525 -0
@@ -132,14 +132,14 @@ shaka.text.VttTextParser = class {
132
132
  for (const [key, value] of Object.entries(textColor)) {
133
133
  const cue = new shaka.text.Cue(0, 0, '');
134
134
  cue.color = value;
135
- styles.set(key, cue);
135
+ styles.set('.' + key, cue);
136
136
  }
137
137
 
138
138
  const bgColor = shaka.text.Cue.defaultTextBackgroundColor;
139
139
  for (const [key, value] of Object.entries(bgColor)) {
140
140
  const cue = new shaka.text.Cue(0, 0, '');
141
141
  cue.backgroundColor = value;
142
- styles.set(key, cue);
142
+ styles.set('.' + key, cue);
143
143
  }
144
144
  }
145
145
 
@@ -199,92 +199,121 @@ shaka.text.VttTextParser = class {
199
199
  return;
200
200
  }
201
201
 
202
- if (!text[1].includes('::cue')) {
203
- return;
204
- }
205
- let styleSelector = 'global';
206
- // Look for what is within parentisesis. For example:
207
- // <code>:: cue (b) {</code>, what we are looking for is <code>b</code>
208
- const selector = text[1].match(/\((.*)\)/);
209
- if (selector) {
210
- styleSelector = selector.pop();
202
+ /** @type {!Array.<!Array.<string>>} */
203
+ const styleBlocks = [];
204
+ let lastBlockIndex = -1;
205
+ for (let i = 1; i < text.length; i++) {
206
+ if (text[i].includes('::cue')) {
207
+ styleBlocks.push([]);
208
+ lastBlockIndex = styleBlocks.length - 1;
209
+ }
210
+ if (lastBlockIndex == -1) {
211
+ continue;
212
+ }
213
+ styleBlocks[lastBlockIndex].push(text[i]);
214
+ if (text[i].includes('}')) {
215
+ lastBlockIndex = -1;
216
+ }
211
217
  }
212
218
 
213
- // We start at 2 to avoid '::cue' and end earlier to avoid '}'
214
- let propertyLines = text.slice(2, -1);
215
- if (text[1].includes('}')) {
216
- const payload = /\{(.*?)\}/.exec(text[1]);
217
- if (payload) {
218
- propertyLines = payload[1].split(';');
219
+ for (const styleBlock of styleBlocks) {
220
+ let styleSelector = 'global';
221
+ // Look for what is within parentheses. For example:
222
+ // <code>:: cue (b) {</code>, what we are looking for is <code>b</code>
223
+ const selector = styleBlock[0].match(/\((.*)\)/);
224
+ if (selector) {
225
+ styleSelector = selector.pop();
219
226
  }
220
- }
221
227
 
222
- const cue = new shaka.text.Cue(0, 0, '');
223
- let validStyle = false;
224
- for (let i = 0; i < propertyLines.length; i++) {
225
- // We look for CSS properties. As a general rule they are separated by
226
- // <code>:</code>. Eg: <code>color: red;</code>
227
- const lineParts = /^\s*([^:]+):\s*(.*)/.exec(propertyLines[i]);
228
- if (lineParts) {
229
- const name = lineParts[1].trim();
230
- const value = lineParts[2].trim().replace(';', '');
231
- switch (name) {
232
- case 'background-color':
233
- validStyle = true;
234
- cue.backgroundColor = value;
235
- break;
236
- case 'color':
237
- validStyle = true;
238
- cue.color = value;
239
- break;
240
- case 'font-family':
241
- validStyle = true;
242
- cue.fontFamily = value;
243
- break;
244
- case 'font-size':
245
- validStyle = true;
246
- cue.fontSize = value;
247
- break;
248
- case 'font-weight':
249
- if (parseInt(value, 10) >= 700) {
228
+ // We start at 1 to avoid '::cue' and end earlier to avoid '}'
229
+ let propertyLines = styleBlock.slice(1, -1);
230
+ if (styleBlock[0].includes('}')) {
231
+ const payload = /\{(.*?)\}/.exec(styleBlock[0]);
232
+ if (payload) {
233
+ propertyLines = payload[1].split(';');
234
+ }
235
+ }
236
+
237
+ // Continue styles over multiple selectors if necessary.
238
+ // For example,
239
+ // ::cue(b) { background: white; } ::cue(b) { color: blue; }
240
+ // should set both the background and foreground of bold tags.
241
+ let cue = styles.get(styleSelector);
242
+ if (!cue) {
243
+ cue = new shaka.text.Cue(0, 0, '');
244
+ }
245
+
246
+ let validStyle = false;
247
+ for (let i = 0; i < propertyLines.length; i++) {
248
+ // We look for CSS properties. As a general rule they are separated by
249
+ // <code>:</code>. Eg: <code>color: red;</code>
250
+ const lineParts = /^\s*([^:]+):\s*(.*)/.exec(propertyLines[i]);
251
+ if (lineParts) {
252
+ const name = lineParts[1].trim();
253
+ const value = lineParts[2].trim().replace(';', '');
254
+ switch (name) {
255
+ case 'background-color':
256
+ case 'background':
250
257
  validStyle = true;
251
- cue.fontWeight = shaka.text.Cue.fontWeight.BOLD;
252
- }
253
- break;
254
- case 'font-style':
255
- switch (value) {
256
- case 'normal':
257
- validStyle = true;
258
- cue.fontStyle = shaka.text.Cue.fontStyle.NORMAL;
259
- break;
260
- case 'italic':
261
- validStyle = true;
262
- cue.fontStyle = shaka.text.Cue.fontStyle.ITALIC;
263
- break;
264
- case 'oblique':
258
+ cue.backgroundColor = value;
259
+ break;
260
+ case 'color':
261
+ validStyle = true;
262
+ cue.color = value;
263
+ break;
264
+ case 'font-family':
265
+ validStyle = true;
266
+ cue.fontFamily = value;
267
+ break;
268
+ case 'font-size':
269
+ validStyle = true;
270
+ cue.fontSize = value;
271
+ break;
272
+ case 'font-weight':
273
+ if (parseInt(value, 10) >= 700 || value == 'bold') {
265
274
  validStyle = true;
266
- cue.fontStyle = shaka.text.Cue.fontStyle.OBLIQUE;
267
- break;
268
- }
269
- break;
270
- case 'opacity':
271
- validStyle = true;
272
- cue.opacity = parseFloat(value);
273
- break;
274
- case 'white-space':
275
- validStyle = true;
276
- cue.wrapLine = value != 'noWrap';
277
- break;
278
- default:
279
- shaka.log.warning('VTT parser encountered an unsupported style: ',
280
- lineParts);
281
- break;
275
+ cue.fontWeight = shaka.text.Cue.fontWeight.BOLD;
276
+ }
277
+ break;
278
+ case 'font-style':
279
+ switch (value) {
280
+ case 'normal':
281
+ validStyle = true;
282
+ cue.fontStyle = shaka.text.Cue.fontStyle.NORMAL;
283
+ break;
284
+ case 'italic':
285
+ validStyle = true;
286
+ cue.fontStyle = shaka.text.Cue.fontStyle.ITALIC;
287
+ break;
288
+ case 'oblique':
289
+ validStyle = true;
290
+ cue.fontStyle = shaka.text.Cue.fontStyle.OBLIQUE;
291
+ break;
292
+ }
293
+ break;
294
+ case 'opacity':
295
+ validStyle = true;
296
+ cue.opacity = parseFloat(value);
297
+ break;
298
+ case 'text-shadow':
299
+ validStyle = true;
300
+ cue.textShadow = value;
301
+ break;
302
+ case 'white-space':
303
+ validStyle = true;
304
+ cue.wrapLine = value != 'noWrap';
305
+ break;
306
+ default:
307
+ shaka.log.warning('VTT parser encountered an unsupported style: ',
308
+ lineParts);
309
+ break;
310
+ }
282
311
  }
283
312
  }
284
- }
285
313
 
286
- if (validStyle) {
287
- styles.set(styleSelector, cue);
314
+ if (validStyle) {
315
+ styles.set(styleSelector, cue);
316
+ }
288
317
  }
289
318
  }
290
319
 
@@ -311,8 +340,8 @@ shaka.text.VttTextParser = class {
311
340
  return null;
312
341
  }
313
342
 
314
- // Skip style blocks.
315
- if (text[0] == 'STYLE') {
343
+ // Skip style and region blocks.
344
+ if (text[0] == 'STYLE' || text[0] == 'REGION') {
316
345
  return null;
317
346
  }
318
347
 
@@ -386,6 +415,7 @@ shaka.text.VttTextParser = class {
386
415
  VttTextParser.addDefaultTextColor_(styles);
387
416
  }
388
417
  payload = VttTextParser.replaceColorPayload_(payload);
418
+ payload = VttTextParser.replaceVoiceStylePayload_(payload);
389
419
  const xmlPayload = '<span>' + payload + '</span>';
390
420
  const element = shaka.util.XmlUtils.parseXmlString(xmlPayload, 'span');
391
421
  if (element) {
@@ -396,7 +426,7 @@ shaka.text.VttTextParser = class {
396
426
  const childNode = childNodes[0];
397
427
  if (childNode.nodeType == Node.TEXT_NODE ||
398
428
  childNode.nodeType == Node.CDATA_SECTION_NODE) {
399
- rootCue.payload = payload;
429
+ rootCue.payload = VttTextParser.htmlUnescape_(payload);
400
430
  return;
401
431
  }
402
432
  }
@@ -407,8 +437,81 @@ shaka.text.VttTextParser = class {
407
437
  rootCue.nestedCues = cues;
408
438
  } else {
409
439
  shaka.log.warning('The cue\'s markup could not be parsed: ', payload);
410
- rootCue.payload = payload;
440
+ rootCue.payload = VttTextParser.htmlUnescape_(payload);
441
+ }
442
+ }
443
+
444
+ /**
445
+ * Converts voice style tag to be valid for xml parsing
446
+ * For example,
447
+ * input: <v Shaka>Test
448
+ * output: <v.voice-Shaka>Test</v.voice-Shaka>
449
+ *
450
+ * @param {string} payload
451
+ * @return {string} processed payload
452
+ * @private
453
+ */
454
+ static replaceVoiceStylePayload_(payload) {
455
+ const voiceTag = 'v';
456
+ const names = [];
457
+ let nameStart = -1;
458
+ let newPayload = '';
459
+ let hasVoiceEndTag = false;
460
+ for (let i = 0; i < payload.length; i++) {
461
+ // This condition is used to manage tags that have end tags.
462
+ if (payload[i] === '/') {
463
+ const end = payload.indexOf('>', i);
464
+ if (end === -1) {
465
+ return payload;
466
+ }
467
+ const tagEnd = payload.substring(i + 1, end);
468
+ if (!tagEnd || tagEnd != voiceTag) {
469
+ newPayload += payload[i];
470
+ continue;
471
+ }
472
+ hasVoiceEndTag = true;
473
+ let tagStart = null;
474
+ if (names.length) {
475
+ tagStart = names[names.length -1];
476
+ }
477
+ if (!tagStart) {
478
+ newPayload += payload[i];
479
+ } else if (tagStart === tagEnd) {
480
+ newPayload += '/' + tagEnd + '>';
481
+ i += tagEnd.length + 1;
482
+ } else {
483
+ if (!tagStart.startsWith(voiceTag)) {
484
+ newPayload += payload[i];
485
+ continue;
486
+ }
487
+ newPayload += '/' + tagStart + '>';
488
+ i += tagEnd.length + 1;
489
+ }
490
+ } else {
491
+ // Here we only want the tag name, not any other payload.
492
+ if (payload[i] === '<') {
493
+ nameStart = i + 1;
494
+ if (payload[nameStart] != voiceTag) {
495
+ nameStart = -1;
496
+ }
497
+ } else if (payload[i] === '>') {
498
+ if (nameStart > 0) {
499
+ names.push(payload.substr(nameStart, i - nameStart));
500
+ nameStart = -1;
501
+ }
502
+ }
503
+ newPayload += payload[i];
504
+ }
411
505
  }
506
+ for (const name of names) {
507
+ const newName = name.replace(' ', '.voice-');
508
+ newPayload = newPayload.replace(`<${name}>`, `<${newName}>`);
509
+ newPayload = newPayload.replace(`</${name}>`, `</${newName}>`);
510
+ if (!hasVoiceEndTag) {
511
+ newPayload += `</${newName}>`;
512
+ }
513
+ }
514
+ return newPayload;
412
515
  }
413
516
 
414
517
  /**
@@ -429,28 +532,36 @@ shaka.text.VttTextParser = class {
429
532
  let nameStart = -1;
430
533
  let newPayload = '';
431
534
  for (let i = 0; i < payload.length; i++) {
432
- if (payload[i] === '/') {
535
+ if (payload[i] === '/' && i > 0 && payload[i - 1] === '<') {
433
536
  const end = payload.indexOf('>', i);
434
537
  if (end <= i) {
435
538
  return payload;
436
539
  }
437
540
  const tagEnd = payload.substring(i + 1, end);
541
+ if (!tagEnd || tagEnd !== 'c') {
542
+ newPayload += payload[i];
543
+ continue;
544
+ }
438
545
  const tagStart = names.pop();
439
- if (!tagEnd || !tagStart) {
440
- return payload;
546
+ if (!tagStart) {
547
+ newPayload += payload[i];
441
548
  } else if (tagStart === tagEnd) {
442
549
  newPayload += '/' + tagEnd + '>';
443
550
  i += tagEnd.length + 1;
444
551
  } else {
445
- if (!tagStart.startsWith('c.') || tagEnd !== 'c') {
446
- return payload;
552
+ if (!tagStart.startsWith('c.')) {
553
+ newPayload += payload[i];
554
+ continue;
447
555
  }
448
- newPayload += '/' + tagStart + '>';
449
556
  i += tagEnd.length + 1;
557
+ newPayload += '/' + tagStart + '>';
450
558
  }
451
559
  } else {
452
560
  if (payload[i] === '<') {
453
561
  nameStart = i + 1;
562
+ if (payload[nameStart] != 'c') {
563
+ nameStart = -1;
564
+ }
454
565
  } else if (payload[i] === '>') {
455
566
  if (nameStart > 0) {
456
567
  names.push(payload.substr(nameStart, i - nameStart));
@@ -519,12 +630,29 @@ shaka.text.VttTextParser = class {
519
630
  const bold = shaka.text.Cue.fontWeight.BOLD;
520
631
  const italic = shaka.text.Cue.fontStyle.ITALIC;
521
632
  const underline = shaka.text.Cue.textDecoration.UNDERLINE;
522
- const tags = element.nodeName.split(/[ .]+/);
633
+ const tags = element.nodeName.split(/(?=[ .])+/g);
523
634
  for (const tag of tags) {
524
- if (styles.has(tag)) {
525
- VttTextParser.mergeStyle_(nestedCue, styles.get(tag));
635
+ let styleTag = tag;
636
+ // White blanks at start indicate that the style is a voice
637
+ if (styleTag.startsWith('.voice-')) {
638
+ const voice = styleTag.split('-').pop();
639
+ styleTag = `v[voice="${voice}"]`;
640
+ // The specification allows to have quotes and not, so we check to
641
+ // see which one is being used.
642
+ if (!styles.has(styleTag)) {
643
+ styleTag = `v[voice=${voice}]`;
644
+ }
645
+ }
646
+ if (styles.has(styleTag)) {
647
+ VttTextParser.mergeStyle_(nestedCue, styles.get(styleTag));
526
648
  }
527
649
  switch (tag) {
650
+ case 'br': {
651
+ const lineBreakCue = rootCue.clone();
652
+ lineBreakCue.lineBreak = true;
653
+ cues.push(lineBreakCue);
654
+ break;
655
+ }
528
656
  case 'b':
529
657
  nestedCue.fontWeight = bold;
530
658
  break;
@@ -553,7 +681,7 @@ shaka.text.VttTextParser = class {
553
681
  }
554
682
  if (text.length > 0) {
555
683
  const textCue = nestedCue.clone();
556
- textCue.payload = text;
684
+ textCue.payload = VttTextParser.htmlUnescape_(text);
557
685
  cues.push(textCue);
558
686
  }
559
687
  isFirst = false;
@@ -769,6 +897,42 @@ shaka.text.VttTextParser = class {
769
897
 
770
898
  return (milliseconds / 1000) + seconds + (minutes * 60) + (hours * 3600);
771
899
  }
900
+
901
+ /**
902
+ * This method converts the HTML entities &amp;, &lt;, &gt;, &quot;, &#39;,
903
+ * &nbsp;, &lrm; and &rlm; in string to their corresponding characters.
904
+ *
905
+ * @param {!string} input
906
+ * @return {string}
907
+ * @private
908
+ */
909
+ static htmlUnescape_(input) {
910
+ // Used to map HTML entities to characters.
911
+ const htmlUnescapes = {
912
+ '&amp;': '&',
913
+ '&lt;': '<',
914
+ '&gt;': '>',
915
+ '&quot;': '"',
916
+ '&#39;': '\'',
917
+ '&nbsp;': '\u{a0}',
918
+ '&lrm;': '\u{200e}',
919
+ '&rlm;': '\u{200f}',
920
+ };
921
+
922
+ // Used to match HTML entities and HTML characters.
923
+ const reEscapedHtml = /&(?:amp|lt|gt|quot|#(0+)?39|nbsp|lrm|rlm);/g;
924
+ const reHasEscapedHtml = RegExp(reEscapedHtml.source);
925
+ // This check is an optimization, since replace always makes a copy
926
+ if (input && reHasEscapedHtml.test(input)) {
927
+ return input.replace(reEscapedHtml, (entity) => {
928
+ // The only thing that might not match the dictionary above is the
929
+ // single quote, which can be matched by many strings in the regex, but
930
+ // only has a single entry in the dictionary.
931
+ return htmlUnescapes[entity] || '\'';
932
+ });
933
+ }
934
+ return input || '';
935
+ }
772
936
  };
773
937
 
774
938
  /**
@@ -50,7 +50,7 @@ shaka.util.MimeUtils = class {
50
50
  return shaka.media.Transmuxer.convertTsCodecs(
51
51
  contentType, fullMimeType);
52
52
  }
53
- } else if (contentType == ContentType.AUDIO) {
53
+ } else if (mimeType != 'video/mp2t' && contentType == ContentType.AUDIO) {
54
54
  // video/mp2t is the correct mime type for TS audio, so only replace the
55
55
  // word "video" with "audio" for non-TS audio content.
56
56
  return fullMimeType.replace('video', 'audio');
@@ -248,6 +248,14 @@ shaka.util.Platform = class {
248
248
  return shaka.util.Platform.userAgentContains_('PlayStation 4');
249
249
  }
250
250
 
251
+ /**
252
+ * Check if the current platform is Hisense.
253
+ */
254
+ static isHisense() {
255
+ return shaka.util.Platform.userAgentContains_('Hisense') ||
256
+ shaka.util.Platform.userAgentContains_('VIDAA');
257
+ }
258
+
251
259
  /**
252
260
  * Check if the current platform is Virgin Media device.
253
261
  */
@@ -703,26 +703,38 @@ shaka.util.StreamUtils = class {
703
703
  }
704
704
 
705
705
  if (audio) {
706
- if (!keySystemConfig.audio) {
706
+ // See: https://github.com/shaka-project/shaka-player/issues/4659
707
+ if (info.audioRobustness != '') {
708
+ if (!keySystemConfig.audio) {
709
+ // KeySystemTrackConfiguration
710
+ keySystemConfig.audio = {
711
+ robustness: info.audioRobustness,
712
+ };
713
+ } else {
714
+ keySystemConfig.audio.robustness =
715
+ keySystemConfig.audio.robustness || info.audioRobustness;
716
+ }
717
+ } else if (!keySystemConfig.audio) {
707
718
  // KeySystemTrackConfiguration
708
- keySystemConfig.audio = {
709
- robustness: info.audioRobustness,
710
- };
711
- } else {
712
- keySystemConfig.audio.robustness =
713
- keySystemConfig.audio.robustness || info.audioRobustness;
719
+ keySystemConfig.audio = {};
714
720
  }
715
721
  }
716
722
 
717
723
  if (video) {
718
- if (!keySystemConfig.video) {
724
+ // See: https://github.com/shaka-project/shaka-player/issues/4659
725
+ if (info.videoRobustness != '') {
726
+ if (!keySystemConfig.video) {
727
+ // KeySystemTrackConfiguration
728
+ keySystemConfig.video = {
729
+ robustness: info.videoRobustness,
730
+ };
731
+ } else {
732
+ keySystemConfig.video.robustness =
733
+ keySystemConfig.video.robustness || info.videoRobustness;
734
+ }
735
+ } else if (!keySystemConfig.video) {
719
736
  // KeySystemTrackConfiguration
720
- keySystemConfig.video = {
721
- robustness: info.videoRobustness,
722
- };
723
- } else {
724
- keySystemConfig.video.robustness =
725
- keySystemConfig.video.robustness || info.videoRobustness;
737
+ keySystemConfig.video = {};
726
738
  }
727
739
  }
728
740
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "shaka-player",
3
3
  "description": "DASH/EME video player library",
4
- "version": "3.3.16",
4
+ "version": "3.3.18",
5
5
  "homepage": "https://github.com/shaka-project/shaka-player",
6
6
  "author": "Google",
7
7
  "maintainers": [
package/support.html CHANGED
@@ -38,6 +38,7 @@
38
38
  }
39
39
 
40
40
  </style>
41
+ <script defer src="node_modules/mux.js/dist/mux.min.js"></script>
41
42
  <script src="dist/shaka-player.compiled.js"></script>
42
43
  <script>
43
44
  function whenLoaded(fn) {
@@ -194,42 +194,6 @@ describe('DashParser Manifest', () => {
194
194
  }));
195
195
  });
196
196
 
197
- it('rejects periods after one without duration', async () => {
198
- const periodContents = [
199
- ' <AdaptationSet mimeType="video/mp4" lang="en" group="1">',
200
- ' <Representation bandwidth="100">',
201
- ' <SegmentTemplate startNumber="1" media="l-$Number$.mp4">',
202
- ' <SegmentTimeline>',
203
- ' <S t="0" d="10" />',
204
- ' </SegmentTimeline>',
205
- ' </SegmentTemplate>',
206
- ' </Representation>',
207
- ' </AdaptationSet>',
208
- ].join('\n');
209
- const template = [
210
- '<MPD mediaPresentationDuration="PT75S">',
211
- ' <Period id="1">',
212
- '%(periodContents)s',
213
- ' </Period>',
214
- ' <Period id="2">',
215
- '%(periodContents)s',
216
- ' </Period>',
217
- '</MPD>',
218
- ].join('\n');
219
- const source = sprintf(template, {periodContents: periodContents});
220
-
221
- fakeNetEngine.setResponseText('dummy://foo', source);
222
- /** @type {shaka.extern.Manifest} */
223
- const manifest = await parser.start('dummy://foo', playerInterface);
224
- const video = manifest.variants[0].video;
225
- await video.createSegmentIndex();
226
-
227
- // The first period has a segment from 0-10.
228
- // With the second period skipping, we should fail to find a segment at 10.
229
- expect(video.segmentIndex.find(0)).not.toBe(null);
230
- expect(video.segmentIndex.find(10)).toBe(null);
231
- });
232
-
233
197
  it('calculates Period times when missing', async () => {
234
198
  const periodContents = [
235
199
  ' <AdaptationSet mimeType="video/mp4" lang="en" group="1">',
@@ -5,7 +5,6 @@
5
5
  */
6
6
 
7
7
  describe('MediaCapabilities', () => {
8
- const Util = shaka.test.Util;
9
8
  const originalCast = window['cast'];
10
9
  const originalVendor = navigator.vendor;
11
10
  const originalUserAgent = navigator.userAgent;
@@ -168,30 +167,29 @@ describe('MediaCapabilities', () => {
168
167
  expect(result.keySystemAccess).toEqual(mockResult);
169
168
  });
170
169
 
171
- it('throws when the cast namespace is not available', async () => {
172
- // Temporarily remove window.cast to trigger error. It's restored after
173
- // every test.
174
- delete window['cast'];
170
+ it('falls back to isTypeSupported() when cast namespace is not available',
171
+ async () => {
172
+ // Temporarily remove window.cast to trigger error. It's restored
173
+ // after every test.
174
+ delete window['cast'];
175
175
 
176
- const isChromecastSpy =
177
- spyOn(shaka['util']['Platform'],
178
- 'isChromecast').and.returnValue(true);
179
- const expected = Util.jasmineError(new shaka.util.Error(
180
- shaka.util.Error.Severity.CRITICAL,
181
- shaka.util.Error.Category.CAST,
182
- shaka.util.Error.Code.CAST_API_UNAVAILABLE));
183
- const isTypeSupportedSpy =
184
- spyOn(window['MediaSource'], 'isTypeSupported').and.returnValue(true);
176
+ const isChromecastSpy =
177
+ spyOn(shaka['util']['Platform'],
178
+ 'isChromecast').and.returnValue(true);
179
+ const isTypeSupportedSpy =
180
+ spyOn(window['MediaSource'], 'isTypeSupported')
181
+ .and.returnValue(true);
185
182
 
186
- shaka.polyfill.MediaCapabilities.install();
187
- await expectAsync(
188
- navigator.mediaCapabilities.decodingInfo(mockDecodingConfig))
189
- .toBeRejectedWith(expected);
183
+ shaka.polyfill.MediaCapabilities.install();
184
+ await navigator.mediaCapabilities.decodingInfo(mockDecodingConfig);
190
185
 
191
- expect(isTypeSupportedSpy).not.toHaveBeenCalled();
192
- // 1 (during install()) + 1 (for video config check).
193
- expect(isChromecastSpy).toHaveBeenCalledTimes(2);
194
- });
186
+ expect(mockCanDisplayType).not.toHaveBeenCalled();
187
+ // 1 (during install()) + 1 (for video config check).
188
+ expect(isChromecastSpy).toHaveBeenCalledTimes(2);
189
+ // 1 (fallback in canCastDisplayType()) +
190
+ // 1 (mockDecodingConfig.audio).
191
+ expect(isTypeSupportedSpy).toHaveBeenCalledTimes(2);
192
+ });
195
193
 
196
194
  it('falls back to isTypeSupported() when canDisplayType() missing',
197
195
  async () => {