livekit-client 2.15.2 → 2.15.4

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.
package/README.md CHANGED
@@ -402,9 +402,9 @@ Also when targeting legacy browsers, older than the ones specified in our browse
402
402
  <br/><table>
403
403
  <thead><tr><th colspan="2">LiveKit Ecosystem</th></tr></thead>
404
404
  <tbody>
405
- <tr><td>LiveKit SDKs</td><td><b>Browser</b> · <a href="https://github.com/livekit/client-sdk-swift">iOS/macOS/visionOS</a> · <a href="https://github.com/livekit/client-sdk-android">Android</a> · <a href="https://github.com/livekit/client-sdk-flutter">Flutter</a> · <a href="https://github.com/livekit/client-sdk-react-native">React Native</a> · <a href="https://github.com/livekit/rust-sdks">Rust</a> · <a href="https://github.com/livekit/node-sdks">Node.js</a> · <a href="https://github.com/livekit/python-sdks">Python</a> · <a href="https://github.com/livekit/client-sdk-unity">Unity</a> · <a href="https://github.com/livekit/client-sdk-unity-web">Unity (WebGL)</a></td></tr><tr></tr>
405
+ <tr><td>LiveKit SDKs</td><td><b>Browser</b> · <a href="https://github.com/livekit/client-sdk-swift">iOS/macOS/visionOS</a> · <a href="https://github.com/livekit/client-sdk-android">Android</a> · <a href="https://github.com/livekit/client-sdk-flutter">Flutter</a> · <a href="https://github.com/livekit/client-sdk-react-native">React Native</a> · <a href="https://github.com/livekit/rust-sdks">Rust</a> · <a href="https://github.com/livekit/node-sdks">Node.js</a> · <a href="https://github.com/livekit/python-sdks">Python</a> · <a href="https://github.com/livekit/client-sdk-unity">Unity</a> · <a href="https://github.com/livekit/client-sdk-unity-web">Unity (WebGL)</a> · <a href="https://github.com/livekit/client-sdk-esp32">ESP32</a></td></tr><tr></tr>
406
406
  <tr><td>Server APIs</td><td><a href="https://github.com/livekit/node-sdks">Node.js</a> · <a href="https://github.com/livekit/server-sdk-go">Golang</a> · <a href="https://github.com/livekit/server-sdk-ruby">Ruby</a> · <a href="https://github.com/livekit/server-sdk-kotlin">Java/Kotlin</a> · <a href="https://github.com/livekit/python-sdks">Python</a> · <a href="https://github.com/livekit/rust-sdks">Rust</a> · <a href="https://github.com/agence104/livekit-server-sdk-php">PHP (community)</a> · <a href="https://github.com/pabloFuente/livekit-server-sdk-dotnet">.NET (community)</a></td></tr><tr></tr>
407
- <tr><td>UI Components</td><td><a href="https://github.com/livekit/components-js">React</a> · <a href="https://github.com/livekit/components-android">Android Compose</a> · <a href="https://github.com/livekit/components-swift">SwiftUI</a></td></tr><tr></tr>
407
+ <tr><td>UI Components</td><td><a href="https://github.com/livekit/components-js">React</a> · <a href="https://github.com/livekit/components-android">Android Compose</a> · <a href="https://github.com/livekit/components-swift">SwiftUI</a> · <a href="https://github.com/livekit/components-flutter">Flutter</a></td></tr><tr></tr>
408
408
  <tr><td>Agents Frameworks</td><td><a href="https://github.com/livekit/agents">Python</a> · <a href="https://github.com/livekit/agents-js">Node.js</a> · <a href="https://github.com/livekit/agent-playground">Playground</a></td></tr><tr></tr>
409
409
  <tr><td>Services</td><td><a href="https://github.com/livekit/livekit">LiveKit server</a> · <a href="https://github.com/livekit/egress">Egress</a> · <a href="https://github.com/livekit/ingress">Ingress</a> · <a href="https://github.com/livekit/sip">SIP</a></td></tr><tr></tr>
410
410
  <tr><td>Resources</td><td><a href="https://docs.livekit.io">Docs</a> · <a href="https://github.com/livekit-examples">Example apps</a> · <a href="https://livekit.io/cloud">Cloud</a> · <a href="https://docs.livekit.io/home/self-hosting/deployment">Self-hosting</a> · <a href="https://github.com/livekit/livekit-cli">CLI</a></td></tr>
@@ -11364,7 +11364,7 @@ function getOSVersion(ua) {
11364
11364
  return ua.includes('mac os') ? getMatch(/\(.+?(\d+_\d+(:?_\d+)?)/, ua, 1).replace(/_/g, '.') : undefined;
11365
11365
  }
11366
11366
 
11367
- var version$1 = "2.15.2";
11367
+ var version$1 = "2.15.4";
11368
11368
 
11369
11369
  const version = version$1;
11370
11370
  const protocolVersion = 16;
@@ -11956,7 +11956,7 @@ function isSVCCodec(codec) {
11956
11956
  return codec === 'av1' || codec === 'vp9';
11957
11957
  }
11958
11958
  function supportsSetSinkId(elm) {
11959
- if (!document) {
11959
+ if (!document || isSafariBased()) {
11960
11960
  return false;
11961
11961
  }
11962
11962
  if (!elm) {
@@ -12167,7 +12167,11 @@ function getEmptyAudioStreamTrack() {
12167
12167
  return emptyAudioStreamTrack.clone();
12168
12168
  }
12169
12169
  class Future {
12170
+ get isResolved() {
12171
+ return this._isResolved;
12172
+ }
12170
12173
  constructor(futureBase, onFinally) {
12174
+ this._isResolved = false;
12171
12175
  this.onFinally = onFinally;
12172
12176
  this.promise = new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
12173
12177
  this.resolve = resolve;
@@ -12177,7 +12181,8 @@ class Future {
12177
12181
  }
12178
12182
  })).finally(() => {
12179
12183
  var _a;
12180
- return (_a = this.onFinally) === null || _a === void 0 ? void 0 : _a.call(this);
12184
+ this._isResolved = true;
12185
+ (_a = this.onFinally) === null || _a === void 0 ? void 0 : _a.call(this);
12181
12186
  });
12182
12187
  }
12183
12188
  }
@@ -13634,6 +13639,12 @@ class SignalClient {
13634
13639
  if (_this3.signalLatency) {
13635
13640
  yield sleep(_this3.signalLatency);
13636
13641
  }
13642
+ if (_this3.isDisconnected) {
13643
+ // Skip requests if the signal layer is disconnected
13644
+ // This can happen if an event is sent in the mist of room.connect() initializing
13645
+ _this3.log.debug("skipping signal request (type: ".concat(message.case, ") - SignalClient disconnected"));
13646
+ return;
13647
+ }
13637
13648
  if (!_this3.ws || _this3.ws.readyState !== _this3.ws.OPEN) {
13638
13649
  _this3.log.error("cannot send signal request before connected, type: ".concat(message === null || message === void 0 ? void 0 : message.case), _this3.logContext);
13639
13650
  return;
@@ -15027,7 +15038,7 @@ class PCTransport extends eventsExports.EventEmitter {
15027
15038
  if (codecPayload === 0) {
15028
15039
  return true;
15029
15040
  }
15030
- if (isSVCCodec(trackbr.codec)) {
15041
+ if (isSVCCodec(trackbr.codec) && !isSafari()) {
15031
15042
  this.ensureVideoDDExtensionForSVC(media, sdpParsed);
15032
15043
  }
15033
15044
  // TODO: av1 slow starting issue already fixed in chrome 124, clean this after some versions
@@ -15839,11 +15850,21 @@ class LocalTrackRecorder extends RecorderBase {
15839
15850
  start: controller => {
15840
15851
  streamController = controller;
15841
15852
  dataListener = event => __awaiter(this, void 0, void 0, function* () {
15842
- const arrayBuffer = yield event.data.arrayBuffer();
15853
+ let data;
15854
+ if (event.data.arrayBuffer) {
15855
+ const arrayBuffer = yield event.data.arrayBuffer();
15856
+ data = new Uint8Array(arrayBuffer);
15857
+ // @ts-expect-error react-native passes over Uint8Arrays directly
15858
+ } else if (event.data.byteArray) {
15859
+ // @ts-expect-error
15860
+ data = event.data.byteArray;
15861
+ } else {
15862
+ throw new Error('no data available!');
15863
+ }
15843
15864
  if (isClosed()) {
15844
15865
  return;
15845
15866
  }
15846
- controller.enqueue(new Uint8Array(arrayBuffer));
15867
+ controller.enqueue(data);
15847
15868
  });
15848
15869
  this.addEventListener('dataavailable', dataListener);
15849
15870
  },
@@ -16333,9 +16354,25 @@ class LocalTrack extends Track {
16333
16354
  }
16334
16355
  attachToElement(_this3._mediaStreamTrack, processorElement);
16335
16356
  processorElement.muted = true;
16336
- processorElement.play().catch(error => _this3.log.error('failed to play processor element', Object.assign(Object.assign({}, _this3.logContext), {
16337
- error
16338
- })));
16357
+ processorElement.play().catch(error => {
16358
+ if (error instanceof DOMException && error.name === 'AbortError') {
16359
+ // This happens on Safari when the processor is restarted, try again after a delay
16360
+ _this3.log.warn('failed to play processor element, retrying', Object.assign(Object.assign({}, _this3.logContext), {
16361
+ error
16362
+ }));
16363
+ setTimeout(() => {
16364
+ processorElement.play().catch(err => {
16365
+ _this3.log.error('failed to play processor element', Object.assign(Object.assign({}, _this3.logContext), {
16366
+ err
16367
+ }));
16368
+ });
16369
+ }, 100);
16370
+ } else {
16371
+ _this3.log.error('failed to play processor element', Object.assign(Object.assign({}, _this3.logContext), {
16372
+ error
16373
+ }));
16374
+ }
16375
+ });
16339
16376
  _this3.processor = processor;
16340
16377
  _this3.processorElement = processorElement;
16341
16378
  if (_this3.processor.processedTrack) {
@@ -16395,8 +16432,13 @@ class LocalTrack extends Track {
16395
16432
  return;
16396
16433
  }
16397
16434
  if (!this.localTrackRecorder) {
16435
+ let mimeType = 'audio/webm;codecs=opus';
16436
+ if (!MediaRecorder.isTypeSupported(mimeType)) {
16437
+ // iOS currently only supports video/mp4 as a mime type - even for audio.
16438
+ mimeType = 'video/mp4';
16439
+ }
16398
16440
  this.localTrackRecorder = new LocalTrackRecorder(this, {
16399
- mimeType: 'audio/webm;codecs=opus'
16441
+ mimeType
16400
16442
  });
16401
16443
  } else {
16402
16444
  this.log.warn('preconnect buffer already started');
@@ -16421,6 +16463,10 @@ class LocalTrack extends Track {
16421
16463
  var _a;
16422
16464
  return (_a = this.localTrackRecorder) === null || _a === void 0 ? void 0 : _a.byteStream;
16423
16465
  }
16466
+ getPreConnectBufferMimeType() {
16467
+ var _a;
16468
+ return (_a = this.localTrackRecorder) === null || _a === void 0 ? void 0 : _a.mimeType;
16469
+ }
16424
16470
  }
16425
16471
 
16426
16472
  class LocalAudioTrack extends LocalTrack {
@@ -20514,7 +20560,7 @@ class LocalParticipant extends Participant {
20514
20560
  this.reconnectFuture = undefined;
20515
20561
  this.updateTrackSubscriptionPermissions();
20516
20562
  };
20517
- this.handleDisconnected = () => {
20563
+ this.handleClosing = () => {
20518
20564
  var _a, _b, _c, _d, _e, _f;
20519
20565
  if (this.reconnectFuture) {
20520
20566
  this.reconnectFuture.promise.catch(e => this.log.warn(e.message, this.logContext));
@@ -20748,6 +20794,7 @@ class LocalParticipant extends Participant {
20748
20794
  * @internal
20749
20795
  */
20750
20796
  setupEngine(engine) {
20797
+ var _a;
20751
20798
  this.engine = engine;
20752
20799
  this.engine.on(EngineEvent.RemoteMute, (trackSid, muted) => {
20753
20800
  const pub = this.trackPublications.get(trackSid);
@@ -20760,7 +20807,10 @@ class LocalParticipant extends Participant {
20760
20807
  pub.unmute();
20761
20808
  }
20762
20809
  });
20763
- this.engine.on(EngineEvent.Connected, this.handleReconnected).on(EngineEvent.SignalConnected, this.handleSignalConnected).on(EngineEvent.SignalRestarted, this.handleReconnected).on(EngineEvent.SignalResumed, this.handleReconnected).on(EngineEvent.Restarting, this.handleReconnecting).on(EngineEvent.Resuming, this.handleReconnecting).on(EngineEvent.LocalTrackUnpublished, this.handleLocalTrackUnpublished).on(EngineEvent.SubscribedQualityUpdate, this.handleSubscribedQualityUpdate).on(EngineEvent.Disconnected, this.handleDisconnected).on(EngineEvent.SignalRequestResponse, this.handleSignalRequestResponse).on(EngineEvent.DataPacketReceived, this.handleDataPacket);
20810
+ if ((_a = this.signalConnectedFuture) === null || _a === void 0 ? void 0 : _a.isResolved) {
20811
+ this.signalConnectedFuture = undefined;
20812
+ }
20813
+ this.engine.on(EngineEvent.Connected, this.handleReconnected).on(EngineEvent.SignalConnected, this.handleSignalConnected).on(EngineEvent.SignalRestarted, this.handleReconnected).on(EngineEvent.SignalResumed, this.handleReconnected).on(EngineEvent.Restarting, this.handleReconnecting).on(EngineEvent.Resuming, this.handleReconnecting).on(EngineEvent.LocalTrackUnpublished, this.handleLocalTrackUnpublished).on(EngineEvent.SubscribedQualityUpdate, this.handleSubscribedQualityUpdate).on(EngineEvent.Closing, this.handleClosing).on(EngineEvent.SignalRequestResponse, this.handleSignalRequestResponse).on(EngineEvent.DataPacketReceived, this.handleDataPacket);
20764
20814
  }
20765
20815
  /**
20766
20816
  * Sets and updates the metadata of the local participant.
@@ -20944,7 +20994,8 @@ class LocalParticipant extends Participant {
20944
20994
  throw e;
20945
20995
  }
20946
20996
  for (const localTrack of localTracks) {
20947
- if (source === Track.Source.Microphone && isAudioTrack(localTrack) && (publishOptions === null || publishOptions === void 0 ? void 0 : publishOptions.preConnectBuffer)) {
20997
+ const opts = Object.assign(Object.assign({}, this.roomOptions.publishDefaults), options);
20998
+ if (source === Track.Source.Microphone && isAudioTrack(localTrack) && opts.preConnectBuffer) {
20948
20999
  this.log.info('starting preconnect buffer for microphone', Object.assign({}, this.logContext));
20949
21000
  localTrack.startPreConnectBuffer();
20950
21001
  }
@@ -21544,6 +21595,7 @@ class LocalParticipant extends Participant {
21544
21595
  this.emit(ParticipantEvent.LocalTrackPublished, publication);
21545
21596
  if (isLocalAudioTrack(track) && ti.audioFeatures.includes(AudioTrackFeature.TF_PRECONNECT_BUFFER)) {
21546
21597
  const stream = track.getPreConnectBuffer();
21598
+ const mimeType = track.getPreConnectBufferMimeType();
21547
21599
  // TODO: we're registering the listener after negotiation, so there might be a race
21548
21600
  this.on(ParticipantEvent.LocalTrackSubscribed, pub => {
21549
21601
  if (pub.trackSid === ti.sid) {
@@ -21569,7 +21621,7 @@ class LocalParticipant extends Participant {
21569
21621
  this.log.debug('sending preconnect buffer', Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(track)));
21570
21622
  const writer = yield this.streamBytes({
21571
21623
  name: 'preconnect-buffer',
21572
- mimeType: 'audio/opus',
21624
+ mimeType,
21573
21625
  topic: 'lk.agent.pre-connect-audio-buffer',
21574
21626
  destinationIdentities: [agent.identity],
21575
21627
  attributes: {
@@ -24266,7 +24318,7 @@ class Room extends eventsExports.EventEmitter {
24266
24318
  } else if (kind === 'audiooutput') {
24267
24319
  shouldTriggerImmediateDeviceChange = true;
24268
24320
  if (!supportsSetSinkId() && !_this3.options.webAudioMix || _this3.options.webAudioMix && _this3.audioContext && !('setSinkId' in _this3.audioContext)) {
24269
- throw new Error('cannot switch audio output, setSinkId not supported');
24321
+ throw new Error('cannot switch audio output, the current browser does not support it');
24270
24322
  }
24271
24323
  if (_this3.options.webAudioMix) {
24272
24324
  // setting `default` for web audio output doesn't work, so we need to normalize the id before