@livekit/rtc-node 0.13.19 → 0.13.21

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/Cargo.toml +6 -3
  2. package/dist/audio_source.cjs +1 -0
  3. package/dist/audio_source.cjs.map +1 -1
  4. package/dist/audio_source.d.ts.map +1 -1
  5. package/dist/audio_source.js +1 -0
  6. package/dist/audio_source.js.map +1 -1
  7. package/dist/index.d.cts +1 -0
  8. package/dist/index.d.ts +1 -0
  9. package/dist/participant.cjs +43 -32
  10. package/dist/participant.cjs.map +1 -1
  11. package/dist/participant.d.cts +3 -0
  12. package/dist/participant.d.ts +3 -0
  13. package/dist/participant.d.ts.map +1 -1
  14. package/dist/participant.js +43 -32
  15. package/dist/participant.js.map +1 -1
  16. package/dist/proto/data_stream_pb.cjs +91 -2
  17. package/dist/proto/data_stream_pb.cjs.map +1 -1
  18. package/dist/proto/data_stream_pb.d.cts +92 -1
  19. package/dist/proto/data_stream_pb.d.ts +92 -1
  20. package/dist/proto/data_stream_pb.d.ts.map +1 -1
  21. package/dist/proto/data_stream_pb.js +88 -2
  22. package/dist/proto/data_stream_pb.js.map +1 -1
  23. package/dist/proto/ffi_pb.cjs +6 -3
  24. package/dist/proto/ffi_pb.cjs.map +1 -1
  25. package/dist/proto/ffi_pb.d.cts +19 -1
  26. package/dist/proto/ffi_pb.d.ts +19 -1
  27. package/dist/proto/ffi_pb.d.ts.map +1 -1
  28. package/dist/proto/ffi_pb.js +7 -4
  29. package/dist/proto/ffi_pb.js.map +1 -1
  30. package/dist/proto/room_pb.cjs +32 -2
  31. package/dist/proto/room_pb.cjs.map +1 -1
  32. package/dist/proto/room_pb.d.cts +38 -2
  33. package/dist/proto/room_pb.d.ts +38 -2
  34. package/dist/proto/room_pb.d.ts.map +1 -1
  35. package/dist/proto/room_pb.js +31 -2
  36. package/dist/proto/room_pb.js.map +1 -1
  37. package/dist/room.cjs +300 -205
  38. package/dist/room.cjs.map +1 -1
  39. package/dist/room.d.cts +16 -3
  40. package/dist/room.d.ts +16 -3
  41. package/dist/room.d.ts.map +1 -1
  42. package/dist/room.js +301 -206
  43. package/dist/room.js.map +1 -1
  44. package/dist/version.cjs +1 -1
  45. package/dist/version.cjs.map +1 -1
  46. package/dist/version.d.cts +1 -1
  47. package/dist/version.d.ts +1 -1
  48. package/dist/version.js +1 -1
  49. package/dist/version.js.map +1 -1
  50. package/package.json +6 -6
  51. package/src/audio_source.ts +1 -0
  52. package/src/participant.ts +48 -30
  53. package/src/proto/data_stream_pb.ts +159 -0
  54. package/src/proto/ffi_pb.ts +22 -1
  55. package/src/proto/room_pb.ts +64 -1
  56. package/src/room.ts +319 -214
  57. package/src/version.ts +1 -1
package/src/room.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  // SPDX-FileCopyrightText: 2024 LiveKit, Inc.
2
2
  //
3
3
  // SPDX-License-Identifier: Apache-2.0
4
+ import { Mutex } from '@livekit/mutex';
4
5
  import type { TypedEventEmitter as TypedEmitter } from '@livekit/typed-emitter';
5
6
  import EventEmitter from 'events';
6
7
  import { ByteStreamReader, TextStreamReader } from './data_streams/stream_reader.js';
@@ -17,10 +18,10 @@ import { FfiClient, FfiClientEvent, FfiHandle } from './ffi_client.js';
17
18
  import { log } from './log.js';
18
19
  import type { Participant } from './participant.js';
19
20
  import { LocalParticipant, RemoteParticipant } from './participant.js';
20
- import { EncryptionState } from './proto/e2ee_pb.js';
21
+ import { EncryptionState, EncryptionType } from './proto/e2ee_pb.js';
21
22
  import type { FfiEvent } from './proto/ffi_pb.js';
22
23
  import type { DisconnectReason, OwnedParticipant } from './proto/participant_pb.js';
23
- import type { DataStream_Trailer } from './proto/room_pb.js';
24
+ import type { DataStream_Trailer, DisconnectCallback } from './proto/room_pb.js';
24
25
  import {
25
26
  type ConnectCallback,
26
27
  ConnectRequest,
@@ -60,7 +61,11 @@ export const defaultRtcConfiguration: RtcConfiguration = {
60
61
  export interface RoomOptions {
61
62
  autoSubscribe: boolean;
62
63
  dynacast: boolean;
64
+ /**
65
+ * @deprecated Use `encryption` instead. See x for details
66
+ */
63
67
  e2ee?: E2EEOptions;
68
+ encryption?: E2EEOptions;
64
69
  rtcConfig?: RtcConfiguration;
65
70
  }
66
71
 
@@ -68,6 +73,7 @@ export const defaultRoomOptions = new FfiRoomOptions({
68
73
  autoSubscribe: true,
69
74
  dynacast: false,
70
75
  e2ee: undefined,
76
+ encryption: undefined,
71
77
  rtcConfig: undefined,
72
78
  adaptiveStream: false,
73
79
  joinRetries: 1,
@@ -76,6 +82,12 @@ export const defaultRoomOptions = new FfiRoomOptions({
76
82
  export class Room extends (EventEmitter as new () => TypedEmitter<RoomCallbacks>) {
77
83
  private info?: RoomInfo;
78
84
  private ffiHandle?: FfiHandle;
85
+ /**
86
+ * used to ensure events are processed sequentially and allow for
87
+ * the local participant to acquire the lock while doing state updates related to FFI events
88
+ * before processing the next events
89
+ */
90
+ private roomEventLock = new Mutex();
79
91
 
80
92
  private byteStreamControllers = new Map<string, StreamController<DataStream_Chunk>>();
81
93
  private textStreamControllers = new Map<string, StreamController<DataStream_Chunk>>();
@@ -174,7 +186,10 @@ export class Room extends (EventEmitter as new () => TypedEmitter<RoomCallbacks>
174
186
  */
175
187
  async connect(url: string, token: string, opts?: RoomOptions) {
176
188
  const options = { ...defaultRoomOptions, ...opts };
177
- const e2eeOptions = { ...defaultE2EEOptions, ...options.e2ee };
189
+ const e2eeEnabled = options.encryption || options.e2ee;
190
+ const e2eeOptions = options.encryption
191
+ ? { ...defaultE2EEOptions, ...options.encryption }
192
+ : { ...defaultE2EEOptions, ...options.e2ee };
178
193
 
179
194
  const req = new ConnectRequest({
180
195
  url: url,
@@ -200,11 +215,14 @@ export class Room extends (EventEmitter as new () => TypedEmitter<RoomCallbacks>
200
215
  switch (cb.message.case) {
201
216
  case 'result':
202
217
  this.ffiHandle = new FfiHandle(cb.message.value.room!.handle!.id!);
203
- this.e2eeManager = options.e2ee && new E2EEManager(this.ffiHandle.handle, e2eeOptions);
218
+ this.e2eeManager = e2eeEnabled && new E2EEManager(this.ffiHandle.handle, e2eeOptions);
204
219
 
205
220
  this.info = cb.message.value.room!.info;
206
221
  this.connectionState = ConnectionState.CONN_CONNECTED;
207
- this.localParticipant = new LocalParticipant(cb.message.value.localParticipant!);
222
+ this.localParticipant = new LocalParticipant(
223
+ cb.message.value.localParticipant!,
224
+ this.roomEventLock,
225
+ );
208
226
 
209
227
  for (const pt of cb.message.value.participants) {
210
228
  const rp = this.createRemoteParticipant(pt.participant!);
@@ -230,7 +248,7 @@ export class Room extends (EventEmitter as new () => TypedEmitter<RoomCallbacks>
230
248
  return;
231
249
  }
232
250
 
233
- FfiClient.instance.request<DisconnectResponse>({
251
+ const res = FfiClient.instance.request<DisconnectResponse>({
234
252
  message: {
235
253
  case: 'disconnect',
236
254
  value: {
@@ -239,6 +257,10 @@ export class Room extends (EventEmitter as new () => TypedEmitter<RoomCallbacks>
239
257
  },
240
258
  });
241
259
 
260
+ await FfiClient.instance.waitFor<DisconnectCallback>((ev: FfiEvent) => {
261
+ return ev.message.case == 'connect' && ev.message.value.asyncId == res.asyncId;
262
+ });
263
+
242
264
  FfiClient.instance.removeListener(FfiClientEvent.FfiEvent, this.onFfiEvent);
243
265
  this.removeAllListeners();
244
266
  }
@@ -279,7 +301,7 @@ export class Room extends (EventEmitter as new () => TypedEmitter<RoomCallbacks>
279
301
  this.byteStreamHandlers.delete(topic);
280
302
  }
281
303
 
282
- private onFfiEvent = (ffiEvent: FfiEvent) => {
304
+ private onFfiEvent = async (ffiEvent: FfiEvent) => {
283
305
  if (!this.localParticipant || !this.ffiHandle || !this.info) {
284
306
  this.preConnectEvents.push(ffiEvent);
285
307
  return;
@@ -287,234 +309,314 @@ export class Room extends (EventEmitter as new () => TypedEmitter<RoomCallbacks>
287
309
 
288
310
  // process preConnectEvents if we received the connectCallback after the events were queued
289
311
  for (const ev of this.preConnectEvents) {
290
- this.processFfiEvent(ev);
312
+ await this.processFfiEvent(ev);
291
313
  }
292
314
  this.preConnectEvents = [];
293
315
 
294
- this.processFfiEvent(ffiEvent);
316
+ await this.processFfiEvent(ffiEvent);
295
317
  };
296
318
 
297
- private processFfiEvent = (ffiEvent: FfiEvent) => {
319
+ private processFfiEvent = async (ffiEvent: FfiEvent) => {
298
320
  if (!this.localParticipant || !this.ffiHandle || !this.info) {
299
321
  throw new Error('processFfiEvent called before connect');
300
322
  }
301
323
 
302
- if (ffiEvent.message.case == 'rpcMethodInvocation') {
303
- if (
304
- ffiEvent.message.value.localParticipantHandle == this.localParticipant.ffi_handle.handle
324
+ const unlock = await this.roomEventLock.lock();
325
+
326
+ try {
327
+ if (ffiEvent.message.case == 'rpcMethodInvocation') {
328
+ if (
329
+ ffiEvent.message.value.localParticipantHandle == this.localParticipant.ffi_handle.handle
330
+ ) {
331
+ this.localParticipant.handleRpcMethodInvocation(
332
+ ffiEvent.message.value.invocationId!,
333
+ ffiEvent.message.value.method!,
334
+ ffiEvent.message.value.requestId!,
335
+ ffiEvent.message.value.callerIdentity!,
336
+ ffiEvent.message.value.payload!,
337
+ ffiEvent.message.value.responseTimeoutMs!,
338
+ );
339
+ }
340
+ return;
341
+ } else if (
342
+ ffiEvent.message.case != 'roomEvent' ||
343
+ ffiEvent.message.value.roomHandle != this.ffiHandle.handle
305
344
  ) {
306
- this.localParticipant.handleRpcMethodInvocation(
307
- ffiEvent.message.value.invocationId!,
308
- ffiEvent.message.value.method!,
309
- ffiEvent.message.value.requestId!,
310
- ffiEvent.message.value.callerIdentity!,
311
- ffiEvent.message.value.payload!,
312
- ffiEvent.message.value.responseTimeoutMs!,
313
- );
345
+ return;
314
346
  }
315
- return;
316
- } else if (
317
- ffiEvent.message.case != 'roomEvent' ||
318
- ffiEvent.message.value.roomHandle != this.ffiHandle.handle
319
- ) {
320
- return;
321
- }
322
347
 
323
- const ev = ffiEvent.message.value.message;
324
- if (process.env.LIVEKIT_DEBUG_LOG_ROOM_EVENTS) {
325
- console.log('Room event:', ev);
326
- }
327
- if (ev.case == 'participantConnected') {
328
- const participant = this.createRemoteParticipant(ev.value.info!);
329
- this.remoteParticipants.set(participant.identity!, participant);
330
- this.emit(RoomEvent.ParticipantConnected, participant);
331
- } else if (ev.case == 'participantDisconnected') {
332
- const participant = this.remoteParticipants.get(ev.value.participantIdentity!);
333
- this.remoteParticipants.delete(participant!.identity);
334
- participant!.info.disconnectReason = ev.value.disconnectReason;
335
- this.emit(RoomEvent.ParticipantDisconnected, participant!);
336
- } else if (ev.case == 'localTrackPublished') {
337
- const publication = this.localParticipant!.trackPublications.get(ev.value.trackSid!);
338
- this.emit(RoomEvent.LocalTrackPublished, publication!, this.localParticipant!);
339
- } else if (ev.case == 'localTrackUnpublished') {
340
- const publication = this.localParticipant!.trackPublications.get(ev.value.publicationSid!);
341
- this.localParticipant!.trackPublications.delete(ev.value.publicationSid!);
342
- this.emit(RoomEvent.LocalTrackUnpublished, publication!, this.localParticipant!);
343
- } else if (ev.case == 'localTrackSubscribed') {
344
- const publication = this.localParticipant!.trackPublications.get(ev.value.trackSid!);
345
- publication!.resolveFirstSubscription();
346
- this.emit(RoomEvent.LocalTrackSubscribed, publication!.track!);
347
- } else if (ev.case == 'trackPublished') {
348
- const participant = this.remoteParticipants.get(ev.value.participantIdentity!);
349
- const publication = new RemoteTrackPublication(ev.value.publication!);
350
- participant!.trackPublications.set(publication.sid!, publication);
351
- this.emit(RoomEvent.TrackPublished, publication, participant!);
352
- } else if (ev.case == 'trackUnpublished') {
353
- const participant = this.requireRemoteParticipant(ev.value.participantIdentity!);
354
- const publication = participant.trackPublications.get(ev.value.publicationSid!);
355
- participant.trackPublications.delete(ev.value.publicationSid!);
356
- if (publication) {
357
- this.emit(RoomEvent.TrackUnpublished, publication, participant);
358
- }
359
- } else if (ev.case == 'trackSubscribed') {
360
- const ownedTrack = ev.value.track!;
361
- const trackInfo = ownedTrack.info!;
362
- const { participant, publication } = this.requirePublicationOfRemoteParticipant(
363
- ev.value.participantIdentity!,
364
- trackInfo.sid!,
365
- );
366
- publication.subscribed = true;
367
- if (trackInfo.kind == TrackKind.KIND_VIDEO) {
368
- publication.track = new RemoteVideoTrack(ownedTrack);
369
- } else if (trackInfo.kind == TrackKind.KIND_AUDIO) {
370
- publication.track = new RemoteAudioTrack(ownedTrack);
348
+ const ev = ffiEvent.message.value.message;
349
+ if (process.env.LIVEKIT_DEBUG_LOG_ROOM_EVENTS) {
350
+ console.log('Room event:', ev);
371
351
  }
352
+ if (ev.case == 'participantConnected') {
353
+ const participant = this.createRemoteParticipant(ev.value.info!);
354
+ this.remoteParticipants.set(participant.identity!, participant);
355
+ this.emit(RoomEvent.ParticipantConnected, participant);
356
+ } else if (ev.case == 'participantDisconnected') {
357
+ const participant = this.remoteParticipants.get(ev.value.participantIdentity!);
358
+ if (participant) {
359
+ this.remoteParticipants.delete(participant.identity);
360
+ participant.info.disconnectReason = ev.value.disconnectReason;
361
+ this.emit(RoomEvent.ParticipantDisconnected, participant);
362
+ } else {
363
+ console.log(`RoomEvent.ParticipantDisconnected: Could not find participant`);
364
+ }
365
+ } else if (ev.case == 'localTrackPublished') {
366
+ const publication = this.localParticipant.trackPublications.get(ev.value.trackSid!);
367
+ this.emit(RoomEvent.LocalTrackPublished, publication!, this.localParticipant);
368
+ } else if (ev.case == 'localTrackUnpublished') {
369
+ const publication = this.localParticipant.trackPublications.get(ev.value.publicationSid!);
370
+ this.localParticipant.trackPublications.delete(ev.value.publicationSid!);
371
+ this.emit(RoomEvent.LocalTrackUnpublished, publication!, this.localParticipant!);
372
+ } else if (ev.case == 'localTrackSubscribed') {
373
+ const publication = this.localParticipant.trackPublications.get(ev.value.trackSid!);
374
+ if (publication) {
375
+ publication.resolveFirstSubscription();
376
+ this.emit(RoomEvent.LocalTrackSubscribed, publication!.track!);
377
+ } else {
378
+ console.warn(
379
+ `RoomEvent.LocalTrackSubscribed: Publication not found: ${ev.value.trackSid}`,
380
+ );
381
+ }
382
+ } else if (ev.case == 'trackPublished') {
383
+ const participant = this.remoteParticipants.get(ev.value.participantIdentity!);
384
+ const publication = new RemoteTrackPublication(ev.value.publication!);
385
+ if (participant) {
386
+ participant.trackPublications.set(publication.sid!, publication);
387
+ } else {
388
+ console.warn(
389
+ `RoomEvent.TrackPublished: Could not find participant: ${ev.value.participantIdentity}`,
390
+ );
391
+ }
392
+ this.emit(RoomEvent.TrackPublished, publication, participant!);
393
+ } else if (ev.case == 'trackUnpublished') {
394
+ const participant = this.requireRemoteParticipant(ev.value.participantIdentity!);
395
+ const publication = participant.trackPublications.get(ev.value.publicationSid!);
396
+ participant.trackPublications.delete(ev.value.publicationSid!);
397
+ if (publication) {
398
+ this.emit(RoomEvent.TrackUnpublished, publication, participant);
399
+ } else {
400
+ console.warn(`RoomEvent.TrackUnpublished: Could not find publication`);
401
+ }
402
+ } else if (ev.case == 'trackSubscribed') {
403
+ const ownedTrack = ev.value.track!;
404
+ const trackInfo = ownedTrack.info!;
405
+ try {
406
+ const { participant, publication } = this.requirePublicationOfRemoteParticipant(
407
+ ev.value.participantIdentity!,
408
+ trackInfo.sid!,
409
+ );
410
+ publication.subscribed = true;
411
+ if (trackInfo.kind == TrackKind.KIND_VIDEO) {
412
+ publication.track = new RemoteVideoTrack(ownedTrack);
413
+ } else if (trackInfo.kind == TrackKind.KIND_AUDIO) {
414
+ publication.track = new RemoteAudioTrack(ownedTrack);
415
+ }
372
416
 
373
- this.emit(RoomEvent.TrackSubscribed, publication.track!, publication, participant);
374
- } else if (ev.case == 'trackUnsubscribed') {
375
- const { participant, publication } = this.requirePublicationOfRemoteParticipant(
376
- ev.value.participantIdentity!,
377
- ev.value.trackSid!,
378
- );
379
- const track = publication.track!;
380
- publication.track = undefined;
381
- publication.subscribed = false;
382
- this.emit(RoomEvent.TrackUnsubscribed, track, publication, participant);
383
- } else if (ev.case == 'trackSubscriptionFailed') {
384
- const participant = this.requireRemoteParticipant(ev.value.participantIdentity!);
385
- this.emit(RoomEvent.TrackSubscriptionFailed, ev.value.trackSid!, participant, ev.value.error);
386
- } else if (ev.case == 'trackMuted') {
387
- const { participant, publication } = this.requirePublicationOfParticipant(
388
- ev.value.participantIdentity!,
389
- ev.value.trackSid!,
390
- );
391
- publication.info!.muted = true;
392
- if (publication.track) {
393
- publication.track.info!.muted = true;
394
- }
395
- this.emit(RoomEvent.TrackMuted, publication, participant);
396
- } else if (ev.case == 'trackUnmuted') {
397
- const { participant, publication } = this.requirePublicationOfParticipant(
398
- ev.value.participantIdentity!,
399
- ev.value.trackSid!,
400
- );
401
- publication.info!.muted = false;
402
- if (publication.track) {
403
- publication.track.info!.muted = false;
404
- }
405
- this.emit(RoomEvent.TrackUnmuted, publication, participant);
406
- } else if (ev.case == 'activeSpeakersChanged') {
407
- const activeSpeakers = ev.value.participantIdentities.map((identity) =>
408
- this.requireParticipantByIdentity(identity),
409
- );
410
- this.emit(RoomEvent.ActiveSpeakersChanged, activeSpeakers);
411
- } else if (ev.case == 'roomMetadataChanged') {
412
- this.info!.metadata = ev.value.metadata;
413
- this.emit(RoomEvent.RoomMetadataChanged, this.info!.metadata!);
414
- } else if (ev.case == 'participantMetadataChanged') {
415
- const participant = this.requireParticipantByIdentity(ev.value.participantIdentity!);
416
- participant.info.metadata = ev.value.metadata;
417
- this.emit(RoomEvent.ParticipantMetadataChanged, participant.metadata, participant);
418
- } else if (ev.case == 'participantNameChanged') {
419
- const participant = this.requireParticipantByIdentity(ev.value.participantIdentity!);
420
- participant.info.name = ev.value.name;
421
- this.emit(RoomEvent.ParticipantNameChanged, participant.name!, participant);
422
- } else if (ev.case == 'participantAttributesChanged') {
423
- const participant = this.requireParticipantByIdentity(ev.value.participantIdentity!);
424
- participant.info.attributes = ev.value.attributes.reduce(
425
- (acc, value) => {
426
- acc[value.key!] = value.value!;
427
- return acc;
428
- },
429
- {} as Record<string, string>,
430
- );
431
- if (Object.keys(ev.value.changedAttributes).length > 0) {
432
- const changedAttributes = ev.value.changedAttributes.reduce(
433
- (acc, value) => {
434
- acc[value.key!] = value.value!;
435
- return acc;
436
- },
437
- {} as Record<string, string>,
438
- );
439
- this.emit(RoomEvent.ParticipantAttributesChanged, changedAttributes, participant);
440
- }
441
- } else if (ev.case == 'connectionQualityChanged') {
442
- const participant = this.requireParticipantByIdentity(ev.value.participantIdentity!);
443
- this.emit(RoomEvent.ConnectionQualityChanged, ev.value.quality!, participant);
444
- } else if (ev.case == 'chatMessage') {
445
- const participant = this.retrieveParticipantByIdentity(ev.value.participantIdentity!);
446
- const { id, message: messageText, timestamp, editTimestamp, generated } = ev.value.message!;
447
- const message: ChatMessage = {
448
- id: id!,
449
- message: messageText!,
450
- timestamp: Number(timestamp),
451
- editTimestamp: Number(editTimestamp),
452
- generated,
453
- };
454
- this.emit(RoomEvent.ChatMessage, message, participant);
455
- } else if (ev.case == 'dataPacketReceived') {
456
- // Can be undefined if the data is sent from a Server SDK
457
- const participant = this.remoteParticipants.get(ev.value.participantIdentity!);
458
- const dataPacket = ev.value.value;
459
- switch (dataPacket.case) {
460
- case 'user':
461
- const buffer = FfiClient.instance.copyBuffer(
462
- dataPacket.value.data!.data!.dataPtr!,
463
- Number(dataPacket.value.data!.data!.dataLen),
417
+ this.emit(RoomEvent.TrackSubscribed, publication.track!, publication, participant);
418
+ } catch (e: any) {
419
+ console.warn(`RoomEvent.TrackSubscribed: ${e.message}`);
420
+ }
421
+ } else if (ev.case == 'trackUnsubscribed') {
422
+ try {
423
+ const { participant, publication } = this.requirePublicationOfRemoteParticipant(
424
+ ev.value.participantIdentity!,
425
+ ev.value.trackSid!,
464
426
  );
465
- new FfiHandle(dataPacket.value.data!.handle!.id!).dispose();
427
+ const track = publication.track!;
428
+ publication.track = undefined;
429
+ publication.subscribed = false;
430
+ this.emit(RoomEvent.TrackUnsubscribed, track, publication, participant);
431
+ } catch (e: any) {
432
+ console.warn(`RoomEvent.TrackUnsubscribed: ${e.message}`);
433
+ }
434
+ } else if (ev.case == 'trackSubscriptionFailed') {
435
+ try {
436
+ const participant = this.requireRemoteParticipant(ev.value.participantIdentity!);
466
437
  this.emit(
467
- RoomEvent.DataReceived,
468
- buffer,
438
+ RoomEvent.TrackSubscriptionFailed,
439
+ ev.value.trackSid!,
469
440
  participant,
470
- ev.value.kind,
471
- dataPacket.value.topic,
441
+ ev.value.error,
472
442
  );
473
- break;
474
- case 'sipDtmf':
475
- const { code, digit } = dataPacket.value;
476
- this.emit(RoomEvent.DtmfReceived, code!, digit!, participant!);
477
- break;
478
- default:
479
- break;
480
- }
481
- } else if (ev.case == 'e2eeStateChanged') {
482
- if (ev.value.state == EncryptionState.INTERNAL_ERROR) {
483
- // throw generic error until Rust SDK is updated to supply the error alongside INTERNAL_ERROR
484
- this.emit(RoomEvent.EncryptionError, new Error('internal server error'));
485
- }
486
- } else if (ev.case == 'connectionStateChanged') {
487
- this.connectionState = ev.value.state!;
488
- this.emit(RoomEvent.ConnectionStateChanged, this.connectionState);
489
- /*} else if (ev.case == 'connected') {
443
+ } catch (e: any) {
444
+ console.warn(`RoomEvent.TrackSubscriptionFailed: ${e.message}`);
445
+ }
446
+ } else if (ev.case == 'trackMuted') {
447
+ try {
448
+ const { participant, publication } = this.requirePublicationOfParticipant(
449
+ ev.value.participantIdentity!,
450
+ ev.value.trackSid!,
451
+ );
452
+ publication.info!.muted = true;
453
+ if (publication.track) {
454
+ publication.track.info!.muted = true;
455
+ }
456
+ this.emit(RoomEvent.TrackMuted, publication, participant);
457
+ } catch (e: any) {
458
+ console.warn(`RoomEvent.TrackMuted: ${e.message}`);
459
+ }
460
+ } else if (ev.case == 'trackUnmuted') {
461
+ try {
462
+ const { participant, publication } = this.requirePublicationOfParticipant(
463
+ ev.value.participantIdentity!,
464
+ ev.value.trackSid!,
465
+ );
466
+ publication.info!.muted = false;
467
+ if (publication.track) {
468
+ publication.track.info!.muted = false;
469
+ }
470
+ this.emit(RoomEvent.TrackUnmuted, publication, participant);
471
+ } catch (e: any) {
472
+ console.warn(`RoomEvent.TrackUnmuted: ${e.message}`);
473
+ }
474
+ } else if (ev.case == 'activeSpeakersChanged') {
475
+ try {
476
+ const activeSpeakers = ev.value.participantIdentities.map((identity) =>
477
+ this.requireParticipantByIdentity(identity),
478
+ );
479
+ this.emit(RoomEvent.ActiveSpeakersChanged, activeSpeakers);
480
+ } catch (e: any) {
481
+ console.warn(`RoomEvent.ActiveSpeakersChanged: ${e.message}`);
482
+ }
483
+ } else if (ev.case == 'roomMetadataChanged') {
484
+ this.info.metadata = ev.value.metadata ?? '';
485
+ this.emit(RoomEvent.RoomMetadataChanged, this.info.metadata);
486
+ } else if (ev.case == 'participantMetadataChanged') {
487
+ try {
488
+ const participant = this.requireParticipantByIdentity(ev.value.participantIdentity!);
489
+ participant.info.metadata = ev.value.metadata;
490
+ this.emit(RoomEvent.ParticipantMetadataChanged, participant.metadata, participant);
491
+ } catch (e: any) {
492
+ console.warn(`RoomEvent.ParticipantMetadataChanged: ${e.message}`);
493
+ }
494
+ } else if (ev.case == 'participantNameChanged') {
495
+ try {
496
+ const participant = this.requireParticipantByIdentity(ev.value.participantIdentity!);
497
+ participant.info.name = ev.value.name;
498
+ this.emit(RoomEvent.ParticipantNameChanged, participant.name!, participant);
499
+ } catch (e: any) {
500
+ console.warn(`RoomEvent.ParticipantNameChanged: ${e.message}`);
501
+ }
502
+ } else if (ev.case == 'participantAttributesChanged') {
503
+ try {
504
+ const participant = this.requireParticipantByIdentity(ev.value.participantIdentity!);
505
+ participant.info.attributes = ev.value.attributes.reduce(
506
+ (acc, value) => {
507
+ acc[value.key!] = value.value!;
508
+ return acc;
509
+ },
510
+ {} as Record<string, string>,
511
+ );
512
+ if (Object.keys(ev.value.changedAttributes).length > 0) {
513
+ const changedAttributes = ev.value.changedAttributes.reduce(
514
+ (acc, value) => {
515
+ acc[value.key!] = value.value!;
516
+ return acc;
517
+ },
518
+ {} as Record<string, string>,
519
+ );
520
+ this.emit(RoomEvent.ParticipantAttributesChanged, changedAttributes, participant);
521
+ }
522
+ } catch (e: any) {
523
+ console.warn(`RoomEvent.ParticipantAttributesChanged: ${e.message}`);
524
+ }
525
+ } else if (ev.case == 'connectionQualityChanged') {
526
+ try {
527
+ const participant = this.requireParticipantByIdentity(ev.value.participantIdentity!);
528
+ this.emit(RoomEvent.ConnectionQualityChanged, ev.value.quality!, participant);
529
+ } catch (e: any) {
530
+ console.warn(`RoomEvent.ConnectionQualityChanged: ${e.message}`);
531
+ }
532
+ } else if (ev.case == 'chatMessage') {
533
+ const participant = this.retrieveParticipantByIdentity(ev.value.participantIdentity!);
534
+ const { id, message: messageText, timestamp, editTimestamp, generated } = ev.value.message!;
535
+ const message: ChatMessage = {
536
+ id: id!,
537
+ message: messageText!,
538
+ timestamp: Number(timestamp),
539
+ editTimestamp: Number(editTimestamp),
540
+ generated,
541
+ };
542
+ this.emit(RoomEvent.ChatMessage, message, participant);
543
+ } else if (ev.case == 'dataPacketReceived') {
544
+ // Can be undefined if the data is sent from a Server SDK
545
+ const participant = this.remoteParticipants.get(ev.value.participantIdentity!);
546
+ const dataPacket = ev.value.value;
547
+ switch (dataPacket.case) {
548
+ case 'user':
549
+ const buffer = FfiClient.instance.copyBuffer(
550
+ dataPacket.value.data!.data!.dataPtr!,
551
+ Number(dataPacket.value.data!.data!.dataLen),
552
+ );
553
+ new FfiHandle(dataPacket.value.data!.handle!.id!).dispose();
554
+ this.emit(
555
+ RoomEvent.DataReceived,
556
+ buffer,
557
+ participant,
558
+ ev.value.kind,
559
+ dataPacket.value.topic,
560
+ );
561
+ break;
562
+ case 'sipDtmf':
563
+ const { code, digit } = dataPacket.value;
564
+ this.emit(RoomEvent.DtmfReceived, code!, digit!, participant!);
565
+ break;
566
+ default:
567
+ break;
568
+ }
569
+ } else if (ev.case == 'e2eeStateChanged') {
570
+ if (ev.value.state == EncryptionState.INTERNAL_ERROR) {
571
+ // throw generic error until Rust SDK is updated to supply the error alongside INTERNAL_ERROR
572
+ this.emit(RoomEvent.EncryptionError, new Error('internal server error'));
573
+ }
574
+ } else if (ev.case == 'connectionStateChanged') {
575
+ this.connectionState = ev.value.state!;
576
+ this.emit(RoomEvent.ConnectionStateChanged, this.connectionState);
577
+ /*} else if (ev.case == 'connected') {
490
578
  this.emit(RoomEvent.Connected);*/
491
- } else if (ev.case == 'disconnected') {
492
- this.emit(RoomEvent.Disconnected, ev.value.reason!);
493
- } else if (ev.case == 'reconnecting') {
494
- this.emit(RoomEvent.Reconnecting);
495
- } else if (ev.case == 'reconnected') {
496
- this.emit(RoomEvent.Reconnected);
497
- } else if (ev.case == 'roomSidChanged') {
498
- this.emit(RoomEvent.RoomSidChanged, ev.value.sid!);
499
- } else if (ev.case === 'streamHeaderReceived' && ev.value.header) {
500
- this.handleStreamHeader(ev.value.header, ev.value.participantIdentity!);
501
- } else if (ev.case === 'streamChunkReceived' && ev.value.chunk) {
502
- this.handleStreamChunk(ev.value.chunk);
503
- } else if (ev.case === 'streamTrailerReceived' && ev.value.trailer) {
504
- this.handleStreamTrailer(ev.value.trailer);
505
- } else if (ev.case === 'roomUpdated') {
506
- this.info = ev.value;
507
- this.emit(RoomEvent.RoomUpdated);
508
- } else if (ev.case === 'moved') {
509
- this.info = ev.value;
510
- this.emit(RoomEvent.Moved);
511
- } else if (ev.case === 'participantsUpdated') {
512
- for (const info of ev.value.participants) {
513
- const participant = this.retrieveParticipantByIdentity(info.identity!);
514
- if (participant) {
515
- participant.info = info;
579
+ } else if (ev.case == 'disconnected') {
580
+ this.emit(RoomEvent.Disconnected, ev.value.reason!);
581
+ } else if (ev.case == 'reconnecting') {
582
+ this.emit(RoomEvent.Reconnecting);
583
+ } else if (ev.case == 'reconnected') {
584
+ this.emit(RoomEvent.Reconnected);
585
+ } else if (ev.case == 'roomSidChanged') {
586
+ this.emit(RoomEvent.RoomSidChanged, ev.value.sid!);
587
+ } else if (ev.case === 'streamHeaderReceived' && ev.value.header) {
588
+ this.handleStreamHeader(ev.value.header, ev.value.participantIdentity!);
589
+ } else if (ev.case === 'streamChunkReceived' && ev.value.chunk) {
590
+ this.handleStreamChunk(ev.value.chunk);
591
+ } else if (ev.case === 'streamTrailerReceived' && ev.value.trailer) {
592
+ this.handleStreamTrailer(ev.value.trailer);
593
+ } else if (ev.case === 'roomUpdated') {
594
+ this.info = ev.value;
595
+ this.emit(RoomEvent.RoomUpdated);
596
+ } else if (ev.case === 'moved') {
597
+ this.info = ev.value;
598
+ this.emit(RoomEvent.Moved);
599
+ } else if (ev.case === 'participantsUpdated') {
600
+ for (const info of ev.value.participants) {
601
+ const participant = this.retrieveParticipantByIdentity(info.identity!);
602
+ if (participant) {
603
+ participant.info = info;
604
+ }
605
+ }
606
+ } else if (ev.case === 'participantEncryptionStatusChanged') {
607
+ try {
608
+ const participant = this.requireParticipantByIdentity(ev.value.participantIdentity!);
609
+ this.emit(
610
+ RoomEvent.ParticipantEncryptionStatusChanged,
611
+ !!ev.value.isEncrypted,
612
+ participant,
613
+ );
614
+ } catch (e: any) {
615
+ console.warn(`RoomEvent.ParticipantEncryptionStatusChanged: ${e.message}`);
516
616
  }
517
617
  }
618
+ } finally {
619
+ unlock();
518
620
  }
519
621
  };
520
622
 
@@ -716,12 +818,14 @@ export type RoomCallbacks = {
716
818
  changedAttributes: Record<string, string>,
717
819
  participant: Participant,
718
820
  ) => void;
821
+ participantEncryptionStatusChanged: (isEncrypted: boolean, participant: Participant) => void;
719
822
  connectionQualityChanged: (quality: ConnectionQuality, participant: Participant) => void;
720
823
  dataReceived: (
721
824
  payload: Uint8Array,
722
825
  participant?: RemoteParticipant,
723
826
  kind?: DataPacketKind,
724
827
  topic?: string,
828
+ encryptionType?: EncryptionType,
725
829
  ) => void;
726
830
  chatMessage: (message: ChatMessage, participant?: Participant) => void;
727
831
  dtmfReceived: (code: number, digit: string, participant: RemoteParticipant) => void;
@@ -755,6 +859,7 @@ export enum RoomEvent {
755
859
  ParticipantMetadataChanged = 'participantMetadataChanged',
756
860
  ParticipantNameChanged = 'participantNameChanged',
757
861
  ParticipantAttributesChanged = 'participantAttributesChanged',
862
+ ParticipantEncryptionStatusChanged = 'participantEncryptionStatusChanged',
758
863
  ConnectionQualityChanged = 'connectionQualityChanged',
759
864
  DataReceived = 'dataReceived',
760
865
  ChatMessage = 'chatMessage',
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const SDK_VERSION = "0.13.19";
1
+ export const SDK_VERSION = "0.13.21";