livekit-client 1.6.6 → 1.6.8

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 (57) hide show
  1. package/dist/livekit-client.esm.mjs +508 -317
  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/proto/google/protobuf/timestamp.d.ts +9 -2
  6. package/dist/src/proto/google/protobuf/timestamp.d.ts.map +1 -1
  7. package/dist/src/proto/livekit_models.d.ts +958 -58
  8. package/dist/src/proto/livekit_models.d.ts.map +1 -1
  9. package/dist/src/proto/livekit_rtc.d.ts +8394 -3725
  10. package/dist/src/proto/livekit_rtc.d.ts.map +1 -1
  11. package/dist/src/room/PCTransport.d.ts.map +1 -1
  12. package/dist/src/room/RTCEngine.d.ts +10 -3
  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/events.d.ts +1 -1
  17. package/dist/src/room/participant/LocalParticipant.d.ts +18 -0
  18. package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
  19. package/dist/src/room/participant/RemoteParticipant.d.ts.map +1 -1
  20. package/dist/src/room/track/RemoteTrackPublication.d.ts +1 -1
  21. package/dist/src/room/track/RemoteTrackPublication.d.ts.map +1 -1
  22. package/dist/src/room/track/RemoteVideoTrack.d.ts.map +1 -1
  23. package/dist/src/room/track/Track.d.ts +2 -1
  24. package/dist/src/room/track/Track.d.ts.map +1 -1
  25. package/dist/src/room/track/options.d.ts +10 -2
  26. package/dist/src/room/track/options.d.ts.map +1 -1
  27. package/dist/src/room/types.d.ts +7 -0
  28. package/dist/src/room/types.d.ts.map +1 -1
  29. package/dist/src/version.d.ts +1 -1
  30. package/dist/ts4.2/src/proto/google/protobuf/timestamp.d.ts +11 -2
  31. package/dist/ts4.2/src/proto/livekit_models.d.ts +1199 -177
  32. package/dist/ts4.2/src/proto/livekit_rtc.d.ts +9260 -4023
  33. package/dist/ts4.2/src/room/RTCEngine.d.ts +10 -3
  34. package/dist/ts4.2/src/room/Room.d.ts +1 -1
  35. package/dist/ts4.2/src/room/events.d.ts +1 -1
  36. package/dist/ts4.2/src/room/participant/LocalParticipant.d.ts +18 -0
  37. package/dist/ts4.2/src/room/track/RemoteTrackPublication.d.ts +1 -1
  38. package/dist/ts4.2/src/room/track/Track.d.ts +2 -1
  39. package/dist/ts4.2/src/room/track/options.d.ts +10 -2
  40. package/dist/ts4.2/src/room/types.d.ts +7 -0
  41. package/dist/ts4.2/src/version.d.ts +1 -1
  42. package/package.json +14 -14
  43. package/src/proto/google/protobuf/timestamp.ts +4 -0
  44. package/src/proto/livekit_models.ts +128 -31
  45. package/src/proto/livekit_rtc.ts +262 -161
  46. package/src/room/PCTransport.ts +2 -0
  47. package/src/room/RTCEngine.ts +50 -55
  48. package/src/room/Room.ts +60 -43
  49. package/src/room/events.ts +1 -1
  50. package/src/room/participant/LocalParticipant.ts +118 -28
  51. package/src/room/participant/RemoteParticipant.ts +2 -3
  52. package/src/room/track/RemoteTrackPublication.ts +3 -2
  53. package/src/room/track/RemoteVideoTrack.ts +0 -3
  54. package/src/room/track/Track.ts +2 -1
  55. package/src/room/track/options.ts +14 -2
  56. package/src/room/types.ts +9 -0
  57. package/src/version.ts +1 -1
@@ -29,7 +29,8 @@ import {
29
29
  } from '../track/options';
30
30
  import { Track } from '../track/Track';
31
31
  import { constraintsForOptions, mergeDefaultOptions } from '../track/utils';
32
- import { isFireFox, isSafari, isWeb, supportsAV1 } from '../utils';
32
+ import type { DataPublishOptions } from '../types';
33
+ import { Future, isFireFox, isSafari, isWeb, supportsAV1 } from '../utils';
33
34
  import Participant from './Participant';
34
35
  import { ParticipantTrackPermission, trackPermissionToProto } from './ParticipantTrackPermission';
35
36
  import {
@@ -52,6 +53,8 @@ export default class LocalParticipant extends Participant {
52
53
 
53
54
  private pendingPublishing = new Set<Track.Source>();
54
55
 
56
+ private pendingPublishPromises = new Map<LocalTrack, Promise<LocalTrackPublication>>();
57
+
55
58
  private cameraError: Error | undefined;
56
59
 
57
60
  private microphoneError: Error | undefined;
@@ -63,6 +66,8 @@ export default class LocalParticipant extends Participant {
63
66
  // keep a pointer to room options
64
67
  private roomOptions: InternalRoomOptions;
65
68
 
69
+ private reconnectFuture?: Future<void>;
70
+
66
71
  /** @internal */
67
72
  constructor(sid: string, identity: string, engine: RTCEngine, options: InternalRoomOptions) {
68
73
  super(sid, identity);
@@ -118,11 +123,25 @@ export default class LocalParticipant extends Participant {
118
123
  this.engine.client.onLocalTrackUnpublished = this.handleLocalTrackUnpublished;
119
124
 
120
125
  this.engine
121
- .on(EngineEvent.Connected, this.updateTrackSubscriptionPermissions)
122
- .on(EngineEvent.Restarted, this.updateTrackSubscriptionPermissions)
123
- .on(EngineEvent.Resumed, this.updateTrackSubscriptionPermissions);
126
+ .on(EngineEvent.Connected, this.handleReconnected)
127
+ .on(EngineEvent.Restarted, this.handleReconnected)
128
+ .on(EngineEvent.Resumed, this.handleReconnected)
129
+ .on(EngineEvent.Restarting, this.handleReconnecting)
130
+ .on(EngineEvent.Resuming, this.handleReconnecting);
124
131
  }
125
132
 
133
+ private handleReconnecting = () => {
134
+ if (!this.reconnectFuture) {
135
+ this.reconnectFuture = new Future<void>();
136
+ }
137
+ };
138
+
139
+ private handleReconnected = () => {
140
+ this.reconnectFuture?.resolve?.();
141
+ this.reconnectFuture = undefined;
142
+ this.updateTrackSubscriptionPermissions();
143
+ };
144
+
126
145
  /**
127
146
  * Enable or disable a participant's camera track.
128
147
  *
@@ -382,6 +401,11 @@ export default class LocalParticipant extends Participant {
382
401
  const stream: MediaStream = await navigator.mediaDevices.getDisplayMedia({
383
402
  audio: options.audio ?? false,
384
403
  video: videoConstraints,
404
+ // @ts-expect-error support for experimental display media features
405
+ controller: options.controller,
406
+ selfBrowserSurface: options.selfBrowserSurface,
407
+ surfaceSwitching: options.surfaceSwitching,
408
+ systemAudio: options.systemAudio,
385
409
  });
386
410
 
387
411
  const tracks = stream.getVideoTracks();
@@ -408,6 +432,10 @@ export default class LocalParticipant extends Participant {
408
432
  track: LocalTrack | MediaStreamTrack,
409
433
  options?: TrackPublishOptions,
410
434
  ): Promise<LocalTrackPublication> {
435
+ await this.reconnectFuture?.promise;
436
+ if (track instanceof LocalTrack && this.pendingPublishPromises.has(track)) {
437
+ await this.pendingPublishPromises.get(track);
438
+ }
411
439
  // convert raw media track into audio or video track
412
440
  if (track instanceof MediaStreamTrack) {
413
441
  switch (track.kind) {
@@ -422,6 +450,22 @@ export default class LocalParticipant extends Participant {
422
450
  }
423
451
  }
424
452
 
453
+ // is it already published? if so skip
454
+ let existingPublication: LocalTrackPublication | undefined;
455
+ this.tracks.forEach((publication) => {
456
+ if (!publication.track) {
457
+ return;
458
+ }
459
+ if (publication.track === track) {
460
+ existingPublication = <LocalTrackPublication>publication;
461
+ }
462
+ });
463
+
464
+ if (existingPublication) {
465
+ log.warn('track has already been published, skipping');
466
+ return existingPublication;
467
+ }
468
+
425
469
  const isStereo =
426
470
  options?.forceStereo ||
427
471
  ('channelCount' in track.mediaStreamTrack.getSettings() &&
@@ -439,29 +483,40 @@ export default class LocalParticipant extends Participant {
439
483
  `Opus DTX will be disabled for stereo tracks by default. Enable them explicitly to make it work.`,
440
484
  );
441
485
  }
486
+ if (options.red === undefined) {
487
+ log.info(
488
+ `Opus RED will be disabled for stereo tracks by default. Enable them explicitly to make it work.`,
489
+ );
490
+ }
442
491
  options.dtx ??= false;
492
+ options.red ??= false;
443
493
  }
444
494
  const opts: TrackPublishOptions = {
445
495
  ...this.roomOptions.publishDefaults,
446
496
  ...options,
447
497
  };
448
498
 
449
- // is it already published? if so skip
450
- let existingPublication: LocalTrackPublication | undefined;
451
- this.tracks.forEach((publication) => {
452
- if (!publication.track) {
453
- return;
454
- }
455
- if (publication.track === track) {
456
- existingPublication = <LocalTrackPublication>publication;
457
- }
458
- });
459
-
460
- if (existingPublication) return existingPublication;
461
-
462
499
  if (opts.source) {
463
500
  track.source = opts.source;
464
501
  }
502
+ const publishPromise = this.publish(track, opts, options, isStereo);
503
+ this.pendingPublishPromises.set(track, publishPromise);
504
+ try {
505
+ const publication = await publishPromise;
506
+ return publication;
507
+ } catch (e) {
508
+ throw e;
509
+ } finally {
510
+ this.pendingPublishPromises.delete(track);
511
+ }
512
+ }
513
+
514
+ private async publish(
515
+ track: LocalTrack,
516
+ opts: TrackPublishOptions,
517
+ options: TrackPublishOptions | undefined,
518
+ isStereo: boolean,
519
+ ) {
465
520
  const existingTrackOfSource = Array.from(this.tracks.values()).find(
466
521
  (publishedTrack) => track instanceof LocalTrack && publishedTrack.source === track.source,
467
522
  );
@@ -721,17 +776,24 @@ export default class LocalParticipant extends Participant {
721
776
  track.stop();
722
777
  }
723
778
 
779
+ let negotiationNeeded = false;
780
+ const trackSender = track.sender;
781
+ track.sender = undefined;
724
782
  if (
725
783
  this.engine.publisher &&
726
784
  this.engine.publisher.pc.connectionState !== 'closed' &&
727
- track.sender
785
+ trackSender
728
786
  ) {
729
787
  try {
730
- this.engine.removeTrack(track.sender);
788
+ if (this.engine.removeTrack(trackSender)) {
789
+ negotiationNeeded = true;
790
+ }
731
791
  if (track instanceof LocalVideoTrack) {
732
792
  for (const [, trackInfo] of track.simulcastCodecs) {
733
793
  if (trackInfo.sender) {
734
- this.engine.removeTrack(trackInfo.sender);
794
+ if (this.engine.removeTrack(trackInfo.sender)) {
795
+ negotiationNeeded = true;
796
+ }
735
797
  trackInfo.sender = undefined;
736
798
  }
737
799
  }
@@ -739,13 +801,9 @@ export default class LocalParticipant extends Participant {
739
801
  }
740
802
  } catch (e) {
741
803
  log.warn('failed to unpublish track', { error: e, method: 'unpublishTrack' });
742
- } finally {
743
- await this.engine.negotiate();
744
804
  }
745
805
  }
746
806
 
747
- track.sender = undefined;
748
-
749
807
  // remove from our maps
750
808
  this.tracks.delete(publication.trackSid);
751
809
  switch (publication.kind) {
@@ -762,6 +820,9 @@ export default class LocalParticipant extends Participant {
762
820
  this.emit(ParticipantEvent.LocalTrackUnpublished, publication);
763
821
  publication.setTrack(undefined);
764
822
 
823
+ if (negotiationNeeded) {
824
+ await this.engine.negotiate();
825
+ }
765
826
  return publication;
766
827
  }
767
828
 
@@ -794,6 +855,22 @@ export default class LocalParticipant extends Participant {
794
855
  );
795
856
  }
796
857
 
858
+ /**
859
+ * Publish a new data payload to the room. Data will be forwarded to each
860
+ * participant in the room if the destination field in publishOptions is empty
861
+ *
862
+ * @param data Uint8Array of the payload. To send string data, use TextEncoder.encode
863
+ * @param kind whether to send this as reliable or lossy.
864
+ * For data that you need delivery guarantee (such as chat messages), use Reliable.
865
+ * For data that should arrive as quickly as possible, but you are ok with dropped
866
+ * packets, use Lossy.
867
+ * @param publishOptions optionally specify a `topic` and `destination`
868
+ */
869
+ async publishData(
870
+ data: Uint8Array,
871
+ kind: DataPacket_Kind,
872
+ publishOptions?: DataPublishOptions,
873
+ ): Promise<void>;
797
874
  /**
798
875
  * Publish a new data payload to the room. Data will be forwarded to each
799
876
  * participant in the room if the destination argument is empty
@@ -809,14 +886,26 @@ export default class LocalParticipant extends Participant {
809
886
  data: Uint8Array,
810
887
  kind: DataPacket_Kind,
811
888
  destination?: RemoteParticipant[] | string[],
889
+ ): Promise<void>;
890
+
891
+ async publishData(
892
+ data: Uint8Array,
893
+ kind: DataPacket_Kind,
894
+ publishOptions: DataPublishOptions | RemoteParticipant[] | string[] = {},
812
895
  ) {
813
- const dest: string[] = [];
896
+ const destination = Array.isArray(publishOptions)
897
+ ? publishOptions
898
+ : publishOptions?.destination;
899
+ const destinationSids: string[] = [];
900
+
901
+ const topic = !Array.isArray(publishOptions) ? publishOptions.topic : undefined;
902
+
814
903
  if (destination !== undefined) {
815
904
  destination.forEach((val: any) => {
816
905
  if (val instanceof RemoteParticipant) {
817
- dest.push(val.sid);
906
+ destinationSids.push(val.sid);
818
907
  } else {
819
- dest.push(val);
908
+ destinationSids.push(val);
820
909
  }
821
910
  });
822
911
  }
@@ -828,7 +917,8 @@ export default class LocalParticipant extends Participant {
828
917
  user: {
829
918
  participantSid: this.sid,
830
919
  payload: data,
831
- destinationSids: dest,
920
+ destinationSids: destinationSids,
921
+ topic,
832
922
  },
833
923
  },
834
924
  };
@@ -236,8 +236,7 @@ export default class RemoteParticipant extends Participant {
236
236
  }
237
237
  publication = new RemoteTrackPublication(
238
238
  kind,
239
- ti.sid,
240
- ti.name,
239
+ ti,
241
240
  this.signalClient.connectOptions?.autoSubscribe,
242
241
  );
243
242
  publication.updateInfo(ti);
@@ -246,7 +245,7 @@ export default class RemoteParticipant extends Participant {
246
245
  (publishedTrack) => publishedTrack.source === publication?.source,
247
246
  );
248
247
  if (existingTrackOfSource && publication.source !== Track.Source.Unknown) {
249
- log.warn(
248
+ log.debug(
250
249
  `received a second track publication for ${this.identity} with the same source: ${publication.source}`,
251
250
  {
252
251
  oldTrack: existingTrackOfSource,
@@ -24,9 +24,10 @@ export default class RemoteTrackPublication extends TrackPublication {
24
24
 
25
25
  protected fps?: number;
26
26
 
27
- constructor(kind: Track.Kind, id: string, name: string, autoSubscribe: boolean | undefined) {
28
- super(kind, id, name);
27
+ constructor(kind: Track.Kind, ti: TrackInfo, autoSubscribe: boolean | undefined) {
28
+ super(kind, ti.sid, ti.name);
29
29
  this.subscribed = autoSubscribe;
30
+ this.updateInfo(ti);
30
31
  }
31
32
 
32
33
  /**
@@ -31,9 +31,6 @@ export default class RemoteVideoTrack extends RemoteTrack {
31
31
  ) {
32
32
  super(mediaTrack, sid, Track.Kind.Video, receiver);
33
33
  this.adaptiveStreamSettings = adaptiveStreamSettings;
34
- if (this.isAdaptiveStream) {
35
- this.streamState = Track.StreamState.Paused;
36
- }
37
34
  }
38
35
 
39
36
  get isAdaptiveStream(): boolean {
@@ -32,7 +32,8 @@ export abstract class Track extends (EventEmitter as new () => TypedEventEmitter
32
32
  mediaStream?: MediaStream;
33
33
 
34
34
  /**
35
- * indicates current state of stream
35
+ * indicates current state of stream, it'll indicate `paused` if the track
36
+ * has been paused by congestion controller
36
37
  */
37
38
  streamState: Track.StreamState = Track.StreamState.Active;
38
39
 
@@ -28,12 +28,12 @@ export interface TrackPublishDefaults {
28
28
  audioBitrate?: number;
29
29
 
30
30
  /**
31
- * dtx (Discontinuous Transmission of audio), defaults to true
31
+ * dtx (Discontinuous Transmission of audio), enabled by default for mono tracks.
32
32
  */
33
33
  dtx?: boolean;
34
34
 
35
35
  /**
36
- * red (Redundant Audio Data), defaults to true
36
+ * red (Redundant Audio Data), enabled by default for mono tracks.
37
37
  */
38
38
  red?: boolean;
39
39
 
@@ -133,6 +133,18 @@ export interface ScreenShareCaptureOptions {
133
133
 
134
134
  /** capture resolution, defaults to full HD */
135
135
  resolution?: VideoResolution;
136
+
137
+ /** a CaptureController object instance containing methods that can be used to further manipulate the capture session if included. */
138
+ controller?: unknown; // TODO replace type with CaptureController once it lands in TypeScript
139
+
140
+ /** specifies whether the browser should allow the user to select the current tab for capture */
141
+ selfBrowserSurface?: 'include' | 'exclude';
142
+
143
+ /** specifies whether the browser should display a control to allow the user to dynamically switch the shared tab during screen-sharing. */
144
+ surfaceSwitching?: 'include' | 'exclude';
145
+
146
+ /** specifies whether the browser should include the system audio among the possible audio sources offered to the user */
147
+ systemAudio?: 'include' | 'exclude';
136
148
  }
137
149
 
138
150
  export interface AudioCaptureOptions {
package/src/room/types.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import type RemoteParticipant from './participant/RemoteParticipant';
2
+
1
3
  export type SimulationOptions = {
2
4
  publish?: {
3
5
  audio?: boolean;
@@ -11,3 +13,10 @@ export type SimulationOptions = {
11
13
  video?: boolean;
12
14
  };
13
15
  };
16
+
17
+ export type DataPublishOptions = {
18
+ /** the participants who will receive the message, will be sent to every one if empty */
19
+ destination?: RemoteParticipant[] | string[];
20
+ /** the topic under which the message gets published */
21
+ topic?: string;
22
+ };
package/src/version.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  import { version as v } from '../package.json';
2
2
 
3
3
  export const version = v;
4
- export const protocolVersion = 8;
4
+ export const protocolVersion = 9;