livekit-client 2.9.9 → 2.11.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 (48) hide show
  1. package/dist/livekit-client.e2ee.worker.js.map +1 -1
  2. package/dist/livekit-client.e2ee.worker.mjs +3 -3
  3. package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
  4. package/dist/livekit-client.esm.mjs +341 -135
  5. package/dist/livekit-client.esm.mjs.map +1 -1
  6. package/dist/livekit-client.umd.js +1 -1
  7. package/dist/livekit-client.umd.js.map +1 -1
  8. package/dist/src/api/SignalClient.d.ts.map +1 -1
  9. package/dist/src/api/utils.d.ts +3 -0
  10. package/dist/src/api/utils.d.ts.map +1 -0
  11. package/dist/src/e2ee/KeyProvider.d.ts.map +1 -1
  12. package/dist/src/room/RTCEngine.d.ts +1 -0
  13. package/dist/src/room/RTCEngine.d.ts.map +1 -1
  14. package/dist/src/room/Room.d.ts.map +1 -1
  15. package/dist/src/room/StreamWriter.d.ts +1 -1
  16. package/dist/src/room/StreamWriter.d.ts.map +1 -1
  17. package/dist/src/room/events.d.ts +2 -1
  18. package/dist/src/room/events.d.ts.map +1 -1
  19. package/dist/src/room/participant/LocalParticipant.d.ts +10 -1
  20. package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
  21. package/dist/src/room/participant/publishUtils.d.ts.map +1 -1
  22. package/dist/src/room/track/LocalVideoTrack.d.ts.map +1 -1
  23. package/dist/src/room/track/RemoteTrackPublication.d.ts.map +1 -1
  24. package/dist/src/room/track/create.d.ts.map +1 -1
  25. package/dist/src/room/track/options.d.ts +3 -2
  26. package/dist/src/room/track/options.d.ts.map +1 -1
  27. package/dist/ts4.2/src/api/utils.d.ts +3 -0
  28. package/dist/ts4.2/src/room/RTCEngine.d.ts +1 -0
  29. package/dist/ts4.2/src/room/StreamWriter.d.ts +1 -1
  30. package/dist/ts4.2/src/room/events.d.ts +2 -1
  31. package/dist/ts4.2/src/room/participant/LocalParticipant.d.ts +10 -1
  32. package/dist/ts4.2/src/room/track/options.d.ts +3 -2
  33. package/package.json +13 -13
  34. package/src/api/SignalClient.ts +8 -17
  35. package/src/api/utils.test.ts +112 -0
  36. package/src/api/utils.ts +23 -0
  37. package/src/room/DeviceManager.ts +1 -1
  38. package/src/room/RTCEngine.ts +5 -0
  39. package/src/room/Room.ts +1 -1
  40. package/src/room/StreamWriter.ts +1 -1
  41. package/src/room/defaults.ts +2 -2
  42. package/src/room/events.ts +1 -0
  43. package/src/room/participant/LocalParticipant.ts +148 -49
  44. package/src/room/track/LocalTrack.ts +2 -2
  45. package/src/room/track/LocalVideoTrack.ts +15 -14
  46. package/src/room/track/create.ts +87 -47
  47. package/src/room/track/options.ts +5 -1
  48. package/src/room/track/utils.ts +6 -6
@@ -15,9 +15,9 @@ function _mergeNamespaces(n, m) {
15
15
 
16
16
  var e = Object.defineProperty;
17
17
  var h = (i, s, t) => s in i ? e(i, s, {
18
- enumerable: !0,
19
- configurable: !0,
20
- writable: !0,
18
+ enumerable: true,
19
+ configurable: true,
20
+ writable: true,
21
21
  value: t
22
22
  }) : i[s] = t;
23
23
  var o = (i, s, t) => h(i, typeof s != "symbol" ? s + "" : s, t);
@@ -64,10 +64,10 @@ function assert(condition, msg) {
64
64
  }
65
65
  }
66
66
  const FLOAT32_MAX = 3.4028234663852886e38,
67
- FLOAT32_MIN = -3.4028234663852886e38,
67
+ FLOAT32_MIN = -34028234663852886e22,
68
68
  UINT32_MAX = 0xffffffff,
69
69
  INT32_MAX = 0x7fffffff,
70
- INT32_MIN = -0x80000000;
70
+ INT32_MIN = -2147483648;
71
71
  /**
72
72
  * Assert a valid signed protobuf 32-bit integer.
73
73
  */
@@ -3772,10 +3772,13 @@ const EventMetric = /* @__PURE__ */proto3.makeMessageType("livekit.EventMetric",
3772
3772
  }]);
3773
3773
  const BackupCodecPolicy$1 = /* @__PURE__ */proto3.makeEnum("livekit.BackupCodecPolicy", [{
3774
3774
  no: 0,
3775
- name: "REGRESSION"
3775
+ name: "PREFER_REGRESSION"
3776
3776
  }, {
3777
3777
  no: 1,
3778
3778
  name: "SIMULCAST"
3779
+ }, {
3780
+ no: 2,
3781
+ name: "REGRESSION"
3779
3782
  }]);
3780
3783
  const TrackType = /* @__PURE__ */proto3.makeEnum("livekit.TrackType", [{
3781
3784
  no: 0,
@@ -4169,6 +4172,12 @@ const ParticipantInfo = /* @__PURE__ */proto3.makeMessageType("livekit.Participa
4169
4172
  name: "disconnect_reason",
4170
4173
  kind: "enum",
4171
4174
  T: proto3.getEnumType(DisconnectReason)
4175
+ }, {
4176
+ no: 18,
4177
+ name: "kind_details",
4178
+ kind: "enum",
4179
+ T: proto3.getEnumType(ParticipantInfo_KindDetail),
4180
+ repeated: true
4172
4181
  }]);
4173
4182
  const ParticipantInfo_State = /* @__PURE__ */proto3.makeEnum("livekit.ParticipantInfo.State", [{
4174
4183
  no: 0,
@@ -4199,6 +4208,13 @@ const ParticipantInfo_Kind = /* @__PURE__ */proto3.makeEnum("livekit.Participant
4199
4208
  no: 4,
4200
4209
  name: "AGENT"
4201
4210
  }]);
4211
+ const ParticipantInfo_KindDetail = /* @__PURE__ */proto3.makeEnum("livekit.ParticipantInfo.KindDetail", [{
4212
+ no: 0,
4213
+ name: "CLOUD_AGENT"
4214
+ }, {
4215
+ no: 1,
4216
+ name: "FORWARDED"
4217
+ }]);
4202
4218
  const Encryption_Type = /* @__PURE__ */proto3.makeEnum("livekit.Encryption.Type", [{
4203
4219
  no: 0,
4204
4220
  name: "NONE"
@@ -11011,6 +11027,7 @@ var EngineEvent;
11011
11027
  EngineEvent["LocalTrackSubscribed"] = "localTrackSubscribed";
11012
11028
  EngineEvent["Offline"] = "offline";
11013
11029
  EngineEvent["SignalRequestResponse"] = "signalRequestResponse";
11030
+ EngineEvent["SignalConnected"] = "signalConnected";
11014
11031
  })(EngineEvent || (EngineEvent = {}));
11015
11032
  var TrackEvent;
11016
11033
  (function (TrackEvent) {
@@ -11169,7 +11186,7 @@ function getOSVersion(ua) {
11169
11186
  return ua.includes('mac os') ? getMatch(/\(.+?(\d+_\d+(:?_\d+)?)/, ua, 1).replace(/_/g, '.') : undefined;
11170
11187
  }
11171
11188
 
11172
- var version$1 = "2.9.9";
11189
+ var version$1 = "2.11.0";
11173
11190
 
11174
11191
  const version = version$1;
11175
11192
  const protocolVersion = 15;
@@ -11567,7 +11584,7 @@ function detachTrack(track, element) {
11567
11584
  }
11568
11585
  }
11569
11586
  Track.streamStateFromProto = streamStateFromProto;
11570
- })(Track);
11587
+ })(Track || (Track = {}));
11571
11588
 
11572
11589
  class VideoPreset {
11573
11590
  constructor(widthOrOptions, height, maxBitrate, maxFramerate, priority) {
@@ -11609,8 +11626,12 @@ function isBackupCodec(codec) {
11609
11626
  }
11610
11627
  var BackupCodecPolicy;
11611
11628
  (function (BackupCodecPolicy) {
11612
- BackupCodecPolicy[BackupCodecPolicy["REGRESSION"] = 0] = "REGRESSION";
11629
+ // codec regression is preferred, the sfu will try to regress codec if possible but not guaranteed
11630
+ BackupCodecPolicy[BackupCodecPolicy["PREFER_REGRESSION"] = 0] = "PREFER_REGRESSION";
11631
+ // multi-codec simulcast, publish both primary and backup codec at the same time
11613
11632
  BackupCodecPolicy[BackupCodecPolicy["SIMULCAST"] = 1] = "SIMULCAST";
11633
+ // always use backup codec only
11634
+ BackupCodecPolicy[BackupCodecPolicy["REGRESSION"] = 2] = "REGRESSION";
11614
11635
  })(BackupCodecPolicy || (BackupCodecPolicy = {}));
11615
11636
  var AudioPresets;
11616
11637
  (function (AudioPresets) {
@@ -12041,12 +12062,6 @@ function unwrapConstraint(constraint) {
12041
12062
  }
12042
12063
  throw Error('could not unwrap constraint');
12043
12064
  }
12044
- function toWebsocketUrl(url) {
12045
- if (url.startsWith('http')) {
12046
- return url.replace(/^(http)/, 'ws');
12047
- }
12048
- return url;
12049
- }
12050
12065
  function toHttpUrl(url) {
12051
12066
  if (url.startsWith('ws')) {
12052
12067
  return url.replace(/^(ws)/, 'http');
@@ -12187,14 +12202,18 @@ function mergeDefaultOptions(options, audioDefaults, videoDefaults) {
12187
12202
  // use defaults
12188
12203
  if (clonedOptions.audio) {
12189
12204
  mergeObjectWithoutOverwriting(clonedOptions.audio, audioDefaults);
12190
- (_b = (_d = clonedOptions.audio).deviceId) !== null && _b !== void 0 ? _b : _d.deviceId = 'default';
12205
+ (_b = (_d = clonedOptions.audio).deviceId) !== null && _b !== void 0 ? _b : _d.deviceId = {
12206
+ ideal: 'default'
12207
+ };
12191
12208
  if (audioProcessor || defaultAudioProcessor) {
12192
12209
  clonedOptions.audio.processor = audioProcessor !== null && audioProcessor !== void 0 ? audioProcessor : defaultAudioProcessor;
12193
12210
  }
12194
12211
  }
12195
12212
  if (clonedOptions.video) {
12196
12213
  mergeObjectWithoutOverwriting(clonedOptions.video, videoDefaults);
12197
- (_c = (_e = clonedOptions.video).deviceId) !== null && _c !== void 0 ? _c : _e.deviceId = 'default';
12214
+ (_c = (_e = clonedOptions.video).deviceId) !== null && _c !== void 0 ? _c : _e.deviceId = {
12215
+ ideal: 'default'
12216
+ };
12198
12217
  if (videoProcessor || defaultVideoProcessor) {
12199
12218
  clonedOptions.video.processor = videoProcessor !== null && videoProcessor !== void 0 ? videoProcessor : defaultVideoProcessor;
12200
12219
  }
@@ -12228,10 +12247,14 @@ function constraintsForOptions(options) {
12228
12247
  }
12229
12248
  });
12230
12249
  constraints.video = videoOptions;
12231
- (_a = (_c = constraints.video).deviceId) !== null && _a !== void 0 ? _a : _c.deviceId = 'default';
12250
+ (_a = (_c = constraints.video).deviceId) !== null && _a !== void 0 ? _a : _c.deviceId = {
12251
+ ideal: 'default'
12252
+ };
12232
12253
  } else {
12233
12254
  constraints.video = options.video ? {
12234
- deviceId: 'default'
12255
+ deviceId: {
12256
+ ideal: 'default'
12257
+ }
12235
12258
  } : false;
12236
12259
  }
12237
12260
  } else {
@@ -12240,10 +12263,14 @@ function constraintsForOptions(options) {
12240
12263
  if (options.audio) {
12241
12264
  if (typeof options.audio === 'object') {
12242
12265
  constraints.audio = options.audio;
12243
- (_b = (_d = constraints.audio).deviceId) !== null && _b !== void 0 ? _b : _d.deviceId = 'default';
12266
+ (_b = (_d = constraints.audio).deviceId) !== null && _b !== void 0 ? _b : _d.deviceId = {
12267
+ ideal: 'default'
12268
+ };
12244
12269
  } else {
12245
12270
  constraints.audio = {
12246
- deviceId: 'default'
12271
+ deviceId: {
12272
+ ideal: 'default'
12273
+ }
12247
12274
  };
12248
12275
  }
12249
12276
  } else {
@@ -12848,7 +12875,9 @@ class DeviceManager {
12848
12875
  const permissionsToAcquire = {
12849
12876
  video: kind !== 'audioinput' && kind !== 'audiooutput',
12850
12877
  audio: kind !== 'videoinput' && {
12851
- deviceId: 'default'
12878
+ deviceId: {
12879
+ ideal: 'default'
12880
+ }
12852
12881
  }
12853
12882
  };
12854
12883
  const stream = yield navigator.mediaDevices.getUserMedia(permissionsToAcquire);
@@ -12936,6 +12965,25 @@ class AsyncQueue {
12936
12965
  }
12937
12966
  }
12938
12967
 
12968
+ function createRtcUrl(url, searchParams) {
12969
+ const urlObj = new URL(url);
12970
+ searchParams.forEach((value, key) => {
12971
+ urlObj.searchParams.set(key, value);
12972
+ });
12973
+ return appendUrlPath(urlObj, 'rtc');
12974
+ }
12975
+ function createValidateUrl(rtcWsUrl) {
12976
+ const urlObj = new URL(toHttpUrl(rtcWsUrl));
12977
+ return appendUrlPath(urlObj, 'validate');
12978
+ }
12979
+ function ensureTrailingSlash(path) {
12980
+ return path.endsWith('/') ? path : "".concat(path, "/");
12981
+ }
12982
+ function appendUrlPath(urlObj, path) {
12983
+ urlObj.pathname = "".concat(ensureTrailingSlash(urlObj.pathname)).concat(path);
12984
+ return urlObj.toString();
12985
+ }
12986
+
12939
12987
  const passThroughQueueSignals = ['syncState', 'trickle', 'offer', 'answer', 'simulate', 'leave'];
12940
12988
  function canPassThroughQueue(req) {
12941
12989
  const canPass = passThroughQueueSignals.indexOf(req.case) >= 0;
@@ -13033,15 +13081,10 @@ class SignalClient {
13033
13081
  }
13034
13082
  connect(url, token, opts, abortSignal) {
13035
13083
  this.connectOptions = opts;
13036
- const urlObj = new URL(toWebsocketUrl(url));
13037
- // strip trailing slash
13038
- const hasTrailingSlash = urlObj.pathname.endsWith('/');
13039
- urlObj.pathname += hasTrailingSlash ? 'rtc' : '/rtc';
13040
13084
  const clientInfo = getClientInfo();
13041
13085
  const params = createConnectionParams(token, clientInfo, opts);
13042
- for (const [key, value] of params) {
13043
- urlObj.searchParams.set(key, value);
13044
- }
13086
+ const rtcUrl = createRtcUrl(url, params);
13087
+ const validateUrl = createValidateUrl(rtcUrl);
13045
13088
  return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
13046
13089
  const unlock = yield this.connectionLock.lock();
13047
13090
  try {
@@ -13058,7 +13101,7 @@ class SignalClient {
13058
13101
  abortHandler();
13059
13102
  }
13060
13103
  abortSignal === null || abortSignal === void 0 ? void 0 : abortSignal.addEventListener('abort', abortHandler);
13061
- const redactedUrl = new URL(urlObj.toString());
13104
+ const redactedUrl = new URL(rtcUrl);
13062
13105
  if (redactedUrl.searchParams.has('access_token')) {
13063
13106
  redactedUrl.searchParams.set('access_token', '<redacted>');
13064
13107
  }
@@ -13069,7 +13112,7 @@ class SignalClient {
13069
13112
  if (this.ws) {
13070
13113
  yield this.close(false);
13071
13114
  }
13072
- this.ws = new WebSocket(urlObj.toString());
13115
+ this.ws = new WebSocket(rtcUrl);
13073
13116
  this.ws.binaryType = 'arraybuffer';
13074
13117
  this.ws.onopen = () => {
13075
13118
  clearTimeout(wsTimeout);
@@ -13079,15 +13122,12 @@ class SignalClient {
13079
13122
  this.state = SignalConnectionState.DISCONNECTED;
13080
13123
  clearTimeout(wsTimeout);
13081
13124
  try {
13082
- const validateURL = new URL(urlObj.toString());
13083
- validateURL.protocol = "http".concat(validateURL.protocol.substring(2));
13084
- validateURL.pathname += '/validate';
13085
- const resp = yield fetch(validateURL);
13125
+ const resp = yield fetch(validateUrl);
13086
13126
  if (resp.status.toFixed(0).startsWith('4')) {
13087
13127
  const msg = yield resp.text();
13088
13128
  reject(new ConnectionError(msg, ConnectionErrorReason.NotAllowed, resp.status));
13089
13129
  } else {
13090
- reject(new ConnectionError('Internal error', ConnectionErrorReason.InternalError, resp.status));
13130
+ reject(new ConnectionError("Encountered unknown websocket error during connection: ".concat(ev.toString()), ConnectionErrorReason.InternalError, resp.status));
13091
13131
  }
13092
13132
  } catch (e) {
13093
13133
  reject(new ConnectionError(e instanceof Error ? e.message : 'server was not reachable', ConnectionErrorReason.ServerUnreachable));
@@ -14931,14 +14971,18 @@ const publishDefaults = {
14931
14971
  backupCodec: true
14932
14972
  };
14933
14973
  const audioDefaults = {
14934
- deviceId: 'default',
14974
+ deviceId: {
14975
+ ideal: 'default'
14976
+ },
14935
14977
  autoGainControl: true,
14936
14978
  echoCancellation: true,
14937
14979
  noiseSuppression: true,
14938
14980
  voiceIsolation: true
14939
14981
  };
14940
14982
  const videoDefaults = {
14941
- deviceId: 'default',
14983
+ deviceId: {
14984
+ ideal: 'default'
14985
+ },
14942
14986
  resolution: VideoPresets.h720.resolution
14943
14987
  };
14944
14988
  const roomOptionDefaults = {
@@ -15644,9 +15688,10 @@ class LocalTrack extends Track {
15644
15688
  constraints = this._constraints;
15645
15689
  }
15646
15690
  const {
15647
- deviceId
15691
+ deviceId,
15692
+ facingMode
15648
15693
  } = constraints,
15649
- otherConstraints = __rest(constraints, ["deviceId"]);
15694
+ otherConstraints = __rest(constraints, ["deviceId", "facingMode"]);
15650
15695
  this.log.debug('restarting track with constraints', Object.assign(Object.assign({}, this.logContext), {
15651
15696
  constraints
15652
15697
  }));
@@ -15655,8 +15700,9 @@ class LocalTrack extends Track {
15655
15700
  video: false
15656
15701
  };
15657
15702
  if (this.kind === Track.Kind.Video) {
15658
- streamConstraints.video = deviceId ? {
15659
- deviceId
15703
+ streamConstraints.video = deviceId || facingMode ? {
15704
+ deviceId,
15705
+ facingMode
15660
15706
  } : true;
15661
15707
  } else {
15662
15708
  streamConstraints.audio = deviceId ? {
@@ -16950,13 +16996,44 @@ function setPublishingLayersForSender(sender, senderEncodings, qualities, sender
16950
16996
  return;
16951
16997
  }
16952
16998
  let hasChanged = false;
16953
- /* disable closable spatial layer as it has video blur / frozen issue with current server / client
16954
- 1. chrome 113: when switching to up layer with scalability Mode change, it will generate a
16955
- low resolution frame and recover very quickly, but noticable
16956
- 2. livekit sfu: additional pli request cause video frozen for a few frames, also noticable */
16957
- const closableSpatial = false;
16999
+ const browser = getBrowser();
17000
+ const closableSpatial = (browser === null || browser === void 0 ? void 0 : browser.name) === 'Chrome' && compareVersions(browser === null || browser === void 0 ? void 0 : browser.version, '133') > 0;
16958
17001
  /* @ts-ignore */
16959
- if (closableSpatial && encodings[0].scalabilityMode) ; else {
17002
+ if (closableSpatial && encodings[0].scalabilityMode) {
17003
+ // svc dynacast encodings
17004
+ const encoding = encodings[0];
17005
+ /* @ts-ignore */
17006
+ const mode = new ScalabilityMode(encoding.scalabilityMode);
17007
+ let maxQuality = VideoQuality$1.OFF;
17008
+ qualities.forEach(q => {
17009
+ if (q.enabled && (maxQuality === VideoQuality$1.OFF || q.quality > maxQuality)) {
17010
+ maxQuality = q.quality;
17011
+ }
17012
+ });
17013
+ if (maxQuality === VideoQuality$1.OFF) {
17014
+ if (encoding.active) {
17015
+ encoding.active = false;
17016
+ hasChanged = true;
17017
+ }
17018
+ } else if (!encoding.active || mode.spatial !== maxQuality + 1) {
17019
+ hasChanged = true;
17020
+ encoding.active = true;
17021
+ /* @ts-ignore */
17022
+ const originalMode = new ScalabilityMode(senderEncodings[0].scalabilityMode);
17023
+ mode.spatial = maxQuality + 1;
17024
+ mode.suffix = originalMode.suffix;
17025
+ if (mode.spatial === 1) {
17026
+ // no suffix for L1Tx
17027
+ mode.suffix = undefined;
17028
+ }
17029
+ /* @ts-ignore */
17030
+ encoding.scalabilityMode = mode.toString();
17031
+ encoding.scaleResolutionDownBy = Math.pow(2, 2 - maxQuality);
17032
+ if (senderEncodings[0].maxBitrate) {
17033
+ encoding.maxBitrate = senderEncodings[0].maxBitrate / (encoding.scaleResolutionDownBy * encoding.scaleResolutionDownBy);
17034
+ }
17035
+ }
17036
+ } else {
16960
17037
  // simulcast dynacast encodings
16961
17038
  encodings.forEach((encoding, idx) => {
16962
17039
  var _a;
@@ -17299,6 +17376,10 @@ class RTCEngine extends eventsExports.EventEmitter {
17299
17376
  this.negotiate();
17300
17377
  }
17301
17378
  this.clientConfiguration = joinResponse.clientConfiguration;
17379
+ // emit signal connected event after a short delay to allow for join response to be processed on room
17380
+ setTimeout(() => {
17381
+ this.emit(EngineEvent.SignalConnected);
17382
+ }, 10);
17302
17383
  return joinResponse;
17303
17384
  } catch (e) {
17304
17385
  if (e instanceof ConnectionError) {
@@ -18492,6 +18573,7 @@ class BaseStreamWriter {
18492
18573
  }
18493
18574
  }
18494
18575
  class TextStreamWriter extends BaseStreamWriter {}
18576
+ class ByteStreamWriter extends BaseStreamWriter {}
18495
18577
 
18496
18578
  class RemoteTrack extends Track {
18497
18579
  constructor(mediaTrack, sid, kind, receiver, loggerOptions) {
@@ -19245,7 +19327,7 @@ class TrackPublication extends eventsExports.EventEmitter {
19245
19327
  PermissionStatus["Allowed"] = "allowed";
19246
19328
  PermissionStatus["NotAllowed"] = "not_allowed";
19247
19329
  })(TrackPublication.PermissionStatus || (TrackPublication.PermissionStatus = {}));
19248
- })(TrackPublication);
19330
+ })(TrackPublication || (TrackPublication = {}));
19249
19331
 
19250
19332
  class LocalTrackPublication extends TrackPublication {
19251
19333
  get isUpstreamPaused() {
@@ -20332,7 +20414,41 @@ class LocalParticipant extends Participant {
20332
20414
  if (opts.source) {
20333
20415
  track.source = opts.source;
20334
20416
  }
20335
- const publishPromise = _this2.publish(track, opts, isStereo);
20417
+ const publishPromise = new Promise((resolve, reject) => __awaiter(_this2, void 0, void 0, function* () {
20418
+ try {
20419
+ if (this.engine.client.currentState !== SignalConnectionState.CONNECTED) {
20420
+ this.log.debug('deferring track publication until signal is connected', Object.assign(Object.assign({}, this.logContext), {
20421
+ track: getLogContextFromTrack(track)
20422
+ }));
20423
+ const onSignalConnected = () => __awaiter(this, void 0, void 0, function* () {
20424
+ try {
20425
+ const publication = yield this.publish(track, opts, isStereo);
20426
+ resolve(publication);
20427
+ } catch (e) {
20428
+ reject(e);
20429
+ }
20430
+ });
20431
+ setTimeout(() => {
20432
+ this.engine.off(EngineEvent.SignalConnected, onSignalConnected);
20433
+ reject(new PublishTrackError('publishing rejected as engine not connected within timeout', 408));
20434
+ }, 15000);
20435
+ this.engine.once(EngineEvent.SignalConnected, onSignalConnected);
20436
+ this.engine.on(EngineEvent.Closing, () => {
20437
+ this.engine.off(EngineEvent.SignalConnected, onSignalConnected);
20438
+ reject(new PublishTrackError('publishing rejected as engine closed', 499));
20439
+ });
20440
+ } else {
20441
+ try {
20442
+ const publication = yield this.publish(track, opts, isStereo);
20443
+ resolve(publication);
20444
+ } catch (e) {
20445
+ reject(e);
20446
+ }
20447
+ }
20448
+ } catch (e) {
20449
+ reject(e);
20450
+ }
20451
+ }));
20336
20452
  _this2.pendingPublishPromises.set(track, publishPromise);
20337
20453
  try {
20338
20454
  const publication = yield publishPromise;
@@ -21029,23 +21145,57 @@ class LocalParticipant extends Participant {
21029
21145
  }
21030
21146
  _sendFile(streamId, file, options) {
21031
21147
  return __awaiter(this, void 0, void 0, function* () {
21032
- var _a, _b;
21033
- const totalLength = file.size;
21034
- const header = new DataStream_Header({
21035
- totalLength: numberToBigInt(totalLength),
21036
- mimeType: (_a = options === null || options === void 0 ? void 0 : options.mimeType) !== null && _a !== void 0 ? _a : file.type,
21148
+ var _a;
21149
+ const writer = yield this.streamBytes({
21037
21150
  streamId,
21151
+ totalSize: file.size,
21152
+ name: file.name,
21153
+ mimeType: (_a = options === null || options === void 0 ? void 0 : options.mimeType) !== null && _a !== void 0 ? _a : file.type,
21038
21154
  topic: options === null || options === void 0 ? void 0 : options.topic,
21039
- encryptionType: options === null || options === void 0 ? void 0 : options.encryptionType,
21155
+ destinationIdentities: options === null || options === void 0 ? void 0 : options.destinationIdentities
21156
+ });
21157
+ const reader = file.stream().getReader();
21158
+ while (true) {
21159
+ const {
21160
+ done,
21161
+ value
21162
+ } = yield reader.read();
21163
+ if (done) {
21164
+ break;
21165
+ }
21166
+ yield writer.write(value);
21167
+ }
21168
+ yield writer.close();
21169
+ return writer.info;
21170
+ });
21171
+ }
21172
+ streamBytes(options) {
21173
+ return __awaiter(this, void 0, void 0, function* () {
21174
+ var _a, _b, _c, _d, _e;
21175
+ const streamId = (_a = options === null || options === void 0 ? void 0 : options.streamId) !== null && _a !== void 0 ? _a : crypto.randomUUID();
21176
+ const destinationIdentities = options === null || options === void 0 ? void 0 : options.destinationIdentities;
21177
+ const info = {
21178
+ id: streamId,
21179
+ mimeType: (_b = options === null || options === void 0 ? void 0 : options.mimeType) !== null && _b !== void 0 ? _b : 'application/octet-stream',
21180
+ topic: (_c = options === null || options === void 0 ? void 0 : options.topic) !== null && _c !== void 0 ? _c : '',
21181
+ timestamp: Date.now(),
21182
+ attributes: options === null || options === void 0 ? void 0 : options.attributes,
21183
+ size: options === null || options === void 0 ? void 0 : options.totalSize,
21184
+ name: (_d = options === null || options === void 0 ? void 0 : options.name) !== null && _d !== void 0 ? _d : 'unknown'
21185
+ };
21186
+ const header = new DataStream_Header({
21187
+ totalLength: numberToBigInt((_e = info.size) !== null && _e !== void 0 ? _e : 0),
21188
+ mimeType: info.mimeType,
21189
+ streamId,
21190
+ topic: info.topic,
21040
21191
  timestamp: numberToBigInt(Date.now()),
21041
21192
  contentHeader: {
21042
21193
  case: 'byteHeader',
21043
21194
  value: new DataStream_ByteHeader({
21044
- name: file.name
21195
+ name: info.name
21045
21196
  })
21046
21197
  }
21047
21198
  });
21048
- const destinationIdentities = options === null || options === void 0 ? void 0 : options.destinationIdentities;
21049
21199
  const packet = new DataPacket({
21050
21200
  destinationIdentities,
21051
21201
  value: {
@@ -21054,45 +21204,60 @@ class LocalParticipant extends Participant {
21054
21204
  }
21055
21205
  });
21056
21206
  yield this.engine.sendDataPacket(packet, DataPacket_Kind.RELIABLE);
21057
- function read(b) {
21058
- return new Promise(resolve => {
21059
- const fr = new FileReader();
21060
- fr.onload = () => {
21061
- resolve(new Uint8Array(fr.result));
21062
- };
21063
- fr.readAsArrayBuffer(b);
21064
- });
21065
- }
21066
- const totalChunks = Math.ceil(totalLength / STREAM_CHUNK_SIZE);
21067
- for (let i = 0; i < totalChunks; i++) {
21068
- const chunkData = yield read(file.slice(i * STREAM_CHUNK_SIZE, Math.min((i + 1) * STREAM_CHUNK_SIZE, totalLength)));
21069
- yield this.engine.waitForBufferStatusLow(DataPacket_Kind.RELIABLE);
21070
- const chunk = new DataStream_Chunk({
21071
- content: chunkData,
21072
- streamId,
21073
- chunkIndex: numberToBigInt(i)
21074
- });
21075
- const chunkPacket = new DataPacket({
21076
- destinationIdentities,
21077
- value: {
21078
- case: 'streamChunk',
21079
- value: chunk
21080
- }
21081
- });
21082
- yield this.engine.sendDataPacket(chunkPacket, DataPacket_Kind.RELIABLE);
21083
- (_b = options === null || options === void 0 ? void 0 : options.onProgress) === null || _b === void 0 ? void 0 : _b.call(options, (i + 1) / totalChunks);
21084
- }
21085
- const trailer = new DataStream_Trailer({
21086
- streamId
21087
- });
21088
- const trailerPacket = new DataPacket({
21089
- destinationIdentities,
21090
- value: {
21091
- case: 'streamTrailer',
21092
- value: trailer
21207
+ let chunkId = 0;
21208
+ const writeMutex = new _();
21209
+ const engine = this.engine;
21210
+ const log = this.log;
21211
+ const writableStream = new WritableStream({
21212
+ write(chunk) {
21213
+ return __awaiter(this, void 0, void 0, function* () {
21214
+ const unlock = yield writeMutex.lock();
21215
+ let byteOffset = 0;
21216
+ try {
21217
+ while (byteOffset < chunk.byteLength) {
21218
+ const subChunk = chunk.slice(byteOffset, byteOffset + STREAM_CHUNK_SIZE);
21219
+ yield engine.waitForBufferStatusLow(DataPacket_Kind.RELIABLE);
21220
+ const chunkPacket = new DataPacket({
21221
+ destinationIdentities,
21222
+ value: {
21223
+ case: 'streamChunk',
21224
+ value: new DataStream_Chunk({
21225
+ content: subChunk,
21226
+ streamId,
21227
+ chunkIndex: numberToBigInt(chunkId)
21228
+ })
21229
+ }
21230
+ });
21231
+ yield engine.sendDataPacket(chunkPacket, DataPacket_Kind.RELIABLE);
21232
+ chunkId += 1;
21233
+ byteOffset += subChunk.byteLength;
21234
+ }
21235
+ } finally {
21236
+ unlock();
21237
+ }
21238
+ });
21239
+ },
21240
+ close() {
21241
+ return __awaiter(this, void 0, void 0, function* () {
21242
+ const trailer = new DataStream_Trailer({
21243
+ streamId
21244
+ });
21245
+ const trailerPacket = new DataPacket({
21246
+ destinationIdentities,
21247
+ value: {
21248
+ case: 'streamTrailer',
21249
+ value: trailer
21250
+ }
21251
+ });
21252
+ yield engine.sendDataPacket(trailerPacket, DataPacket_Kind.RELIABLE);
21253
+ });
21254
+ },
21255
+ abort(err) {
21256
+ log.error('Sink error:', err);
21093
21257
  }
21094
21258
  });
21095
- yield this.engine.sendDataPacket(trailerPacket, DataPacket_Kind.RELIABLE);
21259
+ const byteWriter = new ByteStreamWriter(writableStream, info);
21260
+ return byteWriter;
21096
21261
  });
21097
21262
  }
21098
21263
  /**
@@ -23108,7 +23273,7 @@ class Room extends eventsExports.EventEmitter {
23108
23273
  switchActiveDevice(kind_1, deviceId_1) {
23109
23274
  return __awaiter(this, arguments, void 0, function (kind, deviceId) {
23110
23275
  var _this3 = this;
23111
- let exact = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
23276
+ let exact = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
23112
23277
  return function* () {
23113
23278
  var _a, _b, _c, _d, _e, _f, _g;
23114
23279
  var _h;
@@ -24111,63 +24276,104 @@ function createLocalTracks(options) {
24111
24276
  return __awaiter(this, void 0, void 0, function* () {
24112
24277
  var _a, _b;
24113
24278
  // set default options to true
24114
- options !== null && options !== void 0 ? options : options = {};
24115
- (_a = options.audio) !== null && _a !== void 0 ? _a : options.audio = {
24279
+ const internalOptions = Object.assign({}, options !== null && options !== void 0 ? options : {});
24280
+ let attemptExactMatch = false;
24281
+ let retryAudioOptions = options === null || options === void 0 ? void 0 : options.audio;
24282
+ let retryVideoOptions = options === null || options === void 0 ? void 0 : options.video;
24283
+ // if the user passes a device id as a string, we default to exact match
24284
+ if (internalOptions.audio && typeof internalOptions.audio === 'object' && typeof internalOptions.audio.deviceId === 'string') {
24285
+ const deviceId = internalOptions.audio.deviceId;
24286
+ internalOptions.audio.deviceId = {
24287
+ exact: deviceId
24288
+ };
24289
+ attemptExactMatch = true;
24290
+ retryAudioOptions = Object.assign(Object.assign({}, internalOptions.audio), {
24291
+ deviceId: {
24292
+ ideal: deviceId
24293
+ }
24294
+ });
24295
+ }
24296
+ if (internalOptions.video && typeof internalOptions.video === 'object' && typeof internalOptions.video.deviceId === 'string') {
24297
+ const deviceId = internalOptions.video.deviceId;
24298
+ internalOptions.video.deviceId = {
24299
+ exact: deviceId
24300
+ };
24301
+ attemptExactMatch = true;
24302
+ retryVideoOptions = Object.assign(Object.assign({}, internalOptions.video), {
24303
+ deviceId: {
24304
+ ideal: deviceId
24305
+ }
24306
+ });
24307
+ }
24308
+ (_a = internalOptions.audio) !== null && _a !== void 0 ? _a : internalOptions.audio = {
24116
24309
  deviceId: 'default'
24117
24310
  };
24118
- (_b = options.video) !== null && _b !== void 0 ? _b : options.video = {
24311
+ (_b = internalOptions.video) !== null && _b !== void 0 ? _b : internalOptions.video = {
24119
24312
  deviceId: 'default'
24120
24313
  };
24121
24314
  const {
24122
24315
  audioProcessor,
24123
24316
  videoProcessor
24124
- } = extractProcessorsFromOptions(options);
24125
- const opts = mergeDefaultOptions(options, audioDefaults, videoDefaults);
24317
+ } = extractProcessorsFromOptions(internalOptions);
24318
+ const opts = mergeDefaultOptions(internalOptions, audioDefaults, videoDefaults);
24126
24319
  const constraints = constraintsForOptions(opts);
24127
24320
  // Keep a reference to the promise on DeviceManager and await it in getLocalDevices()
24128
24321
  // works around iOS Safari Bug https://bugs.webkit.org/show_bug.cgi?id=179363
24129
24322
  const mediaPromise = navigator.mediaDevices.getUserMedia(constraints);
24130
- if (options.audio) {
24323
+ if (internalOptions.audio) {
24131
24324
  DeviceManager.userMediaPromiseMap.set('audioinput', mediaPromise);
24132
24325
  mediaPromise.catch(() => DeviceManager.userMediaPromiseMap.delete('audioinput'));
24133
24326
  }
24134
- if (options.video) {
24327
+ if (internalOptions.video) {
24135
24328
  DeviceManager.userMediaPromiseMap.set('videoinput', mediaPromise);
24136
24329
  mediaPromise.catch(() => DeviceManager.userMediaPromiseMap.delete('videoinput'));
24137
24330
  }
24138
- const stream = yield mediaPromise;
24139
- return Promise.all(stream.getTracks().map(mediaStreamTrack => __awaiter(this, void 0, void 0, function* () {
24140
- const isAudio = mediaStreamTrack.kind === 'audio';
24141
- isAudio ? opts.audio : opts.video;
24142
- let trackConstraints;
24143
- const conOrBool = isAudio ? constraints.audio : constraints.video;
24144
- if (typeof conOrBool !== 'boolean') {
24145
- trackConstraints = conOrBool;
24146
- }
24147
- // update the constraints with the device id the user gave permissions to in the permission prompt
24148
- // otherwise each track restart (e.g. mute - unmute) will try to initialize the device again -> causing additional permission prompts
24149
- const newDeviceId = mediaStreamTrack.getSettings().deviceId;
24150
- if ((trackConstraints === null || trackConstraints === void 0 ? void 0 : trackConstraints.deviceId) && unwrapConstraint(trackConstraints.deviceId) !== newDeviceId) {
24151
- trackConstraints.deviceId = newDeviceId;
24152
- } else if (!trackConstraints) {
24153
- trackConstraints = {
24154
- deviceId: newDeviceId
24155
- };
24156
- }
24157
- const track = mediaTrackToLocalTrack(mediaStreamTrack, trackConstraints);
24158
- if (track.kind === Track.Kind.Video) {
24159
- track.source = Track.Source.Camera;
24160
- } else if (track.kind === Track.Kind.Audio) {
24161
- track.source = Track.Source.Microphone;
24162
- }
24163
- track.mediaStream = stream;
24164
- if (isAudioTrack(track) && audioProcessor) {
24165
- yield track.setProcessor(audioProcessor);
24166
- } else if (isVideoTrack(track) && videoProcessor) {
24167
- yield track.setProcessor(videoProcessor);
24331
+ try {
24332
+ const stream = yield mediaPromise;
24333
+ return yield Promise.all(stream.getTracks().map(mediaStreamTrack => __awaiter(this, void 0, void 0, function* () {
24334
+ const isAudio = mediaStreamTrack.kind === 'audio';
24335
+ let trackOptions = isAudio ? opts.audio : opts.video;
24336
+ if (typeof trackOptions === 'boolean' || !trackOptions) {
24337
+ trackOptions = {};
24338
+ }
24339
+ let trackConstraints;
24340
+ const conOrBool = isAudio ? constraints.audio : constraints.video;
24341
+ if (typeof conOrBool !== 'boolean') {
24342
+ trackConstraints = conOrBool;
24343
+ }
24344
+ // update the constraints with the device id the user gave permissions to in the permission prompt
24345
+ // otherwise each track restart (e.g. mute - unmute) will try to initialize the device again -> causing additional permission prompts
24346
+ const newDeviceId = mediaStreamTrack.getSettings().deviceId;
24347
+ if ((trackConstraints === null || trackConstraints === void 0 ? void 0 : trackConstraints.deviceId) && unwrapConstraint(trackConstraints.deviceId) !== newDeviceId) {
24348
+ trackConstraints.deviceId = newDeviceId;
24349
+ } else if (!trackConstraints) {
24350
+ trackConstraints = {
24351
+ deviceId: newDeviceId
24352
+ };
24353
+ }
24354
+ const track = mediaTrackToLocalTrack(mediaStreamTrack, trackConstraints);
24355
+ if (track.kind === Track.Kind.Video) {
24356
+ track.source = Track.Source.Camera;
24357
+ } else if (track.kind === Track.Kind.Audio) {
24358
+ track.source = Track.Source.Microphone;
24359
+ }
24360
+ track.mediaStream = stream;
24361
+ if (isAudioTrack(track) && audioProcessor) {
24362
+ yield track.setProcessor(audioProcessor);
24363
+ } else if (isVideoTrack(track) && videoProcessor) {
24364
+ yield track.setProcessor(videoProcessor);
24365
+ }
24366
+ return track;
24367
+ })));
24368
+ } catch (e) {
24369
+ if (!attemptExactMatch) {
24370
+ throw e;
24168
24371
  }
24169
- return track;
24170
- })));
24372
+ return createLocalTracks(Object.assign(Object.assign({}, options), {
24373
+ audio: retryAudioOptions,
24374
+ video: retryVideoOptions
24375
+ }));
24376
+ }
24171
24377
  });
24172
24378
  }
24173
24379
  /**