livekit-client 0.16.5 → 0.17.1

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 (100) hide show
  1. package/dist/api/RequestQueue.js +6 -6
  2. package/dist/api/RequestQueue.js.map +1 -1
  3. package/dist/api/SignalClient.d.ts +3 -0
  4. package/dist/api/SignalClient.js +24 -3
  5. package/dist/api/SignalClient.js.map +1 -1
  6. package/dist/connect.js +1 -1
  7. package/dist/connect.js.map +1 -1
  8. package/dist/index.d.ts +1 -1
  9. package/dist/index.js +1 -1
  10. package/dist/index.js.map +1 -1
  11. package/dist/options.d.ts +7 -2
  12. package/dist/proto/livekit_models.d.ts +33 -0
  13. package/dist/proto/livekit_models.js +213 -3
  14. package/dist/proto/livekit_models.js.map +1 -1
  15. package/dist/proto/livekit_rtc.d.ts +15 -1
  16. package/dist/proto/livekit_rtc.js +128 -2
  17. package/dist/proto/livekit_rtc.js.map +1 -1
  18. package/dist/room/RTCEngine.d.ts +21 -6
  19. package/dist/room/RTCEngine.js +13 -8
  20. package/dist/room/RTCEngine.js.map +1 -1
  21. package/dist/room/Room.d.ts +43 -6
  22. package/dist/room/Room.js +81 -59
  23. package/dist/room/Room.js.map +1 -1
  24. package/dist/room/events.d.ts +14 -2
  25. package/dist/room/events.js +16 -4
  26. package/dist/room/events.js.map +1 -1
  27. package/dist/room/participant/LocalParticipant.d.ts +3 -1
  28. package/dist/room/participant/LocalParticipant.js +17 -1
  29. package/dist/room/participant/LocalParticipant.js.map +1 -1
  30. package/dist/room/participant/Participant.d.ts +30 -4
  31. package/dist/room/participant/Participant.js +2 -2
  32. package/dist/room/participant/Participant.js.map +1 -1
  33. package/dist/room/participant/RemoteParticipant.d.ts +5 -5
  34. package/dist/room/participant/RemoteParticipant.js +3 -3
  35. package/dist/room/participant/RemoteParticipant.js.map +1 -1
  36. package/dist/room/participant/publishUtils.d.ts +6 -0
  37. package/dist/room/participant/publishUtils.js +65 -24
  38. package/dist/room/participant/publishUtils.js.map +1 -1
  39. package/dist/room/participant/publishUtils.test.js +35 -5
  40. package/dist/room/participant/publishUtils.test.js.map +1 -1
  41. package/dist/room/track/LocalAudioTrack.d.ts +2 -0
  42. package/dist/room/track/LocalAudioTrack.js +23 -0
  43. package/dist/room/track/LocalAudioTrack.js.map +1 -1
  44. package/dist/room/track/LocalTrack.d.ts +4 -0
  45. package/dist/room/track/LocalTrack.js +34 -0
  46. package/dist/room/track/LocalTrack.js.map +1 -1
  47. package/dist/room/track/LocalVideoTrack.d.ts +1 -0
  48. package/dist/room/track/LocalVideoTrack.js +13 -0
  49. package/dist/room/track/LocalVideoTrack.js.map +1 -1
  50. package/dist/room/track/RemoteTrack.d.ts +1 -0
  51. package/dist/room/track/RemoteTrack.js +1 -0
  52. package/dist/room/track/RemoteTrack.js.map +1 -1
  53. package/dist/room/track/RemoteVideoTrack.d.ts +4 -2
  54. package/dist/room/track/RemoteVideoTrack.js +23 -8
  55. package/dist/room/track/RemoteVideoTrack.js.map +1 -1
  56. package/dist/room/track/Track.d.ts +20 -4
  57. package/dist/room/track/Track.js +20 -1
  58. package/dist/room/track/Track.js.map +1 -1
  59. package/dist/room/track/defaults.js +2 -2
  60. package/dist/room/track/defaults.js.map +1 -1
  61. package/dist/room/track/options.d.ts +65 -15
  62. package/dist/room/track/options.js +38 -0
  63. package/dist/room/track/options.js.map +1 -1
  64. package/dist/room/track/types.d.ts +15 -4
  65. package/dist/room/track/utils.d.ts +10 -0
  66. package/dist/room/track/utils.js +46 -1
  67. package/dist/room/track/utils.js.map +1 -1
  68. package/dist/room/utils.d.ts +1 -0
  69. package/dist/room/utils.js +5 -1
  70. package/dist/room/utils.js.map +1 -1
  71. package/dist/version.d.ts +1 -1
  72. package/dist/version.js +1 -1
  73. package/package.json +2 -1
  74. package/src/api/RequestQueue.ts +7 -7
  75. package/src/api/SignalClient.ts +31 -4
  76. package/src/connect.ts +1 -1
  77. package/src/index.ts +1 -1
  78. package/src/options.ts +12 -3
  79. package/src/proto/livekit_models.ts +249 -0
  80. package/src/proto/livekit_rtc.ts +155 -0
  81. package/src/room/RTCEngine.ts +43 -11
  82. package/src/room/Room.ts +152 -66
  83. package/src/room/events.ts +16 -2
  84. package/src/room/participant/LocalParticipant.ts +23 -4
  85. package/src/room/participant/Participant.ts +39 -4
  86. package/src/room/participant/RemoteParticipant.ts +10 -8
  87. package/src/room/participant/publishUtils.test.ts +46 -6
  88. package/src/room/participant/publishUtils.ts +72 -27
  89. package/src/room/track/LocalAudioTrack.ts +19 -1
  90. package/src/room/track/LocalTrack.ts +36 -0
  91. package/src/room/track/LocalVideoTrack.ts +9 -1
  92. package/src/room/track/RemoteTrack.ts +2 -0
  93. package/src/room/track/RemoteVideoTrack.ts +22 -9
  94. package/src/room/track/Track.ts +29 -3
  95. package/src/room/track/defaults.ts +2 -2
  96. package/src/room/track/options.ts +55 -3
  97. package/src/room/track/types.ts +16 -4
  98. package/src/room/track/utils.ts +39 -0
  99. package/src/room/utils.ts +4 -0
  100. package/src/version.ts +1 -1
@@ -1,7 +1,12 @@
1
1
  import { EventEmitter } from 'events';
2
+ import type TypedEventEmitter from 'typed-emitter';
2
3
  import { SignalClient, SignalOptions } from '../api/SignalClient';
3
4
  import log from '../logger';
4
- import { DataPacket, DataPacket_Kind, TrackInfo } from '../proto/livekit_models';
5
+ import {
6
+ ClientConfigSetting,
7
+ ClientConfiguration,
8
+ DataPacket, DataPacket_Kind, SpeakerInfo, TrackInfo, UserPacket,
9
+ } from '../proto/livekit_models';
5
10
  import {
6
11
  AddTrackRequest, JoinResponse,
7
12
  LeaveRequest,
@@ -21,7 +26,9 @@ const maxReconnectDuration = 60 * 1000;
21
26
  export const maxICEConnectTimeout = 15 * 1000;
22
27
 
23
28
  /** @internal */
24
- export default class RTCEngine extends EventEmitter {
29
+ export default class RTCEngine extends (
30
+ EventEmitter as new () => TypedEventEmitter<EngineEventCallbacks>
31
+ ) {
25
32
  publisher?: PCTransport;
26
33
 
27
34
  subscriber?: PCTransport;
@@ -65,7 +72,9 @@ export default class RTCEngine extends EventEmitter {
65
72
 
66
73
  private reconnectStart: number = 0;
67
74
 
68
- private fullReconnect: boolean = false;
75
+ private fullReconnectOnNext: boolean = false;
76
+
77
+ private clientConfiguration?: ClientConfiguration;
69
78
 
70
79
  private connectedServerAddr?: string;
71
80
 
@@ -91,6 +100,7 @@ export default class RTCEngine extends EventEmitter {
91
100
  if (!this.subscriberPrimary) {
92
101
  this.negotiate();
93
102
  }
103
+ this.clientConfiguration = joinResponse.clientConfiguration;
94
104
 
95
105
  return joinResponse;
96
106
  }
@@ -164,6 +174,8 @@ export default class RTCEngine extends EventEmitter {
164
174
  this.publisher = new PCTransport(this.rtcConfig);
165
175
  this.subscriber = new PCTransport(this.rtcConfig);
166
176
 
177
+ this.emit(EngineEvent.TransportsCreated, this.publisher, this.subscriber);
178
+
167
179
  this.publisher.pc.onicecandidate = (ev) => {
168
180
  if (!ev.candidate) return;
169
181
  log.trace('adding ICE candidate for peer', ev.candidate);
@@ -296,7 +308,7 @@ export default class RTCEngine extends EventEmitter {
296
308
 
297
309
  this.client.onLeave = (leave?: LeaveRequest) => {
298
310
  if (leave?.canReconnect) {
299
- this.fullReconnect = true;
311
+ this.fullReconnectOnNext = true;
300
312
  this.primaryPC = undefined;
301
313
  } else {
302
314
  this.emit(EngineEvent.Disconnected);
@@ -369,19 +381,19 @@ export default class RTCEngine extends EventEmitter {
369
381
  if (this.isClosed) {
370
382
  return;
371
383
  }
372
- if (isFireFox()) {
373
- // FF does not support DTLS restart.
374
- this.fullReconnect = true;
384
+ if (isFireFox() // TODO remove once clientConfiguration handles firefox case server side
385
+ || this.clientConfiguration?.resumeConnection === ClientConfigSetting.DISABLED) {
386
+ this.fullReconnectOnNext = true;
375
387
  }
376
388
 
377
389
  try {
378
- if (this.fullReconnect) {
390
+ if (this.fullReconnectOnNext) {
379
391
  await this.restartConnection();
380
392
  } else {
381
393
  await this.resumeConnection();
382
394
  }
383
395
  this.reconnectAttempts = 0;
384
- this.fullReconnect = false;
396
+ this.fullReconnectOnNext = false;
385
397
  } catch (e) {
386
398
  this.reconnectAttempts += 1;
387
399
  let recoverable = true;
@@ -391,7 +403,7 @@ export default class RTCEngine extends EventEmitter {
391
403
  recoverable = false;
392
404
  } else if (!(e instanceof SignalReconnectError)) {
393
405
  // cannot resume
394
- this.fullReconnect = true;
406
+ this.fullReconnectOnNext = true;
395
407
  }
396
408
 
397
409
  const duration = Date.now() - this.reconnectStart;
@@ -437,6 +449,7 @@ export default class RTCEngine extends EventEmitter {
437
449
  }
438
450
 
439
451
  await this.waitForPCConnected();
452
+ this.client.setReconnected();
440
453
 
441
454
  // reconnect success
442
455
  this.emit(EngineEvent.Restarted, joinResponse);
@@ -471,6 +484,7 @@ export default class RTCEngine extends EventEmitter {
471
484
  }
472
485
 
473
486
  await this.waitForPCConnected();
487
+ this.client.setReconnected();
474
488
 
475
489
  // resume success
476
490
  this.emit(EngineEvent.Resumed);
@@ -557,7 +571,7 @@ export default class RTCEngine extends EventEmitter {
557
571
  this.publisher.negotiate();
558
572
  }
559
573
 
560
- private dataChannelForKind(kind: DataPacket_Kind): RTCDataChannel | undefined {
574
+ dataChannelForKind(kind: DataPacket_Kind): RTCDataChannel | undefined {
561
575
  if (kind === DataPacket_Kind.LOSSY) {
562
576
  return this.lossyDC;
563
577
  } if (kind === DataPacket_Kind.RELIABLE) {
@@ -602,3 +616,21 @@ async function getConnectedAddress(pc: RTCPeerConnection): Promise<string | unde
602
616
 
603
617
  class SignalReconnectError extends Error {
604
618
  }
619
+
620
+ export type EngineEventCallbacks = {
621
+ connected: () => void,
622
+ disconnected: () => void,
623
+ resuming: () => void,
624
+ resumed: () => void,
625
+ restarting: () => void,
626
+ restarted: (joinResp: JoinResponse) => void,
627
+ signalResumed: () => void,
628
+ mediaTrackAdded: (
629
+ track: MediaStreamTrack,
630
+ streams: MediaStream,
631
+ receiver: RTCRtpReceiver
632
+ ) => void,
633
+ activeSpeakersUpdate: (speakers: Array<SpeakerInfo>) => void,
634
+ dataPacketReceived: (userPacket: UserPacket, kind: DataPacket_Kind) => void,
635
+ transportsCreated: (publisher: PCTransport, subscriber: PCTransport) => void,
636
+ };
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';
@@ -27,7 +28,8 @@ import LocalTrackPublication from './track/LocalTrackPublication';
27
28
  import RemoteTrackPublication from './track/RemoteTrackPublication';
28
29
  import { Track } from './track/Track';
29
30
  import { TrackPublication } from './track/TrackPublication';
30
- import { RemoteTrack } from './track/types';
31
+ import { AdaptiveStreamSettings, RemoteTrack } from './track/types';
32
+ import { getNewAudioContext } from './track/utils';
31
33
  import { unpackStreamId } from './utils';
32
34
 
33
35
  export enum RoomState {
@@ -44,7 +46,7 @@ export enum RoomState {
44
46
  *
45
47
  * @noInheritDoc
46
48
  */
47
- class Room extends EventEmitter {
49
+ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>) {
48
50
  state: RoomState = RoomState.Disconnected;
49
51
 
50
52
  /** map of sid: [[RemoteParticipant]] */
@@ -145,10 +147,12 @@ class Room extends EventEmitter {
145
147
  .on(EngineEvent.Resuming, () => {
146
148
  this.state = RoomState.Reconnecting;
147
149
  this.emit(RoomEvent.Reconnecting);
150
+ this.emit(RoomEvent.StateChanged, this.state);
148
151
  })
149
152
  .on(EngineEvent.Resumed, () => {
150
153
  this.state = RoomState.Connected;
151
154
  this.emit(RoomEvent.Reconnected);
155
+ this.emit(RoomEvent.StateChanged, this.state);
152
156
  this.updateSubscriptions();
153
157
  })
154
158
  .on(EngineEvent.SignalResumed, () => {
@@ -192,7 +196,7 @@ class Room extends EventEmitter {
192
196
 
193
197
  try {
194
198
  const joinResponse = await this.engine.join(url, token, opts);
195
- log.debug('connected to Livekit Server', joinResponse.serverVersion);
199
+ log.debug(`connected to Livekit Server version: ${joinResponse.serverVersion}, region: ${joinResponse.serverRegion}`);
196
200
 
197
201
  if (!joinResponse.serverVersion) {
198
202
  throw new UnsupportedServer('unknown server version');
@@ -205,6 +209,7 @@ class Room extends EventEmitter {
205
209
  }
206
210
 
207
211
  this.state = RoomState.Connected;
212
+ this.emit(RoomEvent.StateChanged, this.state);
208
213
  const pi = joinResponse.participant!;
209
214
  this.localParticipant = new LocalParticipant(
210
215
  pi.sid,
@@ -216,10 +221,10 @@ class Room extends EventEmitter {
216
221
  this.localParticipant.updateInfo(pi);
217
222
  // forward metadata changed for the local participant
218
223
  this.localParticipant
219
- .on(ParticipantEvent.MetadataChanged, (metadata: object) => {
224
+ .on(ParticipantEvent.MetadataChanged, (metadata: string | undefined) => {
220
225
  this.emit(RoomEvent.MetadataChanged, metadata, this.localParticipant);
221
226
  })
222
- .on(ParticipantEvent.ParticipantMetadataChanged, (metadata: object) => {
227
+ .on(ParticipantEvent.ParticipantMetadataChanged, (metadata: string | undefined) => {
223
228
  this.emit(RoomEvent.ParticipantMetadataChanged, metadata, this.localParticipant);
224
229
  })
225
230
  .on(ParticipantEvent.TrackMuted, (pub: TrackPublication) => {
@@ -433,18 +438,27 @@ class Room extends EventEmitter {
433
438
  if (!trackId || trackId === '') trackId = mediaTrack.id;
434
439
 
435
440
  const participant = this.getOrCreateParticipant(participantId);
441
+ let adaptiveStreamSettings: AdaptiveStreamSettings | undefined;
442
+ if (this.options.adaptiveStream) {
443
+ if (typeof this.options.adaptiveStream === 'object') {
444
+ adaptiveStreamSettings = this.options.adaptiveStream;
445
+ } else {
446
+ adaptiveStreamSettings = {};
447
+ }
448
+ }
436
449
  participant.addSubscribedMediaTrack(
437
450
  mediaTrack,
438
451
  trackId,
439
452
  stream,
440
453
  receiver,
441
- this.options.adaptiveStream,
454
+ adaptiveStreamSettings,
442
455
  );
443
456
  }
444
457
 
445
458
  private handleRestarting = () => {
446
459
  this.state = RoomState.Reconnecting;
447
460
  this.emit(RoomEvent.Reconnecting);
461
+ this.emit(RoomEvent.StateChanged, this.state);
448
462
 
449
463
  // also unwind existing participants & existing subscriptions
450
464
  for (const p of this.participants.values()) {
@@ -453,8 +467,10 @@ class Room extends EventEmitter {
453
467
  };
454
468
 
455
469
  private handleRestarted = async (joinResponse: JoinResponse) => {
470
+ log.debug('reconnected to server region', joinResponse.serverRegion);
456
471
  this.state = RoomState.Connected;
457
472
  this.emit(RoomEvent.Reconnected);
473
+ this.emit(RoomEvent.StateChanged, this.state);
458
474
 
459
475
  // rehydrate participants
460
476
  if (joinResponse.participant) {
@@ -509,6 +525,7 @@ class Room extends EventEmitter {
509
525
  navigator.mediaDevices.removeEventListener('devicechange', this.handleDeviceChange);
510
526
  this.state = RoomState.Disconnected;
511
527
  this.emit(RoomEvent.Disconnected);
528
+ this.emit(RoomEvent.StateChanged, this.state);
512
529
  }
513
530
 
514
531
  private handleParticipantUpdates = (participantInfos: ParticipantInfo[]) => {
@@ -710,73 +727,78 @@ class Room extends EventEmitter {
710
727
  }
711
728
  // by using an AudioContext, it reduces lag on audio elements
712
729
  // https://stackoverflow.com/questions/9811429/html5-audio-tag-on-safari-has-a-delay/54119854#54119854
713
- // @ts-ignore
714
- const AudioContext = window.AudioContext || window.webkitAudioContext;
715
- if (AudioContext) {
716
- this.audioContext = new AudioContext();
730
+ const ctx = getNewAudioContext();
731
+ if (ctx) {
732
+ this.audioContext = ctx;
717
733
  }
718
734
  }
719
735
 
736
+ private createParticipant(id: string, info?: ParticipantInfo): RemoteParticipant {
737
+ let participant: RemoteParticipant;
738
+ if (info) {
739
+ participant = RemoteParticipant.fromParticipantInfo(
740
+ this.engine.client,
741
+ info,
742
+ );
743
+ } else {
744
+ participant = new RemoteParticipant(this.engine.client, id, '');
745
+ }
746
+ return participant;
747
+ }
748
+
720
749
  private getOrCreateParticipant(
721
750
  id: string,
722
751
  info?: ParticipantInfo,
723
752
  ): RemoteParticipant {
724
- let participant = this.participants.get(id);
725
- if (!participant) {
726
- // it's possible for the RTC track to arrive before signaling data
727
- // when this happens, we'll create the participant and make the track work
728
- if (info) {
729
- participant = RemoteParticipant.fromParticipantInfo(
730
- this.engine.client,
731
- info,
732
- );
733
- } else {
734
- participant = new RemoteParticipant(this.engine.client, id, '');
735
- }
736
- this.participants.set(id, participant);
737
- // also forward events
738
-
739
- // trackPublished is only fired for tracks added after both local participant
740
- // and remote participant joined the room
741
- participant
742
- .on(ParticipantEvent.TrackPublished, (trackPublication: RemoteTrackPublication) => {
743
- this.emit(RoomEvent.TrackPublished, trackPublication, participant);
744
- })
745
- .on(ParticipantEvent.TrackSubscribed,
746
- (track: RemoteTrack, publication: RemoteTrackPublication) => {
747
- // monitor playback status
748
- if (track.kind === Track.Kind.Audio) {
749
- track.on(TrackEvent.AudioPlaybackStarted, this.handleAudioPlaybackStarted);
750
- track.on(TrackEvent.AudioPlaybackFailed, this.handleAudioPlaybackFailed);
751
- }
752
- this.emit(RoomEvent.TrackSubscribed, track, publication, participant);
753
- })
754
- .on(ParticipantEvent.TrackUnpublished, (publication: RemoteTrackPublication) => {
755
- this.emit(RoomEvent.TrackUnpublished, publication, participant);
756
- })
757
- .on(ParticipantEvent.TrackUnsubscribed,
758
- (track: RemoteTrack, publication: RemoteTrackPublication) => {
759
- this.emit(RoomEvent.TrackUnsubscribed, track, publication, participant);
760
- })
761
- .on(ParticipantEvent.TrackSubscriptionFailed, (sid: string) => {
762
- this.emit(RoomEvent.TrackSubscriptionFailed, sid, participant);
763
- })
764
- .on(ParticipantEvent.TrackMuted, (pub: TrackPublication) => {
765
- this.emit(RoomEvent.TrackMuted, pub, participant);
766
- })
767
- .on(ParticipantEvent.TrackUnmuted, (pub: TrackPublication) => {
768
- this.emit(RoomEvent.TrackUnmuted, pub, participant);
769
- })
770
- .on(ParticipantEvent.MetadataChanged, (metadata: any) => {
771
- this.emit(RoomEvent.MetadataChanged, metadata, participant);
753
+ if (this.participants.has(id)) {
754
+ return (this.participants.get(id) as RemoteParticipant);
755
+ }
756
+ // it's possible for the RTC track to arrive before signaling data
757
+ // when this happens, we'll create the participant and make the track work
758
+ const participant = this.createParticipant(id, info);
759
+ this.participants.set(id, participant);
760
+
761
+ // also forward events
762
+ // trackPublished is only fired for tracks added after both local participant
763
+ // and remote participant joined the room
764
+ participant
765
+ .on(ParticipantEvent.TrackPublished, (trackPublication: RemoteTrackPublication) => {
766
+ this.emit(RoomEvent.TrackPublished, trackPublication, participant);
767
+ })
768
+ .on(ParticipantEvent.TrackSubscribed,
769
+ (track: RemoteTrack, publication: RemoteTrackPublication) => {
770
+ // monitor playback status
771
+ if (track.kind === Track.Kind.Audio) {
772
+ track.on(TrackEvent.AudioPlaybackStarted, this.handleAudioPlaybackStarted);
773
+ track.on(TrackEvent.AudioPlaybackFailed, this.handleAudioPlaybackFailed);
774
+ }
775
+ this.emit(RoomEvent.TrackSubscribed, track, publication, participant);
772
776
  })
773
- .on(ParticipantEvent.ParticipantMetadataChanged, (metadata: any) => {
774
- this.emit(RoomEvent.ParticipantMetadataChanged, metadata, participant);
777
+ .on(ParticipantEvent.TrackUnpublished, (publication: RemoteTrackPublication) => {
778
+ this.emit(RoomEvent.TrackUnpublished, publication, participant);
779
+ })
780
+ .on(ParticipantEvent.TrackUnsubscribed,
781
+ (track: RemoteTrack, publication: RemoteTrackPublication) => {
782
+ this.emit(RoomEvent.TrackUnsubscribed, track, publication, participant);
775
783
  })
776
- .on(ParticipantEvent.ConnectionQualityChanged, (quality: ConnectionQuality) => {
777
- this.emit(RoomEvent.ConnectionQualityChanged, quality, participant);
778
- });
779
- }
784
+ .on(ParticipantEvent.TrackSubscriptionFailed, (sid: string) => {
785
+ this.emit(RoomEvent.TrackSubscriptionFailed, sid, participant);
786
+ })
787
+ .on(ParticipantEvent.TrackMuted, (pub: TrackPublication) => {
788
+ this.emit(RoomEvent.TrackMuted, pub, participant);
789
+ })
790
+ .on(ParticipantEvent.TrackUnmuted, (pub: TrackPublication) => {
791
+ this.emit(RoomEvent.TrackUnmuted, pub, participant);
792
+ })
793
+ .on(ParticipantEvent.MetadataChanged, (metadata: string | undefined) => {
794
+ this.emit(RoomEvent.MetadataChanged, metadata, participant);
795
+ })
796
+ .on(ParticipantEvent.ParticipantMetadataChanged, (metadata: string | undefined) => {
797
+ this.emit(RoomEvent.ParticipantMetadataChanged, metadata, participant);
798
+ })
799
+ .on(ParticipantEvent.ConnectionQualityChanged, (quality: ConnectionQuality) => {
800
+ this.emit(RoomEvent.ConnectionQualityChanged, quality, participant);
801
+ });
780
802
  return participant;
781
803
  }
782
804
 
@@ -813,6 +835,7 @@ class Room extends EventEmitter {
813
835
  participantTracks: [],
814
836
  },
815
837
  publishTracks: this.localParticipant.publishedTracksInfo(),
838
+ dataChannels: this.localParticipant.dataChannelsInfo(),
816
839
  });
817
840
  }
818
841
 
@@ -830,11 +853,74 @@ class Room extends EventEmitter {
830
853
  }
831
854
  }
832
855
 
833
- /** @internal */
834
- emit(event: string | symbol, ...args: any[]): boolean {
856
+ // /** @internal */
857
+ emit<E extends keyof RoomEventCallbacks>(
858
+ event: E, ...args: Parameters<RoomEventCallbacks[E]>
859
+ ): boolean {
835
860
  log.debug('room event', event, ...args);
836
861
  return super.emit(event, ...args);
837
862
  }
838
863
  }
839
864
 
840
865
  export default Room;
866
+
867
+ export type RoomEventCallbacks = {
868
+ reconnecting: () => void,
869
+ reconnected: () => void,
870
+ disconnected: () => void,
871
+ stateChanged: (state: RoomState) => void,
872
+ mediaDevicesChanged: () => void,
873
+ participantConnected: (participant: RemoteParticipant) => void,
874
+ participantDisconnected: (participant: RemoteParticipant) => void,
875
+ trackPublished: (publication: RemoteTrackPublication, participant: RemoteParticipant) => void,
876
+ trackSubscribed: (
877
+ track: RemoteTrack,
878
+ publication: RemoteTrackPublication,
879
+ participant: RemoteParticipant
880
+ ) => void,
881
+ trackSubscriptionFailed: (trackSid: string, participant: RemoteParticipant) => void,
882
+ trackUnpublished: (publication: RemoteTrackPublication, participant: RemoteParticipant) => void,
883
+ trackUnsubscribed: (
884
+ track: RemoteTrack,
885
+ publication: RemoteTrackPublication,
886
+ participant: RemoteParticipant,
887
+ ) => void,
888
+ trackMuted: (publication: TrackPublication, participant: Participant) => void,
889
+ trackUnmuted: (publication: TrackPublication, participant: Participant) => void,
890
+ localTrackPublished: (publication: LocalTrackPublication, participant: LocalParticipant) => void,
891
+ localTrackUnpublished: (
892
+ publication: LocalTrackPublication,
893
+ participant: LocalParticipant
894
+ ) => void,
895
+ /**
896
+ * @deprecated use [[participantMetadataChanged]] instead
897
+ */
898
+ metadataChanged: (
899
+ metadata: string | undefined,
900
+ participant?: RemoteParticipant | LocalParticipant
901
+ ) => void,
902
+ participantMetadataChanged: (
903
+ metadata: string | undefined,
904
+ participant: RemoteParticipant | LocalParticipant
905
+ ) => void,
906
+ activeSpeakersChanged: (speakers: Array<Participant>) => void,
907
+ roomMetadataChanged: (metadata: string) => void,
908
+ dataReceived: (
909
+ payload: Uint8Array,
910
+ participant?: RemoteParticipant,
911
+ kind?: DataPacket_Kind
912
+ ) => void,
913
+ connectionQualityChanged: (quality: ConnectionQuality, participant: Participant) => void,
914
+ mediaDevicesError: (error: Error) => void,
915
+ trackStreamStateChanged: (
916
+ publication: RemoteTrackPublication,
917
+ streamState: Track.StreamState,
918
+ participant: RemoteParticipant,
919
+ ) => void,
920
+ trackSubscriptionPermissionChanged: (
921
+ publication: RemoteTrackPublication,
922
+ status: TrackPublication.SubscriptionStatus,
923
+ participant: RemoteParticipant,
924
+ ) => void,
925
+ audioPlaybackChanged: (playing: boolean) => void,
926
+ };
@@ -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
@@ -177,7 +185,7 @@ export enum RoomEvent {
177
185
  ConnectionQualityChanged = 'connectionQualityChanged',
178
186
 
179
187
  /**
180
- * StreamState indicates if a subscribed track has been paused by the SFU
188
+ * StreamState indicates if a subscribed (remote) track has been paused by the SFU
181
189
  * (typically this happens because of subscriber's bandwidth constraints)
182
190
  *
183
191
  * When bandwidth conditions allow, the track will be resumed automatically.
@@ -366,6 +374,7 @@ 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
380
  Resuming = 'resuming',
@@ -391,6 +400,11 @@ export enum TrackEvent {
391
400
  AudioPlaybackStarted = 'audioPlaybackStarted',
392
401
  /** @internal */
393
402
  AudioPlaybackFailed = 'audioPlaybackFailed',
403
+ /**
404
+ * @internal
405
+ * Only fires on LocalAudioTrack instances
406
+ */
407
+ AudioSilenceDetected = 'audioSilenceDetected',
394
408
  /** @internal */
395
409
  VisibilityChanged = 'visibilityChanged',
396
410
  /** @internal */
@@ -3,7 +3,9 @@ import { RoomOptions } from '../../options';
3
3
  import {
4
4
  DataPacket, DataPacket_Kind,
5
5
  } from '../../proto/livekit_models';
6
- import { AddTrackRequest, SubscribedQualityUpdate, TrackPublishedResponse } from '../../proto/livekit_rtc';
6
+ import {
7
+ AddTrackRequest, DataChannelInfo, SubscribedQualityUpdate, TrackPublishedResponse,
8
+ } from '../../proto/livekit_rtc';
7
9
  import {
8
10
  TrackInvalidError,
9
11
  UnexpectedConnectionState,
@@ -17,7 +19,8 @@ import LocalVideoTrack, { videoLayersFromEncodings } from '../track/LocalVideoTr
17
19
  import {
18
20
  CreateLocalTracksOptions,
19
21
  ScreenShareCaptureOptions,
20
- TrackPublishOptions, VideoCodec, VideoPresets,
22
+ ScreenSharePresets,
23
+ TrackPublishOptions, VideoCodec,
21
24
  } from '../track/options';
22
25
  import { Track } from '../track/Track';
23
26
  import { constraintsForOptions, mergeDefaultOptions } from '../track/utils';
@@ -272,7 +275,7 @@ export default class LocalParticipant extends Participant {
272
275
  options = {};
273
276
  }
274
277
  if (options.resolution === undefined) {
275
- options.resolution = VideoPresets.fhd.resolution;
278
+ options.resolution = ScreenSharePresets.h1080fps15.resolution;
276
279
  }
277
280
 
278
281
  let videoConstraints: MediaTrackConstraints | boolean = true;
@@ -525,7 +528,7 @@ export default class LocalParticipant extends Participant {
525
528
  destination?: RemoteParticipant[] | string[]) {
526
529
  const dest: string[] = [];
527
530
  if (destination !== undefined) {
528
- destination.forEach((val : any) => {
531
+ destination.forEach((val: any) => {
529
532
  if (val instanceof RemoteParticipant) {
530
533
  dest.push(val.sid);
531
534
  } else {
@@ -681,4 +684,20 @@ export default class LocalParticipant extends Participant {
681
684
  });
682
685
  return infos;
683
686
  }
687
+
688
+ /** @internal */
689
+ dataChannelsInfo(): DataChannelInfo[] {
690
+ const infos: DataChannelInfo[] = [];
691
+ const getInfo = (dc: RTCDataChannel | undefined) => {
692
+ if (dc?.id !== undefined && dc.id !== null) {
693
+ infos.push({
694
+ label: dc.label,
695
+ id: dc.id,
696
+ });
697
+ }
698
+ };
699
+ getInfo(this.engine.dataChannelForKind(DataPacket_Kind.LOSSY));
700
+ getInfo(this.engine.dataChannelForKind(DataPacket_Kind.RELIABLE));
701
+ return infos;
702
+ }
684
703
  }
@@ -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
+ };