livekit-client 1.6.3 → 1.6.5

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 (50) hide show
  1. package/dist/livekit-client.esm.mjs +418 -70
  2. package/dist/livekit-client.esm.mjs.map +1 -1
  3. package/dist/livekit-client.umd.js +1 -1
  4. package/dist/livekit-client.umd.js.map +1 -1
  5. package/dist/src/api/SignalClient.d.ts +13 -2
  6. package/dist/src/api/SignalClient.d.ts.map +1 -1
  7. package/dist/src/connectionHelper/ConnectionCheck.d.ts +2 -2
  8. package/dist/src/connectionHelper/ConnectionCheck.d.ts.map +1 -1
  9. package/dist/src/proto/livekit_models.d.ts +20 -3
  10. package/dist/src/proto/livekit_models.d.ts.map +1 -1
  11. package/dist/src/proto/livekit_rtc.d.ts +2565 -464
  12. package/dist/src/proto/livekit_rtc.d.ts.map +1 -1
  13. package/dist/src/room/RTCEngine.d.ts.map +1 -1
  14. package/dist/src/room/Room.d.ts +1 -1
  15. package/dist/src/room/Room.d.ts.map +1 -1
  16. package/dist/src/room/errors.d.ts +3 -0
  17. package/dist/src/room/errors.d.ts.map +1 -1
  18. package/dist/src/room/participant/LocalParticipant.d.ts +1 -3
  19. package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
  20. package/dist/src/room/participant/Participant.d.ts +1 -1
  21. package/dist/src/room/participant/Participant.d.ts.map +1 -1
  22. package/dist/src/room/track/LocalAudioTrack.d.ts.map +1 -1
  23. package/dist/src/room/track/create.d.ts.map +1 -1
  24. package/dist/src/room/track/options.d.ts +2 -2
  25. package/dist/src/room/track/options.d.ts.map +1 -1
  26. package/dist/src/test/mocks.d.ts +1 -1
  27. package/dist/ts4.2/src/api/SignalClient.d.ts +13 -2
  28. package/dist/ts4.2/src/connectionHelper/ConnectionCheck.d.ts +2 -2
  29. package/dist/ts4.2/src/proto/livekit_models.d.ts +24 -3
  30. package/dist/ts4.2/src/proto/livekit_rtc.d.ts +2775 -540
  31. package/dist/ts4.2/src/room/Room.d.ts +1 -1
  32. package/dist/ts4.2/src/room/errors.d.ts +3 -0
  33. package/dist/ts4.2/src/room/participant/LocalParticipant.d.ts +1 -3
  34. package/dist/ts4.2/src/room/participant/Participant.d.ts +1 -1
  35. package/dist/ts4.2/src/room/track/options.d.ts +2 -2
  36. package/dist/ts4.2/src/test/mocks.d.ts +1 -1
  37. package/package.json +17 -17
  38. package/src/api/SignalClient.ts +46 -3
  39. package/src/connectionHelper/ConnectionCheck.ts +1 -1
  40. package/src/proto/google/protobuf/timestamp.ts +2 -2
  41. package/src/proto/livekit_models.ts +93 -9
  42. package/src/proto/livekit_rtc.ts +308 -6
  43. package/src/room/RTCEngine.ts +37 -16
  44. package/src/room/Room.ts +12 -11
  45. package/src/room/errors.ts +6 -0
  46. package/src/room/participant/LocalParticipant.ts +19 -20
  47. package/src/room/participant/Participant.ts +10 -2
  48. package/src/room/track/LocalAudioTrack.ts +5 -1
  49. package/src/room/track/create.ts +6 -1
  50. package/src/room/track/options.ts +2 -2
package/src/room/Room.ts CHANGED
@@ -355,12 +355,16 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
355
355
  } catch (err) {
356
356
  this.recreateEngine();
357
357
  this.handleDisconnect(this.options.stopLocalTrackOnUnpublish);
358
- let errorMessage = '';
358
+ const resultingError = new ConnectionError(`could not establish signal connection`);
359
359
  if (err instanceof Error) {
360
- errorMessage = err.message;
361
- log.debug(`error trying to establish signal connection`, { error: err });
360
+ resultingError.message = `${resultingError.message}: ${err.message}`;
362
361
  }
363
- reject(new ConnectionError(`could not establish signal connection: ${errorMessage}`));
362
+ if (err instanceof ConnectionError) {
363
+ resultingError.reason = err.reason;
364
+ resultingError.status = err.status;
365
+ }
366
+ log.debug(`error trying to establish signal connection`, { error: err });
367
+ reject(resultingError);
364
368
  return;
365
369
  }
366
370
 
@@ -843,10 +847,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
843
847
  private handleParticipantUpdates = (participantInfos: ParticipantInfo[]) => {
844
848
  // handle changes to participant state, and send events
845
849
  participantInfos.forEach((info) => {
846
- if (
847
- info.sid === this.localParticipant.sid ||
848
- info.identity === this.localParticipant.identity
849
- ) {
850
+ if (info.identity === this.localParticipant.identity) {
850
851
  this.localParticipant.updateInfo(info);
851
852
  return;
852
853
  }
@@ -1138,7 +1139,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
1138
1139
  })
1139
1140
  .on(
1140
1141
  ParticipantEvent.ParticipantPermissionsChanged,
1141
- (prevPermissions: ParticipantPermission) => {
1142
+ (prevPermissions?: ParticipantPermission) => {
1142
1143
  this.emitWhenConnected(
1143
1144
  RoomEvent.ParticipantPermissionsChanged,
1144
1145
  prevPermissions,
@@ -1273,7 +1274,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
1273
1274
  this.emit(RoomEvent.MediaDevicesError, e);
1274
1275
  };
1275
1276
 
1276
- private onLocalParticipantPermissionsChanged = (prevPermissions: ParticipantPermission) => {
1277
+ private onLocalParticipantPermissionsChanged = (prevPermissions?: ParticipantPermission) => {
1277
1278
  this.emit(RoomEvent.ParticipantPermissionsChanged, prevPermissions, this.localParticipant);
1278
1279
  };
1279
1280
 
@@ -1442,7 +1443,7 @@ export type RoomEventCallbacks = {
1442
1443
  participant: RemoteParticipant | LocalParticipant,
1443
1444
  ) => void;
1444
1445
  participantPermissionsChanged: (
1445
- prevPermissions: ParticipantPermission,
1446
+ prevPermissions: ParticipantPermission | undefined,
1446
1447
  participant: RemoteParticipant | LocalParticipant,
1447
1448
  ) => void;
1448
1449
  activeSpeakersChanged: (speakers: Array<Participant>) => void;
@@ -25,6 +25,12 @@ export class ConnectionError extends LivekitError {
25
25
  }
26
26
  }
27
27
 
28
+ export class DeviceUnsupportedError extends LivekitError {
29
+ constructor(message?: string) {
30
+ super(21, message ?? 'device is unsupported');
31
+ }
32
+ }
33
+
28
34
  export class TrackInvalidError extends LivekitError {
29
35
  constructor(message?: string) {
30
36
  super(20, message ?? 'track is invalid');
@@ -1,12 +1,7 @@
1
1
  import 'webrtc-adapter';
2
2
  import log from '../../logger';
3
3
  import type { InternalRoomOptions } from '../../options';
4
- import {
5
- DataPacket,
6
- DataPacket_Kind,
7
- ParticipantInfo,
8
- ParticipantPermission,
9
- } from '../../proto/livekit_models';
4
+ import { DataPacket, DataPacket_Kind, ParticipantInfo } from '../../proto/livekit_models';
10
5
  import {
11
6
  AddTrackRequest,
12
7
  DataChannelInfo,
@@ -15,7 +10,7 @@ import {
15
10
  TrackPublishedResponse,
16
11
  TrackUnpublishedResponse,
17
12
  } from '../../proto/livekit_rtc';
18
- import { TrackInvalidError, UnexpectedConnectionState } from '../errors';
13
+ import { DeviceUnsupportedError, TrackInvalidError, UnexpectedConnectionState } from '../errors';
19
14
  import { EngineEvent, ParticipantEvent, TrackEvent } from '../events';
20
15
  import type RTCEngine from '../RTCEngine';
21
16
  import LocalAudioTrack from '../track/LocalAudioTrack';
@@ -168,16 +163,6 @@ export default class LocalParticipant extends Participant {
168
163
  return this.setTrackEnabled(Track.Source.ScreenShare, enabled, options, publishOptions);
169
164
  }
170
165
 
171
- /** @internal */
172
- setPermissions(permissions: ParticipantPermission): boolean {
173
- const prevPermissions = this.permissions;
174
- const changed = super.setPermissions(permissions);
175
- if (changed && prevPermissions) {
176
- this.emit(ParticipantEvent.ParticipantPermissionsChanged, prevPermissions);
177
- }
178
- return changed;
179
- }
180
-
181
166
  /**
182
167
  * Enable or disable publishing for a track by source. This serves as a simple
183
168
  * way to manage the common tracks (camera, mic, or screen share).
@@ -389,6 +374,11 @@ export default class LocalParticipant extends Participant {
389
374
  };
390
375
  }
391
376
  }
377
+
378
+ if (navigator.mediaDevices.getDisplayMedia === undefined) {
379
+ throw new DeviceUnsupportedError('getDisplayMedia not supported');
380
+ }
381
+
392
382
  const stream: MediaStream = await navigator.mediaDevices.getDisplayMedia({
393
383
  audio: options.audio ?? false,
394
384
  video: videoConstraints,
@@ -876,6 +866,11 @@ export default class LocalParticipant extends Participant {
876
866
 
877
867
  /** @internal */
878
868
  updateInfo(info: ParticipantInfo) {
869
+ if (info.sid !== this.sid) {
870
+ // drop updates that specify a wrong sid.
871
+ // the sid for local participant is only explicitly set on join and full reconnect
872
+ return;
873
+ }
879
874
  super.updateInfo(info);
880
875
 
881
876
  // reconcile track mute status.
@@ -1004,7 +999,9 @@ export default class LocalParticipant extends Participant {
1004
999
  // detect granted change after permissions were denied to try and resume then
1005
1000
  currentPermissions.onchange = () => {
1006
1001
  if (currentPermissions.state !== 'denied') {
1007
- track.restartTrack();
1002
+ if (!track.isMuted) {
1003
+ track.restartTrack();
1004
+ }
1008
1005
  currentPermissions.onchange = null;
1009
1006
  }
1010
1007
  };
@@ -1014,8 +1011,10 @@ export default class LocalParticipant extends Participant {
1014
1011
  // permissions query fails for firefox, we continue and try to restart the track
1015
1012
  }
1016
1013
  }
1017
- log.debug('track ended, attempting to use a different device');
1018
- await track.restartTrack();
1014
+ if (!track.isMuted) {
1015
+ log.debug('track ended, attempting to use a different device');
1016
+ await track.restartTrack();
1017
+ }
1019
1018
  } catch (e) {
1020
1019
  log.warn(`could not restart track, muting instead`);
1021
1020
  await track.mute();
@@ -173,14 +173,22 @@ export default class Participant extends (EventEmitter as new () => TypedEmitter
173
173
 
174
174
  /** @internal */
175
175
  setPermissions(permissions: ParticipantPermission): boolean {
176
+ const prevPermissions = this.permissions;
176
177
  const changed =
177
178
  permissions.canPublish !== this.permissions?.canPublish ||
178
179
  permissions.canSubscribe !== this.permissions?.canSubscribe ||
179
180
  permissions.canPublishData !== this.permissions?.canPublishData ||
180
181
  permissions.hidden !== this.permissions?.hidden ||
181
- permissions.recorder !== this.permissions?.recorder;
182
+ permissions.recorder !== this.permissions?.recorder ||
183
+ permissions.canPublishSources.length !== this.permissions.canPublishSources.length ||
184
+ permissions.canPublishSources.some(
185
+ (value, index) => value !== this.permissions?.canPublishSources[index],
186
+ );
182
187
  this.permissions = permissions;
183
188
 
189
+ if (changed) {
190
+ this.emit(ParticipantEvent.ParticipantPermissionsChanged, prevPermissions);
191
+ }
184
192
  return changed;
185
193
  }
186
194
 
@@ -257,7 +265,7 @@ export type ParticipantEventCallbacks = {
257
265
  status: TrackPublication.PermissionStatus,
258
266
  ) => void;
259
267
  mediaDevicesError: (error: Error) => void;
260
- participantPermissionsChanged: (prevPermissions: ParticipantPermission) => void;
268
+ participantPermissionsChanged: (prevPermissions?: ParticipantPermission) => void;
261
269
  trackSubscriptionStatusChanged: (
262
270
  publication: RemoteTrackPublication,
263
271
  status: TrackPublication.SubscriptionStatus,
@@ -53,7 +53,11 @@ export default class LocalAudioTrack extends LocalTrack {
53
53
 
54
54
  async unmute(): Promise<LocalAudioTrack> {
55
55
  await this.muteQueue.run(async () => {
56
- if (this.source === Track.Source.Microphone && this.stopOnMute && !this.isUserProvided) {
56
+ if (
57
+ this.source === Track.Source.Microphone &&
58
+ (this.stopOnMute || this._mediaStreamTrack.readyState === 'ended') &&
59
+ !this.isUserProvided
60
+ ) {
57
61
  log.debug('reacquiring mic track');
58
62
  await this.restartTrack();
59
63
  }
@@ -1,5 +1,5 @@
1
1
  import DeviceManager from '../DeviceManager';
2
- import { TrackInvalidError } from '../errors';
2
+ import { DeviceUnsupportedError, TrackInvalidError } from '../errors';
3
3
  import { mediaTrackToLocalTrack } from '../participant/publishUtils';
4
4
  import { audioDefaults, videoDefaults } from '../defaults';
5
5
  import LocalAudioTrack from './LocalAudioTrack';
@@ -114,6 +114,11 @@ export async function createLocalScreenTracks(
114
114
  height: options.resolution.height,
115
115
  };
116
116
  }
117
+
118
+ if (navigator.mediaDevices.getDisplayMedia === undefined) {
119
+ throw new DeviceUnsupportedError('getDisplayMedia not supported');
120
+ }
121
+
117
122
  // typescript definition is missing getDisplayMedia: https://github.com/microsoft/TypeScript/issues/33232
118
123
  // @ts-ignore
119
124
  const stream: MediaStream = await navigator.mediaDevices.getDisplayMedia({
@@ -232,9 +232,9 @@ export interface AudioPreset {
232
232
  const codecs = ['vp8', 'h264', 'av1'] as const;
233
233
  const backupCodecs = ['vp8', 'h264'] as const;
234
234
 
235
- export type VideoCodec = typeof codecs[number];
235
+ export type VideoCodec = (typeof codecs)[number];
236
236
 
237
- export type BackupVideoCodec = typeof backupCodecs[number];
237
+ export type BackupVideoCodec = (typeof backupCodecs)[number];
238
238
 
239
239
  export function isBackupCodec(codec: string): codec is BackupVideoCodec {
240
240
  return !!backupCodecs.find((backup) => backup === codec);