livekit-client 1.8.0 → 1.9.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. package/README.md +9 -7
  2. package/dist/livekit-client.esm.mjs +13550 -13342
  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 +11 -10
  7. package/dist/src/api/SignalClient.d.ts.map +1 -1
  8. package/dist/src/connectionHelper/ConnectionCheck.d.ts +1 -1
  9. package/dist/src/connectionHelper/ConnectionCheck.d.ts.map +1 -1
  10. package/dist/src/connectionHelper/checks/Checker.d.ts +1 -1
  11. package/dist/src/connectionHelper/checks/Checker.d.ts.map +1 -1
  12. package/dist/src/index.d.ts +5 -7
  13. package/dist/src/index.d.ts.map +1 -1
  14. package/dist/src/proto/livekit_models.d.ts +5 -0
  15. package/dist/src/proto/livekit_models.d.ts.map +1 -1
  16. package/dist/src/proto/livekit_rtc.d.ts +32 -0
  17. package/dist/src/proto/livekit_rtc.d.ts.map +1 -1
  18. package/dist/src/room/PCTransport.d.ts.map +1 -1
  19. package/dist/src/room/RTCEngine.d.ts +5 -3
  20. package/dist/src/room/RTCEngine.d.ts.map +1 -1
  21. package/dist/src/room/Room.d.ts +20 -15
  22. package/dist/src/room/Room.d.ts.map +1 -1
  23. package/dist/src/room/events.d.ts +15 -0
  24. package/dist/src/room/events.d.ts.map +1 -1
  25. package/dist/src/room/participant/LocalParticipant.d.ts +14 -2
  26. package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
  27. package/dist/src/room/participant/Participant.d.ts +4 -2
  28. package/dist/src/room/participant/Participant.d.ts.map +1 -1
  29. package/dist/src/room/participant/RemoteParticipant.d.ts +2 -2
  30. package/dist/src/room/participant/RemoteParticipant.d.ts.map +1 -1
  31. package/dist/src/room/participant/publishUtils.d.ts.map +1 -1
  32. package/dist/src/room/track/LocalAudioTrack.d.ts.map +1 -1
  33. package/dist/src/room/track/LocalTrack.d.ts +1 -1
  34. package/dist/src/room/track/LocalTrack.d.ts.map +1 -1
  35. package/dist/src/room/track/LocalTrackPublication.d.ts +1 -1
  36. package/dist/src/room/track/LocalTrackPublication.d.ts.map +1 -1
  37. package/dist/src/room/track/LocalVideoTrack.d.ts.map +1 -1
  38. package/dist/src/room/track/RemoteAudioTrack.d.ts +1 -1
  39. package/dist/src/room/track/RemoteAudioTrack.d.ts.map +1 -1
  40. package/dist/src/room/track/create.d.ts.map +1 -1
  41. package/dist/src/room/track/options.d.ts +7 -1
  42. package/dist/src/room/track/options.d.ts.map +1 -1
  43. package/dist/src/room/types.d.ts +1 -0
  44. package/dist/src/room/types.d.ts.map +1 -1
  45. package/dist/src/room/utils.d.ts +3 -0
  46. package/dist/src/room/utils.d.ts.map +1 -1
  47. package/dist/ts4.2/src/api/SignalClient.d.ts +14 -10
  48. package/dist/ts4.2/src/connectionHelper/ConnectionCheck.d.ts +1 -1
  49. package/dist/ts4.2/src/connectionHelper/checks/Checker.d.ts +1 -1
  50. package/dist/ts4.2/src/index.d.ts +6 -7
  51. package/dist/ts4.2/src/proto/livekit_models.d.ts +5 -0
  52. package/dist/ts4.2/src/proto/livekit_rtc.d.ts +32 -0
  53. package/dist/ts4.2/src/room/RTCEngine.d.ts +5 -3
  54. package/dist/ts4.2/src/room/Room.d.ts +20 -15
  55. package/dist/ts4.2/src/room/events.d.ts +15 -0
  56. package/dist/ts4.2/src/room/participant/LocalParticipant.d.ts +14 -2
  57. package/dist/ts4.2/src/room/participant/Participant.d.ts +4 -2
  58. package/dist/ts4.2/src/room/participant/RemoteParticipant.d.ts +2 -2
  59. package/dist/ts4.2/src/room/track/LocalTrack.d.ts +1 -1
  60. package/dist/ts4.2/src/room/track/LocalTrackPublication.d.ts +1 -1
  61. package/dist/ts4.2/src/room/track/RemoteAudioTrack.d.ts +1 -1
  62. package/dist/ts4.2/src/room/track/options.d.ts +7 -0
  63. package/dist/ts4.2/src/room/types.d.ts +1 -0
  64. package/dist/ts4.2/src/room/utils.d.ts +3 -0
  65. package/package.json +4 -3
  66. package/src/api/SignalClient.ts +38 -26
  67. package/src/connectionHelper/ConnectionCheck.ts +1 -2
  68. package/src/connectionHelper/checks/Checker.ts +1 -1
  69. package/src/connectionHelper/checks/reconnect.ts +1 -1
  70. package/src/index.ts +8 -10
  71. package/src/proto/livekit_models.ts +15 -0
  72. package/src/room/PCTransport.ts +40 -1
  73. package/src/room/RTCEngine.ts +32 -11
  74. package/src/room/RegionUrlProvider.ts +1 -1
  75. package/src/room/Room.ts +114 -70
  76. package/src/room/events.ts +17 -0
  77. package/src/room/participant/LocalParticipant.ts +65 -11
  78. package/src/room/participant/Participant.ts +27 -3
  79. package/src/room/participant/RemoteParticipant.ts +6 -3
  80. package/src/room/participant/publishUtils.test.ts +1 -1
  81. package/src/room/participant/publishUtils.ts +14 -6
  82. package/src/room/track/LocalAudioTrack.ts +1 -1
  83. package/src/room/track/LocalTrack.ts +2 -2
  84. package/src/room/track/LocalTrackPublication.ts +1 -1
  85. package/src/room/track/LocalVideoTrack.ts +3 -3
  86. package/src/room/track/RemoteAudioTrack.ts +1 -1
  87. package/src/room/track/RemoteVideoTrack.test.ts +1 -1
  88. package/src/room/track/RemoteVideoTrack.ts +3 -3
  89. package/src/room/track/create.ts +2 -2
  90. package/src/room/track/options.ts +14 -1
  91. package/src/room/types.ts +11 -0
  92. package/src/room/utils.ts +18 -8
@@ -2,10 +2,10 @@ import { EventEmitter } from 'events';
2
2
  import type TypedEmitter from 'typed-emitter';
3
3
  import log from '../../logger';
4
4
  import {
5
- ConnectionQuality as ProtoQuality,
6
5
  DataPacket_Kind,
7
6
  ParticipantInfo,
8
7
  ParticipantPermission,
8
+ ConnectionQuality as ProtoQuality,
9
9
  } from '../../proto/livekit_models';
10
10
  import { ParticipantEvent, TrackEvent } from '../events';
11
11
  import type LocalTrackPublication from '../track/LocalTrackPublication';
@@ -144,10 +144,23 @@ export default class Participant extends (EventEmitter as new () => TypedEmitter
144
144
  }
145
145
 
146
146
  /** @internal */
147
- updateInfo(info: ParticipantInfo) {
147
+ updateInfo(info: ParticipantInfo): boolean {
148
+ // it's possible the update could be applied out of order due to await
149
+ // during reconnect sequences. when that happens, it's possible for server
150
+ // to have sent more recent version of participant info while JS is waiting
151
+ // to process the existing payload.
152
+ // when the participant sid remains the same, and we already have a later version
153
+ // of the payload, they can be safely skipped
154
+ if (
155
+ this.participantInfo &&
156
+ this.participantInfo.sid === info.sid &&
157
+ this.participantInfo.version > info.version
158
+ ) {
159
+ return false;
160
+ }
148
161
  this.identity = info.identity;
149
162
  this.sid = info.sid;
150
- this.name = info.name;
163
+ this.setName(info.name);
151
164
  this.setMetadata(info.metadata);
152
165
  if (info.permission) {
153
166
  this.setPermissions(info.permission);
@@ -155,6 +168,7 @@ export default class Participant extends (EventEmitter as new () => TypedEmitter
155
168
  // set this last so setMetadata can detect changes
156
169
  this.participantInfo = info;
157
170
  log.trace('update participant info', { info });
171
+ return true;
158
172
  }
159
173
 
160
174
  /** @internal */
@@ -168,6 +182,15 @@ export default class Participant extends (EventEmitter as new () => TypedEmitter
168
182
  }
169
183
  }
170
184
 
185
+ protected setName(name: string) {
186
+ const changed = this.name !== name;
187
+ this.name = name;
188
+
189
+ if (changed) {
190
+ this.emit(ParticipantEvent.ParticipantNameChanged, name);
191
+ }
192
+ }
193
+
171
194
  /** @internal */
172
195
  setPermissions(permissions: ParticipantPermission): boolean {
173
196
  const prevPermissions = this.permissions;
@@ -250,6 +273,7 @@ export type ParticipantEventCallbacks = {
250
273
  localTrackPublished: (publication: LocalTrackPublication) => void;
251
274
  localTrackUnpublished: (publication: LocalTrackPublication) => void;
252
275
  participantMetadataChanged: (prevMetadata: string | undefined, participant?: any) => void;
276
+ participantNameChanged: (name: string) => void;
253
277
  dataReceived: (payload: Uint8Array, kind: DataPacket_Kind) => void;
254
278
  isSpeakingChanged: (speaking: boolean) => void;
255
279
  connectionQualityChanged: (connectionQuality: ConnectionQuality) => void;
@@ -3,13 +3,13 @@ import log from '../../logger';
3
3
  import type { ParticipantInfo } from '../../proto/livekit_models';
4
4
  import type { UpdateSubscription, UpdateTrackSettings } from '../../proto/livekit_rtc';
5
5
  import { ParticipantEvent, TrackEvent } from '../events';
6
- import type { AudioOutputOptions } from '../track/options';
7
6
  import RemoteAudioTrack from '../track/RemoteAudioTrack';
8
7
  import type RemoteTrack from '../track/RemoteTrack';
9
8
  import RemoteTrackPublication from '../track/RemoteTrackPublication';
10
9
  import RemoteVideoTrack from '../track/RemoteVideoTrack';
11
10
  import { Track } from '../track/Track';
12
11
  import type { TrackPublication } from '../track/TrackPublication';
12
+ import type { AudioOutputOptions } from '../track/options';
13
13
  import type { AdaptiveStreamSettings } from '../track/types';
14
14
  import Participant, { ParticipantEventCallbacks } from './Participant';
15
15
 
@@ -215,8 +215,10 @@ export default class RemoteParticipant extends Participant {
215
215
  }
216
216
 
217
217
  /** @internal */
218
- updateInfo(info: ParticipantInfo) {
219
- super.updateInfo(info);
218
+ updateInfo(info: ParticipantInfo): boolean {
219
+ if (!super.updateInfo(info)) {
220
+ return false;
221
+ }
220
222
 
221
223
  // we are getting a list of all available tracks, reconcile in here
222
224
  // and send out events for changes
@@ -277,6 +279,7 @@ export default class RemoteParticipant extends Participant {
277
279
  newTracks.forEach((publication) => {
278
280
  this.emit(ParticipantEvent.TrackPublished, publication);
279
281
  });
282
+ return true;
280
283
  }
281
284
 
282
285
  /** @internal */
@@ -3,8 +3,8 @@ import {
3
3
  computeDefaultScreenShareSimulcastPresets,
4
4
  computeVideoEncodings,
5
5
  determineAppropriateEncoding,
6
- presets169,
7
6
  presets43,
7
+ presets169,
8
8
  presetsForResolution,
9
9
  presetsScreenShare,
10
10
  sortPresets,
@@ -2,6 +2,7 @@ import log from '../../logger';
2
2
  import { TrackInvalidError } from '../errors';
3
3
  import LocalAudioTrack from '../track/LocalAudioTrack';
4
4
  import LocalVideoTrack from '../track/LocalVideoTrack';
5
+ import { Track } from '../track/Track';
5
6
  import {
6
7
  BackupVideoCodec,
7
8
  ScreenSharePresets,
@@ -12,7 +13,7 @@ import {
12
13
  VideoPresets,
13
14
  VideoPresets43,
14
15
  } from '../track/options';
15
- import { Track } from '../track/Track';
16
+ import { isSVCCodec } from '../utils';
16
17
 
17
18
  /** @internal */
18
19
  export function mediaTrackToLocalTrack(
@@ -121,7 +122,7 @@ export function computeVideoEncodings(
121
122
  videoEncoding.maxFramerate,
122
123
  );
123
124
 
124
- if (scalabilityMode && videoCodec === 'av1') {
125
+ if (scalabilityMode && isSVCCodec(videoCodec)) {
125
126
  log.debug(`using svc with scalabilityMode ${scalabilityMode}`);
126
127
 
127
128
  const encodings: RTCRtpEncodingParameters[] = [];
@@ -248,8 +249,13 @@ export function determineAppropriateEncoding(
248
249
  if (codec) {
249
250
  switch (codec) {
250
251
  case 'av1':
252
+ encoding = { ...encoding };
251
253
  encoding.maxBitrate = encoding.maxBitrate * 0.7;
252
254
  break;
255
+ case 'vp9':
256
+ encoding = { ...encoding };
257
+ encoding.maxBitrate = encoding.maxBitrate * 0.85;
258
+ break;
253
259
  default:
254
260
  break;
255
261
  }
@@ -303,13 +309,15 @@ function encodingsFromPresets(
303
309
  }
304
310
  const size = Math.min(width, height);
305
311
  const rid = videoRids[idx];
306
- encodings.push({
312
+ const encoding: RTCRtpEncodingParameters = {
307
313
  rid,
308
314
  scaleResolutionDownBy: Math.max(1, size / Math.min(preset.width, preset.height)),
309
315
  maxBitrate: preset.encoding.maxBitrate,
310
- /* @ts-ignore */
311
- maxFramerate: preset.encoding.maxFramerate,
312
- });
316
+ };
317
+ if (preset.encoding.maxFramerate) {
318
+ encoding.maxFramerate = preset.encoding.maxFramerate;
319
+ }
320
+ encodings.push(encoding);
313
321
  });
314
322
  return encodings;
315
323
  }
@@ -3,8 +3,8 @@ import { TrackEvent } from '../events';
3
3
  import { AudioSenderStats, computeBitrate, monitorFrequency } from '../stats';
4
4
  import { isWeb } from '../utils';
5
5
  import LocalTrack from './LocalTrack';
6
- import type { AudioCaptureOptions } from './options';
7
6
  import { Track } from './Track';
7
+ import type { AudioCaptureOptions } from './options';
8
8
  import { constraintsForOptions, detectSilence } from './utils';
9
9
 
10
10
  export default class LocalAudioTrack extends LocalTrack {
@@ -3,14 +3,14 @@ import DeviceManager from '../DeviceManager';
3
3
  import { TrackInvalidError } from '../errors';
4
4
  import { TrackEvent } from '../events';
5
5
  import {
6
+ Mutex,
6
7
  getEmptyAudioStreamTrack,
7
8
  getEmptyVideoStreamTrack,
8
9
  isMobile,
9
- Mutex,
10
10
  sleep,
11
11
  } from '../utils';
12
+ import { Track, attachToElement, detachTrack } from './Track';
12
13
  import type { VideoCodec } from './options';
13
- import { attachToElement, detachTrack, Track } from './Track';
14
14
 
15
15
  const defaultDimensionsTimeout = 2 * 1000;
16
16
 
@@ -3,9 +3,9 @@ import { TrackEvent } from '../events';
3
3
  import type LocalAudioTrack from './LocalAudioTrack';
4
4
  import type LocalTrack from './LocalTrack';
5
5
  import type LocalVideoTrack from './LocalVideoTrack';
6
- import type { TrackPublishOptions } from './options';
7
6
  import type { Track } from './Track';
8
7
  import { TrackPublication } from './TrackPublication';
8
+ import type { TrackPublishOptions } from './options';
9
9
 
10
10
  export default class LocalTrackPublication extends TrackPublication {
11
11
  track?: LocalTrack = undefined;
@@ -2,11 +2,11 @@ import type { SignalClient } from '../../api/SignalClient';
2
2
  import log from '../../logger';
3
3
  import { VideoLayer, VideoQuality } from '../../proto/livekit_models';
4
4
  import type { SubscribedCodec, SubscribedQuality } from '../../proto/livekit_rtc';
5
- import { computeBitrate, monitorFrequency, VideoSenderStats } from '../stats';
6
- import { isFireFox, isMobile, isWeb, Mutex } from '../utils';
5
+ import { VideoSenderStats, computeBitrate, monitorFrequency } from '../stats';
6
+ import { Mutex, isFireFox, isMobile, isWeb } from '../utils';
7
7
  import LocalTrack from './LocalTrack';
8
- import type { VideoCaptureOptions, VideoCodec } from './options';
9
8
  import { Track } from './Track';
9
+ import type { VideoCaptureOptions, VideoCodec } from './options';
10
10
  import { constraintsForOptions } from './utils';
11
11
 
12
12
  export class SimulcastTrackInfo {
@@ -2,9 +2,9 @@ import log from '../../logger';
2
2
  import { TrackEvent } from '../events';
3
3
  import { AudioReceiverStats, computeBitrate } from '../stats';
4
4
  import { supportsSetSinkId } from '../utils';
5
- import type { AudioOutputOptions } from './options';
6
5
  import RemoteTrack from './RemoteTrack';
7
6
  import { Track } from './Track';
7
+ import type { AudioOutputOptions } from './options';
8
8
 
9
9
  export default class RemoteAudioTrack extends RemoteTrack {
10
10
  private prevStats?: AudioReceiverStats;
@@ -1,6 +1,6 @@
1
+ import MockMediaStreamTrack from '../../test/MockMediaStreamTrack';
1
2
  import { TrackEvent } from '../events';
2
3
  import RemoteVideoTrack, { ElementInfo } from './RemoteVideoTrack';
3
- import MockMediaStreamTrack from '../../test/MockMediaStreamTrack';
4
4
  import type { Track } from './Track';
5
5
 
6
6
  jest.useFakeTimers();
@@ -1,17 +1,17 @@
1
1
  import { debounce } from 'ts-debounce';
2
2
  import log from '../../logger';
3
3
  import { TrackEvent } from '../events';
4
- import { computeBitrate, VideoReceiverStats } from '../stats';
4
+ import { VideoReceiverStats, computeBitrate } from '../stats';
5
5
  import CriticalTimers from '../timers';
6
6
  import {
7
+ ObservableMediaElement,
7
8
  getDevicePixelRatio,
8
9
  getIntersectionObserver,
9
10
  getResizeObserver,
10
11
  isWeb,
11
- ObservableMediaElement,
12
12
  } from '../utils';
13
13
  import RemoteTrack from './RemoteTrack';
14
- import { attachToElement, detachTrack, Track } from './Track';
14
+ import { Track, attachToElement, detachTrack } from './Track';
15
15
  import type { AdaptiveStreamSettings } from './types';
16
16
 
17
17
  const REACTION_DELAY = 100;
@@ -1,10 +1,11 @@
1
1
  import DeviceManager from '../DeviceManager';
2
+ import { audioDefaults, videoDefaults } from '../defaults';
2
3
  import { DeviceUnsupportedError, TrackInvalidError } from '../errors';
3
4
  import { mediaTrackToLocalTrack } from '../participant/publishUtils';
4
- import { audioDefaults, videoDefaults } from '../defaults';
5
5
  import LocalAudioTrack from './LocalAudioTrack';
6
6
  import type LocalTrack from './LocalTrack';
7
7
  import LocalVideoTrack from './LocalVideoTrack';
8
+ import { Track } from './Track';
8
9
  import {
9
10
  AudioCaptureOptions,
10
11
  CreateLocalTracksOptions,
@@ -12,7 +13,6 @@ import {
12
13
  VideoCaptureOptions,
13
14
  VideoPresets,
14
15
  } from './options';
15
- import { Track } from './Track';
16
16
  import { constraintsForOptions, mergeDefaultOptions } from './utils';
17
17
 
18
18
  /**
@@ -145,6 +145,12 @@ export interface ScreenShareCaptureOptions {
145
145
 
146
146
  /** specifies whether the browser should include the system audio among the possible audio sources offered to the user */
147
147
  systemAudio?: 'include' | 'exclude';
148
+
149
+ /**
150
+ * Experimental option to control whether the audio playing in a tab will continue to be played out of a user's
151
+ * local speakers when the tab is captured.
152
+ */
153
+ suppressLocalAudioPlayback?: boolean;
148
154
  }
149
155
 
150
156
  export interface AudioCaptureOptions {
@@ -241,7 +247,7 @@ export interface AudioPreset {
241
247
  maxBitrate: number;
242
248
  }
243
249
 
244
- const codecs = ['vp8', 'h264', 'av1'] as const;
250
+ const codecs = ['vp8', 'h264', 'vp9', 'av1'] as const;
245
251
  const backupCodecs = ['vp8', 'h264'] as const;
246
252
 
247
253
  export type VideoCodec = (typeof codecs)[number];
@@ -252,6 +258,13 @@ export function isBackupCodec(codec: string): codec is BackupVideoCodec {
252
258
  return !!backupCodecs.find((backup) => backup === codec);
253
259
  }
254
260
 
261
+ export function isCodecEqual(c1: string | undefined, c2: string | undefined): boolean {
262
+ return (
263
+ c1?.toLowerCase().replace(/audio\/|video\//y, '') ===
264
+ c2?.toLowerCase().replace(/audio\/|video\//y, '')
265
+ );
266
+ }
267
+
255
268
  /**
256
269
  * scalability modes for svc, only supprot l3t3 now.
257
270
  */
package/src/room/types.ts CHANGED
@@ -26,3 +26,14 @@ export type LiveKitReactNativeInfo = {
26
26
  platform: 'ios' | 'android' | 'windows' | 'macos' | 'web' | 'native';
27
27
  devicePixelRatio: number;
28
28
  };
29
+
30
+ export type SimulationScenario =
31
+ | 'signal-reconnect'
32
+ | 'speaker'
33
+ | 'node-failure'
34
+ | 'server-leave'
35
+ | 'migration'
36
+ | 'resume-reconnect'
37
+ | 'force-tcp'
38
+ | 'force-tls'
39
+ | 'full-reconnect';
package/src/room/utils.ts CHANGED
@@ -7,6 +7,8 @@ import { getNewAudioContext } from './track/utils';
7
7
  import type { LiveKitReactNativeInfo } from './types';
8
8
 
9
9
  const separator = '|';
10
+ export const ddExtensionURI =
11
+ 'https://aomediacodec.github.io/av1-rtp-spec/#dependency-descriptor-rtp-header-extension';
10
12
 
11
13
  export function unpackStreamId(packed: string): string[] {
12
14
  const parts = packed.split(separator);
@@ -41,7 +43,6 @@ export function supportsDynacast() {
41
43
  export function supportsAV1(): boolean {
42
44
  const capabilities = RTCRtpReceiver.getCapabilities('video');
43
45
  let hasAV1 = false;
44
- let hasDDExt = false;
45
46
  if (capabilities) {
46
47
  for (const codec of capabilities.codecs) {
47
48
  if (codec.mimeType === 'video/AV1') {
@@ -49,17 +50,26 @@ export function supportsAV1(): boolean {
49
50
  break;
50
51
  }
51
52
  }
52
- for (const ext of capabilities.headerExtensions) {
53
- if (
54
- ext.uri ===
55
- 'https://aomediacodec.github.io/av1-rtp-spec/#dependency-descriptor-rtp-header-extension'
56
- ) {
57
- hasDDExt = true;
53
+ }
54
+ return hasAV1;
55
+ }
56
+
57
+ export function supportsVP9(): boolean {
58
+ const capabilities = RTCRtpReceiver.getCapabilities('video');
59
+ let hasVP9 = false;
60
+ if (capabilities) {
61
+ for (const codec of capabilities.codecs) {
62
+ if (codec.mimeType === 'video/VP9') {
63
+ hasVP9 = true;
58
64
  break;
59
65
  }
60
66
  }
61
67
  }
62
- return hasAV1 && hasDDExt;
68
+ return hasVP9;
69
+ }
70
+
71
+ export function isSVCCodec(codec?: string): boolean {
72
+ return codec === 'av1' || codec === 'vp9';
63
73
  }
64
74
 
65
75
  export function supportsSetSinkId(elm?: HTMLMediaElement): boolean {