livekit-client 1.6.6 → 1.6.8

Sign up to get free protection for your applications and to get access to all the features.
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;