livekit-client 1.1.8 → 1.2.1

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 (54) hide show
  1. package/README.md +1 -0
  2. package/dist/livekit-client.esm.mjs +677 -187
  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/PCTransport.d.ts +9 -0
  17. package/dist/src/room/PCTransport.d.ts.map +1 -1
  18. package/dist/src/room/RTCEngine.d.ts +3 -2
  19. package/dist/src/room/RTCEngine.d.ts.map +1 -1
  20. package/dist/src/room/Room.d.ts +3 -3
  21. package/dist/src/room/Room.d.ts.map +1 -1
  22. package/dist/src/room/events.d.ts +8 -1
  23. package/dist/src/room/events.d.ts.map +1 -1
  24. package/dist/src/room/participant/LocalParticipant.d.ts +3 -3
  25. package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
  26. package/dist/src/room/participant/RemoteParticipant.d.ts.map +1 -1
  27. package/dist/src/room/track/LocalAudioTrack.d.ts.map +1 -1
  28. package/dist/src/room/track/LocalTrack.d.ts +2 -0
  29. package/dist/src/room/track/LocalTrack.d.ts.map +1 -1
  30. package/dist/src/room/track/LocalVideoTrack.d.ts.map +1 -1
  31. package/dist/src/room/track/RemoteTrack.d.ts.map +1 -1
  32. package/dist/src/room/track/RemoteTrackPublication.d.ts +4 -1
  33. package/dist/src/room/track/RemoteTrackPublication.d.ts.map +1 -1
  34. package/package.json +3 -1
  35. package/src/api/SignalClient.ts +21 -6
  36. package/src/index.ts +6 -2
  37. package/src/options.ts +1 -0
  38. package/src/proto/livekit_models.ts +179 -4
  39. package/src/proto/livekit_rtc.ts +14 -1
  40. package/src/room/PCTransport.ts +39 -0
  41. package/src/room/RTCEngine.ts +44 -18
  42. package/src/room/Room.ts +30 -24
  43. package/src/room/events.ts +7 -0
  44. package/src/room/participant/LocalParticipant.ts +70 -10
  45. package/src/room/participant/RemoteParticipant.ts +12 -10
  46. package/src/room/participant/publishUtils.ts +1 -1
  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/dist/src/api/RequestQueue.d.ts +0 -13
  53. package/dist/src/api/RequestQueue.d.ts.map +0 -1
  54. 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
  };
@@ -1,6 +1,13 @@
1
1
  import { debounce } from 'ts-debounce';
2
2
  import log from '../logger';
3
3
 
4
+ /** @internal */
5
+ interface TrackBitrateInfo {
6
+ sid: string;
7
+ codec: string;
8
+ maxbr: number;
9
+ }
10
+
4
11
  /** @internal */
5
12
  export default class PCTransport {
6
13
  pc: RTCPeerConnection;
@@ -11,6 +18,8 @@ export default class PCTransport {
11
18
 
12
19
  renegotiate: boolean = false;
13
20
 
21
+ trackBitrates: TrackBitrateInfo[] = [];
22
+
14
23
  onOffer?: (offer: RTCSessionDescriptionInit) => void;
15
24
 
16
25
  constructor(config?: RTCConfiguration) {
@@ -78,10 +87,40 @@ export default class PCTransport {
78
87
  // actually negotiate
79
88
  log.debug('starting to negotiate');
80
89
  const offer = await this.pc.createOffer(options);
90
+
91
+ // mung sdp for codec bitrate setting that can't apply by sendEncoding
92
+ this.trackBitrates.forEach((trackbr) => {
93
+ let sdp = offer.sdp ?? '';
94
+ const sidIndex = sdp.search(new RegExp(`msid.* ${trackbr.sid}`));
95
+ if (sidIndex < 0) {
96
+ return;
97
+ }
98
+
99
+ const mlineStart = sdp.substring(0, sidIndex).lastIndexOf('m=');
100
+ const mlineEnd = sdp.indexOf('m=', sidIndex);
101
+ const mediaSection = sdp.substring(mlineStart, mlineEnd);
102
+
103
+ const mungedMediaSection = mediaSection.replace(
104
+ new RegExp(`a=rtpmap:(\\d+) ${trackbr.codec}/\\d+`, 'i'),
105
+ `$&\r\na=fmtp:$1 x-google-max-bitrate=${trackbr.maxbr}`,
106
+ );
107
+ sdp = sdp.substring(0, mlineStart) + mungedMediaSection + sdp.substring(mlineEnd);
108
+ offer.sdp = sdp;
109
+ });
110
+ this.trackBitrates = [];
111
+
81
112
  await this.pc.setLocalDescription(offer);
82
113
  this.onOffer(offer);
83
114
  }
84
115
 
116
+ setTrackCodecBitrate(sid: string, codec: string, maxbr: number) {
117
+ this.trackBitrates.push({
118
+ sid,
119
+ codec,
120
+ maxbr,
121
+ });
122
+ }
123
+
85
124
  close() {
86
125
  this.pc.close();
87
126
  }
@@ -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,13 +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
  }
366
352
  log.trace('leave request', { leave });
367
353
  };
368
354
  }
369
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
+
370
390
  private handleDataChannel = async ({ channel }: RTCDataChannelEvent) => {
371
391
  if (!channel) {
372
392
  return;
@@ -562,6 +582,12 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
562
582
  await this.waitForPCConnected();
563
583
  this.client.setReconnected();
564
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
+
565
591
  // resume success
566
592
  this.emit(EngineEvent.Resumed);
567
593
  }
@@ -716,7 +742,7 @@ class SignalReconnectError extends Error {}
716
742
 
717
743
  export type EngineEventCallbacks = {
718
744
  connected: () => void;
719
- disconnected: () => void;
745
+ disconnected: (reason?: DisconnectReason) => void;
720
746
  resuming: () => void;
721
747
  resumed: () => void;
722
748
  restarting: () => void;