livekit-client 1.1.1 → 1.1.4

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 (31) hide show
  1. package/dist/livekit-client.esm.mjs +299 -122
  2. package/dist/livekit-client.esm.mjs.map +1 -1
  3. package/dist/livekit-client.umd.js +1 -1
  4. package/dist/livekit-client.umd.js.map +1 -1
  5. package/dist/src/proto/livekit_rtc.d.ts +7 -0
  6. package/dist/src/proto/livekit_rtc.d.ts.map +1 -1
  7. package/dist/src/room/Room.d.ts +2 -0
  8. package/dist/src/room/Room.d.ts.map +1 -1
  9. package/dist/src/room/participant/LocalParticipant.d.ts +7 -2
  10. package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
  11. package/dist/src/room/participant/RemoteParticipant.d.ts.map +1 -1
  12. package/dist/src/room/track/LocalAudioTrack.d.ts +1 -1
  13. package/dist/src/room/track/LocalAudioTrack.d.ts.map +1 -1
  14. package/dist/src/room/track/LocalTrack.d.ts +4 -2
  15. package/dist/src/room/track/LocalTrack.d.ts.map +1 -1
  16. package/dist/src/room/track/LocalVideoTrack.d.ts +3 -2
  17. package/dist/src/room/track/LocalVideoTrack.d.ts.map +1 -1
  18. package/dist/src/room/track/RemoteVideoTrack.d.ts.map +1 -1
  19. package/dist/src/room/utils.d.ts.map +1 -1
  20. package/package.json +1 -1
  21. package/src/proto/livekit_rtc.ts +12 -0
  22. package/src/room/Room.ts +79 -30
  23. package/src/room/participant/LocalParticipant.ts +135 -61
  24. package/src/room/participant/RemoteParticipant.ts +5 -11
  25. package/src/room/participant/publishUtils.ts +2 -2
  26. package/src/room/track/LocalAudioTrack.ts +7 -3
  27. package/src/room/track/LocalTrack.ts +11 -2
  28. package/src/room/track/LocalVideoTrack.ts +41 -10
  29. package/src/room/track/RemoteVideoTrack.ts +30 -2
  30. package/src/room/track/create.ts +2 -2
  31. package/src/room/utils.ts +3 -2
@@ -7049,7 +7049,8 @@ function createBaseAddTrackRequest() {
7049
7049
  disableDtx: false,
7050
7050
  source: 0,
7051
7051
  layers: [],
7052
- simulcastCodecs: []
7052
+ simulcastCodecs: [],
7053
+ sid: ''
7053
7054
  };
7054
7055
  }
7055
7056
 
@@ -7097,6 +7098,10 @@ const AddTrackRequest = {
7097
7098
  SimulcastCodec.encode(v, writer.uint32(82).fork()).ldelim();
7098
7099
  }
7099
7100
 
7101
+ if (message.sid !== '') {
7102
+ writer.uint32(90).string(message.sid);
7103
+ }
7104
+
7100
7105
  return writer;
7101
7106
  },
7102
7107
 
@@ -7149,6 +7154,10 @@ const AddTrackRequest = {
7149
7154
  message.simulcastCodecs.push(SimulcastCodec.decode(reader, reader.uint32()));
7150
7155
  break;
7151
7156
 
7157
+ case 11:
7158
+ message.sid = reader.string();
7159
+ break;
7160
+
7152
7161
  default:
7153
7162
  reader.skipType(tag & 7);
7154
7163
  break;
@@ -7169,7 +7178,8 @@ const AddTrackRequest = {
7169
7178
  disableDtx: isSet(object.disableDtx) ? Boolean(object.disableDtx) : false,
7170
7179
  source: isSet(object.source) ? trackSourceFromJSON(object.source) : 0,
7171
7180
  layers: Array.isArray(object === null || object === void 0 ? void 0 : object.layers) ? object.layers.map(e => VideoLayer.fromJSON(e)) : [],
7172
- simulcastCodecs: Array.isArray(object === null || object === void 0 ? void 0 : object.simulcastCodecs) ? object.simulcastCodecs.map(e => SimulcastCodec.fromJSON(e)) : []
7181
+ simulcastCodecs: Array.isArray(object === null || object === void 0 ? void 0 : object.simulcastCodecs) ? object.simulcastCodecs.map(e => SimulcastCodec.fromJSON(e)) : [],
7182
+ sid: isSet(object.sid) ? String(object.sid) : ''
7173
7183
  };
7174
7184
  },
7175
7185
 
@@ -7196,11 +7206,12 @@ const AddTrackRequest = {
7196
7206
  obj.simulcastCodecs = [];
7197
7207
  }
7198
7208
 
7209
+ message.sid !== undefined && (obj.sid = message.sid);
7199
7210
  return obj;
7200
7211
  },
7201
7212
 
7202
7213
  fromPartial(object) {
7203
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
7214
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
7204
7215
 
7205
7216
  const message = createBaseAddTrackRequest();
7206
7217
  message.cid = (_a = object.cid) !== null && _a !== void 0 ? _a : '';
@@ -7213,6 +7224,7 @@ const AddTrackRequest = {
7213
7224
  message.source = (_h = object.source) !== null && _h !== void 0 ? _h : 0;
7214
7225
  message.layers = ((_j = object.layers) === null || _j === void 0 ? void 0 : _j.map(e => VideoLayer.fromPartial(e))) || [];
7215
7226
  message.simulcastCodecs = ((_k = object.simulcastCodecs) === null || _k === void 0 ? void 0 : _k.map(e => SimulcastCodec.fromPartial(e))) || [];
7227
+ message.sid = (_l = object.sid) !== null && _l !== void 0 ? _l : '';
7216
7228
  return message;
7217
7229
  }
7218
7230
 
@@ -10058,7 +10070,7 @@ function computeBitrate(currentStats, prevStats) {
10058
10070
  return (bytesNow - bytesPrev) * 8 * 1000 / (currentStats.timestamp - prevStats.timestamp);
10059
10071
  }
10060
10072
 
10061
- var version$1 = "1.1.1";
10073
+ var version$1 = "1.1.4";
10062
10074
 
10063
10075
  const version = version$1;
10064
10076
  const protocolVersion = 8;
@@ -10130,9 +10142,10 @@ function getEmptyVideoStreamTrack() {
10130
10142
  var _a;
10131
10143
 
10132
10144
  if (!emptyVideoStreamTrack) {
10133
- const canvas = document.createElement('canvas');
10134
- canvas.width = 2;
10135
- canvas.height = 2;
10145
+ const canvas = document.createElement('canvas'); // the canvas size is set to 16, because electron apps seem to fail with smaller values
10146
+
10147
+ canvas.width = 16;
10148
+ canvas.height = 16;
10136
10149
  (_a = canvas.getContext('2d')) === null || _a === void 0 ? void 0 : _a.fillRect(0, 0, canvas.width, canvas.height); // @ts-ignore
10137
10150
 
10138
10151
  const emptyStream = canvas.captureStream();
@@ -11047,6 +11060,7 @@ function detachTrack(track, element) {
11047
11060
 
11048
11061
  class LocalTrack extends Track {
11049
11062
  constructor(mediaTrack, kind, constraints) {
11063
+ let userProvidedTrack = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
11050
11064
  super(mediaTrack, kind);
11051
11065
  this._isUpstreamPaused = false;
11052
11066
 
@@ -11063,6 +11077,7 @@ class LocalTrack extends Track {
11063
11077
  this.constraints = constraints !== null && constraints !== void 0 ? constraints : mediaTrack.getConstraints();
11064
11078
  this.reacquireTrack = false;
11065
11079
  this.wasMuted = false;
11080
+ this.providedByUser = userProvidedTrack;
11066
11081
  }
11067
11082
 
11068
11083
  get id() {
@@ -11092,6 +11107,10 @@ class LocalTrack extends Track {
11092
11107
  get isUpstreamPaused() {
11093
11108
  return this._isUpstreamPaused;
11094
11109
  }
11110
+
11111
+ get isUserProvided() {
11112
+ return this.providedByUser;
11113
+ }
11095
11114
  /**
11096
11115
  * @returns DeviceID of the device that is currently being used for this track
11097
11116
  */
@@ -11123,6 +11142,8 @@ class LocalTrack extends Track {
11123
11142
  }
11124
11143
 
11125
11144
  async replaceTrack(track) {
11145
+ let userProvidedTrack = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
11146
+
11126
11147
  if (!this.sender) {
11127
11148
  throw new TrackInvalidError('unable to replace an unpublished track');
11128
11149
  } // detach
@@ -11151,6 +11172,7 @@ class LocalTrack extends Track {
11151
11172
  attachToElement(track, el);
11152
11173
  });
11153
11174
  this.mediaStream = new MediaStream([track]);
11175
+ this.providedByUser = userProvidedTrack;
11154
11176
  return this;
11155
11177
  }
11156
11178
 
@@ -11222,7 +11244,7 @@ class LocalTrack extends Track {
11222
11244
  if (!isMobile()) return;
11223
11245
  livekitLogger.debug("visibility changed, is in Background: ".concat(this.isInBackground));
11224
11246
 
11225
- if (!this.isInBackground && this.needsReAcquisition) {
11247
+ if (!this.isInBackground && this.needsReAcquisition && !this.isUserProvided) {
11226
11248
  livekitLogger.debug("track needs to be reaquired, restarting ".concat(this.source));
11227
11249
  await this.restart();
11228
11250
  this.reacquireTrack = false; // Restore muted state if had to be restarted
@@ -11373,7 +11395,8 @@ function getNewAudioContext() {
11373
11395
 
11374
11396
  class LocalAudioTrack extends LocalTrack {
11375
11397
  constructor(mediaTrack, constraints) {
11376
- super(mediaTrack, Track.Kind.Audio, constraints);
11398
+ let userProvidedTrack = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
11399
+ super(mediaTrack, Track.Kind.Audio, constraints, userProvidedTrack);
11377
11400
  /** @internal */
11378
11401
 
11379
11402
  this.stopOnMute = false;
@@ -11433,7 +11456,7 @@ class LocalAudioTrack extends LocalTrack {
11433
11456
  }
11434
11457
 
11435
11458
  async unmute() {
11436
- if (this.source === Track.Source.Microphone && this.stopOnMute) {
11459
+ if (this.source === Track.Source.Microphone && this.stopOnMute && !this.isUserProvided) {
11437
11460
  livekitLogger.debug('reacquiring mic track');
11438
11461
  await this.restartTrack();
11439
11462
  }
@@ -11514,9 +11537,11 @@ class LocalAudioTrack extends LocalTrack {
11514
11537
 
11515
11538
  }
11516
11539
 
11540
+ const refreshSubscribedCodecAfterNewCodec = 5000;
11517
11541
  class LocalVideoTrack extends LocalTrack {
11518
11542
  constructor(mediaTrack, constraints) {
11519
- super(mediaTrack, Track.Kind.Video, constraints);
11543
+ let userProvidedTrack = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
11544
+ super(mediaTrack, Track.Kind.Video, constraints, userProvidedTrack);
11520
11545
  this.simulcastCodecs = new Map();
11521
11546
 
11522
11547
  this.monitorSender = async () => {
@@ -11613,7 +11638,7 @@ class LocalVideoTrack extends LocalTrack {
11613
11638
  }
11614
11639
 
11615
11640
  async unmute() {
11616
- if (this.source === Track.Source.Camera) {
11641
+ if (this.source === Track.Source.Camera && !this.isUserProvided) {
11617
11642
  livekitLogger.debug('reacquiring camera track');
11618
11643
  await this.restartTrack();
11619
11644
  }
@@ -11730,7 +11755,14 @@ class LocalVideoTrack extends LocalTrack {
11730
11755
  return;
11731
11756
  }
11732
11757
 
11733
- simulcastCodecInfo.sender = sender;
11758
+ simulcastCodecInfo.sender = sender; // browser will reenable disabled codec/layers after new codec has been published,
11759
+ // so refresh subscribedCodecs after publish a new codec
11760
+
11761
+ setTimeout(() => {
11762
+ if (this.subscribedCodecs) {
11763
+ this.setPublishingCodecs(this.subscribedCodecs);
11764
+ }
11765
+ }, refreshSubscribedCodecAfterNewCodec);
11734
11766
  }
11735
11767
  /**
11736
11768
  * @internal
@@ -11739,25 +11771,41 @@ class LocalVideoTrack extends LocalTrack {
11739
11771
 
11740
11772
 
11741
11773
  async setPublishingCodecs(codecs) {
11742
- livekitLogger.debug('setting publishing codecs', codecs);
11774
+ livekitLogger.debug('setting publishing codecs', {
11775
+ codecs,
11776
+ currentCodec: this.codec
11777
+ }); // only enable simulcast codec for preference codec setted
11778
+
11779
+ if (!this.codec && codecs.length > 0) {
11780
+ await this.setPublishingLayers(codecs[0].qualities);
11781
+ return [];
11782
+ }
11783
+
11784
+ this.subscribedCodecs = codecs;
11785
+ const newCodecs = [];
11743
11786
 
11744
11787
  for await (const codec of codecs) {
11745
- if (this.codec === codec.codec) {
11788
+ if (!this.codec || this.codec === codec.codec) {
11746
11789
  await this.setPublishingLayers(codec.qualities);
11747
11790
  } else {
11748
11791
  const simulcastCodecInfo = this.simulcastCodecs.get(codec.codec);
11749
11792
  livekitLogger.debug("try setPublishingCodec for ".concat(codec.codec), simulcastCodecInfo);
11750
11793
 
11751
11794
  if (!simulcastCodecInfo || !simulcastCodecInfo.sender) {
11752
- return;
11753
- }
11754
-
11755
- if (simulcastCodecInfo.encodings) {
11795
+ for (const q of codec.qualities) {
11796
+ if (q.enabled) {
11797
+ newCodecs.push(codec.codec);
11798
+ break;
11799
+ }
11800
+ }
11801
+ } else if (simulcastCodecInfo.encodings) {
11756
11802
  livekitLogger.debug("try setPublishingLayersForSender ".concat(codec.codec));
11757
11803
  await setPublishingLayersForSender(simulcastCodecInfo.sender, simulcastCodecInfo.encodings, codec.qualities);
11758
11804
  }
11759
11805
  }
11760
11806
  }
11807
+
11808
+ return newCodecs;
11761
11809
  }
11762
11810
  /**
11763
11811
  * @internal
@@ -12188,6 +12236,8 @@ class RemoteVideoTrack extends RemoteTrack {
12188
12236
 
12189
12237
  this.debouncedHandleResize();
12190
12238
  this.updateVisibility();
12239
+ } else {
12240
+ livekitLogger.warn('visibility resize observer not triggered');
12191
12241
  }
12192
12242
  }
12193
12243
  /**
@@ -12336,9 +12386,7 @@ class RemoteVideoTrack extends RemoteTrack {
12336
12386
  }
12337
12387
 
12338
12388
  class HTMLElementInfo {
12339
- constructor(element) {
12340
- let visible = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
12341
-
12389
+ constructor(element, visible) {
12342
12390
  this.onVisibilityChanged = entry => {
12343
12391
  var _a;
12344
12392
 
@@ -12355,7 +12403,7 @@ class HTMLElementInfo {
12355
12403
  };
12356
12404
 
12357
12405
  this.element = element;
12358
- this.visible = visible;
12406
+ this.visible = visible !== null && visible !== void 0 ? visible : isElementInViewport(element);
12359
12407
  this.visibilityChangedAt = 0;
12360
12408
  }
12361
12409
 
@@ -12386,6 +12434,29 @@ class HTMLElementInfo {
12386
12434
  (_b = getResizeObserver()) === null || _b === void 0 ? void 0 : _b.unobserve(this.element);
12387
12435
  }
12388
12436
 
12437
+ } // does not account for occlusion by other elements
12438
+
12439
+
12440
+ function isElementInViewport(el) {
12441
+ let top = el.offsetTop;
12442
+ let left = el.offsetLeft;
12443
+ const width = el.offsetWidth;
12444
+ const height = el.offsetHeight;
12445
+ const {
12446
+ hidden
12447
+ } = el;
12448
+ const {
12449
+ opacity,
12450
+ display
12451
+ } = getComputedStyle(el);
12452
+
12453
+ while (el.offsetParent) {
12454
+ el = el.offsetParent;
12455
+ top += el.offsetTop;
12456
+ left += el.offsetLeft;
12457
+ }
12458
+
12459
+ return top < window.pageYOffset + window.innerHeight && left < window.pageXOffset + window.innerWidth && top + height > window.pageYOffset && left + width > window.pageXOffset && !hidden && (opacity !== '' ? parseFloat(opacity) > 0 : true) && display !== 'none';
12389
12460
  }
12390
12461
 
12391
12462
  class TrackPublication extends events.exports.EventEmitter {
@@ -12895,10 +12966,10 @@ function trackPermissionToProto(perms) {
12895
12966
  function mediaTrackToLocalTrack(mediaStreamTrack, constraints) {
12896
12967
  switch (mediaStreamTrack.kind) {
12897
12968
  case 'audio':
12898
- return new LocalAudioTrack(mediaStreamTrack, constraints);
12969
+ return new LocalAudioTrack(mediaStreamTrack, constraints, false);
12899
12970
 
12900
12971
  case 'video':
12901
- return new LocalVideoTrack(mediaStreamTrack, constraints);
12972
+ return new LocalVideoTrack(mediaStreamTrack, constraints, false);
12902
12973
 
12903
12974
  default:
12904
12975
  throw new TrackInvalidError("unsupported track type: ".concat(mediaStreamTrack.kind));
@@ -13358,9 +13429,7 @@ class RemoteParticipant extends Participant {
13358
13429
 
13359
13430
 
13360
13431
  static fromParticipantInfo(signalClient, pi) {
13361
- const rp = new RemoteParticipant(signalClient, pi.sid, pi.identity);
13362
- rp.updateInfo(pi);
13363
- return rp;
13432
+ return new RemoteParticipant(signalClient, pi.sid, pi.identity);
13364
13433
  }
13365
13434
 
13366
13435
  addTrackPublication(publication) {
@@ -13501,7 +13570,6 @@ class RemoteParticipant extends Participant {
13501
13570
 
13502
13571
 
13503
13572
  updateInfo(info) {
13504
- const alreadyHasMetadata = this.hasMetadata;
13505
13573
  super.updateInfo(info); // we are getting a list of all available tracks, reconcile in here
13506
13574
  // and send out events for changes
13507
13575
  // reconcile track publications, publish events only if metadata is already there
@@ -13529,14 +13597,11 @@ class RemoteParticipant extends Participant {
13529
13597
  }
13530
13598
 
13531
13599
  validTracks.set(ti.sid, publication);
13532
- }); // send new tracks
13533
-
13534
- if (alreadyHasMetadata) {
13535
- newTracks.forEach(publication => {
13536
- this.emit(ParticipantEvent.TrackPublished, publication);
13537
- });
13538
- } // detect removed tracks
13600
+ }); // always emit events for new publications, Room will not forward them unless it's ready
13539
13601
 
13602
+ newTracks.forEach(publication => {
13603
+ this.emit(ParticipantEvent.TrackPublished, publication);
13604
+ }); // detect removed tracks
13540
13605
 
13541
13606
  this.tracks.forEach(publication => {
13542
13607
  if (!validTracks.has(publication.trackSid)) {
@@ -13605,7 +13670,7 @@ class RemoteParticipant extends Participant {
13605
13670
 
13606
13671
  }
13607
13672
 
13608
- const compatibleCodecForSVC = 'vp8';
13673
+ const compatibleCodec = 'vp8';
13609
13674
  class LocalParticipant extends Participant {
13610
13675
  /** @internal */
13611
13676
  constructor(sid, identity, engine, options) {
@@ -13654,8 +13719,8 @@ class LocalParticipant extends Participant {
13654
13719
  this.onTrackMuted(track, track.isMuted);
13655
13720
  };
13656
13721
 
13657
- this.handleSubscribedQualityUpdate = update => {
13658
- var _a, _b, _c;
13722
+ this.handleSubscribedQualityUpdate = async update => {
13723
+ var _a, _b;
13659
13724
 
13660
13725
  if (!((_a = this.roomOptions) === null || _a === void 0 ? void 0 : _a.dynacast)) {
13661
13726
  return;
@@ -13672,9 +13737,18 @@ class LocalParticipant extends Participant {
13672
13737
  }
13673
13738
 
13674
13739
  if (update.subscribedCodecs.length > 0) {
13675
- (_b = pub.videoTrack) === null || _b === void 0 ? void 0 : _b.setPublishingCodecs(update.subscribedCodecs);
13740
+ if (!pub.videoTrack) {
13741
+ return;
13742
+ }
13743
+
13744
+ const newCodecs = await pub.videoTrack.setPublishingCodecs(update.subscribedCodecs);
13745
+
13746
+ for await (const codec of newCodecs) {
13747
+ livekitLogger.debug("publish ".concat(codec, " for ").concat(pub.videoTrack.sid));
13748
+ await this.publishAdditionalCodecForTrack(pub.videoTrack, codec, pub.options);
13749
+ }
13676
13750
  } else if (update.subscribedQualities.length > 0) {
13677
- (_c = pub.videoTrack) === null || _c === void 0 ? void 0 : _c.setPublishingLayers(update.subscribedQualities);
13751
+ (_b = pub.videoTrack) === null || _b === void 0 ? void 0 : _b.setPublishingLayers(update.subscribedQualities);
13678
13752
  }
13679
13753
  };
13680
13754
 
@@ -14002,12 +14076,12 @@ class LocalParticipant extends Participant {
14002
14076
  throw new TrackInvalidError('no video track found');
14003
14077
  }
14004
14078
 
14005
- const screenVideo = new LocalVideoTrack(tracks[0]);
14079
+ const screenVideo = new LocalVideoTrack(tracks[0], undefined, false);
14006
14080
  screenVideo.source = Track.Source.ScreenShare;
14007
14081
  const localTracks = [screenVideo];
14008
14082
 
14009
14083
  if (stream.getAudioTracks().length > 0) {
14010
- const screenAudio = new LocalAudioTrack(stream.getAudioTracks()[0]);
14084
+ const screenAudio = new LocalAudioTrack(stream.getAudioTracks()[0], undefined, false);
14011
14085
  screenAudio.source = Track.Source.ScreenShareAudio;
14012
14086
  localTracks.push(screenAudio);
14013
14087
  }
@@ -14030,11 +14104,11 @@ class LocalParticipant extends Participant {
14030
14104
  if (track instanceof MediaStreamTrack) {
14031
14105
  switch (track.kind) {
14032
14106
  case 'audio':
14033
- track = new LocalAudioTrack(track);
14107
+ track = new LocalAudioTrack(track, undefined, true);
14034
14108
  break;
14035
14109
 
14036
14110
  case 'video':
14037
- track = new LocalVideoTrack(track);
14111
+ track = new LocalVideoTrack(track, undefined, true);
14038
14112
  break;
14039
14113
 
14040
14114
  default:
@@ -14088,7 +14162,6 @@ class LocalParticipant extends Participant {
14088
14162
 
14089
14163
  let encodings;
14090
14164
  let simEncodings;
14091
- let simulcastTracks;
14092
14165
 
14093
14166
  if (track.kind === Track.Kind.Video) {
14094
14167
  // TODO: support react native, which doesn't expose getSettings
@@ -14099,26 +14172,30 @@ class LocalParticipant extends Participant {
14099
14172
  req.width = width !== null && width !== void 0 ? width : 0;
14100
14173
  req.height = height !== null && height !== void 0 ? height : 0; // for svc codecs, disable simulcast and use vp8 for backup codec
14101
14174
 
14102
- if (track instanceof LocalVideoTrack && ((opts === null || opts === void 0 ? void 0 : opts.videoCodec) === 'vp9' || (opts === null || opts === void 0 ? void 0 : opts.videoCodec) === 'av1')) {
14103
- // set scalabilityMode to 'L3T3' by default
14104
- opts.scalabilityMode = (_g = opts.scalabilityMode) !== null && _g !== void 0 ? _g : 'L3T3'; // add backup codec track
14105
-
14106
- const simOpts = _objectSpread2({}, opts);
14107
-
14108
- simOpts.simulcast = true;
14109
- simOpts.scalabilityMode = undefined;
14110
- simEncodings = computeVideoEncodings(track.source === Track.Source.ScreenShare, width, height, simOpts);
14111
- const simulcastTrack = track.addSimulcastTrack(compatibleCodecForSVC, simEncodings);
14112
- simulcastTracks = [simulcastTrack];
14113
- req.simulcastCodecs = [{
14114
- codec: opts.videoCodec,
14115
- cid: track.mediaStreamTrack.id,
14116
- enableSimulcastLayers: true
14117
- }, {
14118
- codec: simulcastTrack.codec,
14119
- cid: simulcastTrack.mediaStreamTrack.id,
14120
- enableSimulcastLayers: true
14121
- }];
14175
+ if (track instanceof LocalVideoTrack) {
14176
+ if ((opts === null || opts === void 0 ? void 0 : opts.videoCodec) === 'vp9' || (opts === null || opts === void 0 ? void 0 : opts.videoCodec) === 'av1') {
14177
+ // set scalabilityMode to 'L3T3' by default
14178
+ opts.scalabilityMode = (_g = opts.scalabilityMode) !== null && _g !== void 0 ? _g : 'L3T3'; // add backup codec track
14179
+
14180
+ const simOpts = _objectSpread2({}, opts);
14181
+
14182
+ simOpts.simulcast = true;
14183
+ simOpts.scalabilityMode = undefined;
14184
+ simEncodings = computeVideoEncodings(track.source === Track.Source.ScreenShare, width, height, simOpts);
14185
+ } // set vp8 codec as backup for any other codecs
14186
+
14187
+
14188
+ if (opts.videoCodec && opts.videoCodec !== 'vp8') {
14189
+ req.simulcastCodecs = [{
14190
+ codec: opts.videoCodec,
14191
+ cid: track.mediaStreamTrack.id,
14192
+ enableSimulcastLayers: true
14193
+ }, {
14194
+ codec: compatibleCodec,
14195
+ cid: '',
14196
+ enableSimulcastLayers: true
14197
+ }];
14198
+ }
14122
14199
  }
14123
14200
 
14124
14201
  encodings = computeVideoEncodings(track.source === Track.Source.ScreenShare, width, height, opts);
@@ -14163,24 +14240,6 @@ class LocalParticipant extends Participant {
14163
14240
  track.codec = opts.videoCodec;
14164
14241
  }
14165
14242
 
14166
- const localTrack = track;
14167
-
14168
- if (simulcastTracks) {
14169
- for await (const simulcastTrack of simulcastTracks) {
14170
- const simTransceiverInit = {
14171
- direction: 'sendonly'
14172
- };
14173
-
14174
- if (simulcastTrack.encodings) {
14175
- simTransceiverInit.sendEncodings = simulcastTrack.encodings;
14176
- }
14177
-
14178
- const simTransceiver = await this.engine.publisher.pc.addTransceiver(simulcastTrack.mediaStreamTrack, simTransceiverInit);
14179
- this.setPreferredCodec(simTransceiver, localTrack.kind, simulcastTrack.codec);
14180
- localTrack.setSimulcastTrackSender(simulcastTrack.codec, simTransceiver.sender);
14181
- }
14182
- }
14183
-
14184
14243
  this.engine.negotiate(); // store RTPSender
14185
14244
 
14186
14245
  track.sender = transceiver.sender;
@@ -14196,6 +14255,86 @@ class LocalParticipant extends Participant {
14196
14255
  this.emit(ParticipantEvent.LocalTrackPublished, publication);
14197
14256
  return publication;
14198
14257
  }
14258
+ /** @internal
14259
+ * publish additional codec to existing track
14260
+ */
14261
+
14262
+
14263
+ async publishAdditionalCodecForTrack(track, videoCodec, options) {
14264
+ var _a, _b, _c, _d, _e;
14265
+
14266
+ const opts = _objectSpread2(_objectSpread2({}, (_a = this.roomOptions) === null || _a === void 0 ? void 0 : _a.publishDefaults), options); // clear scalabilityMode setting for backup codec
14267
+
14268
+
14269
+ opts.scalabilityMode = undefined;
14270
+ opts.videoCodec = videoCodec; // is it not published? if so skip
14271
+
14272
+ let existingPublication;
14273
+ this.tracks.forEach(publication => {
14274
+ if (!publication.track) {
14275
+ return;
14276
+ }
14277
+
14278
+ if (publication.track === track) {
14279
+ existingPublication = publication;
14280
+ }
14281
+ });
14282
+
14283
+ if (!existingPublication) {
14284
+ throw new TrackInvalidError('track is not published');
14285
+ }
14286
+
14287
+ if (!(track instanceof LocalVideoTrack)) {
14288
+ throw new TrackInvalidError('track is not a video track');
14289
+ }
14290
+
14291
+ const settings = track.mediaStreamTrack.getSettings();
14292
+ const width = (_b = settings.width) !== null && _b !== void 0 ? _b : (_c = track.dimensions) === null || _c === void 0 ? void 0 : _c.width;
14293
+ const height = (_d = settings.height) !== null && _d !== void 0 ? _d : (_e = track.dimensions) === null || _e === void 0 ? void 0 : _e.height;
14294
+ const encodings = computeVideoEncodings(track.source === Track.Source.ScreenShare, width, height, opts);
14295
+ const simulcastTrack = track.addSimulcastTrack(opts.videoCodec, encodings);
14296
+ const req = AddTrackRequest.fromPartial({
14297
+ cid: simulcastTrack.mediaStreamTrack.id,
14298
+ type: Track.kindToProto(track.kind),
14299
+ muted: track.isMuted,
14300
+ source: Track.sourceToProto(track.source),
14301
+ sid: track.sid,
14302
+ simulcastCodecs: [{
14303
+ codec: opts.videoCodec,
14304
+ cid: simulcastTrack.mediaStreamTrack.id,
14305
+ enableSimulcastLayers: opts.simulcast
14306
+ }]
14307
+ });
14308
+ req.layers = videoLayersFromEncodings(req.width, req.height, encodings);
14309
+
14310
+ if (!this.engine || this.engine.isClosed) {
14311
+ throw new UnexpectedConnectionState('cannot publish track when not connected');
14312
+ }
14313
+
14314
+ const ti = await this.engine.addTrack(req);
14315
+
14316
+ if (!this.engine.publisher) {
14317
+ throw new UnexpectedConnectionState('publisher is closed');
14318
+ }
14319
+
14320
+ const transceiverInit = {
14321
+ direction: 'sendonly'
14322
+ };
14323
+
14324
+ if (encodings) {
14325
+ transceiverInit.sendEncodings = encodings;
14326
+ } // addTransceiver for react-native is async. web is synchronous, but await won't effect it.
14327
+
14328
+
14329
+ const transceiver = await this.engine.publisher.pc.addTransceiver(simulcastTrack.mediaStreamTrack, transceiverInit);
14330
+ this.setPreferredCodec(transceiver, track.kind, opts.videoCodec);
14331
+ track.setSimulcastTrackSender(opts.videoCodec, transceiver.sender);
14332
+ this.engine.negotiate();
14333
+ livekitLogger.debug("published ".concat(opts.videoCodec, " for track ").concat(track.sid), {
14334
+ encodings,
14335
+ trackInfo: ti
14336
+ });
14337
+ }
14199
14338
 
14200
14339
  unpublishTrack(track, stopOnUnpublish) {
14201
14340
  var _a, _b; // look through all published tracks to find the right ones
@@ -19266,8 +19405,6 @@ class Room extends events.exports.EventEmitter {
19266
19405
  constructor(options) {
19267
19406
  var _this;
19268
19407
 
19269
- var _a, _b;
19270
-
19271
19408
  super();
19272
19409
  _this = this;
19273
19410
  this.state = ConnectionState.Disconnected;
@@ -19363,7 +19500,7 @@ class Room extends events.exports.EventEmitter {
19363
19500
  this.metadata = joinResponse.room.metadata;
19364
19501
  this.emit(RoomEvent.SignalConnected);
19365
19502
  } catch (err) {
19366
- this.engine.close();
19503
+ this.recreateEngine();
19367
19504
  this.setAndEmitConnectionState(ConnectionState.Disconnected);
19368
19505
  throw err;
19369
19506
  } // don't return until ICE connected
@@ -19374,7 +19511,7 @@ class Room extends events.exports.EventEmitter {
19374
19511
 
19375
19512
  const connectTimeout = setTimeout(() => {
19376
19513
  // timeout
19377
- this.engine.close();
19514
+ this.recreateEngine();
19378
19515
  this.setAndEmitConnectionState(ConnectionState.Disconnected);
19379
19516
  reject(new ConnectionError('could not connect after timeout'));
19380
19517
  }, maxICEConnectTimeout);
@@ -19382,7 +19519,7 @@ class Room extends events.exports.EventEmitter {
19382
19519
  const abortHandler = () => {
19383
19520
  livekitLogger.warn('closing engine');
19384
19521
  clearTimeout(connectTimeout);
19385
- this.engine.close();
19522
+ this.recreateEngine();
19386
19523
  this.setAndEmitConnectionState(ConnectionState.Disconnected);
19387
19524
  reject(new ConnectionError('room connection has been cancelled'));
19388
19525
  };
@@ -19447,14 +19584,14 @@ class Room extends events.exports.EventEmitter {
19447
19584
  };
19448
19585
 
19449
19586
  this.handleRestarting = () => {
19450
- if (this.setAndEmitConnectionState(ConnectionState.Reconnecting)) {
19451
- this.emit(RoomEvent.Reconnecting);
19452
- } // also unwind existing participants & existing subscriptions
19453
-
19454
-
19587
+ // also unwind existing participants & existing subscriptions
19455
19588
  for (const p of this.participants.values()) {
19456
19589
  this.handleParticipantDisconnected(p.sid, p);
19457
19590
  }
19591
+
19592
+ if (this.setAndEmitConnectionState(ConnectionState.Reconnecting)) {
19593
+ this.emit(RoomEvent.Reconnecting);
19594
+ }
19458
19595
  };
19459
19596
 
19460
19597
  this.handleRestarted = async joinResponse => {
@@ -19483,7 +19620,7 @@ class Room extends events.exports.EventEmitter {
19483
19620
  this.localParticipant.unpublishTrack(track, false);
19484
19621
 
19485
19622
  if (!track.isMuted) {
19486
- if (track instanceof LocalAudioTrack || track instanceof LocalVideoTrack) {
19623
+ if ((track instanceof LocalAudioTrack || track instanceof LocalVideoTrack) && !track.isUserProvided) {
19487
19624
  // we need to restart the track before publishing, often a full reconnect
19488
19625
  // is necessary because computer had gone to sleep.
19489
19626
  livekitLogger.debug('restarting existing track', {
@@ -19522,7 +19659,7 @@ class Room extends events.exports.EventEmitter {
19522
19659
  this.handleParticipantDisconnected(info.sid, remoteParticipant);
19523
19660
  } else if (isNewParticipant) {
19524
19661
  // fire connected event
19525
- this.emit(RoomEvent.ParticipantConnected, remoteParticipant);
19662
+ this.emitWhenConnected(RoomEvent.ParticipantConnected, remoteParticipant);
19526
19663
  } else {
19527
19664
  // just update, no events
19528
19665
  remoteParticipant.updateInfo(info);
@@ -19564,7 +19701,7 @@ class Room extends events.exports.EventEmitter {
19564
19701
  }
19565
19702
  });
19566
19703
  this.activeSpeakers = activeSpeakers;
19567
- this.emit(RoomEvent.ActiveSpeakersChanged, activeSpeakers);
19704
+ this.emitWhenConnected(RoomEvent.ActiveSpeakersChanged, activeSpeakers);
19568
19705
  }; // process list of changed speakers
19569
19706
 
19570
19707
 
@@ -19596,7 +19733,7 @@ class Room extends events.exports.EventEmitter {
19596
19733
  const activeSpeakers = Array.from(lastSpeakers.values());
19597
19734
  activeSpeakers.sort((a, b) => b.audioLevel - a.audioLevel);
19598
19735
  this.activeSpeakers = activeSpeakers;
19599
- this.emit(RoomEvent.ActiveSpeakersChanged, activeSpeakers);
19736
+ this.emitWhenConnected(RoomEvent.ActiveSpeakersChanged, activeSpeakers);
19600
19737
  };
19601
19738
 
19602
19739
  this.handleStreamStateUpdate = streamStateUpdate => {
@@ -19615,7 +19752,7 @@ class Room extends events.exports.EventEmitter {
19615
19752
 
19616
19753
  pub.track.streamState = Track.streamStateFromProto(streamState.state);
19617
19754
  participant.emit(ParticipantEvent.TrackStreamStateChanged, pub, pub.track.streamState);
19618
- this.emit(ParticipantEvent.TrackStreamStateChanged, pub, pub.track.streamState, participant);
19755
+ this.emitWhenConnected(ParticipantEvent.TrackStreamStateChanged, pub, pub.track.streamState, participant);
19619
19756
  });
19620
19757
  };
19621
19758
 
@@ -19634,7 +19771,7 @@ class Room extends events.exports.EventEmitter {
19634
19771
 
19635
19772
  pub._allowed = update.allowed;
19636
19773
  participant.emit(ParticipantEvent.TrackSubscriptionPermissionChanged, pub, pub.subscriptionStatus);
19637
- this.emit(ParticipantEvent.TrackSubscriptionPermissionChanged, pub, pub.subscriptionStatus, participant);
19774
+ this.emitWhenConnected(ParticipantEvent.TrackSubscriptionPermissionChanged, pub, pub.subscriptionStatus, participant);
19638
19775
  };
19639
19776
 
19640
19777
  this.handleDataPacket = (userPacket, kind) => {
@@ -19671,7 +19808,7 @@ class Room extends events.exports.EventEmitter {
19671
19808
 
19672
19809
  this.handleRoomUpdate = r => {
19673
19810
  this.metadata = r.metadata;
19674
- this.emit(RoomEvent.RoomMetadataChanged, r.metadata);
19811
+ this.emitWhenConnected(RoomEvent.RoomMetadataChanged, r.metadata);
19675
19812
  };
19676
19813
 
19677
19814
  this.handleConnectionQualityUpdate = update => {
@@ -19692,14 +19829,6 @@ class Room extends events.exports.EventEmitter {
19692
19829
  this.participants = new Map();
19693
19830
  this.identityToSid = new Map();
19694
19831
  this.options = options || {};
19695
-
19696
- switch ((_b = (_a = this.options) === null || _a === void 0 ? void 0 : _a.publishDefaults) === null || _b === void 0 ? void 0 : _b.videoCodec) {
19697
- case 'av1':
19698
- case 'vp9':
19699
- this.options.publishDefaults.simulcast = undefined;
19700
- break;
19701
- }
19702
-
19703
19832
  this.options.audioCaptureDefaults = _objectSpread2(_objectSpread2({}, audioDefaults), options === null || options === void 0 ? void 0 : options.audioCaptureDefaults);
19704
19833
  this.options.videoCaptureDefaults = _objectSpread2(_objectSpread2({}, videoDefaults), options === null || options === void 0 ? void 0 : options.videoCaptureDefaults);
19705
19834
  this.options.publishDefaults = _objectSpread2(_objectSpread2({}, publishDefaults), options === null || options === void 0 ? void 0 : options.publishDefaults);
@@ -19737,6 +19866,10 @@ class Room extends events.exports.EventEmitter {
19737
19866
  this.sendSyncState();
19738
19867
  }
19739
19868
  }).on(EngineEvent.Restarting, this.handleRestarting).on(EngineEvent.Restarted, this.handleRestarted);
19869
+
19870
+ if (this.localParticipant) {
19871
+ this.localParticipant.engine = this.engine;
19872
+ }
19740
19873
  }
19741
19874
  /**
19742
19875
  * getLocalDevices abstracts navigator.mediaDevices.enumerateDevices.
@@ -19913,7 +20046,34 @@ class Room extends events.exports.EventEmitter {
19913
20046
  }
19914
20047
  }
19915
20048
 
20049
+ recreateEngine() {
20050
+ this.engine.close();
20051
+ /* @ts-ignore */
20052
+
20053
+ this.engine = undefined; // clear out existing remote participants, since they may have attached
20054
+ // the old engine
20055
+
20056
+ this.participants.clear();
20057
+ this.createEngine();
20058
+ }
20059
+
19916
20060
  onTrackAdded(mediaTrack, stream, receiver) {
20061
+ // don't fire onSubscribed when connecting
20062
+ // WebRTC fires onTrack as soon as setRemoteDescription is called on the offer
20063
+ // at that time, ICE connectivity has not been established so the track is not
20064
+ // technically subscribed.
20065
+ // We'll defer these events until when the room is connected or eventually disconnected.
20066
+ if (this.state === ConnectionState.Connecting || this.state === ConnectionState.Reconnecting) {
20067
+ setTimeout(() => {
20068
+ this.onTrackAdded(mediaTrack, stream, receiver);
20069
+ }, 50);
20070
+ return;
20071
+ }
20072
+
20073
+ if (this.state === ConnectionState.Disconnected) {
20074
+ livekitLogger.warn('skipping incoming track after Room disconnected');
20075
+ }
20076
+
19917
20077
  const parts = unpackStreamId(stream.id);
19918
20078
  const participantId = parts[0];
19919
20079
  let trackId = parts[1];
@@ -19983,7 +20143,7 @@ class Room extends events.exports.EventEmitter {
19983
20143
  participant.tracks.forEach(publication => {
19984
20144
  participant.unpublishTrack(publication.trackSid, true);
19985
20145
  });
19986
- this.emit(RoomEvent.ParticipantDisconnected, participant);
20146
+ this.emitWhenConnected(RoomEvent.ParticipantDisconnected, participant);
19987
20147
  }
19988
20148
 
19989
20149
  acquireAudioContext() {
@@ -20030,7 +20190,7 @@ class Room extends events.exports.EventEmitter {
20030
20190
 
20031
20191
 
20032
20192
  participant.on(ParticipantEvent.TrackPublished, trackPublication => {
20033
- this.emit(RoomEvent.TrackPublished, trackPublication, participant);
20193
+ this.emitWhenConnected(RoomEvent.TrackPublished, trackPublication, participant);
20034
20194
  }).on(ParticipantEvent.TrackSubscribed, (track, publication) => {
20035
20195
  // monitor playback status
20036
20196
  if (track.kind === Track.Kind.Audio) {
@@ -20040,22 +20200,27 @@ class Room extends events.exports.EventEmitter {
20040
20200
 
20041
20201
  this.emit(RoomEvent.TrackSubscribed, track, publication, participant);
20042
20202
  }).on(ParticipantEvent.TrackUnpublished, publication => {
20043
- this.emit(RoomEvent.TrackUnpublished, publication, participant);
20203
+ this.emitWhenConnected(RoomEvent.TrackUnpublished, publication, participant);
20044
20204
  }).on(ParticipantEvent.TrackUnsubscribed, (track, publication) => {
20045
20205
  this.emit(RoomEvent.TrackUnsubscribed, track, publication, participant);
20046
20206
  }).on(ParticipantEvent.TrackSubscriptionFailed, sid => {
20047
20207
  this.emit(RoomEvent.TrackSubscriptionFailed, sid, participant);
20048
20208
  }).on(ParticipantEvent.TrackMuted, pub => {
20049
- this.emit(RoomEvent.TrackMuted, pub, participant);
20209
+ this.emitWhenConnected(RoomEvent.TrackMuted, pub, participant);
20050
20210
  }).on(ParticipantEvent.TrackUnmuted, pub => {
20051
- this.emit(RoomEvent.TrackUnmuted, pub, participant);
20211
+ this.emitWhenConnected(RoomEvent.TrackUnmuted, pub, participant);
20052
20212
  }).on(ParticipantEvent.ParticipantMetadataChanged, metadata => {
20053
- this.emit(RoomEvent.ParticipantMetadataChanged, metadata, participant);
20213
+ this.emitWhenConnected(RoomEvent.ParticipantMetadataChanged, metadata, participant);
20054
20214
  }).on(ParticipantEvent.ConnectionQualityChanged, quality => {
20055
- this.emit(RoomEvent.ConnectionQualityChanged, quality, participant);
20215
+ this.emitWhenConnected(RoomEvent.ConnectionQualityChanged, quality, participant);
20056
20216
  }).on(ParticipantEvent.ParticipantPermissionsChanged, prevPermissions => {
20057
- this.emit(RoomEvent.ParticipantPermissionsChanged, prevPermissions, participant);
20058
- });
20217
+ this.emitWhenConnected(RoomEvent.ParticipantPermissionsChanged, prevPermissions, participant);
20218
+ }); // update info at the end after callbacks have been set up
20219
+
20220
+ if (info) {
20221
+ participant.updateInfo(info);
20222
+ }
20223
+
20059
20224
  return participant;
20060
20225
  }
20061
20226
 
@@ -20121,12 +20286,24 @@ class Room extends events.exports.EventEmitter {
20121
20286
  this.state = state;
20122
20287
  this.emit(RoomEvent.ConnectionStateChanged, this.state);
20123
20288
  return true;
20289
+ }
20290
+
20291
+ emitWhenConnected(event) {
20292
+ if (this.state === ConnectionState.Connected) {
20293
+ for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
20294
+ args[_key - 1] = arguments[_key];
20295
+ }
20296
+
20297
+ return this.emit(event, ...args);
20298
+ }
20299
+
20300
+ return false;
20124
20301
  } // /** @internal */
20125
20302
 
20126
20303
 
20127
20304
  emit(event) {
20128
- for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
20129
- args[_key - 1] = arguments[_key];
20305
+ for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
20306
+ args[_key2 - 1] = arguments[_key2];
20130
20307
  }
20131
20308
 
20132
20309
  livekitLogger.debug('room event', {
@@ -20235,12 +20412,12 @@ async function createLocalScreenTracks(options) {
20235
20412
  throw new TrackInvalidError('no video track found');
20236
20413
  }
20237
20414
 
20238
- const screenVideo = new LocalVideoTrack(tracks[0]);
20415
+ const screenVideo = new LocalVideoTrack(tracks[0], undefined, false);
20239
20416
  screenVideo.source = Track.Source.ScreenShare;
20240
20417
  const localTracks = [screenVideo];
20241
20418
 
20242
20419
  if (stream.getAudioTracks().length > 0) {
20243
- const screenAudio = new LocalAudioTrack(stream.getAudioTracks()[0]);
20420
+ const screenAudio = new LocalAudioTrack(stream.getAudioTracks()[0], undefined, false);
20244
20421
  screenAudio.source = Track.Source.ScreenShareAudio;
20245
20422
  localTracks.push(screenAudio);
20246
20423
  }