shaka-player 4.10.13 → 4.11.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.
Files changed (91) hide show
  1. package/README.md +5 -1
  2. package/dist/controls.css +3 -3
  3. package/dist/controls.css.map +1 -1
  4. package/dist/deps.js +11 -11
  5. package/dist/locales.js +38 -34
  6. package/dist/shaka-player.compiled.d.ts +68 -33
  7. package/dist/shaka-player.compiled.debug.d.ts +68 -33
  8. package/dist/shaka-player.compiled.debug.externs.js +584 -544
  9. package/dist/shaka-player.compiled.debug.js +715 -676
  10. package/dist/shaka-player.compiled.debug.map +4 -4
  11. package/dist/shaka-player.compiled.externs.js +584 -544
  12. package/dist/shaka-player.compiled.js +1394 -1371
  13. package/dist/shaka-player.compiled.map +4 -4
  14. package/dist/shaka-player.ui.d.ts +76 -34
  15. package/dist/shaka-player.ui.debug.d.ts +76 -34
  16. package/dist/shaka-player.ui.debug.externs.js +77 -26
  17. package/dist/shaka-player.ui.debug.js +774 -720
  18. package/dist/shaka-player.ui.debug.map +4 -4
  19. package/dist/shaka-player.ui.externs.js +77 -26
  20. package/dist/shaka-player.ui.js +1649 -1617
  21. package/dist/shaka-player.ui.map +4 -4
  22. package/externs/mediasession.js +10 -1
  23. package/externs/shaka/ads.js +79 -2
  24. package/externs/shaka/manifest.js +6 -1
  25. package/externs/shaka/manifest_parser.js +6 -2
  26. package/externs/shaka/net.js +23 -2
  27. package/externs/shaka/player.js +414 -103
  28. package/externs/shaka/text.js +2 -1
  29. package/lib/abr/simple_abr_manager.js +5 -0
  30. package/lib/ads/ad_manager.js +45 -1
  31. package/lib/ads/ad_utils.js +124 -1
  32. package/lib/ads/interstitial_ad.js +9 -2
  33. package/lib/ads/interstitial_ad_manager.js +216 -101
  34. package/lib/ads/media_tailor_ad.js +1 -39
  35. package/lib/ads/media_tailor_ad_manager.js +32 -16
  36. package/lib/cea/cea608_data_channel.js +40 -8
  37. package/lib/cea/cea608_memory.js +28 -0
  38. package/lib/dash/dash_parser.js +162 -25
  39. package/lib/dash/segment_base.js +3 -2
  40. package/lib/dash/segment_list.js +6 -3
  41. package/lib/dash/segment_template.js +104 -20
  42. package/lib/hls/hls_parser.js +319 -215
  43. package/lib/media/drm_engine.js +11 -3
  44. package/lib/media/media_source_engine.js +0 -8
  45. package/lib/media/preload_manager.js +16 -4
  46. package/lib/media/segment_reference.js +3 -0
  47. package/lib/media/segment_utils.js +8 -1
  48. package/lib/media/streaming_engine.js +10 -6
  49. package/lib/mss/mss_parser.js +14 -2
  50. package/lib/net/http_fetch_plugin.js +12 -5
  51. package/lib/net/http_xhr_plugin.js +7 -3
  52. package/lib/net/networking_engine.js +21 -1
  53. package/lib/offline/manifest_converter.js +1 -0
  54. package/lib/offline/storage.js +1 -0
  55. package/lib/player.js +336 -121
  56. package/lib/text/cue.js +373 -0
  57. package/lib/text/mp4_ttml_parser.js +51 -1
  58. package/lib/text/mp4_vtt_parser.js +2 -5
  59. package/lib/text/sbv_text_parser.js +2 -31
  60. package/lib/text/srt_text_parser.js +1 -1
  61. package/lib/text/text_engine.js +1 -1
  62. package/lib/text/text_utils.js +1 -0
  63. package/lib/text/ttml_text_parser.js +14 -4
  64. package/lib/text/ui_text_displayer.js +0 -4
  65. package/lib/text/vtt_text_parser.js +10 -415
  66. package/lib/util/abortable_operation.js +8 -0
  67. package/lib/util/cmcd_manager.js +38 -0
  68. package/lib/util/error.js +16 -0
  69. package/lib/util/fake_event.js +1 -0
  70. package/lib/util/manifest_parser_utils.js +12 -3
  71. package/lib/util/player_configuration.js +4 -2
  72. package/lib/util/stream_utils.js +57 -2
  73. package/lib/util/tXml.js +2 -1
  74. package/lib/util/text_parser.js +46 -0
  75. package/lib/util/ts_parser.js +32 -3
  76. package/package.json +1 -1
  77. package/shaka-player.uncompiled.js +1 -1
  78. package/ui/chapter_selection.js +179 -0
  79. package/ui/controls.js +151 -0
  80. package/ui/enums.js +1 -0
  81. package/ui/externs/ui.js +7 -1
  82. package/ui/less/containers.less +3 -4
  83. package/ui/less/overflow_menu.less +5 -0
  84. package/ui/less/tooltip.less +1 -1
  85. package/ui/locales/en.json +1 -0
  86. package/ui/locales/es.json +1 -0
  87. package/ui/locales/fr.json +1 -0
  88. package/ui/locales/source.json +4 -0
  89. package/ui/seek_bar.js +3 -2
  90. package/ui/ui.js +2 -0
  91. package/lib/polyfill/abort_controller.js +0 -158
@@ -15,7 +15,6 @@ goog.require('shaka.text.TextEngine');
15
15
  goog.require('shaka.util.Error');
16
16
  goog.require('shaka.util.StringUtils');
17
17
  goog.require('shaka.util.TextParser');
18
- goog.require('shaka.util.TXml');
19
18
 
20
19
 
21
20
  /**
@@ -106,9 +105,9 @@ shaka.text.VttTextParser = class {
106
105
  }
107
106
  }
108
107
 
109
- /** @type {!Map.<string, shaka.text.Cue>} */
108
+ /** @type {!Map.<string, !shaka.text.Cue>} */
110
109
  const styles = new Map();
111
- VttTextParser.addDefaultTextColor_(styles);
110
+ shaka.text.Cue.addDefaultTextColor(styles);
112
111
 
113
112
  // Parse cues.
114
113
  const ret = [];
@@ -151,8 +150,7 @@ shaka.text.VttTextParser = class {
151
150
  shaka.util.Error.Code.INVALID_TEXT_HEADER);
152
151
  }
153
152
 
154
- const parser = new shaka.util.TextParser(cueTimeMatch[1]);
155
- const cueTime = shaka.text.VttTextParser.parseTime_(parser);
153
+ const cueTime = shaka.util.TextParser.parseTime(cueTimeMatch[1]);
156
154
  if (cueTime == null) {
157
155
  throw new shaka.util.Error(
158
156
  shaka.util.Error.Severity.CRITICAL,
@@ -174,28 +172,6 @@ shaka.text.VttTextParser = class {
174
172
  return time.periodStart + mpegTime / mpegTimescale - cueTime;
175
173
  }
176
174
 
177
- /**
178
- * Add default color
179
- *
180
- * @param {!Map.<string, shaka.text.Cue>} styles
181
- * @private
182
- */
183
- static addDefaultTextColor_(styles) {
184
- const textColor = shaka.text.Cue.defaultTextColor;
185
- for (const [key, value] of Object.entries(textColor)) {
186
- const cue = new shaka.text.Cue(0, 0, '');
187
- cue.color = value;
188
- styles.set('.' + key, cue);
189
- }
190
-
191
- const bgColor = shaka.text.Cue.defaultTextBackgroundColor;
192
- for (const [key, value] of Object.entries(bgColor)) {
193
- const cue = new shaka.text.Cue(0, 0, '');
194
- cue.backgroundColor = value;
195
- styles.set('.' + key, cue);
196
- }
197
- }
198
-
199
175
  /**
200
176
  * Parses a string into a Region object.
201
177
  *
@@ -233,7 +209,7 @@ shaka.text.VttTextParser = class {
233
209
  * Parses a style block into a Cue object.
234
210
  *
235
211
  * @param {!Array.<string>} text
236
- * @param {!Map.<string, shaka.text.Cue>} styles
212
+ * @param {!Map.<string, !shaka.text.Cue>} styles
237
213
  * @private
238
214
  */
239
215
  static parseStyle_(text, styles) {
@@ -380,7 +356,7 @@ shaka.text.VttTextParser = class {
380
356
  * @param {!Array.<string>} text
381
357
  * @param {number} timeOffset
382
358
  * @param {!Array.<!shaka.text.CueRegion>} regions
383
- * @param {!Map.<string, shaka.text.Cue>} styles
359
+ * @param {!Map.<string, !shaka.text.Cue>} styles
384
360
  * @return {shaka.text.Cue}
385
361
  * @private
386
362
  */
@@ -410,9 +386,9 @@ shaka.text.VttTextParser = class {
410
386
 
411
387
  // Parse the times.
412
388
  const parser = new shaka.util.TextParser(text[0]);
413
- let start = VttTextParser.parseTime_(parser);
389
+ let start = parser.parseTime();
414
390
  const expect = parser.readRegex(/[ \t]+-->[ \t]+/g);
415
- let end = VttTextParser.parseTime_(parser);
391
+ let end = parser.parseTime();
416
392
 
417
393
  if (start == null || expect == null || end == null) {
418
394
  shaka.log.alwaysWarn(
@@ -431,9 +407,9 @@ shaka.text.VttTextParser = class {
431
407
  cue = styles.get('global').clone();
432
408
  cue.startTime = start;
433
409
  cue.endTime = end;
434
- cue.payload = '';
410
+ cue.payload = payload;
435
411
  } else {
436
- cue = new shaka.text.Cue(start, end, '');
412
+ cue = new shaka.text.Cue(start, end, payload);
437
413
  }
438
414
 
439
415
  // Parse optional settings.
@@ -449,7 +425,7 @@ shaka.text.VttTextParser = class {
449
425
  word = parser.readWord();
450
426
  }
451
427
 
452
- VttTextParser.parseCueStyles(payload, cue, styles);
428
+ shaka.text.Cue.parseCuePayload(cue, styles);
453
429
 
454
430
  if (id != null) {
455
431
  cue.id = id;
@@ -457,353 +433,6 @@ shaka.text.VttTextParser = class {
457
433
  return cue;
458
434
  }
459
435
 
460
- /**
461
- * Parses a WebVTT styles from the given payload.
462
- *
463
- * @param {string} payload
464
- * @param {!shaka.text.Cue} rootCue
465
- * @param {!Map.<string, shaka.text.Cue>} styles
466
- */
467
- static parseCueStyles(payload, rootCue, styles) {
468
- const VttTextParser = shaka.text.VttTextParser;
469
- const StringUtils = shaka.util.StringUtils;
470
- const TXml = shaka.util.TXml;
471
- // Optimization for unstyled payloads.
472
- if (!payload.includes('<')) {
473
- rootCue.payload = StringUtils.htmlUnescape(payload);
474
- return;
475
- }
476
- if (styles.size === 0) {
477
- VttTextParser.addDefaultTextColor_(styles);
478
- }
479
-
480
- payload = VttTextParser.replaceKaraokeStylePayload_(payload);
481
- payload = VttTextParser.replaceVoiceStylePayload_(payload);
482
- payload = VttTextParser.escapeInvalidChevrons_(payload);
483
- const xmlPayload = '<span>' + payload + '</span>';
484
- let element;
485
- try {
486
- element = TXml.parseXmlString(xmlPayload, 'span');
487
- } catch (e) {
488
- shaka.log.warning('cue parse fail: ', e);
489
- }
490
-
491
- if (element) {
492
- const childNodes = element.children;
493
- if (childNodes.length == 1) {
494
- const childNode = childNodes[0];
495
- if (!TXml.isNode(childNode)) {
496
- rootCue.payload = StringUtils.htmlUnescape(payload);
497
- return;
498
- }
499
- }
500
- for (const childNode of childNodes) {
501
- VttTextParser.generateCueFromElement_(childNode, rootCue, styles);
502
- }
503
- } else {
504
- shaka.log.warning('The cue\'s markup could not be parsed: ', payload);
505
- rootCue.payload = StringUtils.htmlUnescape(payload);
506
- }
507
- }
508
-
509
- /**
510
- * This method converts invalid > chevrons to HTML entities.
511
- * It also removes < chevrons as per spec.
512
- *
513
- * @param {!string} input
514
- * @return {string}
515
- * @private
516
- */
517
- static escapeInvalidChevrons_(input) {
518
- // Used to map HTML entities to characters.
519
- const htmlEscapes = {
520
- '< ': '',
521
- ' >': ' &gt;',
522
- };
523
-
524
- const reEscapedHtml = /(< +>|<\s|\s>)/g;
525
- const reHasEscapedHtml = RegExp(reEscapedHtml.source);
526
- // This check is an optimization, since replace always makes a copy
527
- if (input && reHasEscapedHtml.test(input)) {
528
- return input.replace(reEscapedHtml, (entity) => {
529
- return htmlEscapes[entity] || '';
530
- });
531
- }
532
- return input || '';
533
- }
534
-
535
- /**
536
- * Converts voice style tag to be valid for xml parsing
537
- * For example,
538
- * input: <v Shaka>Test
539
- * output: <v.voice-Shaka>Test</v.voice-Shaka>
540
- *
541
- * @param {string} payload
542
- * @return {string} processed payload
543
- * @private
544
- */
545
- static replaceVoiceStylePayload_(payload) {
546
- const voiceTag = 'v';
547
- const names = [];
548
- let nameStart = -1;
549
- let newPayload = '';
550
- let hasVoiceEndTag = false;
551
- for (let i = 0; i < payload.length; i++) {
552
- // This condition is used to manage tags that have end tags.
553
- if (payload[i] === '/') {
554
- const end = payload.indexOf('>', i);
555
- if (end === -1) {
556
- return payload;
557
- }
558
- const tagEnd = payload.substring(i + 1, end);
559
- if (!tagEnd || tagEnd != voiceTag) {
560
- newPayload += payload[i];
561
- continue;
562
- }
563
- hasVoiceEndTag = true;
564
- let tagStart = null;
565
- if (names.length) {
566
- tagStart = names[names.length -1];
567
- }
568
- if (!tagStart) {
569
- newPayload += payload[i];
570
- } else if (tagStart === tagEnd) {
571
- newPayload += '/' + tagEnd + '>';
572
- i += tagEnd.length + 1;
573
- } else {
574
- if (!tagStart.startsWith(voiceTag)) {
575
- newPayload += payload[i];
576
- continue;
577
- }
578
- newPayload += '/' + tagStart + '>';
579
- i += tagEnd.length + 1;
580
- }
581
- } else {
582
- // Here we only want the tag name, not any other payload.
583
- if (payload[i] === '<') {
584
- nameStart = i + 1;
585
- if (payload[nameStart] != voiceTag) {
586
- nameStart = -1;
587
- }
588
- } else if (payload[i] === '>') {
589
- if (nameStart > 0) {
590
- names.push(payload.substr(nameStart, i - nameStart));
591
- nameStart = -1;
592
- }
593
- }
594
- newPayload += payload[i];
595
- }
596
- }
597
- for (const name of names) {
598
- const newName = name.replace(' ', '.voice-');
599
- newPayload = newPayload.replace(`<${name}>`, `<${newName}>`);
600
- newPayload = newPayload.replace(`</${name}>`, `</${newName}>`);
601
- if (!hasVoiceEndTag) {
602
- newPayload += `</${newName}>`;
603
- }
604
- }
605
- return newPayload;
606
- }
607
-
608
- /**
609
- * Converts karaoke style tag to be valid for xml parsing
610
- * For example,
611
- * input: Text <00:00:00.450> time <00:00:01.450> 1
612
- * output: Text <div time="00:00:00.450"> time
613
- * <div time="00:00:01.450"> 1</div></div>
614
- *
615
- * @param {string} payload
616
- * @return {string} processed payload
617
- * @private
618
- */
619
- static replaceKaraokeStylePayload_(payload) {
620
- const names = [];
621
- let nameStart = -1;
622
- for (let i = 0; i < payload.length; i++) {
623
- if (payload[i] === '<') {
624
- nameStart = i + 1;
625
- } else if (payload[i] === '>') {
626
- if (nameStart > 0) {
627
- const name = payload.substr(nameStart, i - nameStart);
628
- if (name.match(shaka.text.VttTextParser.timeFormat_)) {
629
- names.push(name);
630
- }
631
- nameStart = -1;
632
- }
633
- }
634
- }
635
- let newPayload = payload;
636
- for (const name of names) {
637
- const replaceTag = '<' + name + '>';
638
- const startTag = '<div time="' + name + '">';
639
- const endTag = '</div>';
640
- newPayload = newPayload.replace(replaceTag, startTag);
641
- newPayload += endTag;
642
- }
643
- return newPayload;
644
- }
645
-
646
- /**
647
- * @param {string} value
648
- * @param {string} defaultValue
649
- * @private
650
- */
651
- static getOrDefault_(value, defaultValue) {
652
- if (value && value.length > 0) {
653
- return value;
654
- }
655
- return defaultValue;
656
- }
657
-
658
- /**
659
- * Merges values created in parseStyle_
660
- * @param {!shaka.text.Cue} cue
661
- * @param {shaka.text.Cue} refCue
662
- * @private
663
- */
664
- static mergeStyle_(cue, refCue) {
665
- if (!refCue) {
666
- return;
667
- }
668
-
669
- const VttTextParser = shaka.text.VttTextParser;
670
- // Overwrites if new value string length > 0
671
- cue.backgroundColor = VttTextParser.getOrDefault_(
672
- refCue.backgroundColor, cue.backgroundColor);
673
- cue.color = VttTextParser.getOrDefault_(
674
- refCue.color, cue.color);
675
- cue.fontFamily = VttTextParser.getOrDefault_(
676
- refCue.fontFamily, cue.fontFamily);
677
- cue.fontSize = VttTextParser.getOrDefault_(
678
- refCue.fontSize, cue.fontSize);
679
- cue.textShadow = VttTextParser.getOrDefault_(
680
- refCue.textShadow, cue.textShadow);
681
-
682
- // Overwrite with new values as unable to determine
683
- // if new value is set or not
684
- cue.fontWeight = refCue.fontWeight;
685
- cue.fontStyle = refCue.fontStyle;
686
- cue.opacity = refCue.opacity;
687
- cue.rubyTag = refCue.rubyTag;
688
- cue.textCombineUpright = refCue.textCombineUpright;
689
- cue.wrapLine = refCue.wrapLine;
690
- }
691
-
692
- /**
693
- * @param {!shaka.extern.xml.Node} element
694
- * @param {!shaka.text.Cue} rootCue
695
- * @param {!Map.<string, shaka.text.Cue>} styles
696
- * @private
697
- */
698
- static generateCueFromElement_(element, rootCue, styles) {
699
- const VttTextParser = shaka.text.VttTextParser;
700
- const TXml = shaka.util.TXml;
701
- const nestedCue = rootCue.clone();
702
- // We don't want propagate some properties.
703
- nestedCue.nestedCues = [];
704
- nestedCue.payload = '';
705
- nestedCue.rubyTag = '';
706
- // We don't want propagate some position settings
707
- nestedCue.line = null;
708
- nestedCue.region = new shaka.text.CueRegion();
709
- nestedCue.position = null;
710
- nestedCue.size = 0;
711
-
712
- if (shaka.util.TXml.isNode(element)) {
713
- const bold = shaka.text.Cue.fontWeight.BOLD;
714
- const italic = shaka.text.Cue.fontStyle.ITALIC;
715
- const underline = shaka.text.Cue.textDecoration.UNDERLINE;
716
- const tags = element.tagName.split(/(?=[ .])+/g);
717
- for (const tag of tags) {
718
- let styleTag = tag;
719
- // White blanks at start indicate that the style is a voice
720
- if (styleTag.startsWith('.voice-')) {
721
- const voice = styleTag.split('-').pop();
722
- styleTag = `v[voice="${voice}"]`;
723
- // The specification allows to have quotes and not, so we check to
724
- // see which one is being used.
725
- if (!styles.has(styleTag)) {
726
- styleTag = `v[voice=${voice}]`;
727
- }
728
- }
729
- if (styles.has(styleTag)) {
730
- VttTextParser.mergeStyle_(nestedCue, styles.get(styleTag));
731
- }
732
- switch (tag) {
733
- case 'br': {
734
- const lineBreakCue = shaka.text.Cue.lineBreak(
735
- nestedCue.startTime, nestedCue.endTime);
736
- rootCue.nestedCues.push(lineBreakCue);
737
- return;
738
- }
739
- case 'b':
740
- nestedCue.fontWeight = bold;
741
- break;
742
- case 'i':
743
- nestedCue.fontStyle = italic;
744
- break;
745
- case 'u':
746
- nestedCue.textDecoration.push(underline);
747
- break;
748
- case 'font': {
749
- const color = element.attributes['color'];
750
- if (color) {
751
- nestedCue.color = color;
752
- }
753
- break;
754
- }
755
- case 'div': {
756
- const time = element.attributes['time'];
757
- if (!time) {
758
- break;
759
- }
760
- const parser = new shaka.util.TextParser(time);
761
- const cueTime = shaka.text.VttTextParser.parseTime_(parser);
762
- if (cueTime) {
763
- nestedCue.startTime = cueTime;
764
- }
765
- break;
766
- }
767
- case 'ruby':
768
- case 'rp':
769
- case 'rt':
770
- nestedCue.rubyTag = tag;
771
- break;
772
- default:
773
- break;
774
- }
775
- }
776
- }
777
-
778
- const isTextNode = (item) => shaka.util.TXml.isText(item);
779
- const childNodes = element.children;
780
- if (isTextNode(element) ||
781
- (childNodes.length == 1 && isTextNode(childNodes[0]))) {
782
- // Trailing line breaks may lost when convert cue to HTML tag
783
- // Need to insert line break cue to preserve line breaks
784
- const textArr = TXml.getTextContents(element).split('\n');
785
- let isFirst = true;
786
- for (const text of textArr) {
787
- if (!isFirst) {
788
- const lineBreakCue = shaka.text.Cue.lineBreak(
789
- nestedCue.startTime, nestedCue.endTime);
790
- rootCue.nestedCues.push(lineBreakCue);
791
- }
792
- if (text.length > 0) {
793
- const textCue = nestedCue.clone();
794
- textCue.payload = shaka.util.StringUtils.htmlUnescape(text);
795
- rootCue.nestedCues.push(textCue);
796
- }
797
- isFirst = false;
798
- }
799
- } else {
800
- rootCue.nestedCues.push(nestedCue);
801
- for (const childNode of childNodes) {
802
- VttTextParser.generateCueFromElement_(childNode, nestedCue, styles);
803
- }
804
- }
805
- }
806
-
807
436
  /**
808
437
  * Parses a WebVTT setting from the given word.
809
438
  *
@@ -982,31 +611,6 @@ shaka.text.VttTextParser = class {
982
611
 
983
612
  return true;
984
613
  }
985
-
986
- /**
987
- * Parses a WebVTT time from the given parser.
988
- *
989
- * @param {!shaka.util.TextParser} parser
990
- * @return {?number}
991
- * @private
992
- */
993
- static parseTime_(parser) {
994
- const results = parser.readRegex(shaka.text.VttTextParser.timeFormat_);
995
- if (results == null) {
996
- return null;
997
- }
998
- // This capture is optional, but will still be in the array as undefined,
999
- // in which case it is 0.
1000
- const hours = Number(results[1]) || 0;
1001
- const minutes = Number(results[2]);
1002
- const seconds = Number(results[3]);
1003
- const milliseconds = Number(results[4]);
1004
- if (minutes > 59 || seconds > 59) {
1005
- return null;
1006
- }
1007
-
1008
- return (milliseconds / 1000) + seconds + (minutes * 60) + (hours * 3600);
1009
- }
1010
614
  };
1011
615
 
1012
616
  /**
@@ -1022,15 +626,6 @@ shaka.text.VttTextParser.MPEG_TIMESCALE_ = 90000;
1022
626
  */
1023
627
  shaka.text.VttTextParser.TS_ROLLOVER_ = 0x200000000;
1024
628
 
1025
- /**
1026
- * @const
1027
- * @private {!RegExp}
1028
- * @example 00:00.000 or 00:00:00.000 or 0:00:00.000 or
1029
- * 00:00.00 or 00:00:00.00 or 0:00:00.00
1030
- */
1031
- shaka.text.VttTextParser.timeFormat_ =
1032
- /(?:(\d{1,}):)?(\d{2}):(\d{2})\.(\d{2,3})/g;
1033
-
1034
629
  shaka.text.TextEngine.registerParser(
1035
630
  'text/vtt', () => new shaka.text.VttTextParser());
1036
631
 
@@ -43,6 +43,14 @@ shaka.util.AbortableOperation = class {
43
43
  this.aborted_ = false;
44
44
  }
45
45
 
46
+ /**
47
+ * @return {boolean} True if the operation has been aborted.
48
+ * @export
49
+ */
50
+ get aborted() {
51
+ return this.aborted_;
52
+ }
53
+
46
54
  /**
47
55
  * @param {!shaka.util.Error} error
48
56
  * @return {!shaka.util.AbortableOperation} An operation which has already
@@ -51,6 +51,11 @@ shaka.util.CmcdManager = class {
51
51
  * @private {boolean}
52
52
  */
53
53
  this.starved_ = false;
54
+
55
+ /**
56
+ * @private {boolean}
57
+ */
58
+ this.lowLatency_ = false;
54
59
  }
55
60
 
56
61
  /**
@@ -71,6 +76,7 @@ shaka.util.CmcdManager = class {
71
76
  this.playbackStarted_ = false;
72
77
  this.buffering_ = true;
73
78
  this.starved_ = false;
79
+ this.lowLatency_ = false;
74
80
  }
75
81
 
76
82
  /**
@@ -90,6 +96,30 @@ shaka.util.CmcdManager = class {
90
96
  this.buffering_ = buffering;
91
97
  }
92
98
 
99
+ /**
100
+ * Set the low latency
101
+ *
102
+ * @param {boolean} lowLatency
103
+ */
104
+ setLowLatency(lowLatency) {
105
+ this.lowLatency_ = lowLatency;
106
+
107
+ const StreamingFormat = shaka.util.CmcdManager.StreamingFormat;
108
+ if (this.lowLatency_) {
109
+ if (this.sf_ == StreamingFormat.DASH) {
110
+ this.sf_ = StreamingFormat.LOW_LATENCY_DASH;
111
+ } else if (this.sf_ == StreamingFormat.HLS) {
112
+ this.sf_ = StreamingFormat.LOW_LATENCY_HLS;
113
+ }
114
+ } else {
115
+ if (this.sf_ == StreamingFormat.LOW_LATENCY_DASH) {
116
+ this.sf_ = StreamingFormat.DASH;
117
+ } else if (this.sf_ == StreamingFormat.LOW_LATENCY_HLS) {
118
+ this.sf_ = StreamingFormat.HLS;
119
+ }
120
+ }
121
+ }
122
+
93
123
  /**
94
124
  * Apply CMCD data to a request.
95
125
  *
@@ -624,10 +654,16 @@ shaka.util.CmcdManager = class {
624
654
 
625
655
  switch (type) {
626
656
  case AdvancedRequestType.MPD:
657
+ if (this.lowLatency_) {
658
+ return shaka.util.CmcdManager.StreamingFormat.LOW_LATENCY_DASH;
659
+ }
627
660
  return shaka.util.CmcdManager.StreamingFormat.DASH;
628
661
 
629
662
  case AdvancedRequestType.MASTER_PLAYLIST:
630
663
  case AdvancedRequestType.MEDIA_PLAYLIST:
664
+ if (this.lowLatency_) {
665
+ return shaka.util.CmcdManager.StreamingFormat.LOW_LATENCY_HLS;
666
+ }
631
667
  return shaka.util.CmcdManager.StreamingFormat.HLS;
632
668
 
633
669
  case AdvancedRequestType.MSS:
@@ -887,7 +923,9 @@ shaka.util.CmcdManager.StreamType = {
887
923
  */
888
924
  shaka.util.CmcdManager.StreamingFormat = {
889
925
  DASH: 'd',
926
+ LOW_LATENCY_DASH: 'ld',
890
927
  HLS: 'h',
928
+ LOW_LATENCY_HLS: 'lh',
891
929
  SMOOTH: 's',
892
930
  OTHER: 'o',
893
931
  };
package/lib/util/error.js CHANGED
@@ -779,6 +779,11 @@ shaka.util.Error.Code = {
779
779
  */
780
780
  'DASH_INVALID_PATCH': 4052,
781
781
 
782
+ /**
783
+ * The media playlist has not segments or all segments are gap.
784
+ */
785
+ 'HLS_EMPTY_MEDIA_PLAYLIST': 4053,
786
+
782
787
 
783
788
  // RETIRED: 'INCONSISTENT_BUFFER_STATE': 5000,
784
789
  // RETIRED: 'INVALID_SEGMENT_INDEX': 5001,
@@ -1143,4 +1148,15 @@ shaka.util.Error.Code = {
1143
1148
  * Ad Insertion. Call adManager.initMediaTailor() to do it.
1144
1149
  */
1145
1150
  'MT_AD_MANAGER_NOT_INITIALIZED': 10005,
1151
+
1152
+ /**
1153
+ * Interstitial Ad Manager needs to be initialized to enable interstitial
1154
+ * Ad Insertion. Call adManager.initInterstitial() to do it.
1155
+ */
1156
+ 'INTERSTITIAL_AD_MANAGER_NOT_INITIALIZED': 10006,
1157
+
1158
+ /**
1159
+ * The VAST contained invalid XML markup.
1160
+ */
1161
+ 'VAST_INVALID_XML': 4001,
1146
1162
  };
@@ -173,6 +173,7 @@ shaka.util.FakeEvent.EventName = {
173
173
  ManifestParsed: 'manifestparsed',
174
174
  ManifestUpdated: 'manifestupdated',
175
175
  MediaQualityChanged: 'mediaqualitychanged',
176
+ MediaSourceRecovered: 'mediasourcerecovered',
176
177
  Metadata: 'metadata',
177
178
  Midpoint: 'midpoint',
178
179
  NoSpatialVideoInfoEvent: 'nospatialvideoinfo',
@@ -26,9 +26,10 @@ shaka.util.ManifestParserUtils = class {
26
26
  *
27
27
  * @param {!Array.<string>} baseUris
28
28
  * @param {!Array.<string>} relativeUris
29
+ * @param {string=} extraQueryParams
29
30
  * @return {!Array.<string>}
30
31
  */
31
- static resolveUris(baseUris, relativeUris) {
32
+ static resolveUris(baseUris, relativeUris, extraQueryParams = '') {
32
33
  if (relativeUris.length == 0) {
33
34
  return baseUris;
34
35
  }
@@ -36,7 +37,11 @@ shaka.util.ManifestParserUtils = class {
36
37
  if (baseUris.length == 1 && relativeUris.length == 1) {
37
38
  const baseUri = new goog.Uri(baseUris[0]);
38
39
  const relativeUri = new goog.Uri(relativeUris[0]);
39
- return [baseUri.resolve(relativeUri).toString()];
40
+ const resolvedUri = baseUri.resolve(relativeUri);
41
+ if (extraQueryParams) {
42
+ resolvedUri.setQueryData(extraQueryParams);
43
+ }
44
+ return [resolvedUri.toString()];
40
45
  }
41
46
 
42
47
  const relativeAsGoog = relativeUris.map((uri) => new goog.Uri(uri));
@@ -47,7 +52,11 @@ shaka.util.ManifestParserUtils = class {
47
52
  for (const baseStr of baseUris) {
48
53
  const base = new goog.Uri(baseStr);
49
54
  for (const relative of relativeAsGoog) {
50
- resolvedUris.push(base.resolve(relative).toString());
55
+ const resolvedUri = base.resolve(relative);
56
+ if (extraQueryParams) {
57
+ resolvedUri.setQueryData(extraQueryParams);
58
+ }
59
+ resolvedUris.push(resolvedUri.toString());
51
60
  }
52
61
  }
53
62