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
@@ -4484,6 +4484,82 @@ function clientConfigSettingToJSON(object) {
4484
4484
  return 'UNKNOWN';
4485
4485
  }
4486
4486
  }
4487
+ var DisconnectReason;
4488
+
4489
+ (function (DisconnectReason) {
4490
+ DisconnectReason[DisconnectReason["UNKNOWN_REASON"] = 0] = "UNKNOWN_REASON";
4491
+ DisconnectReason[DisconnectReason["CLIENT_INITIATED"] = 1] = "CLIENT_INITIATED";
4492
+ DisconnectReason[DisconnectReason["DUPLICATE_IDENTITY"] = 2] = "DUPLICATE_IDENTITY";
4493
+ DisconnectReason[DisconnectReason["SERVER_SHUTDOWN"] = 3] = "SERVER_SHUTDOWN";
4494
+ DisconnectReason[DisconnectReason["PARTICIPANT_REMOVED"] = 4] = "PARTICIPANT_REMOVED";
4495
+ DisconnectReason[DisconnectReason["ROOM_DELETED"] = 5] = "ROOM_DELETED";
4496
+ DisconnectReason[DisconnectReason["STATE_MISMATCH"] = 6] = "STATE_MISMATCH";
4497
+ DisconnectReason[DisconnectReason["UNRECOGNIZED"] = -1] = "UNRECOGNIZED";
4498
+ })(DisconnectReason || (DisconnectReason = {}));
4499
+
4500
+ function disconnectReasonFromJSON(object) {
4501
+ switch (object) {
4502
+ case 0:
4503
+ case 'UNKNOWN_REASON':
4504
+ return DisconnectReason.UNKNOWN_REASON;
4505
+
4506
+ case 1:
4507
+ case 'CLIENT_INITIATED':
4508
+ return DisconnectReason.CLIENT_INITIATED;
4509
+
4510
+ case 2:
4511
+ case 'DUPLICATE_IDENTITY':
4512
+ return DisconnectReason.DUPLICATE_IDENTITY;
4513
+
4514
+ case 3:
4515
+ case 'SERVER_SHUTDOWN':
4516
+ return DisconnectReason.SERVER_SHUTDOWN;
4517
+
4518
+ case 4:
4519
+ case 'PARTICIPANT_REMOVED':
4520
+ return DisconnectReason.PARTICIPANT_REMOVED;
4521
+
4522
+ case 5:
4523
+ case 'ROOM_DELETED':
4524
+ return DisconnectReason.ROOM_DELETED;
4525
+
4526
+ case 6:
4527
+ case 'STATE_MISMATCH':
4528
+ return DisconnectReason.STATE_MISMATCH;
4529
+
4530
+ case -1:
4531
+ case 'UNRECOGNIZED':
4532
+ default:
4533
+ return DisconnectReason.UNRECOGNIZED;
4534
+ }
4535
+ }
4536
+ function disconnectReasonToJSON(object) {
4537
+ switch (object) {
4538
+ case DisconnectReason.UNKNOWN_REASON:
4539
+ return 'UNKNOWN_REASON';
4540
+
4541
+ case DisconnectReason.CLIENT_INITIATED:
4542
+ return 'CLIENT_INITIATED';
4543
+
4544
+ case DisconnectReason.DUPLICATE_IDENTITY:
4545
+ return 'DUPLICATE_IDENTITY';
4546
+
4547
+ case DisconnectReason.SERVER_SHUTDOWN:
4548
+ return 'SERVER_SHUTDOWN';
4549
+
4550
+ case DisconnectReason.PARTICIPANT_REMOVED:
4551
+ return 'PARTICIPANT_REMOVED';
4552
+
4553
+ case DisconnectReason.ROOM_DELETED:
4554
+ return 'ROOM_DELETED';
4555
+
4556
+ case DisconnectReason.STATE_MISMATCH:
4557
+ return 'STATE_MISMATCH';
4558
+
4559
+ default:
4560
+ return 'UNKNOWN';
4561
+ }
4562
+ }
4487
4563
  var ParticipantInfo_State;
4488
4564
 
4489
4565
  (function (ParticipantInfo_State) {
@@ -5204,7 +5280,8 @@ function createBaseSimulcastCodecInfo() {
5204
5280
  return {
5205
5281
  mimeType: '',
5206
5282
  mid: '',
5207
- cid: ''
5283
+ cid: '',
5284
+ layers: []
5208
5285
  };
5209
5286
  }
5210
5287
 
@@ -5224,6 +5301,10 @@ const SimulcastCodecInfo = {
5224
5301
  writer.uint32(26).string(message.cid);
5225
5302
  }
5226
5303
 
5304
+ for (const v of message.layers) {
5305
+ VideoLayer.encode(v, writer.uint32(34).fork()).ldelim();
5306
+ }
5307
+
5227
5308
  return writer;
5228
5309
  },
5229
5310
 
@@ -5248,6 +5329,10 @@ const SimulcastCodecInfo = {
5248
5329
  message.cid = reader.string();
5249
5330
  break;
5250
5331
 
5332
+ case 4:
5333
+ message.layers.push(VideoLayer.decode(reader, reader.uint32()));
5334
+ break;
5335
+
5251
5336
  default:
5252
5337
  reader.skipType(tag & 7);
5253
5338
  break;
@@ -5261,7 +5346,8 @@ const SimulcastCodecInfo = {
5261
5346
  return {
5262
5347
  mimeType: isSet$1(object.mimeType) ? String(object.mimeType) : '',
5263
5348
  mid: isSet$1(object.mid) ? String(object.mid) : '',
5264
- cid: isSet$1(object.cid) ? String(object.cid) : ''
5349
+ cid: isSet$1(object.cid) ? String(object.cid) : '',
5350
+ layers: Array.isArray(object === null || object === void 0 ? void 0 : object.layers) ? object.layers.map(e => VideoLayer.fromJSON(e)) : []
5265
5351
  };
5266
5352
  },
5267
5353
 
@@ -5270,16 +5356,24 @@ const SimulcastCodecInfo = {
5270
5356
  message.mimeType !== undefined && (obj.mimeType = message.mimeType);
5271
5357
  message.mid !== undefined && (obj.mid = message.mid);
5272
5358
  message.cid !== undefined && (obj.cid = message.cid);
5359
+
5360
+ if (message.layers) {
5361
+ obj.layers = message.layers.map(e => e ? VideoLayer.toJSON(e) : undefined);
5362
+ } else {
5363
+ obj.layers = [];
5364
+ }
5365
+
5273
5366
  return obj;
5274
5367
  },
5275
5368
 
5276
5369
  fromPartial(object) {
5277
- var _a, _b, _c;
5370
+ var _a, _b, _c, _d;
5278
5371
 
5279
5372
  const message = createBaseSimulcastCodecInfo();
5280
5373
  message.mimeType = (_a = object.mimeType) !== null && _a !== void 0 ? _a : '';
5281
5374
  message.mid = (_b = object.mid) !== null && _b !== void 0 ? _b : '';
5282
5375
  message.cid = (_c = object.cid) !== null && _c !== void 0 ? _c : '';
5376
+ message.layers = ((_d = object.layers) === null || _d === void 0 ? void 0 : _d.map(e => VideoLayer.fromPartial(e))) || [];
5283
5377
  return message;
5284
5378
  }
5285
5379
 
@@ -6179,7 +6273,8 @@ function createBaseClientConfiguration() {
6179
6273
  return {
6180
6274
  video: undefined,
6181
6275
  screen: undefined,
6182
- resumeConnection: 0
6276
+ resumeConnection: 0,
6277
+ disabledCodecs: undefined
6183
6278
  };
6184
6279
  }
6185
6280
 
@@ -6199,6 +6294,10 @@ const ClientConfiguration = {
6199
6294
  writer.uint32(24).int32(message.resumeConnection);
6200
6295
  }
6201
6296
 
6297
+ if (message.disabledCodecs !== undefined) {
6298
+ DisabledCodecs.encode(message.disabledCodecs, writer.uint32(34).fork()).ldelim();
6299
+ }
6300
+
6202
6301
  return writer;
6203
6302
  },
6204
6303
 
@@ -6223,6 +6322,10 @@ const ClientConfiguration = {
6223
6322
  message.resumeConnection = reader.int32();
6224
6323
  break;
6225
6324
 
6325
+ case 4:
6326
+ message.disabledCodecs = DisabledCodecs.decode(reader, reader.uint32());
6327
+ break;
6328
+
6226
6329
  default:
6227
6330
  reader.skipType(tag & 7);
6228
6331
  break;
@@ -6236,7 +6339,8 @@ const ClientConfiguration = {
6236
6339
  return {
6237
6340
  video: isSet$1(object.video) ? VideoConfiguration.fromJSON(object.video) : undefined,
6238
6341
  screen: isSet$1(object.screen) ? VideoConfiguration.fromJSON(object.screen) : undefined,
6239
- resumeConnection: isSet$1(object.resumeConnection) ? clientConfigSettingFromJSON(object.resumeConnection) : 0
6342
+ resumeConnection: isSet$1(object.resumeConnection) ? clientConfigSettingFromJSON(object.resumeConnection) : 0,
6343
+ disabledCodecs: isSet$1(object.disabledCodecs) ? DisabledCodecs.fromJSON(object.disabledCodecs) : undefined
6240
6344
  };
6241
6345
  },
6242
6346
 
@@ -6245,6 +6349,7 @@ const ClientConfiguration = {
6245
6349
  message.video !== undefined && (obj.video = message.video ? VideoConfiguration.toJSON(message.video) : undefined);
6246
6350
  message.screen !== undefined && (obj.screen = message.screen ? VideoConfiguration.toJSON(message.screen) : undefined);
6247
6351
  message.resumeConnection !== undefined && (obj.resumeConnection = clientConfigSettingToJSON(message.resumeConnection));
6352
+ message.disabledCodecs !== undefined && (obj.disabledCodecs = message.disabledCodecs ? DisabledCodecs.toJSON(message.disabledCodecs) : undefined);
6248
6353
  return obj;
6249
6354
  },
6250
6355
 
@@ -6255,6 +6360,7 @@ const ClientConfiguration = {
6255
6360
  message.video = object.video !== undefined && object.video !== null ? VideoConfiguration.fromPartial(object.video) : undefined;
6256
6361
  message.screen = object.screen !== undefined && object.screen !== null ? VideoConfiguration.fromPartial(object.screen) : undefined;
6257
6362
  message.resumeConnection = (_a = object.resumeConnection) !== null && _a !== void 0 ? _a : 0;
6363
+ message.disabledCodecs = object.disabledCodecs !== undefined && object.disabledCodecs !== null ? DisabledCodecs.fromPartial(object.disabledCodecs) : undefined;
6258
6364
  return message;
6259
6365
  }
6260
6366
 
@@ -6321,6 +6427,73 @@ const VideoConfiguration = {
6321
6427
 
6322
6428
  };
6323
6429
 
6430
+ function createBaseDisabledCodecs() {
6431
+ return {
6432
+ codecs: []
6433
+ };
6434
+ }
6435
+
6436
+ const DisabledCodecs = {
6437
+ encode(message) {
6438
+ let writer = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : minimal.Writer.create();
6439
+
6440
+ for (const v of message.codecs) {
6441
+ Codec.encode(v, writer.uint32(10).fork()).ldelim();
6442
+ }
6443
+
6444
+ return writer;
6445
+ },
6446
+
6447
+ decode(input, length) {
6448
+ const reader = input instanceof minimal.Reader ? input : new minimal.Reader(input);
6449
+ let end = length === undefined ? reader.len : reader.pos + length;
6450
+ const message = createBaseDisabledCodecs();
6451
+
6452
+ while (reader.pos < end) {
6453
+ const tag = reader.uint32();
6454
+
6455
+ switch (tag >>> 3) {
6456
+ case 1:
6457
+ message.codecs.push(Codec.decode(reader, reader.uint32()));
6458
+ break;
6459
+
6460
+ default:
6461
+ reader.skipType(tag & 7);
6462
+ break;
6463
+ }
6464
+ }
6465
+
6466
+ return message;
6467
+ },
6468
+
6469
+ fromJSON(object) {
6470
+ return {
6471
+ codecs: Array.isArray(object === null || object === void 0 ? void 0 : object.codecs) ? object.codecs.map(e => Codec.fromJSON(e)) : []
6472
+ };
6473
+ },
6474
+
6475
+ toJSON(message) {
6476
+ const obj = {};
6477
+
6478
+ if (message.codecs) {
6479
+ obj.codecs = message.codecs.map(e => e ? Codec.toJSON(e) : undefined);
6480
+ } else {
6481
+ obj.codecs = [];
6482
+ }
6483
+
6484
+ return obj;
6485
+ },
6486
+
6487
+ fromPartial(object) {
6488
+ var _a;
6489
+
6490
+ const message = createBaseDisabledCodecs();
6491
+ message.codecs = ((_a = object.codecs) === null || _a === void 0 ? void 0 : _a.map(e => Codec.fromPartial(e))) || [];
6492
+ return message;
6493
+ }
6494
+
6495
+ };
6496
+
6324
6497
  var globalThis$1 = (() => {
6325
6498
  if (typeof globalThis$1 !== 'undefined') return globalThis$1;
6326
6499
  if (typeof self !== 'undefined') return self;
@@ -6346,9 +6519,11 @@ const btoa = globalThis$1.btoa || (bin => globalThis$1.Buffer.from(bin, 'binary'
6346
6519
 
6347
6520
  function base64FromBytes(arr) {
6348
6521
  const bin = [];
6349
- arr.forEach(byte => {
6522
+
6523
+ for (const byte of arr) {
6350
6524
  bin.push(String.fromCharCode(byte));
6351
- });
6525
+ }
6526
+
6352
6527
  return btoa(bin.join(''));
6353
6528
  }
6354
6529
 
@@ -8033,7 +8208,8 @@ const UpdateTrackSettings = {
8033
8208
 
8034
8209
  function createBaseLeaveRequest() {
8035
8210
  return {
8036
- canReconnect: false
8211
+ canReconnect: false,
8212
+ reason: 0
8037
8213
  };
8038
8214
  }
8039
8215
 
@@ -8045,6 +8221,10 @@ const LeaveRequest = {
8045
8221
  writer.uint32(8).bool(message.canReconnect);
8046
8222
  }
8047
8223
 
8224
+ if (message.reason !== 0) {
8225
+ writer.uint32(16).int32(message.reason);
8226
+ }
8227
+
8048
8228
  return writer;
8049
8229
  },
8050
8230
 
@@ -8061,6 +8241,10 @@ const LeaveRequest = {
8061
8241
  message.canReconnect = reader.bool();
8062
8242
  break;
8063
8243
 
8244
+ case 2:
8245
+ message.reason = reader.int32();
8246
+ break;
8247
+
8064
8248
  default:
8065
8249
  reader.skipType(tag & 7);
8066
8250
  break;
@@ -8072,21 +8256,24 @@ const LeaveRequest = {
8072
8256
 
8073
8257
  fromJSON(object) {
8074
8258
  return {
8075
- canReconnect: isSet(object.canReconnect) ? Boolean(object.canReconnect) : false
8259
+ canReconnect: isSet(object.canReconnect) ? Boolean(object.canReconnect) : false,
8260
+ reason: isSet(object.reason) ? disconnectReasonFromJSON(object.reason) : 0
8076
8261
  };
8077
8262
  },
8078
8263
 
8079
8264
  toJSON(message) {
8080
8265
  const obj = {};
8081
8266
  message.canReconnect !== undefined && (obj.canReconnect = message.canReconnect);
8267
+ message.reason !== undefined && (obj.reason = disconnectReasonToJSON(message.reason));
8082
8268
  return obj;
8083
8269
  },
8084
8270
 
8085
8271
  fromPartial(object) {
8086
- var _a;
8272
+ var _a, _b;
8087
8273
 
8088
8274
  const message = createBaseLeaveRequest();
8089
8275
  message.canReconnect = (_a = object.canReconnect) !== null && _a !== void 0 ? _a : false;
8276
+ message.reason = (_b = object.reason) !== null && _b !== void 0 ? _b : 0;
8090
8277
  return message;
8091
8278
  }
8092
8279
 
@@ -10002,6 +10189,8 @@ var TrackEvent;
10002
10189
  TrackEvent["Muted"] = "muted";
10003
10190
  TrackEvent["Unmuted"] = "unmuted";
10004
10191
  TrackEvent["Ended"] = "ended";
10192
+ TrackEvent["Subscribed"] = "subscribed";
10193
+ TrackEvent["Unsubscribed"] = "unsubscribed";
10005
10194
  /** @internal */
10006
10195
 
10007
10196
  TrackEvent["UpdateSettings"] = "updateSettings";
@@ -10044,6 +10233,12 @@ var TrackEvent;
10044
10233
  */
10045
10234
 
10046
10235
  TrackEvent["UpstreamResumed"] = "upstreamResumed";
10236
+ /**
10237
+ * @internal
10238
+ * Fires on RemoteTrackPublication
10239
+ */
10240
+
10241
+ TrackEvent["SubscriptionPermissionChanged"] = "subscriptionPermissionChanged";
10047
10242
  })(TrackEvent || (TrackEvent = {}));
10048
10243
 
10049
10244
  const monitorFrequency = 2000;
@@ -10070,7 +10265,7 @@ function computeBitrate(currentStats, prevStats) {
10070
10265
  return (bytesNow - bytesPrev) * 8 * 1000 / (currentStats.timestamp - prevStats.timestamp);
10071
10266
  }
10072
10267
 
10073
- var version$1 = "1.1.7";
10268
+ var version$1 = "1.2.0";
10074
10269
 
10075
10270
  const version = version$1;
10076
10271
  const protocolVersion = 8;
@@ -10190,6 +10385,178 @@ class Future {
10190
10385
 
10191
10386
  }
10192
10387
 
10388
+ class Queue {
10389
+ /**
10390
+ * @class Queue
10391
+ *
10392
+ * Priority queue with rate limiting<br>
10393
+ * See the medium article:<br>
10394
+ * https://mmomtchev.medium.com/parallelizing-download-loops-in-js-with-async-await-queue-670420880cd6
10395
+ *
10396
+ * @param {number} [_maxConcurrent=1] Number of tasks allowed to run simultaneously
10397
+ * @param {number} [_minCycle=0] Minimum number of milliseconds between two consecutive tasks
10398
+ */
10399
+ constructor(_maxConcurrent, _minCycle) {
10400
+ this.maxConcurrent = _maxConcurrent || 1;
10401
+ this.minCycle = _minCycle || 0;
10402
+ this.queueRunning = [];
10403
+ this.queueWaiting = {};
10404
+ this.lastRun = 0;
10405
+ }
10406
+ /** @private */
10407
+
10408
+
10409
+ dequeue(hash) {
10410
+ const q = this.queueRunning;
10411
+ const idx = q.findIndex(x => x.hash === hash);
10412
+ if (idx == -1) throw 'queue desync';
10413
+ const o = q[idx];
10414
+ q.splice(idx, 1);
10415
+ return o;
10416
+ }
10417
+ /** @private */
10418
+
10419
+
10420
+ getFirstWaiting() {
10421
+ for (let p of Object.keys(this.queueWaiting).sort((a, b) => a - b)) if (this.queueWaiting[p] !== undefined && this.queueWaiting[p].length > 0) return this.queueWaiting[p];
10422
+
10423
+ return undefined;
10424
+ }
10425
+ /**
10426
+ * Signal that the task `hash` has finished.<br>
10427
+ * Frees its slot in the queue
10428
+ *
10429
+ * @method end
10430
+ * @param {any} hash Unique hash identifying the task, Symbol() works very well
10431
+ */
10432
+
10433
+
10434
+ end(hash) {
10435
+ const me = this.dequeue(hash);
10436
+ me.resolve();
10437
+ /* Choose the next task to run and unblock its promise */
10438
+
10439
+ const q = this.getFirstWaiting();
10440
+
10441
+ if (q !== undefined) {
10442
+ const next = q.shift();
10443
+ next.resolve();
10444
+ }
10445
+ }
10446
+ /**
10447
+ * Wait for a slot in the queue
10448
+ *
10449
+ * @method wait
10450
+ * @param {any} hash Unique hash identifying the task
10451
+ * @param {number} [priority=0] Optional priority, -1 is higher priority than 1
10452
+ * @return {Promise<void>} Resolved when the task is ready to run
10453
+ */
10454
+
10455
+
10456
+ async wait(hash, _priority) {
10457
+ const priority = _priority === undefined ? 0 : _priority;
10458
+ /* Us on the queue */
10459
+
10460
+ let me = {
10461
+ hash,
10462
+ priority
10463
+ };
10464
+ /* Create priorities on the fly */
10465
+
10466
+ if (this.queueWaiting[priority] == undefined) this.queueWaiting[priority] = [];
10467
+ /* Are we allowed to run? */
10468
+
10469
+ if (this.queueRunning.length >= this.maxConcurrent) {
10470
+ /* This promise will be unlocked from the outside */
10471
+
10472
+ /* and it cannot reject */
10473
+ me.promise = new Promise(resolve => {
10474
+ me.resolve = resolve;
10475
+ });
10476
+ /* Get in the line */
10477
+
10478
+ this.queueWaiting[priority].push(me);
10479
+ await me.promise;
10480
+ }
10481
+
10482
+ this.queueRunning.push(me);
10483
+ me.promise = new Promise(resolve => {
10484
+ me.resolve = resolve;
10485
+ });
10486
+ /* Wait if it is too soon */
10487
+
10488
+ while (Date.now() - this.lastRun < this.minCycle) {
10489
+ await new Promise(resolve => setTimeout(resolve, this.minCycle - Date.now() + this.lastRun));
10490
+ }
10491
+
10492
+ this.lastRun = Date.now();
10493
+ }
10494
+ /**
10495
+ * Run a job (equivalent to calling Queue.wait(), fn() and then Queue.end())<br>
10496
+ * fn can be both synchronous or asynchronous function
10497
+ *
10498
+ * @method run
10499
+ * @param {Function} fn The job
10500
+ * @param {number} [priority=0] Optional priority, -1 is higher priority than 1
10501
+ * @return {Promise<any>} Resolved when the task has finished with the return value of fn
10502
+ */
10503
+
10504
+
10505
+ run(job, _priority) {
10506
+ const priority = _priority === undefined ? 0 : _priority;
10507
+ const id = Symbol();
10508
+ return this.wait(id, priority).then(() => job()).finally(() => {
10509
+ this.end(id);
10510
+ });
10511
+ }
10512
+ /**
10513
+ * @interface QueueStats {running: number, waiting: number, last: number}
10514
+ */
10515
+
10516
+ /**
10517
+ * Return the number of running and waiting jobs
10518
+ *
10519
+ * @method stat
10520
+ * @return {QueueStats} running, waiting, last
10521
+ */
10522
+
10523
+
10524
+ stat() {
10525
+ return {
10526
+ running: this.queueRunning.length,
10527
+ waiting: Object.keys(this.queueWaiting).reduce((t, x) => t += this.queueWaiting[x].length, 0),
10528
+ last: this.lastRun
10529
+ };
10530
+ }
10531
+ /**
10532
+ * Returns a promise that resolves when the queue is empty
10533
+ *
10534
+ * @method flush
10535
+ * @return {Promise<void>}
10536
+ */
10537
+
10538
+
10539
+ async flush() {
10540
+ /* Aways wait on the lowest priority in the queue */
10541
+ while (this.stat().waiting > 0) {
10542
+ for (let p of Object.keys(this.queueWaiting).sort((a, b) => b - a)) {
10543
+ const qp = this.queueWaiting[p];
10544
+
10545
+ if (qp !== undefined && qp.length > 0) {
10546
+ await qp[qp.length - 1].promise;
10547
+ }
10548
+ }
10549
+ }
10550
+ /* And then finish on the running queue */
10551
+
10552
+
10553
+ while (this.queueRunning.length > 0) {
10554
+ await Promise.allSettled(this.queueRunning.map(x => x.promise));
10555
+ }
10556
+ }
10557
+
10558
+ }
10559
+
10193
10560
  const defaultId = 'default';
10194
10561
  class DeviceManager {
10195
10562
  static getInstance() {
@@ -10742,6 +11109,7 @@ class Track extends events.exports.EventEmitter {
10742
11109
 
10743
11110
  this.kind = kind;
10744
11111
  this._mediaStreamTrack = mediaTrack;
11112
+ this._mediaStreamID = mediaTrack.id;
10745
11113
  this.source = Track.Source.Unknown;
10746
11114
 
10747
11115
  if (isWeb()) {
@@ -10761,6 +11129,16 @@ class Track extends events.exports.EventEmitter {
10761
11129
  get mediaStreamTrack() {
10762
11130
  return this._mediaStreamTrack;
10763
11131
  }
11132
+ /**
11133
+ * @internal
11134
+ * used for keep mediaStream's first id, since it's id might change
11135
+ * if we disable/enable a track
11136
+ */
11137
+
11138
+
11139
+ get mediaStreamID() {
11140
+ return this._mediaStreamID;
11141
+ }
10764
11142
 
10765
11143
  attach(element) {
10766
11144
  let elementType = 'audio';
@@ -11087,6 +11465,7 @@ class LocalTrack extends Track {
11087
11465
  this.reacquireTrack = false;
11088
11466
  this.wasMuted = false;
11089
11467
  this.providedByUser = userProvidedTrack;
11468
+ this.muteQueue = new Queue();
11090
11469
  }
11091
11470
 
11092
11471
  get id() {
@@ -11167,7 +11546,9 @@ class LocalTrack extends Track {
11167
11546
  // 'A MediaStreamTrack ended due to a capture failure`
11168
11547
 
11169
11548
 
11170
- this._mediaStreamTrack.stop();
11549
+ if (!this.providedByUser) {
11550
+ this._mediaStreamTrack.stop();
11551
+ }
11171
11552
 
11172
11553
  track.addEventListener('ended', this.handleEnded);
11173
11554
  livekitLogger.debug('replace MediaStreamTrack');
@@ -11177,6 +11558,7 @@ class LocalTrack extends Track {
11177
11558
  }
11178
11559
 
11179
11560
  this._mediaStreamTrack = track;
11561
+ await this.resumeUpstream();
11180
11562
  this.attachedElements.forEach(el => {
11181
11563
  attachToElement(track, el);
11182
11564
  });
@@ -11226,6 +11608,7 @@ class LocalTrack extends Track {
11226
11608
  }
11227
11609
 
11228
11610
  this._mediaStreamTrack = newTrack;
11611
+ await this.resumeUpstream();
11229
11612
  this.attachedElements.forEach(el => {
11230
11613
  attachToElement(newTrack, el);
11231
11614
  });
@@ -11235,6 +11618,8 @@ class LocalTrack extends Track {
11235
11618
  }
11236
11619
 
11237
11620
  setTrackMuted(muted) {
11621
+ livekitLogger.debug("setting ".concat(this.kind, " track ").concat(muted ? 'muted' : 'unmuted'));
11622
+
11238
11623
  if (this.isMuted === muted) {
11239
11624
  return;
11240
11625
  }
@@ -11268,34 +11653,38 @@ class LocalTrack extends Track {
11268
11653
  }
11269
11654
 
11270
11655
  async pauseUpstream() {
11271
- if (this._isUpstreamPaused === true) {
11272
- return;
11273
- }
11656
+ this.muteQueue.run(async () => {
11657
+ if (this._isUpstreamPaused === true) {
11658
+ return;
11659
+ }
11274
11660
 
11275
- if (!this.sender) {
11276
- livekitLogger.warn('unable to pause upstream for an unpublished track');
11277
- return;
11278
- }
11661
+ if (!this.sender) {
11662
+ livekitLogger.warn('unable to pause upstream for an unpublished track');
11663
+ return;
11664
+ }
11279
11665
 
11280
- this._isUpstreamPaused = true;
11281
- this.emit(TrackEvent.UpstreamPaused, this);
11282
- const emptyTrack = this.kind === Track.Kind.Audio ? getEmptyAudioStreamTrack() : getEmptyVideoStreamTrack();
11283
- await this.sender.replaceTrack(emptyTrack);
11666
+ this._isUpstreamPaused = true;
11667
+ this.emit(TrackEvent.UpstreamPaused, this);
11668
+ const emptyTrack = this.kind === Track.Kind.Audio ? getEmptyAudioStreamTrack() : getEmptyVideoStreamTrack();
11669
+ await this.sender.replaceTrack(emptyTrack);
11670
+ });
11284
11671
  }
11285
11672
 
11286
11673
  async resumeUpstream() {
11287
- if (this._isUpstreamPaused === false) {
11288
- return;
11289
- }
11674
+ this.muteQueue.run(async () => {
11675
+ if (this._isUpstreamPaused === false) {
11676
+ return;
11677
+ }
11290
11678
 
11291
- if (!this.sender) {
11292
- livekitLogger.warn('unable to resume upstream for an unpublished track');
11293
- return;
11294
- }
11679
+ if (!this.sender) {
11680
+ livekitLogger.warn('unable to resume upstream for an unpublished track');
11681
+ return;
11682
+ }
11295
11683
 
11296
- this._isUpstreamPaused = false;
11297
- this.emit(TrackEvent.UpstreamResumed, this);
11298
- await this.sender.replaceTrack(this._mediaStreamTrack);
11684
+ this._isUpstreamPaused = false;
11685
+ this.emit(TrackEvent.UpstreamResumed, this);
11686
+ await this.sender.replaceTrack(this._mediaStreamTrack);
11687
+ });
11299
11688
  }
11300
11689
 
11301
11690
  }
@@ -11453,24 +11842,28 @@ class LocalAudioTrack extends LocalTrack {
11453
11842
  }
11454
11843
 
11455
11844
  async mute() {
11456
- // disabled special handling as it will cause BT headsets to switch communication modes
11457
- if (this.source === Track.Source.Microphone && this.stopOnMute) {
11458
- livekitLogger.debug('stopping mic track'); // also stop the track, so that microphone indicator is turned off
11845
+ await this.muteQueue.run(async () => {
11846
+ // disabled special handling as it will cause BT headsets to switch communication modes
11847
+ if (this.source === Track.Source.Microphone && this.stopOnMute && !this.isUserProvided) {
11848
+ livekitLogger.debug('stopping mic track'); // also stop the track, so that microphone indicator is turned off
11459
11849
 
11460
- this._mediaStreamTrack.stop();
11461
- }
11850
+ this._mediaStreamTrack.stop();
11851
+ }
11462
11852
 
11463
- await super.mute();
11853
+ await super.mute();
11854
+ });
11464
11855
  return this;
11465
11856
  }
11466
11857
 
11467
11858
  async unmute() {
11468
- if (this.source === Track.Source.Microphone && this.stopOnMute && !this.isUserProvided) {
11469
- livekitLogger.debug('reacquiring mic track');
11470
- await this.restartTrack();
11471
- }
11859
+ await this.muteQueue.run(async () => {
11860
+ if (this.source === Track.Source.Microphone && this.stopOnMute && !this.isUserProvided) {
11861
+ livekitLogger.debug('reacquiring mic track');
11862
+ await this.restartTrack();
11863
+ }
11472
11864
 
11473
- await super.unmute();
11865
+ await super.unmute();
11866
+ });
11474
11867
  return this;
11475
11868
  }
11476
11869
 
@@ -11636,23 +12029,27 @@ class LocalVideoTrack extends LocalTrack {
11636
12029
  }
11637
12030
 
11638
12031
  async mute() {
11639
- if (this.source === Track.Source.Camera) {
11640
- livekitLogger.debug('stopping camera track'); // also stop the track, so that camera indicator is turned off
12032
+ await this.muteQueue.run(async () => {
12033
+ if (this.source === Track.Source.Camera && !this.isUserProvided) {
12034
+ livekitLogger.debug('stopping camera track'); // also stop the track, so that camera indicator is turned off
11641
12035
 
11642
- this._mediaStreamTrack.stop();
11643
- }
12036
+ this._mediaStreamTrack.stop();
12037
+ }
11644
12038
 
11645
- await super.mute();
12039
+ await super.mute();
12040
+ });
11646
12041
  return this;
11647
12042
  }
11648
12043
 
11649
12044
  async unmute() {
11650
- if (this.source === Track.Source.Camera && !this.isUserProvided) {
11651
- livekitLogger.debug('reacquiring camera track');
11652
- await this.restartTrack();
11653
- }
12045
+ await this.muteQueue.run(async () => {
12046
+ if (this.source === Track.Source.Camera && !this.isUserProvided) {
12047
+ livekitLogger.debug('reacquiring camera track');
12048
+ await this.restartTrack();
12049
+ }
11654
12050
 
11655
- await super.unmute();
12051
+ await super.unmute();
12052
+ });
11656
12053
  return this;
11657
12054
  }
11658
12055
 
@@ -11969,6 +12366,7 @@ class RemoteTrack extends Track {
11969
12366
  setMuted(muted) {
11970
12367
  if (this.isMuted !== muted) {
11971
12368
  this.isMuted = muted;
12369
+ this._mediaStreamTrack.enabled = !muted;
11972
12370
  this.emit(muted ? TrackEvent.Muted : TrackEvent.Unmuted, this);
11973
12371
  }
11974
12372
  }
@@ -12553,6 +12951,9 @@ class TrackPublication extends events.exports.EventEmitter {
12553
12951
  }
12554
12952
 
12555
12953
  this.trackInfo = info;
12954
+ livekitLogger.trace('update publication info', {
12955
+ info
12956
+ });
12556
12957
  }
12557
12958
 
12558
12959
  }
@@ -12875,6 +13276,9 @@ class Participant extends events.exports.EventEmitter {
12875
13276
 
12876
13277
 
12877
13278
  this.participantInfo = info;
13279
+ livekitLogger.trace('update participant info', {
13280
+ info
13281
+ });
12878
13282
  }
12879
13283
  /** @internal */
12880
13284
 
@@ -13218,7 +13622,7 @@ class RemoteTrackPublication extends TrackPublication {
13218
13622
  super(...arguments);
13219
13623
  /** @internal */
13220
13624
 
13221
- this._allowed = true;
13625
+ this.allowed = true;
13222
13626
  this.disabled = false;
13223
13627
  this.currentVideoQuality = VideoQuality.HIGH;
13224
13628
 
@@ -13250,7 +13654,10 @@ class RemoteTrackPublication extends TrackPublication {
13250
13654
 
13251
13655
 
13252
13656
  setSubscribed(subscribed) {
13253
- this.subscribed = subscribed;
13657
+ this.subscribed = subscribed; // reset allowed status when desired subscription state changes
13658
+ // server will notify client via signal message if it's not allowed
13659
+
13660
+ this.allowed = true;
13254
13661
  const sub = {
13255
13662
  trackSids: [this.trackSid],
13256
13663
  subscribe: this.subscribed,
@@ -13266,11 +13673,11 @@ class RemoteTrackPublication extends TrackPublication {
13266
13673
 
13267
13674
  get subscriptionStatus() {
13268
13675
  if (this.subscribed === false || !super.isSubscribed) {
13269
- return TrackPublication.SubscriptionStatus.Unsubscribed;
13270
- }
13676
+ if (!this.allowed) {
13677
+ return TrackPublication.SubscriptionStatus.NotAllowed;
13678
+ }
13271
13679
 
13272
- if (!this._allowed) {
13273
- return TrackPublication.SubscriptionStatus.NotAllowed;
13680
+ return TrackPublication.SubscriptionStatus.Unsubscribed;
13274
13681
  }
13275
13682
 
13276
13683
  return TrackPublication.SubscriptionStatus.Subscribed;
@@ -13285,10 +13692,6 @@ class RemoteTrackPublication extends TrackPublication {
13285
13692
  return false;
13286
13693
  }
13287
13694
 
13288
- if (!this._allowed) {
13289
- return false;
13290
- }
13291
-
13292
13695
  return super.isSubscribed;
13293
13696
  }
13294
13697
 
@@ -13356,11 +13759,14 @@ class RemoteTrackPublication extends TrackPublication {
13356
13759
 
13357
13760
 
13358
13761
  setTrack(track) {
13359
- if (this.track) {
13762
+ const prevStatus = this.subscriptionStatus;
13763
+ const prevTrack = this.track;
13764
+
13765
+ if (prevTrack) {
13360
13766
  // unregister listener
13361
- this.track.off(TrackEvent.VideoDimensionsChanged, this.handleVideoDimensionsChange);
13362
- this.track.off(TrackEvent.VisibilityChanged, this.handleVisibilityChange);
13363
- this.track.off(TrackEvent.Ended, this.handleEnded);
13767
+ prevTrack.off(TrackEvent.VideoDimensionsChanged, this.handleVideoDimensionsChange);
13768
+ prevTrack.off(TrackEvent.VisibilityChanged, this.handleVisibilityChange);
13769
+ prevTrack.off(TrackEvent.Ended, this.handleEnded);
13364
13770
  }
13365
13771
 
13366
13772
  super.setTrack(track);
@@ -13371,6 +13777,25 @@ class RemoteTrackPublication extends TrackPublication {
13371
13777
  track.on(TrackEvent.VisibilityChanged, this.handleVisibilityChange);
13372
13778
  track.on(TrackEvent.Ended, this.handleEnded);
13373
13779
  }
13780
+
13781
+ this.emitSubscriptionUpdateIfChanged(prevStatus);
13782
+
13783
+ if (!!track !== !!prevTrack) {
13784
+ // when undefined status changes, there's a subscription changed event
13785
+ if (track) {
13786
+ this.emit(TrackEvent.Subscribed, track);
13787
+ } else {
13788
+ this.emit(TrackEvent.Unsubscribed, prevTrack);
13789
+ }
13790
+ }
13791
+ }
13792
+ /** @internal */
13793
+
13794
+
13795
+ setAllowed(allowed) {
13796
+ const prevStatus = this.subscriptionStatus;
13797
+ this.allowed = allowed;
13798
+ this.emitSubscriptionUpdateIfChanged(prevStatus);
13374
13799
  }
13375
13800
  /** @internal */
13376
13801
 
@@ -13383,6 +13808,16 @@ class RemoteTrackPublication extends TrackPublication {
13383
13808
  (_a = this.track) === null || _a === void 0 ? void 0 : _a.setMuted(info.muted);
13384
13809
  }
13385
13810
 
13811
+ emitSubscriptionUpdateIfChanged(previousStatus) {
13812
+ const currentStatus = this.subscriptionStatus;
13813
+
13814
+ if (previousStatus === currentStatus) {
13815
+ return;
13816
+ }
13817
+
13818
+ this.emit(TrackEvent.SubscriptionPermissionChanged, currentStatus, previousStatus);
13819
+ }
13820
+
13386
13821
  isManualOperationAllowed() {
13387
13822
  if (this.isAdaptiveStream) {
13388
13823
  livekitLogger.warn('adaptive stream is enabled, cannot change track settings', {
@@ -13457,8 +13892,14 @@ class RemoteParticipant extends Participant {
13457
13892
  });
13458
13893
  this.signalClient.sendUpdateSubscription(sub);
13459
13894
  });
13460
- publication.on(TrackEvent.Ended, track => {
13461
- this.emit(ParticipantEvent.TrackUnsubscribed, track, publication);
13895
+ publication.on(TrackEvent.SubscriptionPermissionChanged, status => {
13896
+ this.emit(ParticipantEvent.TrackSubscriptionPermissionChanged, publication, status);
13897
+ });
13898
+ publication.on(TrackEvent.Subscribed, track => {
13899
+ this.emit(ParticipantEvent.TrackSubscribed, track, publication);
13900
+ });
13901
+ publication.on(TrackEvent.Unsubscribed, previousTrack => {
13902
+ this.emit(ParticipantEvent.TrackUnsubscribed, previousTrack, publication);
13462
13903
  });
13463
13904
  }
13464
13905
 
@@ -13559,15 +14000,12 @@ class RemoteParticipant extends Participant {
13559
14000
  track.isMuted = publication.isMuted;
13560
14001
  track.setMediaStream(mediaStream);
13561
14002
  track.start();
13562
- publication.setTrack(track); // subscription means participant has permissions to subscribe
13563
-
13564
- publication._allowed = true; // set participant volume on new microphone tracks
14003
+ publication.setTrack(track); // set participant volume on new microphone tracks
13565
14004
 
13566
14005
  if (this.volume !== undefined && track instanceof RemoteAudioTrack && track.source === Track.Source.Microphone) {
13567
14006
  track.setVolume(this.volume);
13568
14007
  }
13569
14008
 
13570
- this.emit(ParticipantEvent.TrackSubscribed, track, publication);
13571
14009
  return publication;
13572
14010
  }
13573
14011
  /** @internal */
@@ -13619,6 +14057,10 @@ class RemoteParticipant extends Participant {
13619
14057
 
13620
14058
  this.tracks.forEach(publication => {
13621
14059
  if (!validTracks.has(publication.trackSid)) {
14060
+ livekitLogger.trace('detected removed track on remote participant, unpublishing', {
14061
+ publication,
14062
+ participantSid: this.sid
14063
+ });
13622
14064
  this.unpublishTrack(publication.trackSid, true);
13623
14065
  }
13624
14066
  });
@@ -13651,15 +14093,8 @@ class RemoteParticipant extends Participant {
13651
14093
  } = publication;
13652
14094
 
13653
14095
  if (track) {
13654
- const {
13655
- isSubscribed
13656
- } = publication;
13657
14096
  track.stop();
13658
- publication.setTrack(undefined); // always send unsubscribed, since apps may rely on this
13659
-
13660
- if (isSubscribed) {
13661
- this.emit(ParticipantEvent.TrackUnsubscribed, track, publication);
13662
- }
14097
+ publication.setTrack(undefined);
13663
14098
  }
13664
14099
 
13665
14100
  if (sendUnpublish) {
@@ -13780,11 +14215,47 @@ class LocalParticipant extends Participant {
13780
14215
  this.unpublishTrack(track.track);
13781
14216
  };
13782
14217
 
13783
- this.handleTrackEnded = track => {
13784
- livekitLogger.debug('unpublishing local track due to TrackEnded', {
13785
- track: track.sid
13786
- });
13787
- this.unpublishTrack(track);
14218
+ this.handleTrackEnded = async track => {
14219
+ if (track.source === Track.Source.ScreenShare || track.source === Track.Source.ScreenShareAudio) {
14220
+ livekitLogger.debug('unpublishing local track due to TrackEnded', {
14221
+ track: track.sid
14222
+ });
14223
+ this.unpublishTrack(track);
14224
+ } else if (track.isUserProvided) {
14225
+ await track.pauseUpstream();
14226
+ } else if (track instanceof LocalAudioTrack || track instanceof LocalVideoTrack) {
14227
+ try {
14228
+ if (isWeb()) {
14229
+ try {
14230
+ const currentPermissions = await (navigator === null || navigator === void 0 ? void 0 : navigator.permissions.query({
14231
+ // the permission query for camera and microphone currently not supported in Safari and Firefox
14232
+ // @ts-ignore
14233
+ name: track.source === Track.Source.Camera ? 'camera' : 'microphone'
14234
+ }));
14235
+
14236
+ if (currentPermissions && currentPermissions.state === 'denied') {
14237
+ livekitLogger.warn("user has revoked access to ".concat(track.source)); // detect granted change after permissions were denied to try and resume then
14238
+
14239
+ currentPermissions.onchange = () => {
14240
+ if (currentPermissions.state !== 'denied') {
14241
+ track.restartTrack();
14242
+ currentPermissions.onchange = null;
14243
+ }
14244
+ };
14245
+
14246
+ throw new Error('GetUserMedia Permission denied');
14247
+ }
14248
+ } catch (e) {// permissions query fails for firefox, we continue and try to restart the track
14249
+ }
14250
+ }
14251
+
14252
+ livekitLogger.debug('track ended, attempting to use a different device');
14253
+ await track.restartTrack();
14254
+ } catch (e) {
14255
+ livekitLogger.warn("could not restart track, pausing upstream instead");
14256
+ await track.pauseUpstream();
14257
+ }
14258
+ }
13788
14259
  };
13789
14260
 
13790
14261
  this.audioTracks = new Map();
@@ -14603,7 +15074,7 @@ class LocalParticipant extends Participant {
14603
15074
  this.tracks.forEach(track => {
14604
15075
  if (track.track !== undefined) {
14605
15076
  infos.push({
14606
- cid: track.track.mediaStreamTrack.id,
15077
+ cid: track.track.mediaStreamID,
14607
15078
  track: track.trackInfo
14608
15079
  });
14609
15080
  }
@@ -18120,57 +18591,6 @@ adapterFactory({
18120
18591
  window: typeof window === 'undefined' ? undefined : window
18121
18592
  });
18122
18593
 
18123
- class Queue {
18124
- constructor() {
18125
- this.queue = [];
18126
- this.running = false;
18127
- }
18128
-
18129
- enqueue(cb) {
18130
- livekitLogger.trace('enqueuing request to fire later');
18131
- this.queue.push(cb);
18132
- }
18133
-
18134
- dequeue() {
18135
- const evt = this.queue.shift();
18136
- if (evt) evt();
18137
- livekitLogger.trace('firing request from queue');
18138
- }
18139
-
18140
- async run() {
18141
- if (this.running) return;
18142
- livekitLogger.trace('start queue');
18143
- this.running = true;
18144
-
18145
- while (this.running && this.queue.length > 0) {
18146
- this.dequeue();
18147
- }
18148
-
18149
- this.running = false;
18150
- livekitLogger.trace('queue finished');
18151
- }
18152
-
18153
- pause() {
18154
- livekitLogger.trace('pausing queue');
18155
- this.running = false;
18156
- }
18157
-
18158
- reset() {
18159
- livekitLogger.trace('resetting queue');
18160
- this.running = false;
18161
- this.queue = [];
18162
- }
18163
-
18164
- isRunning() {
18165
- return this.running;
18166
- }
18167
-
18168
- isEmpty() {
18169
- return this.queue.length === 0;
18170
- }
18171
-
18172
- }
18173
-
18174
18594
  const passThroughQueueSignals = ['syncState', 'trickle', 'offer', 'answer', 'simulate', 'leave'];
18175
18595
 
18176
18596
  function canPassThroughQueue(req) {
@@ -18191,6 +18611,7 @@ class SignalClient {
18191
18611
  this.isReconnecting = false;
18192
18612
  this.useJSON = useJSON;
18193
18613
  this.requestQueue = new Queue();
18614
+ this.queuedRequests = [];
18194
18615
  }
18195
18616
 
18196
18617
  async join(url, token, opts, abortSignal) {
@@ -18418,14 +18839,20 @@ class SignalClient {
18418
18839
 
18419
18840
  async sendRequest(req) {
18420
18841
  let fromQueue = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
18421
- // capture all requests while reconnecting and put them in a queue.
18422
- // keep order by queueing up new events as long as the queue is not empty
18842
+ // capture all requests while reconnecting and put them in a queue
18423
18843
  // unless the request originates from the queue, then don't enqueue again
18424
18844
  const canQueue = !fromQueue && !canPassThroughQueue(req);
18425
18845
 
18426
- if (canQueue && (this.isReconnecting || !this.requestQueue.isEmpty())) {
18427
- this.requestQueue.enqueue(() => this.sendRequest(req, true));
18846
+ if (canQueue && this.isReconnecting) {
18847
+ this.queuedRequests.push(async () => {
18848
+ await this.sendRequest(req, true);
18849
+ });
18428
18850
  return;
18851
+ } // make sure previously queued requests are being sent first
18852
+
18853
+
18854
+ if (!fromQueue) {
18855
+ await this.requestQueue.flush();
18429
18856
  }
18430
18857
 
18431
18858
  if (this.signalLatency) {
@@ -18523,8 +18950,15 @@ class SignalClient {
18523
18950
  }
18524
18951
 
18525
18952
  setReconnected() {
18953
+ while (this.queuedRequests.length > 0) {
18954
+ const req = this.queuedRequests.shift();
18955
+
18956
+ if (req) {
18957
+ this.requestQueue.run(req);
18958
+ }
18959
+ }
18960
+
18526
18961
  this.isReconnecting = false;
18527
- this.requestQueue.run();
18528
18962
  }
18529
18963
 
18530
18964
  handleWSError(ev) {
@@ -19061,23 +19495,9 @@ class RTCEngine extends events.exports.EventEmitter {
19061
19495
  const track = ev.stream.getTracks()[0];
19062
19496
  this.emit(EngineEvent.MediaTrackAdded, track, ev.stream);
19063
19497
  };
19064
- } // data channels
19065
-
19066
-
19067
- this.lossyDC = this.publisher.pc.createDataChannel(lossyDataChannel, {
19068
- // will drop older packets that arrive
19069
- ordered: true,
19070
- maxRetransmits: 0
19071
- });
19072
- this.reliableDC = this.publisher.pc.createDataChannel(reliableDataChannel, {
19073
- ordered: true
19074
- }); // also handle messages over the pub channel, for backwards compatibility
19075
-
19076
- this.lossyDC.onmessage = this.handleDataMessage;
19077
- this.reliableDC.onmessage = this.handleDataMessage; // handle datachannel errors
19498
+ }
19078
19499
 
19079
- this.lossyDC.onerror = this.handleDataError;
19080
- this.reliableDC.onerror = this.handleDataError; // configure signaling client
19500
+ this.createDataChannels(); // configure signaling client
19081
19501
 
19082
19502
  this.client.onAnswer = async sd => {
19083
19503
  if (!this.publisher) {
@@ -19152,12 +19572,49 @@ class RTCEngine extends events.exports.EventEmitter {
19152
19572
  this.fullReconnectOnNext = true;
19153
19573
  this.primaryPC = undefined;
19154
19574
  } else {
19155
- this.emit(EngineEvent.Disconnected);
19575
+ this.emit(EngineEvent.Disconnected, leave === null || leave === void 0 ? void 0 : leave.reason);
19156
19576
  this.close();
19157
19577
  }
19578
+
19579
+ livekitLogger.trace('leave request', {
19580
+ leave
19581
+ });
19158
19582
  };
19159
19583
  }
19160
19584
 
19585
+ createDataChannels() {
19586
+ if (!this.publisher) {
19587
+ return;
19588
+ } // clear old data channel callbacks if recreate
19589
+
19590
+
19591
+ if (this.lossyDC) {
19592
+ this.lossyDC.onmessage = null;
19593
+ this.lossyDC.onerror = null;
19594
+ }
19595
+
19596
+ if (this.reliableDC) {
19597
+ this.reliableDC.onmessage = null;
19598
+ this.reliableDC.onerror = null;
19599
+ } // create data channels
19600
+
19601
+
19602
+ this.lossyDC = this.publisher.pc.createDataChannel(lossyDataChannel, {
19603
+ // will drop older packets that arrive
19604
+ ordered: true,
19605
+ maxRetransmits: 0
19606
+ });
19607
+ this.reliableDC = this.publisher.pc.createDataChannel(reliableDataChannel, {
19608
+ ordered: true
19609
+ }); // also handle messages over the pub channel, for backwards compatibility
19610
+
19611
+ this.lossyDC.onmessage = this.handleDataMessage;
19612
+ this.reliableDC.onmessage = this.handleDataMessage; // handle datachannel errors
19613
+
19614
+ this.lossyDC.onerror = this.handleDataError;
19615
+ this.reliableDC.onerror = this.handleDataError;
19616
+ }
19617
+
19161
19618
  async restartConnection() {
19162
19619
  var _a, _b;
19163
19620
 
@@ -19197,6 +19654,8 @@ class RTCEngine extends events.exports.EventEmitter {
19197
19654
  }
19198
19655
 
19199
19656
  async resumeConnection() {
19657
+ var _a;
19658
+
19200
19659
  if (!this.url || !this.token) {
19201
19660
  // permanent failure, don't attempt reconnection
19202
19661
  throw new UnexpectedConnectionState('could not reconnect, url or token not saved');
@@ -19229,7 +19688,13 @@ class RTCEngine extends events.exports.EventEmitter {
19229
19688
  }
19230
19689
 
19231
19690
  await this.waitForPCConnected();
19232
- this.client.setReconnected(); // resume success
19691
+ this.client.setReconnected(); // recreate publish datachannel if it's id is null
19692
+ // (for safari https://bugs.webkit.org/show_bug.cgi?id=184688)
19693
+
19694
+ if (((_a = this.reliableDC) === null || _a === void 0 ? void 0 : _a.readyState) === 'open' && this.reliableDC.id === null) {
19695
+ this.createDataChannels();
19696
+ } // resume success
19697
+
19233
19698
 
19234
19699
  this.emit(EngineEvent.Resumed);
19235
19700
  }
@@ -19537,7 +20002,14 @@ class Room extends events.exports.EventEmitter {
19537
20002
  }); // populate remote participants, these should not trigger new events
19538
20003
 
19539
20004
  joinResponse.otherParticipants.forEach(info => {
19540
- this.getOrCreateParticipant(info.sid, info);
20005
+ if (info.sid !== this.localParticipant.sid && info.identity !== this.localParticipant.identity) {
20006
+ this.getOrCreateParticipant(info.sid, info);
20007
+ } else {
20008
+ livekitLogger.warn('received info to create local participant as remote participant', {
20009
+ info,
20010
+ localParticipant: this.localParticipant
20011
+ });
20012
+ }
19541
20013
  });
19542
20014
  this.name = joinResponse.room.name;
19543
20015
  this.sid = joinResponse.room.sid;
@@ -19592,11 +20064,15 @@ class Room extends events.exports.EventEmitter {
19592
20064
  */
19593
20065
 
19594
20066
 
19595
- this.disconnect = function () {
20067
+ this.disconnect = async function () {
19596
20068
  let stopTracks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
19597
20069
 
19598
20070
  var _a, _b;
19599
20071
 
20072
+ livekitLogger.info('disconnect from room', {
20073
+ identity: _this.localParticipant.identity
20074
+ });
20075
+
19600
20076
  if (_this.state === ConnectionState.Connecting) {
19601
20077
  // try aborting pending connection attempt
19602
20078
  livekitLogger.warn('abort connection attempt');
@@ -19606,7 +20082,7 @@ class Room extends events.exports.EventEmitter {
19606
20082
 
19607
20083
 
19608
20084
  if ((_b = _this.engine) === null || _b === void 0 ? void 0 : _b.client.isConnected) {
19609
- _this.engine.client.sendLeave();
20085
+ await _this.engine.client.sendLeave();
19610
20086
  } // close engine (also closes client)
19611
20087
 
19612
20088
 
@@ -19614,7 +20090,7 @@ class Room extends events.exports.EventEmitter {
19614
20090
  _this.engine.close();
19615
20091
  }
19616
20092
 
19617
- _this.handleDisconnect(stopTracks);
20093
+ _this.handleDisconnect(stopTracks, DisconnectReason.CLIENT_INITIATED);
19618
20094
  /* @ts-ignore */
19619
20095
 
19620
20096
 
@@ -19811,9 +20287,7 @@ class Room extends events.exports.EventEmitter {
19811
20287
  return;
19812
20288
  }
19813
20289
 
19814
- pub._allowed = update.allowed;
19815
- participant.emit(ParticipantEvent.TrackSubscriptionPermissionChanged, pub, pub.subscriptionStatus);
19816
- this.emitWhenConnected(RoomEvent.TrackSubscriptionPermissionChanged, pub, pub.subscriptionStatus, participant);
20290
+ pub.setAllowed(update.allowed);
19817
20291
  };
19818
20292
 
19819
20293
  this.handleDataPacket = (userPacket, kind) => {
@@ -19893,8 +20367,8 @@ class Room extends events.exports.EventEmitter {
19893
20367
  this.engine.client.onConnectionQuality = this.handleConnectionQualityUpdate;
19894
20368
  this.engine.on(EngineEvent.MediaTrackAdded, (mediaTrack, stream, receiver) => {
19895
20369
  this.onTrackAdded(mediaTrack, stream, receiver);
19896
- }).on(EngineEvent.Disconnected, () => {
19897
- this.handleDisconnect();
20370
+ }).on(EngineEvent.Disconnected, reason => {
20371
+ this.handleDisconnect(this.options.stopLocalTrackOnUnpublish, reason);
19898
20372
  }).on(EngineEvent.ActiveSpeakersUpdate, this.handleActiveSpeakersUpdate).on(EngineEvent.DataPacketReceived, this.handleDataPacket).on(EngineEvent.Resuming, () => {
19899
20373
  if (this.setAndEmitConnectionState(ConnectionState.Reconnecting)) {
19900
20374
  this.emit(RoomEvent.Reconnecting);
@@ -20114,21 +20588,28 @@ class Room extends events.exports.EventEmitter {
20114
20588
  // at that time, ICE connectivity has not been established so the track is not
20115
20589
  // technically subscribed.
20116
20590
  // We'll defer these events until when the room is connected or eventually disconnected.
20117
- if (this.state === ConnectionState.Connecting || this.state === ConnectionState.Reconnecting) {
20118
- setTimeout(() => {
20591
+ if (this.connectFuture) {
20592
+ this.connectFuture.promise.then(() => {
20119
20593
  this.onTrackAdded(mediaTrack, stream, receiver);
20120
- }, 50);
20594
+ });
20121
20595
  return;
20122
20596
  }
20123
20597
 
20124
20598
  if (this.state === ConnectionState.Disconnected) {
20125
20599
  livekitLogger.warn('skipping incoming track after Room disconnected');
20600
+ return;
20126
20601
  }
20127
20602
 
20128
20603
  const parts = unpackStreamId(stream.id);
20129
20604
  const participantId = parts[0];
20130
20605
  let trackId = parts[1];
20131
20606
  if (!trackId || trackId === '') trackId = mediaTrack.id;
20607
+
20608
+ if (participantId === this.localParticipant.sid) {
20609
+ livekitLogger.warn('tried to create RemoteParticipant for local participant');
20610
+ return;
20611
+ }
20612
+
20132
20613
  const participant = this.getOrCreateParticipant(participantId);
20133
20614
  let adaptiveStreamSettings;
20134
20615
 
@@ -20145,9 +20626,14 @@ class Room extends events.exports.EventEmitter {
20145
20626
 
20146
20627
  handleDisconnect() {
20147
20628
  let shouldStopTracks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
20629
+ let reason = arguments.length > 1 ? arguments[1] : undefined;
20148
20630
 
20149
20631
  var _a;
20150
20632
 
20633
+ if (this.state === ConnectionState.Disconnected) {
20634
+ return;
20635
+ }
20636
+
20151
20637
  this.participants.forEach(p => {
20152
20638
  p.tracks.forEach(pub => {
20153
20639
  p.unpublishTrack(pub.trackSid);
@@ -20165,6 +20651,9 @@ class Room extends events.exports.EventEmitter {
20165
20651
  (_b = pub.track) === null || _b === void 0 ? void 0 : _b.stop();
20166
20652
  }
20167
20653
  });
20654
+ this.localParticipant.tracks.clear();
20655
+ this.localParticipant.videoTracks.clear();
20656
+ this.localParticipant.audioTracks.clear();
20168
20657
  this.participants.clear();
20169
20658
  this.activeSpeakers = [];
20170
20659
 
@@ -20179,7 +20668,7 @@ class Room extends events.exports.EventEmitter {
20179
20668
  }
20180
20669
 
20181
20670
  this.setAndEmitConnectionState(ConnectionState.Disconnected);
20182
- this.emit(RoomEvent.Disconnected);
20671
+ this.emit(RoomEvent.Disconnected, reason);
20183
20672
  }
20184
20673
 
20185
20674
  handleParticipantDisconnected(sid, participant) {
@@ -20266,6 +20755,8 @@ class Room extends events.exports.EventEmitter {
20266
20755
  this.emitWhenConnected(RoomEvent.ConnectionQualityChanged, quality, participant);
20267
20756
  }).on(ParticipantEvent.ParticipantPermissionsChanged, prevPermissions => {
20268
20757
  this.emitWhenConnected(RoomEvent.ParticipantPermissionsChanged, prevPermissions, participant);
20758
+ }).on(ParticipantEvent.TrackSubscriptionPermissionChanged, (pub, status) => {
20759
+ this.emitWhenConnected(RoomEvent.TrackSubscriptionPermissionChanged, pub, status, participant);
20269
20760
  }); // update info at the end after callbacks have been set up
20270
20761
 
20271
20762
  if (info) {
@@ -20505,5 +20996,5 @@ async function createLocalScreenTracks(options) {
20505
20996
  return localTracks;
20506
20997
  }
20507
20998
 
20508
- export { AudioPresets, ConnectionError, ConnectionQuality, ConnectionState, DataPacket_Kind, EngineEvent, LivekitError, LocalAudioTrack, LocalParticipant, LocalTrack, LocalTrackPublication, LocalVideoTrack, LogLevel, MediaDeviceFailure, Participant, ParticipantEvent, PublishDataError, RemoteAudioTrack, RemoteParticipant, RemoteTrack, RemoteTrackPublication, RemoteVideoTrack, Room, RoomEvent, RoomState, ScreenSharePresets, Track, TrackEvent, TrackInvalidError, TrackPublication, UnexpectedConnectionState, UnsupportedServer, VideoPreset, VideoPresets, VideoPresets43, VideoQuality, attachToElement, createLocalAudioTrack, createLocalScreenTracks, createLocalTracks, createLocalVideoTrack, detachTrack, protocolVersion, setLogExtension, setLogLevel, version };
20999
+ export { AudioPresets, ConnectionError, ConnectionQuality, ConnectionState, DataPacket_Kind, DisconnectReason, EngineEvent, LivekitError, LocalAudioTrack, LocalParticipant, LocalTrack, LocalTrackPublication, LocalVideoTrack, LogLevel, MediaDeviceFailure, Participant, ParticipantEvent, PublishDataError, RemoteAudioTrack, RemoteParticipant, RemoteTrack, RemoteTrackPublication, RemoteVideoTrack, Room, RoomEvent, RoomState, ScreenSharePresets, Track, TrackEvent, TrackInvalidError, TrackPublication, UnexpectedConnectionState, UnsupportedServer, VideoPreset, VideoPresets, VideoPresets43, VideoQuality, attachToElement, createLocalAudioTrack, createLocalScreenTracks, createLocalTracks, createLocalVideoTrack, detachTrack, getEmptyAudioStreamTrack, getEmptyVideoStreamTrack, protocolVersion, setLogExtension, setLogLevel, version };
20509
21000
  //# sourceMappingURL=livekit-client.esm.mjs.map