livekit-client 1.6.2 → 1.6.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. package/dist/livekit-client.esm.mjs +312 -101
  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 -3
  6. package/dist/src/api/SignalClient.d.ts.map +1 -1
  7. package/dist/src/index.d.ts +2 -1
  8. package/dist/src/index.d.ts.map +1 -1
  9. package/dist/src/proto/livekit_models.d.ts +43 -1
  10. package/dist/src/proto/livekit_models.d.ts.map +1 -1
  11. package/dist/src/proto/livekit_rtc.d.ts +473 -4
  12. package/dist/src/proto/livekit_rtc.d.ts.map +1 -1
  13. package/dist/src/room/PCTransport.d.ts +1 -0
  14. package/dist/src/room/PCTransport.d.ts.map +1 -1
  15. package/dist/src/room/RTCEngine.d.ts +2 -0
  16. package/dist/src/room/RTCEngine.d.ts.map +1 -1
  17. package/dist/src/room/Room.d.ts.map +1 -1
  18. package/dist/src/room/timers.d.ts +13 -0
  19. package/dist/src/room/timers.d.ts.map +1 -0
  20. package/dist/src/room/track/RemoteVideoTrack.d.ts.map +1 -1
  21. package/dist/ts4.2/src/api/SignalClient.d.ts +3 -3
  22. package/dist/ts4.2/src/index.d.ts +2 -1
  23. package/dist/ts4.2/src/proto/livekit_models.d.ts +45 -1
  24. package/dist/ts4.2/src/proto/livekit_rtc.d.ts +514 -3
  25. package/dist/ts4.2/src/room/PCTransport.d.ts +1 -0
  26. package/dist/ts4.2/src/room/RTCEngine.d.ts +2 -0
  27. package/dist/ts4.2/src/room/timers.d.ts +13 -0
  28. package/package.json +1 -1
  29. package/src/api/SignalClient.ts +28 -20
  30. package/src/index.ts +2 -0
  31. package/src/proto/livekit_models.ts +116 -1
  32. package/src/proto/livekit_rtc.ts +106 -2
  33. package/src/room/PCTransport.ts +22 -6
  34. package/src/room/RTCEngine.ts +56 -43
  35. package/src/room/Room.ts +4 -3
  36. package/src/room/timers.ts +16 -0
  37. package/src/room/track/RemoteVideoTrack.ts +2 -1
@@ -18,6 +18,7 @@ export default class PCTransport extends EventEmitter {
18
18
  renegotiate: boolean;
19
19
  trackBitrates: TrackBitrateInfo[];
20
20
  remoteStereoMids: string[];
21
+ remoteNackMids: string[];
21
22
  onOffer?: (offer: RTCSessionDescriptionInit) => void;
22
23
  constructor(config?: RTCConfiguration);
23
24
  get isICEConnected(): boolean;
@@ -54,6 +54,7 @@ export default class RTCEngine extends RTCEngine_base {
54
54
  get dataSubscriberReadyState(): string | undefined;
55
55
  get connectedServerAddress(): string | undefined;
56
56
  private configure;
57
+ private makeRTCConfiguration;
57
58
  private createDataChannels;
58
59
  private handleDataChannel;
59
60
  private handleDataMessage;
@@ -79,6 +80,7 @@ export default class RTCEngine extends RTCEngine_base {
79
80
  /** @internal */
80
81
  negotiate(): Promise<void>;
81
82
  dataChannelForKind(kind: DataPacket_Kind, sub?: boolean): RTCDataChannel | undefined;
83
+ private clearReconnectTimeout;
82
84
  private clearPendingReconnect;
83
85
  private handleBrowserOnLine;
84
86
  private registerOnLineListener;
@@ -0,0 +1,13 @@
1
+ /// <reference types="node" />
2
+ /**
3
+ * Timers that can be overridden with platform specific implementations
4
+ * that ensure that they are fired. These should be used when it is critical
5
+ * that the timer fires on time.
6
+ */
7
+ export default class CriticalTimers {
8
+ static setTimeout: (callback: (args: void) => void, ms?: number | undefined) => NodeJS.Timeout;
9
+ static setInterval: (callback: (args: void) => void, ms?: number | undefined) => NodeJS.Timer;
10
+ static clearTimeout: (timeoutId: NodeJS.Timeout) => void;
11
+ static clearInterval: (intervalId: NodeJS.Timeout) => void;
12
+ }
13
+ //# sourceMappingURL=timers.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "livekit-client",
3
- "version": "1.6.2",
3
+ "version": "1.6.3",
4
4
  "description": "JavaScript/TypeScript client SDK for LiveKit",
5
5
  "main": "./dist/livekit-client.umd.js",
6
6
  "unpkg": "./dist/livekit-client.umd.js",
@@ -14,6 +14,7 @@ import {
14
14
  ConnectionQualityUpdate,
15
15
  JoinResponse,
16
16
  LeaveRequest,
17
+ ReconnectResponse,
17
18
  SessionDescription,
18
19
  SignalRequest,
19
20
  SignalResponse,
@@ -30,6 +31,7 @@ import {
30
31
  UpdateTrackSettings,
31
32
  } from '../proto/livekit_rtc';
32
33
  import { ConnectionError, ConnectionErrorReason } from '../room/errors';
34
+ import CriticalTimers from '../room/timers';
33
35
  import { getClientInfo, Mutex, sleep } from '../room/utils';
34
36
 
35
37
  // internal options
@@ -164,7 +166,7 @@ export class SignalClient {
164
166
  return res as JoinResponse;
165
167
  }
166
168
 
167
- async reconnect(url: string, token: string, sid?: string): Promise<void> {
169
+ async reconnect(url: string, token: string, sid?: string): Promise<ReconnectResponse | void> {
168
170
  if (!this.options) {
169
171
  log.warn('attempted to reconnect without signal options being set, ignoring');
170
172
  return;
@@ -173,7 +175,8 @@ export class SignalClient {
173
175
  // clear ping interval and restart it once reconnected
174
176
  this.clearPingInterval();
175
177
 
176
- await this.connect(url, token, { ...this.options, reconnect: true, sid });
178
+ const res = await this.connect(url, token, { ...this.options, reconnect: true, sid });
179
+ return res;
177
180
  }
178
181
 
179
182
  connect(
@@ -181,7 +184,7 @@ export class SignalClient {
181
184
  token: string,
182
185
  opts: ConnectOpts,
183
186
  abortSignal?: AbortSignal,
184
- ): Promise<JoinResponse | void> {
187
+ ): Promise<JoinResponse | ReconnectResponse | void> {
185
188
  this.connectOptions = opts;
186
189
  if (url.startsWith('http')) {
187
190
  url = url.replace('http', 'ws');
@@ -193,7 +196,7 @@ export class SignalClient {
193
196
  const clientInfo = getClientInfo();
194
197
  const params = createConnectionParams(token, clientInfo, opts);
195
198
 
196
- return new Promise<JoinResponse | void>(async (resolve, reject) => {
199
+ return new Promise<JoinResponse | ReconnectResponse | void>(async (resolve, reject) => {
197
200
  const abortHandler = async () => {
198
201
  await this.close();
199
202
  reject(new ConnectionError('room connection has been cancelled'));
@@ -240,16 +243,6 @@ export class SignalClient {
240
243
  this.handleWSError(ev);
241
244
  };
242
245
 
243
- this.ws.onopen = () => {
244
- if (opts.reconnect) {
245
- // upon reconnection, there will not be additional handshake
246
- this.isConnected = true;
247
- // restart ping interval as it's cleared for reconnection
248
- this.startPingInterval();
249
- resolve();
250
- }
251
- };
252
-
253
246
  this.ws.onmessage = async (ev: MessageEvent) => {
254
247
  // not considered connected until JoinResponse is received
255
248
  let resp: SignalResponse;
@@ -264,6 +257,7 @@ export class SignalClient {
264
257
  }
265
258
 
266
259
  if (!this.isConnected) {
260
+ let shouldProcessMessage = false;
267
261
  // handle join message only
268
262
  if (resp.message?.$case === 'join') {
269
263
  this.isConnected = true;
@@ -279,14 +273,28 @@ export class SignalClient {
279
273
  this.startPingInterval();
280
274
  }
281
275
  resolve(resp.message.join);
282
- } else {
276
+ } else if (opts.reconnect) {
277
+ // in reconnecting, any message received means signal reconnected
278
+ this.isConnected = true;
279
+ abortSignal?.removeEventListener('abort', abortHandler);
280
+ this.startPingInterval();
281
+ if (resp.message?.$case === 'reconnect') {
282
+ resolve(resp.message?.reconnect);
283
+ } else {
284
+ resolve();
285
+ shouldProcessMessage = true;
286
+ }
287
+ } else if (!opts.reconnect) {
288
+ // non-reconnect case, should receive join response first
283
289
  reject(
284
290
  new ConnectionError(
285
291
  `did not receive join response, got ${resp.message?.$case} instead`,
286
292
  ),
287
293
  );
288
294
  }
289
- return;
295
+ if (!shouldProcessMessage) {
296
+ return;
297
+ }
290
298
  }
291
299
 
292
300
  if (this.signalLatency) {
@@ -576,7 +584,7 @@ export class SignalClient {
576
584
  log.warn('ping timeout duration not set');
577
585
  return;
578
586
  }
579
- this.pingTimeout = setTimeout(() => {
587
+ this.pingTimeout = CriticalTimers.setTimeout(() => {
580
588
  log.warn(
581
589
  `ping timeout triggered. last pong received at: ${new Date(
582
590
  Date.now() - this.pingTimeoutDuration! * 1000,
@@ -590,7 +598,7 @@ export class SignalClient {
590
598
 
591
599
  private clearPingTimeout() {
592
600
  if (this.pingTimeout) {
593
- clearTimeout(this.pingTimeout);
601
+ CriticalTimers.clearTimeout(this.pingTimeout);
594
602
  }
595
603
  }
596
604
 
@@ -602,7 +610,7 @@ export class SignalClient {
602
610
  return;
603
611
  }
604
612
  log.debug('start ping interval');
605
- this.pingInterval = setInterval(() => {
613
+ this.pingInterval = CriticalTimers.setInterval(() => {
606
614
  this.sendPing();
607
615
  }, this.pingIntervalDuration * 1000);
608
616
  }
@@ -611,7 +619,7 @@ export class SignalClient {
611
619
  log.debug('clearing ping interval');
612
620
  this.clearPingTimeout();
613
621
  if (this.pingInterval) {
614
- clearInterval(this.pingInterval);
622
+ CriticalTimers.clearInterval(this.pingInterval);
615
623
  }
616
624
  }
617
625
  }
package/src/index.ts CHANGED
@@ -16,6 +16,7 @@ import RemoteTrackPublication from './room/track/RemoteTrackPublication';
16
16
  import RemoteVideoTrack from './room/track/RemoteVideoTrack';
17
17
  import type { ElementInfo } from './room/track/RemoteVideoTrack';
18
18
  import { TrackPublication } from './room/track/TrackPublication';
19
+ import CriticalTimers from './room/timers';
19
20
  import {
20
21
  getEmptyAudioStreamTrack,
21
22
  getEmptyVideoStreamTrack,
@@ -71,4 +72,5 @@ export {
71
72
  ConnectionQuality,
72
73
  ElementInfo,
73
74
  DefaultReconnectPolicy,
75
+ CriticalTimers,
74
76
  };
@@ -386,6 +386,48 @@ export function participantInfo_StateToJSON(object: ParticipantInfo_State): stri
386
386
  }
387
387
  }
388
388
 
389
+ export interface Encryption {
390
+ }
391
+
392
+ export enum Encryption_Type {
393
+ NONE = 0,
394
+ GCM = 1,
395
+ CUSTOM = 2,
396
+ UNRECOGNIZED = -1,
397
+ }
398
+
399
+ export function encryption_TypeFromJSON(object: any): Encryption_Type {
400
+ switch (object) {
401
+ case 0:
402
+ case "NONE":
403
+ return Encryption_Type.NONE;
404
+ case 1:
405
+ case "GCM":
406
+ return Encryption_Type.GCM;
407
+ case 2:
408
+ case "CUSTOM":
409
+ return Encryption_Type.CUSTOM;
410
+ case -1:
411
+ case "UNRECOGNIZED":
412
+ default:
413
+ return Encryption_Type.UNRECOGNIZED;
414
+ }
415
+ }
416
+
417
+ export function encryption_TypeToJSON(object: Encryption_Type): string {
418
+ switch (object) {
419
+ case Encryption_Type.NONE:
420
+ return "NONE";
421
+ case Encryption_Type.GCM:
422
+ return "GCM";
423
+ case Encryption_Type.CUSTOM:
424
+ return "CUSTOM";
425
+ case Encryption_Type.UNRECOGNIZED:
426
+ default:
427
+ return "UNRECOGNIZED";
428
+ }
429
+ }
430
+
389
431
  export interface SimulcastCodecInfo {
390
432
  mimeType: string;
391
433
  mid: string;
@@ -416,6 +458,10 @@ export interface TrackInfo {
416
458
  mimeType: string;
417
459
  mid: string;
418
460
  codecs: SimulcastCodecInfo[];
461
+ stereo: boolean;
462
+ /** true if RED (Redundant Encoding) is disabled for audio */
463
+ disableRed: boolean;
464
+ encryption: Encryption_Type;
419
465
  }
420
466
 
421
467
  /** provide information about available spatial layers */
@@ -424,7 +470,7 @@ export interface VideoLayer {
424
470
  quality: VideoQuality;
425
471
  width: number;
426
472
  height: number;
427
- /** target bitrate, server will measure actual */
473
+ /** target bitrate in bit per second (bps), server will measure actual */
428
474
  bitrate: number;
429
475
  ssrc: number;
430
476
  }
@@ -1136,6 +1182,45 @@ export const ParticipantInfo = {
1136
1182
  },
1137
1183
  };
1138
1184
 
1185
+ function createBaseEncryption(): Encryption {
1186
+ return {};
1187
+ }
1188
+
1189
+ export const Encryption = {
1190
+ encode(_: Encryption, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer {
1191
+ return writer;
1192
+ },
1193
+
1194
+ decode(input: _m0.Reader | Uint8Array, length?: number): Encryption {
1195
+ const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input);
1196
+ let end = length === undefined ? reader.len : reader.pos + length;
1197
+ const message = createBaseEncryption();
1198
+ while (reader.pos < end) {
1199
+ const tag = reader.uint32();
1200
+ switch (tag >>> 3) {
1201
+ default:
1202
+ reader.skipType(tag & 7);
1203
+ break;
1204
+ }
1205
+ }
1206
+ return message;
1207
+ },
1208
+
1209
+ fromJSON(_: any): Encryption {
1210
+ return {};
1211
+ },
1212
+
1213
+ toJSON(_: Encryption): unknown {
1214
+ const obj: any = {};
1215
+ return obj;
1216
+ },
1217
+
1218
+ fromPartial<I extends Exact<DeepPartial<Encryption>, I>>(_: I): Encryption {
1219
+ const message = createBaseEncryption();
1220
+ return message;
1221
+ },
1222
+ };
1223
+
1139
1224
  function createBaseSimulcastCodecInfo(): SimulcastCodecInfo {
1140
1225
  return { mimeType: "", mid: "", cid: "", layers: [] };
1141
1226
  }
@@ -1231,6 +1316,9 @@ function createBaseTrackInfo(): TrackInfo {
1231
1316
  mimeType: "",
1232
1317
  mid: "",
1233
1318
  codecs: [],
1319
+ stereo: false,
1320
+ disableRed: false,
1321
+ encryption: 0,
1234
1322
  };
1235
1323
  }
1236
1324
 
@@ -1275,6 +1363,15 @@ export const TrackInfo = {
1275
1363
  for (const v of message.codecs) {
1276
1364
  SimulcastCodecInfo.encode(v!, writer.uint32(106).fork()).ldelim();
1277
1365
  }
1366
+ if (message.stereo === true) {
1367
+ writer.uint32(112).bool(message.stereo);
1368
+ }
1369
+ if (message.disableRed === true) {
1370
+ writer.uint32(120).bool(message.disableRed);
1371
+ }
1372
+ if (message.encryption !== 0) {
1373
+ writer.uint32(128).int32(message.encryption);
1374
+ }
1278
1375
  return writer;
1279
1376
  },
1280
1377
 
@@ -1324,6 +1421,15 @@ export const TrackInfo = {
1324
1421
  case 13:
1325
1422
  message.codecs.push(SimulcastCodecInfo.decode(reader, reader.uint32()));
1326
1423
  break;
1424
+ case 14:
1425
+ message.stereo = reader.bool();
1426
+ break;
1427
+ case 15:
1428
+ message.disableRed = reader.bool();
1429
+ break;
1430
+ case 16:
1431
+ message.encryption = reader.int32() as any;
1432
+ break;
1327
1433
  default:
1328
1434
  reader.skipType(tag & 7);
1329
1435
  break;
@@ -1347,6 +1453,9 @@ export const TrackInfo = {
1347
1453
  mimeType: isSet(object.mimeType) ? String(object.mimeType) : "",
1348
1454
  mid: isSet(object.mid) ? String(object.mid) : "",
1349
1455
  codecs: Array.isArray(object?.codecs) ? object.codecs.map((e: any) => SimulcastCodecInfo.fromJSON(e)) : [],
1456
+ stereo: isSet(object.stereo) ? Boolean(object.stereo) : false,
1457
+ disableRed: isSet(object.disableRed) ? Boolean(object.disableRed) : false,
1458
+ encryption: isSet(object.encryption) ? encryption_TypeFromJSON(object.encryption) : 0,
1350
1459
  };
1351
1460
  },
1352
1461
 
@@ -1373,6 +1482,9 @@ export const TrackInfo = {
1373
1482
  } else {
1374
1483
  obj.codecs = [];
1375
1484
  }
1485
+ message.stereo !== undefined && (obj.stereo = message.stereo);
1486
+ message.disableRed !== undefined && (obj.disableRed = message.disableRed);
1487
+ message.encryption !== undefined && (obj.encryption = encryption_TypeToJSON(message.encryption));
1376
1488
  return obj;
1377
1489
  },
1378
1490
 
@@ -1391,6 +1503,9 @@ export const TrackInfo = {
1391
1503
  message.mimeType = object.mimeType ?? "";
1392
1504
  message.mid = object.mid ?? "";
1393
1505
  message.codecs = object.codecs?.map((e) => SimulcastCodecInfo.fromPartial(e)) || [];
1506
+ message.stereo = object.stereo ?? false;
1507
+ message.disableRed = object.disableRed ?? false;
1508
+ message.encryption = object.encryption ?? 0;
1394
1509
  return message;
1395
1510
  },
1396
1511
  };
@@ -9,6 +9,9 @@ import {
9
9
  DisconnectReason,
10
10
  disconnectReasonFromJSON,
11
11
  disconnectReasonToJSON,
12
+ Encryption_Type,
13
+ encryption_TypeFromJSON,
14
+ encryption_TypeToJSON,
12
15
  ParticipantInfo,
13
16
  ParticipantTracks,
14
17
  Room,
@@ -169,7 +172,8 @@ export interface SignalResponse {
169
172
  | { $case: "subscriptionPermissionUpdate"; subscriptionPermissionUpdate: SubscriptionPermissionUpdate }
170
173
  | { $case: "refreshToken"; refreshToken: string }
171
174
  | { $case: "trackUnpublished"; trackUnpublished: TrackUnpublishedResponse }
172
- | { $case: "pong"; pong: number };
175
+ | { $case: "pong"; pong: number }
176
+ | { $case: "reconnect"; reconnect: ReconnectResponse };
173
177
  }
174
178
 
175
179
  export interface SimulcastCodec {
@@ -198,6 +202,7 @@ export interface AddTrackRequest {
198
202
  stereo: boolean;
199
203
  /** true if RED (Redundant Encoding) is disabled for audio */
200
204
  disableRed: boolean;
205
+ encryption: Encryption_Type;
201
206
  }
202
207
 
203
208
  export interface TrickleRequest {
@@ -232,6 +237,11 @@ export interface JoinResponse {
232
237
  serverInfo?: ServerInfo;
233
238
  }
234
239
 
240
+ export interface ReconnectResponse {
241
+ iceServers: ICEServer[];
242
+ clientConfiguration?: ClientConfiguration;
243
+ }
244
+
235
245
  export interface TrackPublishedResponse {
236
246
  cid: string;
237
247
  track?: TrackInfo;
@@ -267,7 +277,6 @@ export interface UpdateTrackSettings {
267
277
  width: number;
268
278
  /** for video, height to receive */
269
279
  height: number;
270
- /** for video, frame rate to receive */
271
280
  fps: number;
272
281
  }
273
282
 
@@ -703,6 +712,9 @@ export const SignalResponse = {
703
712
  if (message.message?.$case === "pong") {
704
713
  writer.uint32(144).int64(message.message.pong);
705
714
  }
715
+ if (message.message?.$case === "reconnect") {
716
+ ReconnectResponse.encode(message.message.reconnect, writer.uint32(154).fork()).ldelim();
717
+ }
706
718
  return writer;
707
719
  },
708
720
 
@@ -785,6 +797,9 @@ export const SignalResponse = {
785
797
  case 18:
786
798
  message.message = { $case: "pong", pong: longToNumber(reader.int64() as Long) };
787
799
  break;
800
+ case 19:
801
+ message.message = { $case: "reconnect", reconnect: ReconnectResponse.decode(reader, reader.uint32()) };
802
+ break;
788
803
  default:
789
804
  reader.skipType(tag & 7);
790
805
  break;
@@ -835,6 +850,8 @@ export const SignalResponse = {
835
850
  ? { $case: "trackUnpublished", trackUnpublished: TrackUnpublishedResponse.fromJSON(object.trackUnpublished) }
836
851
  : isSet(object.pong)
837
852
  ? { $case: "pong", pong: Number(object.pong) }
853
+ : isSet(object.reconnect)
854
+ ? { $case: "reconnect", reconnect: ReconnectResponse.fromJSON(object.reconnect) }
838
855
  : undefined,
839
856
  };
840
857
  },
@@ -882,6 +899,8 @@ export const SignalResponse = {
882
899
  ? TrackUnpublishedResponse.toJSON(message.message?.trackUnpublished)
883
900
  : undefined);
884
901
  message.message?.$case === "pong" && (obj.pong = Math.round(message.message?.pong));
902
+ message.message?.$case === "reconnect" &&
903
+ (obj.reconnect = message.message?.reconnect ? ReconnectResponse.toJSON(message.message?.reconnect) : undefined);
885
904
  return obj;
886
905
  },
887
906
 
@@ -999,6 +1018,13 @@ export const SignalResponse = {
999
1018
  if (object.message?.$case === "pong" && object.message?.pong !== undefined && object.message?.pong !== null) {
1000
1019
  message.message = { $case: "pong", pong: object.message.pong };
1001
1020
  }
1021
+ if (
1022
+ object.message?.$case === "reconnect" &&
1023
+ object.message?.reconnect !== undefined &&
1024
+ object.message?.reconnect !== null
1025
+ ) {
1026
+ message.message = { $case: "reconnect", reconnect: ReconnectResponse.fromPartial(object.message.reconnect) };
1027
+ }
1002
1028
  return message;
1003
1029
  },
1004
1030
  };
@@ -1085,6 +1111,7 @@ function createBaseAddTrackRequest(): AddTrackRequest {
1085
1111
  sid: "",
1086
1112
  stereo: false,
1087
1113
  disableRed: false,
1114
+ encryption: 0,
1088
1115
  };
1089
1116
  }
1090
1117
 
@@ -1129,6 +1156,9 @@ export const AddTrackRequest = {
1129
1156
  if (message.disableRed === true) {
1130
1157
  writer.uint32(104).bool(message.disableRed);
1131
1158
  }
1159
+ if (message.encryption !== 0) {
1160
+ writer.uint32(112).int32(message.encryption);
1161
+ }
1132
1162
  return writer;
1133
1163
  },
1134
1164
 
@@ -1178,6 +1208,9 @@ export const AddTrackRequest = {
1178
1208
  case 13:
1179
1209
  message.disableRed = reader.bool();
1180
1210
  break;
1211
+ case 14:
1212
+ message.encryption = reader.int32() as any;
1213
+ break;
1181
1214
  default:
1182
1215
  reader.skipType(tag & 7);
1183
1216
  break;
@@ -1203,6 +1236,7 @@ export const AddTrackRequest = {
1203
1236
  sid: isSet(object.sid) ? String(object.sid) : "",
1204
1237
  stereo: isSet(object.stereo) ? Boolean(object.stereo) : false,
1205
1238
  disableRed: isSet(object.disableRed) ? Boolean(object.disableRed) : false,
1239
+ encryption: isSet(object.encryption) ? encryption_TypeFromJSON(object.encryption) : 0,
1206
1240
  };
1207
1241
  },
1208
1242
 
@@ -1229,6 +1263,7 @@ export const AddTrackRequest = {
1229
1263
  message.sid !== undefined && (obj.sid = message.sid);
1230
1264
  message.stereo !== undefined && (obj.stereo = message.stereo);
1231
1265
  message.disableRed !== undefined && (obj.disableRed = message.disableRed);
1266
+ message.encryption !== undefined && (obj.encryption = encryption_TypeToJSON(message.encryption));
1232
1267
  return obj;
1233
1268
  },
1234
1269
 
@@ -1247,6 +1282,7 @@ export const AddTrackRequest = {
1247
1282
  message.sid = object.sid ?? "";
1248
1283
  message.stereo = object.stereo ?? false;
1249
1284
  message.disableRed = object.disableRed ?? false;
1285
+ message.encryption = object.encryption ?? 0;
1250
1286
  return message;
1251
1287
  },
1252
1288
  };
@@ -1550,6 +1586,74 @@ export const JoinResponse = {
1550
1586
  },
1551
1587
  };
1552
1588
 
1589
+ function createBaseReconnectResponse(): ReconnectResponse {
1590
+ return { iceServers: [], clientConfiguration: undefined };
1591
+ }
1592
+
1593
+ export const ReconnectResponse = {
1594
+ encode(message: ReconnectResponse, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer {
1595
+ for (const v of message.iceServers) {
1596
+ ICEServer.encode(v!, writer.uint32(10).fork()).ldelim();
1597
+ }
1598
+ if (message.clientConfiguration !== undefined) {
1599
+ ClientConfiguration.encode(message.clientConfiguration, writer.uint32(18).fork()).ldelim();
1600
+ }
1601
+ return writer;
1602
+ },
1603
+
1604
+ decode(input: _m0.Reader | Uint8Array, length?: number): ReconnectResponse {
1605
+ const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input);
1606
+ let end = length === undefined ? reader.len : reader.pos + length;
1607
+ const message = createBaseReconnectResponse();
1608
+ while (reader.pos < end) {
1609
+ const tag = reader.uint32();
1610
+ switch (tag >>> 3) {
1611
+ case 1:
1612
+ message.iceServers.push(ICEServer.decode(reader, reader.uint32()));
1613
+ break;
1614
+ case 2:
1615
+ message.clientConfiguration = ClientConfiguration.decode(reader, reader.uint32());
1616
+ break;
1617
+ default:
1618
+ reader.skipType(tag & 7);
1619
+ break;
1620
+ }
1621
+ }
1622
+ return message;
1623
+ },
1624
+
1625
+ fromJSON(object: any): ReconnectResponse {
1626
+ return {
1627
+ iceServers: Array.isArray(object?.iceServers) ? object.iceServers.map((e: any) => ICEServer.fromJSON(e)) : [],
1628
+ clientConfiguration: isSet(object.clientConfiguration)
1629
+ ? ClientConfiguration.fromJSON(object.clientConfiguration)
1630
+ : undefined,
1631
+ };
1632
+ },
1633
+
1634
+ toJSON(message: ReconnectResponse): unknown {
1635
+ const obj: any = {};
1636
+ if (message.iceServers) {
1637
+ obj.iceServers = message.iceServers.map((e) => e ? ICEServer.toJSON(e) : undefined);
1638
+ } else {
1639
+ obj.iceServers = [];
1640
+ }
1641
+ message.clientConfiguration !== undefined && (obj.clientConfiguration = message.clientConfiguration
1642
+ ? ClientConfiguration.toJSON(message.clientConfiguration)
1643
+ : undefined);
1644
+ return obj;
1645
+ },
1646
+
1647
+ fromPartial<I extends Exact<DeepPartial<ReconnectResponse>, I>>(object: I): ReconnectResponse {
1648
+ const message = createBaseReconnectResponse();
1649
+ message.iceServers = object.iceServers?.map((e) => ICEServer.fromPartial(e)) || [];
1650
+ message.clientConfiguration = (object.clientConfiguration !== undefined && object.clientConfiguration !== null)
1651
+ ? ClientConfiguration.fromPartial(object.clientConfiguration)
1652
+ : undefined;
1653
+ return message;
1654
+ },
1655
+ };
1656
+
1553
1657
  function createBaseTrackPublishedResponse(): TrackPublishedResponse {
1554
1658
  return { cid: "", track: undefined };
1555
1659
  }
@@ -30,6 +30,8 @@ export default class PCTransport extends EventEmitter {
30
30
 
31
31
  remoteStereoMids: string[] = [];
32
32
 
33
+ remoteNackMids: string[] = [];
34
+
33
35
  onOffer?: (offer: RTCSessionDescriptionInit) => void;
34
36
 
35
37
  constructor(config?: RTCConfiguration) {
@@ -50,7 +52,9 @@ export default class PCTransport extends EventEmitter {
50
52
 
51
53
  async setRemoteDescription(sd: RTCSessionDescriptionInit): Promise<void> {
52
54
  if (sd.type === 'offer') {
53
- this.remoteStereoMids = extractStereoTracksFromOffer(sd);
55
+ let { stereoMids, nackMids } = extractStereoAndNackAudioFromOffer(sd);
56
+ this.remoteStereoMids = stereoMids;
57
+ this.remoteNackMids = nackMids;
54
58
  }
55
59
  await this.pc.setRemoteDescription(sd);
56
60
 
@@ -116,7 +120,7 @@ export default class PCTransport extends EventEmitter {
116
120
  const sdpParsed = parse(offer.sdp ?? '');
117
121
  sdpParsed.media.forEach((media) => {
118
122
  if (media.type === 'audio') {
119
- ensureAudioNackAndStereo(media, []);
123
+ ensureAudioNackAndStereo(media, [], []);
120
124
  } else if (media.type === 'video') {
121
125
  // mung sdp for codec bitrate setting that can't apply by sendEncoding
122
126
  this.trackBitrates.some((trackbr): boolean => {
@@ -169,7 +173,7 @@ export default class PCTransport extends EventEmitter {
169
173
  const sdpParsed = parse(answer.sdp ?? '');
170
174
  sdpParsed.media.forEach((media) => {
171
175
  if (media.type === 'audio') {
172
- ensureAudioNackAndStereo(media, this.remoteStereoMids);
176
+ ensureAudioNackAndStereo(media, this.remoteStereoMids, this.remoteNackMids);
173
177
  }
174
178
  });
175
179
  await this.setMungedLocalDescription(answer, write(sdpParsed));
@@ -226,6 +230,7 @@ function ensureAudioNackAndStereo(
226
230
  payloads?: string | undefined;
227
231
  } & MediaDescription,
228
232
  stereoMids: string[],
233
+ nackMids: string[],
229
234
  ) {
230
235
  // found opus codec to add nack fb
231
236
  let opusPayload = 0;
@@ -243,7 +248,10 @@ function ensureAudioNackAndStereo(
243
248
  media.rtcpFb = [];
244
249
  }
245
250
 
246
- if (!media.rtcpFb.some((fb) => fb.payload === opusPayload && fb.type === 'nack')) {
251
+ if (
252
+ nackMids.includes(media.mid!) &&
253
+ !media.rtcpFb.some((fb) => fb.payload === opusPayload && fb.type === 'nack')
254
+ ) {
247
255
  media.rtcpFb.push({
248
256
  payload: opusPayload,
249
257
  type: 'nack',
@@ -264,8 +272,12 @@ function ensureAudioNackAndStereo(
264
272
  }
265
273
  }
266
274
 
267
- function extractStereoTracksFromOffer(offer: RTCSessionDescriptionInit): string[] {
275
+ function extractStereoAndNackAudioFromOffer(offer: RTCSessionDescriptionInit): {
276
+ stereoMids: string[];
277
+ nackMids: string[];
278
+ } {
268
279
  const stereoMids: string[] = [];
280
+ const nackMids: string[] = [];
269
281
  const sdpParsed = parse(offer.sdp ?? '');
270
282
  let opusPayload = 0;
271
283
  sdpParsed.media.forEach((media) => {
@@ -278,6 +290,10 @@ function extractStereoTracksFromOffer(offer: RTCSessionDescriptionInit): string[
278
290
  return false;
279
291
  });
280
292
 
293
+ if (media.rtcpFb?.some((fb) => fb.payload === opusPayload && fb.type === 'nack')) {
294
+ nackMids.push(media.mid!);
295
+ }
296
+
281
297
  media.fmtp.some((fmtp): boolean => {
282
298
  if (fmtp.payload === opusPayload) {
283
299
  if (fmtp.config.includes('sprop-stereo=1')) {
@@ -289,5 +305,5 @@ function extractStereoTracksFromOffer(offer: RTCSessionDescriptionInit): string[
289
305
  });
290
306
  }
291
307
  });
292
- return stereoMids;
308
+ return { stereoMids, nackMids };
293
309
  }