shaka-player 4.2.4 → 4.3.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 (137) hide show
  1. package/.github/workflows/sync-labels.yaml +1 -1
  2. package/.github/workflows/update-issues.yaml +1 -1
  3. package/AUTHORS +4 -0
  4. package/CHANGELOG.md +85 -54
  5. package/CONTRIBUTORS +4 -0
  6. package/README.md +14 -0
  7. package/demo/common/assets.js +58 -0
  8. package/demo/common/message_ids.js +22 -1
  9. package/demo/config.js +89 -5
  10. package/demo/demo.less +7 -1
  11. package/demo/index.html +8 -1
  12. package/demo/locales/en.json +21 -1
  13. package/demo/locales/source.json +82 -2
  14. package/demo/main.js +57 -1
  15. package/demo/search.js +2 -0
  16. package/demo/visualizer.js +337 -0
  17. package/demo/visualizer_button.js +103 -0
  18. package/dist/controls.css +1 -1
  19. package/dist/controls.css.map +1 -1
  20. package/dist/demo.compiled.debug.js +83 -58
  21. package/dist/demo.compiled.debug.map +4 -4
  22. package/dist/demo.compiled.js +83 -58
  23. package/dist/demo.compiled.map +4 -4
  24. package/dist/demo.css +1 -1
  25. package/dist/demo.css.map +1 -1
  26. package/dist/deps.js +16 -9
  27. package/dist/locales.js +32 -32
  28. package/dist/receiver.compiled.debug.js +23 -19
  29. package/dist/receiver.compiled.debug.map +3 -3
  30. package/dist/receiver.compiled.js +23 -19
  31. package/dist/receiver.compiled.map +3 -3
  32. package/dist/shaka-player.compiled.d.ts +173 -26
  33. package/dist/shaka-player.compiled.debug.d.ts +173 -26
  34. package/dist/shaka-player.compiled.debug.externs.js +161 -31
  35. package/dist/shaka-player.compiled.debug.js +464 -401
  36. package/dist/shaka-player.compiled.debug.map +4 -4
  37. package/dist/shaka-player.compiled.externs.js +161 -31
  38. package/dist/shaka-player.compiled.js +897 -862
  39. package/dist/shaka-player.compiled.map +4 -4
  40. package/dist/shaka-player.ui.d.ts +173 -26
  41. package/dist/shaka-player.ui.debug.d.ts +173 -26
  42. package/dist/shaka-player.ui.debug.externs.js +161 -31
  43. package/dist/shaka-player.ui.debug.js +488 -422
  44. package/dist/shaka-player.ui.debug.map +4 -4
  45. package/dist/shaka-player.ui.externs.js +161 -31
  46. package/dist/shaka-player.ui.js +1053 -1018
  47. package/dist/shaka-player.ui.map +4 -4
  48. package/docs/design/lcevc-architecture.png +0 -0
  49. package/docs/design/lcevc-demo.png +0 -0
  50. package/docs/design/lcevc-integration.md +72 -0
  51. package/docs/tutorials/fairplay.md +7 -0
  52. package/docs/tutorials/faq.md +1 -1
  53. package/docs/tutorials/ui.md +7 -7
  54. package/docs/tutorials/upgrade.md +6 -0
  55. package/externs/ima.js +13 -1
  56. package/externs/lcevc.js +81 -0
  57. package/externs/mux.js +1 -42
  58. package/externs/shaka/ads.js +11 -1
  59. package/externs/shaka/manifest_parser.js +5 -1
  60. package/externs/shaka/player.js +178 -15
  61. package/lib/abr/simple_abr_manager.js +7 -0
  62. package/lib/ads/ad_manager.js +0 -8
  63. package/lib/ads/client_side_ad.js +15 -0
  64. package/lib/ads/client_side_ad_manager.js +21 -8
  65. package/lib/ads/server_side_ad.js +15 -0
  66. package/lib/ads/server_side_ad_manager.js +3 -3
  67. package/lib/cast/cast_utils.js +2 -0
  68. package/lib/cea/dummy_cea_parser.js +28 -0
  69. package/lib/cea/i_cea_parser.js +14 -2
  70. package/lib/cea/mp4_cea_parser.js +87 -5
  71. package/lib/config/auto_show_text.js +33 -0
  72. package/lib/dash/dash_parser.js +1 -1
  73. package/lib/hls/hls_parser.js +790 -374
  74. package/lib/lcevc/lcevc_dil.js +152 -0
  75. package/lib/media/closed_caption_parser.js +9 -6
  76. package/lib/media/drm_engine.js +14 -6
  77. package/lib/media/media_source_engine.js +106 -26
  78. package/lib/media/presentation_timeline.js +26 -11
  79. package/lib/media/segment_reference.js +49 -1
  80. package/lib/media/streaming_engine.js +142 -12
  81. package/lib/media/transmuxer.js +73 -18
  82. package/lib/offline/storage.js +1 -0
  83. package/lib/player.js +454 -75
  84. package/lib/polyfill/patchedmediakeys_apple.js +55 -1
  85. package/lib/text/ui_text_displayer.js +5 -5
  86. package/lib/text/vtt_text_parser.js +35 -1
  87. package/lib/util/dom_utils.js +0 -1
  88. package/lib/util/error.js +17 -0
  89. package/lib/util/fake_event.js +2 -0
  90. package/lib/util/id3_utils.js +300 -0
  91. package/lib/util/mime_utils.js +3 -6
  92. package/lib/util/mp4_box_parsers.js +15 -5
  93. package/lib/util/platform.js +9 -1
  94. package/lib/util/player_configuration.js +23 -1
  95. package/lib/util/ts_parser.js +499 -0
  96. package/maintained-branches.md +2 -2
  97. package/package.json +4 -1
  98. package/roadmap.md +5 -3
  99. package/test/ads/ad_manager_unit.js +180 -29
  100. package/test/cast/cast_utils_unit.js +3 -1
  101. package/test/dash/dash_parser_content_protection_unit.js +1 -0
  102. package/test/dash/dash_parser_live_unit.js +1 -0
  103. package/test/dash/dash_parser_manifest_unit.js +93 -1
  104. package/test/dash/dash_parser_segment_base_unit.js +1 -0
  105. package/test/dash/dash_parser_segment_list_unit.js +1 -0
  106. package/test/dash/dash_parser_segment_template_unit.js +1 -0
  107. package/test/demo/demo_unit.js +2 -1
  108. package/test/hls/hls_live_unit.js +253 -19
  109. package/test/hls/hls_parser_unit.js +500 -12
  110. package/test/media/closed_caption_parser_unit.js +2 -1
  111. package/test/media/drm_engine_integration.js +3 -1
  112. package/test/media/media_source_engine_integration.js +71 -6
  113. package/test/media/media_source_engine_unit.js +10 -5
  114. package/test/media/streaming_engine_integration.js +4 -1
  115. package/test/media/streaming_engine_unit.js +144 -1
  116. package/test/media/transmuxer_integration.js +21 -10
  117. package/test/player_integration.js +224 -82
  118. package/test/player_unit.js +5 -2
  119. package/test/polyfill/patchedmediakeys_apple_unit.js +124 -0
  120. package/test/test/assets/id3-metadata.aac +0 -0
  121. package/test/test/assets/id3-metadata.ts +0 -0
  122. package/test/test/assets/thumbnails-sprites.vtt +10 -0
  123. package/test/test/assets/thumbnails.vtt +10 -0
  124. package/test/test/util/dash_parser_util.js +2 -0
  125. package/test/test/util/fake_ad.js +21 -0
  126. package/test/test/util/fake_media_source_engine.js +7 -0
  127. package/test/test/util/id3_generator.js +100 -0
  128. package/test/test/util/manifest_generator.js +3 -6
  129. package/test/test/util/simple_fakes.js +4 -0
  130. package/test/test/util/stream_generator.js +38 -0
  131. package/test/test/util/test_scheme.js +43 -14
  132. package/test/text/vtt_text_parser_unit.js +11 -0
  133. package/test/ui/text_displayer_layout_unit.js +14 -0
  134. package/test/util/id3_utils_unit.js +116 -0
  135. package/test/util/ts_parser_unit.js +77 -0
  136. package/ui/less/containers.less +7 -0
  137. package/ui/ui.js +43 -4
package/lib/player.js CHANGED
@@ -8,6 +8,7 @@ goog.provide('shaka.Player');
8
8
 
9
9
  goog.require('goog.asserts');
10
10
  goog.require('shaka.Deprecate');
11
+ goog.require('shaka.config.AutoShowText');
11
12
  goog.require('shaka.log');
12
13
  goog.require('shaka.media.AdaptationSetCriteria');
13
14
  goog.require('shaka.media.BufferingObserver');
@@ -17,7 +18,6 @@ goog.require('shaka.media.ManifestParser');
17
18
  goog.require('shaka.media.MediaSourceEngine');
18
19
  goog.require('shaka.media.MediaSourcePlayhead');
19
20
  goog.require('shaka.media.MetaSegmentIndex');
20
- goog.require('shaka.media.ClosedCaptionParser');
21
21
  goog.require('shaka.media.PlayRateController');
22
22
  goog.require('shaka.media.Playhead');
23
23
  goog.require('shaka.media.PlayheadObserverManager');
@@ -26,6 +26,7 @@ goog.require('shaka.media.QualityObserver');
26
26
  goog.require('shaka.media.RegionObserver');
27
27
  goog.require('shaka.media.RegionTimeline');
28
28
  goog.require('shaka.media.SegmentIndex');
29
+ goog.require('shaka.media.SegmentReference');
29
30
  goog.require('shaka.media.SrcEqualsPlayhead');
30
31
  goog.require('shaka.media.StreamingEngine');
31
32
  goog.require('shaka.media.TimeRangesUtils');
@@ -55,7 +56,7 @@ goog.require('shaka.util.PublicPromise');
55
56
  goog.require('shaka.util.Stats');
56
57
  goog.require('shaka.util.StreamUtils');
57
58
  goog.require('shaka.util.Timer');
58
- goog.requireType('shaka.media.IClosedCaptionParser');
59
+ goog.require('shaka.lcevc.Dil');
59
60
  goog.requireType('shaka.media.PresentationTimeline');
60
61
  goog.requireType('shaka.routing.Node');
61
62
  goog.requireType('shaka.routing.Payload');
@@ -325,7 +326,7 @@ goog.requireType('shaka.routing.Payload');
325
326
  * the cue applies.
326
327
  * @property {string} metadataType
327
328
  * Type of metadata. Eg: org.id3 or org.mp4ra
328
- * @property {shaka.extern.ID3Metadata} payload
329
+ * @property {shaka.extern.MetadataFrame} payload
329
330
  * The metadata itself
330
331
  * @exportDoc
331
332
  */
@@ -367,6 +368,21 @@ goog.requireType('shaka.routing.Payload');
367
368
  */
368
369
 
369
370
 
371
+ /**
372
+ * @event shaka.Player.SegmentAppended
373
+ * @description Fired when a segment is appended to the media element.
374
+ * @property {string} type
375
+ * 'segmentappended'
376
+ * @property {number} start
377
+ * The start time of the segment.
378
+ * @property {number} end
379
+ * The end time of the segment.
380
+ * @property {string} contentType
381
+ * The content type of the segment. E.g. 'video', 'audio', or 'text'.
382
+ * @exportDoc
383
+ */
384
+
385
+
370
386
  /**
371
387
  * @event shaka.Player.SessionDataEvent
372
388
  * @description Fired when the manifest parser find info about session data.
@@ -505,6 +521,15 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
505
521
  /** @private {shaka.util.CmcdManager} */
506
522
  this.cmcdManager_ = null;
507
523
 
524
+ // This is the canvas element that will be used for rendering LCEVC
525
+ // enhanced frames.
526
+ /** @private {?HTMLCanvasElement} */
527
+ this.lcevcCanvas_ = null;
528
+
529
+ // This is the LCEVC Dil object to decode LCEVC.
530
+ /** @private {?shaka.lcevc.Dil} */
531
+ this.lcevcDil_ = null;
532
+
508
533
  /** @private {shaka.media.QualityObserver} */
509
534
  this.qualityObserver_ = null;
510
535
 
@@ -523,6 +548,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
523
548
  /** @private {?string} */
524
549
  this.assetUri_ = null;
525
550
 
551
+ /** @private {boolean} */
552
+ this.fullyLoaded_ = false;
553
+
526
554
  /** @private {shaka.extern.AbrManager} */
527
555
  this.abrManager_ = null;
528
556
 
@@ -726,6 +754,66 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
726
754
  }
727
755
  }
728
756
 
757
+ /**
758
+ * Create a shaka.lcevc.Dil object
759
+ * @param {shaka.extern.LcevcConfiguration} config
760
+ * @private
761
+ */
762
+ createLcevcDil_(config) {
763
+ if (this.lcevcDil_ == null) {
764
+ this.lcevcDil_ = new shaka.lcevc.Dil(
765
+ /** @type {HTMLVideoElement} */ (this.video_),
766
+ this.lcevcCanvas_,
767
+ config,
768
+ );
769
+ if (this.mediaSourceEngine_) {
770
+ this.mediaSourceEngine_.updateLcevcDil(this.lcevcDil_);
771
+ }
772
+ }
773
+ }
774
+
775
+ /**
776
+ * Close a shaka.lcevc.Dil object if present and hide the canvas.
777
+ * @private
778
+ */
779
+ closeLcevcDil_() {
780
+ if (this.lcevcDil_ != null) {
781
+ this.lcevcDil_.release();
782
+ this.lcevcDil_ = null;
783
+ }
784
+ }
785
+
786
+ /**
787
+ * Setup shaka.lcevc.Dil object
788
+ * @param {?shaka.extern.PlayerConfiguration} config
789
+ * @private
790
+ */
791
+ setupLcevc_(config) {
792
+ if (config.lcevc.enabled) {
793
+ const tracks = this.getVariantTracks();
794
+ if (tracks && tracks[0] &&
795
+ tracks[0].videoMimeType ==
796
+ shaka.Player.SRC_EQUAL_EXTENSIONS_TO_MIME_TYPES_['ts']) {
797
+ const edge = shaka.util.Platform.isEdge() ||
798
+ shaka.util.Platform.isLegacyEdge();
799
+ if (edge) {
800
+ if (!config.streaming.forceTransmux) {
801
+ // If forceTransmux is disabled for Microsoft Edge, LCEVC data
802
+ // is stripped out in case of a MPEG-2 TS container.
803
+ // Hence the warning for Microsoft Edge when playing content with
804
+ // MPEG-2 TS container.
805
+ shaka.log.alwaysWarn('LCEVC Warning: For MPEG-2 TS decoding '+
806
+ 'the config.streaming.forceTransmux must be enabled.');
807
+ }
808
+ }
809
+ }
810
+ this.closeLcevcDil_();
811
+ this.createLcevcDil_(config.lcevc);
812
+ } else {
813
+ this.closeLcevcDil_();
814
+ }
815
+ }
816
+
729
817
  /**
730
818
  * @param {!shaka.util.FakeEvent.EventName} name
731
819
  * @param {Map.<string, Object>=} data
@@ -748,6 +836,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
748
836
  return;
749
837
  }
750
838
 
839
+ // If LCEVC Dil exists close it.
840
+ this.closeLcevcDil_();
841
+
751
842
  // Mark as "dead". This should stop external-facing calls from changing our
752
843
  // internal state any more. This will stop calls to |attach|, |detach|, etc.
753
844
  // from interrupting our final move to the detached state.
@@ -985,6 +1076,18 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
985
1076
  return this.wrapWalkerListenersWithPromise_(events);
986
1077
  }
987
1078
 
1079
+
1080
+ /**
1081
+ * Calling <code>attachCanvas</code> will tell the player to set canvas
1082
+ * element for LCEVC decoding.
1083
+ *
1084
+ * @param {HTMLCanvasElement} canvas
1085
+ * @export
1086
+ */
1087
+ attachCanvas(canvas) {
1088
+ this.lcevcCanvas_ = canvas;
1089
+ }
1090
+
988
1091
  /**
989
1092
  * Tell the player to stop using its current media element. If the player is:
990
1093
  * <ul>
@@ -1050,12 +1153,17 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1050
1153
  return Promise.reject(this.createAbortLoadError_());
1051
1154
  }
1052
1155
 
1156
+ this.fullyLoaded_ = false;
1157
+
1053
1158
  // If the platform does not support media source, we will never want to
1054
1159
  // initialize media source.
1055
1160
  if (!shaka.util.Platform.supportsMediaSource()) {
1056
1161
  initializeMediaSource = false;
1057
1162
  }
1058
1163
 
1164
+ // If LCEVC Dil exists close it.
1165
+ this.closeLcevcDil_();
1166
+
1059
1167
  // Since we are going either to attached or detached (through unloaded), we
1060
1168
  // can't allow it to be interrupted or else we could lose track of what
1061
1169
  // media element we are suppose to use.
@@ -1141,6 +1249,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1141
1249
  load(assetUri, startTime, mimeType) {
1142
1250
  this.updatedStartTime_ = null;
1143
1251
 
1252
+ this.fullyLoaded_ = false;
1253
+
1144
1254
  // Do not allow the player to be used after |destroy| is called.
1145
1255
  if (this.loadMode_ == shaka.Player.LoadMode.DESTROYED) {
1146
1256
  return Promise.reject(this.createAbortLoadError_());
@@ -1578,8 +1688,6 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1578
1688
  this.mediaSourceEngine_ == null,
1579
1689
  'We should not have a media source engine yet.');
1580
1690
 
1581
- const closedCaptionsParser = new shaka.media.ClosedCaptionParser();
1582
-
1583
1691
  // When changing text visibility we need to update both the text displayer
1584
1692
  // and streaming engine because we don't always stream text. To ensure that
1585
1693
  // text displayer and streaming engine are always in sync, wait until they
@@ -1590,11 +1698,12 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1590
1698
 
1591
1699
  const mediaSourceEngine = this.createMediaSourceEngine(
1592
1700
  has.mediaElement,
1593
- closedCaptionsParser,
1594
1701
  textDisplayer,
1595
1702
  (metadata, offset, endTime) => {
1596
1703
  this.processTimedMetadataMediaSrc_(metadata, offset, endTime);
1597
- });
1704
+ },
1705
+ this.lcevcDil_);
1706
+ mediaSourceEngine.configure(this.config_.mediaSource);
1598
1707
  const {segmentRelativeVttTiming} = this.config_.manifest;
1599
1708
  mediaSourceEngine.setSegmentRelativeVttTiming(segmentRelativeVttTiming);
1600
1709
 
@@ -1775,6 +1884,15 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1775
1884
  this.streamingEngine_.updateDuration();
1776
1885
  }
1777
1886
  },
1887
+ newDrmInfo: (stream) => {
1888
+ // We may need to create new sessions for any new init data.
1889
+ const currentDrmInfo =
1890
+ this.drmEngine_ ? this.drmEngine_.getDrmInfo() : null;
1891
+ // DrmEngine.newInitData() requires mediaKeys to be available.
1892
+ if (currentDrmInfo && this.drmEngine_.getMediaKeys()) {
1893
+ this.processDrmInfos_(currentDrmInfo.keySystem, stream);
1894
+ }
1895
+ },
1778
1896
  };
1779
1897
 
1780
1898
  const startTime = Date.now() / 1000;
@@ -1865,6 +1983,15 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1865
1983
  const now = Date.now() / 1000;
1866
1984
  const delta = now - startTime;
1867
1985
  this.stats_.setDrmTime(delta);
1986
+ // LCEVC data by itself is not encrypted in DRM protected streams and
1987
+ // can therefore be accessed and decoded as normal. However, the LCEVC
1988
+ // decoder needs access to the VideoElement output in order to apply
1989
+ // the enhancement. In DRM contexts where the browser CDM restricts
1990
+ // access from our decoder, the enhancement cannot be applied and
1991
+ // therefore the LCEVC output canvas is hidden accordingly.
1992
+ if (this.lcevcDil_) {
1993
+ this.lcevcDil_.hideCanvas();
1994
+ }
1868
1995
  }
1869
1996
  },
1870
1997
  });
@@ -1949,6 +2076,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1949
2076
  this.loadEventManager_.listen(mediaElement, 'ended', updateStateHistory);
1950
2077
  this.loadEventManager_.listen(mediaElement, 'ratechange', onRateChange);
1951
2078
 
2079
+ // Check status of the LCEVC DIL Object and reset or
2080
+ // create or close based on config
2081
+ this.setupLcevc_(this.config_);
2082
+
1952
2083
  const abrFactory = this.config_.abrFactory;
1953
2084
  if (!this.abrManager_ || this.abrManagerFactory_ != abrFactory) {
1954
2085
  this.abrManagerFactory_ = abrFactory;
@@ -1983,16 +2114,6 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1983
2114
  });
1984
2115
  this.abrManager_.setMediaElement(mediaElement);
1985
2116
 
1986
- this.playhead_ = this.createPlayhead(has.startTime);
1987
- this.playheadObservers_ = this.createPlayheadObserversForMSE_();
1988
-
1989
- // We need to start the buffer management code near the end because it will
1990
- // set the initial buffering state and that depends on other components
1991
- // being initialized.
1992
- const rebufferThreshold = Math.max(
1993
- this.manifest_.minBufferTime, this.config_.streaming.rebufferingGoal);
1994
- this.startBufferManagement_(rebufferThreshold);
1995
-
1996
2117
  // If the content is multi-codec and the browser can play more than one of
1997
2118
  // them, choose codecs now before we initialize streaming.
1998
2119
  shaka.util.StreamUtils.chooseCodecsAndFilterManifest(
@@ -2036,14 +2157,42 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2036
2157
  this.makeEvent_(shaka.util.FakeEvent.EventName.Streaming));
2037
2158
 
2038
2159
  // Pick the initial streams to play.
2039
- // however, we would skip switch to initial variant
2040
- // if user already pick variant track (via selectVariantTrack api)
2160
+ // Unless the user has already picked a variant, anyway, by calling
2161
+ // selectVariantTrack before this loading stage.
2041
2162
  let initialVariant = null;
2042
2163
  const activeVariantTrack = this.getVariantTracks().find((t) => t.active);
2043
-
2044
2164
  if (!activeVariantTrack) {
2045
2165
  initialVariant = this.chooseVariant_();
2046
2166
  goog.asserts.assert(initialVariant, 'Must choose an initial variant!');
2167
+ }
2168
+
2169
+ // Lazy-load the stream, so we will have enough info to make the playhead.
2170
+ const createSegmentIndexPromises = [];
2171
+ const toLazyLoad = activeVariantTrack || initialVariant;
2172
+ for (const stream of [toLazyLoad.video, toLazyLoad.audio]) {
2173
+ if (stream && !stream.segmentIndex) {
2174
+ createSegmentIndexPromises.push(stream.createSegmentIndex());
2175
+ }
2176
+ }
2177
+ if (createSegmentIndexPromises.length > 0) {
2178
+ await Promise.all(createSegmentIndexPromises);
2179
+ }
2180
+
2181
+ this.playhead_ = this.createPlayhead(has.startTime);
2182
+ this.playheadObservers_ = this.createPlayheadObserversForMSE_();
2183
+
2184
+ // We need to start the buffer management code near the end because it will
2185
+ // set the initial buffering state and that depends on other components
2186
+ // being initialized.
2187
+ const rebufferThreshold = Math.max(
2188
+ this.manifest_.minBufferTime, this.config_.streaming.rebufferingGoal);
2189
+ this.startBufferManagement_(rebufferThreshold);
2190
+
2191
+ // Now we can switch to the initial variant.
2192
+ if (!activeVariantTrack) {
2193
+ goog.asserts.assert(initialVariant,
2194
+ 'Must have choosen an initial variant!');
2195
+
2047
2196
  this.switchVariant_(initialVariant, /* fromAdaptation= */ true,
2048
2197
  /* clearBuffer= */ false, /* safeMargin= */ 0);
2049
2198
 
@@ -2117,6 +2266,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2117
2266
  'arbitrary language initially');
2118
2267
  }
2119
2268
 
2269
+ this.fullyLoaded_ = true;
2270
+
2120
2271
  // Wait for the 'loadedmetadata' event to measure load() latency.
2121
2272
  this.loadEventManager_.listenOnce(mediaElement, 'loadedmetadata', () => {
2122
2273
  const now = Date.now() / 1000;
@@ -2463,6 +2614,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2463
2614
  shaka.util.Error.Code.OPERATION_ABORTED);
2464
2615
  fullyLoaded.reject(abortedError);
2465
2616
  return Promise.resolve(); // Abort complete.
2617
+ }).chain(() => {
2618
+ this.fullyLoaded_ = true;
2466
2619
  });
2467
2620
  }
2468
2621
 
@@ -2575,11 +2728,15 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2575
2728
  */
2576
2729
  processTimedMetadataMediaSrc_(metadata, offset, segmentEndTime) {
2577
2730
  for (const sample of metadata) {
2578
- if (sample['data'] && sample['cueTime'] && sample['frames']) {
2579
- const start = sample['cueTime'] + offset;
2580
- const end = segmentEndTime;
2581
- const metadataType = 'ID3';
2582
- for (const frame of sample['frames']) {
2731
+ if (sample.data && sample.cueTime && sample.frames) {
2732
+ const start = sample.cueTime + offset;
2733
+ let end = segmentEndTime;
2734
+ // This can happen when the ID3 info arrives in a previous segment.
2735
+ if (end && start > end) {
2736
+ end = start;
2737
+ }
2738
+ const metadataType = 'org.id3';
2739
+ for (const frame of sample.frames) {
2583
2740
  const payload = frame;
2584
2741
  this.dispatchMetadataEvent_(start, end, metadataType, payload);
2585
2742
  }
@@ -2598,7 +2755,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2598
2755
  * @param {number} startTime
2599
2756
  * @param {?number} endTime
2600
2757
  * @param {string} metadataType
2601
- * @param {shaka.extern.ID3Metadata} payload
2758
+ * @param {shaka.extern.MetadataFrame} payload
2602
2759
  * @private
2603
2760
  */
2604
2761
  dispatchMetadataEvent_(startTime, endTime, metadataType, payload) {
@@ -2896,17 +3053,19 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2896
3053
  * way to inject fake media source engine instances.
2897
3054
  *
2898
3055
  * @param {!HTMLMediaElement} mediaElement
2899
- * @param {!shaka.media.IClosedCaptionParser} closedCaptionsParser
2900
3056
  * @param {!shaka.extern.TextDisplayer} textDisplayer
2901
3057
  * @param {!function(!Array.<shaka.extern.ID3Metadata>, number, ?number)}
2902
3058
  * onMetadata
3059
+ * @param {shaka.lcevc.Dil} lcevcDil
2903
3060
  *
2904
3061
  * @return {!shaka.media.MediaSourceEngine}
2905
3062
  */
2906
- createMediaSourceEngine(mediaElement, closedCaptionsParser, textDisplayer,
2907
- onMetadata) {
3063
+ createMediaSourceEngine(mediaElement, textDisplayer, onMetadata, lcevcDil) {
2908
3064
  return new shaka.media.MediaSourceEngine(
2909
- mediaElement, closedCaptionsParser, textDisplayer, onMetadata);
3065
+ mediaElement,
3066
+ textDisplayer,
3067
+ onMetadata,
3068
+ lcevcDil);
2910
3069
  }
2911
3070
 
2912
3071
  /**
@@ -2937,13 +3096,13 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2937
3096
  */
2938
3097
  createStreamingEngine() {
2939
3098
  goog.asserts.assert(
2940
- this.playhead_ && this.abrManager_ && this.mediaSourceEngine_ &&
3099
+ this.abrManager_ && this.mediaSourceEngine_ &&
2941
3100
  this.cmcdManager_ && this.manifest_,
2942
3101
  'Must not be destroyed');
2943
3102
 
2944
3103
  /** @type {shaka.media.StreamingEngine.PlayerInterface} */
2945
3104
  const playerInterface = {
2946
- getPresentationTime: () => this.playhead_.getTime(),
3105
+ getPresentationTime: () => this.playhead_ ? this.playhead_.getTime() : 0,
2947
3106
  getBandwidthEstimate: () => this.abrManager_.getBandwidthEstimate(),
2948
3107
  modifySegmentRequest: (request, segmentInfo) => {
2949
3108
  this.cmcdManager_.applySegmentData(request, segmentInfo);
@@ -2953,7 +3112,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2953
3112
  onError: (error) => this.onError_(error),
2954
3113
  onEvent: (event) => this.dispatchEvent(event),
2955
3114
  onManifestUpdate: () => this.onManifestUpdate_(),
2956
- onSegmentAppended: () => this.onSegmentAppended_(),
3115
+ onSegmentAppended: (start, end, contentType) => {
3116
+ this.onSegmentAppended_(start, end, contentType);
3117
+ },
2957
3118
  onInitSegmentAppended: (position, initSegment) => {
2958
3119
  const mediaQuality = initSegment.getMediaQuality();
2959
3120
  if (mediaQuality && this.qualityObserver_) {
@@ -2963,6 +3124,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2963
3124
  beforeAppendSegment: (contentType, segment) => {
2964
3125
  return this.drmEngine_.parseInbandPssh(contentType, segment);
2965
3126
  },
3127
+ onMetadata: (metadata, offset, endTime) => {
3128
+ this.processTimedMetadataMediaSrc_(metadata, offset, endTime);
3129
+ },
2966
3130
  };
2967
3131
 
2968
3132
  return new shaka.media.StreamingEngine(this.manifest_, playerInterface);
@@ -3006,6 +3170,16 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
3006
3170
 
3007
3171
  goog.asserts.assert(typeof(config) == 'object', 'Should be an object!');
3008
3172
 
3173
+ // Deprecate 'streaming.forceTransmuxTS' configuration.
3174
+ if (config['streaming'] && 'forceTransmuxTS' in config['streaming']) {
3175
+ shaka.Deprecate.deprecateFeature(5,
3176
+ 'streaming.forceTransmuxTS configuration',
3177
+ 'Please Use streaming.forceTransmux instead.');
3178
+ config['streaming']['forceTransmux'] =
3179
+ config['streaming']['forceTransmuxTS'];
3180
+ delete config['streaming']['forceTransmuxTS'];
3181
+ }
3182
+
3009
3183
  // If lowLatencyMode is enabled, and inaccurateManifestTolerance and
3010
3184
  // rebufferingGoal are not specified, set inaccurateManifestTolerance to 0
3011
3185
  // and rebufferingGoal to 0.01 by default for low latency streaming.
@@ -3072,6 +3246,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
3072
3246
  }
3073
3247
 
3074
3248
  if (this.mediaSourceEngine_) {
3249
+ this.mediaSourceEngine_.configure(this.config_.mediaSource);
3075
3250
  const {segmentRelativeVttTiming} = this.config_.manifest;
3076
3251
  this.mediaSourceEngine_.setSegmentRelativeVttTiming(
3077
3252
  segmentRelativeVttTiming);
@@ -3359,6 +3534,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
3359
3534
  * @export
3360
3535
  */
3361
3536
  seekRange() {
3537
+ if (!this.fullyLoaded_) {
3538
+ return {'start': 0, 'end': 0};
3539
+ }
3540
+
3362
3541
  if (this.manifest_) {
3363
3542
  const timeline = this.manifest_.presentationTimeline;
3364
3543
 
@@ -3701,8 +3880,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
3701
3880
  const fullImageHeight = imageStream.height || 0;
3702
3881
  const columns = parseInt(match[1], 10);
3703
3882
  const rows = parseInt(match[2], 10);
3704
- const width = fullImageWidth / columns;
3705
- const height = fullImageHeight / rows;
3883
+ let width = fullImageWidth / columns;
3884
+ let height = fullImageHeight / rows;
3706
3885
  const totalImages = columns * rows;
3707
3886
  const segmentDuration = reference.trueEndTime - reference.startTime;
3708
3887
  const thumbnailDuration =
@@ -3728,6 +3907,15 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
3728
3907
  positionX = (thumbnailPosition % columns) * width;
3729
3908
  positionY = Math.floor(thumbnailPosition / columns) * height;
3730
3909
  }
3910
+ let sprite = false;
3911
+ const thumbnailSprite = reference.getThumbnailSprite();
3912
+ if (thumbnailSprite) {
3913
+ sprite = true;
3914
+ height = thumbnailSprite.height;
3915
+ positionX = thumbnailSprite.positionX;
3916
+ positionY = thumbnailSprite.positionY;
3917
+ width = thumbnailSprite.width;
3918
+ }
3731
3919
  return {
3732
3920
  imageHeight: fullImageHeight,
3733
3921
  imageWidth: fullImageWidth,
@@ -3738,6 +3926,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
3738
3926
  duration: thumbnailDuration,
3739
3927
  uris: reference.getUris(),
3740
3928
  width: width,
3929
+ sprite: sprite,
3741
3930
  };
3742
3931
  }
3743
3932
  return null;
@@ -3921,20 +4110,21 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
3921
4110
 
3922
4111
  /**
3923
4112
  * Sets the current audio language and current variant role to the selected
3924
- * language and role, and chooses a new variant if need be. If the player has
3925
- * not loaded any content, this will be a no-op.
4113
+ * language, role and channel count, and chooses a new variant if need be.
4114
+ * If the player has not loaded any content, this will be a no-op.
3926
4115
  *
3927
4116
  * @param {string} language
3928
4117
  * @param {string=} role
4118
+ * @param {number=} channelsCount
3929
4119
  * @export
3930
4120
  */
3931
- selectAudioLanguage(language, role) {
4121
+ selectAudioLanguage(language, role, channelsCount = 0) {
3932
4122
  const LanguageUtils = shaka.util.LanguageUtils;
3933
4123
 
3934
4124
  if (this.manifest_ && this.playhead_) {
3935
4125
  this.currentAdaptationSetCriteria_ =
3936
4126
  new shaka.media.PreferenceBasedCriteria(language, role || '',
3937
- /* channelCount= */ 0, /* label= */ '');
4127
+ channelsCount, /* label= */ '');
3938
4128
 
3939
4129
  const diff = (a, b) => {
3940
4130
  if (!a.video && !b.video) {
@@ -4613,6 +4803,146 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
4613
4803
  return shaka.util.StreamUtils.textStreamToTrack(stream);
4614
4804
  }
4615
4805
 
4806
+ /**
4807
+ * Adds the given thumbnails track to the loaded manifest.
4808
+ * <code>load()</code> must resolve before calling. The presentation must
4809
+ * have a duration.
4810
+ *
4811
+ * This returns the created track, which can immediately be used by the
4812
+ * application.
4813
+ *
4814
+ * @param {string} uri
4815
+ * @param {string=} mimeType
4816
+ * @return {!Promise.<shaka.extern.Track>}
4817
+ * @export
4818
+ */
4819
+ async addThumbnailsTrack(uri, mimeType) {
4820
+ if (this.loadMode_ != shaka.Player.LoadMode.MEDIA_SOURCE &&
4821
+ this.loadMode_ != shaka.Player.LoadMode.SRC_EQUALS) {
4822
+ shaka.log.error(
4823
+ 'Must call load() and wait for it to resolve before adding image ' +
4824
+ 'tracks.');
4825
+ throw new shaka.util.Error(
4826
+ shaka.util.Error.Severity.RECOVERABLE,
4827
+ shaka.util.Error.Category.PLAYER,
4828
+ shaka.util.Error.Code.CONTENT_NOT_LOADED);
4829
+ }
4830
+
4831
+ if (this.loadMode_ == shaka.Player.LoadMode.SRC_EQUALS) {
4832
+ shaka.log.error('Cannot add this thumbnail track when loaded with src=');
4833
+ throw new shaka.util.Error(
4834
+ shaka.util.Error.Severity.RECOVERABLE,
4835
+ shaka.util.Error.Category.TEXT,
4836
+ shaka.util.Error.Code.CANNOT_ADD_EXTERNAL_THUMBNAILS_TO_SRC_EQUALS);
4837
+ }
4838
+
4839
+ if (!mimeType) {
4840
+ mimeType = await this.getTextMimetype_(uri);
4841
+ }
4842
+
4843
+ if (mimeType != 'text/vtt') {
4844
+ throw new shaka.util.Error(
4845
+ shaka.util.Error.Severity.RECOVERABLE,
4846
+ shaka.util.Error.Category.TEXT,
4847
+ shaka.util.Error.Code.UNSUPPORTED_EXTERNAL_THUMBNAILS_URI,
4848
+ uri);
4849
+ }
4850
+
4851
+ const ContentType = shaka.util.ManifestParserUtils.ContentType;
4852
+
4853
+ const duration = this.manifest_.presentationTimeline.getDuration();
4854
+ if (duration == Infinity) {
4855
+ throw new shaka.util.Error(
4856
+ shaka.util.Error.Severity.RECOVERABLE,
4857
+ shaka.util.Error.Category.MANIFEST,
4858
+ shaka.util.Error.Code.CANNOT_ADD_EXTERNAL_THUMBNAILS_TO_LIVE_STREAM);
4859
+ }
4860
+
4861
+ goog.asserts.assert(
4862
+ this.networkingEngine_, 'Need networking engine.');
4863
+ const buffer = await this.getTextData_(uri,
4864
+ this.networkingEngine_,
4865
+ this.config_.streaming.retryParameters);
4866
+
4867
+ const factory = shaka.text.TextEngine.findParser(mimeType);
4868
+ if (!factory) {
4869
+ throw new shaka.util.Error(
4870
+ shaka.util.Error.Severity.CRITICAL,
4871
+ shaka.util.Error.Category.TEXT,
4872
+ shaka.util.Error.Code.MISSING_TEXT_PLUGIN,
4873
+ mimeType);
4874
+ }
4875
+ const TextParser = factory();
4876
+ const time = {
4877
+ periodStart: 0,
4878
+ segmentStart: 0,
4879
+ segmentEnd: duration,
4880
+ vttOffset: 0,
4881
+ };
4882
+ const data = shaka.util.BufferUtils.toUint8(buffer);
4883
+ const cues = TextParser.parseMedia(data, time);
4884
+
4885
+ const references = [];
4886
+ for (const cue of cues) {
4887
+ const imageUri = shaka.util.ManifestParserUtils.resolveUris(
4888
+ [uri], [cue.payload])[0];
4889
+ const reference = new shaka.media.SegmentReference(
4890
+ cue.startTime,
4891
+ cue.endTime,
4892
+ () => [imageUri],
4893
+ /* startByte= */ 0,
4894
+ /* endByte= */ null,
4895
+ /* initSegmentReference= */ null,
4896
+ /* timestampOffset= */ 0,
4897
+ /* appendWindowStart= */ 0,
4898
+ /* appendWindowEnd= */ Infinity,
4899
+ );
4900
+ if (imageUri.includes('#xywh')) {
4901
+ const spriteInfo = imageUri.split('#xywh=')[1].split(',');
4902
+ if (spriteInfo.length === 4) {
4903
+ reference.setThumbnailSprite({
4904
+ height: parseInt(spriteInfo[3], 10),
4905
+ positionX: parseInt(spriteInfo[0], 10),
4906
+ positionY: parseInt(spriteInfo[1], 10),
4907
+ width: parseInt(spriteInfo[2], 10),
4908
+ });
4909
+ }
4910
+ }
4911
+ references.push(reference);
4912
+ }
4913
+
4914
+ /** @type {shaka.extern.Stream} */
4915
+ const stream = {
4916
+ id: this.nextExternalStreamId_++,
4917
+ originalId: null,
4918
+ createSegmentIndex: () => Promise.resolve(),
4919
+ segmentIndex: new shaka.media.SegmentIndex(references),
4920
+ mimeType: mimeType || '',
4921
+ codecs: '',
4922
+ kind: '',
4923
+ encrypted: false,
4924
+ drmInfos: [],
4925
+ keyIds: new Set(),
4926
+ language: 'und',
4927
+ label: null,
4928
+ type: ContentType.IMAGE,
4929
+ primary: false,
4930
+ trickModeVideo: null,
4931
+ emsgSchemeIdUris: null,
4932
+ roles: [],
4933
+ forced: false,
4934
+ channelsCount: null,
4935
+ audioSamplingRate: null,
4936
+ spatialAudio: false,
4937
+ closedCaptions: null,
4938
+ tilesLayout: '1x1',
4939
+ };
4940
+
4941
+ this.manifest_.imageStreams.push(stream);
4942
+ this.onTracksChanged_();
4943
+ return shaka.util.StreamUtils.imageStreamToTrack(stream);
4944
+ }
4945
+
4616
4946
  /**
4617
4947
  * Adds the given chapters track to the loaded manifest. <code>load()</code>
4618
4948
  * must resolve before calling. The presentation must have a duration.
@@ -5084,28 +5414,39 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5084
5414
  }
5085
5415
 
5086
5416
  // We may need to create new sessions for any new init data.
5087
- const curDrmInfo = this.drmEngine_ ? this.drmEngine_.getDrmInfo() : null;
5417
+ const currentDrmInfo =
5418
+ this.drmEngine_ ? this.drmEngine_.getDrmInfo() : null;
5088
5419
  // DrmEngine.newInitData() requires mediaKeys to be available.
5089
- if (curDrmInfo && this.drmEngine_.getMediaKeys()) {
5420
+ if (currentDrmInfo && this.drmEngine_.getMediaKeys()) {
5090
5421
  for (const variant of manifest.variants) {
5091
- const videoDrmInfos = variant.video ? variant.video.drmInfos : [];
5092
- const audioDrmInfos = variant.audio ? variant.audio.drmInfos : [];
5093
- const drmInfos = videoDrmInfos.concat(audioDrmInfos);
5094
- for (const drmInfo of drmInfos) {
5095
- // Ignore any data for different key systems.
5096
- if (drmInfo.keySystem == curDrmInfo.keySystem) {
5097
- for (const initData of (drmInfo.initData || [])) {
5098
- this.drmEngine_.newInitData(
5099
- initData.initDataType, initData.initData);
5100
- }
5101
- }
5102
- }
5422
+ this.processDrmInfos_(currentDrmInfo.keySystem, variant.video);
5423
+ this.processDrmInfos_(currentDrmInfo.keySystem, variant.audio);
5103
5424
  }
5104
5425
  }
5105
-
5106
5426
  this.checkRestrictedVariants_(manifest);
5107
5427
  }
5108
5428
 
5429
+ /**
5430
+ * @param {string} keySystem
5431
+ * @param {?shaka.extern.Stream} stream
5432
+ * @private
5433
+ */
5434
+ processDrmInfos_(keySystem, stream) {
5435
+ if (!stream) {
5436
+ return;
5437
+ }
5438
+
5439
+ for (const drmInfo of stream.drmInfos) {
5440
+ // Ignore any data for different key systems.
5441
+ if (drmInfo.keySystem == keySystem) {
5442
+ for (const initData of (drmInfo.initData || [])) {
5443
+ this.drmEngine_.newInitData(
5444
+ initData.initDataType, initData.initData);
5445
+ }
5446
+ }
5447
+ }
5448
+ }
5449
+
5109
5450
  /**
5110
5451
  * @private
5111
5452
  */
@@ -5481,38 +5822,62 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5481
5822
  /**
5482
5823
  * Check if we should show text on screen automatically.
5483
5824
  *
5484
- * The text should automatically be shown if the text is language-compatible
5485
- * with the user's text language preference, but not compatible with the
5486
- * audio.
5487
- *
5488
- * For example:
5489
- * preferred | chosen | chosen |
5490
- * text | text | audio | show
5491
- * -----------------------------------
5492
- * en-CA | en | jp | true
5493
- * en | en-US | fr | true
5494
- * fr-CA | en-US | jp | false
5495
- * en-CA | en-US | en-US | false
5496
- *
5497
5825
  * @param {shaka.extern.Stream} audioStream
5498
5826
  * @param {shaka.extern.Stream} textStream
5499
5827
  * @return {boolean}
5500
5828
  * @private
5501
5829
  */
5502
5830
  shouldInitiallyShowText_(audioStream, textStream) {
5831
+ const AutoShowText = shaka.config.AutoShowText;
5832
+
5833
+ if (this.config_.autoShowText == AutoShowText.NEVER) {
5834
+ return false;
5835
+ }
5836
+ if (this.config_.autoShowText == AutoShowText.ALWAYS) {
5837
+ return true;
5838
+ }
5839
+
5503
5840
  const LanguageUtils = shaka.util.LanguageUtils;
5504
5841
 
5505
5842
  /** @type {string} */
5506
5843
  const preferredTextLocale =
5507
5844
  LanguageUtils.normalize(this.config_.preferredTextLanguage);
5508
5845
  /** @type {string} */
5509
- const audioLocale = LanguageUtils.normalize(audioStream.language);
5510
- /** @type {string} */
5511
5846
  const textLocale = LanguageUtils.normalize(textStream.language);
5512
5847
 
5513
- return (
5514
- LanguageUtils.areLanguageCompatible(textLocale, preferredTextLocale) &&
5515
- !LanguageUtils.areLanguageCompatible(audioLocale, textLocale));
5848
+ if (this.config_.autoShowText == AutoShowText.IF_PREFERRED_TEXT_LANGUAGE) {
5849
+ // Only the text language match matters.
5850
+ return LanguageUtils.areLanguageCompatible(
5851
+ textLocale,
5852
+ preferredTextLocale);
5853
+ }
5854
+
5855
+ if (this.config_.autoShowText == AutoShowText.IF_SUBTITLES_MAY_BE_NEEDED) {
5856
+ /* The text should automatically be shown if the text is
5857
+ * language-compatible with the user's text language preference, but not
5858
+ * compatible with the audio. These are cases where we deduce that
5859
+ * subtitles may be needed.
5860
+ *
5861
+ * For example:
5862
+ * preferred | chosen | chosen |
5863
+ * text | text | audio | show
5864
+ * -----------------------------------
5865
+ * en-CA | en | jp | true
5866
+ * en | en-US | fr | true
5867
+ * fr-CA | en-US | jp | false
5868
+ * en-CA | en-US | en-US | false
5869
+ *
5870
+ */
5871
+ /** @type {string} */
5872
+ const audioLocale = LanguageUtils.normalize(audioStream.language);
5873
+
5874
+ return (
5875
+ LanguageUtils.areLanguageCompatible(textLocale, preferredTextLocale) &&
5876
+ !LanguageUtils.areLanguageCompatible(audioLocale, textLocale));
5877
+ }
5878
+
5879
+ shaka.log.alwaysWarn('Invalid autoShowText setting!');
5880
+ return false;
5516
5881
  }
5517
5882
 
5518
5883
  /**
@@ -5531,7 +5896,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5531
5896
  *
5532
5897
  * @private
5533
5898
  */
5534
- onSegmentAppended_() {
5899
+ onSegmentAppended_(start, end, contentType) {
5535
5900
  // When we append a segment to media source (via streaming engine) we are
5536
5901
  // changing what data we have buffered, so notify the playhead of the
5537
5902
  // change.
@@ -5539,6 +5904,14 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5539
5904
  this.playhead_.notifyOfBufferingChange();
5540
5905
  }
5541
5906
  this.pollBufferState_();
5907
+
5908
+ // Dispatch an event for users to consume, too.
5909
+ const data = new Map()
5910
+ .set('start', start)
5911
+ .set('end', end)
5912
+ .set('contentType', contentType);
5913
+ this.dispatchEvent(this.makeEvent_(
5914
+ shaka.util.FakeEvent.EventName.SegmentAppended, data));
5542
5915
  }
5543
5916
 
5544
5917
  /**
@@ -5584,6 +5957,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5584
5957
  const data = new Map()
5585
5958
  .set('oldTrack', from)
5586
5959
  .set('newTrack', to);
5960
+ if (this.lcevcDil_) {
5961
+ this.lcevcDil_.updateVariant(to);
5962
+ }
5587
5963
  const event =
5588
5964
  this.makeEvent_(shaka.util.FakeEvent.EventName.Adaptation, data);
5589
5965
  this.delayDispatchEvent_(event);
@@ -5612,6 +5988,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5612
5988
  const data = new Map()
5613
5989
  .set('oldTrack', from)
5614
5990
  .set('newTrack', to);
5991
+ if (this.lcevcDil_) {
5992
+ this.lcevcDil_.updateVariant(to);
5993
+ }
5994
+
5615
5995
  const event =
5616
5996
  this.makeEvent_(shaka.util.FakeEvent.EventName.VariantChanged, data);
5617
5997
  this.delayDispatchEvent_(event);
@@ -6556,7 +6936,6 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
6556
6936
  }
6557
6937
  };
6558
6938
 
6559
-
6560
6939
  /**
6561
6940
  * In order to know what method of loading the player used for some content, we
6562
6941
  * have this enum. It lets us know if content has not been loaded, loaded with
@@ -6595,7 +6974,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
6595
6974
  * @define {string} A version number taken from git at compile time.
6596
6975
  * @export
6597
6976
  */
6598
- shaka.Player.version = 'v4.2.4-uncompiled';
6977
+ shaka.Player.version = 'v4.3.0-uncompiled';
6599
6978
 
6600
6979
  // Initialize the deprecation system using the version string we just set
6601
6980
  // on the player.