livekit-client 0.18.6 → 1.0.2

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 (154) hide show
  1. package/README.md +1 -1
  2. package/dist/livekit-client.esm.mjs +1034 -438
  3. package/dist/livekit-client.esm.mjs.map +1 -1
  4. package/dist/livekit-client.umd.js +1 -1
  5. package/dist/livekit-client.umd.js.map +1 -1
  6. package/dist/{api → src/api}/RequestQueue.d.ts +0 -0
  7. package/dist/src/api/RequestQueue.d.ts.map +1 -0
  8. package/dist/{api → src/api}/SignalClient.d.ts +3 -3
  9. package/dist/src/api/SignalClient.d.ts.map +1 -0
  10. package/dist/{index.d.ts → src/index.d.ts} +3 -4
  11. package/dist/src/index.d.ts.map +1 -0
  12. package/dist/{logger.d.ts → src/logger.d.ts} +0 -0
  13. package/dist/src/logger.d.ts.map +1 -0
  14. package/dist/src/options.d.ts +61 -0
  15. package/dist/src/options.d.ts.map +1 -0
  16. package/dist/{proto → src/proto}/google/protobuf/timestamp.d.ts +0 -0
  17. package/dist/src/proto/google/protobuf/timestamp.d.ts.map +1 -0
  18. package/dist/{proto → src/proto}/livekit_models.d.ts +80 -0
  19. package/dist/src/proto/livekit_models.d.ts.map +1 -0
  20. package/dist/{proto → src/proto}/livekit_rtc.d.ts +661 -0
  21. package/dist/src/proto/livekit_rtc.d.ts.map +1 -0
  22. package/dist/{room → src/room}/DeviceManager.d.ts +0 -0
  23. package/dist/src/room/DeviceManager.d.ts.map +1 -0
  24. package/dist/{room → src/room}/PCTransport.d.ts +0 -0
  25. package/dist/src/room/PCTransport.d.ts.map +1 -0
  26. package/dist/{room → src/room}/RTCEngine.d.ts +4 -2
  27. package/dist/src/room/RTCEngine.d.ts.map +1 -0
  28. package/dist/{room → src/room}/Room.d.ts +13 -7
  29. package/dist/src/room/Room.d.ts.map +1 -0
  30. package/dist/{room → src/room}/errors.d.ts +0 -0
  31. package/dist/src/room/errors.d.ts.map +1 -0
  32. package/dist/{room → src/room}/events.d.ts +11 -13
  33. package/dist/src/room/events.d.ts.map +1 -0
  34. package/dist/{room → src/room}/participant/LocalParticipant.d.ts +4 -1
  35. package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -0
  36. package/dist/{room → src/room}/participant/Participant.d.ts +0 -4
  37. package/dist/src/room/participant/Participant.d.ts.map +1 -0
  38. package/dist/{room → src/room}/participant/ParticipantTrackPermission.d.ts +0 -0
  39. package/dist/src/room/participant/ParticipantTrackPermission.d.ts.map +1 -0
  40. package/dist/{room → src/room}/participant/RemoteParticipant.d.ts +3 -2
  41. package/dist/src/room/participant/RemoteParticipant.d.ts.map +1 -0
  42. package/dist/{room → src/room}/participant/publishUtils.d.ts +0 -0
  43. package/dist/src/room/participant/publishUtils.d.ts.map +1 -0
  44. package/dist/{room → src/room}/stats.d.ts +1 -0
  45. package/dist/src/room/stats.d.ts.map +1 -0
  46. package/dist/{room → src/room}/track/LocalAudioTrack.d.ts +0 -0
  47. package/dist/src/room/track/LocalAudioTrack.d.ts.map +1 -0
  48. package/dist/{room → src/room}/track/LocalTrack.d.ts +3 -0
  49. package/dist/src/room/track/LocalTrack.d.ts.map +1 -0
  50. package/dist/{room → src/room}/track/LocalTrackPublication.d.ts +0 -0
  51. package/dist/src/room/track/LocalTrackPublication.d.ts.map +1 -0
  52. package/dist/{room → src/room}/track/LocalVideoTrack.d.ts +17 -2
  53. package/dist/src/room/track/LocalVideoTrack.d.ts.map +1 -0
  54. package/dist/{room → src/room}/track/RemoteAudioTrack.d.ts +0 -0
  55. package/dist/src/room/track/RemoteAudioTrack.d.ts.map +1 -0
  56. package/dist/{room → src/room}/track/RemoteTrack.d.ts +0 -1
  57. package/dist/src/room/track/RemoteTrack.d.ts.map +1 -0
  58. package/dist/{room → src/room}/track/RemoteTrackPublication.d.ts +0 -0
  59. package/dist/src/room/track/RemoteTrackPublication.d.ts.map +1 -0
  60. package/dist/{room → src/room}/track/RemoteVideoTrack.d.ts +25 -1
  61. package/dist/src/room/track/RemoteVideoTrack.d.ts.map +1 -0
  62. package/dist/{room → src/room}/track/Track.d.ts +6 -1
  63. package/dist/src/room/track/Track.d.ts.map +1 -0
  64. package/dist/{room → src/room}/track/TrackPublication.d.ts +0 -0
  65. package/dist/src/room/track/TrackPublication.d.ts.map +1 -0
  66. package/dist/{room → src/room}/track/create.d.ts +0 -0
  67. package/dist/src/room/track/create.d.ts.map +1 -0
  68. package/dist/{room → src/room}/track/defaults.d.ts +0 -0
  69. package/dist/src/room/track/defaults.d.ts.map +1 -0
  70. package/dist/{room → src/room}/track/options.d.ts +2 -31
  71. package/dist/src/room/track/options.d.ts.map +1 -0
  72. package/dist/{room → src/room}/track/types.d.ts +5 -0
  73. package/dist/src/room/track/types.d.ts.map +1 -0
  74. package/dist/{room → src/room}/track/utils.d.ts +0 -0
  75. package/dist/src/room/track/utils.d.ts.map +1 -0
  76. package/dist/{room → src/room}/utils.d.ts +0 -0
  77. package/dist/src/room/utils.d.ts.map +1 -0
  78. package/dist/src/test/MockMediaStreamTrack.d.ts +26 -0
  79. package/dist/src/test/MockMediaStreamTrack.d.ts.map +1 -0
  80. package/dist/{test → src/test}/mocks.d.ts +0 -0
  81. package/dist/src/test/mocks.d.ts.map +1 -0
  82. package/dist/src/version.d.ts +3 -0
  83. package/dist/src/version.d.ts.map +1 -0
  84. package/package.json +6 -2
  85. package/src/api/SignalClient.ts +34 -9
  86. package/src/index.ts +4 -3
  87. package/src/options.ts +0 -82
  88. package/src/proto/livekit_models.ts +90 -0
  89. package/src/proto/livekit_rtc.ts +235 -1
  90. package/src/room/DeviceManager.ts +4 -1
  91. package/src/room/RTCEngine.ts +46 -9
  92. package/src/room/Room.ts +122 -53
  93. package/src/room/events.ts +12 -14
  94. package/src/room/participant/LocalParticipant.ts +108 -23
  95. package/src/room/participant/Participant.ts +0 -5
  96. package/src/room/participant/RemoteParticipant.ts +17 -5
  97. package/src/room/participant/publishUtils.test.ts +2 -2
  98. package/src/room/stats.ts +2 -0
  99. package/src/room/track/LocalAudioTrack.ts +4 -0
  100. package/src/room/track/LocalTrack.ts +12 -5
  101. package/src/room/track/LocalVideoTrack.ts +144 -56
  102. package/src/room/track/RemoteTrack.ts +0 -2
  103. package/src/room/track/RemoteVideoTrack.test.ts +149 -0
  104. package/src/room/track/RemoteVideoTrack.ts +118 -37
  105. package/src/room/track/Track.ts +23 -2
  106. package/src/room/track/create.ts +1 -1
  107. package/src/room/track/options.ts +2 -31
  108. package/src/room/track/types.ts +5 -0
  109. package/src/room/track/utils.test.ts +6 -6
  110. package/src/test/MockMediaStreamTrack.ts +83 -0
  111. package/src/version.ts +4 -2
  112. package/dist/api/RequestQueue.d.ts.map +0 -1
  113. package/dist/api/SignalClient.d.ts.map +0 -1
  114. package/dist/connect.d.ts +0 -24
  115. package/dist/connect.d.ts.map +0 -1
  116. package/dist/index.d.ts.map +0 -1
  117. package/dist/logger.d.ts.map +0 -1
  118. package/dist/options.d.ts +0 -128
  119. package/dist/options.d.ts.map +0 -1
  120. package/dist/proto/google/protobuf/timestamp.d.ts.map +0 -1
  121. package/dist/proto/livekit_models.d.ts.map +0 -1
  122. package/dist/proto/livekit_rtc.d.ts.map +0 -1
  123. package/dist/room/DeviceManager.d.ts.map +0 -1
  124. package/dist/room/PCTransport.d.ts.map +0 -1
  125. package/dist/room/RTCEngine.d.ts.map +0 -1
  126. package/dist/room/Room.d.ts.map +0 -1
  127. package/dist/room/errors.d.ts.map +0 -1
  128. package/dist/room/events.d.ts.map +0 -1
  129. package/dist/room/participant/LocalParticipant.d.ts.map +0 -1
  130. package/dist/room/participant/Participant.d.ts.map +0 -1
  131. package/dist/room/participant/ParticipantTrackPermission.d.ts.map +0 -1
  132. package/dist/room/participant/RemoteParticipant.d.ts.map +0 -1
  133. package/dist/room/participant/publishUtils.d.ts.map +0 -1
  134. package/dist/room/stats.d.ts.map +0 -1
  135. package/dist/room/track/LocalAudioTrack.d.ts.map +0 -1
  136. package/dist/room/track/LocalTrack.d.ts.map +0 -1
  137. package/dist/room/track/LocalTrackPublication.d.ts.map +0 -1
  138. package/dist/room/track/LocalVideoTrack.d.ts.map +0 -1
  139. package/dist/room/track/RemoteAudioTrack.d.ts.map +0 -1
  140. package/dist/room/track/RemoteTrack.d.ts.map +0 -1
  141. package/dist/room/track/RemoteTrackPublication.d.ts.map +0 -1
  142. package/dist/room/track/RemoteVideoTrack.d.ts.map +0 -1
  143. package/dist/room/track/Track.d.ts.map +0 -1
  144. package/dist/room/track/TrackPublication.d.ts.map +0 -1
  145. package/dist/room/track/create.d.ts.map +0 -1
  146. package/dist/room/track/defaults.d.ts.map +0 -1
  147. package/dist/room/track/options.d.ts.map +0 -1
  148. package/dist/room/track/types.d.ts.map +0 -1
  149. package/dist/room/track/utils.d.ts.map +0 -1
  150. package/dist/room/utils.d.ts.map +0 -1
  151. package/dist/test/mocks.d.ts.map +0 -1
  152. package/dist/version.d.ts +0 -3
  153. package/dist/version.d.ts.map +0 -1
  154. package/src/connect.ts +0 -98
package/src/room/Room.ts CHANGED
@@ -27,7 +27,9 @@ import Participant, { ConnectionQuality } from './participant/Participant';
27
27
  import RemoteParticipant from './participant/RemoteParticipant';
28
28
  import RTCEngine, { maxICEConnectTimeout } from './RTCEngine';
29
29
  import { audioDefaults, publishDefaults, videoDefaults } from './track/defaults';
30
+ import LocalAudioTrack from './track/LocalAudioTrack';
30
31
  import LocalTrackPublication from './track/LocalTrackPublication';
32
+ import LocalVideoTrack from './track/LocalVideoTrack';
31
33
  import RemoteTrackPublication from './track/RemoteTrackPublication';
32
34
  import { Track } from './track/Track';
33
35
  import { TrackPublication } from './track/TrackPublication';
@@ -35,12 +37,16 @@ import { AdaptiveStreamSettings, RemoteTrack } from './track/types';
35
37
  import { getNewAudioContext } from './track/utils';
36
38
  import { isWeb, unpackStreamId } from './utils';
37
39
 
38
- export enum RoomState {
40
+ export enum ConnectionState {
39
41
  Disconnected = 'disconnected',
42
+ Connecting = 'connecting',
40
43
  Connected = 'connected',
41
44
  Reconnecting = 'reconnecting',
42
45
  }
43
46
 
47
+ /** @deprecated RoomState has been renamed to [[ConnectionState]] */
48
+ export const RoomState = ConnectionState;
49
+
44
50
  /**
45
51
  * In LiveKit, a room is the logical grouping for a list of participants.
46
52
  * Participants in a room can publish tracks, and subscribe to others' tracks.
@@ -50,7 +56,7 @@ export enum RoomState {
50
56
  * @noInheritDoc
51
57
  */
52
58
  class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>) {
53
- state: RoomState = RoomState.Disconnected;
59
+ state: ConnectionState = ConnectionState.Disconnected;
54
60
 
55
61
  /** map of sid: [[RemoteParticipant]] */
56
62
  participants: Map<string, RemoteParticipant>;
@@ -80,6 +86,8 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
80
86
  /** options of room */
81
87
  options: RoomOptions;
82
88
 
89
+ private identityToSid: Map<string, string>;
90
+
83
91
  /** connect options of room */
84
92
  private connOptions?: RoomConnectOptions;
85
93
 
@@ -87,6 +95,9 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
87
95
 
88
96
  private audioContext?: AudioContext;
89
97
 
98
+ /** used for aborting pending connections to a LiveKit server */
99
+ private abortController?: AbortController;
100
+
90
101
  /**
91
102
  * Creates a new Room, the primary construct for a LiveKit session.
92
103
  * @param options
@@ -94,6 +105,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
94
105
  constructor(options?: RoomOptions) {
95
106
  super();
96
107
  this.participants = new Map();
108
+ this.identityToSid = new Map();
97
109
  this.options = options || {};
98
110
 
99
111
  switch (this.options?.publishDefaults?.videoCodec) {
@@ -150,18 +162,17 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
150
162
  .on(EngineEvent.ActiveSpeakersUpdate, this.handleActiveSpeakersUpdate)
151
163
  .on(EngineEvent.DataPacketReceived, this.handleDataPacket)
152
164
  .on(EngineEvent.Resuming, () => {
153
- this.state = RoomState.Reconnecting;
154
- this.emit(RoomEvent.Reconnecting);
155
- this.emit(RoomEvent.StateChanged, this.state);
165
+ if (this.setAndEmitConnectionState(ConnectionState.Reconnecting)) {
166
+ this.emit(RoomEvent.Reconnecting);
167
+ }
156
168
  })
157
169
  .on(EngineEvent.Resumed, () => {
158
- this.state = RoomState.Connected;
170
+ this.setAndEmitConnectionState(ConnectionState.Connected);
159
171
  this.emit(RoomEvent.Reconnected);
160
- this.emit(RoomEvent.StateChanged, this.state);
161
172
  this.updateSubscriptions();
162
173
  })
163
174
  .on(EngineEvent.SignalResumed, () => {
164
- if (this.state === RoomState.Reconnecting) {
175
+ if (this.state === ConnectionState.Reconnecting) {
165
176
  this.sendSyncState();
166
177
  }
167
178
  })
@@ -186,11 +197,17 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
186
197
 
187
198
  connect = async (url: string, token: string, opts?: RoomConnectOptions) => {
188
199
  // guard against calling connect
189
- if (this.state !== RoomState.Disconnected) {
200
+ if (this.state !== ConnectionState.Disconnected) {
190
201
  log.warn(`already connected to room ${this.name}`);
191
202
  return;
192
203
  }
193
204
 
205
+ this.setAndEmitConnectionState(ConnectionState.Connecting);
206
+
207
+ if (!this.abortController || this.abortController.signal.aborted) {
208
+ this.abortController = new AbortController();
209
+ }
210
+
194
211
  // recreate engine if previously disconnected
195
212
  this.createEngine();
196
213
 
@@ -203,12 +220,17 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
203
220
  this.connOptions = opts;
204
221
 
205
222
  try {
206
- const joinResponse = await this.engine.join(url, token, {
207
- autoSubscribe: opts?.autoSubscribe,
208
- publishOnly: opts?.publishOnly,
209
- adaptiveStream:
210
- typeof this.options?.adaptiveStream === 'object' ? true : this.options?.adaptiveStream,
211
- });
223
+ const joinResponse = await this.engine.join(
224
+ url,
225
+ token,
226
+ {
227
+ autoSubscribe: opts?.autoSubscribe,
228
+ publishOnly: opts?.publishOnly,
229
+ adaptiveStream:
230
+ typeof this.options?.adaptiveStream === 'object' ? true : this.options?.adaptiveStream,
231
+ },
232
+ this.abortController.signal,
233
+ );
212
234
  log.debug(
213
235
  `connected to Livekit Server version: ${joinResponse.serverVersion}, region: ${joinResponse.serverRegion}`,
214
236
  );
@@ -223,7 +245,6 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
223
245
  this.options.dynacast = false;
224
246
  }
225
247
 
226
- this.state = RoomState.Connected;
227
248
  const pi = joinResponse.participant!;
228
249
 
229
250
  this.localParticipant.sid = pi.sid;
@@ -232,9 +253,6 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
232
253
  this.localParticipant.updateInfo(pi);
233
254
  // forward metadata changed for the local participant
234
255
  this.localParticipant
235
- .on(ParticipantEvent.MetadataChanged, (metadata: string | undefined) => {
236
- this.emit(RoomEvent.MetadataChanged, metadata, this.localParticipant);
237
- })
238
256
  .on(ParticipantEvent.ParticipantMetadataChanged, (metadata: string | undefined) => {
239
257
  this.emit(RoomEvent.ParticipantMetadataChanged, metadata, this.localParticipant);
240
258
  })
@@ -275,9 +293,10 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
275
293
  this.name = joinResponse.room!.name;
276
294
  this.sid = joinResponse.room!.sid;
277
295
  this.metadata = joinResponse.room!.metadata;
278
- this.emit(RoomEvent.StateChanged, this.state);
296
+ this.emit(RoomEvent.SignalConnected);
279
297
  } catch (err) {
280
298
  this.engine.close();
299
+ this.setAndEmitConnectionState(ConnectionState.Disconnected);
281
300
  throw err;
282
301
  }
283
302
 
@@ -286,18 +305,30 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
286
305
  const connectTimeout = setTimeout(() => {
287
306
  // timeout
288
307
  this.engine.close();
308
+ this.setAndEmitConnectionState(ConnectionState.Disconnected);
289
309
  reject(new ConnectionError('could not connect after timeout'));
290
310
  }, maxICEConnectTimeout);
311
+ const abortHandler = () => {
312
+ log.warn('closing engine');
313
+ clearTimeout(connectTimeout);
314
+ this.engine.close();
315
+ this.setAndEmitConnectionState(ConnectionState.Disconnected);
316
+ reject(new ConnectionError('room connection has been cancelled'));
317
+ };
318
+ if (this.abortController?.signal.aborted) {
319
+ abortHandler();
320
+ }
321
+ this.abortController?.signal.addEventListener('abort', abortHandler);
291
322
 
292
323
  this.engine.once(EngineEvent.Connected, () => {
293
324
  clearTimeout(connectTimeout);
294
-
325
+ this.abortController?.signal.removeEventListener('abort', abortHandler);
295
326
  // also hook unload event
296
327
  if (isWeb()) {
297
328
  window.addEventListener('beforeunload', this.onBeforeUnload);
298
329
  navigator.mediaDevices?.addEventListener('devicechange', this.handleDeviceChange);
299
330
  }
300
-
331
+ this.setAndEmitConnectionState(ConnectionState.Connected);
301
332
  resolve(this);
302
333
  });
303
334
  });
@@ -307,11 +338,21 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
307
338
  * disconnects the room, emits [[RoomEvent.Disconnected]]
308
339
  */
309
340
  disconnect = (stopTracks = true) => {
341
+ if (this.state === ConnectionState.Connecting) {
342
+ // try aborting pending connection attempt
343
+ log.warn('abort connection attempt');
344
+ this.abortController?.abort();
345
+ return;
346
+ }
310
347
  // send leave
311
- if (this.engine) {
348
+ if (this.engine?.client.isConnected) {
312
349
  this.engine.client.sendLeave();
350
+ }
351
+ // close engine (also closes client)
352
+ if (this.engine) {
313
353
  this.engine.close();
314
354
  }
355
+
315
356
  this.handleDisconnect(stopTracks);
316
357
  /* @ts-ignore */
317
358
  this.engine = undefined;
@@ -323,20 +364,20 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
323
364
  * @returns
324
365
  */
325
366
  getParticipantByIdentity(identity: string): Participant | undefined {
326
- for (const [, p] of this.participants) {
327
- if (p.identity === identity) {
328
- return p;
329
- }
330
- }
331
367
  if (this.localParticipant.identity === identity) {
332
368
  return this.localParticipant;
333
369
  }
370
+ const sid = this.identityToSid.get(identity);
371
+ if (sid) {
372
+ return this.participants.get(sid);
373
+ }
334
374
  }
335
375
 
336
376
  /**
337
377
  * @internal for testing
338
378
  */
339
379
  simulateScenario(scenario: string) {
380
+ let postAction = () => {};
340
381
  let req: SimulateScenario | undefined;
341
382
  switch (scenario) {
342
383
  case 'speaker':
@@ -359,10 +400,19 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
359
400
  migration: true,
360
401
  });
361
402
  break;
403
+ case 'switch-candidate':
404
+ req = SimulateScenario.fromPartial({
405
+ switchCandidateProtocol: 1,
406
+ });
407
+ postAction = () => {
408
+ this.engine.publisher?.createAndSendOffer({ iceRestart: true });
409
+ };
410
+ break;
362
411
  default:
363
412
  }
364
413
  if (req) {
365
414
  this.engine.client.sendSimulateScenario(req);
415
+ postAction();
366
416
  }
367
417
  }
368
418
 
@@ -482,9 +532,9 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
482
532
  }
483
533
 
484
534
  private handleRestarting = () => {
485
- this.state = RoomState.Reconnecting;
486
- this.emit(RoomEvent.Reconnecting);
487
- this.emit(RoomEvent.StateChanged, this.state);
535
+ if (this.setAndEmitConnectionState(ConnectionState.Reconnecting)) {
536
+ this.emit(RoomEvent.Reconnecting);
537
+ }
488
538
 
489
539
  // also unwind existing participants & existing subscriptions
490
540
  for (const p of this.participants.values()) {
@@ -493,10 +543,11 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
493
543
  };
494
544
 
495
545
  private handleRestarted = async (joinResponse: JoinResponse) => {
496
- log.debug(`reconnected to server region ${joinResponse.serverRegion}`);
497
- this.state = RoomState.Connected;
546
+ log.debug(`reconnected to server`, {
547
+ region: joinResponse.serverRegion,
548
+ });
549
+ this.setAndEmitConnectionState(ConnectionState.Connected);
498
550
  this.emit(RoomEvent.Reconnected);
499
- this.emit(RoomEvent.StateChanged, this.state);
500
551
 
501
552
  // rehydrate participants
502
553
  if (joinResponse.participant) {
@@ -518,15 +569,22 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
518
569
  localPubs.map(async (pub) => {
519
570
  const track = pub.track!;
520
571
  this.localParticipant.unpublishTrack(track, false);
521
- this.localParticipant.publishTrack(track, pub.options);
572
+ if (!track.isMuted) {
573
+ if (track instanceof LocalAudioTrack || track instanceof LocalVideoTrack) {
574
+ // we need to restart the track before publishing, often a full reconnect
575
+ // is necessary because computer had gone to sleep.
576
+ log.debug('restarting existing track', {
577
+ track: pub.trackSid,
578
+ });
579
+ await track.restartTrack();
580
+ }
581
+ await this.localParticipant.publishTrack(track, pub.options);
582
+ }
522
583
  }),
523
584
  );
524
585
  };
525
586
 
526
587
  private handleDisconnect(shouldStopTracks = true) {
527
- if (this.state === RoomState.Disconnected) {
528
- return;
529
- }
530
588
  this.participants.forEach((p) => {
531
589
  p.tracks.forEach((pub) => {
532
590
  p.unpublishTrack(pub.trackSid);
@@ -535,7 +593,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
535
593
 
536
594
  this.localParticipant.tracks.forEach((pub) => {
537
595
  if (pub.track) {
538
- this.localParticipant.unpublishTrack(pub.track);
596
+ this.localParticipant.unpublishTrack(pub.track, shouldStopTracks);
539
597
  }
540
598
  if (shouldStopTracks) {
541
599
  pub.track?.detach();
@@ -553,9 +611,8 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
553
611
  window.removeEventListener('beforeunload', this.onBeforeUnload);
554
612
  navigator.mediaDevices?.removeEventListener('devicechange', this.handleDeviceChange);
555
613
  }
556
- this.state = RoomState.Disconnected;
614
+ this.setAndEmitConnectionState(ConnectionState.Disconnected);
557
615
  this.emit(RoomEvent.Disconnected);
558
- this.emit(RoomEvent.StateChanged, this.state);
559
616
  }
560
617
 
561
618
  private handleParticipantUpdates = (participantInfos: ParticipantInfo[]) => {
@@ -569,6 +626,13 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
569
626
  return;
570
627
  }
571
628
 
629
+ // ensure identity <=> sid mapping
630
+ const sid = this.identityToSid.get(info.identity);
631
+ if (sid && sid !== info.sid) {
632
+ // sid had changed, need to remove previous participant
633
+ this.handleParticipantDisconnected(sid, this.participants.get(sid));
634
+ }
635
+
572
636
  let remoteParticipant = this.participants.get(info.sid);
573
637
  const isNewParticipant = !remoteParticipant;
574
638
 
@@ -579,6 +643,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
579
643
  if (info.state === ParticipantInfo_State.DISCONNECTED) {
580
644
  this.handleParticipantDisconnected(info.sid, remoteParticipant);
581
645
  } else if (isNewParticipant) {
646
+ this.identityToSid.set(info.identity, info.sid);
582
647
  // fire connected event
583
648
  this.emit(RoomEvent.ParticipantConnected, remoteParticipant);
584
649
  } else {
@@ -595,8 +660,9 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
595
660
  return;
596
661
  }
597
662
 
663
+ this.identityToSid.delete(participant.identity);
598
664
  participant.tracks.forEach((publication) => {
599
- participant.unpublishTrack(publication.trackSid);
665
+ participant.unpublishTrack(publication.trackSid, true);
600
666
  });
601
667
  this.emit(RoomEvent.ParticipantDisconnected, participant);
602
668
  }
@@ -821,9 +887,6 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
821
887
  .on(ParticipantEvent.TrackUnmuted, (pub: TrackPublication) => {
822
888
  this.emit(RoomEvent.TrackUnmuted, pub, participant);
823
889
  })
824
- .on(ParticipantEvent.MetadataChanged, (metadata: string | undefined) => {
825
- this.emit(RoomEvent.MetadataChanged, metadata, participant);
826
- })
827
890
  .on(ParticipantEvent.ParticipantMetadataChanged, (metadata: string | undefined) => {
828
891
  this.emit(RoomEvent.ParticipantMetadataChanged, metadata, participant);
829
892
  })
@@ -892,6 +955,16 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
892
955
  }
893
956
  }
894
957
 
958
+ private setAndEmitConnectionState(state: ConnectionState): boolean {
959
+ if (state === this.state) {
960
+ // unchanged
961
+ return false;
962
+ }
963
+ this.state = state;
964
+ this.emit(RoomEvent.ConnectionStateChanged, this.state);
965
+ return true;
966
+ }
967
+
895
968
  // /** @internal */
896
969
  emit<E extends keyof RoomEventCallbacks>(
897
970
  event: E,
@@ -908,7 +981,9 @@ export type RoomEventCallbacks = {
908
981
  reconnecting: () => void;
909
982
  reconnected: () => void;
910
983
  disconnected: () => void;
911
- stateChanged: (state: RoomState) => void;
984
+ /** @deprecated stateChanged has been renamed to connectionStateChanged */
985
+ stateChanged: (state: ConnectionState) => void;
986
+ connectionStateChanged: (state: ConnectionState) => void;
912
987
  mediaDevicesChanged: () => void;
913
988
  participantConnected: (participant: RemoteParticipant) => void;
914
989
  participantDisconnected: (participant: RemoteParticipant) => void;
@@ -932,13 +1007,6 @@ export type RoomEventCallbacks = {
932
1007
  publication: LocalTrackPublication,
933
1008
  participant: LocalParticipant,
934
1009
  ) => void;
935
- /**
936
- * @deprecated use [[participantMetadataChanged]] instead
937
- */
938
- metadataChanged: (
939
- metadata: string | undefined,
940
- participant?: RemoteParticipant | LocalParticipant,
941
- ) => void;
942
1010
  participantMetadataChanged: (
943
1011
  metadata: string | undefined,
944
1012
  participant: RemoteParticipant | LocalParticipant,
@@ -967,4 +1035,5 @@ export type RoomEventCallbacks = {
967
1035
  participant: RemoteParticipant,
968
1036
  ) => void;
969
1037
  audioPlaybackChanged: (playing: boolean) => void;
1038
+ signalConnected: () => void;
970
1039
  };
@@ -29,9 +29,14 @@ export enum RoomEvent {
29
29
  /**
30
30
  * Whenever the connection state of the room changes
31
31
  *
32
- * args: ([[RoomState]])
32
+ * args: ([[ConnectionState]])
33
33
  */
34
- StateChanged = 'stateChanged',
34
+ ConnectionStateChanged = 'connectionStateChanged',
35
+
36
+ /**
37
+ * @deprecated StateChanged has been renamed to ConnectionStateChanged
38
+ */
39
+ StateChanged = 'connectionStateChanged',
35
40
 
36
41
  /**
37
42
  * When input or output devices on the machine have changed.
@@ -139,12 +144,6 @@ export enum RoomEvent {
139
144
  */
140
145
  ActiveSpeakersChanged = 'activeSpeakersChanged',
141
146
 
142
- /**
143
- * @deprecated Use ParticipantMetadataChanged instead
144
- * @internal
145
- */
146
- MetadataChanged = 'metadataChanged',
147
-
148
147
  /**
149
148
  * Participant metadata is a simple way for app-specific state to be pushed to
150
149
  * all users.
@@ -231,6 +230,11 @@ export enum RoomEvent {
231
230
  * args: (prevPermissions: [[ParticipantPermission]], participant: [[Participant]])
232
231
  */
233
232
  ParticipantPermissionsChanged = 'participantPermissionsChanged',
233
+
234
+ /**
235
+ * Signal connected, can publish tracks.
236
+ */
237
+ SignalConnected = 'signalConnected',
234
238
  }
235
239
 
236
240
  export enum ParticipantEvent {
@@ -308,12 +312,6 @@ export enum ParticipantEvent {
308
312
  */
309
313
  LocalTrackUnpublished = 'localTrackUnpublished',
310
314
 
311
- /**
312
- * @deprecated Use ParticipantMetadataChanged instead
313
- * @internal
314
- */
315
- MetadataChanged = 'metadataChanged',
316
-
317
315
  /**
318
316
  * Participant metadata is a simple way for app-specific state to be pushed to
319
317
  * all users.