livekit-client 1.5.0 → 1.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. package/dist/livekit-client.esm.mjs +2257 -5488
  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/api/SignalClient.d.ts +3 -2
  6. package/dist/src/api/SignalClient.d.ts.map +1 -1
  7. package/dist/src/connectionHelper/ConnectionCheck.d.ts +1 -1
  8. package/dist/src/connectionHelper/ConnectionCheck.d.ts.map +1 -1
  9. package/dist/src/connectionHelper/checks/Checker.d.ts +4 -4
  10. package/dist/src/connectionHelper/checks/Checker.d.ts.map +1 -1
  11. package/dist/src/index.d.ts +3 -2
  12. package/dist/src/index.d.ts.map +1 -1
  13. package/dist/src/logger.d.ts +3 -3
  14. package/dist/src/logger.d.ts.map +1 -1
  15. package/dist/src/options.d.ts +4 -1
  16. package/dist/src/options.d.ts.map +1 -1
  17. package/dist/src/proto/google/protobuf/timestamp.d.ts +4 -4
  18. package/dist/src/proto/google/protobuf/timestamp.d.ts.map +1 -1
  19. package/dist/src/proto/livekit_models.d.ts +4 -4
  20. package/dist/src/proto/livekit_models.d.ts.map +1 -1
  21. package/dist/src/proto/livekit_rtc.d.ts +4 -4
  22. package/dist/src/proto/livekit_rtc.d.ts.map +1 -1
  23. package/dist/src/room/PCTransport.d.ts +7 -1
  24. package/dist/src/room/PCTransport.d.ts.map +1 -1
  25. package/dist/src/room/RTCEngine.d.ts +10 -4
  26. package/dist/src/room/RTCEngine.d.ts.map +1 -1
  27. package/dist/src/room/Room.d.ts +21 -4
  28. package/dist/src/room/Room.d.ts.map +1 -1
  29. package/dist/src/room/events.d.ts +5 -0
  30. package/dist/src/room/events.d.ts.map +1 -1
  31. package/dist/src/room/participant/LocalParticipant.d.ts +3 -2
  32. package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
  33. package/dist/src/room/participant/Participant.d.ts +1 -1
  34. package/dist/src/room/participant/Participant.d.ts.map +1 -1
  35. package/dist/src/room/track/LocalTrack.d.ts +1 -0
  36. package/dist/src/room/track/LocalTrack.d.ts.map +1 -1
  37. package/dist/src/room/track/RemoteVideoTrack.d.ts.map +1 -1
  38. package/dist/src/room/track/Track.d.ts +2 -1
  39. package/dist/src/room/track/Track.d.ts.map +1 -1
  40. package/dist/src/room/track/TrackPublication.d.ts +1 -1
  41. package/dist/src/room/track/TrackPublication.d.ts.map +1 -1
  42. package/dist/src/room/track/options.d.ts +3 -3
  43. package/dist/src/room/track/options.d.ts.map +1 -1
  44. package/dist/src/room/track/types.d.ts +3 -3
  45. package/dist/src/room/track/types.d.ts.map +1 -1
  46. package/dist/src/room/types.d.ts +13 -0
  47. package/dist/src/room/types.d.ts.map +1 -0
  48. package/dist/src/room/utils.d.ts +44 -0
  49. package/dist/src/room/utils.d.ts.map +1 -1
  50. package/dist/ts4.2/src/api/SignalClient.d.ts +3 -2
  51. package/dist/ts4.2/src/connectionHelper/ConnectionCheck.d.ts +1 -1
  52. package/dist/ts4.2/src/connectionHelper/checks/Checker.d.ts +4 -4
  53. package/dist/ts4.2/src/index.d.ts +3 -2
  54. package/dist/ts4.2/src/logger.d.ts +3 -3
  55. package/dist/ts4.2/src/options.d.ts +4 -1
  56. package/dist/ts4.2/src/proto/google/protobuf/timestamp.d.ts +4 -4
  57. package/dist/ts4.2/src/proto/livekit_models.d.ts +4 -4
  58. package/dist/ts4.2/src/proto/livekit_rtc.d.ts +4 -4
  59. package/dist/ts4.2/src/room/PCTransport.d.ts +7 -1
  60. package/dist/ts4.2/src/room/RTCEngine.d.ts +10 -4
  61. package/dist/ts4.2/src/room/Room.d.ts +21 -4
  62. package/dist/ts4.2/src/room/events.d.ts +5 -0
  63. package/dist/ts4.2/src/room/participant/LocalParticipant.d.ts +3 -2
  64. package/dist/ts4.2/src/room/participant/Participant.d.ts +1 -1
  65. package/dist/ts4.2/src/room/track/LocalTrack.d.ts +1 -0
  66. package/dist/ts4.2/src/room/track/Track.d.ts +2 -1
  67. package/dist/ts4.2/src/room/track/TrackPublication.d.ts +1 -1
  68. package/dist/ts4.2/src/room/track/options.d.ts +3 -3
  69. package/dist/ts4.2/src/room/track/types.d.ts +3 -3
  70. package/dist/ts4.2/src/room/types.d.ts +13 -0
  71. package/dist/ts4.2/src/room/utils.d.ts +44 -0
  72. package/package.json +23 -23
  73. package/src/api/SignalClient.ts +40 -16
  74. package/src/connectionHelper/checks/turn.ts +1 -1
  75. package/src/connectionHelper/checks/websocket.ts +1 -1
  76. package/src/index.ts +5 -0
  77. package/src/options.ts +5 -1
  78. package/src/room/PCTransport.ts +11 -1
  79. package/src/room/RTCEngine.ts +111 -49
  80. package/src/room/Room.ts +234 -63
  81. package/src/room/events.ts +5 -0
  82. package/src/room/participant/LocalParticipant.ts +46 -22
  83. package/src/room/participant/RemoteParticipant.ts +5 -5
  84. package/src/room/participant/publishUtils.ts +1 -1
  85. package/src/room/track/LocalAudioTrack.ts +1 -1
  86. package/src/room/track/LocalTrack.ts +20 -1
  87. package/src/room/track/LocalVideoTrack.ts +1 -1
  88. package/src/room/track/RemoteVideoTrack.ts +4 -0
  89. package/src/room/track/Track.ts +22 -5
  90. package/src/room/types.ts +12 -0
  91. package/src/room/utils.ts +150 -12
@@ -29,7 +29,7 @@ import {
29
29
  UnexpectedConnectionState,
30
30
  } from './errors';
31
31
  import { EngineEvent } from './events';
32
- import PCTransport from './PCTransport';
32
+ import PCTransport, { PCEvents } from './PCTransport';
33
33
  import type { ReconnectContext, ReconnectPolicy } from './ReconnectPolicy';
34
34
  import type LocalTrack from './track/LocalTrack';
35
35
  import type LocalVideoTrack from './track/LocalVideoTrack';
@@ -38,6 +38,7 @@ import type { TrackPublishOptions, VideoCodec } from './track/options';
38
38
  import { Track } from './track/Track';
39
39
  import {
40
40
  isWeb,
41
+ Mutex,
41
42
  sleep,
42
43
  supportsAddTrack,
43
44
  supportsSetCodecPreferences,
@@ -130,12 +131,15 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
130
131
  /** specifies how often an initial join connection is allowed to retry */
131
132
  private maxJoinAttempts: number = 1;
132
133
 
134
+ private closingLock: Mutex;
135
+
133
136
  constructor(private options: InternalRoomOptions) {
134
137
  super();
135
138
  this.client = new SignalClient();
136
139
  this.client.signalLatency = this.options.expSignalLatency;
137
140
  this.reconnectPolicy = this.options.reconnectPolicy;
138
141
  this.registerOnLineListener();
142
+ this.closingLock = new Mutex();
139
143
  }
140
144
 
141
145
  async join(
@@ -179,30 +183,40 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
179
183
  }
180
184
  }
181
185
 
182
- close() {
183
- this._isClosed = true;
184
- this.removeAllListeners();
185
- this.deregisterOnLineListener();
186
- this.clearPendingReconnect();
187
- if (this.publisher && this.publisher.pc.signalingState !== 'closed') {
188
- this.publisher.pc.getSenders().forEach((sender) => {
189
- try {
190
- // TODO: react-native-webrtc doesn't have removeTrack yet.
191
- if (this.publisher?.pc.removeTrack) {
192
- this.publisher?.pc.removeTrack(sender);
193
- }
194
- } catch (e) {
195
- log.warn('could not removeTrack', { error: e });
196
- }
197
- });
198
- this.publisher.close();
199
- this.publisher = undefined;
186
+ async close() {
187
+ const unlock = await this.closingLock.lock();
188
+ if (this.isClosed) {
189
+ unlock();
190
+ return;
200
191
  }
201
- if (this.subscriber) {
202
- this.subscriber.close();
203
- this.subscriber = undefined;
192
+ try {
193
+ this._isClosed = true;
194
+ this.emit(EngineEvent.Closing);
195
+ this.removeAllListeners();
196
+ this.deregisterOnLineListener();
197
+ this.clearPendingReconnect();
198
+ if (this.publisher && this.publisher.pc.signalingState !== 'closed') {
199
+ this.publisher.pc.getSenders().forEach((sender) => {
200
+ try {
201
+ // TODO: react-native-webrtc doesn't have removeTrack yet.
202
+ if (this.publisher?.pc.removeTrack) {
203
+ this.publisher?.pc.removeTrack(sender);
204
+ }
205
+ } catch (e) {
206
+ log.warn('could not removeTrack', { error: e });
207
+ }
208
+ });
209
+ this.publisher.close();
210
+ this.publisher = undefined;
211
+ }
212
+ if (this.subscriber) {
213
+ this.subscriber.close();
214
+ this.subscriber = undefined;
215
+ }
216
+ await this.client.close();
217
+ } finally {
218
+ unlock();
204
219
  }
205
- this.client.close();
206
220
  }
207
221
 
208
222
  addTrack(req: AddTrackRequest): Promise<TrackInfo> {
@@ -335,7 +349,7 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
335
349
  const shouldEmit = this.pcState === PCState.New;
336
350
  this.pcState = PCState.Connected;
337
351
  if (shouldEmit) {
338
- this.emit(EngineEvent.Connected);
352
+ this.emit(EngineEvent.Connected, joinResponse);
339
353
  }
340
354
  } else if (primaryPC.connectionState === 'failed') {
341
355
  // on Safari, PeerConnection will switch to 'disconnected' during renegotiation
@@ -784,9 +798,9 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
784
798
  }
785
799
 
786
800
  if (this.client.isConnected) {
787
- this.client.sendLeave();
801
+ await this.client.sendLeave();
788
802
  }
789
- this.client.close();
803
+ await this.client.close();
790
804
  this.primaryPC = undefined;
791
805
  this.publisher?.close();
792
806
  this.publisher = undefined;
@@ -906,52 +920,99 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
906
920
  }
907
921
  }
908
922
 
909
- private async ensurePublisherConnected(kind: DataPacket_Kind) {
910
- if (!this.subscriberPrimary) {
911
- return;
912
- }
913
-
914
- if (!this.publisher) {
915
- throw new ConnectionError('publisher connection not set');
923
+ /**
924
+ * @internal
925
+ */
926
+ async ensureDataTransportConnected(
927
+ kind: DataPacket_Kind,
928
+ subscriber: boolean = this.subscriberPrimary,
929
+ ) {
930
+ const transport = subscriber ? this.subscriber : this.publisher;
931
+ const transportName = subscriber ? 'Subscriber' : 'Publisher';
932
+ if (!transport) {
933
+ throw new ConnectionError(`${transportName} connection not set`);
916
934
  }
917
935
 
918
- if (!this.publisher.isICEConnected && this.publisher.pc.iceConnectionState !== 'checking') {
936
+ if (
937
+ !subscriber &&
938
+ !this.publisher?.isICEConnected &&
939
+ this.publisher?.pc.iceConnectionState !== 'checking'
940
+ ) {
919
941
  // start negotiation
920
942
  this.negotiate();
921
943
  }
922
944
 
923
- const targetChannel = this.dataChannelForKind(kind);
945
+ const targetChannel = this.dataChannelForKind(kind, subscriber);
924
946
  if (targetChannel?.readyState === 'open') {
925
947
  return;
926
948
  }
927
949
 
928
- // wait until publisher ICE connected
950
+ // wait until ICE connected
929
951
  const endTime = new Date().getTime() + this.peerConnectionTimeout;
930
952
  while (new Date().getTime() < endTime) {
931
- if (this.publisher.isICEConnected && this.dataChannelForKind(kind)?.readyState === 'open') {
953
+ if (
954
+ transport.isICEConnected &&
955
+ this.dataChannelForKind(kind, subscriber)?.readyState === 'open'
956
+ ) {
932
957
  return;
933
958
  }
934
959
  await sleep(50);
935
960
  }
936
961
 
937
962
  throw new ConnectionError(
938
- `could not establish publisher connection, state ${this.publisher?.pc.iceConnectionState}`,
963
+ `could not establish ${transportName} connection, state: ${transport.pc.iceConnectionState}`,
939
964
  );
940
965
  }
941
966
 
967
+ private async ensurePublisherConnected(kind: DataPacket_Kind) {
968
+ await this.ensureDataTransportConnected(kind, false);
969
+ }
970
+
942
971
  /** @internal */
943
- negotiate() {
944
- if (!this.publisher) {
945
- return;
946
- }
972
+ negotiate(): Promise<void> {
973
+ // observe signal state
974
+ return new Promise<void>((resolve, reject) => {
975
+ if (!this.publisher) {
976
+ reject(new NegotiationError('publisher is not defined'));
977
+ return;
978
+ }
947
979
 
948
- this.hasPublished = true;
980
+ this.hasPublished = true;
949
981
 
950
- this.publisher.negotiate((e) => {
951
- if (e instanceof NegotiationError) {
952
- this.fullReconnectOnNext = true;
953
- }
954
- this.handleDisconnect('negotiation');
982
+ const handleClosed = () => {
983
+ log.debug('engine disconnected while negotiation was ongoing');
984
+ cleanup();
985
+ resolve();
986
+ return;
987
+ };
988
+
989
+ this.on(EngineEvent.Closing, handleClosed);
990
+
991
+ const negotiationTimeout = setTimeout(() => {
992
+ reject('negotiation timed out');
993
+ this.handleDisconnect('negotiation');
994
+ }, this.peerConnectionTimeout);
995
+
996
+ const cleanup = () => {
997
+ clearTimeout(negotiationTimeout);
998
+ this.off(EngineEvent.Closing, handleClosed);
999
+ };
1000
+
1001
+ this.publisher.once(PCEvents.NegotiationStarted, () => {
1002
+ this.publisher?.once(PCEvents.NegotiationComplete, () => {
1003
+ cleanup();
1004
+ resolve();
1005
+ });
1006
+ });
1007
+
1008
+ this.publisher.negotiate((e) => {
1009
+ cleanup();
1010
+ reject(e);
1011
+ if (e instanceof NegotiationError) {
1012
+ this.fullReconnectOnNext = true;
1013
+ }
1014
+ this.handleDisconnect('negotiation');
1015
+ });
955
1016
  });
956
1017
  }
957
1018
 
@@ -1040,13 +1101,14 @@ async function getConnectedAddress(pc: RTCPeerConnection): Promise<string | unde
1040
1101
  class SignalReconnectError extends Error {}
1041
1102
 
1042
1103
  export type EngineEventCallbacks = {
1043
- connected: () => void;
1104
+ connected: (joinResp: JoinResponse) => void;
1044
1105
  disconnected: (reason?: DisconnectReason) => void;
1045
1106
  resuming: () => void;
1046
1107
  resumed: () => void;
1047
1108
  restarting: () => void;
1048
1109
  restarted: (joinResp: JoinResponse) => void;
1049
1110
  signalResumed: () => void;
1111
+ closing: () => void;
1050
1112
  mediaTrackAdded: (
1051
1113
  track: MediaStreamTrack,
1052
1114
  streams: MediaStream,