livekit-client 1.1.7 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/README.md +1 -0
  2. package/dist/livekit-client.esm.mjs +667 -176
  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 +4 -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/options.d.ts +1 -0
  11. package/dist/src/options.d.ts.map +1 -1
  12. package/dist/src/proto/livekit_models.d.ts +234 -0
  13. package/dist/src/proto/livekit_models.d.ts.map +1 -1
  14. package/dist/src/proto/livekit_rtc.d.ts +944 -6
  15. package/dist/src/proto/livekit_rtc.d.ts.map +1 -1
  16. package/dist/src/room/RTCEngine.d.ts +3 -2
  17. package/dist/src/room/RTCEngine.d.ts.map +1 -1
  18. package/dist/src/room/Room.d.ts +3 -3
  19. package/dist/src/room/Room.d.ts.map +1 -1
  20. package/dist/src/room/events.d.ts +8 -1
  21. package/dist/src/room/events.d.ts.map +1 -1
  22. package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
  23. package/dist/src/room/participant/Participant.d.ts.map +1 -1
  24. package/dist/src/room/participant/RemoteParticipant.d.ts.map +1 -1
  25. package/dist/src/room/track/LocalAudioTrack.d.ts.map +1 -1
  26. package/dist/src/room/track/LocalTrack.d.ts +2 -0
  27. package/dist/src/room/track/LocalTrack.d.ts.map +1 -1
  28. package/dist/src/room/track/LocalVideoTrack.d.ts.map +1 -1
  29. package/dist/src/room/track/RemoteTrack.d.ts.map +1 -1
  30. package/dist/src/room/track/RemoteTrackPublication.d.ts +4 -1
  31. package/dist/src/room/track/RemoteTrackPublication.d.ts.map +1 -1
  32. package/dist/src/room/track/Track.d.ts +7 -0
  33. package/dist/src/room/track/Track.d.ts.map +1 -1
  34. package/dist/src/room/track/TrackPublication.d.ts.map +1 -1
  35. package/package.json +3 -1
  36. package/src/api/SignalClient.ts +21 -6
  37. package/src/index.ts +6 -2
  38. package/src/options.ts +1 -0
  39. package/src/proto/livekit_models.ts +179 -4
  40. package/src/proto/livekit_rtc.ts +14 -1
  41. package/src/room/RTCEngine.ts +45 -18
  42. package/src/room/Room.ts +46 -25
  43. package/src/room/events.ts +7 -0
  44. package/src/room/participant/LocalParticipant.ts +45 -7
  45. package/src/room/participant/Participant.ts +2 -0
  46. package/src/room/participant/RemoteParticipant.ts +16 -10
  47. package/src/room/track/LocalAudioTrack.ts +16 -12
  48. package/src/room/track/LocalTrack.ts +41 -25
  49. package/src/room/track/LocalVideoTrack.ts +15 -11
  50. package/src/room/track/RemoteTrack.ts +1 -0
  51. package/src/room/track/RemoteTrackPublication.ts +37 -11
  52. package/src/room/track/Track.ts +12 -0
  53. package/src/room/track/TrackPublication.ts +2 -0
  54. package/dist/src/api/RequestQueue.d.ts +0 -13
  55. package/dist/src/api/RequestQueue.d.ts.map +0 -1
  56. 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,
@@ -276,23 +277,8 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
276
277
  this.emit(EngineEvent.MediaTrackAdded, track, ev.stream);
277
278
  };
278
279
  }
279
- // data channels
280
- this.lossyDC = this.publisher.pc.createDataChannel(lossyDataChannel, {
281
- // will drop older packets that arrive
282
- ordered: true,
283
- maxRetransmits: 0,
284
- });
285
- this.reliableDC = this.publisher.pc.createDataChannel(reliableDataChannel, {
286
- ordered: true,
287
- });
288
280
 
289
- // also handle messages over the pub channel, for backwards compatibility
290
- this.lossyDC.onmessage = this.handleDataMessage;
291
- this.reliableDC.onmessage = this.handleDataMessage;
292
-
293
- // handle datachannel errors
294
- this.lossyDC.onerror = this.handleDataError;
295
- this.reliableDC.onerror = this.handleDataError;
281
+ this.createDataChannels();
296
282
 
297
283
  // configure signaling client
298
284
  this.client.onAnswer = async (sd) => {
@@ -360,12 +346,47 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
360
346
  this.fullReconnectOnNext = true;
361
347
  this.primaryPC = undefined;
362
348
  } else {
363
- this.emit(EngineEvent.Disconnected);
349
+ this.emit(EngineEvent.Disconnected, leave?.reason);
364
350
  this.close();
365
351
  }
352
+ log.trace('leave request', { leave });
366
353
  };
367
354
  }
368
355
 
356
+ private createDataChannels() {
357
+ if (!this.publisher) {
358
+ return;
359
+ }
360
+
361
+ // clear old data channel callbacks if recreate
362
+ if (this.lossyDC) {
363
+ this.lossyDC.onmessage = null;
364
+ this.lossyDC.onerror = null;
365
+ }
366
+ if (this.reliableDC) {
367
+ this.reliableDC.onmessage = null;
368
+ this.reliableDC.onerror = null;
369
+ }
370
+
371
+ // create data channels
372
+ this.lossyDC = this.publisher.pc.createDataChannel(lossyDataChannel, {
373
+ // will drop older packets that arrive
374
+ ordered: true,
375
+ maxRetransmits: 0,
376
+ });
377
+ this.reliableDC = this.publisher.pc.createDataChannel(reliableDataChannel, {
378
+ ordered: true,
379
+ });
380
+
381
+ // also handle messages over the pub channel, for backwards compatibility
382
+ this.lossyDC.onmessage = this.handleDataMessage;
383
+ this.reliableDC.onmessage = this.handleDataMessage;
384
+
385
+ // handle datachannel errors
386
+ this.lossyDC.onerror = this.handleDataError;
387
+ this.reliableDC.onerror = this.handleDataError;
388
+ }
389
+
369
390
  private handleDataChannel = async ({ channel }: RTCDataChannelEvent) => {
370
391
  if (!channel) {
371
392
  return;
@@ -561,6 +582,12 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
561
582
  await this.waitForPCConnected();
562
583
  this.client.setReconnected();
563
584
 
585
+ // recreate publish datachannel if it's id is null
586
+ // (for safari https://bugs.webkit.org/show_bug.cgi?id=184688)
587
+ if (this.reliableDC?.readyState === 'open' && this.reliableDC.id === null) {
588
+ this.createDataChannels();
589
+ }
590
+
564
591
  // resume success
565
592
  this.emit(EngineEvent.Resumed);
566
593
  }
@@ -715,7 +742,7 @@ class SignalReconnectError extends Error {}
715
742
 
716
743
  export type EngineEventCallbacks = {
717
744
  connected: () => void;
718
- disconnected: () => void;
745
+ disconnected: (reason?: DisconnectReason) => void;
719
746
  resuming: () => void;
720
747
  resumed: () => void;
721
748
  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;
@@ -347,7 +358,8 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
347
358
  /**
348
359
  * disconnects the room, emits [[RoomEvent.Disconnected]]
349
360
  */
350
- disconnect = (stopTracks = true) => {
361
+ disconnect = async (stopTracks = true) => {
362
+ log.info('disconnect from room', { identity: this.localParticipant.identity });
351
363
  if (this.state === ConnectionState.Connecting) {
352
364
  // try aborting pending connection attempt
353
365
  log.warn('abort connection attempt');
@@ -356,14 +368,14 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
356
368
  }
357
369
  // send leave
358
370
  if (this.engine?.client.isConnected) {
359
- this.engine.client.sendLeave();
371
+ await this.engine.client.sendLeave();
360
372
  }
361
373
  // close engine (also closes client)
362
374
  if (this.engine) {
363
375
  this.engine.close();
364
376
  }
365
377
 
366
- this.handleDisconnect(stopTracks);
378
+ this.handleDisconnect(stopTracks, DisconnectReason.CLIENT_INITIATED);
367
379
  /* @ts-ignore */
368
380
  this.engine = undefined;
369
381
  };
@@ -541,21 +553,27 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
541
553
  // at that time, ICE connectivity has not been established so the track is not
542
554
  // technically subscribed.
543
555
  // We'll defer these events until when the room is connected or eventually disconnected.
544
- if (this.state === ConnectionState.Connecting || this.state === ConnectionState.Reconnecting) {
545
- setTimeout(() => {
556
+ if (this.connectFuture) {
557
+ this.connectFuture.promise.then(() => {
546
558
  this.onTrackAdded(mediaTrack, stream, receiver);
547
- }, 50);
559
+ });
548
560
  return;
549
561
  }
550
562
  if (this.state === ConnectionState.Disconnected) {
551
563
  log.warn('skipping incoming track after Room disconnected');
564
+ return;
552
565
  }
553
566
  const parts = unpackStreamId(stream.id);
554
567
  const participantId = parts[0];
555
568
  let trackId = parts[1];
556
569
  if (!trackId || trackId === '') trackId = mediaTrack.id;
557
570
 
571
+ if (participantId === this.localParticipant.sid) {
572
+ log.warn('tried to create RemoteParticipant for local participant');
573
+ return;
574
+ }
558
575
  const participant = this.getOrCreateParticipant(participantId);
576
+
559
577
  let adaptiveStreamSettings: AdaptiveStreamSettings | undefined;
560
578
  if (this.options.adaptiveStream) {
561
579
  if (typeof this.options.adaptiveStream === 'object') {
@@ -629,7 +647,10 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
629
647
  );
630
648
  };
631
649
 
632
- private handleDisconnect(shouldStopTracks = true) {
650
+ private handleDisconnect(shouldStopTracks = true, reason?: DisconnectReason) {
651
+ if (this.state === ConnectionState.Disconnected) {
652
+ return;
653
+ }
633
654
  this.participants.forEach((p) => {
634
655
  p.tracks.forEach((pub) => {
635
656
  p.unpublishTrack(pub.trackSid);
@@ -645,6 +666,9 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
645
666
  pub.track?.stop();
646
667
  }
647
668
  });
669
+ this.localParticipant.tracks.clear();
670
+ this.localParticipant.videoTracks.clear();
671
+ this.localParticipant.audioTracks.clear();
648
672
 
649
673
  this.participants.clear();
650
674
  this.activeSpeakers = [];
@@ -657,7 +681,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
657
681
  navigator.mediaDevices?.removeEventListener('devicechange', this.handleDeviceChange);
658
682
  }
659
683
  this.setAndEmitConnectionState(ConnectionState.Disconnected);
660
- this.emit(RoomEvent.Disconnected);
684
+ this.emit(RoomEvent.Disconnected, reason);
661
685
  }
662
686
 
663
687
  private handleParticipantUpdates = (participantInfos: ParticipantInfo[]) => {
@@ -806,18 +830,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
806
830
  return;
807
831
  }
808
832
 
809
- pub._allowed = update.allowed;
810
- participant.emit(
811
- ParticipantEvent.TrackSubscriptionPermissionChanged,
812
- pub,
813
- pub.subscriptionStatus,
814
- );
815
- this.emitWhenConnected(
816
- RoomEvent.TrackSubscriptionPermissionChanged,
817
- pub,
818
- pub.subscriptionStatus,
819
- participant,
820
- );
833
+ pub.setAllowed(update.allowed);
821
834
  };
822
835
 
823
836
  private handleDataPacket = (userPacket: UserPacket, kind: DataPacket_Kind) => {
@@ -954,7 +967,15 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
954
967
  participant,
955
968
  );
956
969
  },
957
- );
970
+ )
971
+ .on(ParticipantEvent.TrackSubscriptionPermissionChanged, (pub, status) => {
972
+ this.emitWhenConnected(
973
+ RoomEvent.TrackSubscriptionPermissionChanged,
974
+ pub,
975
+ status,
976
+ participant,
977
+ );
978
+ });
958
979
 
959
980
  // update info at the end after callbacks have been set up
960
981
  if (info) {
@@ -1075,7 +1096,7 @@ export default Room;
1075
1096
  export type RoomEventCallbacks = {
1076
1097
  reconnecting: () => void;
1077
1098
  reconnected: () => void;
1078
- disconnected: () => void;
1099
+ disconnected: (reason?: DisconnectReason) => void;
1079
1100
  /** @deprecated stateChanged has been renamed to connectionStateChanged */
1080
1101
  stateChanged: (state: ConnectionState) => void;
1081
1102
  connectionStateChanged: (state: ConnectionState) => void;