livekit-client 0.15.4 → 0.16.3

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 (73) hide show
  1. package/dist/api/RequestQueue.d.ts +12 -0
  2. package/dist/api/RequestQueue.js +61 -0
  3. package/dist/api/RequestQueue.js.map +1 -0
  4. package/dist/api/SignalClient.d.ts +7 -3
  5. package/dist/api/SignalClient.js +25 -4
  6. package/dist/api/SignalClient.js.map +1 -1
  7. package/dist/index.d.ts +1 -1
  8. package/dist/index.js +1 -1
  9. package/dist/index.js.map +1 -1
  10. package/dist/options.d.ts +0 -10
  11. package/dist/proto/livekit_rtc.d.ts +15 -10
  12. package/dist/proto/livekit_rtc.js +36 -22
  13. package/dist/proto/livekit_rtc.js.map +1 -1
  14. package/dist/room/RTCEngine.d.ts +27 -6
  15. package/dist/room/RTCEngine.js +163 -46
  16. package/dist/room/RTCEngine.js.map +1 -1
  17. package/dist/room/Room.d.ts +50 -6
  18. package/dist/room/Room.js +128 -67
  19. package/dist/room/Room.js.map +1 -1
  20. package/dist/room/events.d.ts +13 -4
  21. package/dist/room/events.js +15 -6
  22. package/dist/room/events.js.map +1 -1
  23. package/dist/room/participant/LocalParticipant.d.ts +1 -2
  24. package/dist/room/participant/LocalParticipant.js +7 -8
  25. package/dist/room/participant/LocalParticipant.js.map +1 -1
  26. package/dist/room/participant/Participant.d.ts +30 -4
  27. package/dist/room/participant/Participant.js +2 -2
  28. package/dist/room/participant/Participant.js.map +1 -1
  29. package/dist/room/participant/RemoteParticipant.d.ts +3 -4
  30. package/dist/room/participant/RemoteParticipant.js +3 -0
  31. package/dist/room/participant/RemoteParticipant.js.map +1 -1
  32. package/dist/room/track/LocalAudioTrack.js +8 -1
  33. package/dist/room/track/LocalAudioTrack.js.map +1 -1
  34. package/dist/room/track/LocalTrackPublication.d.ts +2 -0
  35. package/dist/room/track/LocalTrackPublication.js.map +1 -1
  36. package/dist/room/track/LocalVideoTrack.d.ts +1 -5
  37. package/dist/room/track/LocalVideoTrack.js +12 -117
  38. package/dist/room/track/LocalVideoTrack.js.map +1 -1
  39. package/dist/room/track/RemoteTrackPublication.d.ts +1 -1
  40. package/dist/room/track/RemoteTrackPublication.js +7 -1
  41. package/dist/room/track/RemoteTrackPublication.js.map +1 -1
  42. package/dist/room/track/RemoteVideoTrack.js +12 -7
  43. package/dist/room/track/RemoteVideoTrack.js.map +1 -1
  44. package/dist/room/track/Track.d.ts +16 -3
  45. package/dist/room/track/Track.js +30 -20
  46. package/dist/room/track/Track.js.map +1 -1
  47. package/dist/room/track/types.d.ts +4 -4
  48. package/dist/room/utils.d.ts +1 -0
  49. package/dist/room/utils.js +5 -21
  50. package/dist/room/utils.js.map +1 -1
  51. package/dist/version.d.ts +2 -2
  52. package/dist/version.js +2 -2
  53. package/package.json +3 -4
  54. package/src/api/RequestQueue.ts +53 -0
  55. package/src/api/SignalClient.ts +33 -5
  56. package/src/index.ts +1 -1
  57. package/src/options.ts +0 -12
  58. package/src/proto/livekit_rtc.ts +55 -41
  59. package/src/room/RTCEngine.ts +198 -53
  60. package/src/room/Room.ts +227 -96
  61. package/src/room/events.ts +15 -4
  62. package/src/room/participant/LocalParticipant.ts +6 -7
  63. package/src/room/participant/Participant.ts +39 -4
  64. package/src/room/participant/RemoteParticipant.ts +9 -4
  65. package/src/room/track/LocalAudioTrack.ts +8 -1
  66. package/src/room/track/LocalTrackPublication.ts +3 -0
  67. package/src/room/track/LocalVideoTrack.ts +11 -142
  68. package/src/room/track/RemoteTrackPublication.ts +8 -2
  69. package/src/room/track/RemoteVideoTrack.ts +14 -7
  70. package/src/room/track/Track.ts +46 -24
  71. package/src/room/track/types.ts +4 -4
  72. package/src/room/utils.ts +4 -16
  73. package/src/version.ts +2 -2
package/src/room/Room.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { EventEmitter } from 'events';
2
+ import type TypedEmitter from 'typed-emitter';
2
3
  import { toProtoSessionDescription } from '../api/SignalClient';
3
4
  import log from '../logger';
4
5
  import { RoomConnectOptions, RoomOptions } from '../options';
@@ -7,7 +8,11 @@ import {
7
8
  ParticipantInfo_State, Room as RoomModel, SpeakerInfo, UserPacket,
8
9
  } from '../proto/livekit_models';
9
10
  import {
10
- ConnectionQualityUpdate, SimulateScenario, StreamStateUpdate, SubscriptionPermissionUpdate,
11
+ ConnectionQualityUpdate,
12
+ JoinResponse,
13
+ SimulateScenario,
14
+ StreamStateUpdate,
15
+ SubscriptionPermissionUpdate,
11
16
  } from '../proto/livekit_rtc';
12
17
  import DeviceManager from './DeviceManager';
13
18
  import { ConnectionError, UnsupportedServer } from './errors';
@@ -40,7 +45,7 @@ export enum RoomState {
40
45
  *
41
46
  * @noInheritDoc
42
47
  */
43
- class Room extends EventEmitter {
48
+ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>) {
44
49
  state: RoomState = RoomState.Disconnected;
45
50
 
46
51
  /** map of sid: [[RemoteParticipant]] */
@@ -113,48 +118,49 @@ class Room extends EventEmitter {
113
118
  }
114
119
 
115
120
  this.engine = new RTCEngine();
116
- this.engine.client.signalLatency = this.options.expSignalLatency;
117
-
118
- this.engine.on(
119
- EngineEvent.MediaTrackAdded,
120
- (
121
- mediaTrack: MediaStreamTrack,
122
- stream: MediaStream,
123
- receiver?: RTCRtpReceiver,
124
- ) => {
125
- this.onTrackAdded(mediaTrack, stream, receiver);
126
- },
127
- );
128
-
129
- this.engine.on(EngineEvent.Disconnected, () => {
130
- this.handleDisconnect();
131
- });
132
121
 
122
+ this.engine.client.signalLatency = this.options.expSignalLatency;
133
123
  this.engine.client.onParticipantUpdate = this.handleParticipantUpdates;
134
124
  this.engine.client.onRoomUpdate = this.handleRoomUpdate;
135
125
  this.engine.client.onSpeakersChanged = this.handleSpeakersChanged;
136
126
  this.engine.client.onStreamStateUpdate = this.handleStreamStateUpdate;
137
127
  this.engine.client.onSubscriptionPermissionUpdate = this.handleSubscriptionPermissionUpdate;
138
- this.engine.on(EngineEvent.ActiveSpeakersUpdate, this.handleActiveSpeakersUpdate);
139
- this.engine.on(EngineEvent.DataPacketReceived, this.handleDataPacket);
140
-
141
- this.engine.on(EngineEvent.Reconnecting, () => {
142
- this.state = RoomState.Reconnecting;
143
- this.emit(RoomEvent.Reconnecting);
144
- });
145
-
146
- this.engine.on(EngineEvent.Reconnected, () => {
147
- this.state = RoomState.Connected;
148
- this.emit(RoomEvent.Reconnected);
149
- });
150
-
151
- this.engine.on(EngineEvent.SignalConnected, () => {
152
- if (this.state === RoomState.Reconnecting) {
153
- this.sendSyncState();
154
- }
155
- });
156
-
157
128
  this.engine.client.onConnectionQuality = this.handleConnectionQualityUpdate;
129
+
130
+ this.engine
131
+ .on(
132
+ EngineEvent.MediaTrackAdded,
133
+ (
134
+ mediaTrack: MediaStreamTrack,
135
+ stream: MediaStream,
136
+ receiver?: RTCRtpReceiver,
137
+ ) => {
138
+ this.onTrackAdded(mediaTrack, stream, receiver);
139
+ },
140
+ )
141
+ .on(EngineEvent.Disconnected, () => {
142
+ this.handleDisconnect();
143
+ })
144
+ .on(EngineEvent.ActiveSpeakersUpdate, this.handleActiveSpeakersUpdate)
145
+ .on(EngineEvent.DataPacketReceived, this.handleDataPacket)
146
+ .on(EngineEvent.Resuming, () => {
147
+ this.state = RoomState.Reconnecting;
148
+ this.emit(RoomEvent.Reconnecting);
149
+ this.emit(RoomEvent.StateChanged, this.state);
150
+ })
151
+ .on(EngineEvent.Resumed, () => {
152
+ this.state = RoomState.Connected;
153
+ this.emit(RoomEvent.Reconnected);
154
+ this.emit(RoomEvent.StateChanged, this.state);
155
+ this.updateSubscriptions();
156
+ })
157
+ .on(EngineEvent.SignalResumed, () => {
158
+ if (this.state === RoomState.Reconnecting) {
159
+ this.sendSyncState();
160
+ }
161
+ })
162
+ .on(EngineEvent.Restarting, this.handleRestarting)
163
+ .on(EngineEvent.Restarted, this.handleRestarted);
158
164
  }
159
165
 
160
166
  /**
@@ -202,6 +208,7 @@ class Room extends EventEmitter {
202
208
  }
203
209
 
204
210
  this.state = RoomState.Connected;
211
+ this.emit(RoomEvent.StateChanged, this.state);
205
212
  const pi = joinResponse.participant!;
206
213
  this.localParticipant = new LocalParticipant(
207
214
  pi.sid,
@@ -213,10 +220,10 @@ class Room extends EventEmitter {
213
220
  this.localParticipant.updateInfo(pi);
214
221
  // forward metadata changed for the local participant
215
222
  this.localParticipant
216
- .on(ParticipantEvent.MetadataChanged, (metadata: object) => {
223
+ .on(ParticipantEvent.MetadataChanged, (metadata: string | undefined) => {
217
224
  this.emit(RoomEvent.MetadataChanged, metadata, this.localParticipant);
218
225
  })
219
- .on(ParticipantEvent.ParticipantMetadataChanged, (metadata: object) => {
226
+ .on(ParticipantEvent.ParticipantMetadataChanged, (metadata: string | undefined) => {
220
227
  this.emit(RoomEvent.ParticipantMetadataChanged, metadata, this.localParticipant);
221
228
  })
222
229
  .on(ParticipantEvent.TrackMuted, (pub: TrackPublication) => {
@@ -439,6 +446,45 @@ class Room extends EventEmitter {
439
446
  );
440
447
  }
441
448
 
449
+ private handleRestarting = () => {
450
+ this.state = RoomState.Reconnecting;
451
+ this.emit(RoomEvent.Reconnecting);
452
+ this.emit(RoomEvent.StateChanged, this.state);
453
+
454
+ // also unwind existing participants & existing subscriptions
455
+ for (const p of this.participants.values()) {
456
+ this.handleParticipantDisconnected(p.sid, p);
457
+ }
458
+ };
459
+
460
+ private handleRestarted = async (joinResponse: JoinResponse) => {
461
+ this.state = RoomState.Connected;
462
+ this.emit(RoomEvent.Reconnected);
463
+ this.emit(RoomEvent.StateChanged, this.state);
464
+
465
+ // rehydrate participants
466
+ if (joinResponse.participant) {
467
+ // with a restart, the sid will have changed, we'll map our understanding to it
468
+ this.localParticipant.sid = joinResponse.participant.sid;
469
+ this.handleParticipantUpdates([joinResponse.participant]);
470
+ }
471
+ this.handleParticipantUpdates(joinResponse.otherParticipants);
472
+
473
+ // unpublish & republish tracks
474
+ const localPubs: LocalTrackPublication[] = [];
475
+ this.localParticipant.tracks.forEach((pub) => {
476
+ if (pub.track) {
477
+ localPubs.push(pub);
478
+ }
479
+ });
480
+
481
+ await Promise.all(localPubs.map(async (pub) => {
482
+ const track = pub.track!;
483
+ this.localParticipant.unpublishTrack(track, false);
484
+ this.localParticipant.publishTrack(track, pub.options);
485
+ }));
486
+ };
487
+
442
488
  private handleDisconnect(shouldStopTracks = true) {
443
489
  if (this.state === RoomState.Disconnected) {
444
490
  return;
@@ -469,12 +515,14 @@ class Room extends EventEmitter {
469
515
  navigator.mediaDevices.removeEventListener('devicechange', this.handleDeviceChange);
470
516
  this.state = RoomState.Disconnected;
471
517
  this.emit(RoomEvent.Disconnected);
518
+ this.emit(RoomEvent.StateChanged, this.state);
472
519
  }
473
520
 
474
521
  private handleParticipantUpdates = (participantInfos: ParticipantInfo[]) => {
475
522
  // handle changes to participant state, and send events
476
523
  participantInfos.forEach((info) => {
477
- if (info.sid === this.localParticipant.sid) {
524
+ if (info.sid === this.localParticipant.sid
525
+ || info.identity === this.localParticipant.identity) {
478
526
  this.localParticipant.updateInfo(info);
479
527
  return;
480
528
  }
@@ -676,66 +724,72 @@ class Room extends EventEmitter {
676
724
  }
677
725
  }
678
726
 
727
+ private createParticipant(id: string, info?: ParticipantInfo): RemoteParticipant {
728
+ let participant: RemoteParticipant;
729
+ if (info) {
730
+ participant = RemoteParticipant.fromParticipantInfo(
731
+ this.engine.client,
732
+ info,
733
+ );
734
+ } else {
735
+ participant = new RemoteParticipant(this.engine.client, id, '');
736
+ }
737
+ return participant;
738
+ }
739
+
679
740
  private getOrCreateParticipant(
680
741
  id: string,
681
742
  info?: ParticipantInfo,
682
743
  ): RemoteParticipant {
683
- let participant = this.participants.get(id);
684
- if (!participant) {
685
- // it's possible for the RTC track to arrive before signaling data
686
- // when this happens, we'll create the participant and make the track work
687
- if (info) {
688
- participant = RemoteParticipant.fromParticipantInfo(
689
- this.engine.client,
690
- info,
691
- );
692
- } else {
693
- participant = new RemoteParticipant(this.engine.client, id, '');
694
- }
695
- this.participants.set(id, participant);
696
- // also forward events
697
-
698
- // trackPublished is only fired for tracks added after both local participant
699
- // and remote participant joined the room
700
- participant
701
- .on(ParticipantEvent.TrackPublished, (trackPublication: RemoteTrackPublication) => {
702
- this.emit(RoomEvent.TrackPublished, trackPublication, participant);
703
- })
704
- .on(ParticipantEvent.TrackSubscribed,
705
- (track: RemoteTrack, publication: RemoteTrackPublication) => {
706
- // monitor playback status
707
- if (track.kind === Track.Kind.Audio) {
708
- track.on(TrackEvent.AudioPlaybackStarted, this.handleAudioPlaybackStarted);
709
- track.on(TrackEvent.AudioPlaybackFailed, this.handleAudioPlaybackFailed);
710
- }
711
- this.emit(RoomEvent.TrackSubscribed, track, publication, participant);
712
- })
713
- .on(ParticipantEvent.TrackUnpublished, (publication: RemoteTrackPublication) => {
714
- this.emit(RoomEvent.TrackUnpublished, publication, participant);
715
- })
716
- .on(ParticipantEvent.TrackUnsubscribed,
717
- (track: RemoteTrack, publication: RemoteTrackPublication) => {
718
- this.emit(RoomEvent.TrackUnsubscribed, track, publication, participant);
719
- })
720
- .on(ParticipantEvent.TrackSubscriptionFailed, (sid: string) => {
721
- this.emit(RoomEvent.TrackSubscriptionFailed, sid, participant);
722
- })
723
- .on(ParticipantEvent.TrackMuted, (pub: TrackPublication) => {
724
- this.emit(RoomEvent.TrackMuted, pub, participant);
725
- })
726
- .on(ParticipantEvent.TrackUnmuted, (pub: TrackPublication) => {
727
- this.emit(RoomEvent.TrackUnmuted, pub, participant);
728
- })
729
- .on(ParticipantEvent.MetadataChanged, (metadata: any) => {
730
- this.emit(RoomEvent.MetadataChanged, metadata, participant);
744
+ if (this.participants.has(id)) {
745
+ return (this.participants.get(id) as RemoteParticipant);
746
+ }
747
+ // it's possible for the RTC track to arrive before signaling data
748
+ // when this happens, we'll create the participant and make the track work
749
+ const participant = this.createParticipant(id, info);
750
+ this.participants.set(id, participant);
751
+
752
+ // also forward events
753
+ // trackPublished is only fired for tracks added after both local participant
754
+ // and remote participant joined the room
755
+ participant
756
+ .on(ParticipantEvent.TrackPublished, (trackPublication: RemoteTrackPublication) => {
757
+ this.emit(RoomEvent.TrackPublished, trackPublication, participant);
758
+ })
759
+ .on(ParticipantEvent.TrackSubscribed,
760
+ (track: RemoteTrack, publication: RemoteTrackPublication) => {
761
+ // monitor playback status
762
+ if (track.kind === Track.Kind.Audio) {
763
+ track.on(TrackEvent.AudioPlaybackStarted, this.handleAudioPlaybackStarted);
764
+ track.on(TrackEvent.AudioPlaybackFailed, this.handleAudioPlaybackFailed);
765
+ }
766
+ this.emit(RoomEvent.TrackSubscribed, track, publication, participant);
731
767
  })
732
- .on(ParticipantEvent.ParticipantMetadataChanged, (metadata: any) => {
733
- this.emit(RoomEvent.ParticipantMetadataChanged, metadata, participant);
768
+ .on(ParticipantEvent.TrackUnpublished, (publication: RemoteTrackPublication) => {
769
+ this.emit(RoomEvent.TrackUnpublished, publication, participant);
770
+ })
771
+ .on(ParticipantEvent.TrackUnsubscribed,
772
+ (track: RemoteTrack, publication: RemoteTrackPublication) => {
773
+ this.emit(RoomEvent.TrackUnsubscribed, track, publication, participant);
734
774
  })
735
- .on(ParticipantEvent.ConnectionQualityChanged, (quality: ConnectionQuality) => {
736
- this.emit(RoomEvent.ConnectionQualityChanged, quality, participant);
737
- });
738
- }
775
+ .on(ParticipantEvent.TrackSubscriptionFailed, (sid: string) => {
776
+ this.emit(RoomEvent.TrackSubscriptionFailed, sid, participant);
777
+ })
778
+ .on(ParticipantEvent.TrackMuted, (pub: TrackPublication) => {
779
+ this.emit(RoomEvent.TrackMuted, pub, participant);
780
+ })
781
+ .on(ParticipantEvent.TrackUnmuted, (pub: TrackPublication) => {
782
+ this.emit(RoomEvent.TrackUnmuted, pub, participant);
783
+ })
784
+ .on(ParticipantEvent.MetadataChanged, (metadata: string | undefined) => {
785
+ this.emit(RoomEvent.MetadataChanged, metadata, participant);
786
+ })
787
+ .on(ParticipantEvent.ParticipantMetadataChanged, (metadata: string | undefined) => {
788
+ this.emit(RoomEvent.ParticipantMetadataChanged, metadata, participant);
789
+ })
790
+ .on(ParticipantEvent.ConnectionQualityChanged, (quality: ConnectionQuality) => {
791
+ this.emit(RoomEvent.ConnectionQualityChanged, quality, participant);
792
+ });
739
793
  return participant;
740
794
  }
741
795
 
@@ -775,11 +829,88 @@ class Room extends EventEmitter {
775
829
  });
776
830
  }
777
831
 
778
- /** @internal */
779
- emit(event: string | symbol, ...args: any[]): boolean {
832
+ /**
833
+ * After resuming, we'll need to notify the server of the current
834
+ * subscription settings.
835
+ */
836
+ private updateSubscriptions() {
837
+ for (const p of this.participants.values()) {
838
+ for (const pub of p.videoTracks.values()) {
839
+ if (pub.isSubscribed && pub instanceof RemoteTrackPublication) {
840
+ pub.emitTrackUpdate();
841
+ }
842
+ }
843
+ }
844
+ }
845
+
846
+ // /** @internal */
847
+ emit<E extends keyof RoomEventCallbacks>(
848
+ event: E, ...args: Parameters<RoomEventCallbacks[E]>
849
+ ): boolean {
780
850
  log.debug('room event', event, ...args);
781
851
  return super.emit(event, ...args);
782
852
  }
783
853
  }
784
854
 
785
855
  export default Room;
856
+
857
+ export type RoomEventCallbacks = {
858
+ reconnecting: () => void,
859
+ reconnected: () => void,
860
+ disconnected: () => void,
861
+ stateChanged: (state: RoomState) => void,
862
+ mediaDevicesChanged: () => void,
863
+ participantConnected: (participant: RemoteParticipant) => void,
864
+ participantDisconnected: (participant: RemoteParticipant) => void,
865
+ trackPublished: (publication: RemoteTrackPublication, participant: RemoteParticipant) => void,
866
+ trackSubscribed: (
867
+ track: RemoteTrack,
868
+ publication: RemoteTrackPublication,
869
+ participant: RemoteParticipant
870
+ ) => void,
871
+ trackSubscriptionFailed: (trackSid: string, participant: RemoteParticipant) => void,
872
+ trackUnpublished: (publication: RemoteTrackPublication, participant: RemoteParticipant) => void,
873
+ trackUnsubscribed: (
874
+ track: RemoteTrack,
875
+ publication: RemoteTrackPublication,
876
+ participant: RemoteParticipant,
877
+ ) => void,
878
+ trackMuted: (publication: TrackPublication, participant: Participant) => void,
879
+ trackUnmuted: (publication: TrackPublication, participant: Participant) => void,
880
+ localTrackPublished: (publication: LocalTrackPublication, participant: LocalParticipant) => void,
881
+ localTrackUnpublished: (
882
+ publication: LocalTrackPublication,
883
+ participant: LocalParticipant
884
+ ) => void,
885
+ /**
886
+ * @deprecated use [[participantMetadataChanged]] instead
887
+ */
888
+ metadataChanged: (
889
+ metadata: string | undefined,
890
+ participant?: RemoteParticipant | LocalParticipant
891
+ ) => void,
892
+ participantMetadataChanged: (
893
+ metadata: string | undefined,
894
+ participant: RemoteParticipant | LocalParticipant
895
+ ) => void,
896
+ activeSpeakersChanged: (speakers: Array<Participant>) => void,
897
+ roomMetadataChanged: (metadata: string) => void,
898
+ dataReceived: (
899
+ payload: Uint8Array,
900
+ participant?: RemoteParticipant,
901
+ kind?: DataPacket_Kind
902
+ ) => void,
903
+ connectionQualityChanged: (quality: ConnectionQuality, participant: Participant) => void,
904
+ mediaDevicesError: (error: Error) => void,
905
+ trackStreamStateChanged: (
906
+ publication: RemoteTrackPublication,
907
+ streamState: Track.StreamState,
908
+ participant: RemoteParticipant,
909
+ ) => void,
910
+ trackSubscriptionPermissionChanged: (
911
+ publication: RemoteTrackPublication,
912
+ status: TrackPublication.SubscriptionStatus,
913
+ participant: RemoteParticipant,
914
+ ) => void,
915
+ audioPlaybackChanged: (playing: boolean) => void,
916
+ };
@@ -7,6 +7,7 @@
7
7
  * room.on(RoomEvent.TrackPublished, (track, publication, participant) => {})
8
8
  * ```
9
9
  */
10
+
10
11
  export enum RoomEvent {
11
12
  /**
12
13
  * When the connection to the server has been interrupted and it's attempting
@@ -25,6 +26,13 @@ export enum RoomEvent {
25
26
  */
26
27
  Disconnected = 'disconnected',
27
28
 
29
+ /**
30
+ * Whenever the connection state of the room changes
31
+ *
32
+ * args: ([[RoomState]])
33
+ */
34
+ StateChanged = 'stateChanged',
35
+
28
36
  /**
29
37
  * When input or output devices on the machine have changed.
30
38
  */
@@ -146,7 +154,7 @@ export enum RoomEvent {
146
154
  * args: (prevMetadata: string, [[Participant]])
147
155
  *
148
156
  */
149
- ParticipantMetadataChanged = 'participantMetaDataChanged',
157
+ ParticipantMetadataChanged = 'participantMetadataChanged',
150
158
 
151
159
  /**
152
160
  * Room metadata is a simple way for app-specific state to be pushed to
@@ -366,11 +374,14 @@ export enum ParticipantEvent {
366
374
 
367
375
  /** @internal */
368
376
  export enum EngineEvent {
377
+ TransportsCreated = 'transportsCreated',
369
378
  Connected = 'connected',
370
379
  Disconnected = 'disconnected',
371
- Reconnecting = 'reconnecting',
372
- Reconnected = 'reconnected',
373
- SignalConnected = 'singalConnected',
380
+ Resuming = 'resuming',
381
+ Resumed = 'resumed',
382
+ Restarting = 'restarting',
383
+ Restarted = 'restarted',
384
+ SignalResumed = 'signalResumed',
374
385
  MediaTrackAdded = 'mediaTrackAdded',
375
386
  ActiveSpeakersUpdate = 'activeSpeakersUpdate',
376
387
  DataPacketReceived = 'dataPacketReceived',
@@ -416,8 +416,7 @@ export default class LocalParticipant extends Participant {
416
416
  // store RTPSender
417
417
  track.sender = transceiver.sender;
418
418
  if (track instanceof LocalVideoTrack) {
419
- const disableLayerPause = this.roomOptions?.expDisableLayerPause ?? false;
420
- track.startMonitor(this.engine.client, disableLayerPause);
419
+ track.startMonitor(this.engine.client);
421
420
  } else if (track instanceof LocalAudioTrack) {
422
421
  track.startMonitor();
423
422
  }
@@ -434,6 +433,7 @@ export default class LocalParticipant extends Participant {
434
433
 
435
434
  unpublishTrack(
436
435
  track: LocalTrack | MediaStreamTrack,
436
+ stopOnUnpublish?: boolean,
437
437
  ): LocalTrackPublication | null {
438
438
  // look through all published tracks to find the right ones
439
439
  const publication = this.getPublicationForTrack(track);
@@ -455,7 +455,10 @@ export default class LocalParticipant extends Participant {
455
455
  track.off(TrackEvent.Unmuted, this.onTrackUnmuted);
456
456
  track.off(TrackEvent.Ended, this.onTrackUnpublish);
457
457
 
458
- if (this.roomOptions?.stopLocalTrackOnUnpublish ?? true) {
458
+ if (stopOnUnpublish === undefined) {
459
+ stopOnUnpublish = this.roomOptions?.stopLocalTrackOnUnpublish ?? true;
460
+ }
461
+ if (stopOnUnpublish) {
459
462
  track.stop();
460
463
  }
461
464
 
@@ -507,10 +510,6 @@ export default class LocalParticipant extends Participant {
507
510
  return publications;
508
511
  }
509
512
 
510
- get publisherMetrics(): any {
511
- return null;
512
- }
513
-
514
513
  /**
515
514
  * Publish a new data payload to the room. Data will be forwarded to each
516
515
  * participant in the room if the destination argument is empty
@@ -1,8 +1,12 @@
1
1
  import { EventEmitter } from 'events';
2
- import { ConnectionQuality as ProtoQuality, ParticipantInfo } from '../../proto/livekit_models';
2
+ import type TypedEmitter from 'typed-emitter';
3
+ import { ConnectionQuality as ProtoQuality, DataPacket_Kind, ParticipantInfo } from '../../proto/livekit_models';
3
4
  import { ParticipantEvent, TrackEvent } from '../events';
5
+ import LocalTrackPublication from '../track/LocalTrackPublication';
6
+ import RemoteTrackPublication from '../track/RemoteTrackPublication';
4
7
  import { Track } from '../track/Track';
5
8
  import { TrackPublication } from '../track/TrackPublication';
9
+ import { RemoteTrack } from '../track/types';
6
10
 
7
11
  export enum ConnectionQuality {
8
12
  Excellent = 'excellent',
@@ -24,7 +28,9 @@ function qualityFromProto(q: ProtoQuality): ConnectionQuality {
24
28
  }
25
29
  }
26
30
 
27
- export default class Participant extends EventEmitter {
31
+ export default class Participant extends (
32
+ EventEmitter as new () => TypedEmitter<ParticipantEventCallbacks>
33
+ ) {
28
34
  protected participantInfo?: ParticipantInfo;
29
35
 
30
36
  audioTracks: Map<string, TrackPublication>;
@@ -158,8 +164,8 @@ export default class Participant extends EventEmitter {
158
164
  this.metadata = md;
159
165
 
160
166
  if (changed) {
161
- this.emit(ParticipantEvent.MetadataChanged, prevMetadata, this);
162
- this.emit(ParticipantEvent.ParticipantMetadataChanged, prevMetadata, this);
167
+ this.emit(ParticipantEvent.MetadataChanged, prevMetadata);
168
+ this.emit(ParticipantEvent.ParticipantMetadataChanged, prevMetadata);
163
169
  }
164
170
  }
165
171
 
@@ -212,3 +218,32 @@ export default class Participant extends EventEmitter {
212
218
  }
213
219
  }
214
220
  }
221
+
222
+ export type ParticipantEventCallbacks = {
223
+ trackPublished: (publication: RemoteTrackPublication) => void,
224
+ trackSubscribed: (track: RemoteTrack, publication: RemoteTrackPublication) => void,
225
+ trackSubscriptionFailed: (trackSid: string) => void,
226
+ trackUnpublished: (publication: RemoteTrackPublication) => void,
227
+ trackUnsubscribed: (track: RemoteTrack, publication: RemoteTrackPublication) => void,
228
+ trackMuted: (publication: TrackPublication) => void,
229
+ trackUnmuted: (publication: TrackPublication) => void,
230
+ localTrackPublished: (publication: LocalTrackPublication) => void,
231
+ localTrackUnpublished: (publication: LocalTrackPublication) => void,
232
+ /**
233
+ * @deprecated use [[participantMetadataChanged]] instead
234
+ */
235
+ metadataChanged: (prevMetadata: string | undefined, participant?: any) => void,
236
+ participantMetadataChanged: (prevMetadata: string | undefined, participant?: any) => void,
237
+ dataReceived: (payload: Uint8Array, kind: DataPacket_Kind) => void,
238
+ isSpeakingChanged: (speaking: boolean) => void,
239
+ connectionQualityChanged: (connectionQuality: ConnectionQuality) => void,
240
+ trackStreamStateChanged: (
241
+ publication: RemoteTrackPublication,
242
+ streamState: Track.StreamState
243
+ ) => void,
244
+ trackSubscriptionPermissionChanged: (
245
+ publication: RemoteTrackPublication,
246
+ status: TrackPublication.SubscriptionStatus
247
+ ) => void,
248
+ mediaDevicesError: (error: Error) => void,
249
+ };
@@ -10,9 +10,8 @@ import RemoteAudioTrack from '../track/RemoteAudioTrack';
10
10
  import RemoteTrackPublication from '../track/RemoteTrackPublication';
11
11
  import RemoteVideoTrack from '../track/RemoteVideoTrack';
12
12
  import { Track } from '../track/Track';
13
- import { TrackPublication } from '../track/TrackPublication';
14
13
  import { RemoteTrack } from '../track/types';
15
- import Participant from './Participant';
14
+ import Participant, { ParticipantEventCallbacks } from './Participant';
16
15
 
17
16
  export default class RemoteParticipant extends Participant {
18
17
  audioTracks: Map<string, RemoteTrackPublication>;
@@ -42,7 +41,7 @@ export default class RemoteParticipant extends Participant {
42
41
  this.videoTracks = new Map();
43
42
  }
44
43
 
45
- protected addTrackPublication(publication: TrackPublication) {
44
+ protected addTrackPublication(publication: RemoteTrackPublication) {
46
45
  super.addTrackPublication(publication);
47
46
 
48
47
  // register action events
@@ -53,6 +52,9 @@ export default class RemoteParticipant extends Participant {
53
52
  },
54
53
  );
55
54
  publication.on(TrackEvent.UpdateSubscription, (sub: UpdateSubscription) => {
55
+ sub.participantTracks.forEach((pt) => {
56
+ pt.participantSid = this.sid;
57
+ });
56
58
  this.signalClient.sendUpdateSubscription(sub);
57
59
  });
58
60
  publication.on(TrackEvent.Ended, (track: RemoteTrack) => {
@@ -231,7 +233,10 @@ export default class RemoteParticipant extends Participant {
231
233
  }
232
234
 
233
235
  /** @internal */
234
- emit(event: string | symbol, ...args: any[]): boolean {
236
+ emit<E extends keyof ParticipantEventCallbacks>(
237
+ event: E,
238
+ ...args: Parameters<ParticipantEventCallbacks[E]>
239
+ ): boolean {
235
240
  log.trace('participant event', this.sid, event, ...args);
236
241
  return super.emit(event, ...args);
237
242
  }
@@ -73,7 +73,14 @@ export default class LocalAudioTrack extends LocalTrack {
73
73
  this._currentBitrate = 0;
74
74
  return;
75
75
  }
76
- const stats = await this.getSenderStats();
76
+
77
+ let stats: AudioSenderStats | undefined;
78
+ try {
79
+ stats = await this.getSenderStats();
80
+ } catch (e) {
81
+ log.error('could not get audio sender stats', e);
82
+ return;
83
+ }
77
84
 
78
85
  if (stats && this.prevStats) {
79
86
  this._currentBitrate = computeBitrate(stats, this.prevStats);
@@ -3,12 +3,15 @@ import { TrackEvent } from '../events';
3
3
  import LocalAudioTrack from './LocalAudioTrack';
4
4
  import LocalTrack from './LocalTrack';
5
5
  import LocalVideoTrack from './LocalVideoTrack';
6
+ import { TrackPublishOptions } from './options';
6
7
  import { Track } from './Track';
7
8
  import { TrackPublication } from './TrackPublication';
8
9
 
9
10
  export default class LocalTrackPublication extends TrackPublication {
10
11
  track?: LocalTrack;
11
12
 
13
+ options?: TrackPublishOptions;
14
+
12
15
  constructor(kind: Track.Kind, ti: TrackInfo, track?: LocalTrack) {
13
16
  super(kind, ti.sid, ti.name);
14
17