livekit-client 1.1.6 → 1.1.9

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 (48) hide show
  1. package/README.md +1 -0
  2. package/dist/livekit-client.esm.mjs +535 -120
  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/src/api/SignalClient.d.ts +2 -1
  7. package/dist/src/api/SignalClient.d.ts.map +1 -1
  8. package/dist/src/index.d.ts +4 -3
  9. package/dist/src/index.d.ts.map +1 -1
  10. package/dist/src/proto/livekit_models.d.ts +234 -0
  11. package/dist/src/proto/livekit_models.d.ts.map +1 -1
  12. package/dist/src/proto/livekit_rtc.d.ts +944 -6
  13. package/dist/src/proto/livekit_rtc.d.ts.map +1 -1
  14. package/dist/src/room/RTCEngine.d.ts +2 -2
  15. package/dist/src/room/RTCEngine.d.ts.map +1 -1
  16. package/dist/src/room/Room.d.ts +2 -2
  17. package/dist/src/room/Room.d.ts.map +1 -1
  18. package/dist/src/room/participant/LocalParticipant.d.ts +3 -1
  19. package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
  20. package/dist/src/room/participant/Participant.d.ts.map +1 -1
  21. package/dist/src/room/participant/RemoteParticipant.d.ts.map +1 -1
  22. package/dist/src/room/track/LocalAudioTrack.d.ts.map +1 -1
  23. package/dist/src/room/track/LocalTrack.d.ts +2 -0
  24. package/dist/src/room/track/LocalTrack.d.ts.map +1 -1
  25. package/dist/src/room/track/LocalVideoTrack.d.ts.map +1 -1
  26. package/dist/src/room/track/RemoteTrack.d.ts.map +1 -1
  27. package/dist/src/room/track/Track.d.ts +7 -0
  28. package/dist/src/room/track/Track.d.ts.map +1 -1
  29. package/dist/src/room/track/TrackPublication.d.ts.map +1 -1
  30. package/package.json +3 -1
  31. package/src/api/SignalClient.ts +19 -6
  32. package/src/index.ts +6 -2
  33. package/src/proto/livekit_models.ts +179 -4
  34. package/src/proto/livekit_rtc.ts +14 -1
  35. package/src/room/RTCEngine.ts +4 -2
  36. package/src/room/Room.ts +32 -9
  37. package/src/room/participant/LocalParticipant.ts +30 -2
  38. package/src/room/participant/Participant.ts +2 -0
  39. package/src/room/participant/RemoteParticipant.ts +4 -0
  40. package/src/room/track/LocalAudioTrack.ts +16 -12
  41. package/src/room/track/LocalTrack.ts +37 -25
  42. package/src/room/track/LocalVideoTrack.ts +15 -11
  43. package/src/room/track/RemoteTrack.ts +1 -0
  44. package/src/room/track/Track.ts +12 -0
  45. package/src/room/track/TrackPublication.ts +2 -0
  46. package/dist/src/api/RequestQueue.d.ts +0 -13
  47. package/dist/src/api/RequestQueue.d.ts.map +0 -1
  48. package/src/api/RequestQueue.ts +0 -53
@@ -213,6 +213,68 @@ export function clientConfigSettingToJSON(object: ClientConfigSetting): string {
213
213
  }
214
214
  }
215
215
 
216
+ export enum DisconnectReason {
217
+ UNKNOWN_REASON = 0,
218
+ CLIENT_INITIATED = 1,
219
+ DUPLICATE_IDENTITY = 2,
220
+ SERVER_SHUTDOWN = 3,
221
+ PARTICIPANT_REMOVED = 4,
222
+ ROOM_DELETED = 5,
223
+ STATE_MISMATCH = 6,
224
+ UNRECOGNIZED = -1,
225
+ }
226
+
227
+ export function disconnectReasonFromJSON(object: any): DisconnectReason {
228
+ switch (object) {
229
+ case 0:
230
+ case 'UNKNOWN_REASON':
231
+ return DisconnectReason.UNKNOWN_REASON;
232
+ case 1:
233
+ case 'CLIENT_INITIATED':
234
+ return DisconnectReason.CLIENT_INITIATED;
235
+ case 2:
236
+ case 'DUPLICATE_IDENTITY':
237
+ return DisconnectReason.DUPLICATE_IDENTITY;
238
+ case 3:
239
+ case 'SERVER_SHUTDOWN':
240
+ return DisconnectReason.SERVER_SHUTDOWN;
241
+ case 4:
242
+ case 'PARTICIPANT_REMOVED':
243
+ return DisconnectReason.PARTICIPANT_REMOVED;
244
+ case 5:
245
+ case 'ROOM_DELETED':
246
+ return DisconnectReason.ROOM_DELETED;
247
+ case 6:
248
+ case 'STATE_MISMATCH':
249
+ return DisconnectReason.STATE_MISMATCH;
250
+ case -1:
251
+ case 'UNRECOGNIZED':
252
+ default:
253
+ return DisconnectReason.UNRECOGNIZED;
254
+ }
255
+ }
256
+
257
+ export function disconnectReasonToJSON(object: DisconnectReason): string {
258
+ switch (object) {
259
+ case DisconnectReason.UNKNOWN_REASON:
260
+ return 'UNKNOWN_REASON';
261
+ case DisconnectReason.CLIENT_INITIATED:
262
+ return 'CLIENT_INITIATED';
263
+ case DisconnectReason.DUPLICATE_IDENTITY:
264
+ return 'DUPLICATE_IDENTITY';
265
+ case DisconnectReason.SERVER_SHUTDOWN:
266
+ return 'SERVER_SHUTDOWN';
267
+ case DisconnectReason.PARTICIPANT_REMOVED:
268
+ return 'PARTICIPANT_REMOVED';
269
+ case DisconnectReason.ROOM_DELETED:
270
+ return 'ROOM_DELETED';
271
+ case DisconnectReason.STATE_MISMATCH:
272
+ return 'STATE_MISMATCH';
273
+ default:
274
+ return 'UNKNOWN';
275
+ }
276
+ }
277
+
216
278
  export interface Room {
217
279
  sid: string;
218
280
  name: string;
@@ -315,6 +377,7 @@ export interface SimulcastCodecInfo {
315
377
  mimeType: string;
316
378
  mid: string;
317
379
  cid: string;
380
+ layers: VideoLayer[];
318
381
  }
319
382
 
320
383
  export interface TrackInfo {
@@ -499,12 +562,17 @@ export interface ClientConfiguration {
499
562
  video?: VideoConfiguration;
500
563
  screen?: VideoConfiguration;
501
564
  resumeConnection: ClientConfigSetting;
565
+ disabledCodecs?: DisabledCodecs;
502
566
  }
503
567
 
504
568
  export interface VideoConfiguration {
505
569
  hardwareEncoder: ClientConfigSetting;
506
570
  }
507
571
 
572
+ export interface DisabledCodecs {
573
+ codecs: Codec[];
574
+ }
575
+
508
576
  export interface RTPStats {
509
577
  startTime?: Date;
510
578
  endTime?: Date;
@@ -531,7 +599,9 @@ export interface RTPStats {
531
599
  jitterMax: number;
532
600
  gapHistogram: { [key: number]: number };
533
601
  nacks: number;
602
+ nackAcks: number;
534
603
  nackMisses: number;
604
+ nackRepeated: number;
535
605
  plis: number;
536
606
  lastPli?: Date;
537
607
  firs: number;
@@ -1015,7 +1085,7 @@ export const ParticipantInfo = {
1015
1085
  };
1016
1086
 
1017
1087
  function createBaseSimulcastCodecInfo(): SimulcastCodecInfo {
1018
- return { mimeType: '', mid: '', cid: '' };
1088
+ return { mimeType: '', mid: '', cid: '', layers: [] };
1019
1089
  }
1020
1090
 
1021
1091
  export const SimulcastCodecInfo = {
@@ -1029,6 +1099,9 @@ export const SimulcastCodecInfo = {
1029
1099
  if (message.cid !== '') {
1030
1100
  writer.uint32(26).string(message.cid);
1031
1101
  }
1102
+ for (const v of message.layers) {
1103
+ VideoLayer.encode(v!, writer.uint32(34).fork()).ldelim();
1104
+ }
1032
1105
  return writer;
1033
1106
  },
1034
1107
 
@@ -1048,6 +1121,9 @@ export const SimulcastCodecInfo = {
1048
1121
  case 3:
1049
1122
  message.cid = reader.string();
1050
1123
  break;
1124
+ case 4:
1125
+ message.layers.push(VideoLayer.decode(reader, reader.uint32()));
1126
+ break;
1051
1127
  default:
1052
1128
  reader.skipType(tag & 7);
1053
1129
  break;
@@ -1061,6 +1137,9 @@ export const SimulcastCodecInfo = {
1061
1137
  mimeType: isSet(object.mimeType) ? String(object.mimeType) : '',
1062
1138
  mid: isSet(object.mid) ? String(object.mid) : '',
1063
1139
  cid: isSet(object.cid) ? String(object.cid) : '',
1140
+ layers: Array.isArray(object?.layers)
1141
+ ? object.layers.map((e: any) => VideoLayer.fromJSON(e))
1142
+ : [],
1064
1143
  };
1065
1144
  },
1066
1145
 
@@ -1069,6 +1148,11 @@ export const SimulcastCodecInfo = {
1069
1148
  message.mimeType !== undefined && (obj.mimeType = message.mimeType);
1070
1149
  message.mid !== undefined && (obj.mid = message.mid);
1071
1150
  message.cid !== undefined && (obj.cid = message.cid);
1151
+ if (message.layers) {
1152
+ obj.layers = message.layers.map((e) => (e ? VideoLayer.toJSON(e) : undefined));
1153
+ } else {
1154
+ obj.layers = [];
1155
+ }
1072
1156
  return obj;
1073
1157
  },
1074
1158
 
@@ -1077,6 +1161,7 @@ export const SimulcastCodecInfo = {
1077
1161
  message.mimeType = object.mimeType ?? '';
1078
1162
  message.mid = object.mid ?? '';
1079
1163
  message.cid = object.cid ?? '';
1164
+ message.layers = object.layers?.map((e) => VideoLayer.fromPartial(e)) || [];
1080
1165
  return message;
1081
1166
  },
1082
1167
  };
@@ -1820,7 +1905,7 @@ export const ClientInfo = {
1820
1905
  };
1821
1906
 
1822
1907
  function createBaseClientConfiguration(): ClientConfiguration {
1823
- return { video: undefined, screen: undefined, resumeConnection: 0 };
1908
+ return { video: undefined, screen: undefined, resumeConnection: 0, disabledCodecs: undefined };
1824
1909
  }
1825
1910
 
1826
1911
  export const ClientConfiguration = {
@@ -1834,6 +1919,9 @@ export const ClientConfiguration = {
1834
1919
  if (message.resumeConnection !== 0) {
1835
1920
  writer.uint32(24).int32(message.resumeConnection);
1836
1921
  }
1922
+ if (message.disabledCodecs !== undefined) {
1923
+ DisabledCodecs.encode(message.disabledCodecs, writer.uint32(34).fork()).ldelim();
1924
+ }
1837
1925
  return writer;
1838
1926
  },
1839
1927
 
@@ -1853,6 +1941,9 @@ export const ClientConfiguration = {
1853
1941
  case 3:
1854
1942
  message.resumeConnection = reader.int32() as any;
1855
1943
  break;
1944
+ case 4:
1945
+ message.disabledCodecs = DisabledCodecs.decode(reader, reader.uint32());
1946
+ break;
1856
1947
  default:
1857
1948
  reader.skipType(tag & 7);
1858
1949
  break;
@@ -1868,6 +1959,9 @@ export const ClientConfiguration = {
1868
1959
  resumeConnection: isSet(object.resumeConnection)
1869
1960
  ? clientConfigSettingFromJSON(object.resumeConnection)
1870
1961
  : 0,
1962
+ disabledCodecs: isSet(object.disabledCodecs)
1963
+ ? DisabledCodecs.fromJSON(object.disabledCodecs)
1964
+ : undefined,
1871
1965
  };
1872
1966
  },
1873
1967
 
@@ -1879,6 +1973,10 @@ export const ClientConfiguration = {
1879
1973
  (obj.screen = message.screen ? VideoConfiguration.toJSON(message.screen) : undefined);
1880
1974
  message.resumeConnection !== undefined &&
1881
1975
  (obj.resumeConnection = clientConfigSettingToJSON(message.resumeConnection));
1976
+ message.disabledCodecs !== undefined &&
1977
+ (obj.disabledCodecs = message.disabledCodecs
1978
+ ? DisabledCodecs.toJSON(message.disabledCodecs)
1979
+ : undefined);
1882
1980
  return obj;
1883
1981
  },
1884
1982
 
@@ -1895,6 +1993,10 @@ export const ClientConfiguration = {
1895
1993
  ? VideoConfiguration.fromPartial(object.screen)
1896
1994
  : undefined;
1897
1995
  message.resumeConnection = object.resumeConnection ?? 0;
1996
+ message.disabledCodecs =
1997
+ object.disabledCodecs !== undefined && object.disabledCodecs !== null
1998
+ ? DisabledCodecs.fromPartial(object.disabledCodecs)
1999
+ : undefined;
1898
2000
  return message;
1899
2001
  },
1900
2002
  };
@@ -1951,6 +2053,59 @@ export const VideoConfiguration = {
1951
2053
  },
1952
2054
  };
1953
2055
 
2056
+ function createBaseDisabledCodecs(): DisabledCodecs {
2057
+ return { codecs: [] };
2058
+ }
2059
+
2060
+ export const DisabledCodecs = {
2061
+ encode(message: DisabledCodecs, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer {
2062
+ for (const v of message.codecs) {
2063
+ Codec.encode(v!, writer.uint32(10).fork()).ldelim();
2064
+ }
2065
+ return writer;
2066
+ },
2067
+
2068
+ decode(input: _m0.Reader | Uint8Array, length?: number): DisabledCodecs {
2069
+ const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input);
2070
+ let end = length === undefined ? reader.len : reader.pos + length;
2071
+ const message = createBaseDisabledCodecs();
2072
+ while (reader.pos < end) {
2073
+ const tag = reader.uint32();
2074
+ switch (tag >>> 3) {
2075
+ case 1:
2076
+ message.codecs.push(Codec.decode(reader, reader.uint32()));
2077
+ break;
2078
+ default:
2079
+ reader.skipType(tag & 7);
2080
+ break;
2081
+ }
2082
+ }
2083
+ return message;
2084
+ },
2085
+
2086
+ fromJSON(object: any): DisabledCodecs {
2087
+ return {
2088
+ codecs: Array.isArray(object?.codecs) ? object.codecs.map((e: any) => Codec.fromJSON(e)) : [],
2089
+ };
2090
+ },
2091
+
2092
+ toJSON(message: DisabledCodecs): unknown {
2093
+ const obj: any = {};
2094
+ if (message.codecs) {
2095
+ obj.codecs = message.codecs.map((e) => (e ? Codec.toJSON(e) : undefined));
2096
+ } else {
2097
+ obj.codecs = [];
2098
+ }
2099
+ return obj;
2100
+ },
2101
+
2102
+ fromPartial<I extends Exact<DeepPartial<DisabledCodecs>, I>>(object: I): DisabledCodecs {
2103
+ const message = createBaseDisabledCodecs();
2104
+ message.codecs = object.codecs?.map((e) => Codec.fromPartial(e)) || [];
2105
+ return message;
2106
+ },
2107
+ };
2108
+
1954
2109
  function createBaseRTPStats(): RTPStats {
1955
2110
  return {
1956
2111
  startTime: undefined,
@@ -1978,7 +2133,9 @@ function createBaseRTPStats(): RTPStats {
1978
2133
  jitterMax: 0,
1979
2134
  gapHistogram: {},
1980
2135
  nacks: 0,
2136
+ nackAcks: 0,
1981
2137
  nackMisses: 0,
2138
+ nackRepeated: 0,
1982
2139
  plis: 0,
1983
2140
  lastPli: undefined,
1984
2141
  firs: 0,
@@ -2072,9 +2229,15 @@ export const RTPStats = {
2072
2229
  if (message.nacks !== 0) {
2073
2230
  writer.uint32(200).uint32(message.nacks);
2074
2231
  }
2232
+ if (message.nackAcks !== 0) {
2233
+ writer.uint32(296).uint32(message.nackAcks);
2234
+ }
2075
2235
  if (message.nackMisses !== 0) {
2076
2236
  writer.uint32(208).uint32(message.nackMisses);
2077
2237
  }
2238
+ if (message.nackRepeated !== 0) {
2239
+ writer.uint32(304).uint32(message.nackRepeated);
2240
+ }
2078
2241
  if (message.plis !== 0) {
2079
2242
  writer.uint32(216).uint32(message.plis);
2080
2243
  }
@@ -2193,9 +2356,15 @@ export const RTPStats = {
2193
2356
  case 25:
2194
2357
  message.nacks = reader.uint32();
2195
2358
  break;
2359
+ case 37:
2360
+ message.nackAcks = reader.uint32();
2361
+ break;
2196
2362
  case 26:
2197
2363
  message.nackMisses = reader.uint32();
2198
2364
  break;
2365
+ case 38:
2366
+ message.nackRepeated = reader.uint32();
2367
+ break;
2199
2368
  case 27:
2200
2369
  message.plis = reader.uint32();
2201
2370
  break;
@@ -2273,7 +2442,9 @@ export const RTPStats = {
2273
2442
  )
2274
2443
  : {},
2275
2444
  nacks: isSet(object.nacks) ? Number(object.nacks) : 0,
2445
+ nackAcks: isSet(object.nackAcks) ? Number(object.nackAcks) : 0,
2276
2446
  nackMisses: isSet(object.nackMisses) ? Number(object.nackMisses) : 0,
2447
+ nackRepeated: isSet(object.nackRepeated) ? Number(object.nackRepeated) : 0,
2277
2448
  plis: isSet(object.plis) ? Number(object.plis) : 0,
2278
2449
  lastPli: isSet(object.lastPli) ? fromJsonTimestamp(object.lastPli) : undefined,
2279
2450
  firs: isSet(object.firs) ? Number(object.firs) : 0,
@@ -2327,7 +2498,9 @@ export const RTPStats = {
2327
2498
  });
2328
2499
  }
2329
2500
  message.nacks !== undefined && (obj.nacks = Math.round(message.nacks));
2501
+ message.nackAcks !== undefined && (obj.nackAcks = Math.round(message.nackAcks));
2330
2502
  message.nackMisses !== undefined && (obj.nackMisses = Math.round(message.nackMisses));
2503
+ message.nackRepeated !== undefined && (obj.nackRepeated = Math.round(message.nackRepeated));
2331
2504
  message.plis !== undefined && (obj.plis = Math.round(message.plis));
2332
2505
  message.lastPli !== undefined && (obj.lastPli = message.lastPli.toISOString());
2333
2506
  message.firs !== undefined && (obj.firs = Math.round(message.firs));
@@ -2376,7 +2549,9 @@ export const RTPStats = {
2376
2549
  return acc;
2377
2550
  }, {});
2378
2551
  message.nacks = object.nacks ?? 0;
2552
+ message.nackAcks = object.nackAcks ?? 0;
2379
2553
  message.nackMisses = object.nackMisses ?? 0;
2554
+ message.nackRepeated = object.nackRepeated ?? 0;
2380
2555
  message.plis = object.plis ?? 0;
2381
2556
  message.lastPli = object.lastPli ?? undefined;
2382
2557
  message.firs = object.firs ?? 0;
@@ -2480,9 +2655,9 @@ const btoa: (bin: string) => string =
2480
2655
  globalThis.btoa || ((bin) => globalThis.Buffer.from(bin, 'binary').toString('base64'));
2481
2656
  function base64FromBytes(arr: Uint8Array): string {
2482
2657
  const bin: string[] = [];
2483
- arr.forEach((byte) => {
2658
+ for (const byte of arr) {
2484
2659
  bin.push(String.fromCharCode(byte));
2485
- });
2660
+ }
2486
2661
  return btoa(bin.join(''));
2487
2662
  }
2488
2663
 
@@ -9,6 +9,7 @@ import {
9
9
  ClientConfiguration,
10
10
  TrackInfo,
11
11
  VideoQuality,
12
+ DisconnectReason,
12
13
  ConnectionQuality,
13
14
  VideoLayer,
14
15
  ParticipantTracks,
@@ -19,6 +20,8 @@ import {
19
20
  trackSourceToJSON,
20
21
  videoQualityFromJSON,
21
22
  videoQualityToJSON,
23
+ disconnectReasonFromJSON,
24
+ disconnectReasonToJSON,
22
25
  connectionQualityFromJSON,
23
26
  connectionQualityToJSON,
24
27
  } from './livekit_models';
@@ -283,6 +286,7 @@ export interface LeaveRequest {
283
286
  * indicates clients should attempt full-reconnect sequence
284
287
  */
285
288
  canReconnect: boolean;
289
+ reason: DisconnectReason;
286
290
  }
287
291
 
288
292
  /** message to indicate published video track dimensions are changing */
@@ -1836,7 +1840,7 @@ export const UpdateTrackSettings = {
1836
1840
  };
1837
1841
 
1838
1842
  function createBaseLeaveRequest(): LeaveRequest {
1839
- return { canReconnect: false };
1843
+ return { canReconnect: false, reason: 0 };
1840
1844
  }
1841
1845
 
1842
1846
  export const LeaveRequest = {
@@ -1844,6 +1848,9 @@ export const LeaveRequest = {
1844
1848
  if (message.canReconnect === true) {
1845
1849
  writer.uint32(8).bool(message.canReconnect);
1846
1850
  }
1851
+ if (message.reason !== 0) {
1852
+ writer.uint32(16).int32(message.reason);
1853
+ }
1847
1854
  return writer;
1848
1855
  },
1849
1856
 
@@ -1857,6 +1864,9 @@ export const LeaveRequest = {
1857
1864
  case 1:
1858
1865
  message.canReconnect = reader.bool();
1859
1866
  break;
1867
+ case 2:
1868
+ message.reason = reader.int32() as any;
1869
+ break;
1860
1870
  default:
1861
1871
  reader.skipType(tag & 7);
1862
1872
  break;
@@ -1868,18 +1878,21 @@ export const LeaveRequest = {
1868
1878
  fromJSON(object: any): LeaveRequest {
1869
1879
  return {
1870
1880
  canReconnect: isSet(object.canReconnect) ? Boolean(object.canReconnect) : false,
1881
+ reason: isSet(object.reason) ? disconnectReasonFromJSON(object.reason) : 0,
1871
1882
  };
1872
1883
  },
1873
1884
 
1874
1885
  toJSON(message: LeaveRequest): unknown {
1875
1886
  const obj: any = {};
1876
1887
  message.canReconnect !== undefined && (obj.canReconnect = message.canReconnect);
1888
+ message.reason !== undefined && (obj.reason = disconnectReasonToJSON(message.reason));
1877
1889
  return obj;
1878
1890
  },
1879
1891
 
1880
1892
  fromPartial<I extends Exact<DeepPartial<LeaveRequest>, I>>(object: I): LeaveRequest {
1881
1893
  const message = createBaseLeaveRequest();
1882
1894
  message.canReconnect = object.canReconnect ?? false;
1895
+ message.reason = object.reason ?? 0;
1883
1896
  return message;
1884
1897
  },
1885
1898
  };
@@ -7,6 +7,7 @@ import {
7
7
  ClientConfiguration,
8
8
  DataPacket,
9
9
  DataPacket_Kind,
10
+ DisconnectReason,
10
11
  SpeakerInfo,
11
12
  TrackInfo,
12
13
  UserPacket,
@@ -360,9 +361,10 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
360
361
  this.fullReconnectOnNext = true;
361
362
  this.primaryPC = undefined;
362
363
  } else {
363
- this.emit(EngineEvent.Disconnected);
364
+ this.emit(EngineEvent.Disconnected, leave?.reason);
364
365
  this.close();
365
366
  }
367
+ log.trace('leave request', { leave });
366
368
  };
367
369
  }
368
370
 
@@ -715,7 +717,7 @@ class SignalReconnectError extends Error {}
715
717
 
716
718
  export type EngineEventCallbacks = {
717
719
  connected: () => void;
718
- disconnected: () => void;
720
+ disconnected: (reason?: DisconnectReason) => void;
719
721
  resuming: () => void;
720
722
  resumed: () => void;
721
723
  restarting: () => void;
package/src/room/Room.ts CHANGED
@@ -5,6 +5,7 @@ import log from '../logger';
5
5
  import { RoomConnectOptions, RoomOptions } from '../options';
6
6
  import {
7
7
  DataPacket_Kind,
8
+ DisconnectReason,
8
9
  ParticipantInfo,
9
10
  ParticipantInfo_State,
10
11
  ParticipantPermission,
@@ -150,8 +151,8 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
150
151
  this.onTrackAdded(mediaTrack, stream, receiver);
151
152
  },
152
153
  )
153
- .on(EngineEvent.Disconnected, () => {
154
- this.handleDisconnect();
154
+ .on(EngineEvent.Disconnected, (reason?: DisconnectReason) => {
155
+ this.handleDisconnect(this.options.stopLocalTrackOnUnpublish, reason);
155
156
  })
156
157
  .on(EngineEvent.ActiveSpeakersUpdate, this.handleActiveSpeakersUpdate)
157
158
  .on(EngineEvent.DataPacketReceived, this.handleDataPacket)
@@ -288,7 +289,17 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
288
289
 
289
290
  // populate remote participants, these should not trigger new events
290
291
  joinResponse.otherParticipants.forEach((info) => {
291
- this.getOrCreateParticipant(info.sid, info);
292
+ if (
293
+ info.sid !== this.localParticipant.sid &&
294
+ info.identity !== this.localParticipant.identity
295
+ ) {
296
+ this.getOrCreateParticipant(info.sid, info);
297
+ } else {
298
+ log.warn('received info to create local participant as remote participant', {
299
+ info,
300
+ localParticipant: this.localParticipant,
301
+ });
302
+ }
292
303
  });
293
304
 
294
305
  this.name = joinResponse.room!.name;
@@ -390,6 +401,12 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
390
401
  let postAction = () => {};
391
402
  let req: SimulateScenario | undefined;
392
403
  switch (scenario) {
404
+ case 'signal-reconnect':
405
+ this.engine.client.close();
406
+ if (this.engine.client.onClose) {
407
+ this.engine.client.onClose('simulate disconnect');
408
+ }
409
+ break;
393
410
  case 'speaker':
394
411
  req = SimulateScenario.fromPartial({
395
412
  speakerUpdate: 3,
@@ -535,21 +552,27 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
535
552
  // at that time, ICE connectivity has not been established so the track is not
536
553
  // technically subscribed.
537
554
  // We'll defer these events until when the room is connected or eventually disconnected.
538
- if (this.state === ConnectionState.Connecting || this.state === ConnectionState.Reconnecting) {
539
- setTimeout(() => {
555
+ if (this.connectFuture) {
556
+ this.connectFuture.promise.then(() => {
540
557
  this.onTrackAdded(mediaTrack, stream, receiver);
541
- }, 50);
558
+ });
542
559
  return;
543
560
  }
544
561
  if (this.state === ConnectionState.Disconnected) {
545
562
  log.warn('skipping incoming track after Room disconnected');
563
+ return;
546
564
  }
547
565
  const parts = unpackStreamId(stream.id);
548
566
  const participantId = parts[0];
549
567
  let trackId = parts[1];
550
568
  if (!trackId || trackId === '') trackId = mediaTrack.id;
551
569
 
570
+ if (participantId === this.localParticipant.sid) {
571
+ log.warn('tried to create RemoteParticipant for local participant');
572
+ return;
573
+ }
552
574
  const participant = this.getOrCreateParticipant(participantId);
575
+
553
576
  let adaptiveStreamSettings: AdaptiveStreamSettings | undefined;
554
577
  if (this.options.adaptiveStream) {
555
578
  if (typeof this.options.adaptiveStream === 'object') {
@@ -623,7 +646,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
623
646
  );
624
647
  };
625
648
 
626
- private handleDisconnect(shouldStopTracks = true) {
649
+ private handleDisconnect(shouldStopTracks = true, reason?: DisconnectReason) {
627
650
  this.participants.forEach((p) => {
628
651
  p.tracks.forEach((pub) => {
629
652
  p.unpublishTrack(pub.trackSid);
@@ -651,7 +674,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
651
674
  navigator.mediaDevices?.removeEventListener('devicechange', this.handleDeviceChange);
652
675
  }
653
676
  this.setAndEmitConnectionState(ConnectionState.Disconnected);
654
- this.emit(RoomEvent.Disconnected);
677
+ this.emit(RoomEvent.Disconnected, reason);
655
678
  }
656
679
 
657
680
  private handleParticipantUpdates = (participantInfos: ParticipantInfo[]) => {
@@ -1069,7 +1092,7 @@ export default Room;
1069
1092
  export type RoomEventCallbacks = {
1070
1093
  reconnecting: () => void;
1071
1094
  reconnected: () => void;
1072
- disconnected: () => void;
1095
+ disconnected: (reason?: DisconnectReason) => void;
1073
1096
  /** @deprecated stateChanged has been renamed to connectionStateChanged */
1074
1097
  stateChanged: (state: ConnectionState) => void;
1075
1098
  connectionStateChanged: (state: ConnectionState) => void;
@@ -1,6 +1,11 @@
1
1
  import log from '../../logger';
2
2
  import { RoomOptions } from '../../options';
3
- import { DataPacket, DataPacket_Kind, ParticipantPermission } from '../../proto/livekit_models';
3
+ import {
4
+ DataPacket,
5
+ DataPacket_Kind,
6
+ ParticipantInfo,
7
+ ParticipantPermission,
8
+ } from '../../proto/livekit_models';
4
9
  import {
5
10
  AddTrackRequest,
6
11
  DataChannelInfo,
@@ -787,6 +792,29 @@ export default class LocalParticipant extends Participant {
787
792
  }
788
793
  }
789
794
 
795
+ /** @internal */
796
+ updateInfo(info: ParticipantInfo) {
797
+ super.updateInfo(info);
798
+
799
+ // reconcile track mute status.
800
+ // if server's track mute status doesn't match actual, we'll have to update
801
+ // the server's copy
802
+ info.tracks.forEach((ti) => {
803
+ const pub = this.tracks.get(ti.sid);
804
+
805
+ if (pub) {
806
+ const mutedOnServer = pub.isMuted || (pub.track?.isUpstreamPaused ?? false);
807
+ if (mutedOnServer !== ti.muted) {
808
+ log.debug('updating server mute state after reconcile', {
809
+ sid: ti.sid,
810
+ muted: mutedOnServer,
811
+ });
812
+ this.engine.client.sendMuteTrack(ti.sid, mutedOnServer);
813
+ }
814
+ }
815
+ });
816
+ }
817
+
790
818
  private updateTrackSubscriptionPermissions = () => {
791
819
  log.debug('updating track subscription permissions', {
792
820
  allParticipantsAllowed: this.allParticipantsAllowedToSubscribe,
@@ -947,7 +975,7 @@ export default class LocalParticipant extends Participant {
947
975
  this.tracks.forEach((track: LocalTrackPublication) => {
948
976
  if (track.track !== undefined) {
949
977
  infos.push({
950
- cid: track.track.mediaStreamTrack.id,
978
+ cid: track.track.mediaStreamID,
951
979
  track: track.trackInfo,
952
980
  });
953
981
  }
@@ -12,6 +12,7 @@ import RemoteTrackPublication from '../track/RemoteTrackPublication';
12
12
  import { Track } from '../track/Track';
13
13
  import { TrackPublication } from '../track/TrackPublication';
14
14
  import { RemoteTrack } from '../track/types';
15
+ import log from '../../logger';
15
16
 
16
17
  export enum ConnectionQuality {
17
18
  Excellent = 'excellent',
@@ -179,6 +180,7 @@ export default class Participant extends (EventEmitter as new () => TypedEmitter
179
180
  }
180
181
  // set this last so setMetadata can detect changes
181
182
  this.participantInfo = info;
183
+ log.trace('update participant info', { info });
182
184
  }
183
185
 
184
186
  /** @internal */
@@ -218,6 +218,10 @@ export default class RemoteParticipant extends Participant {
218
218
  // detect removed tracks
219
219
  this.tracks.forEach((publication) => {
220
220
  if (!validTracks.has(publication.trackSid)) {
221
+ log.trace('detected removed track on remote participant, unpublishing', {
222
+ publication,
223
+ participantSid: this.sid,
224
+ });
221
225
  this.unpublishTrack(publication.trackSid, true);
222
226
  }
223
227
  });
@@ -35,22 +35,26 @@ export default class LocalAudioTrack extends LocalTrack {
35
35
  }
36
36
 
37
37
  async mute(): Promise<LocalAudioTrack> {
38
- // disabled special handling as it will cause BT headsets to switch communication modes
39
- if (this.source === Track.Source.Microphone && this.stopOnMute) {
40
- log.debug('stopping mic track');
41
- // also stop the track, so that microphone indicator is turned off
42
- this._mediaStreamTrack.stop();
43
- }
44
- await super.mute();
38
+ await this.muteQueue.run(async () => {
39
+ // disabled special handling as it will cause BT headsets to switch communication modes
40
+ if (this.source === Track.Source.Microphone && this.stopOnMute && !this.isUserProvided) {
41
+ log.debug('stopping mic track');
42
+ // also stop the track, so that microphone indicator is turned off
43
+ this._mediaStreamTrack.stop();
44
+ }
45
+ await super.mute();
46
+ });
45
47
  return this;
46
48
  }
47
49
 
48
50
  async unmute(): Promise<LocalAudioTrack> {
49
- if (this.source === Track.Source.Microphone && this.stopOnMute && !this.isUserProvided) {
50
- log.debug('reacquiring mic track');
51
- await this.restartTrack();
52
- }
53
- await super.unmute();
51
+ await this.muteQueue.run(async () => {
52
+ if (this.source === Track.Source.Microphone && this.stopOnMute && !this.isUserProvided) {
53
+ log.debug('reacquiring mic track');
54
+ await this.restartTrack();
55
+ }
56
+ await super.unmute();
57
+ });
54
58
  return this;
55
59
  }
56
60